From d25580c9a66c68843d49ebe8dbec24056654e27c Mon Sep 17 00:00:00 2001 From: drc38 <20024196+drc38@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:03:18 +1300 Subject: [PATCH] breaking change: remove deprecated Action items and update docs (#694) ### Changes included in this PR ***Breaking change*** As per the warning in 1.0.0 this PR removes deprecated Action items from the `enums.py` files to coincide with the 2.0.0 release, updates code references and updates documentation to be consistent with the change including string references. For example: - replaces deprecated Action enums such as `Action.BootNotification` with `Action.boot_notification` - replaces string references such as `@on("BootNotification")` and `action="BootNotification"` with `Action.boot_notification` If users of the library have not already migrated to the new Action enums they will need to do so before using 2.0.0. ### Checklist 1. [x] Does your submission pass the existing tests? 2. [x] Are there new tests that cover these additions/changes? 3. [x] Have you linted your code locally before submission? --- docs/source/central_system.rst | 2 +- examples/v16/central_system.py | 2 +- examples/v201/central_system.py | 5 +- ocpp/charge_point.py | 10 ++-- ocpp/routing.py | 10 ++-- ocpp/v16/enums.py | 52 ------------------- ocpp/v201/enums.py | 76 ---------------------------- tests/test_charge_point.py | 16 +++--- tests/test_exceptions.py | 5 +- tests/test_messages.py | 24 ++++----- tests/test_routing.py | 10 ++-- tests/v16/conftest.py | 6 +-- tests/v16/test_v16_charge_point.py | 8 +-- tests/v201/conftest.py | 7 +-- tests/v201/test_v201_charge_point.py | 5 +- 15 files changed, 58 insertions(+), 180 deletions(-) diff --git a/docs/source/central_system.rst b/docs/source/central_system.rst index be5052cec..c1a80af5a 100644 --- a/docs/source/central_system.rst +++ b/docs/source/central_system.rst @@ -97,7 +97,7 @@ Remove the `on_connect()` handler from the code above and replace it by the foll class MyChargePoint(cp): - @on(Action.BootNotification) + @on(Action.boot_notification) async def on_boot_notification(self, charge_point_vendor, charge_point_model, **kwargs): return call_result.BootNotificationPayload( current_time=datetime.utcnow().isoformat(), diff --git a/examples/v16/central_system.py b/examples/v16/central_system.py index 5f2343289..7ae27d418 100644 --- a/examples/v16/central_system.py +++ b/examples/v16/central_system.py @@ -22,7 +22,7 @@ class ChargePoint(cp): - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification( self, charge_point_vendor: str, charge_point_model: str, **kwargs ): diff --git a/examples/v201/central_system.py b/examples/v201/central_system.py index 54a880d89..350626830 100644 --- a/examples/v201/central_system.py +++ b/examples/v201/central_system.py @@ -16,18 +16,19 @@ from ocpp.routing import on from ocpp.v201 import ChargePoint as cp from ocpp.v201 import call_result +from ocpp.v201.enums import Action logging.basicConfig(level=logging.INFO) class ChargePoint(cp): - @on("BootNotification") + @on(Action.boot_notification) def on_boot_notification(self, charging_station, reason, **kwargs): return call_result.BootNotificationPayload( current_time=datetime.utcnow().isoformat(), interval=10, status="Accepted" ) - @on("Heartbeat") + @on(Action.heartbeat) def on_heartbeat(self): print("Got a Heartbeat!") return call_result.HeartbeatPayload( diff --git a/ocpp/charge_point.py b/ocpp/charge_point.py index 0f3ceacff..b85557f0d 100644 --- a/ocpp/charge_point.py +++ b/ocpp/charge_point.py @@ -170,20 +170,22 @@ def _raise_key_error(action, version): from ocpp.v201.enums import Action as v201_Action if version == "1.6": - if hasattr(v16_Action, action): + try: + v16_Action(action) raise NotImplementedError( details={"cause": f"No handler for {action} registered."} ) - else: + except ValueError: raise NotSupportedError( details={"cause": f"{action} not supported by OCPP{version}."} ) elif version in ["2.0", "2.0.1"]: - if hasattr(v201_Action, action): + try: + v201_Action(action) raise NotImplementedError( details={"cause": f"No handler for {action} registered."} ) - else: + except ValueError: raise NotSupportedError( details={"cause": f"{action} not supported by OCPP{version}."} ) diff --git a/ocpp/routing.py b/ocpp/routing.py index dbdc641cf..bf4b12f2b 100644 --- a/ocpp/routing.py +++ b/ocpp/routing.py @@ -19,7 +19,7 @@ def on(action, *, skip_schema_validation=False): ``` class MyChargePoint(cp): - @on(Action.BootNotification): + @on(Action.boot_notification): async def on_boot_notification( self, charge_point_model, @@ -64,7 +64,7 @@ def after(action): It can be used like so: - @after(Action.BootNotification): + @after(Action.boot_notification): def after_boot_notification(): pass @@ -93,11 +93,11 @@ def create_route_map(obj): class ChargePoint: - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification(self, *args, **kwargs): pass - @after(Action.BootNotification) + @after(Action.boot_notification) def after_boot_notification(self, *args, **kwargs): pass @@ -105,7 +105,7 @@ def after_boot_notification(self, *args, **kwargs): In this case this returns: { - Action.BootNotification: { + Action.boot_notification: { '_on_action': , '_after_action': , '_skip_schema_validation': False, diff --git a/ocpp/v16/enums.py b/ocpp/v16/enums.py index e80175d01..56aed63df 100644 --- a/ocpp/v16/enums.py +++ b/ocpp/v16/enums.py @@ -1,5 +1,3 @@ -from warnings import warn - try: # breaking change introduced in python 3.11 from enum import StrEnum @@ -13,56 +11,6 @@ class StrEnum(str, Enum): # pragma: no cover class Action(StrEnum): """An Action is a required part of a Call message.""" - def __init__(self, *args, **kwargs): - warn( - message="Action enum contains deprecated members and will be removed in " - "the next major release, please use snake case members.", - category=DeprecationWarning, - ) - - # --------- Soon to be deprecated --------------------- - Authorize = "Authorize" - BootNotification = "BootNotification" - CancelReservation = "CancelReservation" - CertificateSigned = "CertificateSigned" - ChangeAvailability = "ChangeAvailability" - ChangeConfiguration = "ChangeConfiguration" - ClearCache = "ClearCache" - ClearChargingProfile = "ClearChargingProfile" - DataTransfer = "DataTransfer" - DeleteCertificate = "DeleteCertificate" - DiagnosticsStatusNotification = "DiagnosticsStatusNotification" - ExtendedTriggerMessage = "ExtendedTriggerMessage" - FirmwareStatusNotification = "FirmwareStatusNotification" - GetCompositeSchedule = "GetCompositeSchedule" - GetConfiguration = "GetConfiguration" - GetDiagnostics = "GetDiagnostics" - GetInstalledCertificateIds = "GetInstalledCertificateIds" - GetLocalListVersion = "GetLocalListVersion" - GetLog = "GetLog" - Heartbeat = "Heartbeat" - InstallCertificate = "InstallCertificate" - LogStatusNotification = "LogStatusNotification" - MeterValues = "MeterValues" - RemoteStartTransaction = "RemoteStartTransaction" - RemoteStopTransaction = "RemoteStopTransaction" - ReserveNow = "ReserveNow" - Reset = "Reset" - SecurityEventNotification = "SecurityEventNotification" - SendLocalList = "SendLocalList" - SetChargingProfile = "SetChargingProfile" - SignCertificate = "SignCertificate" - SignedFirmwareStatusNotification = "SignedFirmwareStatusNotification" - SignedUpdateFirmware = "SignedUpdateFirmware" - StartTransaction = "StartTransaction" - StatusNotification = "StatusNotification" - StopTransaction = "StopTransaction" - TriggerMessage = "TriggerMessage" - UnlockConnector = "UnlockConnector" - UpdateFirmware = "UpdateFirmware" - - # -------------------------------------------------------- - authorize = "Authorize" boot_notification = "BootNotification" cancel_reservation = "CancelReservation" diff --git a/ocpp/v201/enums.py b/ocpp/v201/enums.py index 44a634c2d..2bcd2b162 100644 --- a/ocpp/v201/enums.py +++ b/ocpp/v201/enums.py @@ -1,5 +1,3 @@ -from warnings import warn - try: # breaking change introduced in python 3.11 from enum import StrEnum @@ -13,80 +11,6 @@ class StrEnum(str, Enum): # pragma: no cover class Action(StrEnum): """An Action is a required part of a Call message.""" - def __init__(self, *args, **kwargs): - warn( - message="Action enum contains deprecated members and will be removed in " - "the next major release, please use snake case members.", - category=DeprecationWarning, - ) - - # --------- Soon to be deprecated --------------------- - Authorize = "Authorize" - BootNotification = "BootNotification" - CancelReservation = "CancelReservation" - CertificateSigned = "CertificateSigned" - ChangeAvailability = "ChangeAvailability" - ClearCache = "ClearCache" - ClearChargingProfile = "ClearChargingProfile" - ClearDisplayMessage = "ClearDisplayMessage" - ClearedChargingLimit = "ClearedChargingLimit" - ClearVariableMonitoring = "ClearVariableMonitoring" - CostUpdated = "CostUpdated" - CustomerInformation = "CustomerInformation" - DataTransfer = "DataTransfer" - DeleteCertificate = "DeleteCertificate" - FirmwareStatusNotification = "FirmwareStatusNotification" - Get15118EVCertificate = "Get15118EVCertificate" - GetBaseReport = "GetBaseReport" - GetCertificateStatus = "GetCertificateStatus" - GetChargingProfiles = "GetChargingProfiles" - GetCompositeSchedule = "GetCompositeSchedule" - GetDisplayMessages = "GetDisplayMessages" - GetInstalledCertificateIds = "GetInstalledCertificateIds" - GetLocalListVersion = "GetLocalListVersion" - GetLog = "GetLog" - GetMonitoringReport = "GetMonitoringReport" - GetReport = "GetReport" - GetTransactionStatus = "GetTransactionStatus" - GetVariables = "GetVariables" - Heartbeat = "Heartbeat" - InstallCertificate = "InstallCertificate" - LogStatusNotification = "LogStatusNotification" - MeterValues = "MeterValues" - NotifyChargingLimit = "NotifyChargingLimit" - NotifyCustomerInformation = "NotifyCustomerInformation" - NotifyDisplayMessages = "NotifyDisplayMessages" - NotifyEVChargingNeeds = "NotifyEVChargingNeeds" - NotifyEVChargingSchedule = "NotifyEVChargingSchedule" - NotifyEvent = "NotifyEvent" - NotifyMonitoringReport = "NotifyMonitoringReport" - NotifyReport = "NotifyReport" - PublishFirmware = "PublishFirmware" - PublishFirmwareStatusNotification = "PublishFirmwareStatusNotification" - ReportChargingProfiles = "ReportChargingProfiles" - RequestStartTransaction = "RequestStartTransaction" - RequestStopTransaction = "RequestStopTransaction" - ReservationStatusUpdate = "ReservationStatusUpdate" - ReserveNow = "ReserveNow" - Reset = "Reset" - SecurityEventNotification = "SecurityEventNotification" - SendLocalList = "SendLocalList" - SetChargingProfile = "SetChargingProfile" - SetDisplayMessage = "SetDisplayMessage" - SetMonitoringBase = "SetMonitoringBase" - SetMonitoringLevel = "SetMonitoringLevel" - SetNetworkProfile = "SetNetworkProfile" - SetVariableMonitoring = "SetVariableMonitoring" - SetVariables = "SetVariables" - SignCertificate = "SignCertificate" - StatusNotification = "StatusNotification" - TransactionEvent = "TransactionEvent" - TriggerMessage = "TriggerMessage" - UnlockConnector = "UnlockConnector" - UnpublishFirmware = "UnpublishFirmware" - UpdateFirmware = "UpdateFirmware" - # -------------------------------------------------------- - authorize = "Authorize" boot_notification = "BootNotification" cancel_reservation = "CancelReservation" diff --git a/tests/test_charge_point.py b/tests/test_charge_point.py index 074b4afd7..25a7c8e0d 100644 --- a/tests/test_charge_point.py +++ b/tests/test_charge_point.py @@ -48,12 +48,12 @@ def foo(self): def test_multiple_classes_with_same_name_for_handler(): class ChargerA(cp_201): - @on(Action.Heartbeat) + @on(Action.heartbeat) def heartbeat(self, **kwargs): pass class ChargerB(cp_201): - @on(Action.Heartbeat) + @on(Action.heartbeat) def heartbeat(self, **kwargs): pass @@ -409,7 +409,7 @@ class ChargerA(cp_16): on_boot_notification_call_count = 0 after_boot_notification_call_count = 0 - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification(self, *args, **kwargs): # call_unique_id should not be passed as arg nor kwarg assert kwargs == camel_to_snake_case(payload_a) @@ -419,7 +419,7 @@ def on_boot_notification(self, *args, **kwargs): current_time="foo", interval=1, status=RegistrationStatus.accepted ) - @after(Action.BootNotification) + @after(Action.boot_notification) def after_boot_notification(self, call_unique_id, *args, **kwargs): assert call_unique_id == charger_a_test_call_unique_id assert kwargs == camel_to_snake_case(payload_a) @@ -434,7 +434,7 @@ class ChargerB(cp_16): on_boot_notification_call_count = 0 after_boot_notification_call_count = 0 - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification(self, call_unique_id, *args, **kwargs): assert call_unique_id == charger_b_test_call_unique_id assert kwargs == camel_to_snake_case(payload_b) @@ -445,7 +445,7 @@ def on_boot_notification(self, call_unique_id, *args, **kwargs): current_time="foo", interval=1, status=RegistrationStatus.accepted ) - @after(Action.BootNotification) + @after(Action.boot_notification) def after_boot_notification(self, *args, **kwargs): # call_unique_id should not be passed as arg nor kwarg assert kwargs == camel_to_snake_case(payload_b) @@ -460,14 +460,14 @@ def after_boot_notification(self, *args, **kwargs): msg_a = Call( unique_id=charger_a_test_call_unique_id, - action=Action.BootNotification.value, + action=Action.boot_notification.value, payload=payload_a, ) await charger_a._handle_call(msg_a) msg_b = Call( unique_id=charger_b_test_call_unique_id, - action=Action.BootNotification.value, + action=Action.boot_notification.value, payload=payload_b, ) await charger_b._handle_call(msg_b) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 59beb5fa2..f58060fb8 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -6,6 +6,7 @@ TypeConstraintViolationError, ) from ocpp.messages import Call, _validate_payload +from ocpp.v16.enums import Action def test_exception_with_error_details(): @@ -27,7 +28,7 @@ def test_exception_show_triggered_message_type_constraint(): # so this should raise a TypeConstraintViolationError call = Call( unique_id=123456, - action="BootNotification", + action=Action.boot_notification, payload={"chargePointVendor": 1, "chargePointModel": "SingleSocketCharger"}, ) ocpp_message = ( @@ -44,7 +45,7 @@ def test_exception_show_triggered_message_format(): # The payload is syntactically incorrect, should trigger a FormatViolationError call = Call( unique_id=123457, - action="BootNotification", + action=Action.boot_notification, payload={"syntactically": "incorrect"}, ) diff --git a/tests/test_messages.py b/tests/test_messages.py index 343691aa3..0ada09e00 100644 --- a/tests/test_messages.py +++ b/tests/test_messages.py @@ -123,7 +123,7 @@ def test_validate_set_charging_profile_payload(): """ message = Call( unique_id="1234", - action="SetChargingProfile", + action=Action.set_charging_profile, payload={ "connectorId": 1, "csChargingProfiles": { @@ -152,7 +152,7 @@ def test_validate_get_composite_profile_payload(): """ message = CallResult( unique_id="1234", - action="GetCompositeSchedule", + action=Action.get_composite_schedule, payload={ "status": "Accepted", "connectorId": 1, @@ -176,7 +176,7 @@ def test_validate_payload_with_valid_payload(ocpp_version): """ message = CallResult( unique_id="1234", - action="Heartbeat", + action=Action.heartbeat, payload={"currentTime": datetime.now().isoformat()}, ) @@ -190,7 +190,7 @@ def test_validate_payload_with_invalid_additional_properties_payload(): """ message = CallResult( unique_id="1234", - action="Heartbeat", + action=Action.heartbeat, payload={"invalid_key": True}, ) @@ -205,7 +205,7 @@ def test_validate_payload_with_invalid_type_payload(): """ message = Call( unique_id="1234", - action="StartTransaction", + action=Action.start_transaction, payload={ "connectorId": 1, "idTag": "okTag", @@ -225,7 +225,7 @@ def test_validate_payload_with_invalid_missing_property_payload(): """ message = Call( unique_id="1234", - action="StartTransaction", + action=Action.start_transaction, payload={ "connectorId": 1, "idTag": "okTag", @@ -277,14 +277,14 @@ def test_call_error_representation(): def test_call_representation(): - call = Call(unique_id="1", action=Action.Heartbeat, payload={}) + call = Call(unique_id="1", action=Action.heartbeat, payload={}) assert str(call) == "" def test_call_result_representation(): call = CallResult( - unique_id="1", action=Action.Authorize, payload={"status": "Accepted"} + unique_id="1", action=Action.authorize, payload={"status": "Accepted"} ) assert ( @@ -335,7 +335,7 @@ def test_serializing_custom_types(): """ message = Call( unique_id="1234", - action="StartTransaction", + action=Action.start_transaction, payload={ "connectorId": 1, "idTag": "okTag", @@ -360,7 +360,7 @@ def test_validate_meter_values_hertz(): """ message = Call( unique_id="1234", - action="MeterValues", + action=Action.meter_values, payload={ "connectorId": 1, "transactionId": 123456789, @@ -389,7 +389,7 @@ def test_validate_set_maxlength_violation_payload(): """ message = Call( unique_id="1234", - action="StartTransaction", + action=Action.start_transaction, payload={ "idTag": "012345678901234567890", "connectorId": 1, @@ -408,7 +408,7 @@ async def test_validate_payload_threads(use_threads): """ message = CallResult( unique_id="1234", - action="Heartbeat", + action=Action.heartbeat, payload={"currentTime": datetime.now().isoformat()}, ) diff --git a/tests/test_routing.py b/tests/test_routing.py index d556f17ba..b443a3c1f 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -10,15 +10,15 @@ def test_create_route_map(): """ class ChargePoint: - @on(Action.Heartbeat, skip_schema_validation=True) + @on(Action.heartbeat, skip_schema_validation=True) def on_heartbeat(self): pass - @after(Action.Heartbeat) + @after(Action.heartbeat) def after_heartbeat(self): pass - @on(Action.MeterValues) + @on(Action.meter_values) def meter_values(self): pass @@ -29,12 +29,12 @@ def undecorated(self): route_map = create_route_map(cp) assert route_map == { - Action.Heartbeat: { + Action.heartbeat: { "_on_action": cp.on_heartbeat, "_after_action": cp.after_heartbeat, "_skip_schema_validation": True, }, - Action.MeterValues: { + Action.meter_values: { "_on_action": cp.meter_values, "_skip_schema_validation": False, }, diff --git a/tests/v16/conftest.py b/tests/v16/conftest.py index 301d49f12..3d4006a42 100644 --- a/tests/v16/conftest.py +++ b/tests/v16/conftest.py @@ -10,7 +10,7 @@ @pytest.fixture def heartbeat_call(): - return Call(unique_id=1, action=Action.Heartbeat, payload={}).to_json() + return Call(unique_id=1, action=Action.heartbeat, payload={}).to_json() @pytest.fixture @@ -22,7 +22,7 @@ def not_supported_call(): def boot_notification_call(): return Call( unique_id="1", - action=Action.BootNotification, + action=Action.boot_notification, payload={ "chargePointVendor": "Alfen BV", "chargePointModel": "ICU Eve Mini", @@ -64,7 +64,7 @@ class BootNotification: def mock_base_central_system(base_central_system): mock_result_call = CallResult( unique_id=str(base_central_system._unique_id_generator()), - action="BootNotification", + action=Action.boot_notification, payload={ "currentTime": "2018-05-29T17:37:05.495259", "interval": 350, diff --git a/tests/v16/test_v16_charge_point.py b/tests/v16/test_v16_charge_point.py index 3204f60e3..249076233 100644 --- a/tests/v16/test_v16_charge_point.py +++ b/tests/v16/test_v16_charge_point.py @@ -20,7 +20,7 @@ async def test_route_message_with_existing_route( """ - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification(charge_point_model, charge_point_vendor, **kwargs): # noqa assert charge_point_vendor == "Alfen BV" assert charge_point_model == "ICU Eve Mini" @@ -32,7 +32,7 @@ def on_boot_notification(charge_point_model, charge_point_vendor, **kwargs): # status="Accepted", ) - @after(Action.BootNotification) + @after(Action.boot_notification) async def after_boot_notification( charge_point_model, charge_point_vendor, **kwargs ): # noqa @@ -62,7 +62,7 @@ async def after_boot_notification( @pytest.mark.asyncio async def test_route_message_without_validation(base_central_system): - @on(Action.BootNotification, skip_schema_validation=True) + @on(Action.boot_notification, skip_schema_validation=True) def on_boot_notification(**kwargs): # noqa assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223" @@ -116,7 +116,7 @@ async def test_route_message_not_supported(base_central_system, not_supported_ca """ - @on(Action.BootNotification) + @on(Action.boot_notification) def on_boot_notification(**kwargs): # noqa assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223" diff --git a/tests/v201/conftest.py b/tests/v201/conftest.py index c9b269192..cf372bda5 100644 --- a/tests/v201/conftest.py +++ b/tests/v201/conftest.py @@ -4,6 +4,7 @@ from ocpp.messages import Call, CallResult from ocpp.v201 import ChargePoint, call +from ocpp.v201.enums import Action chargingStation = { "vendorName": "ICU Eve Mini", @@ -14,14 +15,14 @@ @pytest.fixture def heartbeat_call(): - return Call(unique_id=1, action="Heartbeat", payload={}).to_json() + return Call(unique_id=1, action=Action.heartbeat, payload={}).to_json() @pytest.fixture def boot_notification_call(): return Call( unique_id="1", - action="BootNotification", + action=Action.boot_notification, payload={ "reason": "PowerUp", "chargingStation": chargingStation, @@ -53,7 +54,7 @@ def mock_boot_request(): def mock_base_central_system(base_central_system): mock_result_call = CallResult( unique_id=str(base_central_system._unique_id_generator()), - action="BootNotification", + action=Action.boot_notification, payload={ "currentTime": "2018-05-29T17:37:05.495259", "interval": 350, diff --git a/tests/v201/test_v201_charge_point.py b/tests/v201/test_v201_charge_point.py index ba32e7f24..0961c0322 100644 --- a/tests/v201/test_v201_charge_point.py +++ b/tests/v201/test_v201_charge_point.py @@ -4,6 +4,7 @@ from ocpp.routing import after, create_route_map, on from ocpp.v201 import call_result +from ocpp.v201.enums import Action @pytest.mark.asyncio @@ -15,7 +16,7 @@ async def test_route_message_with_existing_route( """ - @on("BootNotification") + @on(Action.boot_notification) def on_boot_notification(reason, charging_station, **kwargs): assert reason == "PowerUp" assert charging_station == { @@ -30,7 +31,7 @@ def on_boot_notification(reason, charging_station, **kwargs): status="Accepted", ) - @after("BootNotification") + @after(Action.boot_notification) def after_boot_notification(reason, charging_station, **kwargs): assert reason == "PowerUp" assert charging_station == {