Skip to content

Commit

Permalink
Merge pull request #50 from NodeJSmith/feature/allow_extra_values
Browse files Browse the repository at this point in the history
Feature/allow extra values, bump version
  • Loading branch information
NodeJSmith authored Sep 23, 2024
2 parents 96bc812 + 06ad79d commit aab4c6a
Show file tree
Hide file tree
Showing 5 changed files with 12 additions and 174 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "0.6.0"
current_version = "0.6.1"

parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<dev_l>dev)(?P<dev>0|[1-9]\\d*))?"

Expand Down
43 changes: 4 additions & 39 deletions poetry.lock

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

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -66,6 +65,7 @@ mkdocstrings-python = "1.10.3"
pkginfo = "<1.11"
setuptools = "^70.0.0"
virtualenv = "^20.26.2"
griffe = "<1.0.0"


[build-system]
Expand Down
2 changes: 1 addition & 1 deletion src/otf_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .api import Otf
from .auth import OtfUser

__version__ = "0.6.0"
__version__ = "0.6.1"


__all__ = ["Otf", "OtfUser"]
Expand Down
135 changes: 4 additions & 131 deletions src/otf_api/models/base.py
Original file line number Diff line number Diff line change
@@ -1,141 +1,14 @@
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):
model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="forbid")
class OtfItemBase(BaseModel):
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
Expand Down

0 comments on commit aab4c6a

Please sign in to comment.