s2-python 0.0.1__py3-none-any.whl → 0.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.
Files changed (46) hide show
  1. {s2_python-0.0.1.dist-info → s2_python-0.1.0.dist-info}/METADATA +20 -4
  2. s2_python-0.1.0.dist-info/RECORD +50 -0
  3. {s2_python-0.0.1.dist-info → s2_python-0.1.0.dist-info}/WHEEL +1 -1
  4. s2python/__init__.py +1 -1
  5. s2python/common/duration.py +2 -2
  6. s2python/common/handshake.py +2 -2
  7. s2python/common/handshake_response.py +2 -2
  8. s2python/common/instruction_status_update.py +2 -2
  9. s2python/common/number_range.py +10 -7
  10. s2python/common/power_forecast.py +3 -3
  11. s2python/common/power_forecast_element.py +4 -5
  12. s2python/common/power_forecast_value.py +2 -4
  13. s2python/common/power_measurement.py +3 -3
  14. s2python/common/power_range.py +6 -3
  15. s2python/common/power_value.py +2 -2
  16. s2python/common/reception_status.py +2 -2
  17. s2python/common/resource_manager_details.py +4 -3
  18. s2python/common/revoke_object.py +2 -2
  19. s2python/common/role.py +2 -2
  20. s2python/common/select_control_type.py +2 -2
  21. s2python/common/session_request.py +2 -2
  22. s2python/common/support.py +6 -4
  23. s2python/common/timer.py +2 -2
  24. s2python/common/transition.py +3 -3
  25. s2python/frbc/frbc_actuator_description.py +10 -4
  26. s2python/frbc/frbc_actuator_status.py +2 -4
  27. s2python/frbc/frbc_fill_level_target_profile.py +5 -3
  28. s2python/frbc/frbc_fill_level_target_profile_element.py +2 -2
  29. s2python/frbc/frbc_instruction.py +2 -2
  30. s2python/frbc/frbc_leakage_behaviour.py +3 -5
  31. s2python/frbc/frbc_leakage_behaviour_element.py +2 -2
  32. s2python/frbc/frbc_operation_mode.py +5 -4
  33. s2python/frbc/frbc_operation_mode_element.py +2 -2
  34. s2python/frbc/frbc_storage_description.py +2 -2
  35. s2python/frbc/frbc_storage_status.py +2 -2
  36. s2python/frbc/frbc_system_description.py +4 -3
  37. s2python/frbc/frbc_timer_status.py +2 -2
  38. s2python/frbc/frbc_usage_forecast.py +3 -3
  39. s2python/frbc/frbc_usage_forecast_element.py +2 -2
  40. s2python/s2_parser.py +113 -0
  41. s2python/s2_validation_error.py +3 -1
  42. s2python/utils.py +7 -2
  43. s2python/validate_values_mixin.py +38 -17
  44. s2_python-0.0.1.dist-info/RECORD +0 -49
  45. {s2_python-0.0.1.dist-info → s2_python-0.1.0.dist-info}/entry_points.txt +0 -0
  46. {s2_python-0.0.1.dist-info → s2_python-0.1.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: s2-python
3
- Version: 0.0.1
3
+ Version: 0.1.0
4
4
  Summary: S2 Protocol Python Wrapper
5
5
  Home-page: https://github.com/flexiblepower/s2-ws-json-python
6
6
  Author: Flexiblepower
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Description-Content-Type: text/x-rst; charset=UTF-8
16
- Requires-Dist: pydantic <=1.10.7
16
+ Requires-Dist: pydantic ~=1.10.7
17
17
  Requires-Dist: pytz
18
18
  Requires-Dist: click
19
19
  Provides-Extra: development
@@ -33,13 +33,14 @@ Requires-Dist: pytest ; extra == 'testing'
33
33
  Requires-Dist: pytest-coverage ; extra == 'testing'
34
34
  Requires-Dist: pytest-timer ; extra == 'testing'
35
35
  Requires-Dist: mypy ; extra == 'testing'
36
+ Requires-Dist: types-pytz ; extra == 'testing'
36
37
  Requires-Dist: pylint ; extra == 'testing'
37
38
 
38
39
  Python Wrapper for S2 Flexibility Protocol
39
40
  ===========================================
40
41
  .. image:: https://img.shields.io/pypi/v/s2-python
41
42
  :alt: PyPI - Version
42
- .. image:: https://img.shields.io/pypi/pyversions/v/s2-python
43
+ .. image:: https://img.shields.io/pypi/pyversions/s2-python
43
44
  :alt: PyPI - Python Version
44
45
  .. image:: https://img.shields.io/pypi/l/s2-python
45
46
  :alt: PyPI - License
@@ -49,14 +50,29 @@ is based on the asyncapi description of the protocol provided in the `s2-ws-json
49
50
 
50
51
  Currently, the package supports the *common* and *FILL RATE BASED CONTROL* types and messages.
51
52
 
53
+ To Install
54
+ -----------
55
+ You can install this package using pip or any Python dependency manager that collects the packages from Pypi:
56
+
57
+ .. code-block:: bash
58
+
59
+ pip install s2-python
60
+
61
+ The packages on Pypi may be found `here <https://pypi.org/project/s2-python/>`_
52
62
 
53
63
  Example
54
64
  ---------
55
65
 
56
66
  .. code-block:: python
57
67
 
68
+ from s2python.common import PowerRange, CommodityQuantity
69
+
58
70
  # create s2 messages as Python objects
59
- number_range = PowerRange(start_of_range=4.0, end_of_range=5.0, commodity_quantity=CommodityQuantity.ELECTRIC_POWER_L1)
71
+ number_range = PowerRange(
72
+ start_of_range=4.0,
73
+ end_of_range=5.0,
74
+ commodity_quantity=CommodityQuantity.ELECTRIC_POWER_L1,
75
+ )
60
76
  # serialize s2 messages
61
77
  number_range.to_json()
62
78
  # deserialize s2 messages
@@ -0,0 +1,50 @@
1
+ s2python/__init__.py,sha256=e5lwvqsPl-z7IfEd0hRQhLBRKBYcuw2eqrecXnMfLdg,384
2
+ s2python/s2_parser.py,sha256=4oN4qBeW7RVNcXQ8GVib_oodK1_gszCrcXMQjPqRGUI,4088
3
+ s2python/s2_validation_error.py,sha256=fdV2tdRhYslDStTw8Cvb3mly7PpLiCbXbrkfOe3n2_w,295
4
+ s2python/utils.py,sha256=QX9b-mi-H_YUGTmGmJsrAbaWWM3dgaoaRLRXHHlaZDE,212
5
+ s2python/validate_values_mixin.py,sha256=RbhxqqhfhsNoTSeRxb7msQ6nEZWwAoUH6EQKkZkzH4k,4109
6
+ s2python/version.py,sha256=w9Iw7QVvd8lme2wKwEbCo5IgetVjSfFBOOYAcA_Q0Ns,18
7
+ s2python/common/__init__.py,sha256=fVJ0n13kRzzAtJcOxZHz3CEDexWXnyueJMcEXGdq_88,1345
8
+ s2python/common/duration.py,sha256=fMuftby-8wLeqpLKXd7VQqcmgTUUhTmWSW53R2b2vHM,661
9
+ s2python/common/handshake.py,sha256=IsbA41wBN4n0jopHn-vNDf2y1HP47p2kahM3NSwxAfc,440
10
+ s2python/common/handshake_response.py,sha256=Pa7kwRUFiecvPU84-z3D9_woxGqM4-_G9XtLgVmzMKA,496
11
+ s2python/common/instruction_status_update.py,sha256=w3bbVZ8jF3VBLKe3Q3fhWK9WhldxkkNgSzXuXyPzklA,694
12
+ s2python/common/number_range.py,sha256=NRND5K1-_U_6S4lVxfszI4A9o8hFYCrFQieska_ELgU,1163
13
+ s2python/common/power_forecast.py,sha256=-i1Z4JMGpmoo4N2DfmzMYiEh7o25xxsesKkUxhSWjAw,700
14
+ s2python/common/power_forecast_element.py,sha256=nQopGR0-uuE9jGyg2L-zusvkVACWqMJNxOyjm3PJYI4,787
15
+ s2python/common/power_forecast_value.py,sha256=UHiP8Go64RW6VzszzmAZ3JIyaGf2WOMGyx_PTmdmBoY,375
16
+ s2python/common/power_measurement.py,sha256=l_Na6Yw6twQveLw6j6FsX6BQh0fbmveHdHdsmnDzegs,675
17
+ s2python/common/power_range.py,sha256=Pazitj5zPzH2rz0gnKAzinwEhM28Dftglf3z6sHjIqA,796
18
+ s2python/common/power_value.py,sha256=xpu-MV_lkFYksfVDRRFbWiha8fA7KAF_p3CBoNNoyyg,327
19
+ s2python/common/reception_status.py,sha256=yd3tzG8UNMCjPy3WXpgMHjYnuFW3wyCv82tnq2kgEME,512
20
+ s2python/common/resource_manager_details.py,sha256=YOKnw7wMAX5JIt3LAnxfCvG0kTZjshHF8Yotfzyqbew,1050
21
+ s2python/common/revoke_object.py,sha256=EWuTqHoVyaPO-eIcFSCE7zROJXyR6RWkajX60IL8aM8,567
22
+ s2python/common/role.py,sha256=PV0noDy3D30dIodJgIWfGWcz-4hwZyBnpsWkyjc8c7g,291
23
+ s2python/common/select_control_type.py,sha256=y6yXcuCGPsm-QKSg6jJTzfUzAbZqvMkWepMhXERVQlM,496
24
+ s2python/common/session_request.py,sha256=bCnyAjSdeLShAC513x7ifaNRKZLAdzoJF2rU_zXxZEI,475
25
+ s2python/common/support.py,sha256=Kbrf_KGB45Wfr8j2pqDe1lLde6CIr3nl_LYkWnilmV0,1015
26
+ s2python/common/timer.py,sha256=-KhxxyJwK0pfVrpLBg9thYzOb2-LB5FgRBznwKCu76Y,538
27
+ s2python/common/transition.py,sha256=pYfo4sfWdnBR2Tvn3cW34zVT53d4PZSc3Ee3mubAYAg,1096
28
+ s2python/frbc/__init__.py,sha256=ROV3qZoldPkdgVFfMQr5Mf3GDfBzXaMfhNNCuXY6T0s,1104
29
+ s2python/frbc/frbc_actuator_description.py,sha256=9Zzzjhii3vABsLPOIN6qGavs0YyWthNV7whjcJadyGY,6801
30
+ s2python/frbc/frbc_actuator_status.py,sha256=xnTqGyh_h03BsBWRQfV7SMHn1yZV2KLV02FpIgojzr4,973
31
+ s2python/frbc/frbc_fill_level_target_profile.py,sha256=0kGB-uD0mqOZPdRBn_EJ8rMrA6DfTHMk7_nsSm7icRA,896
32
+ s2python/frbc/frbc_fill_level_target_profile_element.py,sha256=fqYwvbfEAM-e_hbUB6MN9NjGoCCdBcd-VGtmB9UyLbc,832
33
+ s2python/frbc/frbc_instruction.py,sha256=y8hZqh1jDKWVwehweWHQnbkFiSHlY87T4Al5kxEH9rQ,809
34
+ s2python/frbc/frbc_leakage_behaviour.py,sha256=CAuIAgaBDv82rPHmjJYchuVQilKLLjIOfI71UDnKXYk,776
35
+ s2python/frbc/frbc_leakage_behaviour_element.py,sha256=Bvw_4zuS5PgoZu_Pwy9kFCst75JbQyGKSvxOU7owYCA,636
36
+ s2python/frbc/frbc_operation_mode.py,sha256=bkMxQMP1pxF4rb0plUzBnFv7IG261lDq3FDf0HId0uE,2030
37
+ s2python/frbc/frbc_operation_mode_element.py,sha256=Bc5pJDwd83SbJiFYT2GA6sTzeLtsa8ohAM8LuY3uRc4,1079
38
+ s2python/frbc/frbc_storage_description.py,sha256=bxFVxYPhNCejMjBJtIiGjCCS7fyoOXHc3TUeWho7CYM,601
39
+ s2python/frbc/frbc_storage_status.py,sha256=PZCs-fh43in7LlYjalygW57iRQWwBp51tVEoZemS9-Y,496
40
+ s2python/frbc/frbc_system_description.py,sha256=RefubMI735vDV-cp6FEAEDe4bfcEYwY_EhCu5rC1oSs,991
41
+ s2python/frbc/frbc_timer_status.py,sha256=tyhjNia_I2j6rSggFiXp8i6QcZRiqhcOkMtn_TkXQ0Q,702
42
+ s2python/frbc/frbc_usage_forecast.py,sha256=8zWLBYpRfjiPKRz5x0QiZ2IF8HDGA8qlUgbOofAsQPA,743
43
+ s2python/frbc/frbc_usage_forecast_element.py,sha256=OuwrN4KarVe4tq6R4qkNz5j-sQstQDzLWP4g5B6L-iw,580
44
+ s2python/generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ s2python/generated/gen_s2.py,sha256=DXLCNE1gUS7XnRTgWlBpy73szr5HrHcrN0TZXvq8N_g,61928
46
+ s2_python-0.1.0.dist-info/METADATA,sha256=-V9F_j0oh6IkAVzrcX5oFFyY7bON8FwRwc7NUsLjIM4,3257
47
+ s2_python-0.1.0.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
48
+ s2_python-0.1.0.dist-info/entry_points.txt,sha256=feX-xmgJZgSe5-jxMgFKPKCJz4Ys3eQcGrsXsirNZyM,61
49
+ s2_python-0.1.0.dist-info/top_level.txt,sha256=OLFq0oDhr77Mp-EYLEcWk5P3jvooOt4IHkTI5KYJMc8,9
50
+ s2_python-0.1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.41.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
s2python/__init__.py CHANGED
@@ -2,7 +2,7 @@ from importlib.metadata import PackageNotFoundError, version # pragma: no cover
2
2
 
3
3
  try:
4
4
  # Change here if project is renamed and does not equal the package name
5
- dist_name = "s2-python"
5
+ dist_name = "s2-python" # pylint: disable=invalid-name
6
6
  __version__ = version(dist_name)
7
7
  except PackageNotFoundError: # pragma: no cover
8
8
  __version__ = "unknown"
@@ -4,12 +4,12 @@ import math
4
4
  from s2python.generated.gen_s2 import Duration as GenDuration
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
9
 
10
10
 
11
11
  @catch_and_convert_exceptions
12
- class Duration(GenDuration, ValidateValuesMixin["Duration"]):
12
+ class Duration(GenDuration, S2Message["Duration"]):
13
13
  def to_timedelta(self) -> timedelta:
14
14
  return timedelta(milliseconds=self.__root__)
15
15
 
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import Handshake as GenHandshake
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class Handshake(GenHandshake, ValidateValuesMixin["Handshake"]):
11
+ class Handshake(GenHandshake, S2Message["Handshake"]):
12
12
  class Config(GenHandshake.Config):
13
13
  validate_assignment = True
14
14
 
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import HandshakeResponse as GenHandshakeResponse
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class HandshakeResponse(GenHandshakeResponse, ValidateValuesMixin["HandshakeResponse"]):
11
+ class HandshakeResponse(GenHandshakeResponse, S2Message["HandshakeResponse"]):
12
12
  class Config(GenHandshakeResponse.Config):
13
13
  validate_assignment = True
14
14
 
@@ -5,13 +5,13 @@ from s2python.generated.gen_s2 import (
5
5
  )
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
13
  class InstructionStatusUpdate(
14
- GenInstructionStatusUpdate, ValidateValuesMixin["InstructionStatusUpdate"]
14
+ GenInstructionStatusUpdate, S2Message["InstructionStatusUpdate"]
15
15
  ):
16
16
  class Config(GenInstructionStatusUpdate.Config):
17
17
  validate_assignment = True
@@ -3,19 +3,22 @@ from typing import Any, Dict
3
3
  from pydantic import root_validator
4
4
 
5
5
  from s2python.validate_values_mixin import (
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  catch_and_convert_exceptions,
8
8
  )
9
9
  from s2python.generated.gen_s2 import NumberRange as GenNumberRange
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class NumberRange(GenNumberRange, ValidateValuesMixin["NumberRange"]):
13
+ class NumberRange(GenNumberRange, S2Message["NumberRange"]):
14
14
  class Config(GenNumberRange.Config):
15
15
  validate_assignment = True
16
16
 
17
17
  @root_validator(pre=False)
18
- def validate_start_end_order(cls, values: Dict[str, Any]) -> Dict[str, Any]:
18
+ @classmethod
19
+ def validate_start_end_order( # pylint: disable=duplicate-code
20
+ cls, values: Dict[str, Any]
21
+ ) -> Dict[str, Any]:
19
22
  if values.get("start_of_range", 0.0) > values.get("end_of_range", 0.0):
20
23
  raise ValueError(
21
24
  cls, "start_of_range should not be higher than end_of_range"
@@ -23,14 +26,14 @@ class NumberRange(GenNumberRange, ValidateValuesMixin["NumberRange"]):
23
26
 
24
27
  return values
25
28
 
26
- def __hash__(self):
29
+ def __hash__(self) -> int:
27
30
  return hash(f"{self.start_of_range}|{self.end_of_range}")
28
31
 
29
- def __eq__(self, other):
32
+ def __eq__(self, other: Any) -> bool:
30
33
  if isinstance(other, NumberRange):
31
34
  return (
32
35
  self.start_of_range == other.start_of_range
33
36
  and self.end_of_range == other.end_of_range
34
37
  )
35
- else:
36
- return False
38
+
39
+ return False
@@ -1,16 +1,16 @@
1
1
  from typing import List
2
2
  import uuid
3
3
 
4
- from s2python.common import PowerForecastElement
4
+ from s2python.common.power_forecast_element import PowerForecastElement
5
5
  from s2python.generated.gen_s2 import PowerForecast as GenPowerForecast
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class PowerForecast(GenPowerForecast, ValidateValuesMixin["PowerForecast"]):
13
+ class PowerForecast(GenPowerForecast, S2Message["PowerForecast"]):
14
14
  class Config(GenPowerForecast.Config):
15
15
  validate_assignment = True
16
16
 
@@ -3,15 +3,14 @@ from typing import List
3
3
  from s2python.generated.gen_s2 import PowerForecastElement as GenPowerForecastElement
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
- from s2python.common import Duration, PowerForecastValue
8
+ from s2python.common.duration import Duration
9
+ from s2python.common.power_forecast_value import PowerForecastValue
9
10
 
10
11
 
11
12
  @catch_and_convert_exceptions
12
- class PowerForecastElement(
13
- GenPowerForecastElement, ValidateValuesMixin["PowerForecastElement"]
14
- ):
13
+ class PowerForecastElement(GenPowerForecastElement, S2Message["PowerForecastElement"]):
15
14
  class Config(GenPowerForecastElement.Config):
16
15
  validate_assignment = True
17
16
 
@@ -1,13 +1,11 @@
1
1
  from s2python.generated.gen_s2 import PowerForecastValue as GenPowerForecastValue
2
2
  from s2python.validate_values_mixin import (
3
3
  catch_and_convert_exceptions,
4
- ValidateValuesMixin,
4
+ S2Message,
5
5
  )
6
6
 
7
7
 
8
8
  @catch_and_convert_exceptions
9
- class PowerForecastValue(
10
- GenPowerForecastValue, ValidateValuesMixin["PowerForecastValue"]
11
- ):
9
+ class PowerForecastValue(GenPowerForecastValue, S2Message["PowerForecastValue"]):
12
10
  class Config(GenPowerForecastValue.Config):
13
11
  validate_assignment = True
@@ -1,16 +1,16 @@
1
1
  from typing import List
2
2
  import uuid
3
3
 
4
- from s2python.common import PowerValue
4
+ from s2python.common.power_value import PowerValue
5
5
  from s2python.generated.gen_s2 import PowerMeasurement as GenPowerMeasurement
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class PowerMeasurement(GenPowerMeasurement, ValidateValuesMixin["PowerMeasurement"]):
13
+ class PowerMeasurement(GenPowerMeasurement, S2Message["PowerMeasurement"]):
14
14
  class Config(GenPowerMeasurement.Config):
15
15
  validate_assignment = True
16
16
 
@@ -4,18 +4,21 @@ from pydantic import root_validator
4
4
 
5
5
  from s2python.generated.gen_s2 import PowerRange as GenPowerRange
6
6
  from s2python.validate_values_mixin import (
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  catch_and_convert_exceptions,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class PowerRange(GenPowerRange, ValidateValuesMixin["PowerRange"]):
13
+ class PowerRange(GenPowerRange, S2Message["PowerRange"]):
14
14
  class Config(GenPowerRange.Config):
15
15
  validate_assignment = True
16
16
 
17
17
  @root_validator(pre=False)
18
- def validate_start_end_order(cls, values: Dict[str, Any]) -> Dict[str, Any]:
18
+ @classmethod
19
+ def validate_start_end_order(
20
+ cls, values: Dict[str, Any]
21
+ ) -> Dict[str, Any]: # pylint: disable=duplicate-code
19
22
  if values.get("start_of_range", 0.0) > values.get("end_of_range", 0.0):
20
23
  raise ValueError(
21
24
  cls, "start_of_range should not be higher than end_of_range"
@@ -1,11 +1,11 @@
1
1
  from s2python.generated.gen_s2 import PowerValue as GenPowerValue
2
2
  from s2python.validate_values_mixin import (
3
3
  catch_and_convert_exceptions,
4
- ValidateValuesMixin,
4
+ S2Message,
5
5
  )
6
6
 
7
7
 
8
8
  @catch_and_convert_exceptions
9
- class PowerValue(GenPowerValue, ValidateValuesMixin["PowerValue"]):
9
+ class PowerValue(GenPowerValue, S2Message["PowerValue"]):
10
10
  class Config(GenPowerValue.Config):
11
11
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import ReceptionStatus as GenReceptionStatus
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class ReceptionStatus(GenReceptionStatus, ValidateValuesMixin["ReceptionStatus"]):
11
+ class ReceptionStatus(GenReceptionStatus, S2Message["ReceptionStatus"]):
12
12
  class Config(GenReceptionStatus.Config):
13
13
  validate_assignment = True
14
14
 
@@ -1,19 +1,20 @@
1
1
  from typing import List
2
2
  import uuid
3
3
 
4
- from s2python.common import Duration, Role
4
+ from s2python.common.duration import Duration
5
+ from s2python.common.role import Role
5
6
  from s2python.generated.gen_s2 import (
6
7
  ResourceManagerDetails as GenResourceManagerDetails,
7
8
  )
8
9
  from s2python.validate_values_mixin import (
9
10
  catch_and_convert_exceptions,
10
- ValidateValuesMixin,
11
+ S2Message,
11
12
  )
12
13
 
13
14
 
14
15
  @catch_and_convert_exceptions
15
16
  class ResourceManagerDetails(
16
- GenResourceManagerDetails, ValidateValuesMixin["ResourceManagerDetails"]
17
+ GenResourceManagerDetails, S2Message["ResourceManagerDetails"]
17
18
  ):
18
19
  class Config(GenResourceManagerDetails.Config):
19
20
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import RevokeObject as GenRevokeObject
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class RevokeObject(GenRevokeObject, ValidateValuesMixin["RevokeObject"]):
11
+ class RevokeObject(GenRevokeObject, S2Message["RevokeObject"]):
12
12
  class Config(GenRevokeObject.Config):
13
13
  validate_assignment = True
14
14
 
s2python/common/role.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from s2python.generated.gen_s2 import Role as GenRole
2
2
  from s2python.validate_values_mixin import (
3
- ValidateValuesMixin,
3
+ S2Message,
4
4
  catch_and_convert_exceptions,
5
5
  )
6
6
 
7
7
 
8
8
  @catch_and_convert_exceptions
9
- class Role(GenRole, ValidateValuesMixin["Role"]):
9
+ class Role(GenRole, S2Message["Role"]):
10
10
  class Config(GenRole.Config):
11
11
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import SelectControlType as GenSelectControlType
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class SelectControlType(GenSelectControlType, ValidateValuesMixin["SelectControlType"]):
11
+ class SelectControlType(GenSelectControlType, S2Message["SelectControlType"]):
12
12
  class Config(GenSelectControlType.Config):
13
13
  validate_assignment = True
14
14
 
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import SessionRequest as GenSessionRequest
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class SessionRequest(GenSessionRequest, ValidateValuesMixin["SessionRequest"]):
11
+ class SessionRequest(GenSessionRequest, S2Message["SessionRequest"]):
12
12
  class Config(GenSessionRequest.Config):
13
13
  validate_assignment = True
14
14
 
@@ -3,23 +3,25 @@ from s2python.common import CommodityQuantity, Commodity
3
3
 
4
4
  def commodity_has_quantity(commodity: "Commodity", quantity: CommodityQuantity) -> bool:
5
5
  if commodity == Commodity.HEAT:
6
- return quantity in [
6
+ result = quantity in [
7
7
  CommodityQuantity.HEAT_THERMAL_POWER,
8
8
  CommodityQuantity.HEAT_TEMPERATURE,
9
9
  CommodityQuantity.HEAT_FLOW_RATE,
10
10
  ]
11
11
  elif commodity == Commodity.ELECTRICITY:
12
- return quantity in [
12
+ result = quantity in [
13
13
  CommodityQuantity.ELECTRIC_POWER_3_PHASE_SYMMETRIC,
14
14
  CommodityQuantity.ELECTRIC_POWER_L1,
15
15
  CommodityQuantity.ELECTRIC_POWER_L2,
16
16
  CommodityQuantity.ELECTRIC_POWER_L3,
17
17
  ]
18
18
  elif commodity == Commodity.GAS:
19
- return quantity in [CommodityQuantity.NATURAL_GAS_FLOW_RATE]
19
+ result = quantity in [CommodityQuantity.NATURAL_GAS_FLOW_RATE]
20
20
  elif commodity == Commodity.OIL:
21
- return quantity in [CommodityQuantity.OIL_FLOW_RATE]
21
+ result = quantity in [CommodityQuantity.OIL_FLOW_RATE]
22
22
  else:
23
23
  raise RuntimeError(
24
24
  f"Unsupported commodity {commodity}. Missing implementation."
25
25
  )
26
+
27
+ return result
s2python/common/timer.py CHANGED
@@ -3,13 +3,13 @@ import uuid
3
3
  from s2python.common.duration import Duration
4
4
  from s2python.generated.gen_s2 import Timer as GenTimer
5
5
  from s2python.validate_values_mixin import (
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  catch_and_convert_exceptions,
8
8
  )
9
9
 
10
10
 
11
11
  @catch_and_convert_exceptions
12
- class Timer(GenTimer, ValidateValuesMixin["Timer"]):
12
+ class Timer(GenTimer, S2Message["Timer"]):
13
13
  class Config(GenTimer.Config):
14
14
  validate_assignment = True
15
15
 
@@ -1,16 +1,16 @@
1
1
  import uuid
2
2
  from typing import Optional, List
3
3
 
4
- from s2python.common import Duration
4
+ from s2python.common.duration import Duration
5
5
  from s2python.generated.gen_s2 import Transition as GenTransition
6
6
  from s2python.validate_values_mixin import (
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  catch_and_convert_exceptions,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class Transition(GenTransition, ValidateValuesMixin["Transition"]):
13
+ class Transition(GenTransition, S2Message["Transition"]):
14
14
  class Config(GenTransition.Config):
15
15
  validate_assignment = True
16
16
 
@@ -6,20 +6,20 @@ from pydantic import root_validator
6
6
 
7
7
  from s2python.common import Transition, Timer, Commodity
8
8
  from s2python.common.support import commodity_has_quantity
9
- from s2python.frbc import FRBCOperationMode
9
+ from s2python.frbc.frbc_operation_mode import FRBCOperationMode
10
10
  from s2python.generated.gen_s2 import (
11
11
  FRBCActuatorDescription as GenFRBCActuatorDescription,
12
12
  CommodityQuantity,
13
13
  )
14
14
  from s2python.validate_values_mixin import (
15
- ValidateValuesMixin,
15
+ S2Message,
16
16
  catch_and_convert_exceptions,
17
17
  )
18
18
 
19
19
 
20
20
  @catch_and_convert_exceptions
21
21
  class FRBCActuatorDescription(
22
- GenFRBCActuatorDescription, ValidateValuesMixin["FRBCActuatorDescription"]
22
+ GenFRBCActuatorDescription, S2Message["FRBCActuatorDescription"]
23
23
  ):
24
24
  class Config(GenFRBCActuatorDescription.Config):
25
25
  validate_assignment = True
@@ -37,6 +37,7 @@ class FRBCActuatorDescription(
37
37
  ].field_info # type: ignore[assignment]
38
38
 
39
39
  @root_validator(pre=False)
40
+ @classmethod
40
41
  def validate_timers_in_transitions(cls, values: Dict[str, Any]) -> Dict[str, Any]:
41
42
  timers_by_id = {timer.id: timer for timer in values.get("timers", {})}
42
43
  transition: Transition
@@ -60,6 +61,7 @@ class FRBCActuatorDescription(
60
61
  return values
61
62
 
62
63
  @root_validator(pre=False)
64
+ @classmethod
63
65
  def validate_timers_unique_ids(cls, values: Dict[str, Any]) -> Dict[str, Any]:
64
66
  ids = []
65
67
  timer: Timer
@@ -73,6 +75,7 @@ class FRBCActuatorDescription(
73
75
  return values
74
76
 
75
77
  @root_validator(pre=False)
78
+ @classmethod
76
79
  def validate_operation_modes_in_transitions(
77
80
  cls, values: Dict[str, Any]
78
81
  ) -> Dict[str, Any]:
@@ -99,6 +102,7 @@ class FRBCActuatorDescription(
99
102
  return values
100
103
 
101
104
  @root_validator(pre=False)
105
+ @classmethod
102
106
  def validate_operation_modes_unique_ids(
103
107
  cls, values: Dict[str, Any]
104
108
  ) -> Dict[str, Any]:
@@ -115,6 +119,7 @@ class FRBCActuatorDescription(
115
119
  return values
116
120
 
117
121
  @root_validator(pre=False)
122
+ @classmethod
118
123
  def validate_operation_mode_elements_have_all_supported_commodities(
119
124
  cls, values: Dict[str, Any]
120
125
  ) -> Dict[str, Any]:
@@ -148,10 +153,11 @@ class FRBCActuatorDescription(
148
153
  return values
149
154
 
150
155
  @root_validator(pre=False)
156
+ @classmethod
151
157
  def validate_unique_supported_commodities(
152
158
  cls, values: Dict[str, Any]
153
159
  ) -> Dict[str, Any]:
154
- supported_commodities: list[CommodityQuantity] = values.get(
160
+ supported_commodities: List[CommodityQuantity] = values.get(
155
161
  "supported_commodities", []
156
162
  )
157
163
 
@@ -4,14 +4,12 @@ import uuid
4
4
  from s2python.generated.gen_s2 import FRBCActuatorStatus as GenFRBCActuatorStatus
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
9
 
10
10
 
11
11
  @catch_and_convert_exceptions
12
- class FRBCActuatorStatus(
13
- GenFRBCActuatorStatus, ValidateValuesMixin["FRBCActuatorStatus"]
14
- ):
12
+ class FRBCActuatorStatus(GenFRBCActuatorStatus, S2Message["FRBCActuatorStatus"]):
15
13
  class Config(GenFRBCActuatorStatus.Config):
16
14
  validate_assignment = True
17
15
 
@@ -1,19 +1,21 @@
1
1
  from typing import List
2
2
  import uuid
3
3
 
4
- from s2python.frbc import FRBCFillLevelTargetProfileElement
4
+ from s2python.frbc.frbc_fill_level_target_profile_element import (
5
+ FRBCFillLevelTargetProfileElement,
6
+ )
5
7
  from s2python.generated.gen_s2 import (
6
8
  FRBCFillLevelTargetProfile as GenFRBCFillLevelTargetProfile,
7
9
  )
8
10
  from s2python.validate_values_mixin import (
9
11
  catch_and_convert_exceptions,
10
- ValidateValuesMixin,
12
+ S2Message,
11
13
  )
12
14
 
13
15
 
14
16
  @catch_and_convert_exceptions
15
17
  class FRBCFillLevelTargetProfile(
16
- GenFRBCFillLevelTargetProfile, ValidateValuesMixin["FRBCFillLevelTargetProfile"]
18
+ GenFRBCFillLevelTargetProfile, S2Message["FRBCFillLevelTargetProfile"]
17
19
  ):
18
20
  class Config(GenFRBCFillLevelTargetProfile.Config):
19
21
  validate_assignment = True
@@ -5,14 +5,14 @@ from s2python.generated.gen_s2 import (
5
5
  )
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
13
  class FRBCFillLevelTargetProfileElement(
14
14
  GenFRBCFillLevelTargetProfileElement,
15
- ValidateValuesMixin["FRBCFillLevelTargetProfileElement"],
15
+ S2Message["FRBCFillLevelTargetProfileElement"],
16
16
  ):
17
17
  class Config(GenFRBCFillLevelTargetProfileElement.Config):
18
18
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import FRBCInstruction as GenFRBCInstruction
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class FRBCInstruction(GenFRBCInstruction, ValidateValuesMixin["FRBCInstruction"]):
11
+ class FRBCInstruction(GenFRBCInstruction, S2Message["FRBCInstruction"]):
12
12
  class Config(GenFRBCInstruction.Config):
13
13
  validate_assignment = True
14
14
 
@@ -1,18 +1,16 @@
1
1
  from typing import List
2
2
  import uuid
3
3
 
4
- from s2python.frbc import FRBCLeakageBehaviourElement
4
+ from s2python.frbc.frbc_leakage_behaviour_element import FRBCLeakageBehaviourElement
5
5
  from s2python.generated.gen_s2 import FRBCLeakageBehaviour as GenFRBCLeakageBehaviour
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class FRBCLeakageBehaviour(
14
- GenFRBCLeakageBehaviour, ValidateValuesMixin["FRBCLeakageBehaviour"]
15
- ):
13
+ class FRBCLeakageBehaviour(GenFRBCLeakageBehaviour, S2Message["FRBCLeakageBehaviour"]):
16
14
  class Config(GenFRBCLeakageBehaviour.Config):
17
15
  validate_assignment = True
18
16
 
@@ -4,13 +4,13 @@ from s2python.generated.gen_s2 import (
4
4
  )
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
9
 
10
10
 
11
11
  @catch_and_convert_exceptions
12
12
  class FRBCLeakageBehaviourElement(
13
- GenFRBCLeakageBehaviourElement, ValidateValuesMixin["FRBCLeakageBehaviourElement"]
13
+ GenFRBCLeakageBehaviourElement, S2Message["FRBCLeakageBehaviourElement"]
14
14
  ):
15
15
  class Config(GenFRBCLeakageBehaviourElement.Config):
16
16
  validate_assignment = True
@@ -1,21 +1,21 @@
1
1
  # from itertools import pairwise
2
2
  import uuid
3
- from typing import List, Dict, Any, Generator, Tuple
3
+ from typing import List, Dict, Any
4
4
 
5
5
  from pydantic import root_validator
6
6
 
7
7
  from s2python.common import NumberRange
8
- from s2python.frbc import FRBCOperationModeElement
8
+ from s2python.frbc.frbc_operation_mode_element import FRBCOperationModeElement
9
9
  from s2python.generated.gen_s2 import FRBCOperationMode as GenFRBCOperationMode
10
10
  from s2python.validate_values_mixin import (
11
- ValidateValuesMixin,
11
+ S2Message,
12
12
  catch_and_convert_exceptions,
13
13
  )
14
14
  from s2python.utils import pairwise
15
15
 
16
16
 
17
17
  @catch_and_convert_exceptions
18
- class FRBCOperationMode(GenFRBCOperationMode, ValidateValuesMixin["FRBCOperationMode"]):
18
+ class FRBCOperationMode(GenFRBCOperationMode, S2Message["FRBCOperationMode"]):
19
19
  class Config(GenFRBCOperationMode.Config):
20
20
  validate_assignment = True
21
21
 
@@ -25,6 +25,7 @@ class FRBCOperationMode(GenFRBCOperationMode, ValidateValuesMixin["FRBCOperation
25
25
  ].field_info # type: ignore[assignment]
26
26
 
27
27
  @root_validator(pre=False)
28
+ @classmethod
28
29
  def validate_contiguous_fill_levels_operation_mode_elements(
29
30
  cls, values: Dict[str, Any]
30
31
  ) -> Dict[str, Any]:
@@ -5,14 +5,14 @@ from s2python.generated.gen_s2 import (
5
5
  FRBCOperationModeElement as GenFRBCOperationModeElement,
6
6
  )
7
7
  from s2python.validate_values_mixin import (
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  catch_and_convert_exceptions,
10
10
  )
11
11
 
12
12
 
13
13
  @catch_and_convert_exceptions
14
14
  class FRBCOperationModeElement(
15
- GenFRBCOperationModeElement, ValidateValuesMixin["FRBCOperationModeElement"]
15
+ GenFRBCOperationModeElement, S2Message["FRBCOperationModeElement"]
16
16
  ):
17
17
  class Config(GenFRBCOperationModeElement.Config):
18
18
  validate_assignment = True
@@ -4,13 +4,13 @@ from s2python.generated.gen_s2 import (
4
4
  )
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
9
 
10
10
 
11
11
  @catch_and_convert_exceptions
12
12
  class FRBCStorageDescription(
13
- GenFRBCStorageDescription, ValidateValuesMixin["FRBCStorageDescription"]
13
+ GenFRBCStorageDescription, S2Message["FRBCStorageDescription"]
14
14
  ):
15
15
  class Config(GenFRBCStorageDescription.Config):
16
16
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import FRBCStorageStatus as GenFRBCStorageStatus
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class FRBCStorageStatus(GenFRBCStorageStatus, ValidateValuesMixin["FRBCStorageStatus"]):
11
+ class FRBCStorageStatus(GenFRBCStorageStatus, S2Message["FRBCStorageStatus"]):
12
12
  class Config(GenFRBCStorageStatus.Config):
13
13
  validate_assignment = True
14
14
 
@@ -4,14 +4,15 @@ import uuid
4
4
  from s2python.generated.gen_s2 import FRBCSystemDescription as GenFRBCSystemDescription
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
- from s2python.frbc import FRBCActuatorDescription, FRBCStorageDescription
9
+ from s2python.frbc.frbc_actuator_description import FRBCActuatorDescription
10
+ from s2python.frbc.frbc_storage_description import FRBCStorageDescription
10
11
 
11
12
 
12
13
  @catch_and_convert_exceptions
13
14
  class FRBCSystemDescription(
14
- GenFRBCSystemDescription, ValidateValuesMixin["FRBCSystemDescription"]
15
+ GenFRBCSystemDescription, S2Message["FRBCSystemDescription"]
15
16
  ):
16
17
  class Config(GenFRBCSystemDescription.Config):
17
18
  validate_assignment = True
@@ -3,12 +3,12 @@ import uuid
3
3
  from s2python.generated.gen_s2 import FRBCTimerStatus as GenFRBCTimerStatus
4
4
  from s2python.validate_values_mixin import (
5
5
  catch_and_convert_exceptions,
6
- ValidateValuesMixin,
6
+ S2Message,
7
7
  )
8
8
 
9
9
 
10
10
  @catch_and_convert_exceptions
11
- class FRBCTimerStatus(GenFRBCTimerStatus, ValidateValuesMixin["FRBCTimerStatus"]):
11
+ class FRBCTimerStatus(GenFRBCTimerStatus, S2Message["FRBCTimerStatus"]):
12
12
  class Config(GenFRBCTimerStatus.Config):
13
13
  validate_assignment = True
14
14
 
@@ -4,13 +4,13 @@ import uuid
4
4
  from s2python.generated.gen_s2 import FRBCUsageForecast as GenFRBCUsageForecast
5
5
  from s2python.validate_values_mixin import (
6
6
  catch_and_convert_exceptions,
7
- ValidateValuesMixin,
7
+ S2Message,
8
8
  )
9
- from s2python.frbc import FRBCUsageForecastElement
9
+ from s2python.frbc.frbc_usage_forecast_element import FRBCUsageForecastElement
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
- class FRBCUsageForecast(GenFRBCUsageForecast, ValidateValuesMixin["FRBCUsageForecast"]):
13
+ class FRBCUsageForecast(GenFRBCUsageForecast, S2Message["FRBCUsageForecast"]):
14
14
  class Config(GenFRBCUsageForecast.Config):
15
15
  validate_assignment = True
16
16
 
@@ -5,13 +5,13 @@ from s2python.generated.gen_s2 import (
5
5
  )
6
6
  from s2python.validate_values_mixin import (
7
7
  catch_and_convert_exceptions,
8
- ValidateValuesMixin,
8
+ S2Message,
9
9
  )
10
10
 
11
11
 
12
12
  @catch_and_convert_exceptions
13
13
  class FRBCUsageForecastElement(
14
- GenFRBCUsageForecastElement, ValidateValuesMixin["FRBCUsageForecastElement"]
14
+ GenFRBCUsageForecastElement, S2Message["FRBCUsageForecastElement"]
15
15
  ):
16
16
  class Config(GenFRBCUsageForecastElement.Config):
17
17
  validate_assignment = True
s2python/s2_parser.py ADDED
@@ -0,0 +1,113 @@
1
+ import json
2
+ import logging
3
+ from typing import Optional, TypeVar, Union, Type, Dict
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.validate_values_mixin import S2Message
28
+ from s2python.s2_validation_error import S2ValidationError
29
+
30
+
31
+ LOGGER = logging.getLogger(__name__)
32
+ S2MessageType = str
33
+
34
+ M = TypeVar("M", bound=S2Message)
35
+
36
+
37
+ # May be generated with development_utilities/generate_s2_message_type_to_class.py
38
+ TYPE_TO_MESSAGE_CLASS: Dict[str, Type[S2Message]] = {
39
+ "FRBC.ActuatorStatus": FRBCActuatorStatus,
40
+ "FRBC.FillLevelTargetProfile": FRBCFillLevelTargetProfile,
41
+ "FRBC.Instruction": FRBCInstruction,
42
+ "FRBC.LeakageBehaviour": FRBCLeakageBehaviour,
43
+ "FRBC.StorageStatus": FRBCStorageStatus,
44
+ "FRBC.SystemDescription": FRBCSystemDescription,
45
+ "FRBC.TimerStatus": FRBCTimerStatus,
46
+ "FRBC.UsageForecast": FRBCUsageForecast,
47
+ "Handshake": Handshake,
48
+ "HandshakeResponse": HandshakeResponse,
49
+ "InstructionStatusUpdate": InstructionStatusUpdate,
50
+ "PowerForecast": PowerForecast,
51
+ "PowerMeasurement": PowerMeasurement,
52
+ "ReceptionStatus": ReceptionStatus,
53
+ "ResourceManagerDetails": ResourceManagerDetails,
54
+ "RevokeObject": RevokeObject,
55
+ "SelectControlType": SelectControlType,
56
+ "SessionRequest": SessionRequest,
57
+ }
58
+
59
+
60
+ class S2Parser:
61
+ @staticmethod
62
+ def _parse_json_if_required(unparsed_message: Union[dict, str]) -> dict:
63
+ if isinstance(unparsed_message, str):
64
+ return json.loads(unparsed_message)
65
+ return unparsed_message
66
+
67
+ @staticmethod
68
+ def parse_as_any_message(unparsed_message: Union[dict, str]) -> S2Message:
69
+ """Parse the message as any S2 python message regardless of message type.
70
+
71
+ :param unparsed_message: The message as a JSON-formatted string or as a json-parsed dictionary.
72
+ :raises: S2ValidationError, json.JSONDecodeError
73
+ :return: The parsed S2 message if no errors were found.
74
+ """
75
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
76
+ message_type = S2Parser.parse_message_type(message_json)
77
+
78
+ if message_type not in TYPE_TO_MESSAGE_CLASS:
79
+ raise S2ValidationError(
80
+ message_json,
81
+ f"Unable to parse {message_type} as an S2 message. Type unknown.",
82
+ )
83
+
84
+ return TYPE_TO_MESSAGE_CLASS[message_type].parse_obj(message_json)
85
+
86
+ @staticmethod
87
+ def parse_as_message(unparsed_message: Union[dict, str], as_message: Type[M]) -> M:
88
+ """Parse the message to a specific S2 python message.
89
+
90
+ :param unparsed_message: The message as a JSON-formatted string or as a JSON-parsed dictionary.
91
+ :param as_message: The type of message that is expected within the `message`
92
+ :raises: S2ValidationError, json.JSONDecodeError
93
+ :return: The parsed S2 message if no errors were found.
94
+ """
95
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
96
+ return as_message.from_dict(message_json)
97
+
98
+ @staticmethod
99
+ def parse_message_type(
100
+ unparsed_message: Union[dict, str]
101
+ ) -> Optional[S2MessageType]:
102
+ """Parse only the message type from the unparsed message.
103
+
104
+ This is useful to call before `parse_as_message` to retrieve the message type and allows for strictly-typed
105
+ parsing.
106
+
107
+ :param unparsed_message: The message as a JSON-formatted string or as a JSON-parsed dictionary.
108
+ :raises: json.JSONDecodeError
109
+ :return: The parsed S2 message type if no errors were found.
110
+ """
111
+ message_json = S2Parser._parse_json_if_required(unparsed_message)
112
+
113
+ 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, Tuple, List, 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]
@@ -3,10 +3,8 @@ from typing import (
3
3
  Generic,
4
4
  Protocol,
5
5
  Type,
6
- Tuple,
7
6
  Optional,
8
7
  Callable,
9
- cast,
10
8
  Any,
11
9
  Union,
12
10
  AbstractSet,
@@ -15,31 +13,41 @@ from typing import (
15
13
  Dict,
16
14
  )
17
15
 
18
- from pydantic import BaseModel, StrBytes, Protocol as PydanticProtocol, ValidationError
16
+ from pydantic import ( # pylint: disable=no-name-in-module
17
+ BaseModel,
18
+ StrBytes,
19
+ Protocol as PydanticProtocol,
20
+ ValidationError,
21
+ )
22
+ from pydantic.error_wrappers import display_errors # pylint: disable=no-name-in-module
19
23
 
20
24
  from s2python.s2_validation_error import S2ValidationError
21
25
 
22
- B = TypeVar("B", bound=BaseModel, covariant=True)
26
+ B_co = TypeVar("B_co", bound=BaseModel, covariant=True)
23
27
 
24
28
  IntStr = Union[int, str]
25
29
  AbstractSetIntStr = AbstractSet[IntStr]
26
30
  MappingIntStrAny = Mapping[IntStr, Any]
27
31
 
28
32
 
29
- class SupportsValidation(Protocol[B]):
33
+ class SupportsValidation(Protocol[B_co]):
30
34
  # ValidateValuesMixin methods
31
35
  def to_json(self) -> str:
32
36
  ...
33
37
 
34
- def to_dict(self) -> dict:
38
+ def to_dict(self) -> Dict:
39
+ ...
40
+
41
+ @classmethod
42
+ def from_json(cls, json_str: str) -> B_co:
35
43
  ...
36
44
 
37
45
  @classmethod
38
- def from_json(cls, json_str: str) -> B:
46
+ def from_dict(cls, json_dict: Dict) -> B_co:
39
47
  ...
40
48
 
41
49
  # Pydantic methods
42
- def json(
50
+ def json( # pylint: disable=too-many-arguments
43
51
  self,
44
52
  *,
45
53
  include: Optional[Union["AbstractSetIntStr", "MappingIntStrAny"]] = None,
@@ -55,7 +63,7 @@ class SupportsValidation(Protocol[B]):
55
63
  ) -> str:
56
64
  ...
57
65
 
58
- def dict(
66
+ def dict( # pylint: disable=too-many-arguments
59
67
  self,
60
68
  *,
61
69
  include: Optional[Union["AbstractSetIntStr", "MappingIntStrAny"]] = None,
@@ -69,7 +77,7 @@ class SupportsValidation(Protocol[B]):
69
77
  ...
70
78
 
71
79
  @classmethod
72
- def parse_raw(
80
+ def parse_raw( # pylint: disable=too-many-arguments
73
81
  cls,
74
82
  b: StrBytes,
75
83
  *,
@@ -77,7 +85,11 @@ class SupportsValidation(Protocol[B]):
77
85
  encoding: str = ...,
78
86
  proto: PydanticProtocol = ...,
79
87
  allow_pickle: bool = ...,
80
- ) -> B:
88
+ ) -> B_co:
89
+ ...
90
+
91
+ @classmethod
92
+ def parse_obj(cls, obj: Any) -> "B_co":
81
93
  ...
82
94
 
83
95
 
@@ -101,15 +113,24 @@ class ValidateValuesMixin(Generic[C]):
101
113
  gen_model: C = cls.parse_raw(json_str)
102
114
  return gen_model
103
115
 
116
+ @classmethod
117
+ def from_dict(cls: Type[C], json_dict: dict) -> C:
118
+ gen_model: C = cls.parse_obj(json_dict)
119
+ return gen_model
120
+
121
+
122
+ class S2Message(Generic[C], ValidateValuesMixin[C], BaseModel):
123
+ pass
124
+
104
125
 
105
126
  def convert_to_s2exception(f: Callable) -> Callable:
106
127
  def inner(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
107
128
  try:
108
129
  return f(*args, **kwargs)
109
- except (ValidationError, TypeError) as e:
110
- raise S2ValidationError(
111
- args, "Pydantic raised a format validation error."
112
- ) from e
130
+ except ValidationError as e:
131
+ raise S2ValidationError(args, display_errors(e.errors())) from e
132
+ except TypeError as e:
133
+ raise S2ValidationError(args, str(e)) from e
113
134
 
114
135
  inner.__doc__ = f.__doc__
115
136
  inner.__annotations__ = f.__annotations__
@@ -118,8 +139,8 @@ def convert_to_s2exception(f: Callable) -> Callable:
118
139
 
119
140
 
120
141
  def catch_and_convert_exceptions(
121
- input_class: Type[SupportsValidation[B]],
122
- ) -> Type[SupportsValidation[B]]:
142
+ input_class: Type[SupportsValidation[B_co]],
143
+ ) -> Type[SupportsValidation[B_co]]:
123
144
  input_class.__init__ = convert_to_s2exception(input_class.__init__) # type: ignore[method-assign]
124
145
  input_class.__setattr__ = convert_to_s2exception(input_class.__setattr__) # type: ignore[method-assign]
125
146
  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,,