mms-client 1.0.6__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,7 +17,7 @@ class RequestType(Enum):
17
17
 
18
18
  INFO = "mp.info"
19
19
  MARKET = "mp.market"
20
- REGISTRATION = "mp.registration"
20
+ REGISTRATION = "mpr"
21
21
  REPORT = "mp.report"
22
22
  OMI = "mp.omi"
23
23
 
@@ -129,7 +129,7 @@ class Serializer:
129
129
  # Now, verify that the response doesn't contain an unexpected data type and then retrieve the payload data
130
130
  # from within the envelope
131
131
  self._verify_tree_data_tag(envelope_node, data_type)
132
- resp.payload = self._from_tree_data(envelope_node.find(data_type.__name__), data_type)
132
+ resp.payload = self._from_tree_data(envelope_node.find(get_tag(data_type)), data_type)
133
133
 
134
134
  # Finally, attempt to extract the messages from within the payload
135
135
  resp.messages = self._from_tree_messages(raw, envelope_type, data_type, self._payload_key, False)
@@ -167,7 +167,7 @@ class Serializer:
167
167
  # Note: apparently, mypy doesn't know about setter-getter properties either...
168
168
  self._verify_tree_data_tag(env_node, data_type)
169
169
  resp.payload = [
170
- self._from_tree_data(item, data_type) for item in env_node.findall(data_type.__name__) # type: ignore[misc]
170
+ self._from_tree_data(item, data_type) for item in env_node.findall(get_tag(data_type)) # type: ignore[misc]
171
171
  ]
172
172
 
173
173
  # Finally, attempt to extract the messages from within the payload
@@ -216,7 +216,7 @@ class Serializer:
216
216
  ValueError: If the expected data type is not found in the response.
217
217
  """
218
218
  data_tags = set(node.tag for node in raw)
219
- if not data_tags.issubset([data_type.__name__, "Messages"]):
219
+ if not data_tags.issubset([data_type.__name__, data_type.__xml_tag__, "Messages"]):
220
220
  raise ValueError(f"Expected data type '{data_type.__name__}' not found in response")
221
221
 
222
222
  def _from_tree_data(self, raw: Optional[Element], data_type: Type[P]) -> Optional[ResponseData[P]]:
@@ -291,8 +291,7 @@ class Serializer:
291
291
  )
292
292
  else:
293
293
  # Iterate over each field on the current type...
294
- for name, field in current_type.model_fields.items():
295
- print(f"Checking field {name} with type {field.annotation}...")
294
+ for field in current_type.model_fields.values():
296
295
 
297
296
  # First, get the arguments and origin of the field's annotation
298
297
  args = get_args(field.annotation)
@@ -420,7 +419,7 @@ def _create_response_payload_type(key: str, envelope_type: Type[E], data_type: T
420
419
 
421
420
 
422
421
  @lru_cache(maxsize=None)
423
- def _create_response_common_type(tag_type: Type) -> Type[ResponseCommon]:
422
+ def _create_response_common_type(tag_type: Type[Union[E, P]]) -> Type[ResponseCommon]:
424
423
  """Create a new wrapper for the ResponseCommon type with the given tag.
425
424
 
426
425
  This method is intended to save us the overhead of writing a new class for each tag type. Instead, we can
@@ -432,7 +431,7 @@ def _create_response_common_type(tag_type: Type) -> Type[ResponseCommon]:
432
431
  Returns: The wrapper type that will be used for deserialization.
433
432
  """ # fmt: skip
434
433
  # First, create a new wrapper type that contains the ResponseCommon type with the appropriate XML tag
435
- class Wrapper(ResponseCommon, tag=tag_type.__name__): # type: ignore[call-arg]
434
+ class Wrapper(ResponseCommon, tag=get_tag(tag_type)): # type: ignore[call-arg]
436
435
  """Wrapper for the validation object with the proper XML tag."""
437
436
 
438
437
  # Finally, return the wrapper type so we can instantiate it
@@ -462,7 +461,7 @@ def _create_request_payload_type(key: str, envelope_type: Type[E], data_type: Ty
462
461
  """Wrapper for the data type that will be used to store the data in the payload."""
463
462
 
464
463
  # The data to be stored in the payload
465
- data: data_type = element(tag=data_type.__name__) # type: ignore[valid-type]
464
+ data: data_type = element(tag=get_tag(data_type)) # type: ignore[valid-type]
466
465
 
467
466
  def __init__(self, envelope: envelope_type, data: data_type): # type: ignore[valid-type]
468
467
  """Create a new envelope to store payload data.
@@ -511,3 +510,14 @@ def _find_or_fail(node: Element, tag: str) -> Element:
511
510
  if found is None:
512
511
  raise ValueError(f"Expected tag '{tag}' not found in node") # pragma: no cover
513
512
  return found
513
+
514
+
515
+ def get_tag(data_type: Type[P]) -> str:
516
+ """Get the tag for the given data type.
517
+
518
+ Arguments:
519
+ data_type (Type[Payload]): The data type to get the tag for.
520
+
521
+ Returns: The tag for the given data type.
522
+ """
523
+ return data_type.__xml_tag__ or data_type.__name__
@@ -1,12 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mms-client
3
- Version: 1.0.6
3
+ Version: 1.1.0
4
4
  Summary: API client for accessing the MMS
5
+ Home-page: https://github.com/ElectroRoute-Japan/mms-client
5
6
  Author: Ryan Wood
6
7
  Author-email: ryan.wood@electroroute.co.jp
7
8
  Requires-Python: >=3.11,<4.0
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Framework :: Pydantic :: 2
11
+ Classifier: Framework :: Pytest
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
14
+ Classifier: Natural Language :: English
15
+ Classifier: Operating System :: OS Independent
8
16
  Classifier: Programming Language :: Python :: 3
9
17
  Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
10
21
  Requires-Dist: backoff (>=2.2.1,<3.0.0)
11
22
  Requires-Dist: cryptography (>=42.0.5,<43.0.0)
12
23
  Requires-Dist: lxml (>=5.1.0,<6.0.0)
@@ -32,6 +43,9 @@ The underlying API sends and receives XML documents. Each of these request or re
32
43
 
33
44
  After the data has been converted and added to the outer request object, it is sent to the appropriate server endpoint via a Zeep client. The client certificate is also injected into the request using a PCKS12 adaptor.
34
45
 
46
+ # Serialization
47
+ This library relies on Pydantic 2 and the pydantic-xml library for serialization/deserialization. As such, any type in this library can be converted to not only XML, but to JSON as well. This is extremely useful if you're trying to build a pass-through API service or something similar.
48
+
35
49
  ## Client Types
36
50
  Clients cannot call any and all endpoints in this API, willy-nilly. Some endpoints are restricted to particular clients. At the moment, there are two clients: Balance Service Providers (BSPs) and Transmission Service Operators (TSOs). Most likely you're operating as a BSP, in which case you'll have access to all endpoints. However, it makes little sense for a TSO to be able to submit bids on their own power, so they are restricted to a read-only role in most cases.
37
51
 
@@ -43,7 +57,7 @@ The MMS has two separate endpoints, depending on whether you want to access mark
43
57
  # Object Hierarchy
44
58
  The object hierarchy contained in this project is not trivial, and perhaps that reflects a bit of overengineering on our part. However, we chose the paradigm we did to reduce the number of types which had to be exposed to the user. The diagram below indicates how this hierarchy works:
45
59
 
46
- ![MMS Client Object Hierarchy](https://github.com/ElectroRoute-Japan/mms-client/blob/main/docs/mmsclient_hierarchy.drawio.png)
60
+ ![MMS Client Object Hierarchy](https://github.com/ElectroRoute-Japan/mms-client/raw/main/docs/mmsclient_hierarchy.drawio.png)
47
61
 
48
62
  Note that there are some types here that are shared between the request and response: mainly, anything inheriting from `mms_client.types.base.Envelop` or `mms_client.types.base.Payload`. For users of the client, only the Payload types need ever be used. Everything else has been obfuscated away, so it is unecessary for the user to have access to these. However, we will explain our reasoning here to provide additional context.
49
63
 
@@ -184,6 +198,8 @@ This client is not complete. Currently, it supports the following endpoints:
184
198
  - MarketSubmit_OfferData
185
199
  - MarketQuery_OfferQuery
186
200
  - MarketCancel_OfferCancel
201
+ - RegistrationSubmit_Resource
202
+ - RegistrationQuery_Resource
187
203
 
188
204
  We can add support for additional endpoints as time goes on, and independent contribution is, of course, welcome. However, support for attachments is currently limited because none of the endpoints we support currently require them. We have implemented attachment support up to the client level, but we haven't developed an architecture for submitting them through an endpoint yet.
189
205
 
@@ -15,20 +15,22 @@ mms_client/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
15
15
  mms_client/services/base.py,sha256=8yWBeDoBimY9BZeMOkMDV-ggvPTzZHnlqXiv9eo8fIQ,25363
16
16
  mms_client/services/market.py,sha256=kwcbXz1Op4hwXbOn_Xsx_qBNuHfvWAxBH6G2f4AWzPI,4892
17
17
  mms_client/services/omi.py,sha256=h6cM5U3-iSm0YiIaJwqYTZeI5uhLbA7FPxh_qy_3qww,521
18
- mms_client/services/registration.py,sha256=MUUp0SDE7F0d_lV5pXNqPhrU87CAZc6AA72AdmRAu6Q,571
18
+ mms_client/services/registration.py,sha256=9pNZdgwRbJJCnmsVgESNnc3ywkn-wdQryumUhLn6Xvg,3419
19
19
  mms_client/services/report.py,sha256=HYVJNwEHo6ZC6497UqO5y1IqZ2ga3kVH5AepdxhYfug,537
20
20
  mms_client/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- mms_client/types/base.py,sha256=EMGCDvf_B_8q1YMH-l31c_lV0OdxxAReFNU3zPs0K0Q,9681
22
- mms_client/types/enums.py,sha256=WzFD9fG9jKFsA91oKyXCAW0oKzUTH3NyUkV4A_N0sXg,329
23
- mms_client/types/fields.py,sha256=QaNnfU0goAld8VADD_DXZ6qEuUrxISY7rFg5gB6k948,6250
21
+ mms_client/types/base.py,sha256=wrDPn9io30in_w2qKa4A503gX_gAaMT7a1MpZ3HcmIc,9701
22
+ mms_client/types/enums.py,sha256=wXlXvifWXWm9dowfG5iQlkax_6OX6z2nHid94WkhQwE,464
23
+ mms_client/types/fields.py,sha256=SAvrDJGSvOVtbGvaaqLFBx2mCr0AxzqVFUTJgPSEDOY,12065
24
24
  mms_client/types/market.py,sha256=OKjBIx9aIgaLUci6b5WoB4NZbFncRbbd2FxeAvW1iWw,2588
25
- mms_client/types/offer.py,sha256=EdxgHlU9FWPuMq3tLYcbILBWnL5SWMsH5Qe7NRhGz9Y,6300
26
- mms_client/types/transport.py,sha256=SGzDGoTpUvhGpkKxoyJR22z-Ao_Dj5idz97vd6u7fqk,4419
25
+ mms_client/types/offer.py,sha256=orlohxAWZlW2rwPJpGAoiy1knoCT2x2sXJyzThkXYVY,7693
26
+ mms_client/types/registration.py,sha256=Nir73S3ffpk0O_fnTD2alFaqV1k67_8dcyyduXvPBI4,1381
27
+ mms_client/types/resource.py,sha256=_pRNAqOX8A0lUXmU8qO_8QwRtRx9BwZDdrN-w_Fnu38,66466
28
+ mms_client/types/transport.py,sha256=vyosoeGSdYthqlmiyDAkZusYa8yVHUwOzfTqwZne2Ik,4407
27
29
  mms_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
30
  mms_client/utils/errors.py,sha256=jYdlG4OPI82s0fJcXCRNlKeEixDUSSAxjs_7C16qVL4,2306
29
- mms_client/utils/serialization.py,sha256=Zp1pZvrWcbV4p_PERS5e9zNKPSeHQFijYzJiby3y0Pk,25206
31
+ mms_client/utils/serialization.py,sha256=7DW-xD89DNukcwk6rgS4eBHsMP5kBdiwkvVM4kv2_F8,25438
30
32
  mms_client/utils/web.py,sha256=fcdCtdDrHBPyhIlTcyiuAk3D3TlW8HmUw-wGfpG4KTA,9653
31
- mms_client-1.0.6.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
32
- mms_client-1.0.6.dist-info/METADATA,sha256=sImN-laCo5emcrt6KlfJZGmdb85XlBbTumjRlmIkUZQ,13840
33
- mms_client-1.0.6.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
34
- mms_client-1.0.6.dist-info/RECORD,,
33
+ mms_client-1.1.0.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
34
+ mms_client-1.1.0.dist-info/METADATA,sha256=jb3k6RPJGTpxMiIAaJTKzGfZcgoEgrrxeOPNrSoRoUM,14742
35
+ mms_client-1.1.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
36
+ mms_client-1.1.0.dist-info/RECORD,,