Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<Name>OptionsEnum.MEMBER` with `<Name>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
Expand All @@ -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
Expand Down
23 changes: 10 additions & 13 deletions comcheck_api/schemas/comCheck.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5475,8 +5475,7 @@
"MASONRY_AG_WALL",
"OTHER_AG_WALL",
"OTHER_BG_WALL",
"OTHER_FRAME",
null
"OTHER_FRAME"
],
"descriptions": [
"Wood-Framed, 16in. o.c.",
Expand All @@ -5489,8 +5488,7 @@
"Concrete Block",
"Other Above Grade Wall Type",
"Other Below Grade Wall Type",
"Other Framing Type",
"Unspecified"
"Other Framing Type"
]
},
"BgWallTypeOptions": {
Expand Down Expand Up @@ -6410,7 +6408,10 @@
]
},
"TrackLightingWattageBasisTypeOptions": {
"type": "string",
"type": [
"string",
"null"
],
"enum": [
"NONE",
"WATTAGE_BASIS_NOT_SET",
Expand Down Expand Up @@ -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": {
Expand All @@ -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",
Expand All @@ -7347,8 +7345,7 @@
"Moves gases > 482 F",
"Explosive atmosphere only",
"Reversible for tunnel ventilation",
"None",
"Missing"
"None"
]
},
"FanTypeOptions": {
Expand Down
33 changes: 6 additions & 27 deletions comcheck_api/types/core_types.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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'
Expand All @@ -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',
Expand Down Expand Up @@ -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'
)
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading