From e37fc1889e377633b4281cc02c5d644a4dbace89 Mon Sep 17 00:00:00 2001 From: Ben Avrahami Date: Tue, 2 Jan 2024 10:27:46 +0200 Subject: [PATCH] exclude children --- CHANGELOG.md | 3 +++ docs/envvar.rst | 4 ++-- envolved/_version.py | 2 +- envolved/describe/__init__.py | 31 +++++++++++++++++++------------ envolved/envvar.py | 4 ++-- pyproject.toml | 2 +- tests/unittests/test_describe.py | 10 ++++++++++ 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c86ad85..c7cc14a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # envolved Changelog +## 1.2.1 +### Fixed +* The children of envvars that are excluded from the description are now also excluded. ## 1.2.0 ### Added * new argument `strip_items` for `CollectionParser`. diff --git a/docs/envvar.rst b/docs/envvar.rst index 4c9a077..f564769 100644 --- a/docs/envvar.rst +++ b/docs/envvar.rst @@ -28,7 +28,7 @@ EnvVars pos_args: collections.base.Sequence[envvar.EnvVar | InferEnvVar] = ..., \ description: str | collections.abc.Sequence[str] | None = None,\ validators: collections.abc.Iterable[collections.abc.Callable[[T], T]] = (), \ - on_partial: T | missing | as_default | discard = missing) -> envvar.SchemaEnvVar[T]: + on_partial: T | missing | as_default | discard = missing) -> envvar.SchemaEnvVar[T] :noindex: Creates an EnvVar that reads from multiple environment variables. @@ -217,7 +217,7 @@ EnvVars The EnvVar's :attr:`default` must not be :data:`missing` if this option is used. - * If set to :data:`missing`, an :exc:`~exceptions.MissingEnvError` will be raised, even if the EnvVar's + * If set to :data:`missing`, a :exc:`~exceptions.MissingEnvError` will be raised, even if the EnvVar's :attr:`~EnvVar.default` is set. * If set to a value, that value will be returned. diff --git a/envolved/_version.py b/envolved/_version.py index c68196d..a955fda 100644 --- a/envolved/_version.py +++ b/envolved/_version.py @@ -1 +1 @@ -__version__ = "1.2.0" +__version__ = "1.2.1" diff --git a/envolved/describe/__init__.py b/envolved/describe/__init__.py index 8ae3bf8..598b7b8 100644 --- a/envolved/describe/__init__.py +++ b/envolved/describe/__init__.py @@ -4,8 +4,8 @@ from envolved.describe.flat import FlatEnvVarsDescription from envolved.describe.nested import NestedEnvVarsDescription, RootNestedDescription -from envolved.envvar import EnvVar, InferEnvVar, top_level_env_vars - +from envolved.envvar import EnvVar, InferEnvVar, all_env_vars +from itertools import chain def describe_env_vars(**kwargs: Any) -> List[str]: ret = EnvVarsDescription().nested().wrap(**kwargs) @@ -19,12 +19,19 @@ def __init__(self, env_vars: Iterable[EnvVar] | None = None) -> None: children: Set[EnvVar] = set() if env_vars is None: - env_vars = top_level_env_vars + env_vars = all_env_vars + to_exclude = roots_to_exclude_from_description | set(chain.from_iterable(r._get_descendants() for r in roots_to_exclude_from_description)) + else: + to_exclude = set() + + for env_var in env_vars: self.env_var_roots.add(env_var) children.update(env_var._get_descendants()) # remove any children we found along the way self.env_var_roots -= children + # remove any children we were asked to exclude + self.env_var_roots -= to_exclude def flat(self) -> FlatEnvVarsDescription: return FlatEnvVarsDescription.from_envvars(self.env_var_roots) @@ -38,21 +45,21 @@ def nested(self) -> NestedEnvVarsDescription: bound=Union[EnvVar, InferEnvVar, Iterable[Union[EnvVar, InferEnvVar]], Mapping[Any, Union[EnvVar, InferEnvVar]]], ) +roots_to_exclude_from_description: Set[EnvVar] = set() # noqa: PLW0603 def exclude_from_description(to_exclude: T) -> T: - global top_level_env_vars # noqa: PLW0603 - + global roots_to_exclude_from_description # noqa: PLW0603 + if isinstance(to_exclude, EnvVar): - evs = frozenset((to_exclude,)) - elif isinstance(to_exclude, InferEnvVar): - evs = frozenset() + roots_to_exclude_from_description.add(to_exclude) elif isinstance(to_exclude, Mapping): - evs = frozenset(to_exclude.values()) + exclude_from_description(to_exclude.values()) elif isinstance(to_exclude, Iterable): - evs = frozenset(to_exclude) + for v in to_exclude: + exclude_from_description(v) + elif isinstance(to_exclude, InferEnvVar): + pass else: raise TypeError(f"cannot exclude unrecognized type {type(to_exclude)!r}") - top_level_env_vars -= evs - return to_exclude diff --git a/envolved/envvar.py b/envolved/envvar.py index 0d77073..ef62f3d 100644 --- a/envolved/envvar.py +++ b/envolved/envvar.py @@ -438,13 +438,13 @@ def env_var( # type: ignore[misc] return register_env_var(ev) -top_level_env_vars: MutableSet[EnvVar] = WeakSet() +all_env_vars: MutableSet[EnvVar] = WeakSet() EV = TypeVar("EV", bound=EnvVar) def register_env_var(ev: EV) -> EV: - top_level_env_vars.add(ev) + all_env_vars.add(ev) return ev diff --git a/pyproject.toml b/pyproject.toml index 954de1a..f380202 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "envolved" -version = "1.2.0" +version = "1.2.1" description = "" authors = ["ben avrahami "] license = "MIT" diff --git a/tests/unittests/test_describe.py b/tests/unittests/test_describe.py index 06137a2..0001dc8 100644 --- a/tests/unittests/test_describe.py +++ b/tests/unittests/test_describe.py @@ -20,8 +20,15 @@ def test_describe(): } exclude_from_description(point_args) + _p = exclude_from_description(env_var("_p_", type=SimpleNamespace, args=point_args)) # noqa: F841 + p = env_var("p_", type=SimpleNamespace, args=point_args) # noqa: F841 + _w_p = exclude_from_description(p.with_prefix("_w_")) # noqa: F841 + + j_p = _p.with_prefix("j") # noqa: F841 + j_p.description = "j point" + q = env_var( # noqa: F841 "q_", type=SimpleNamespace, @@ -51,6 +58,9 @@ def test_describe(): assert describe_env_vars() == [ "A: full description of A", "B", + "j point:", + " J_P_X: x coordinate", + " J_P_Y: y coordinate", " P_X: x coordinate", " P_Y: y coordinate", "point Q next line:",