Module attrbox.env
Load configuration from environment files.
The file format is similar to a Bash file, but it is not as complete as python-dotenv.
Supported:
- unquoted and single-quoted key
- unquoted, single- and double-quoted value
- spaces before and after key, equal sign, and value are ignored
exportat the start of the line is ignored#comment at the start of the line- value expansion for values in the environment
- value expansion for values only in the file (e.g., when
update_env=False)
Unsupported:
- key without a value
- multiline value
#comment after value- escape sequences in value
Examples
>>> loads('''
... # a comment
... normal=value
... ' quoted '=" space "
... export expanded="expanded-${normal}"
... ''')
{'normal': 'value', ' quoted ': ' space ', 'expanded': 'expanded-value'}
Global variables
var PathStr-
Type representing a
Pathor a string to a path.
Functions
def expand(value: str, store: Optional[Mapping[str, str]] = None, *, dotted_keys: bool = False) ‑> str-
Expand variables of the form
$varand${var}.A simplified form of
os.path.expandvars.Args
value:str- value to expand
store:Mapping[str, str], optional- valid substitutions.
If
None,os.environis used. Defaults toNone. dotted_keys:bool- if
Trueallow${dotted.name}to map to nested values{"dotted": {"name": "value"}}. Defaults toFalse.
Returns
str- expanded value. Unknown variables are left unchanged.
Examples
Regular expansion works as expected:
>>> expand("$a ${b}", {'a': 'hello', 'b': 'world'}) 'hello world'Unknown variables are left unchanged:
>>> expand("$a is $b", {'a': 'this'}) 'this is $b' >>> expand("no vars", {}) 'no vars'Values are passed to
str:>>> expand("$a", {'a': 5}) '5'Dotted names are optionally possible:
>>> expand("${a.b}", {"a": {"b": "works"}}, dotted_keys=True) 'works' def load(file: SupportsRead, /, *, update_env: bool = True, dotted_keys: bool = True) ‑> Dict[str, str]-
Load an environment
file.Args
file:SupportsRead- file-like (has
.read()). update_env:bool, optional- If
True, update theos.path.environas values are read in. Defaults toTrue. dotted_keys:bool, optional- If
True, split the key by.and use that to create a nesteddict. Defaults toTrue.
Returns
Dict[str, str]- configuration values
Examples
>>> root = Path(__file__).parent.parent.parent >>> load((root / "test/config_3.env").open()) {'section': {'key': 'value3', 'env': 'loaded'}} def loads(text: str, /, *, update_env: bool = True, dotted_keys: bool = True) ‑> Dict[str, str]-
Parse an environment file from a string.
Args
text:str- text to parse.
update_env:bool, optional- If
True, update theos.path.environas values are read in. Defaults toTrue. dotted_keys:bool, optional- If
True, split the key by.and use that to create a nesteddict. Defaults toTrue.
Returns
Dict[str, str]- configuration values
Examples
If you don't want to update the environment:
>>> 'fake' in ENV False >>> loads('''export 'fake'=ignored ... works=not $fake''', update_env=False) {'fake': 'ignored', 'works': 'not ignored'} >>> 'fake' in ENV FalseKeys with dots in them create nested dicts, but are optional:
>>> loads('section.key=value', update_env=False, dotted_keys=True) {'section': {'key': 'value'}} >>> loads('section.key=value', update_env=False, dotted_keys=False) {'section.key': 'value'} def find_env(path: Union[pathlib._local.Path, str, ForwardRef(None)] = None, name: str = '.env') ‑> Optional[pathlib._local.Path]-
Find the
.envfile in the ancestors of the current path.Args
path:PathLike, optional- A starting path to check. If
None, starts with the current working directory. Defaults toNone. name:str, optional- file name to search for. Defaults to
".env".
Returns
Optional[Path]- path to environment file or
Noneif it is not found.
Examples
Search from the current working directory:
>>> str(find_env()) '.../.env'Search from a specific directory:
>>> str(find_env(".")) '.../.env'Pass a
Pathobject:>>> str(find_env(Path(__file__))) '.../.env'Point directly to the
.envfile:>>> str(find_env(Path(__file__).parent.parent.parent / ".env")) '.../.env' def load_env(path: Union[pathlib._local.Path, str, ForwardRef(None)] = None) ‑> Dict[str, str]-
Load an environment file.
We recursively search for a
.envfile from the path given or the current working directory, if omitted.Args
path:PathStr, optional- starting path. If
None, start from the current working directory. Defaults toNone.
Raises
FileNotFoundError- If not
.envfile is found.
Returns
Dict[str, str]- configuration values
Examples
>>> load_env() # our .env doesn't have any values {}If no
.envcan be found, aFileNotFoundErroris raised:>>> load_env("/") Traceback (most recent call last): ... FileNotFoundError: Cannot find .env file to load.
Classes
class SupportsRead (*args, **kwargs)-
Protocol for a class that implements a
.read()method.Expand source code
class SupportsRead(Protocol): # pylint: disable=too-few-public-methods """Protocol for a class that implements a `.read()` method.""" def read(self) -> str: """Read the contents of the file-like object.""" return "" # pragma: no coverAncestors
- typing.Protocol
- typing.Generic
Methods
def read(self) ‑> str-
Read the contents of the file-like object.