marsh


Namemarsh JSON
Version 0.2.6 PyPI version JSON
download
home_pagehttps://github.com/adriansahlman/marsh
Summary(Un)marshaling framework.
upload_time2023-01-02 20:53:18
maintainer
docs_urlNone
authorAdrian Sahlman
requires_python>=3.8
licenseMIT
keywords marshal unmarshal configuration
VCS
bugtrack_url
requirements typing-extensions omegaconf docstring-parser dateparser mypy flake8 add-trailing-comma pytest types-PyYAML types-setuptools types-typing-extensions types-dateparser
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
# Marsh

(Un)marshaling library for objects in Python 3.8+.

Tested on Ubuntu, Windows and Mac OSX for Python 3.8, 3.9 and 3.10.

Relies heavily on type-hint reflection. All (un)marshaling is performed recursively which allows for support of nested types.

Influenced by the work of [Omry Yadan](https://github.com/omry) on [hydra](hydra.cc).

The [documentation](https://marsh.readthedocs.io) is hosted on ReadTheDocs.

## Getting Started

### Install

marsh is available through pip
```shell
pip install marsh
```

### Entry point

Using the unmarshaling capabilities of ``marsh`` we can
create entry points for python code.

These entry points allow for arguments to be given on the command
line and/or through config files which are then validated, converted to the correct types and passed to the entry point function.

Most python types are supported (primitives, type aliases, dataclasses, namedtuple e.t.c.)

#### Create

Creating an entry point is as simple as decorating a function
and calling it without arguments.

```python
# app.py
import marsh
from typing import Sequence, Union


@marsh.main
def run(
    a: int,
    b: Union[float, Sequence[int]],
    c: dict[str, bool],
) -> None:
    """Example of an entry point.

    Arguments:
        a: An integer argument.
        b: A floating point value or a sequence of ints.
        c: A dictionary with string keys and
            bool values. If this was python 3.8
            we would instead use typing.Dict[str, bool] as
            type hint as the builtin dict did not support
            type annotations.
    """
    print(a, type(a))
    print(b, type(b))
    print(c, type(c))


if __name__ == '__main__':
    run()
```

#### Run

When running the application we can use the positional arguments
on the command line to pass values to our function.

```shell
$ python app.py a=1 b=5e-1 c.key1=true c.key2=false
1 <class 'int'>
0.5 <class 'float'>
{'key1': True, 'key2': False} <class 'dict'>
```

#### Argument validation

When giving invalid values or when required arguments are missing an error message is printed and the application exits.

```shell
$ python app.py a=1.5 b=0 c.some_key=true
failed to unmarshal config: int: could not convert: 1.5
	path: a
```
```shell
$ python app.py b=0 c.some_key=true
failed to unmarshal config: MissingValueError
	path: a
```

#### Help

Using --help we can also get a help message for the arguments. Here the output was piped to `tail` to truncate the output into displaying only the arguments of our entry point.
```shell
$ python app.py --help | tail -n 11
fields:
  a: <int>              An integer argument.

  b: <float> | [<int>, ...]
                        A floating point value or a sequence of ints.

  c: {<str>: <bool>, ...}
                        A dictionary with string keys and bool values. If this
                        was python 3.8 we would instead use typing.Dict[str,
                        bool] as type hint as the builtin dict did not support
                        type annotations.
```



### Marshal
Marshaling values simply means taking a python object and turning it into JSON-like data.

```python
# marshal.py
import dataclasses
import marsh


@dataclasses.dataclass
class Config:
    a: int
    b: float


config = Config(1, 5e-1)
print(marsh.marshal(config))
```

```shell
$ python marshal.py
{'a': 1, 'b': 0.5}
```

### Unmarshal
Unmarshaling is the opposite of marshaling. A type is instantiated using JSON-like data.

```python
# unmarshal.py
import dataclasses
import typing
import marsh


class Range(typing.NamedTuple):
    start: typing.Optional[int]
    stop: int


@dataclasses.dataclass
class Config:
    a: int
    b: float
    c: Range


config = marsh.unmarshal(
    Config,
    {
        'a': 1,
        'b': 1.5,
        'c': {
            'start': None,
            'stop': 5,
        },
    }
)
print(config)
```

```shell
$ python umarshal.py
Config(a=1, b=1.5, c=Range(start=None, stop=5))
```

## License
[MIT License](LICENSE).



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/adriansahlman/marsh",
    "name": "marsh",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "marshal unmarshal configuration",
    "author": "Adrian Sahlman",
    "author_email": "adrian.sahlman@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/6b/53/cd3ff0229f34f5e485b9c9378aaa1df599cb72a4f6c3e669fb0c3fa05bf4/marsh-0.2.6.tar.gz",
    "platform": null,
    "description": "\n# Marsh\n\n(Un)marshaling library for objects in Python 3.8+.\n\nTested on Ubuntu, Windows and Mac OSX for Python 3.8, 3.9 and 3.10.\n\nRelies heavily on type-hint reflection. All (un)marshaling is performed recursively which allows for support of nested types.\n\nInfluenced by the work of [Omry Yadan](https://github.com/omry) on [hydra](hydra.cc).\n\nThe [documentation](https://marsh.readthedocs.io) is hosted on ReadTheDocs.\n\n## Getting Started\n\n### Install\n\nmarsh is available through pip\n```shell\npip install marsh\n```\n\n### Entry point\n\nUsing the unmarshaling capabilities of ``marsh`` we can\ncreate entry points for python code.\n\nThese entry points allow for arguments to be given on the command\nline and/or through config files which are then validated, converted to the correct types and passed to the entry point function.\n\nMost python types are supported (primitives, type aliases, dataclasses, namedtuple e.t.c.)\n\n#### Create\n\nCreating an entry point is as simple as decorating a function\nand calling it without arguments.\n\n```python\n# app.py\nimport marsh\nfrom typing import Sequence, Union\n\n\n@marsh.main\ndef run(\n    a: int,\n    b: Union[float, Sequence[int]],\n    c: dict[str, bool],\n) -> None:\n    \"\"\"Example of an entry point.\n\n    Arguments:\n        a: An integer argument.\n        b: A floating point value or a sequence of ints.\n        c: A dictionary with string keys and\n            bool values. If this was python 3.8\n            we would instead use typing.Dict[str, bool] as\n            type hint as the builtin dict did not support\n            type annotations.\n    \"\"\"\n    print(a, type(a))\n    print(b, type(b))\n    print(c, type(c))\n\n\nif __name__ == '__main__':\n    run()\n```\n\n#### Run\n\nWhen running the application we can use the positional arguments\non the command line to pass values to our function.\n\n```shell\n$ python app.py a=1 b=5e-1 c.key1=true c.key2=false\n1 <class 'int'>\n0.5 <class 'float'>\n{'key1': True, 'key2': False} <class 'dict'>\n```\n\n#### Argument validation\n\nWhen giving invalid values or when required arguments are missing an error message is printed and the application exits.\n\n```shell\n$ python app.py a=1.5 b=0 c.some_key=true\nfailed to unmarshal config: int: could not convert: 1.5\n\tpath: a\n```\n```shell\n$ python app.py b=0 c.some_key=true\nfailed to unmarshal config: MissingValueError\n\tpath: a\n```\n\n#### Help\n\nUsing --help we can also get a help message for the arguments. Here the output was piped to `tail` to truncate the output into displaying only the arguments of our entry point.\n```shell\n$ python app.py --help | tail -n 11\nfields:\n  a: <int>              An integer argument.\n\n  b: <float> | [<int>, ...]\n                        A floating point value or a sequence of ints.\n\n  c: {<str>: <bool>, ...}\n                        A dictionary with string keys and bool values. If this\n                        was python 3.8 we would instead use typing.Dict[str,\n                        bool] as type hint as the builtin dict did not support\n                        type annotations.\n```\n\n\n\n### Marshal\nMarshaling values simply means taking a python object and turning it into JSON-like data.\n\n```python\n# marshal.py\nimport dataclasses\nimport marsh\n\n\n@dataclasses.dataclass\nclass Config:\n    a: int\n    b: float\n\n\nconfig = Config(1, 5e-1)\nprint(marsh.marshal(config))\n```\n\n```shell\n$ python marshal.py\n{'a': 1, 'b': 0.5}\n```\n\n### Unmarshal\nUnmarshaling is the opposite of marshaling. A type is instantiated using JSON-like data.\n\n```python\n# unmarshal.py\nimport dataclasses\nimport typing\nimport marsh\n\n\nclass Range(typing.NamedTuple):\n    start: typing.Optional[int]\n    stop: int\n\n\n@dataclasses.dataclass\nclass Config:\n    a: int\n    b: float\n    c: Range\n\n\nconfig = marsh.unmarshal(\n    Config,\n    {\n        'a': 1,\n        'b': 1.5,\n        'c': {\n            'start': None,\n            'stop': 5,\n        },\n    }\n)\nprint(config)\n```\n\n```shell\n$ python umarshal.py\nConfig(a=1, b=1.5, c=Range(start=None, stop=5))\n```\n\n## License\n[MIT License](LICENSE).\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "(Un)marshaling framework.",
    "version": "0.2.6",
    "split_keywords": [
        "marshal",
        "unmarshal",
        "configuration"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "68d18f9dc2a4df06590f481642420df5da6aecd82770f2623da63c8b48412e94",
                "md5": "205a54a40bbd99d76e0b77b5e797ee31",
                "sha256": "9b7999d78be426a2b617ce9a87fe04ddf22017db4c07c03666c57b222095abf5"
            },
            "downloads": -1,
            "filename": "marsh-0.2.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "205a54a40bbd99d76e0b77b5e797ee31",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 86410,
            "upload_time": "2023-01-02T20:53:17",
            "upload_time_iso_8601": "2023-01-02T20:53:17.290508Z",
            "url": "https://files.pythonhosted.org/packages/68/d1/8f9dc2a4df06590f481642420df5da6aecd82770f2623da63c8b48412e94/marsh-0.2.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6b53cd3ff0229f34f5e485b9c9378aaa1df599cb72a4f6c3e669fb0c3fa05bf4",
                "md5": "2e284672c1a425d7c64c29621e0c3cca",
                "sha256": "14c359d12b2a5e5d66cba2ebad460adf84215f22db016354867ccf682bf65e54"
            },
            "downloads": -1,
            "filename": "marsh-0.2.6.tar.gz",
            "has_sig": false,
            "md5_digest": "2e284672c1a425d7c64c29621e0c3cca",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 64193,
            "upload_time": "2023-01-02T20:53:18",
            "upload_time_iso_8601": "2023-01-02T20:53:18.623986Z",
            "url": "https://files.pythonhosted.org/packages/6b/53/cd3ff0229f34f5e485b9c9378aaa1df599cb72a4f6c3e669fb0c3fa05bf4/marsh-0.2.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-02 20:53:18",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "adriansahlman",
    "github_project": "marsh",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "typing-extensions",
            "specs": [
                [
                    ">=",
                    "4.2"
                ]
            ]
        },
        {
            "name": "omegaconf",
            "specs": [
                [
                    ">=",
                    "2.1.1"
                ]
            ]
        },
        {
            "name": "docstring-parser",
            "specs": [
                [
                    ">=",
                    "0.7.3"
                ]
            ]
        },
        {
            "name": "dateparser",
            "specs": [
                [
                    ">=",
                    "1.1.1"
                ]
            ]
        },
        {
            "name": "mypy",
            "specs": [
                [
                    "==",
                    "0.982"
                ]
            ]
        },
        {
            "name": "flake8",
            "specs": [
                [
                    ">=",
                    "3.8"
                ]
            ]
        },
        {
            "name": "add-trailing-comma",
            "specs": [
                [
                    ">=",
                    "2.2.2"
                ]
            ]
        },
        {
            "name": "pytest",
            "specs": [
                [
                    ">=",
                    "6.1.2"
                ]
            ]
        },
        {
            "name": "types-PyYAML",
            "specs": []
        },
        {
            "name": "types-setuptools",
            "specs": []
        },
        {
            "name": "types-typing-extensions",
            "specs": [
                [
                    ">=",
                    "3.7.3"
                ]
            ]
        },
        {
            "name": "types-dateparser",
            "specs": []
        }
    ],
    "lcname": "marsh"
}
        
Elapsed time: 0.02418s