Skip to content

Commit

Permalink
:doc:🚨 refactor ozi.spec.base.Default
Browse files Browse the repository at this point in the history
add iter method
help in Field metadata
improved docstring for asdict
  • Loading branch information
rjdbcm committed Jun 9, 2024
1 parent 791fcf6 commit 66408f5
Showing 1 changed file with 39 additions and 27 deletions.
66 changes: 39 additions & 27 deletions ozi/spec/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
from __future__ import annotations

import reprlib
from dataclasses import MISSING
from dataclasses import Field
from dataclasses import asdict
from dataclasses import dataclass
from dataclasses import fields
from typing import TYPE_CHECKING
from typing import ClassVar
from typing import Iterator
from typing import Protocol
from typing import TypeAlias

Expand All @@ -16,28 +22,23 @@
from collections.abc import Callable
from collections.abc import Mapping

_VT: TypeAlias = list['_KT'] | Mapping[str, '_KT']
_KT: TypeAlias = str | int | float | None | _VT
_Val: TypeAlias = list['_Key'] | Mapping[str, '_Key']
_Key: TypeAlias = str | int | float | None | _Val
_Lambda: TypeAlias = Callable[[], '_FactoryMethod']
_FactoryMethod: TypeAlias = Callable[[], _Lambda]
_FactoryMethod: TypeAlias = Callable[[], _Lambda] | Field

if sys.version_info >= (3, 11):
from typing import Self
elif sys.version_info < (3, 11):
from typing_extensions import Self

from dataclasses import asdict
from dataclasses import dataclass
from dataclasses import field
from dataclasses import fields


class _FactoryDataclass(Protocol):
"""A dataclass that, when called, returns a factory method."""

__dataclass_fields__: ClassVar[dict[str, _VT]]
__dataclass_fields__: ClassVar[dict[str, _Val]]

def asdict(self: Self) -> dict[str, _VT]: ...
def asdict(self: Self) -> dict[str, _Val]: ...

def __call__(self: Self) -> _FactoryMethod: ...

Expand All @@ -47,28 +48,39 @@ class Default(_FactoryDataclass):
"""A dataclass that, when called, returns it's own default factory method."""

def __call__(self: Self) -> _FactoryMethod: # pragma: defer to python
return field(default_factory=lambda: self())
return Field(
default=MISSING,
default_factory=self,
init=True,
repr=True,
hash=None,
compare=True,
metadata={'help': str(self.__class__.__doc__).replace('\n ', '')},
kw_only=MISSING, # type: ignore
)

def asdict(self: Self) -> dict[str, _VT]:
def __iter__(self: Self) -> Iterator[tuple[str, _Val]]:
for f in fields(self):
if f.repr:
yield (
f.name,
(
getattr(self, f.name)
if not isinstance(getattr(self, f.name), Default)
else getattr(self, f.name).asdict()
),
)

def asdict(self: Self) -> dict[str, _Val | str]:
"""Return a dictionary of all fields where repr=True.
Hide a variable from the dict by setting repr to False and using
a Default subclass as the default_factory.
Typing is compatible with Jinja2 Environment and JSON.
"""
all_fields = (
(
f.name,
(
getattr(self, f.name)
if not isinstance(getattr(self, f.name), Default)
else getattr(self, f.name).asdict()
),
)
for f in fields(self)
if f.repr
)
Typing is compatible with :term:`JSON` and Jinja2 global namespace.
return dict(all_fields) | {
.. seealso::
:std:label:`jinja2:global-namespace`
"""
return dict(iter(self)) | {
'help': str(self.__class__.__doc__).replace('\n ', ''),
}

Expand Down

0 comments on commit 66408f5

Please sign in to comment.