Skip to content

Commit

Permalink
with_prefix can now override
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Avrahami committed Apr 8, 2024
1 parent fdf3ad7 commit a33a63d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* `envolved` no longer supports python 3.7
### Added
* `FindIterCollectionParser`
* `with_prefix` can now override many of an env-var's parameters
### Fixed
* `CollectionParser`'s `opener` and `closer` arguments now correctly handle matches that would be split by the delimiter
* `CollectionParser`'s `closer` argument now correctly handles overlapping matches
Expand Down
14 changes: 12 additions & 2 deletions docs/envvar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,14 @@ EnvVars
.. warning::
Even if the validator does not mutate the value, it should still return the original value.

.. method:: with_prefix(prefix: str) -> EnvVar[T]
.. method:: with_prefix(prefix: str, *, default = ..., description = ...) -> EnvVar[T]

Return a new EnvVar with the parameters but with a given prefix. This method can be used to re-use an env-var
schema to multiple env-vars.
schema to multiple env-vars. Can also override additional parameters of the new EnvVar.

:param prefix: The prefix to use.
:param other: If specified, will override the parameters of the new EnvVar. If not specified, the
parameters of the original EnvVar will be used. Different subclasses can allow to override additional parameters.
:return: A new EnvVar with the given prefix, of the same type as the envar being used.

.. method:: patch(value: T | missing | discard) -> typing.ContextManager
Expand Down Expand Up @@ -202,6 +204,10 @@ EnvVars
users = users_ev.get(reverse=True) # will return a list of usernames sorted in reverse order
else:
users = users_ev.get() # will return a list of usernames sorted in ascending order
.. method:: with_prefix(prefix: str, *, default = ..., description = ..., type = ..., case_sensitive = ..., strip_whitespaces = ...) -> SingleEnvVar[T]

See :meth:`Superclass method <EnvVar.with_prefix>`


.. class:: SchemaEnvVar
Expand Down Expand Up @@ -273,6 +279,10 @@ EnvVars
user_ev.get(age=20, height=168) # will return a User object with the name taken from the environment variables,
# but with the age and height overridden by the keyword arguments.
.. method:: with_prefix(prefix: str, *, default = ..., description = ..., type = ..., on_partial = ...) -> SchemaEnvVar[T]

See :meth:`Superclass method <EnvVar.with_prefix>`

.. class:: Factory(callback: collections.abc.Callable[[], T])

Expand Down
74 changes: 62 additions & 12 deletions envolved/envvar.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ class Discard(Enum):

discard = Discard.discard


class Unchanged(Enum):
unchanged = auto()


unchanged = Unchanged.unchanged

Description = Union[str, Sequence[str]]


Expand Down Expand Up @@ -134,7 +141,13 @@ def _get(self, **kwargs: Any) -> T:
pass

@abstractmethod
def with_prefix(self: Self, prefix: str) -> Self:
def with_prefix(
self: Self,
prefix: str,
*,
default: Union[T, Factory[T], Missing, Discard, Unchanged] = unchanged,
description: Union[Description, None, Unchanged] = unchanged,
) -> Self:
pass

@abstractmethod
Expand Down Expand Up @@ -194,15 +207,35 @@ def _get(self, **kwargs: Any) -> T:
raw_value = raw_value.strip()
return self.type(raw_value, **kwargs)

def with_prefix(self, prefix: str) -> SingleEnvVar[T]:
def with_prefix(
self,
prefix: str,
*,
default: Union[T, Factory[T], Missing, Discard, Unchanged] = unchanged,
description: Union[Description, None, Unchanged] = unchanged,
type: Union[Type[T], Parser[T], Unchanged] = unchanged,
case_sensitive: Union[bool, Unchanged] = unchanged,
strip_whitespaces: Union[bool, Unchanged] = unchanged,
) -> SingleEnvVar[T]:
if default is unchanged:
default = self.default
if description is unchanged:
description = self.description
type_ = type
if type_ is unchanged:
type_ = self.type
if case_sensitive is unchanged:
case_sensitive = self.case_sensitive
if strip_whitespaces is unchanged:
strip_whitespaces = self.strip_whitespaces
return register_env_var(
SingleEnvVar(
prefix + self._key,
self.default,
type=self.type,
description=self.description,
case_sensitive=self.case_sensitive,
strip_whitespaces=self.strip_whitespaces,
default,
type=type_,
description=description,
case_sensitive=case_sensitive,
strip_whitespaces=strip_whitespaces,
validators=self._validators,
)
)
Expand Down Expand Up @@ -292,14 +325,31 @@ def _get(self, **kwargs: Any) -> T:
raise errs[0]
return self._type(*pos_values, **kw_values)

def with_prefix(self, prefix: str) -> SchemaEnvVar[T]:
def with_prefix(
self,
prefix: str,
*,
default: Union[T, Factory[T], Missing, Discard, Unchanged] = unchanged,
description: Union[Unchanged, None, Description] = unchanged,
type: Union[Type[T], Parser[T], Unchanged] = unchanged,
on_partial: Union[T, Missing, AsDefault, Discard, Factory[T], Unchanged] = unchanged,
) -> SchemaEnvVar[T]:
if default is unchanged:
default = self.default
if description is unchanged:
description = self.description
type_ = type
if type_ is unchanged:
type_ = self.type
if on_partial is unchanged:
on_partial = self.on_partial
return register_env_var(
SchemaEnvVar(
{k: v.with_prefix(prefix) for k, v in self._args.items()},
self.default,
type=self._type,
description=self.description,
on_partial=self.on_partial,
default,
type=type_,
description=description,
on_partial=on_partial,
validators=self._validators,
pos_args=tuple(v.with_prefix(prefix) for v in self._pos_args),
)
Expand Down
18 changes: 18 additions & 0 deletions tests/unittests/test_single_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@ def test_override_default(monkeypatch):
assert a1.get() == 1


def test_override_default_in_constr(monkeypatch):
parent = env_var("a", type=int)

a0 = parent.with_prefix("0")
a1 = parent.with_prefix("1", default=1)
monkeypatch.setenv("0a", "0")
assert a0.get() == 0
assert a1.get() == 1


def test_override_type(monkeypatch):
parent = env_var("a", type=int)

a1 = parent.with_prefix("1", default=1, type=len)
assert a1.default == 1
assert a1.type == len


def test_patch():
a = env_var("a", type=int)
with a.patch(-1):
Expand Down

0 comments on commit a33a63d

Please sign in to comment.