load-environ-typed


Nameload-environ-typed JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryYou define a class, we load it up from environment in a type safe way
upload_time2024-03-29 15:46:54
maintainerNone
docs_urlNone
authorJohan B.W. de Vries
requires_python>=3.8
licenseCopyright (c) 2023, Johan B.W. de Vries 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
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # load-environ-typed

This library lets you declare a NamedTuple or dataclass with
field types, and gives you a function that will `load` the
values from the environment, validating your types for you.

## Getting started

```python
from load_environ_typed import load

from dataclasses import dataclass

@dataclass
class MyEnviron:
	db_host: str
	db_port: int

environ = load(MyEnviron)
```

## FAQ

### What types can I use? What about custom types?

By default, any type that takes a single string in it's constructor can be used
as type. Think of `int`, `str`, `float`, `pathlib.Path`, etc.

We've added default loaders for the types below:

- `bool` - "true" or "false", case insenstive
- `datetime.date` - Using `datetime.date.fromisoformat`
- `datetime.time` - Using `datetime.time.fromisoformat`
- `datetime.datetime` - Using `datetime.datetime.fromisoformat`

### How are fields matched to enviroment variables?

The loader assumes the names are the same, except that the class fields
are lowercase, and the environment fields are uppercase. If you have
different or more complicated rules, you can pass a name conversion
function via `field_name_to_var_name`.

```python
@dataclass
class MyEnviron:
    iso_date: datetime.date

environ = sut.load(MyEnviron, environ={
    'ISO_DATE': '2021-01-01',
})
```

### Can values be optional?

Certainly:

```python
@dataclass
class MyEnviron:
	DB_HOST: Optional[str]
```

However:

- empty string and "none" (case insensitive) count as `None`
- defaults take precedence over optionality

### What if my type cannot take a string in its constructor?

You can pass so-called loader functions. These take in a string, and are
expected to return a value of the given type, or raise a `ValueError` when
the given string is not valid for the given type. This is also the
mechanism that we use to support standard Python types such `datetime.date`,
which is shown below.

```python
@dataclass
class MyEnviron:
    ISO_DATE: datetime.date

environ = sut.load(MyEnviron, environ={
    'ISO_DATE': '2021-01-01',
}, loaders={
    'ISO_DATE': datetime.date.fromisoformat,
    # or, if you want ALL `date`s to use this loader:
    datetime.date: datetime.date.fromisoformat,
})
```

NOTE: date has a default loader, so you don't need to do this for `date`.

### Can I use Unions?

You can, but you need to use a default loader, as you need some
way to distinguish between the types, and there is no general
way to do so, at least not without enforcing our way of working on you.

### How do I work with default values?

If you want default values, it's probably best to have use a dataclass
with `kw_only=True`, as otherwise you have to order your variables based
on whether there's a default or not.

```python
@dataclass(kw_only=True)
class MyEnviron:
	DB_HOST: str
	DB_PORT: int = 3306
```

For most types, you can simply set the default value as you're used to
with dataclasses. However, you may not want to instantiate an (expensive)
property as default. In those cases, you can pass defaults along using
the `defaults` argument.

```python
@dataclass
class MyEnviron:
	VAR: SomeExpensiveClass

environ = load(MyEnviron, defaults={
	'VAR': '#!serialized.data!#',
})
```

*NOTE*: `kw_only` requires Python3.10 or higher. Below 3.10, you can use
the `defaults` argument or order your variables. Similarly, if you plan
on solely using the `defaults` argument, you don't need `kw_only`.

### Load returns an instance. What if I want a global?

Since `environ` is available at startup, and doesn't change, it's perfectly
valid to just instantiate a global variable. You should probably use a
frozen dataclass for this.

```python
@dataclass(frozen=True)
class MyEnviron:
	DB_HOST: str
	DB_PORT: int

ENVIRON = load(MyEnviron)
```

### What if there's an issue with the default loaders?

First, the loaders you pass will be taken before using the default loaders.

Second, if you have more structural issues with the default loaders, simply
pass `use_default_loaders=False`.

## Contributing

Check out this repo, and then use the following steps to test it:

```sh
python3 -m venv venv
venv/bin/pip install -r requirements-dev.txt
make test
```

## Deploying

First, update pyproject.toml with the new version number, and commit that.

Then:

```sh
rm -f dist/* # Clean old build files
venv/bin/python -m build
venv/bin/python -m twine upload dist/*
```

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "load-environ-typed",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": "Johan B.W. de Vries",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/3b/4a/9384f4fce4bf216b232a70c8fea72ec235897bd92bc6867cb44bb83d2c71/load_environ_typed-0.3.0.tar.gz",
    "platform": null,
    "description": "# load-environ-typed\n\nThis library lets you declare a NamedTuple or dataclass with\nfield types, and gives you a function that will `load` the\nvalues from the environment, validating your types for you.\n\n## Getting started\n\n```python\nfrom load_environ_typed import load\n\nfrom dataclasses import dataclass\n\n@dataclass\nclass MyEnviron:\n\tdb_host: str\n\tdb_port: int\n\nenviron = load(MyEnviron)\n```\n\n## FAQ\n\n### What types can I use? What about custom types?\n\nBy default, any type that takes a single string in it's constructor can be used\nas type. Think of `int`, `str`, `float`, `pathlib.Path`, etc.\n\nWe've added default loaders for the types below:\n\n- `bool` - \"true\" or \"false\", case insenstive\n- `datetime.date` - Using `datetime.date.fromisoformat`\n- `datetime.time` - Using `datetime.time.fromisoformat`\n- `datetime.datetime` - Using `datetime.datetime.fromisoformat`\n\n### How are fields matched to enviroment variables?\n\nThe loader assumes the names are the same, except that the class fields\nare lowercase, and the environment fields are uppercase. If you have\ndifferent or more complicated rules, you can pass a name conversion\nfunction via `field_name_to_var_name`.\n\n```python\n@dataclass\nclass MyEnviron:\n    iso_date: datetime.date\n\nenviron = sut.load(MyEnviron, environ={\n    'ISO_DATE': '2021-01-01',\n})\n```\n\n### Can values be optional?\n\nCertainly:\n\n```python\n@dataclass\nclass MyEnviron:\n\tDB_HOST: Optional[str]\n```\n\nHowever:\n\n- empty string and \"none\" (case insensitive) count as `None`\n- defaults take precedence over optionality\n\n### What if my type cannot take a string in its constructor?\n\nYou can pass so-called loader functions. These take in a string, and are\nexpected to return a value of the given type, or raise a `ValueError` when\nthe given string is not valid for the given type. This is also the\nmechanism that we use to support standard Python types such `datetime.date`,\nwhich is shown below.\n\n```python\n@dataclass\nclass MyEnviron:\n    ISO_DATE: datetime.date\n\nenviron = sut.load(MyEnviron, environ={\n    'ISO_DATE': '2021-01-01',\n}, loaders={\n    'ISO_DATE': datetime.date.fromisoformat,\n    # or, if you want ALL `date`s to use this loader:\n    datetime.date: datetime.date.fromisoformat,\n})\n```\n\nNOTE: date has a default loader, so you don't need to do this for `date`.\n\n### Can I use Unions?\n\nYou can, but you need to use a default loader, as you need some\nway to distinguish between the types, and there is no general\nway to do so, at least not without enforcing our way of working on you.\n\n### How do I work with default values?\n\nIf you want default values, it's probably best to have use a dataclass\nwith `kw_only=True`, as otherwise you have to order your variables based\non whether there's a default or not.\n\n```python\n@dataclass(kw_only=True)\nclass MyEnviron:\n\tDB_HOST: str\n\tDB_PORT: int = 3306\n```\n\nFor most types, you can simply set the default value as you're used to\nwith dataclasses. However, you may not want to instantiate an (expensive)\nproperty as default. In those cases, you can pass defaults along using\nthe `defaults` argument.\n\n```python\n@dataclass\nclass MyEnviron:\n\tVAR: SomeExpensiveClass\n\nenviron = load(MyEnviron, defaults={\n\t'VAR': '#!serialized.data!#',\n})\n```\n\n*NOTE*: `kw_only` requires Python3.10 or higher. Below 3.10, you can use\nthe `defaults` argument or order your variables. Similarly, if you plan\non solely using the `defaults` argument, you don't need `kw_only`.\n\n### Load returns an instance. What if I want a global?\n\nSince `environ` is available at startup, and doesn't change, it's perfectly\nvalid to just instantiate a global variable. You should probably use a\nfrozen dataclass for this.\n\n```python\n@dataclass(frozen=True)\nclass MyEnviron:\n\tDB_HOST: str\n\tDB_PORT: int\n\nENVIRON = load(MyEnviron)\n```\n\n### What if there's an issue with the default loaders?\n\nFirst, the loaders you pass will be taken before using the default loaders.\n\nSecond, if you have more structural issues with the default loaders, simply\npass `use_default_loaders=False`.\n\n## Contributing\n\nCheck out this repo, and then use the following steps to test it:\n\n```sh\npython3 -m venv venv\nvenv/bin/pip install -r requirements-dev.txt\nmake test\n```\n\n## Deploying\n\nFirst, update pyproject.toml with the new version number, and commit that.\n\nThen:\n\n```sh\nrm -f dist/* # Clean old build files\nvenv/bin/python -m build\nvenv/bin/python -m twine upload dist/*\n```\n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2023, Johan B.W. de Vries  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.",
    "summary": "You define a class, we load it up from environment in a type safe way",
    "version": "0.3.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/jbwdevries/python-load-environ-typed/issues",
        "Homepage": "https://github.com/jbwdevries/python-load-environ-typed"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9d26d3061192e085e2e52cdd54b669e0b9eb673f5a96a46244a69d3ecdbdeb31",
                "md5": "7e0930668a0c298dbb98ea8a796a7e71",
                "sha256": "200f5fd65ab479c234ce921bb9c23012259dd3171881a301476b5c3976d42ec4"
            },
            "downloads": -1,
            "filename": "load_environ_typed-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7e0930668a0c298dbb98ea8a796a7e71",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 9236,
            "upload_time": "2024-03-29T15:46:53",
            "upload_time_iso_8601": "2024-03-29T15:46:53.736138Z",
            "url": "https://files.pythonhosted.org/packages/9d/26/d3061192e085e2e52cdd54b669e0b9eb673f5a96a46244a69d3ecdbdeb31/load_environ_typed-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3b4a9384f4fce4bf216b232a70c8fea72ec235897bd92bc6867cb44bb83d2c71",
                "md5": "a1086d0070d7ac33464c34651fcb75e6",
                "sha256": "11d315ca1ef7f9c4be149490168516e6b01928da56a18151843fdcbfa8582a24"
            },
            "downloads": -1,
            "filename": "load_environ_typed-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "a1086d0070d7ac33464c34651fcb75e6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 12502,
            "upload_time": "2024-03-29T15:46:54",
            "upload_time_iso_8601": "2024-03-29T15:46:54.922922Z",
            "url": "https://files.pythonhosted.org/packages/3b/4a/9384f4fce4bf216b232a70c8fea72ec235897bd92bc6867cb44bb83d2c71/load_environ_typed-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-29 15:46:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jbwdevries",
    "github_project": "python-load-environ-typed",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "load-environ-typed"
}
        
Elapsed time: 0.29224s