Name | standby-variables JSON |
Version |
1.1.0
JSON |
| download |
home_page | None |
Summary | Dynamic variables for static namespaces |
upload_time | 2025-10-09 14:22:28 |
maintainer | None |
docs_url | None |
author | Ilia Khaustov |
requires_python | >=3.11 |
license | MIT License
Copyright (c) 2025 Ilia Khaustov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. |
keywords |
configuration
environment
typing
validation
variables
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# standby-variables
[](https://pypi.org/project/standby-variables/)
[](https://github.com/ilia-khaustov/standby-variables/actions/workflows/)
[](https://coveralls.io/github/ilia-khaustov/standby-variables)
Dynamic variables for static namespaces.
## About
standby-variables is a tiny library for declaring dynamic values (like environment variables) in static, typed namespaces (like Python classes).
- Treat environment configuration as first-class, typed values.
- Compose behavior with a small and expressive API.
- Keep your code statically analyzable: every variable has a concrete type, and mypy understands it.
You get:
- Plain attributes on classes that evaluate lazily.
- A consistent way to make variables optional or provide defaults.
- Validation and error messages that carry useful context.
- Building blocks to extend beyond environment variables.
## Rationale
Most applications mix a static structure (modules, classes) with dynamic sources (env vars, files, CLI args).
It’s tempting to sprinkle `os.environ.get(...)` everywhere - but that dilutes typing, validation, and clarity.
With standby-variables your dynamic values become explicit and composable:
- Declare what a variable is, how it’s parsed, and what to do if it’s missing.
- Configure the exact behaviour right next to a variable definition:
* use operator `>>` or chained call `given(*hints: standby.Hint)` to apply "hints" like Default, Required or Validated
* use operator `|` or chained call `otherwise(*backups: standby.Variable)` to specify one or more backup variables
- Keep strong typing across your configuration surface.
- Use dataclass-like descriptors that work as class attributes and cache nothing implicitly.
The result is readable configuration code that feels like constants, but evaluates at runtime.
## Basic usage and syntax
- Every dynamic value is a `Variable[T]` for some type `T`.
- You can "extract" the runtime value by either:
* Calling the variable: `var()` (may return `None` if defined as not required),
* Or forcing the value: `var.value()` (never returns `None`; raises if unavailable).
- String representations include context, and exceptions propagate that context upward, which makes debugging easier.
### Hints
- `Default(value)` returns the default when the source is missing.
- `Required(True)` means the variable must be present (default behaviour).
- `Required(False)` means "optional": `var()` can return `None`.
- `Validated(predicate, raises=True)` ensures values pass a check.
- `Validated(predicate, raises=False)` will "nullify" invalid values and return `None` instead of raising an exception.
Example:
```python
import os
from standby import Const
from standby.hint import Default, Required, Validated
from standby import env
# simulate environment
os.environ.setdefault("APP_PORT", "8080")
def parse_bool(s: str) -> bool:
return s.strip() in {"1", "true", "yes", "on", "True"}
class Settings:
# A required int, with a default when missing
PORT: int =~ env.Var("APP_PORT", int).given(Default(8000))
# Optional bool: returns None if missing
DEBUG: bool | None =~ (env.Var("APP_DEBUG", parse_bool) >> Required(False))
# A purely static value with validation
TIMEOUT: int =~ Const(10).given(Validated(lambda v: v > 0))
print(Settings.PORT) # 8080 (from env), defaults to 8000 if unset
print(Settings.DEBUG) # None if APP_DEBUG is missing; True/False otherwise
print(Settings.TIMEOUT) # 10
```
Fallback to "backup" variables:
```python
from standby import env, Const
class Settings:
class API:
# Prefer ENV var; if missing, use another Variable as a fallback option
URL: str =~ (env.Var("API_URL", str) | env.Var("FALLBACK_API_URL", str))
KEY: str =~ env.Var("API_KEY", str).otherwise(env.Var("FALLBACK_API_KEY", str))
print(Settings.API.URL) # Returns first ENV value which is present
```
Forcing with `.value()`:
```python
from standby import env
from standby.exc import VariableNotSet
try:
must_have_port = env.Var("MUST_HAVE_PORT", int).value()
except VariableNotSet as e:
# Rich context in e.args for easier debugging
print("Missing env:", e.args)
```
## Type safety
Every variable is parameterized by its type, and mypy can verify usage end-to-end.
You might have noticed a weird operator `=~` used in example of `Settings` class.
In fact, it is two distinct operators: `=` and `~` joined together due to subjective preference.
It is possible to have `~` attached to Variable definition like:
URL: str = ~(...) # this is why operator expressions are inside parenthesis
The role of this operator is to instruct type checker that nothing wrong happens when we define `Variable[T]` as its
wrapped type `T`. When you access a variable via parent class/instance, Python invokes `__get__()` descriptor
method which is implemented for all basic primitives in `standby`. So, reading the value `T` "dynamically" happens just
by accessing its container `Variable[T]` which is initialized together with its parent class. You can think about it as
a special case of "class property".
```python
from standby import Const
from standby.hint import Default, Required, Validated
from standby import env
def parse_bool(s: str) -> bool:
return s.strip() in {"1", "true", "yes", "on", "True"}
class Settings:
# Expression has real type Variable[int] but defined inside a class as just int
PORT: int =~ env.Var("APP_PORT", int).given(Default(8000))
# Expression has real type Variable[bool] but is set to "bool | None" because it's hinted with Required(false)
# Currently, unary operator casting to "T | None" is not implemented
# Type checker (mypy) allows result of cast(T, ...) to be assigned a "T | None" type
DEBUG: bool | None =~ (env.Var("APP_DEBUG", bool) >> Required(False))
```
## Environment variables
The `standby.env` module provides handy tools to read from `os.environ`.
- `env.Var[T](name, parser)` reads `os.environ[name]` and parses it into type `T`.
- `env.SeparatedList[T]` takes a source `Variable[str]`, splits by a given `split_sep` (`,` by default), and parses each element.
- `env.Ref[T]` takes a source `Variable[str]` and uses its value to find another environment variable with that name.
```python
import base64
from standby import Const
from standby.hint import Default, Required
from standby import env
# Required int with a default
POOL_SIZE = env.Var[int]("DB_POOL_SIZE", int) >> Default(5)
# Optional boolean
USE_CACHE = env.Var[bool]("USE_CACHE", lambda s: s == "1") >> Required(False)
# ALLOWED_HOSTS="example.com, api.example.com, localhost"
ALLOWED_HOSTS = env.SeparatedList[str](
src=env.Var("ALLOWED_HOSTS", str), # read raw string from env
split_sep=",",
parser=lambda s: s.strip(), # trim each piece
)
# Default to ["localhost"] if the variable is missing
ALLOWED_HOSTS_WITH_DEFAULT = ALLOWED_HOSTS >> Default(["localhost"])
# If ALLOWED_HOSTS is missing, and you want "optional list" semantics:
OPTIONAL_HOSTS = ALLOWED_HOSTS >> Required(False) # .__call__ returns None
# SECRET_ENV_VAR="<AUTO_GENERATED_VARIABLE_WITH_SECRET_VALUE_IN_BASE64>"
SECRET = env.Ref(
src=env.Var("SECRET_ENV_VAR", str), # taking the secret var name here
parser=base64.b64decode, # decoding it to bytes
)
decoded_secret: bytes = SECRET()
```
## Link and List customization
`env.Ref` and `env.SeparatedList` are built from `standby` primitives: `Link` and `List`.
Links let one variable refer to another variable’s name.
This is handy for indirection (think: "the name of the variable that contains the key").
- `Link[T, S]`: a generic link-variable where:
* `src` yields some `S`;
* `linker` maps that `S` to another `Variable[T]`.
- `List[T, P, S]`: a generic list-variable where:
* `src` yields some `S`;
* `S` is split in parts of `P` by a `splitter` function given as keyword arg;
* Each `P` part is parsed to `T` by a `parser` function given as keyword arg;
* `T` items are used to create a new `Variable[list[T]]`.
Custom link can be used like this:
```python
import os
from standby import Link, env, exc, hint, Variable
# Suppose "WORKERS_VAR" contains the name of another env var that holds an int.
# e.g. WORKERS_VAR="MAX_WORKERS", and MAX_WORKERS="16"
ENV_INT = env.Var.factory(parser=int)
MAX_WORKERS = Link(
src=env.Var("WORKERS_VAR", str), # the name of the target var
linker=ENV_INT, # how to create Var[int] from the name
)
try:
print(MAX_WORKERS())
except exc.VariableNotSet:
# Missing source raises exception
raise
# Now let's parse a list with worker args
# Supposing it is stored in environment variable with a name we don't know now
# We know that this variable will be also put in environment as WORKER_ARGS_VAR
# Prepare parser for our list
ENV_LIST_STR = env.SeparatedList.factory(split_sep=",", parser=str)
# Define a Link variable
OPTIONAL_WORKER_ARGS: Variable[list[str]] = Link(
src=env.Var("WORKER_ARGS_VAR", str) >> hint.Required(False),
linker=ENV_LIST_STR
)
# WORKER_ARGS_VAR is not set and not required
# Link returns None:
assert OPTIONAL_WORKER_ARGS() is None
# Now, set WORKER_ARGS_VAR to empty string
os.environ["WORKER_ARGS_VAR"] = ""
# env.SeparatedList considers empty/whitespace source as empty list
# (natively, Python str.split implementation returns a list [""] with empty string)
assert OPTIONAL_WORKER_ARGS() == []
```
Custom lists:
```python
import os
from pathlib import Path
from standby import List
from standby.env import Var
# PATH-like variables
os.environ["PLUGIN_PATHS"] = "/opt/plugins:/usr/local/plugins:/tmp/plugins"
PATH_LIST = List[Path, str, str](
src=Var("PLUGIN_PATHS", str),
splitter=lambda s: s.split(":"), # use ':' as a separator
parser=Path, # create a Path from each part
)
print(PATH_LIST()) # [PosixPath('/opt/plugins'), PosixPath('/usr/local/plugins'), PosixPath('/tmp/plugins')]
# empty string returns a list with single element as expected from Python str.split()
os.environ["PLUGIN_PATHS"] = ""
print(PATH_LIST()) # [PosixPath('.')]
```
Summary on semantics:
- If the source variable of a `List` or `Link` is missing:
* Without hints, a `VariableNotSet` error is raised;
* With `Required(False)` on the source, it returns `None`;
* With `Default([...])` on the result variable, the default is used.
## Development
This project uses a src/ layout and provides optional development dependencies for testing and type checking.
### Setup
#### Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
#### Install in editable mode with dev extras
pip install -e .[dev]
### Run tests with coverage
Run tests:
pytest
By default, pytest is configured (via pyproject.toml) to:
- discover tests in the test/ directory,
- run with branch coverage for the standby package,
- show missing lines (term-missing)
To run without coverage flags, use:
pytest -q
### Type checking
Run mypy on the package sources:
mypy
or explicitly:
mypy src/standby
### Linting
Check linting with ruff:
ruff lint
Check and fix if possible:
ruff link --fix
### Tox
Tox allows to run all checks on all supported Python versions:
tox
Make sure you have all Python versions installed before running Tox.
Recommended way is to use [pyenv](https://github.com/pyenv/pyenv).
Raw data
{
"_id": null,
"home_page": null,
"name": "standby-variables",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "configuration, environment, typing, validation, variables",
"author": "Ilia Khaustov",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/ef/0e/a8bce8184c37880558a72efb3b96dd797a178378c38e2baa1d708325c5b8/standby_variables-1.1.0.tar.gz",
"platform": null,
"description": "# standby-variables\n\n[](https://pypi.org/project/standby-variables/)\n[](https://github.com/ilia-khaustov/standby-variables/actions/workflows/)\n[](https://coveralls.io/github/ilia-khaustov/standby-variables)\n\nDynamic variables for static namespaces.\n\n## About\n\nstandby-variables is a tiny library for declaring dynamic values (like environment variables) in static, typed namespaces (like Python classes).\n\n- Treat environment configuration as first-class, typed values.\n- Compose behavior with a small and expressive API.\n- Keep your code statically analyzable: every variable has a concrete type, and mypy understands it.\n\nYou get:\n\n- Plain attributes on classes that evaluate lazily.\n- A consistent way to make variables optional or provide defaults.\n- Validation and error messages that carry useful context.\n- Building blocks to extend beyond environment variables.\n\n## Rationale\n\nMost applications mix a static structure (modules, classes) with dynamic sources (env vars, files, CLI args).\nIt\u2019s tempting to sprinkle `os.environ.get(...)` everywhere - but that dilutes typing, validation, and clarity.\n\nWith standby-variables your dynamic values become explicit and composable:\n\n- Declare what a variable is, how it\u2019s parsed, and what to do if it\u2019s missing.\n- Configure the exact behaviour right next to a variable definition:\n * use operator `>>` or chained call `given(*hints: standby.Hint)` to apply \"hints\" like Default, Required or Validated\n * use operator `|` or chained call `otherwise(*backups: standby.Variable)` to specify one or more backup variables\n- Keep strong typing across your configuration surface.\n- Use dataclass-like descriptors that work as class attributes and cache nothing implicitly.\n\nThe result is readable configuration code that feels like constants, but evaluates at runtime.\n\n## Basic usage and syntax\n\n- Every dynamic value is a `Variable[T]` for some type `T`.\n- You can \"extract\" the runtime value by either:\n * Calling the variable: `var()` (may return `None` if defined as not required),\n * Or forcing the value: `var.value()` (never returns `None`; raises if unavailable).\n- String representations include context, and exceptions propagate that context upward, which makes debugging easier.\n\n### Hints\n\n- `Default(value)` returns the default when the source is missing.\n- `Required(True)` means the variable must be present (default behaviour).\n- `Required(False)` means \"optional\": `var()` can return `None`.\n- `Validated(predicate, raises=True)` ensures values pass a check.\n- `Validated(predicate, raises=False)` will \"nullify\" invalid values and return `None` instead of raising an exception.\n\nExample:\n\n```python\nimport os\nfrom standby import Const\nfrom standby.hint import Default, Required, Validated\nfrom standby import env\n\n# simulate environment\nos.environ.setdefault(\"APP_PORT\", \"8080\")\n\ndef parse_bool(s: str) -> bool:\n return s.strip() in {\"1\", \"true\", \"yes\", \"on\", \"True\"}\n\nclass Settings:\n # A required int, with a default when missing\n PORT: int =~ env.Var(\"APP_PORT\", int).given(Default(8000))\n\n # Optional bool: returns None if missing\n DEBUG: bool | None =~ (env.Var(\"APP_DEBUG\", parse_bool) >> Required(False))\n\n # A purely static value with validation\n TIMEOUT: int =~ Const(10).given(Validated(lambda v: v > 0))\n\nprint(Settings.PORT) # 8080 (from env), defaults to 8000 if unset\nprint(Settings.DEBUG) # None if APP_DEBUG is missing; True/False otherwise\nprint(Settings.TIMEOUT) # 10\n```\n\nFallback to \"backup\" variables:\n\n```python\nfrom standby import env, Const\n\nclass Settings:\n class API:\n # Prefer ENV var; if missing, use another Variable as a fallback option\n URL: str =~ (env.Var(\"API_URL\", str) | env.Var(\"FALLBACK_API_URL\", str))\n KEY: str =~ env.Var(\"API_KEY\", str).otherwise(env.Var(\"FALLBACK_API_KEY\", str))\n \nprint(Settings.API.URL) # Returns first ENV value which is present\n```\n\nForcing with `.value()`:\n\n```python\nfrom standby import env\nfrom standby.exc import VariableNotSet\n\ntry:\n must_have_port = env.Var(\"MUST_HAVE_PORT\", int).value()\nexcept VariableNotSet as e:\n # Rich context in e.args for easier debugging\n print(\"Missing env:\", e.args)\n```\n\n## Type safety\n\nEvery variable is parameterized by its type, and mypy can verify usage end-to-end.\n\nYou might have noticed a weird operator `=~` used in example of `Settings` class.\n\nIn fact, it is two distinct operators: `=` and `~` joined together due to subjective preference.\nIt is possible to have `~` attached to Variable definition like:\n\n URL: str = ~(...) # this is why operator expressions are inside parenthesis\n\nThe role of this operator is to instruct type checker that nothing wrong happens when we define `Variable[T]` as its\nwrapped type `T`. When you access a variable via parent class/instance, Python invokes `__get__()` descriptor \nmethod which is implemented for all basic primitives in `standby`. So, reading the value `T` \"dynamically\" happens just \nby accessing its container `Variable[T]` which is initialized together with its parent class. You can think about it as \na special case of \"class property\".\n\n```python\nfrom standby import Const\nfrom standby.hint import Default, Required, Validated\nfrom standby import env\n\ndef parse_bool(s: str) -> bool:\n return s.strip() in {\"1\", \"true\", \"yes\", \"on\", \"True\"}\n\nclass Settings:\n # Expression has real type Variable[int] but defined inside a class as just int \n PORT: int =~ env.Var(\"APP_PORT\", int).given(Default(8000))\n\n # Expression has real type Variable[bool] but is set to \"bool | None\" because it's hinted with Required(false)\n # Currently, unary operator casting to \"T | None\" is not implemented\n # Type checker (mypy) allows result of cast(T, ...) to be assigned a \"T | None\" type\n DEBUG: bool | None =~ (env.Var(\"APP_DEBUG\", bool) >> Required(False))\n```\n\n\n## Environment variables\n\nThe `standby.env` module provides handy tools to read from `os.environ`.\n\n- `env.Var[T](name, parser)` reads `os.environ[name]` and parses it into type `T`.\n- `env.SeparatedList[T]` takes a source `Variable[str]`, splits by a given `split_sep` (`,` by default), and parses each element.\n- `env.Ref[T]` takes a source `Variable[str]` and uses its value to find another environment variable with that name.\n\n```python\nimport base64\nfrom standby import Const\nfrom standby.hint import Default, Required\nfrom standby import env\n\n# Required int with a default\nPOOL_SIZE = env.Var[int](\"DB_POOL_SIZE\", int) >> Default(5)\n\n# Optional boolean\nUSE_CACHE = env.Var[bool](\"USE_CACHE\", lambda s: s == \"1\") >> Required(False)\n\n# ALLOWED_HOSTS=\"example.com, api.example.com, localhost\"\nALLOWED_HOSTS = env.SeparatedList[str](\n src=env.Var(\"ALLOWED_HOSTS\", str), # read raw string from env\n split_sep=\",\",\n parser=lambda s: s.strip(), # trim each piece\n)\n\n# Default to [\"localhost\"] if the variable is missing\nALLOWED_HOSTS_WITH_DEFAULT = ALLOWED_HOSTS >> Default([\"localhost\"])\n\n# If ALLOWED_HOSTS is missing, and you want \"optional list\" semantics:\nOPTIONAL_HOSTS = ALLOWED_HOSTS >> Required(False) # .__call__ returns None\n\n# SECRET_ENV_VAR=\"<AUTO_GENERATED_VARIABLE_WITH_SECRET_VALUE_IN_BASE64>\"\nSECRET = env.Ref(\n src=env.Var(\"SECRET_ENV_VAR\", str), # taking the secret var name here\n parser=base64.b64decode, # decoding it to bytes\n)\n\ndecoded_secret: bytes = SECRET()\n\n```\n\n## Link and List customization\n\n`env.Ref` and `env.SeparatedList` are built from `standby` primitives: `Link` and `List`.\nLinks let one variable refer to another variable\u2019s name.\nThis is handy for indirection (think: \"the name of the variable that contains the key\").\n\n- `Link[T, S]`: a generic link-variable where:\n * `src` yields some `S`;\n * `linker` maps that `S` to another `Variable[T]`.\n- `List[T, P, S]`: a generic list-variable where:\n * `src` yields some `S`;\n * `S` is split in parts of `P` by a `splitter` function given as keyword arg;\n * Each `P` part is parsed to `T` by a `parser` function given as keyword arg;\n * `T` items are used to create a new `Variable[list[T]]`.\n\nCustom link can be used like this:\n\n```python\nimport os\nfrom standby import Link, env, exc, hint, Variable\n\n# Suppose \"WORKERS_VAR\" contains the name of another env var that holds an int.\n# e.g. WORKERS_VAR=\"MAX_WORKERS\", and MAX_WORKERS=\"16\"\nENV_INT = env.Var.factory(parser=int)\nMAX_WORKERS = Link(\n src=env.Var(\"WORKERS_VAR\", str), # the name of the target var\n linker=ENV_INT, # how to create Var[int] from the name\n)\ntry:\n print(MAX_WORKERS())\nexcept exc.VariableNotSet:\n # Missing source raises exception\n raise\n\n# Now let's parse a list with worker args\n# Supposing it is stored in environment variable with a name we don't know now\n# We know that this variable will be also put in environment as WORKER_ARGS_VAR\n\n# Prepare parser for our list\nENV_LIST_STR = env.SeparatedList.factory(split_sep=\",\", parser=str)\n\n# Define a Link variable\nOPTIONAL_WORKER_ARGS: Variable[list[str]] = Link(\n src=env.Var(\"WORKER_ARGS_VAR\", str) >> hint.Required(False),\n linker=ENV_LIST_STR\n)\n\n# WORKER_ARGS_VAR is not set and not required\n# Link returns None:\nassert OPTIONAL_WORKER_ARGS() is None\n\n# Now, set WORKER_ARGS_VAR to empty string\nos.environ[\"WORKER_ARGS_VAR\"] = \"\"\n\n# env.SeparatedList considers empty/whitespace source as empty list\n# (natively, Python str.split implementation returns a list [\"\"] with empty string)\nassert OPTIONAL_WORKER_ARGS() == []\n```\n\nCustom lists:\n\n```python\nimport os\nfrom pathlib import Path\nfrom standby import List\nfrom standby.env import Var\n\n# PATH-like variables\nos.environ[\"PLUGIN_PATHS\"] = \"/opt/plugins:/usr/local/plugins:/tmp/plugins\"\n\nPATH_LIST = List[Path, str, str](\n src=Var(\"PLUGIN_PATHS\", str),\n splitter=lambda s: s.split(\":\"), # use ':' as a separator\n parser=Path, # create a Path from each part\n)\n\nprint(PATH_LIST()) # [PosixPath('/opt/plugins'), PosixPath('/usr/local/plugins'), PosixPath('/tmp/plugins')]\n\n# empty string returns a list with single element as expected from Python str.split() \nos.environ[\"PLUGIN_PATHS\"] = \"\"\n\nprint(PATH_LIST()) # [PosixPath('.')]\n\n\n```\n\nSummary on semantics:\n\n- If the source variable of a `List` or `Link` is missing:\n * Without hints, a `VariableNotSet` error is raised;\n * With `Required(False)` on the source, it returns `None`;\n * With `Default([...])` on the result variable, the default is used.\n\n## Development\n\nThis project uses a src/ layout and provides optional development dependencies for testing and type checking.\n\n### Setup\n\n#### Create and activate a virtual environment\n \n python -m venv .venv\n source .venv/bin/activate # On Windows: .venv\\Scripts\\activate\n\n#### Install in editable mode with dev extras\n\n pip install -e .[dev]\n\n### Run tests with coverage\n\nRun tests:\n\n pytest\n\nBy default, pytest is configured (via pyproject.toml) to:\n\n- discover tests in the test/ directory,\n- run with branch coverage for the standby package,\n- show missing lines (term-missing)\n\nTo run without coverage flags, use:\n\n pytest -q\n\n### Type checking\n\nRun mypy on the package sources:\n\n mypy\n\nor explicitly:\n\n mypy src/standby\n\n\n### Linting\n\nCheck linting with ruff:\n\n ruff lint\n\nCheck and fix if possible:\n\n ruff link --fix\n\n\n### Tox\n\nTox allows to run all checks on all supported Python versions:\n\n tox\n\nMake sure you have all Python versions installed before running Tox.\n\nRecommended way is to use [pyenv](https://github.com/pyenv/pyenv).",
"bugtrack_url": null,
"license": "MIT License\n \n Copyright (c) 2025 Ilia Khaustov\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.",
"summary": "Dynamic variables for static namespaces",
"version": "1.1.0",
"project_urls": {
"Homepage": "https://github.com/ilia-khaustov/standby-variables",
"Issues": "https://github.com/ilia-khaustov/standby-variables/issues",
"Repository": "https://github.com/ilia-khaustov/standby-variables"
},
"split_keywords": [
"configuration",
" environment",
" typing",
" validation",
" variables"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "909bbc17eb7d123e9b45ca1b80ae99ba399bb1942832d453b1fa8459c189048a",
"md5": "09f4d9880e2ed6564c1a4c367255e610",
"sha256": "1b2194bd136b75b748cacf2cbd419ca4ab21da9408dea88f40ad124291bc89a8"
},
"downloads": -1,
"filename": "standby_variables-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "09f4d9880e2ed6564c1a4c367255e610",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 10720,
"upload_time": "2025-10-09T14:22:27",
"upload_time_iso_8601": "2025-10-09T14:22:27.941025Z",
"url": "https://files.pythonhosted.org/packages/90/9b/bc17eb7d123e9b45ca1b80ae99ba399bb1942832d453b1fa8459c189048a/standby_variables-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ef0ea8bce8184c37880558a72efb3b96dd797a178378c38e2baa1d708325c5b8",
"md5": "ea9d3e36f3a8ef5f7733251b9bfc4fae",
"sha256": "bc6942d4ded09cc9c136e621f426d3a57af846db1169c63c47e12e88cbabd463"
},
"downloads": -1,
"filename": "standby_variables-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "ea9d3e36f3a8ef5f7733251b9bfc4fae",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 8832,
"upload_time": "2025-10-09T14:22:28",
"upload_time_iso_8601": "2025-10-09T14:22:28.953339Z",
"url": "https://files.pythonhosted.org/packages/ef/0e/a8bce8184c37880558a72efb3b96dd797a178378c38e2baa1d708325c5b8/standby_variables-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-09 14:22:28",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ilia-khaustov",
"github_project": "standby-variables",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "standby-variables"
}