s2-python 0.0.1__py3-none-any.whl → 0.1.3__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.
Files changed (50) hide show
  1. {s2_python-0.0.1.dist-info → s2_python-0.1.3.dist-info}/METADATA +37 -20
  2. s2_python-0.1.3.dist-info/RECORD +51 -0
  3. {s2_python-0.0.1.dist-info → s2_python-0.1.3.dist-info}/WHEEL +1 -1
  4. s2python/__init__.py +1 -1
  5. s2python/common/__init__.py +17 -16
  6. s2python/common/duration.py +3 -6
  7. s2python/common/handshake.py +6 -7
  8. s2python/common/handshake_response.py +6 -7
  9. s2python/common/instruction_status_update.py +8 -10
  10. s2python/common/number_range.py +10 -10
  11. s2python/common/power_forecast.py +7 -9
  12. s2python/common/power_forecast_element.py +4 -8
  13. s2python/common/power_forecast_value.py +2 -7
  14. s2python/common/power_measurement.py +7 -9
  15. s2python/common/power_range.py +6 -6
  16. s2python/common/power_value.py +2 -5
  17. s2python/common/reception_status.py +6 -9
  18. s2python/common/resource_manager_details.py +11 -10
  19. s2python/common/revoke_object.py +6 -8
  20. s2python/common/role.py +2 -5
  21. s2python/common/select_control_type.py +6 -7
  22. s2python/common/session_request.py +6 -7
  23. s2python/common/support.py +7 -5
  24. s2python/common/timer.py +2 -8
  25. s2python/common/transition.py +4 -15
  26. s2python/frbc/__init__.py +7 -7
  27. s2python/frbc/frbc_actuator_description.py +13 -13
  28. s2python/frbc/frbc_actuator_status.py +6 -17
  29. s2python/frbc/frbc_fill_level_target_profile.py +11 -11
  30. s2python/frbc/frbc_fill_level_target_profile_element.py +2 -6
  31. s2python/frbc/frbc_instruction.py +6 -10
  32. s2python/frbc/frbc_leakage_behaviour.py +9 -11
  33. s2python/frbc/frbc_leakage_behaviour_element.py +2 -5
  34. s2python/frbc/frbc_operation_mode.py +5 -9
  35. s2python/frbc/frbc_operation_mode_element.py +3 -6
  36. s2python/frbc/frbc_storage_description.py +2 -5
  37. s2python/frbc/frbc_storage_status.py +6 -7
  38. s2python/frbc/frbc_system_description.py +10 -9
  39. s2python/frbc/frbc_timer_status.py +6 -9
  40. s2python/frbc/frbc_usage_forecast.py +7 -9
  41. s2python/frbc/frbc_usage_forecast_element.py +2 -6
  42. s2python/generated/gen_s2.py +848 -833
  43. s2python/message.py +47 -0
  44. s2python/s2_parser.py +112 -0
  45. s2python/s2_validation_error.py +3 -1
  46. s2python/utils.py +7 -2
  47. s2python/validate_values_mixin.py +43 -25
  48. s2_python-0.0.1.dist-info/RECORD +0 -49
  49. {s2_python-0.0.1.dist-info → s2_python-0.1.3.dist-info}/entry_points.txt +0 -0
  50. {s2_python-0.0.1.dist-info → s2_python-0.1.3.dist-info}/top_level.txt +0 -0
s2python/message.py ADDED
@@ -0,0 +1,47 @@
1
+ from typing import Union
2
+
3
+ from pydantic import BaseModel # pylint: disable=no-name-in-module, unused-import; ignore E0611 and W0611
4
+
5
+ from s2python.common import (
6
+ Handshake,
7
+ HandshakeResponse,
8
+ InstructionStatusUpdate,
9
+ PowerForecast,
10
+ PowerMeasurement,
11
+ ReceptionStatus,
12
+ ResourceManagerDetails,
13
+ RevokeObject,
14
+ SelectControlType,
15
+ SessionRequest,
16
+ )
17
+ from s2python.frbc import (
18
+ FRBCActuatorStatus,
19
+ FRBCFillLevelTargetProfile,
20
+ FRBCInstruction,
21
+ FRBCLeakageBehaviour,
22
+ FRBCStorageStatus,
23
+ FRBCSystemDescription,
24
+ FRBCTimerStatus,
25
+ FRBCUsageForecast,
26
+ )
27
+
28
+ S2Message = Union[
29
+ FRBCActuatorStatus,
30
+ FRBCFillLevelTargetProfile,
31
+ FRBCInstruction,
32
+ FRBCLeakageBehaviour,
33
+ FRBCStorageStatus,
34
+ FRBCSystemDescription,
35
+ FRBCTimerStatus,
36
+ FRBCUsageForecast,
37
+ Handshake,
38
+ HandshakeResponse,
39
+ InstructionStatusUpdate,
40
+ PowerForecast,
41
+ PowerMeasurement,
42
+ ReceptionStatus,
43
+ ResourceManagerDetails,
44
+ RevokeObject,
45
+ SelectControlType,
46
+ SessionRequest,
47
+ ]
s2python/s2_parser.py ADDED
@@ -0,0 +1,112 @@
1
+ import json
2
+ import logging
3
+ from typing import Dict, Optional, Type, TypeVar, Union
4
+
5
+ from s2python.common import (
6
+ Handshake,
7
+ HandshakeResponse,
8
+ InstructionStatusUpdate,
9
+ PowerForecast,
10
+ PowerMeasurement,
11
+ ReceptionStatus,
12
+ ResourceManagerDetails,
13
+ RevokeObject,
14
+ SelectControlType,
15
+ SessionRequest,
16
+ )
17
+ from s2python.frbc import (
18
+ FRBCActuatorStatus,
19
+ FRBCFillLevelTargetProfile,
20
+ FRBCInstruction,
21
+ FRBCLeakageBehaviour,
22
+ FRBCStorageStatus,
23
+ FRBCSystemDescription,
24
+ FRBCTimerStatus,
25
+ FRBCUsageForecast,
26
+ )
27
+ from s2python.s2_validation_error import S2ValidationError
28
+ from s2python.validate_values_mixin import S2Message
29
+
30
+ LOGGER = logging.getLogger(__name__)
31
+ S2MessageType = str
32
+
33
+ M = TypeVar("M", bound=S2Message)
34
+
35
+
36
+ # May be generated with development_utilities/generate_s2_message_type_to_class.py
37
+ TYPE_TO_MESSAGE_CLASS: Dict[str, Type[S2Message]] = {
38
+ "FRBC.ActuatorStatus": FRBCActuatorStatus,
39
+ "FRBC.FillLevelTargetProfile": FRBCFillLevelTargetProfile,
40
+ "FRBC.Instruction": FRBCInstruction,
41
+ "FRBC.LeakageBehaviour": FRBCLeakageBehaviour,
42
+ "FRBC.StorageStatus": FRBCStorageStatus,
43
+ "FRBC.SystemDescription": FRBCSystemDescription,
44
+ "FRBC.TimerStatus": FRBCTimerStatus,
45
+ "FRBC.UsageForecast": FRBCUsageForecast,
46
+ "Handshake": Handshake,
47
+ "HandshakeResponse": HandshakeResponse,
48
+ "InstructionStatusUpdate": InstructionStatusUpdate,
49
+ "PowerForecast": PowerForecast,
50
+ "PowerMeasurement": PowerMeasurement,
51
+ "ReceptionStatus": ReceptionStatus,
52
+ "ResourceManagerDetails": ResourceManagerDetails,
53
+ "RevokeObject": RevokeObject,
54
+ "SelectControlType": SelectControlType,
55
+ "SessionRequest": SessionRequest,
56
+ }
57
+
58
+
59
+ class S2Parser:
60
+ @staticmethod
61
+ def _parse_json_if_required(unparsed_message: Union[dict, str]) -> dict:
62
+ if isinstance(unparsed_message, str):
63
+ return json.loads(unparsed_message)
64
+ return unparsed_message
65
+
66
+ @staticmethod
67
+ def parse_as_any_message(unparsed_message: Union[dict, str]) -> S2Message:
68
+ """Parse the message as any S2 python message regardless of message type.
69
+
70
+ :param unparsed_message: The message as a JSON-formatted string or as a json-parsed dictionary.
71
+ :raises: S2ValidationError, json.JSONDecodeError
72
+ :return: The parsed S2 message if no errors were found.
73
+ """
74
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
75
+ message_type = S2Parser.parse_message_type(message_json)
76
+
77
+ if message_type not in TYPE_TO_MESSAGE_CLASS:
78
+ raise S2ValidationError(
79
+ message_json,
80
+ f"Unable to parse {message_type} as an S2 message. Type unknown.",
81
+ )
82
+
83
+ return TYPE_TO_MESSAGE_CLASS[message_type].parse_obj(message_json)
84
+
85
+ @staticmethod
86
+ def parse_as_message(unparsed_message: Union[dict, str], as_message: Type[M]) -> M:
87
+ """Parse the message to a specific S2 python message.
88
+
89
+ :param unparsed_message: The message as a JSON-formatted string or as a JSON-parsed dictionary.
90
+ :param as_message: The type of message that is expected within the `message`
91
+ :raises: S2ValidationError, json.JSONDecodeError
92
+ :return: The parsed S2 message if no errors were found.
93
+ """
94
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
95
+ return as_message.from_dict(message_json)
96
+
97
+ @staticmethod
98
+ def parse_message_type(
99
+ unparsed_message: Union[dict, str]
100
+ ) -> Optional[S2MessageType]:
101
+ """Parse only the message type from the unparsed message.
102
+
103
+ This is useful to call before `parse_as_message` to retrieve the message type and allows for strictly-typed
104
+ parsing.
105
+
106
+ :param unparsed_message: The message as a JSON-formatted string or as a JSON-parsed dictionary.
107
+ :raises: json.JSONDecodeError
108
+ :return: The parsed S2 message type if no errors were found.
109
+ """
110
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
111
+
112
+ return message_json.get("message_type")
@@ -1,10 +1,12 @@
1
+ from typing import Union
2
+
1
3
  from pydantic import ValidationError
2
4
 
3
5
 
4
6
  class S2ValidationError(Exception):
5
7
  obj: object
6
8
  msg: str
7
- pydantic_validation_error: "ValidationError | TypeError | None"
9
+ pydantic_validation_error: Union[ValidationError, TypeError, None]
8
10
 
9
11
  def __init__(self, obj: object, msg: str):
10
12
  self.obj = obj
s2python/utils.py CHANGED
@@ -1,3 +1,8 @@
1
- def pairwise(arr: list):
1
+ from typing import Generator, List, Tuple, TypeVar
2
+
3
+ P = TypeVar("P")
4
+
5
+
6
+ def pairwise(arr: List[P]) -> Generator[Tuple[P, P], None, None]:
2
7
  for i in range(max(len(arr) - 1, 0)):
3
- yield (arr[i], arr[i + 1])
8
+ yield arr[i], arr[i + 1]
@@ -1,45 +1,50 @@
1
1
  from typing import (
2
- TypeVar,
2
+ AbstractSet,
3
+ Any,
4
+ Callable,
5
+ Dict,
3
6
  Generic,
7
+ List,
8
+ Mapping,
9
+ Optional,
4
10
  Protocol,
5
11
  Type,
6
- Tuple,
7
- Optional,
8
- Callable,
9
- cast,
10
- Any,
12
+ TypeVar,
11
13
  Union,
12
- AbstractSet,
13
- Mapping,
14
- List,
15
- Dict,
16
14
  )
17
15
 
18
- from pydantic import BaseModel, StrBytes, Protocol as PydanticProtocol, ValidationError
16
+ from pydantic import BaseModel # pylint: disable=no-name-in-module; ignore E0611
17
+ from pydantic import Protocol as PydanticProtocol # pylint: disable=no-name-in-module
18
+ from pydantic import StrBytes, ValidationError # pylint: disable=no-name-in-module; ignore E0611
19
+ from pydantic.error_wrappers import display_errors # pylint: disable=no-name-in-module
19
20
 
20
21
  from s2python.s2_validation_error import S2ValidationError
21
22
 
22
- B = TypeVar("B", bound=BaseModel, covariant=True)
23
+ B_co = TypeVar("B_co", bound=BaseModel, covariant=True)
23
24
 
24
25
  IntStr = Union[int, str]
25
26
  AbstractSetIntStr = AbstractSet[IntStr]
26
27
  MappingIntStrAny = Mapping[IntStr, Any]
27
28
 
28
29
 
29
- class SupportsValidation(Protocol[B]):
30
+ class SupportsValidation(Protocol[B_co]):
30
31
  # ValidateValuesMixin methods
31
32
  def to_json(self) -> str:
32
33
  ...
33
34
 
34
- def to_dict(self) -> dict:
35
+ def to_dict(self) -> Dict:
36
+ ...
37
+
38
+ @classmethod
39
+ def from_json(cls, json_str: str) -> B_co:
35
40
  ...
36
41
 
37
42
  @classmethod
38
- def from_json(cls, json_str: str) -> B:
43
+ def from_dict(cls, json_dict: Dict) -> B_co:
39
44
  ...
40
45
 
41
46
  # Pydantic methods
42
- def json(
47
+ def json( # pylint: disable=too-many-arguments
43
48
  self,
44
49
  *,
45
50
  include: Optional[Union["AbstractSetIntStr", "MappingIntStrAny"]] = None,
@@ -55,7 +60,7 @@ class SupportsValidation(Protocol[B]):
55
60
  ) -> str:
56
61
  ...
57
62
 
58
- def dict(
63
+ def dict( # pylint: disable=too-many-arguments
59
64
  self,
60
65
  *,
61
66
  include: Optional[Union["AbstractSetIntStr", "MappingIntStrAny"]] = None,
@@ -69,7 +74,7 @@ class SupportsValidation(Protocol[B]):
69
74
  ...
70
75
 
71
76
  @classmethod
72
- def parse_raw(
77
+ def parse_raw( # pylint: disable=too-many-arguments
73
78
  cls,
74
79
  b: StrBytes,
75
80
  *,
@@ -77,7 +82,11 @@ class SupportsValidation(Protocol[B]):
77
82
  encoding: str = ...,
78
83
  proto: PydanticProtocol = ...,
79
84
  allow_pickle: bool = ...,
80
- ) -> B:
85
+ ) -> B_co:
86
+ ...
87
+
88
+ @classmethod
89
+ def parse_obj(cls, obj: Any) -> "B_co":
81
90
  ...
82
91
 
83
92
 
@@ -101,15 +110,24 @@ class ValidateValuesMixin(Generic[C]):
101
110
  gen_model: C = cls.parse_raw(json_str)
102
111
  return gen_model
103
112
 
113
+ @classmethod
114
+ def from_dict(cls: Type[C], json_dict: dict) -> C:
115
+ gen_model: C = cls.parse_obj(json_dict)
116
+ return gen_model
117
+
118
+
119
+ class S2Message(Generic[C], ValidateValuesMixin[C], BaseModel):
120
+ pass
121
+
104
122
 
105
123
  def convert_to_s2exception(f: Callable) -> Callable:
106
124
  def inner(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
107
125
  try:
108
126
  return f(*args, **kwargs)
109
- except (ValidationError, TypeError) as e:
110
- raise S2ValidationError(
111
- args, "Pydantic raised a format validation error."
112
- ) from e
127
+ except ValidationError as e:
128
+ raise S2ValidationError(args, display_errors(e.errors())) from e
129
+ except TypeError as e:
130
+ raise S2ValidationError(args, str(e)) from e
113
131
 
114
132
  inner.__doc__ = f.__doc__
115
133
  inner.__annotations__ = f.__annotations__
@@ -118,8 +136,8 @@ def convert_to_s2exception(f: Callable) -> Callable:
118
136
 
119
137
 
120
138
  def catch_and_convert_exceptions(
121
- input_class: Type[SupportsValidation[B]],
122
- ) -> Type[SupportsValidation[B]]:
139
+ input_class: Type[SupportsValidation[B_co]],
140
+ ) -> Type[SupportsValidation[B_co]]:
123
141
  input_class.__init__ = convert_to_s2exception(input_class.__init__) # type: ignore[method-assign]
124
142
  input_class.__setattr__ = convert_to_s2exception(input_class.__setattr__) # type: ignore[method-assign]
125
143
  input_class.parse_raw = convert_to_s2exception(input_class.parse_raw) # type: ignore[method-assign]
@@ -1,49 +0,0 @@
1
- s2python/__init__.py,sha256=aLCMCipFsPGFEsc8ritsDiYDSUBTW5kA34N_3po08p8,352
2
- s2python/s2_validation_error.py,sha256=Veij_ReS0VCTC8fCRhxTgmZVVMCUiBu2VmtzYjJ6NBc,266
3
- s2python/utils.py,sha256=YJw3-i4fD_ud7rbm68VWE3PRjp-EgsnS_YtMxf6UnhA,102
4
- s2python/validate_values_mixin.py,sha256=CfRXeSkAOBBcS8zy4eNl3dx-01Sm6Wgn0bgTEhsdZks,3448
5
- s2python/version.py,sha256=w9Iw7QVvd8lme2wKwEbCo5IgetVjSfFBOOYAcA_Q0Ns,18
6
- s2python/common/__init__.py,sha256=fVJ0n13kRzzAtJcOxZHz3CEDexWXnyueJMcEXGdq_88,1345
7
- s2python/common/duration.py,sha256=-0_llJYXD5Z6Sx96XgY_unH3vcyCfhddxERAM7-8CGg,681
8
- s2python/common/handshake.py,sha256=v1JoZaYIk1faZFCAuf8sINq0xR_W03RaHhjkLODYLg4,460
9
- s2python/common/handshake_response.py,sha256=0o7miKQCWJ8cXFtvblQM81nUznyxZ2zrNrI22XAzTJg,516
10
- s2python/common/instruction_status_update.py,sha256=AzhgyIdcgtwbCAqNtUamFDttrMbyoMEqGD3qB-bFAgc,714
11
- s2python/common/number_range.py,sha256=b9KQ2XDyrcyqiHzN2rI7ldG-rsNk3b5os1BLDZOqTf4,1115
12
- s2python/common/power_forecast.py,sha256=zrFzR7Apg8OBkP_GQo4kseT2ZMjlinYRnh_KXe1e8vw,697
13
- s2python/common/power_forecast_element.py,sha256=MU6ndeMAkubJDrLfFON4IwyykCWHHqloQ_GvZp1Y-bo,756
14
- s2python/common/power_forecast_value.py,sha256=UP3IoyZVpkVhYrpN4CZvQHC8EQq7H_Q_S9BTs5us1KU,401
15
- s2python/common/power_measurement.py,sha256=Tiyb8ROub8m-tGCBQuZ8JTYV3d4HlyhqapP5Upvssms,683
16
- s2python/common/power_range.py,sha256=EQgFppktiusyZ6Ib7CKhx1-C167YQddgvVeu49xVY7o,751
17
- s2python/common/power_value.py,sha256=rqKXBrBwNCRpF13fl8zVhacxUQYPYPC2WbeP0OXZwdM,347
18
- s2python/common/reception_status.py,sha256=Vkj6ZzTkl-WYprszRStBuHIyKfoJW-gYp6NK6MuEwRo,532
19
- s2python/common/resource_manager_details.py,sha256=tDbR8hfaQ4Nz8po954HNm0YSJW95C7JomwTW1t5q33A,1029
20
- s2python/common/revoke_object.py,sha256=0KHb8klzsREE5oB1XwdXxBP_MIb3cQUDAz8V-Uv3moQ,587
21
- s2python/common/role.py,sha256=Vk732JKk05jOb6wPkG18me34kFT1MngKnLg1a8VWq5I,311
22
- s2python/common/select_control_type.py,sha256=bgZw9oePLBLAN7TEWwNsqMvO2zQMj3PdFUZ-KuqZfJk,516
23
- s2python/common/session_request.py,sha256=iDjW5HwDbAuwaJmpJROQAW3vqgNJddsLOKejWDPdbVc,495
24
- s2python/common/support.py,sha256=L08puzgMLvslKRqVQAnXH_Q5YRT5HTOPj8vDDNyzNYc,988
25
- s2python/common/timer.py,sha256=o2L48hefqC8cu6FAgjavjWMMnI8OxOHizP5zfL1abHY,558
26
- s2python/common/transition.py,sha256=k34bSvf8eU1Ys9SZkZKSye-NcxdSAqKjoWCNiT-8uxM,1107
27
- s2python/frbc/__init__.py,sha256=ROV3qZoldPkdgVFfMQr5Mf3GDfBzXaMfhNNCuXY6T0s,1104
28
- s2python/frbc/frbc_actuator_description.py,sha256=v2pWCUSGaw4IftMhNOjeLXE8B86XZNCyiZr3dQkziuo,6699
29
- s2python/frbc/frbc_actuator_status.py,sha256=Y8VUF0AA52tCG32qVb6jn93DMq0GPeaA9T5UPOm3m1c,999
30
- s2python/frbc/frbc_fill_level_target_profile.py,sha256=s-SqjWCEFc9oDm4zUW4RiFEnyWXh6HNlZgSF5P9kHHc,868
31
- s2python/frbc/frbc_fill_level_target_profile_element.py,sha256=0R2rJ8-rm-2igLgIdZI0SXMt450Ah0i54CBzehVxyI0,852
32
- s2python/frbc/frbc_instruction.py,sha256=uXKec7nUsQBmAkS6slBiyZ_1lo2i6cpuljRC_eV7LK8,829
33
- s2python/frbc/frbc_leakage_behaviour.py,sha256=4TnFHsP-wdtHGpWJ1xauE3H2Q_N5FLflG6YJvuKYpcA,771
34
- s2python/frbc/frbc_leakage_behaviour_element.py,sha256=Z7d8rELLnQDx-qGAVVtuoNnWHyj1CUYG_FOliBOpqwk,656
35
- s2python/frbc/frbc_operation_mode.py,sha256=DlQa4cRQKmPRoWmabpa9j3A8U0dBQRkEQHG5r7Y4VoM,2023
36
- s2python/frbc/frbc_operation_mode_element.py,sha256=8Cfp7sTHT4n0KQmFdnWCdzUgPW09NDUm3NL3RBBl2Uc,1099
37
- s2python/frbc/frbc_storage_description.py,sha256=AMuVZpRk5Pvxx6SnKg9WyELD8kqRJbAmJ1JL5tT0gdk,621
38
- s2python/frbc/frbc_storage_status.py,sha256=zMgdLYrAgv6Jne5oKWgGMFhUIbJegX501Rskaz_a5-g,516
39
- s2python/frbc/frbc_system_description.py,sha256=SGxtRNTlBw3-SU5A8J6Y2cxdYLSZiztR2RdrstUwCSc,935
40
- s2python/frbc/frbc_timer_status.py,sha256=RxbnLzAFJy-OXwwFbQ0QpzRcJ2awmysLcapzjDEU0K8,722
41
- s2python/frbc/frbc_usage_forecast.py,sha256=-bm6XSDq_wkbGeBj_d1i-C1RdaZpIfD1yJqZ6mWX4bw,735
42
- s2python/frbc/frbc_usage_forecast_element.py,sha256=bgFG01udYSizOyoGZPIPom0vj_dB3qokffU8hZ7BbC8,600
43
- s2python/generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- s2python/generated/gen_s2.py,sha256=DXLCNE1gUS7XnRTgWlBpy73szr5HrHcrN0TZXvq8N_g,61928
45
- s2_python-0.0.1.dist-info/METADATA,sha256=7PixoPcZdw86h9LfGGX2ithyEEhyipz2IlP0-b3LWmw,2855
46
- s2_python-0.0.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
47
- s2_python-0.0.1.dist-info/entry_points.txt,sha256=feX-xmgJZgSe5-jxMgFKPKCJz4Ys3eQcGrsXsirNZyM,61
48
- s2_python-0.0.1.dist-info/top_level.txt,sha256=OLFq0oDhr77Mp-EYLEcWk5P3jvooOt4IHkTI5KYJMc8,9
49
- s2_python-0.0.1.dist-info/RECORD,,