dataclass-settings


Namedataclass-settings JSON
Version 0.3.1 PyPI version JSON
download
home_pagehttps://github.com/dancardin/dataclass-settings
SummaryDeclarative dataclass settings.
upload_time2024-04-30 15:12:30
maintainerNone
docs_urlNone
authorDanCardin
requires_python<4,>=3.8
licenseApache-2.0
keywords dataclass attrs pydantic settings
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # dataclass-settings

[![Actions Status](https://github.com/DanCardin/dataclass-settings/actions/workflows/test.yml/badge.svg)](https://github.com/dancardin/dataclass-settings/actions)
[![Coverage Status](https://coveralls.io/repos/github/DanCardin/dataclass-settings/badge.svg?branch=main)](https://coveralls.io/github/DanCardin/dataclass-settings?branch=main)
[![Documentation Status](https://readthedocs.org/projects/dataclass-settings/badge/?version=latest)](https://dataclass-settings.readthedocs.io/en/latest/?badge=latest)

- [Full documentation here](https://dataclass-settings.readthedocs.io/en/latest/).
- [Bundled Loaders](https://dataclass-settings.readthedocs.io/en/latest/loaders.html).

`dataclass-settings` intends to work with any
[PEP-681](https://peps.python.org/pep-0681/)-compliant dataclass-like object,
including but not limited to:

- [Pydantic models](https://pydantic-docs.helpmanual.io/) (v1/v2),
- [dataclasses](https://docs.python.org/3/library/dataclasses.html)
- [attrs classes](https://www.attrs.org/en/stable/).

`dataclass-settings` owes its existence
[pydantic-settings](https://github.com/pydantic/pydantic-settings), in that
pydantic-settings will be a benchmark for `dataclass-settings`'s featureset.
However it was bourne out of frustration with pydantic-setting's approach to
implementing that featureset.

## Example

```python
from __future__ import annotations
from dataclass_settings import load_settings, Env, Secret
from pydantic import BaseModel


class Example(BaseModel):
    env: Annotated[str, Env("ENVIRONMENT")] = "local"
    dsn: Annotated[str, Env("DSN"), Secret('dsn')] = "dsn://"

    sub_config: SubConfig


class SubConfig(BaseModel):
    nested: Annotated[int, Env("NESTED")] = "4"


example: Example = load_settings(Example)

# or, if you want `nested` to be `SUB_CONFIG_NESTED`
example: Example = load_settings(Example, nested_delimiter='_')
```

## vs Pydantic Settings

### Simplicity

- `pydantic-settings` alters how you go about defining your normal pydantic
  models. You need to switch (some of the) base classes, you need to configure
  the magical `model_config = SettingsConfigDict(...)` object, etc.

  The model becomes inherently entangled with the settings-loading library.

- `dataclass-settings` attaches targeted Annotations metadata to a vanilla
  pydantic model. You can **choose** to not use `load_settings` (for example, in
  tests), and construct the model instance however you'd like.

### Clarity

- `pydantic-settings` makes it really, really difficult to intuit what the
  concrete environment varibale that's going to be loaded for a given field is
  **actually** going to be. Based on my own experience, and from perusing their
  issue tracker, it seems like this is not an uncommon experience.

  The combination of field name, `SettingsConfigDict` settings, casing,
  `alias`/`validation_alias`/`serialization_alias`, and relative position of the
  env var in the greater config all contribute to it being a **task** to deduce
  which concrete name will be used when loading.

- `dataclass-settings` by **default** requires an explicit, concrete name, which
  maps directly to the value being loaded (`Env('FOO')` loads `FOO`, for sure!)

  If you want to opt into a less explcict, more inferred setup (like
  pydantic-settings), you can do so by utilizing the `nested_delimiter='_'` and
  `infer_name=True` arguments.

### Typing

- `pydantic-settings` does not play **super** well with type checkers,
  necessitating the use of a mypy plugin for it to not emit type errors into
  user code.

  The code recommended in their documentation for namespacing settings, looks
  like:

  ```python
  class Settings(BaseSettings):
      more_settings: SubModel = SubModel()
  ```

  This only type-checks with mypy (after using the plugin), but not
  pyright/pylance. Additionally, this **actually** evaluates the `SubModel`
  constructor during module parsing!

  These issues seem(?) to be inherent to the strategy of subclassing
  `BaseModel`, and building in its logic into the object construction process

- `dataclass-settings` sidesteps this problem by decoupling the definition of
  the settings from the loading of settings.

  As such, you're more able to define the model, exactly as you would have with
  vanilla pydantic:

  ```python
  class Settings(BaseModel):
      more_settings: SubModel
  ```

  Internally, the `load_settings` function handles the work of constructing the
  requisite input structure pydantic expects to construct the whole object tree.

### Compatibility

- `pydantic-settings`'s `BaseSettings` inherits from pydantic's `BaseModel`. And
  thus can only function against pydantic models, as the name would imply.

- `dataclass-settings`'s primary entrypoint is a function that accepts a
  supportable type. As such, it can theoretically support any type that has a
  well defined object structure, like all of `pydantic`, `dataclasses`, and
  `attrs`.

  Practically, `pydantic` has the most robust system for parsing/validating a
  json-like structure into the models, so it's probably to be the most flexible
  anyways. But for many simple cases, particuarly those without nesting, or that
  only deal in simple types (like int, float, str, etc); then dataclasses/attrs
  can certainly provide a similar experience.

### Flexibility

- At time of writing, `pydantic-settings`'s strategy around "loaders", i.e.
  supportable settings sources is relatively inflexible. Their issue tracker
  contains a decent number of requests for a more flexible way of defining
  settings priorities among different loaders, or even using different settings
  from within a loader.

  This, at least, doesn't seem to be an inherent issue to the library
  necessarily. Just that at present, their API appears to try to reuse
  pydantic's `Field` and `alias` mechanisms to infer the settings for all
  loaders.

- `dataclass-settings` instead annotates each field individually, with the
  loaders that field should use. That means you can have different priorities
  (or entirely different loaders!) per field.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/dancardin/dataclass-settings",
    "name": "dataclass-settings",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4,>=3.8",
    "maintainer_email": null,
    "keywords": "dataclass, attrs, pydantic, settings",
    "author": "DanCardin",
    "author_email": "ddcardin@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/ac/df/c5fbddaa38954217ce5ffd0926090a48af446bc1c50267eee563fc97936d/dataclass_settings-0.3.1.tar.gz",
    "platform": null,
    "description": "# dataclass-settings\n\n[![Actions Status](https://github.com/DanCardin/dataclass-settings/actions/workflows/test.yml/badge.svg)](https://github.com/dancardin/dataclass-settings/actions)\n[![Coverage Status](https://coveralls.io/repos/github/DanCardin/dataclass-settings/badge.svg?branch=main)](https://coveralls.io/github/DanCardin/dataclass-settings?branch=main)\n[![Documentation Status](https://readthedocs.org/projects/dataclass-settings/badge/?version=latest)](https://dataclass-settings.readthedocs.io/en/latest/?badge=latest)\n\n- [Full documentation here](https://dataclass-settings.readthedocs.io/en/latest/).\n- [Bundled Loaders](https://dataclass-settings.readthedocs.io/en/latest/loaders.html).\n\n`dataclass-settings` intends to work with any\n[PEP-681](https://peps.python.org/pep-0681/)-compliant dataclass-like object,\nincluding but not limited to:\n\n- [Pydantic models](https://pydantic-docs.helpmanual.io/) (v1/v2),\n- [dataclasses](https://docs.python.org/3/library/dataclasses.html)\n- [attrs classes](https://www.attrs.org/en/stable/).\n\n`dataclass-settings` owes its existence\n[pydantic-settings](https://github.com/pydantic/pydantic-settings), in that\npydantic-settings will be a benchmark for `dataclass-settings`'s featureset.\nHowever it was bourne out of frustration with pydantic-setting's approach to\nimplementing that featureset.\n\n## Example\n\n```python\nfrom __future__ import annotations\nfrom dataclass_settings import load_settings, Env, Secret\nfrom pydantic import BaseModel\n\n\nclass Example(BaseModel):\n    env: Annotated[str, Env(\"ENVIRONMENT\")] = \"local\"\n    dsn: Annotated[str, Env(\"DSN\"), Secret('dsn')] = \"dsn://\"\n\n    sub_config: SubConfig\n\n\nclass SubConfig(BaseModel):\n    nested: Annotated[int, Env(\"NESTED\")] = \"4\"\n\n\nexample: Example = load_settings(Example)\n\n# or, if you want `nested` to be `SUB_CONFIG_NESTED`\nexample: Example = load_settings(Example, nested_delimiter='_')\n```\n\n## vs Pydantic Settings\n\n### Simplicity\n\n- `pydantic-settings` alters how you go about defining your normal pydantic\n  models. You need to switch (some of the) base classes, you need to configure\n  the magical `model_config = SettingsConfigDict(...)` object, etc.\n\n  The model becomes inherently entangled with the settings-loading library.\n\n- `dataclass-settings` attaches targeted Annotations metadata to a vanilla\n  pydantic model. You can **choose** to not use `load_settings` (for example, in\n  tests), and construct the model instance however you'd like.\n\n### Clarity\n\n- `pydantic-settings` makes it really, really difficult to intuit what the\n  concrete environment varibale that's going to be loaded for a given field is\n  **actually** going to be. Based on my own experience, and from perusing their\n  issue tracker, it seems like this is not an uncommon experience.\n\n  The combination of field name, `SettingsConfigDict` settings, casing,\n  `alias`/`validation_alias`/`serialization_alias`, and relative position of the\n  env var in the greater config all contribute to it being a **task** to deduce\n  which concrete name will be used when loading.\n\n- `dataclass-settings` by **default** requires an explicit, concrete name, which\n  maps directly to the value being loaded (`Env('FOO')` loads `FOO`, for sure!)\n\n  If you want to opt into a less explcict, more inferred setup (like\n  pydantic-settings), you can do so by utilizing the `nested_delimiter='_'` and\n  `infer_name=True` arguments.\n\n### Typing\n\n- `pydantic-settings` does not play **super** well with type checkers,\n  necessitating the use of a mypy plugin for it to not emit type errors into\n  user code.\n\n  The code recommended in their documentation for namespacing settings, looks\n  like:\n\n  ```python\n  class Settings(BaseSettings):\n      more_settings: SubModel = SubModel()\n  ```\n\n  This only type-checks with mypy (after using the plugin), but not\n  pyright/pylance. Additionally, this **actually** evaluates the `SubModel`\n  constructor during module parsing!\n\n  These issues seem(?) to be inherent to the strategy of subclassing\n  `BaseModel`, and building in its logic into the object construction process\n\n- `dataclass-settings` sidesteps this problem by decoupling the definition of\n  the settings from the loading of settings.\n\n  As such, you're more able to define the model, exactly as you would have with\n  vanilla pydantic:\n\n  ```python\n  class Settings(BaseModel):\n      more_settings: SubModel\n  ```\n\n  Internally, the `load_settings` function handles the work of constructing the\n  requisite input structure pydantic expects to construct the whole object tree.\n\n### Compatibility\n\n- `pydantic-settings`'s `BaseSettings` inherits from pydantic's `BaseModel`. And\n  thus can only function against pydantic models, as the name would imply.\n\n- `dataclass-settings`'s primary entrypoint is a function that accepts a\n  supportable type. As such, it can theoretically support any type that has a\n  well defined object structure, like all of `pydantic`, `dataclasses`, and\n  `attrs`.\n\n  Practically, `pydantic` has the most robust system for parsing/validating a\n  json-like structure into the models, so it's probably to be the most flexible\n  anyways. But for many simple cases, particuarly those without nesting, or that\n  only deal in simple types (like int, float, str, etc); then dataclasses/attrs\n  can certainly provide a similar experience.\n\n### Flexibility\n\n- At time of writing, `pydantic-settings`'s strategy around \"loaders\", i.e.\n  supportable settings sources is relatively inflexible. Their issue tracker\n  contains a decent number of requests for a more flexible way of defining\n  settings priorities among different loaders, or even using different settings\n  from within a loader.\n\n  This, at least, doesn't seem to be an inherent issue to the library\n  necessarily. Just that at present, their API appears to try to reuse\n  pydantic's `Field` and `alias` mechanisms to infer the settings for all\n  loaders.\n\n- `dataclass-settings` instead annotates each field individually, with the\n  loaders that field should use. That means you can have different priorities\n  (or entirely different loaders!) per field.\n\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Declarative dataclass settings.",
    "version": "0.3.1",
    "project_urls": {
        "Homepage": "https://github.com/dancardin/dataclass-settings",
        "Repository": "https://github.com/dancardin/dataclass-settings"
    },
    "split_keywords": [
        "dataclass",
        " attrs",
        " pydantic",
        " settings"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7aa0edab8f058fd24c04c49d5f6ea9c7093fa325385361df13f1f45fbdc51cb6",
                "md5": "a4c353326ed083734f6030e5c6c06a8b",
                "sha256": "983d0be65f0af8043378456ad10a756327ea4ab5e5ec224052e0e9b317172a8f"
            },
            "downloads": -1,
            "filename": "dataclass_settings-0.3.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a4c353326ed083734f6030e5c6c06a8b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4,>=3.8",
            "size": 15956,
            "upload_time": "2024-04-30T15:12:28",
            "upload_time_iso_8601": "2024-04-30T15:12:28.468336Z",
            "url": "https://files.pythonhosted.org/packages/7a/a0/edab8f058fd24c04c49d5f6ea9c7093fa325385361df13f1f45fbdc51cb6/dataclass_settings-0.3.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "acdfc5fbddaa38954217ce5ffd0926090a48af446bc1c50267eee563fc97936d",
                "md5": "9d38e7e29b8ee3f2d55660cfbcf05fd8",
                "sha256": "b5d7057d06a256f239fe9ca4798e29ede4e3a1bf9f4a873e5e011d297dd0c6a8"
            },
            "downloads": -1,
            "filename": "dataclass_settings-0.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "9d38e7e29b8ee3f2d55660cfbcf05fd8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4,>=3.8",
            "size": 14825,
            "upload_time": "2024-04-30T15:12:30",
            "upload_time_iso_8601": "2024-04-30T15:12:30.094031Z",
            "url": "https://files.pythonhosted.org/packages/ac/df/c5fbddaa38954217ce5ffd0926090a48af446bc1c50267eee563fc97936d/dataclass_settings-0.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-30 15:12:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dancardin",
    "github_project": "dataclass-settings",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "dataclass-settings"
}
        
Elapsed time: 0.35375s