{"id":21578709,"url":"https://github.com/julian-nash/dictparse","last_synced_at":"2025-03-18T07:32:16.334Z","repository":{"id":62568109,"uuid":"294146142","full_name":"Julian-Nash/dictparse","owner":"Julian-Nash","description":"A Python dictionary parser, useful for REST API's, web applications, data validation \u0026 more!","archived":false,"fork":false,"pushed_at":"2020-10-28T14:42:52.000Z","size":113,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-26T13:49:10.986Z","etag":null,"topics":["dictionary","enforcing-rules","parser","python"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/dictparse","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Julian-Nash.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-09T15:00:52.000Z","updated_at":"2022-11-21T22:46:10.000Z","dependencies_parsed_at":"2022-11-03T17:34:30.531Z","dependency_job_id":null,"html_url":"https://github.com/Julian-Nash/dictparse","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fdictparse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fdictparse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fdictparse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julian-Nash%2Fdictparse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Julian-Nash","download_url":"https://codeload.github.com/Julian-Nash/dictparse/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244177715,"owners_count":20411009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dictionary","enforcing-rules","parser","python"],"created_at":"2024-11-24T13:11:25.444Z","updated_at":"2025-03-18T07:32:16.307Z","avatar_url":"https://github.com/Julian-Nash.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dictparse\n\n![Python package](https://github.com/Julian-Nash/dictparse/workflows/Python%20package/badge.svg?branch=master)\n\nA simple, slim and useful, zero-dependency utility for parsing dictionaries or dictionary-like objects.\n\nIt's particularly useful for parsing incoming request data in REST APIs \u0026 web applications, for example in the case\n of Flask, parsing form data from `request.form`, query string arguments from`request.args` or JSON data from \n`request.json`.\n \nThe `dictparse` design takes inspiration from Python's own `argparse` library, similar to the `ArgumentParser` class\n, taking input as a dictionary or dictionary-like object, enforcing rules, types, applying functions, default values and\n returning a `NameSpace`, with values mapped to attributes.\n \n### Installation\n\n```shell script\npip install dictparse\n```\n\n### Example\n\nThe following code is a Python program that takes takes some data in the form of a dictionary and parses it:\n\n```pycon\n\u003e\u003e\u003e from dictparse import DictionaryParser\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"name\", str, required=True)\n\u003e\u003e\u003e params = parser.parse_dict({\"name\": \"FooBar\"})\n\u003e\u003e\u003e params.name\n'FooBar'\n```\n\n### Creating a parser\n\nThe first step is to create the DictionaryParser object\n\n```pycon\n\u003e\u003e\u003e from dictparse import DictionaryParser\n\u003e\u003e\u003e parser = DictionaryParser(description=\"Create a new user\")\n```\n\n### Adding parameters\n\nAdding parameters to the parser is done by making calls to the `add_param` method. These calls tell the\n `DictionaryParser` how to handle the values passed in and turn them into the desired output, enforcing rules\n , changing types and transforming values based on the arguments passed to the `add_param` method.\n \n```pycon\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"name\", str, required=True)\n\u003e\u003e\u003e parser.add_param(\"language\", str, choices=[\"python\", \"javascript\", \"rust\"])\n\u003e\u003e\u003e parser.add_param(\"tags\", str, action=lambda x: x.split(\",\"))\n\u003e\u003e\u003e params = parser.parse_dict({\"name\": \"FooBar\", \"language\": \"python\", \"tags\": \"foo,bar,baz\"})\n\u003e\u003e\u003e params.name\n'FooBar'\n\u003e\u003e\u003e params.language\n'python'\n\u003e\u003e\u003e params.tags\n['foo', 'bar', 'baz']\n\u003e\u003e\u003e params.to_dict()\n{'name': 'FooBar', 'language': 'python', 'tags': ['foo', 'bar', 'baz']}\n```\n\nIf the parser does not find a value matching the name, the default value is `None`\n\n### Arguments available for `add_param`\n\n```py3\nDictionaryParser.add_param(\n    name: str,\n    type_: Optional[Union[Type[str], Type[int], Type[float], Type[bool], Type[list], Type[dict], Type[set], Type[tuple]]] = None,\n    dest: Optional[str] = None,\n    required: Optional[bool] = False,\n    choices: Optional[Union[list, set, tuple]] = None,\n    action: Optional[Callable] = None,\n    description: Optional[str] = None,\n    default: Optional[Any] = None,\n    regex: Optional[str] = None\n) -\u003e None\n```\n\n- `name`: The parameter name (required - See note below)\n- `type_`: The common parameter type (The parser will attempt to convert the parameter value to the given type)\n- `dest`: The destination name of the parameter (See note below)\n- `required`: If `True`, enforce a value for the parameter must exists\n- `choices`: A list, set, or tuple of possible choices\n- `action`: A function to apply to the value (Applied after any type conversion)\n- `description`: A description of the parameter\n- `default`: A default value for the parameter if not found\n- `regex`: A regular expression to match against (Sets the parameter to `None` if the match is negative)\n\n\u003e Note - The `name` and `dest` parameters must comply with standard Python variable naming conventions (only start\n\u003e with a letter or underscore \u0026 only contain alpha-numeric characters), not be a Python keyword and not start and end\n\u003e with a double underscore (dunder)\n\n### Parsing the data\n\nAfter creating the parser and adding parameters to it, data can be parsed by calling the `parse_dict` method, passing\nin the data to be parsed. This returns a `NameSpace` object.\n\n```py3\nDictionaryParser.parse_dict(\n    data: Dict[str, Any], \n    strict: Optional[bool] = False, \n    action: Optional[Callable] = None\n) -\u003e NameSpace:\n```\n\n- `data`: A dictionary or dictionary-like object\n- `strict`: If `True`, raises an exception if any parameters not added to the parser are received\n- `action`: A function to apply to all parameters (after any type conversion and after action passed to `add_param`)\n\n### The `NameSpace` object\n\nA `NameSpace` object is returned when calling `parse_dict` and contains the parsed data after applying your rules\ndefined when adding arguments.\n\nParameters can be accessed as attributes of the `NameSpace` using dot notation:\n\n```pycon\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"age\", int, required=True)\n\u003e\u003e\u003e params = parser.parse_dict({\"age\": 30})\n\u003e\u003e\u003e params.age\n30\n```\n\nA standard `AttributeError` will be raised if an attribute is accessed without being added to the parser:\n\n```pycon\n\u003e\u003e\u003e params.foo\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nAttributeError: 'NameSpace' object has no attribute 'foo'\n```\n\nif the `dest` parameter is supplied when adding a parameter in `add_param`, the value can only be accessed by using the\n`dest` value:\n\n```pycon\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"bar\", str, dest=\"foo\")\n\u003e\u003e\u003e params = parser.parse_dict({\"bar\": \"bar\"})\n\u003e\u003e\u003e params.foo\n'bar'\n```\n\nThe `NameSpace` object has the following available methods:\n\n#### `get`\n\n```py3\nNameSpace.get(\n    name: str, \n    default: Optional[Any] = None\n) -\u003e Union[None, Any]:\n```\n\nCalling the `get` method on the `NameSpace` and passing in a key works in the same way as calling `get` on a dictionary\n, returning either the value for the parameter requested or `None` if the `NameSpace` does not have that attribute.\n\nAn optional default value can be supplied using the `default` parameter to be returned if the attribute does not exist.\n \n```pycon\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"age\", int, required=True)\n\u003e\u003e\u003e parser.add_param(\"weight\", int)\n\u003e\u003e\u003e params = parser.parse_dict({\"age\": 30, \"height\": 1.9})\n\u003e\u003e\u003e params.weight\nNone\n\u003e\u003e\u003e params.get(\"age\")\n30\n\u003e\u003e\u003e params.get(\"foo\", 42)\n42\n```\n\n#### `to_dict`\n\n```py3\nNameSpace.to_dict() -\u003e dict\n```\n\nReturns a dictionary of the parsed parameters.\n\n```pycon\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"one\", str)\n\u003e\u003e\u003e parser.add_param(\"two\", int)\n\u003e\u003e\u003e parser.add_param(\"three\", list)\n\u003e\u003e\u003e params = parser.parse_dict({\"one\": \"one\", \"two\": 2, \"three\": [1, 2, 3]})\n\u003e\u003e\u003e params.to_dict()\n{'one': 'one', 'two': 2, 'three': [1, 2, 3]}\n```\n\n`to_dict()` accepts an optional parameter `exclude`, a list of keys to exclude from the returned dictionary\n\n```pycon\n\u003e\u003e\u003e from dictparse import DictionaryParser\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"csrf_token\", str, required=True)\n\u003e\u003e\u003e parser.add_param(\"name\", str)\n\u003e\u003e\u003e parser.add_param(\"email\", str)\n\u003e\u003e\u003e params = parser.parse_dict({\"csrf_token\": \"xxyyzz112233\", \"name\": \"foo\", \"email\": \"foo@bar.com\"})\n\u003e\u003e\u003e params.to_dict(exclude=[\"csrf_token\"])\n{'name': 'foo', 'email': 'foo@bar.com'}\n```\n\n#### `get_param`\n\nReturns a `Param` object\n\n```pycon\n\u003e\u003e\u003e from dictparse import DictionaryParser\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"names\", list, default=[])\n\u003e\u003e\u003e params = parser.parse_dict({\"names\": [\"foo\", \"bar\"]})\n\u003e\u003e\u003e names = params.get_param(\"names\")\n\u003e\u003e\u003e names.name\n'names'\n\u003e\u003e\u003e names.value\n['foo', 'bar']\n\u003e\u003e\u003e names.default\n[]\n```\n\n`Param` objects are hold all data associated with the parameter, as can be seen below in the `Param.__init__` method:\n\n```shell script\nclass Param(object):\n\n    def __init__(\n            self,\n            name: str,\n            type_: Optional[Union[Type[str], Type[int], Type[float], Type[bool], Type[list], Type[dict], Type[set], Type[tuple]]] = None,\n            dest: Optional[str] = None,\n            required: Optional[bool] = False,\n            choices: Optional[Union[list, set, tuple]] = None,\n            action: Optional[Callable] = None,\n            description: Optional[str] = None,\n            default: Optional[Any] = None,\n            regex: Optional[str] = None,\n            value: Optional[Any] = None\n    ):\n```\n\n\u003e Note - The `NameSpace` will be assigned the value for `dest` if supplied in `add_param`\n\n```pycon\n\u003e\u003e\u003e from dictparse import DictionaryParser\n\u003e\u003e\u003e parser = DictionaryParser()\n\u003e\u003e\u003e parser.add_param(\"foo\", str, dest=\"bar\")\n\u003e\u003e\u003e params = parser.parse_dict({\"foo\": 42})\n\u003e\u003e\u003e param = params.get_param(\"bar\")\n\u003e\u003e\u003e param.name\n'foo'\n\u003e\u003e\u003e param.dest\n'bar'\n\u003e\u003e\u003e param.value\n'42'\n```\n\n### Flask example\n\nAn example of parsing JSON data sent in a POST request to a Flask route:\n\n```py3\nfrom app.users import create_user\n\nfrom flask import Flask, request\nfrom respond import JSONResponse\nfrom dictparse import DictionaryParser\n\n\ndef create_app():\n\n    app = Flask(__name__)\n\n    @app.route(\"/\", methods=[\"POST\"])\n    def post():\n\n        parser = DictionaryParser(description=\"Create a new user\")\n\n        parser.add_param(\"name\", str, required=True)\n        parser.add_param(\"age\", int)\n        parser.add_param(\"password\", str, required=True, action=lambda x: x.encode(\"utf-8\"))\n        parser.add_param(\"interests\", list, action=lambda x: [i.strip() for i in x])\n        parser.add_param(\"level\", float, default=1.5)\n        parser.add_param(\"stage\", str, choices=[\"alpha\", \"beta\"])\n\n        try:\n            params = parser.parse_dict(request.get_json())\n        except Exception as e:\n            return JSONResponse.bad_request(str(e))\n\n        user = create_user(\n            name=params.name,\n            age=params.age,\n            password=params.password,\n            interests=params.interests,\n            level=params.level,\n            stage=params.stage\n        )\n\n        return JSONResponse.created(user.to_dict())\n\n    return app\n\n\nif __name__ == \"__main__\":\n    app = create_app()\n    app.run()\n\n```\n\n### Exception handling\n\nExceptions will be raised in the following scenarios:\n\n##### `ParserTypeError`\n\nRaised when a parameter cannot be parsed to the type declared in `add_param`\n\n```py3\nfrom dictparse import DictionaryParser\nfrom dictparse.exceptions import ParserTypeError\n\nparser = DictionaryParser()\nparser.add_param(\"age\", int)\n\ntry:\n    params = parser.parse_dict({\"age\": \"thirty\"})\nexcept ParserTypeError as e:\n    print(e)  # Invalid value 'thirty' for parameter 'age', expected 'int' not 'str'\n```\n\n`ParserTypeError` contains the following attributes:\n\n- `param`: The parameter name (`str`)\n- `value`: The parameter value (`Any`)\n- `expected`: The expected type (`type`)\n\n##### `ParserRequiredParameterError`\n\nRaised when `parse_dict` is called and a parameter is required, but not found\n\n```py3\nfrom dictparse import DictionaryParser\nfrom dictparse.exceptions import ParserRequiredParameterError\n\nparser = DictionaryParser()\nparser.add_param(\"name\", str)\nparser.add_param(\"email\", str, required=True)\n\ntry:\n    params = parser.parse_dict({\"name\": \"John Doe\"})\nexcept ParserRequiredParameterError as e:\n    print(e)  # Missing required parameter 'email'\n```\n\n- `ParserRequiredParameterError` has a single attribute `param`, the name of the parameter (str)\n\n##### `ParserInvalidChoiceError`\n\nRaised when `parse_dict` is called and parses a value not defined in the `choices` parameter of `add_param`\n\n```py3\nfrom dictparse import DictionaryParser\nfrom dictparse.exceptions import ParserInvalidChoiceError\n\nparser = DictionaryParser()\nparser.add_param(\"name\", str)\nparser.add_param(\"language\", str, choices=[\"python\", \"bash\"])\n\ntry:\n    params = parser.parse_dict({\"name\": \"John Doe\", \"language\": \"javascript\"})\nexcept ParserInvalidChoiceError as e:\n    print(e)  # Parameter 'language' must be one of '['python', 'bash']', not 'javascript'\n```\n\n`ParserInvalidChoiceError` has the following 3 attributes:\n\n- `param`: The parameter name (str)\n- `value`: The parameter value (Any)\n- `choices`: The available choices added via `add_param` (list|set|tuple)\n\n\n##### `ParserInvalidParameterError`\n\nRaised calling `parse_dict` with `strict` set to `True`\n\nThe `strict` parameter enforces the parser to only accept parameters that have been added to the parser\n\n```py3\nfrom dictparse import DictionaryParser\nfrom dictparse.exceptions import ParserInvalidParameterError\n\nparser = DictionaryParser()\nparser.add_param(\"name\", str)\nparser.add_param(\"language\", str, choices=[\"python\", \"bash\"])\n\ntry:\n    params = parser.parse_dict({\"name\": \"John Doe\", \"language\": \"python\", \"email\": \"jdoe@gmail.com\"}, strict=True)\nexcept ParserInvalidParameterError as e:\n    print(e)  # Invalid parameter 'email'\n```\n\n`ParserInvalidParameterError` has a single attribute `param`, the name of the parameter (str)\n\n### Other runtime considerations for `parse_dict`\n\nIf an invalid data type for `data` is passed to `parse_dict` (such as a list or string), it raises a \n`ParserInvalidDataTypeError`\n\n```py3\nfrom dictparse import DictionaryParser\nfrom dictparse.exceptions import ParserInvalidDataTypeError\n\nparser = DictionaryParser()\nparser.add_param(\"name\", str)\n\ntry:\n    params = parser.parse_dict([{\"name\", \"John Doe\"}])\nexcept ParserInvalidDataTypeError as e:\n    print(e)  # Invalid type for 'data', must be a dict or dict-like object, not 'list'\n\ntry:\n    params = parser.parse_dict(\"foo\")\nexcept ParserInvalidDataTypeError as e:\n    print(e)  # Invalid type for 'data', must be a dict or dict-like object, not 'str'\n```\n\n### Tests \u0026 coverage\n\nA test suite is available in the `tests` directory with 100% coverage (15/Sep/2020)\n\n```shell script\nName                      Stmts   Miss  Cover\n---------------------------------------------\ndictparse/__init__.py         1      0   100%\ndictparse/exceptions.py      37      0   100%\ndictparse/parser.py         106      0   100%\ntests/__init__.py             0      0   100%\ntests/test_parser.py        310      0   100%\n---------------------------------------------\nTOTAL                       454      0   100%\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian-nash%2Fdictparse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulian-nash%2Fdictparse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian-nash%2Fdictparse/lists"}