diff --git a/envier/env.py b/envier/env.py index fea4a9f..e9ee301 100644 --- a/envier/env.py +++ b/envier/env.py @@ -41,6 +41,7 @@ def __init__( map: t.Optional[MapType] = None, default: t.Union[T, NoDefaultType] = NoDefault, deprecations: t.Optional[t.List[DeprecationInfo]] = None, + private: bool = False, help: t.Optional[str] = None, help_type: t.Optional[str] = None, help_default: t.Optional[str] = None, @@ -60,6 +61,7 @@ def __init__( self.map = map self.default = default self.deprecations = deprecations + self.private = private self.help = help self.help_type = help_type @@ -69,10 +71,14 @@ def _retrieve(self, env: "Env", prefix: str) -> T: source = env.source full_name = prefix + _normalized(self.name) + if self.private: + full_name = f"_{full_name}" raw = source.get(full_name.format(**env.dynamic)) if raw is None and self.deprecations: for name, deprecated_when, removed_when in self.deprecations: full_deprecated_name = prefix + _normalized(name) + if self.private: + full_deprecated_name = f"_{full_deprecated_name}" raw = source.get(full_deprecated_name.format(**env.dynamic)) if raw is not None: deprecated_when_message = ( @@ -261,6 +267,7 @@ def var( map: t.Optional[MapType] = None, default: t.Union[T, NoDefaultType] = NoDefault, deprecations: t.Optional[t.List[DeprecationInfo]] = None, + private: bool = False, help: t.Optional[str] = None, help_type: t.Optional[str] = None, help_default: t.Optional[str] = None, @@ -273,6 +280,7 @@ def var( map, default, deprecations, + private, help, help_type, help_default, @@ -288,6 +296,7 @@ def v( map: t.Optional[MapType] = None, default: t.Union[T, NoDefaultType] = NoDefault, deprecations: t.Optional[t.List[DeprecationInfo]] = None, + private: bool = False, help: t.Optional[str] = None, help_type: t.Optional[str] = None, help_default: t.Optional[str] = None, @@ -300,6 +309,7 @@ def v( map, default, deprecations, + private, help, help_type, help_default, @@ -379,7 +389,9 @@ def include( setattr(cls, k, v) @classmethod - def help_info(cls, recursive: bool = False) -> t.List[HelpInfo]: + def help_info( + cls, recursive: bool = False, include_private: bool = False + ) -> t.List[HelpInfo]: """Extract the help information from the class. Returns a list of all the environment variables declared by the class. @@ -388,6 +400,9 @@ def help_info(cls, recursive: bool = False) -> t.List[HelpInfo]: Set ``recursive`` to ``True`` to include variables from nested Env classes. + + Set ``include_private`` to ``True`` to include variables that are + marked as private (i.e. their name starts with an underscore). """ entries = [] @@ -398,6 +413,9 @@ def add_entries(full_prefix: str, config: t.Type[Env]) -> None: ) for v in vars: + if not include_private and v.private: + continue + # Add a period at the end if necessary. help_message = v.help.strip() if v.help is not None else "" if help_message and not help_message.endswith("."): @@ -412,9 +430,11 @@ def add_entries(full_prefix: str, config: t.Type[Env]) -> None: # typing.t.Union[, NoneType] help_type = v.type.__args__[0].__name__ # type: ignore[attr-defined] + private_prefix = "_" if v.private else "" + entries.append( ( - "``" + full_prefix + _normalized(v.name) + "``", + f"``{private_prefix}{full_prefix}{_normalized(v.name)}``", help_type, # type: ignore[attr-defined] v.help_default if v.help_default is not None @@ -428,7 +448,7 @@ def add_entries(full_prefix: str, config: t.Type[Env]) -> None: while configs: full_prefix, config = configs.pop() new_prefix = full_prefix + _normalized(config.__prefix__) - if not new_prefix.endswith("_"): + if new_prefix and not new_prefix.endswith("_"): new_prefix += "_" add_entries(new_prefix, config) diff --git a/tests/test_env.py b/tests/test_env.py index fa29b20..4374a00 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -357,3 +357,23 @@ class Config(Env): config = Config(dynamic={"prefix": "pfx", "dyn": "bar"}) assert config.foobar == 24 + + +def test_env_private(monkeypatch): + monkeypatch.setenv("_PRIVATE_FOO", "24") + monkeypatch.setenv("PUBLIC_FOO", "25") + + class Config(Env): + private = Env.var(int, "private.foo", default=42, private=True) + public = Env.var(int, "public.foo", default=42) + + config = Config() + + assert config.private == 24 + assert config.public == 25 + + assert Config.help_info() == [("``PUBLIC_FOO``", "``int``", "42", "")] + assert set(Config.help_info(include_private=True)) == { + ("``_PRIVATE_FOO``", "``int``", "42", ""), + ("``PUBLIC_FOO``", "``int``", "42", ""), + }