From 2c96d37bac2d3f0644fff456636dea1dc51f5f3c Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Mon, 23 Sep 2024 10:09:55 -0500 Subject: [PATCH 1/5] fix(models/base.py): change model_config extra from "forbid" to "allow" to permit additional fields --- src/otf_api/models/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/otf_api/models/base.py b/src/otf_api/models/base.py index 8fe4870..61bf0ef 100644 --- a/src/otf_api/models/base.py +++ b/src/otf_api/models/base.py @@ -131,11 +131,11 @@ def model_dump( class OtfItemBase(BetterDumperMixin, BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="forbid") + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="allow") class OtfListBase(BaseModel): - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="forbid") + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="allow") collection_field: ClassVar[str] = "data" @property From 5e4f3888e7773aa11a2426172a153008beb9f98e Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Mon, 23 Sep 2024 10:10:07 -0500 Subject: [PATCH 2/5] chore(poetry.lock): update poetry.lock --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 5451244..4136bcc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3224,4 +3224,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "4808e21c3c301ccae27151e7289356b010702a5a6033ebdb6409bfb4bd1c5045" +content-hash = "ab2196415460f019dc7191ac41376d528297f76bf530f86ccec0118e8bffd457" From 5bd55a2599e1e8fe50d5033cdf93141e5a91cc71 Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Mon, 23 Sep 2024 10:10:24 -0500 Subject: [PATCH 3/5] bump --- .bumpversion.toml | 2 +- pyproject.toml | 2 +- src/otf_api/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index c68cd20..fb1220a 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.6.0" +current_version = "0.6.1" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(?:-(?Pdev)(?P0|[1-9]\\d*))?" diff --git a/pyproject.toml b/pyproject.toml index 1014125..a2c1ee5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "otf-api" -version = "0.6.0" +version = "0.6.1" description = "Python OrangeTheory Fitness API Client" authors = ["Jessica Smith "] license = "MIT" diff --git a/src/otf_api/__init__.py b/src/otf_api/__init__.py index 74759d9..86de253 100644 --- a/src/otf_api/__init__.py +++ b/src/otf_api/__init__.py @@ -6,7 +6,7 @@ from .api import Otf from .auth import OtfUser -__version__ = "0.6.0" +__version__ = "0.6.1" __all__ = ["Otf", "OtfUser"] From 1d3ce5f41fe2fb0f3f682b1f2c096575745754d8 Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Mon, 23 Sep 2024 10:37:58 -0500 Subject: [PATCH 4/5] chore(pyproject.toml): remove unused python-box dependency chore(pyproject.toml): add griffe dependency for documentation generation refactor(base.py): remove BetterDumperMixin and related code to simplify model serialization --- poetry.lock | 37 +---------- pyproject.toml | 2 +- src/otf_api/models/base.py | 131 +------------------------------------ 3 files changed, 4 insertions(+), 166 deletions(-) diff --git a/poetry.lock b/poetry.lock index bd3c808..a88f0eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2482,41 +2482,6 @@ loguru = "*" [package.extras] test = ["pytest", "pytest-cov"] -[[package]] -name = "python-box" -version = "7.2.0" -description = "Advanced Python dictionaries with dot notation access" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python_box-7.2.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:6bdeec791e25258351388b3029a3ec5da302bb9ed3be175493c43cdc6c47f5e3"}, - {file = "python_box-7.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c449f7b3756a71479fa9c61a86e344ac00ed782a66d7662590f0afa294249d18"}, - {file = "python_box-7.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:6b0d61f182d394106d963232854e495b51edc178faa5316a797be1178212d7e0"}, - {file = "python_box-7.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e2d752de8c1204255bf7b0c814c59ef48293c187a7e9fdcd2fefa28024b72032"}, - {file = "python_box-7.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a6c35ea356a386077935958a5debcd5b229b9a1b3b26287a52dfe1a7e65d99"}, - {file = "python_box-7.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:32ed58ec4d9e5475efe69f9c7d773dfea90a6a01979e776da93fd2b0a5d04429"}, - {file = "python_box-7.2.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2a2d664c6a27f7515469b6f1e461935a2038ee130b7d194b4b4db4e85d363618"}, - {file = "python_box-7.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5a7365db1aaf600d3e8a2747fcf6833beb5d45439a54318548f02e302e3ec"}, - {file = "python_box-7.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:739f827056ea148cbea3122d4617c994e829b420b1331183d968b175304e3a4f"}, - {file = "python_box-7.2.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:2617ef3c3d199f55f63c908f540a4dc14ced9b18533a879e6171c94a6a436f23"}, - {file = "python_box-7.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd866bed03087b1d8340014da8c3aaae19135767580641df1b4ae6fff6ac0aa"}, - {file = "python_box-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:9681f059e7e92bdf20782cd9ea6e533d4711fc7b8c57a462922a025d46add4d0"}, - {file = "python_box-7.2.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:6b59b1e2741c9ceecdf5a5bd9b90502c24650e609cd824d434fed3b6f302b7bb"}, - {file = "python_box-7.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23fae825d809ae7520fdeac88bb52be55a3b63992120a00e381783669edf589"}, - {file = "python_box-7.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:573b1abdcb7bd745fa404444f060ee62fc35a74f067181e55dcb43cfe92f2827"}, - {file = "python_box-7.2.0-py3-none-any.whl", hash = "sha256:a3c90832dd772cb0197fdb5bc06123b6e1b846899a1b53d9c39450d27a584829"}, - {file = "python_box-7.2.0.tar.gz", hash = "sha256:551af20bdab3a60a2a21e3435120453c4ca32f7393787c3a5036e1d9fc6a0ede"}, -] - -[package.extras] -all = ["msgpack", "ruamel.yaml (>=0.17)", "toml"] -msgpack = ["msgpack"] -pyyaml = ["PyYAML"] -ruamel-yaml = ["ruamel.yaml (>=0.17)"] -toml = ["toml"] -tomli = ["tomli", "tomli-w"] -yaml = ["ruamel.yaml (>=0.17)"] - [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -3302,4 +3267,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "18e222c6dad4e586ace3f58dd67479bb7829c2e36f3c3ebc736b4f127d4a4e16" +content-hash = "20fc83d1172d99d28cd8eeeb83bb6f79e622364de9d7de9687c544ae3b6e507a" diff --git a/pyproject.toml b/pyproject.toml index 4e5c46a..ce21eee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,6 @@ pendulum = "^3.0.0" pint = "0.24.*" pycognito = "2024.5.1" pydantic = "2.7.3" -python-box = "^7.2.0" [tool.poetry.group.dev.dependencies] aioresponses = "0.7.6" @@ -66,6 +65,7 @@ mkdocstrings-python = "1.10.3" pkginfo = "<1.11" setuptools = "^70.0.0" virtualenv = "^20.26.2" +griffe = "^1.3.1" [build-system] diff --git a/src/otf_api/models/base.py b/src/otf_api/models/base.py index 61bf0ef..72dfa8d 100644 --- a/src/otf_api/models/base.py +++ b/src/otf_api/models/base.py @@ -1,136 +1,9 @@ -import inspect -import typing -from typing import ClassVar, TypeVar +from typing import ClassVar -from box import Box from pydantic import BaseModel, ConfigDict -if typing.TYPE_CHECKING: - from pydantic.main import IncEx -T = TypeVar("T", bound="OtfItemBase") - - -class BetterDumperMixin: - """A better dumper for Pydantic models that includes properties in the dumped data. Must be mixed - into a Pydantic model, as it overrides the `model_dump` method. - - Includes support for nested models, and has an option to not include properties when dumping. - """ - - def get_properties(self) -> list[str]: - """Get the properties of the model.""" - cls = type(self) - - properties: list[str] = [] - methods = inspect.getmembers(self, lambda f: not (inspect.isroutine(f))) - for prop_name, _ in methods: - if hasattr(cls, prop_name) and isinstance(getattr(cls, prop_name), property): - properties.append(prop_name) - - return properties - - @typing.overload - def model_dump( - self, - *, - mode: typing.Literal["json", "python"] | str = "python", - include: "IncEx" = None, - exclude: "IncEx" = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - round_trip: bool = False, - warnings: bool = True, - include_properties: bool = True, - ) -> Box[str, typing.Any]: ... - - @typing.overload - def model_dump( - self, - *, - mode: typing.Literal["json", "python"] | str = "python", - include: "IncEx" = None, - exclude: "IncEx" = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - round_trip: bool = False, - warnings: bool = True, - include_properties: bool = False, - ) -> dict[str, typing.Any]: ... - - def model_dump( - self, - *, - mode: typing.Literal["json", "python"] | str = "python", - include: "IncEx" = None, - exclude: "IncEx" = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - round_trip: bool = False, - warnings: bool = True, - include_properties: bool = True, - ) -> dict[str, typing.Any] | Box[str, typing.Any]: - """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump - - Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. - - Args: - mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. - by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. - include_properties: Whether to include properties in the dumped data. - - Returns: - A dictionary representation of the model. Will be a `Box` if `include_properties` is `True`, otherwise a - regular dictionary. - - """ - dumped_data = typing.cast(BaseModel, super()).model_dump( - mode=mode, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - round_trip=round_trip, - warnings=warnings, - ) - - if not include_properties: - return dumped_data - - properties = self.get_properties() - - # set properties to their values - for prop_name in properties: - dumped_data[prop_name] = getattr(self, prop_name) - - # if the property is a Pydantic model, dump it as well - for k, v in dumped_data.items(): - if issubclass(type(getattr(self, k)), BaseModel): - dumped_data[k] = getattr(self, k).model_dump() - elif hasattr(v, "model_dump"): - dumped_data[k] = v.model_dump() - - return Box(dumped_data) - - -class OtfItemBase(BetterDumperMixin, BaseModel): +class OtfItemBase(BaseModel): model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="allow") From 06ad79d10236e99afc8ad6261b8d16fcf7fd7dfd Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Mon, 23 Sep 2024 11:10:09 -0500 Subject: [PATCH 5/5] fix(pyproject.toml): pin griffe < 1 to fix mkdocs build --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index a88f0eb..ea8077f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1006,13 +1006,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "1.3.1" +version = "0.49.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, - {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, + {file = "griffe-0.49.0-py3-none-any.whl", hash = "sha256:c0d505f2a444ac342b22f4647d6444c8db64964b6a379c14f401fc467c0741a3"}, + {file = "griffe-0.49.0.tar.gz", hash = "sha256:a7e1235c27d8139e0fd24a5258deef6061bc876a9fda8117a5cf7b53ee940a91"}, ] [package.dependencies] @@ -3267,4 +3267,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "20fc83d1172d99d28cd8eeeb83bb6f79e622364de9d7de9687c544ae3b6e507a" +content-hash = "4a4e79967b4a36bdce74f42fcc3c8d47193190d4451a97e6c5017c6342f31f00" diff --git a/pyproject.toml b/pyproject.toml index ce21eee..ad9effd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ mkdocstrings-python = "1.10.3" pkginfo = "<1.11" setuptools = "^70.0.0" virtualenv = "^20.26.2" -griffe = "^1.3.1" +griffe = "<1.0.0" [build-system]