From d91f89633a4033e10f91344833341dd42079ae78 Mon Sep 17 00:00:00 2001 From: yanz571 Date: Fri, 18 Sep 2026 17:15:01 -0700 Subject: [PATCH] revert the option type to remove the option wrapper --- CHANGELOG.md | 59 +++++++++++++++++++++++ comcheck_api/schemas/comCheck.schema.json | 23 ++++----- comcheck_api/types/core_types.py | 33 +++---------- pyproject.toml | 2 +- uv.lock | 2 +- 5 files changed, 77 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a590944..83729c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,63 @@ All notable changes to this project are documented in this file. +## [2.0.1] - 2026-09-18 + +Reverts the enum renames introduced in 2.0.0. If you updated code to use the +`*OptionsEnum` names or `.root` accessors, those changes must be undone — +upgrading from 2.0.0 is otherwise a drop-in. + +### ⚠️ Breaking Changes (relative to 2.0.0) + +- **The four `*OptionsEnum` renames are reverted.** `WallTypeOptions`, + `FanSystemComplianceMethodOptions`, `FanEfficiencyExceptionTypeOptions`, and + `TrackLightingWattageBasisTypeOptions` are plain enum classes again, and the + `WallTypeOptionsEnum` / `FanSystemComplianceMethodOptionsEnum` / + `FanEfficiencyExceptionTypeOptionsEnum` / + `TrackLightingWattageBasisTypeOptionsEnum` classes no longer exist. Member + access works directly (`WallTypeOptions.WOOD_FRAME_16_AG_WALL`) and the `.root` + accessor is gone. No `RootModel` enum wrappers remain. + - Three are `StrEnum` and compare equal to their string values again: + `WallTypeOptions` (11 members), `FanEfficiencyExceptionTypeOptions` (16), + `FanSystemComplianceMethodOptions` (2). + - `TrackLightingWattageBasisTypeOptions` (7 members) is a plain `Enum`, so it + does **not** compare equal to plain strings — a `StrEnum` cannot hold the + `None` member this field still requires (see below). + +### Fixed + +- **Root cause of the 2.0.0 enum churn.** The renames were never a deliberate + design change: `core_types.py` is generated by `datamodel-codegen` + (`tools/generate_core_types.py`), which must wrap an enum in a `RootModel` when + a schema definition declares `"type": "string"` *and* lists `null` as an enum + member — a contradictory combination, since a `StrEnum` cannot hold `None`. + Codegen renamed the enum to `*OptionsEnum` to free the name for the wrapper. + Exactly four definitions in `comCheck.schema.json` had that combination; all + four are now fixed at the schema level, so the generated types are clean. +- **Nullability made explicit for `TrackLightingWattageBasisTypeOptions`.** The + API returns `null` for `trackLightingWattageBasisType` on real projects, so + removing `null` outright made `get_project()` raise `ValidationError` on + existing data. The definition now declares `"type": ["string", "null"]` — + matching `AdvancedControlsAllowanceTypeOptions` — which keeps `null` valid + without a wrapper. +- Removed the redundant `null` enum member from + `FanEfficiencyExceptionTypeOptions` (which already has explicit `UNSPECIFIED` + and `NONE` members), `WallTypeOptions`, and + `FanSystemComplianceMethodOptions`. These three fields now reject an explicit + `null`; leaving them unset is still valid and serializes as omitted. + +### Migration notes + +- Replace `OptionsEnum.MEMBER` with `Options.MEMBER`, and drop any + `.root` access on these four types. +- String comparisons work again for `WallTypeOptions`, + `FanEfficiencyExceptionTypeOptions`, and `FanSystemComplianceMethodOptions`. + Keep using `.value` or enum members for + `TrackLightingWattageBasisTypeOptions`. +- Stop sending an explicit `null` for `wallType`, + `fanEfficiencyExceptionType`, or `fanSystemComplianceMethod`; omit the field + instead. + ## [2.0.0] - 2026-09-18 This release tracks a breaking update to the COMcheck Web API schema. **Projects @@ -24,6 +81,8 @@ pip install --upgrade comcheck-api now `RootModel` wrappers around a renamed `*Enum` class (e.g. `WallTypeOptionsEnum`); the wrapper holds the member in `.root`, so code that used these as enums directly must be updated. + **Superseded in 2.0.1 — these four renames were reverted; prefer upgrading + straight to 2.0.1 rather than adopting the `*OptionsEnum` names.** - **Serialization now uses pydantic's `MISSING` sentinel instead of `exclude_unset`.** Unset fields in `core_types` models default to `MISSING` and `CustomBaseModel` defines a plain `@model_serializer` that drops diff --git a/comcheck_api/schemas/comCheck.schema.json b/comcheck_api/schemas/comCheck.schema.json index 59146ed..e31c52e 100644 --- a/comcheck_api/schemas/comCheck.schema.json +++ b/comcheck_api/schemas/comCheck.schema.json @@ -5475,8 +5475,7 @@ "MASONRY_AG_WALL", "OTHER_AG_WALL", "OTHER_BG_WALL", - "OTHER_FRAME", - null + "OTHER_FRAME" ], "descriptions": [ "Wood-Framed, 16in. o.c.", @@ -5489,8 +5488,7 @@ "Concrete Block", "Other Above Grade Wall Type", "Other Below Grade Wall Type", - "Other Framing Type", - "Unspecified" + "Other Framing Type" ] }, "BgWallTypeOptions": { @@ -6410,7 +6408,10 @@ ] }, "TrackLightingWattageBasisTypeOptions": { - "type": "string", + "type": [ + "string", + "null" + ], "enum": [ "NONE", "WATTAGE_BASIS_NOT_SET", @@ -7301,13 +7302,11 @@ "type": "string", "enum": [ "FAN_SYSTEM_MOTOR_NAMEPLATE_HP", - "FAN_SYSTEM_MOTOR_BRAKE_HP", - null + "FAN_SYSTEM_MOTOR_BRAKE_HP" ], "descriptions": [ "Motor Nameplate HP", - "Brake HP (BHP)", - "Missing" + "Brake HP (BHP)" ] }, "FanEfficiencyExceptionTypeOptions": { @@ -7328,8 +7327,7 @@ "FAN_EFFICIENCY_EXCEPTION_TYPE_HIGH_TEMPERATURE", "FAN_EFFICIENCY_EXCEPTION_TYPE_EXPLOSIVE", "FAN_EFFICIENCY_EXCEPTION_TYPE_REVERSIBLE_TUNNEL_VENT", - "NONE", - null + "NONE" ], "descriptions": [ "Unspecified", @@ -7347,8 +7345,7 @@ "Moves gases > 482 F", "Explosive atmosphere only", "Reversible for tunnel ventilation", - "None", - "Missing" + "None" ] }, "FanTypeOptions": { diff --git a/comcheck_api/types/core_types.py b/comcheck_api/types/core_types.py index b9bbfad..e19491c 100644 --- a/comcheck_api/types/core_types.py +++ b/comcheck_api/types/core_types.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: comCheck.schema.json -# timestamp: 2026-08-11T17:22:04+00:00 +# timestamp: 2026-09-19T00:01:20+00:00 from __future__ import annotations @@ -147,7 +147,7 @@ class Project(CustomBaseModel): notes: Annotated[str | None, Field(description='$comment')] = None -class WallTypeOptionsEnum(StrEnum): +class WallTypeOptions(StrEnum): WOOD_FRAME_16_AG_WALL = 'WOOD_FRAME_16_AG_WALL' WOOD_FRAME_24_AG_WALL = 'WOOD_FRAME_24_AG_WALL' METAL_FRAME_16_AG_WALL = 'METAL_FRAME_16_AG_WALL' @@ -161,10 +161,6 @@ class WallTypeOptionsEnum(StrEnum): OTHER_FRAME = 'OTHER_FRAME' -class WallTypeOptions(RootModel[WallTypeOptionsEnum | None | MISSING]): - root: WallTypeOptionsEnum | None | MISSING = MISSING - - class Location(CustomBaseModel): model_config = ConfigDict( extra='ignore', @@ -723,18 +719,12 @@ class HasPressureDropCredits(Enum): NoneType_None = None -class FanSystemComplianceMethodOptionsEnum(StrEnum): +class FanSystemComplianceMethodOptions(StrEnum): FAN_SYSTEM_MOTOR_NAMEPLATE_HP = 'FAN_SYSTEM_MOTOR_NAMEPLATE_HP' FAN_SYSTEM_MOTOR_BRAKE_HP = 'FAN_SYSTEM_MOTOR_BRAKE_HP' -class FanSystemComplianceMethodOptions( - RootModel[FanSystemComplianceMethodOptionsEnum | None | MISSING] -): - root: FanSystemComplianceMethodOptionsEnum | None | MISSING = MISSING - - -class FanEfficiencyExceptionTypeOptionsEnum(StrEnum): +class FanEfficiencyExceptionTypeOptions(StrEnum): FAN_EFFICIENCY_EXCEPTION_TYPE_UNSPECIFIED = ( 'FAN_EFFICIENCY_EXCEPTION_TYPE_UNSPECIFIED' ) @@ -777,12 +767,6 @@ class FanEfficiencyExceptionTypeOptionsEnum(StrEnum): NONE = 'NONE' -class FanEfficiencyExceptionTypeOptions( - RootModel[FanEfficiencyExceptionTypeOptionsEnum | None | MISSING] -): - root: FanEfficiencyExceptionTypeOptionsEnum | None | MISSING = MISSING - - class PressureDropTypeOptions(StrEnum): PRESSURE_DROP_CREDIT_TYPE_UNSPECIFIED = 'PRESSURE_DROP_CREDIT_TYPE_UNSPECIFIED' FULLY_DUCTED_RETURN_ANDOR_EXHAUST_AIR_SYSTEMS = ( @@ -1387,19 +1371,14 @@ class LightingExemptionTypeOptions(StrEnum): EXEMPTION_VISUALLY_IMPAIRED = 'EXEMPTION_VISUALLY_IMPAIRED' -class TrackLightingWattageBasisTypeOptionsEnum(StrEnum): +class TrackLightingWattageBasisTypeOptions(Enum): NONE = 'NONE' WATTAGE_BASIS_NOT_SET = 'WATTAGE_BASIS_NOT_SET' TRACK_BASIS = 'TRACK_BASIS' CIRCUIT_BREAKER_CAPACITY = 'CIRCUIT_BREAKER_CAPACITY' CURRENT_LIMITING_DEVICE_CAPACITY = 'CURRENT_LIMITING_DEVICE_CAPACITY' TRANSFORMER_CAPACITY = 'TRANSFORMER_CAPACITY' - - -class TrackLightingWattageBasisTypeOptions( - RootModel[TrackLightingWattageBasisTypeOptionsEnum | None | MISSING] -): - root: TrackLightingWattageBasisTypeOptionsEnum | None | MISSING = MISSING + NoneType_None = None class ExteriorLightingZoneTypeOptions(StrEnum): diff --git a/pyproject.toml b/pyproject.toml index 9861770..58e8eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "comcheck_api" -version = "2.0.0" +version = "2.0.1" description = "COMcheck API Python Package" readme = "README.md" requires-python = ">=3.12" diff --git a/uv.lock b/uv.lock index 2c899de..8dd8b9b 100644 --- a/uv.lock +++ b/uv.lock @@ -217,7 +217,7 @@ wheels = [ [[package]] name = "comcheck-api" -version = "2.0.0" +version = "2.0.1" source = { editable = "." } dependencies = [ { name = "httpx" },