# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['records',
 'records.fillers',
 'records.fillers.builtin_fillers',
 'records.utils']

package_data = \
{'': ['*']}

setup_kwargs = {
    'name': 'recordclasses',
    'version': '0.0.1',
    'description': 'Record Classes',
    'long_description': '![Test records](https://github.com/bentheiii/Records/workflows/Test%20records/badge.svg?branch=master)\n[![codecov](https://codecov.io/gh/bentheiii/Records/branch/master/graph/badge.svg)](https://codecov.io/gh/bentheiii/Records)\n\n# Records\nRecords is a python library that makes powerful structure classes easy.\n\n## Simplest Example\nA feature of Records is that, by default, it does nothing more than a namedtuple or dataclass would.\n```python\nfrom records import RecordBase\n\nclass Point(RecordBase):\n    x: float\n    y: float\n    z: float = 0.0\n\np0 = Point(x=0, y=0)\nprint(p0)  # Point(x=0, y=0)\nprint(p0.x)  # 0\n# note that no type checking or coercion is performed\nprint(type(p0.y))  # int\n# by default, the type hints are not even run\np1 = Point(x="hello", y="world", z="1.0")\nprint(p1.y)  # world\nprint(type(p1.z))  # str\n```\n\n## Checking, Coercion, and Validation\nSometimes we\'d like to perform some additional processing to arguments before they are entered into the struct. For that we have three steps: type-checking, coercion, and validation\n* type-checking is the first and simplest step, it simply checks that the argument is of the type we expect. If it isn\'t, then we perform coercion.\n* type coercion only occurs if typechecking has failed, it will attempt to convert the into the type we expect. As one may expect, there is a large number of potential coercers, so they must be added each individually.\n* validation occurs after either type checking or type coercion have succeeded. By this point we are certain that the input is of the correct type and we want to ensure/manipulate its value.\n\nThese methods are described per-field with the `Annotation` type hint (`Annotation` is new in python 3.9, but has been backported by `records` for older versions to use)\n\nThese three steps are expanded upon in the documentation, for now we will show some brief examples:\n```python\nfrom typing import List\nfrom records import RecordBase, Annotated, check, check_strict, Loose, Within, Eval\n\nclass Person(RecordBase):\n    first_name: str  # no coercion or checking here, this is what we call a "hollow" field\n    last_name: Annotated[str, check]  # now we will raise a TypeError if anyone tries to enter a non-string last_name  \n    year_of_birth: Annotated[int, check_strict]  # we will raise a TypeError if year_of_birth isn\'t exactly an int (so passing True will throw an error)\n    lucky_number: Annotated[int, check, Loose]  # the "Loose" built-in coerser will simply call the destination type with the input as an argument, so that using `lucky_number="7"` would be equivalent to `lucky_number=int("7")`\n    number_of_children: Annotated[int, check, Within(ge=0)]  # the Within built-in validator ensures the value is within stated bounds (in this case, at least zero)\n    # field tokens can even be more complex in case of nested field types\n    names_of_children: Annotated[List[Annotated[int, check, Eval]], check]  # the list will be checked to be a list, and each item individually will be checked or coerced to be an int using the built-in Eval coercer.\n    \n    # validators can also be added after declaration with pre_bind\n    @classmethod\n    def pre_bind(cls):\n        @cls.last_name.add_validator\n        def no_bad_words(last_name):\n            # we want to remove some words from the last name\n            return last_name.replace(\'richard\', \'*******\')\n\n    # we can also add some more pre-processing on an entire instance with "post_new"\n    def post_new(self):\n        if len(self.names_of_children) != self.number_of_children:\n            raise ValueError("children mismatch")\n```\n## Parsing\nRecords can also be parsed from various python primitives. Including parsing from `dict`s, jsons, and even generic namepaces.\n```python\nfrom types import SimpleNamespace\nfrom records import RecordBase, check\n\nclass User(RecordBase, default_type_check=check):\n    name: str\n    password: str\n    age: int = 18\n    \n\nprint(User.from_mapping({"name": "richard", "password": "swordfish"}))\nprint(User.from_json(\'{"name": "richard", "password": "swordfish"})\'))\nn = SimpleNamespace(user="rich", password="ard", age= 7)\nprint(User.from_instance(n))\n\n# parsing can even be done if you expect misnamed fields!\nfrom_upper_dict = User.from_mapping.select(keys_to_rename=[(\'user\',\'name\')], keys_to_remove=[\'favorite_color\'])\nprint(from_upper_dict({\'user\':\'richard\', \'password\': \'pw\', \'favorite_color\': \'red\'}))\n```\n\nYou can also define your own parsers and even use them in construction!\n```python\nfrom math import sqrt\nfrom records import RecordBase, check, SelectableFactory, parser\n\nclass Point(RecordBase, default_type_check=check):\n    x: float\n    y: float\n    z: float = 0\n    \n    @parser\n    @SelectableConstructor\n    @classmethod\n    def from_tuple(cls, v):\n        return {\'x\':v[0], \'y\':v[1], \'z\':v[2] if len(v) > 2 else 0}\n\n    @property\n    def norm(self):\n        return sqrt(self.x**2 + self.y**2 + self.z**2)\n\np = Point([2,3,6])\nprint(p.norm)  # 7\n```\n## Exporting\nRecords can also be exporting to various formats (same as parsing).\n```python\nfrom records import RecordBase, check\n\nclass Point(RecordBase, default_type_check=check):\n    x: float\n    y: float\n    z: float\n\np = Point(x=2, y=3, z=6)\nprint(p.to_dict())\nprint(p.to_pickle())\n#  again, we can select to change the keys\nprint(\n    p.to_json.select(keys_to_add=[(\'w\',0)])()\n)\n```',
    'author': 'Ben Avrahami',
    'author_email': None,
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/bentheiii/Records',
    'packages': packages,
    'package_data': package_data,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
