Typical: Fast, simple, & correct data-validation using Python 3 typing.

Overview

typical: Python's Typing Toolkit

image image image image Test & Lint Coverage Code style: black Netlify Status

How Typical

Introduction

Typical is a library devoted to runtime analysis, inference, validation, and enforcement of Python types, PEP 484 Type Hints, and custom user-defined data-types.

It provides a high-level Functional API and Object API to suite most any occasion.

Getting Started

Installation is as simple as pip install -U typical. For more installation options to make typical even faster, see the Install section in the documentation.

Help

The latest documentation is hosted at python-typical.org.

Starting with version 2.0, All documentation is hand-crafted markdown & versioned documentation can be found at typical's Git Repo. (Versioned documentation is still in-the-works directly on our domain.)

A Typical Use-Case

The decorator that started it all:

typic.al(...)

import typic


@typic.al
def hard_math(a: int, b: int, *c: int) -> int:
    return a + b + sum(c)

hard_math(1, "3")
#> 4


@typic.al(strict=True)
def strict_math(a: int, b: int, *c: int) -> int:
    return a + b + sum(c)

strict_math(1, 2, 3, "4")
#> Traceback (most recent call last):
#>  ...
#> typic.constraints.error.ConstraintValueError: Given value <'4'> fails constraints: (type=int, nullable=False, coerce=False)
  

Typical has both a high-level Object API and high-level Functional API. In general, any method registered to one API is also available to the other.

The Object API

from typing import Iterable

import typic


@typic.constrained(ge=1)
class ID(int):
    ...


@typic.constrained(max_length=280)
class Tweet(str):
    ...


@typic.klass
class Tweeter:
    id: ID
    tweets: Iterable[Tweet]
    

json = '{"id":1,"tweets":["I don\'t understand Twitter"]}'
t = Tweeter.transmute(json)

print(t)
#> Tweeter(id=1, tweets=["I don't understand Twitter"])

print(t.tojson())
#> '{"id":1,"tweets":["I don\'t understand Twitter"]}'

Tweeter.validate({"id": 0, "tweets": []})
#> Traceback (most recent call last):
#>  ...
#> typic.constraints.error.ConstraintValueError: Given value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)

The Functional API

import dataclasses
from typing import Iterable

import typic


@typic.constrained(ge=1)
class ID(int):
    ...


@typic.constrained(max_length=280)
class Tweet(str):
    ...


@dataclasses.dataclass # or typing.TypedDict or typing.NamedTuple or annotated class...
class Tweeter:
    id: ID
    tweets: Iterable[Tweet]


json = '{"id":1,"tweets":["I don\'t understand Twitter"]}'
protocol = typic.protocol(Tweeter)

t = protocol.transmute(json)  # or typic.transmute()
print(t)
#> Tweeter(id=1, tweets=["I don't understand Twitter"])

print(protocol.tojson(t))
#> '{"id":1,"tweets":["I don\'t understand Twitter"]}'

protocol.validate({"id": 0, "tweets": []})  # or typic.validate()
#> Traceback (most recent call last):
#>  ...
#> typic.constraints.error.ConstraintValueError: Tweeter.id: value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)

Changelog

See our Releases.

Issues
  • Wrong behavior when Union with constrained types

    Wrong behavior when Union with constrained types

    • typical version: 2.6.3
    • Python version: 3.8.5
    • Operating System: CentOS 8

    Description

    In this version(2.6.3), it can handle Union if the given argument is just an instance of one of the types in the Union, but not support subtypes, checking type by issubclass or isinstance can sovle this. But if Union contains constrained types, the transmute function did not behave like we hope.

    import typic, typing
    
    PositiveFloat = typic.constrained(float, gt=0)
    PositiveInt = typic.constrained(int, gt=0)
    
    
    proto = typic.protocol(typing.Union[PositiveInt, PositiveFloat])
    
    proto.transmute(PositiveFloat(2.1))  # 2.1
    proto.transmute(2.1)  # 2
    isinstance(2.1, PositiveFloat)  # False
    issubclass(PositiveFloat, float)  # True
    
    opened by xbanke 8
  • Coercion to Union does not result in error but returns passed in value -- Union could be expanded

    Coercion to Union does not result in error but returns passed in value -- Union could be expanded

    It's stated in the README:

    The following annotations are special forms which cannot be resolved: Union Any Because these signal an unclear resolution, Typical will ignore this flavor of annotation ...

    However, when I try to coerce some values to Unions, I didn't expect Typical to silently ignore Union annotations: (e.g. it returns a str when a str is passed in, when none of the Union members are str). Since it is ignored, I can't rely on passing my value through coercion to actually get the correct type returned to me. I think this is a major limitation and a source of errors.

    from typing import Union
    import typic
    
    
    # Case A. Incorrect. Expected: ValueError
    print(typic.coerce("foo", Union[float, int]))  # foo
    
    from enum import Enum
    
    class MyEnum(Enum):
      a = "foo"
      b = "bar"
    
    # Case C1. Correct: Union with None works.
    print(typic.coerce("foo", Union[MyEnum, None]))  # MyEnum.a
    print(typic.coerce(None, Union[MyEnum, None]))  # None
    
    # Case C1. Incorrect. Expected: MyEnum.a
    print(typic.coerce("foo", Union[MyEnum, int]))  # foo
    print(typic.coerce("foo", Union[MyEnum, bool]))  # foo
    
    class Sentinel:
      pass
    
    # Case B. Incorrect. Expected: MyEnum.a
    print(typic.coerce("foo", Union[MyEnum, Sentinel]))  # foo
    
    # Case C1: Correct: The value is an instance of one of the types
    print(typic.coerce(1, Union[bool, int]))  # 1
    print(typic.coerce(False, Union[bool, int]))  # False
    
    # Case B. Incorrect. Expected: True (since it can be coerced to bool, but not to int)
    print(typic.coerce("asdf", Union[bool, int]))  # asdf
    
    # Case C2. Expected: TypeError, since it could be coerced to both
    print(typic.coerce("1", Union[bool, int]))  # 1
    
    
    

    Would it really be problematic to handle coercing to Union in some cases?

    I can imagine scenarios where it's unambiguous, and one scenario where it is and an error could be raised:

    • A. If the value cannot be coerced to any of the types: raise one of the coercion errors (e.g. the last)
    • B. If the value can be coerced to only one of the types (the others may raise ValueError/TypeError): return that coerced value
    • C. If the value can be coerced to multiple of the types:
      • C1: If the value is an instance of one of the union's types, then "coerce" it to that type -- coerce in this context means just perform any constraint checks that there might be (This coercion is already handled for Union[T, None])
      • C2: Otherwise: Raise a TypeError (this is the ambiguous scenario)

    What do you think?

    In my mind, it might look something like this:

    from typing import Any, Iterable, Type
    from typic import coerce
    
    # Sentinel value
    class Empty:
        pass
    
    
    _empty = Empty()
    
    
    def coerce_union(value: Any, union: Iterable[Type]) -> Any:
        coerced_value = _empty
    
        for typ in union:
            # If the value is already an instance of one of the types in the Union, coerce using that type
            # Presumably, if the type was constrained, coercing could raise errors here, but that would be expected.
            if isinstance(value, typ):
                return coerce(value, typ)
        error = None
    
        for typ in union:
            try:
                new_coerced_value = coerce(value, typ)
            except (ValueError, TypeError) as e:
                # Store the error and continue
                error = e
                continue
            if coerced_value is not _empty:
                raise TypeError("Ambiguous coercion: the value could be coerced to multiple types in the Union")
            coerced_value = new_coerced_value
        if coerced_value is not _empty:
            return coerced_value
        # The value couldn't be coerced to any of the types in the Union
        raise error
    
    enhancement 
    opened by syastrov 8
  • Schema generation: Name objects after their type rather the field nam…

    Schema generation: Name objects after their type rather the field nam…

    …e where they were referenced

    This helps to allow definitions to be reused. Previously, the behavior would be that there would be 2 definitions (named First and Second) rather than one.

    It's also the behavior I originally expected, as I wondered why my definitions got a different name than the name of the e.g. dataclass referencced.

    However this change is backwards-incompatible. Not sure if there should be an option somewhere to configure the schema builder's behavior (it could be that you could provide callable which returns the name for an object)? If so, I'm not sure where the appropriate place, API-wise, to place such an option would be. Another option would be to make defname not a staticmethod so you could easily subclass SchemaBuilder and override defname.

    There is also the issue that the name could conflict with another object (which can also happen with the previous naming behavior) from a different module with the same name. In that case, either an error should be thrown or perhaps the module name should be included and prepended to the type's __name__?

    What do you think?

    enhancement 
    opened by syastrov 7
  • Fix type annotations in docs.

    Fix type annotations in docs.

    Fix wrong type annotations for example of Namespace strict mode.

    opened by kfollesdal 6
  • Schema generation: Name objects after their type rather the field nam…

    Schema generation: Name objects after their type rather the field nam…

    …e where they were referenced

    This helps to allow definitions to be reused.

    bug 
    opened by syastrov 6
  • Not expected behavior for datetime with strict option.

    Not expected behavior for datetime with strict option.

    • typical version: 2.0.0
    • Python version: 3.8.2
    • Operating System: macOS Catalina

    Expected that when using strict with datetime, that it only accept datetime object. And do not do any type coercer. But the following example do not behave as I expected. Expected to get an exception in both cases.

    >>> import typic
    >>> typic.transmute(typic.Strict[pendulum.DateTime], '2020-04-09') 
    DateTime(2020, 4, 9, 0, 0, 0, tzinfo=Timezone('UTC'))
    

    or similary

    >>> @typic.klass(strict=True)
    ... class Test:
    ... date: pendulum.DateTime
    >>> Test('2020-04-09') 
    Test(date=DateTime(2020, 4, 9, 0, 0, 0, tzinfo=Timezone('UTC')))
    
    enhancement 
    opened by kfollesdal 6
  • Still not working with PEP 604 Union Operators

    Still not working with PEP 604 Union Operators

    • typical version: 2.4.0
    • Python version: 3.10.b2
    • Operating System: Linux
    import typic
    
    @typic.al
    def foo(bar: str | int):
        pass
    
    

    Would raise these errors:

    Traceback (most recent call last):
      File "/home/lsongzhi/code/python/pydemo/main.py", line 4, in <module>
        def foo(bar: str | int):
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/api.py", line 384, in typed
        return _typed(_cls_or_callable) if _cls_or_callable is not None else _typed
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/api.py", line 378, in _typed
        return wrap(obj, delay=delay, strict=strict)  # type: ignore
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/api.py", line 172, in wrap
        protocols(func)
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/serde/resolver.py", line 782, in protocols
        resolved = self.resolve(
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/serde/resolver.py", line 709, in resolve
        resolved = self._resolve_from_annotation(anno, namespace=namespace)
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/serde/resolver.py", line 524, in _resolve_from_annotation
        deserializer, validator = self.des.factory(
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/serde/des.py", line 866, in factory
        deserializer = self._build_des(annotation, key, namespace)
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/serde/des.py", line 709, in _build_des
        anno_name = get_unique_name(origin)
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/util.py", line 255, in get_unique_name
        return f"{get_name(obj)}_{id(obj)}".replace("-", "_")
      File "/home/lsongzhi/.pyenv/versions/3.10.0b2/lib/python3.10/site-packages/typic/util.py", line 236, in get_name
        return obj.__name__
    AttributeError: 'types.Union' object has no attribute '__name__'. Did you mean: '__ne__'?
    
    opened by songzhi 5
  • Coercion from None to str

    Coercion from None to str

    I didn't expect that None would be coerced to a string for a field marked as str. Can that behavior be disabled?

    >>> @typic.al
    ... @dataclass
    ... class Y:
    ...   a: str
    ... 
    >>> Y(None)
    Y(a='None')
    

    The same behavior happens if you try to coerce other types:

    >>> Y(dict)
    Y(a="<class 'dict'>")
    
    question 
    opened by syastrov 4
  • bug: RecursionError when typic.klass used in metaclass if slots = True

    bug: RecursionError when typic.klass used in metaclass if slots = True

    • typical version: 2.0.19
    • Python version: 3.7.4
    • Operating System:

    Description

    import typic
    
    class FooMeta(type):
        def __new__(mcs, name, bases, namespace):
            cls = super().__new__(mcs, name, bases, namespace)
            ...
            cls = typic.klass(cls, slots=True)
            ...
            return cls
    
    class Foo(metaclass=FooMeta):   # RecursionError
        a: int
    

    What I Did

    Paste the command(s) you ran and the output.
    If there was a crash, please include the traceback here.
    
    wontfix 
    opened by xbanke 4
  • JSON schema generated for date field is invalid upon calling `validate`

    JSON schema generated for date field is invalid upon calling `validate`

    It seems like typical doesn't convert the StringFormat.DATE enum field into a primitive (e.g. str), which fastjsonschema doesn't like.

    >>> import typic
    >>> from datetime import date
    >>> from dataclasses import dataclass
    >>> @typic.al
    ... @dataclass
    ... class Foo:
    ...   foo: date
    ...
    >>> typic.schema(Foo)
    ObjectSchemaField(title='Foo', description='Foo(foo: datetime.date)', properties={'foo': StrSchemaField(format=<StringFormat.DATE: 'date'>)}, additionalProperties=False, required=('foo',))
    >>> typic.schema(Foo).validate({})
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File ".../venv/lib/python3.7/site-packages/typic/schema/field.py", line 194, in validate
        return self.validator(obj)
      File ".../venv/lib/python3.7/site-packages/typic/util.py", line 226, in __get__
        cache[attrname] = self.func(instance)
      File ".../venv/lib/python3.7/site-packages/typic/schema/field.py", line 189, in validator
        return fastjsonschema.compile(self.asdict())
      File ".../venv/lib/python3.7/site-packages/fastjsonschema/__init__.py", line 167, in compile
        exec(code_generator.func_code, global_state)
      File "<string>", line 5
        raise JsonSchemaException("data must be object", value=data, name="data", definition={'type': 'object', 'title': 'Foo', 'description': 'Foo(foo: datetime.date)', 'properties': {'foo': {'type': 'string', 'format': <StringFormat.DATE: 'date'>}}, 'additionalProperties': False, 'required': ['foo'], 'definitions': {}}, rule='type')
                                                                                                                                                                                                                             ^
    SyntaxError: invalid syntax
    
    bug 
    opened by syastrov 4
  • Switch to poetry-core

    Switch to poetry-core

    poetry-core is intended to be a light weight, fully compliant, self-contained package allowing PEP 517 compatible build frontends to build Poetry managed projects.

    Using poetry-core allows distribution packages to depend only on the build backend.

    opened by fabaff 0
  • Use orjson for other libraries benchmark

    Use orjson for other libraries benchmark

    Current benchmark is not fair for libraries like pydantic or marshmallow because they use standard json library while typical use orjson, which is a lot faster.

    opened by wyfo 0
  • Contributing

    Contributing

    Hey @seandstewart, I have been using typical for a while now and I absolutely love it, far better than every other similar tool out there. I would really like to offer some help to advance it's capability and contribute to some of the features I have seen on your roadmap.

    What things do you most need help with, and if you are open to the idea of some help do you have a platform (Discord etc.) we can discuss on?

    Many Thanks!

    opened by ahobsonsayers 3
  • Documentation for typic.register

    Documentation for typic.register

    Description

    In the documentation under the warning about handling unions it is specified that to handle unions we have to define a custom converter, however the link in the documentation goes nowhere.

    Through browsing the issues I found out about the ability to define a coercer & type-checker which can then be registered with typic.register. I think i have mostly figured out how to use this, but it would be useful if there was some documentation about it and how it can be used.

    I would be very willing to open a PR myself but I don't think I fully understand the API.

    Also wanted to say i think typical is great, thanks for the wonderful library!

    enhancement 
    opened by ahobsonsayers 3
  • TypeVar Support

    TypeVar Support

    • typical version: 2.0.25
    • Python version: 3.8.1
    • Operating System: Unix

    Description

    Typical currently stumbles over classes which make use of TypeVar in their definitions, such as Generics. At the very least, we should be able to treat anonymous (unbound) typevars as "Any", and at best, map bound TypeVars correctly.

    Implementation

    1. Implement TypeVar resolution, perhaps in Resolver.annotation, to determine whether this should be treated as a Union or Any. If the TypeVar is constrained, treat it as a Union for resolution, otherwise, an Any:
    # If done as a branch in `Resolver.annotation`
    if use.__class__ is TypeVar:
        if use.__constraints__:
            use = Union[*use.__constraints__]
        else:
            use = Any
    

    For consideration - do we want to create a NewType for this Union?

    We probably also want to retain a flag on the ResolvedAnnotation that signals this came from a TypeVar.

    1. Implement resolution for Generics - this will take additional experimentation, and will likely need to tweak internal funcsigs. The easiest way to pre-emptively map subscripted generics to their given bound types:
    import typic
    from typing import Generic, TypeVar, get_type_hints
    import dataclasses
    T = TypeVar("T")
    @dataclasses.dataclass
    class Foo(Generic[T]):
        bar: T
    
    FooStrT = Foo[str]
    
    bindings = dict(zip(Foo.__parameters__, FooStrT.__args__))
    # If no bindings, our TypeVar resolution works for us. Otherwise, we should override the TypeVar anno with the binding.
    hints = {name: bindings.get(t, t) for name, t in get_type_hints(FooStrT)}
    
    opened by seandstewart 0
  • how to define custom transmute?

    how to define custom transmute?

    • typical version: 2.0.15
    • Python version: 3.7.4
    • Operating System: Centos

    Description

    Hey, @seandstewart . There's some cases which user want to define custom transmute:

    import json
    import typic
    from typing import Any
    
    @typic.klass
    class Foo:
        a: int
        b: str
        
        @classmethod
        def transmute(cls, v: Any) -> 'Foo':
            if isinstance(v, str):
                try:
                    v = json.loads(v)
                except:
                    pass
            if isinstance(v, str):
                v = v.split(',')
            if isinstance(v, list):
                return cls(*v)
            return typic.transmute(cls, v)
    

    Expect:

    Foo.transmute('1,abc')
    # >>> Foo(a=1, b='abc')
    

    However, after decorated by typic.klass, transmute is replaced.

    What I Did

    Currently, I have to define a metaclass as following:

    import json
    import typic
    from typing import Any, Dict, Tuple, Type
    from types import MethodType
    
    class FooMeta(type):
        def __new__(mcs, name: str, bases: Tuple[Type, ...], namespace: Dict[str, Any]):
            cls = type.__new__(mcs, name, bases, namespace)
            transmute = getattr(cls, 'transmute', None)
            cls = typic.klass(cls)
            if isinstance(transmute, MethodType):
                setattr(cls, 'transmute', transmute)
                typic.register(transmute, lambda a: a is cls)
            return cls
    
        
    class Foo(metaclass=FooMeta):
        a: int
        b: str
        
        @classmethod
        def transmute(cls, v: Any) -> 'Foo':
            if isinstance(v, str):
                try:
                    v = json.loads(v)
                except:
                    pass
            if isinstance(v, str):
                v = v.split(',')
            if isinstance(v, list):
                return cls(*v)
            return typic.transmute(cls, v)
    
    @typic.al
    def foo(f: Foo):
        print(f)
    
    foo('1,abc')  # Foo(a=1, b='abc')
    

    But, there's some error with pycharm type hinting.

    enhancement 
    opened by xbanke 1
  • Support for typing.Iterator and better support for typing.Iterable.

    Support for typing.Iterator and better support for typing.Iterable.

    • typical version: 2.0.17
    • Python version: 3.7.4
    • Operating System:

    Description

    Iterable can be infinitely.

    
    from typing import Iterable
    import typic
    
    def infinite_numbers():
        i = 0
        while 1:
            yield i
            i += 1
    
    @typic.al
    def foo(iterable: Iterable[int]):
        print(iterable)
    
    foo(range(5))  # [0, 1, 2, 3, 4]
    foo(infinite_numbers())  # never stop
    
    

    What I Did

    Paste the command(s) you ran and the output.
    If there was a crash, please include the traceback here.
    
    enhancement 
    opened by xbanke 1
  • Dynamic default value or factory for function argument

    Dynamic default value or factory for function argument

    • typical version: 2.0.1
    • Python version: 3.7.4
    • Operating System:

    Description

    Hey, @seandstewart . I have another feature request. For data model, there is default_factory which can be used to set dynamic default value. For function argument, if set the default value as a instance of typic.Field, it won't be validated as a default value. datetime.now as a default_factory is quite common. Every time I have to handle it like this:

    from datetime import datetime
    import typic
    
    @typic.al
    def foo(dt: datetime = None):
        dt = datetime.now() if dt is None else dt
        ...
    

    Especilly when we have many functions with dynamic default value, it would be a problem. Currently, beacuse the default value would not be validated, I defined a Helper class to solve this.

    from typing import Callable, Optional, Any
    from functools import wraps
    import inspect
    from dataclasses import MISSING
    import typic
    
    class DefaultArgument:
        def __init__(self, default: Any = MISSING, *, factory: Optional[Callable] = None):
            assert default is not MISSING or callable(factory)
            self.default = default
            self.factory = factory
    
        def __call__(self, *args, **kwargs):
            if self.default is not MISSING:
                return self.default
            return self.factory(*args, **kwargs)
    
        def __repr__(self):
            return f"{self.__class__.__name__}(default={self.default}, factory={self.factory!r})"
    
    
    def validate(func: Optional[Callable] = None, *, coerce: bool = True, strict: bool = False) -> Callable:
        def _validate(_func: Callable):
            @wraps(func)
            def wrapped(*args, **kwargs):
                bind = inspect.signature(_func).bind(*args, **kwargs)
                bind.apply_defaults()  # apply_defaults is not available in typic.bind
                for k, v in bind.arguments.items():
                    if isinstance(v, DefaultArgument):
                        bind.arguments[k] = v()
                return typic.bind(_func, *bind.args, partial=False, coerce=coerce, strict=strict, **bind.kwargs).eval()
    
            return wrapped
    
        if callable(func):
            return _validate(func)
        else:
            return _validate
    
    

    Now we can redefine foo like this:

    from datetime import datetime
    
    @validate
    def foo(dt: datetime = DefaultArgument(factory=datetime.now)):
        ...
    
    

    By the way, what if the user want to use the key words such as dict, schema, coerce, strict and so on, as the function argument or data attribute?

    enhancement 
    opened by xbanke 2
  • Is it possible to simplify registering user defined type?

    Is it possible to simplify registering user defined type?

    • typical version: 2.0.1
    • Python version: 3.7
    • Operating System: CentOS 8

    Description

    Hi, I like typical and I think it's more lightweight than pydantic. But add user defined type by typic.register is something tedious or there's another convenient way I didn't found. If so, is it possible to simplify it like pydantic's __get_validators__ or some else?

    enhancement 
    opened by xbanke 4
  • Custom validation feature request

    Custom validation feature request

    Description

    Would like to see a feature to implement custom validations that could span multiple fields. For example:

    Let's say you had a vacation start date and vacation end date field. Vacation start date should be before vacation end date.

    enhancement 
    opened by jae-lee 2
Releases(v2.8.0)
  • v2.8.0(Dec 23, 2021)

    What's Changed

    • Improved Serialization by @seandstewart in https://github.com/seandstewart/typical/pull/190

    Full Changelog: https://github.com/seandstewart/typical/compare/v2.7.11...v2.8.0

    Source code(tar.gz)
    Source code(zip)
  • v2.7.11(Dec 15, 2021)

    What's Changed

    • CPython 3.9.8+ and 3.10.1+ have new logic for type resolution of ForwardRefs which caused a regression in our runtime type analysis. This change adds handling for this new logic.

    Full Changelog: https://github.com/seandstewart/typical/compare/v2.7.10...v2.7.11

    Source code(tar.gz)
    Source code(zip)
  • v2.7.10(Dec 15, 2021)

    This release fixes a regression in our serializer factory which failed to account for optional/nullable enum types when dumping to a primitive, leading to an AttributeError when attempting to access the enum value.

    Source code(tar.gz)
    Source code(zip)
  • v2.7.9(Nov 3, 2021)

    What's Changed

    • Fix Decimal serdes by @qhelix7 in https://github.com/seandstewart/typical/pull/189

    New Contributors

    • @qhelix7 made their first contribution in https://github.com/seandstewart/typical/pull/189

    Full Changelog: https://github.com/seandstewart/typical/compare/v2.7.8...v2.7.9

    Source code(tar.gz)
    Source code(zip)
  • v2.7.8(Nov 2, 2021)

    sqlite3.Row objects are C extension types which meet the contract for a mapping, but do not evaluate as a subclass of the Mapping generic. This change adds them to our list of "mapping-compliant" types.

    Source code(tar.gz)
    Source code(zip)
  • v2.7.6(Nov 1, 2021)

    NamedTuple objects were improperly treated as a simple builtin subtype when coercing user-defined types, which resulted in unexpected behavior. This fixes deserialization logic to use the standard translator protocol for named tuples.

    Source code(tar.gz)
    Source code(zip)
  • v2.7.5(Oct 15, 2021)

    Derived classes of a typic.klass object were improperly recognized as a simple Iterable.

    This caused a recursion error when attempting to serialize these objects, since the __iter__ magic method relies upon the iterator factory, but the iterator factory simply called the __iter__ magic method.

    Resolves #185

    Source code(tar.gz)
    Source code(zip)
  • v2.7.4(Oct 11, 2021)

  • v2.7.3(Oct 8, 2021)

    Fixes:

    • Support collections.deque as Array constraint (resolves #181)
    • Fix union constraints generation on stable py3.10 release.

    Misc:

    • Extend DSN support for sqlite urls.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.2(Oct 5, 2021)

    Fixes

    • Nested mappings with exactly two keys could be improperly deserialized (#179)
    • Early return with stdlib get_tojson meant we weren't properly bootstrapping docs.

    Improvements

    • asyncpg.Record objects are now recognized as a Mapping type.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.1(Sep 28, 2021)

    Enhancements

    • typical will now default to orjson for json serialization and deserialization if it is installed. This provides up to 3X performance boost, but has different behavior and output than ujson or stdlib json.
    • Callable class instances are now treated as function types when wrapped (thank you @xbanke, #173)

    Fixes

    • Expose the always parameter to typic.klass, since we are raising warnings about its usage (#177)
    Source code(tar.gz)
    Source code(zip)
  • v2.7.0(Sep 21, 2021)

    Features

    • Wrapped routines and frozen dataclasses now coerce inputs up to 10x faster than before (on-par with wrapped classes or bound protocols) (#175).
    • More descriptive and correct type-hints for the public API.
    • Type-hinting with an abstract base class (ABC) will now result in validation against that type rather than coercion (#170)

    Fixes

    • Resolves issue where we'd sometimes fail to clean up the repr of subscripted generics (#170)
    • Resolves issue where we failed to locate the correct target type for a value within a Union of overlapping types (#170)
    • Resolves issue where we'd iterate over the fields of a non-iterable, primitive type when coercing to a collection, rather than raise a TypeError as expected (#174).
    Source code(tar.gz)
    Source code(zip)
  • v2.6.3(Sep 1, 2021)

    Fixes

    • Add intelligent instance-checks for unions (resolves #167)
    • Make field.default positional (resolves #168)
    • Add __version__ to typic.__init__ and add bumpver to manage multiple versions (resolves #168)
    Source code(tar.gz)
    Source code(zip)
  • v2.6.2(Aug 26, 2021)

    Fixes

    • This release resolves an issue when building serializers for Unions of pandas collection types (e.g., DataFrame, Series) which prevented proper compilation (#166)

    Changes

    • Tagged Unions must be tagged with "public" fields (e.g., no fields starting with _ are considered).
    Source code(tar.gz)
    Source code(zip)
  • v2.6.1(Aug 26, 2021)

  • v2.6.0(Aug 24, 2021)

    Features

    • Support un-tagged, "Generic" Unions (e.g., Union[int, str]) (#19, docs)
    • Basic support for Callable types (#159)

    Misc

    • Re-worked handler dispatch in deserializer builder.
    • More idiomatic implementation of delayed SerdeProtocols
    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Aug 19, 2021)

    Features

    • This release adds support for Python @ 3.10.0rc1 (#162)

    Bugfixes

    • Fix resolution of env-var aliases.
    • Use lazy iterator factory when we can't guess the type.
    Source code(tar.gz)
    Source code(zip)
  • v2.4.2(Jul 21, 2021)

    Bugfixes

    • The new serialization protocol didn't properly check for whether the type was a TypedDict.
    • Translation and Iteration now work as expected against TypeDict types.

    Misc

    • Allow __slots__ to indicates fields which can be used during iteration and translation.
    Source code(tar.gz)
    Source code(zip)
  • v2.4.1(Jul 20, 2021)

  • v2.4.0(Jul 14, 2021)

    Features

    • Improve serialization performance by 3x (#161)
    • Add iterate(...) to top-level API (#161, docs)
    • Add support for "downcasting" an object to a naive collection or iterator in translator (#161)
    • Add support for PEP 585 builtin generics and PEP 604 Union Operators (#161)
    • Advanced type hints for SerdeProtocol using Generics and Protocols, to support SerdeProtocol[int], etc. (#158)

    Bugfixes

    • Fix support for NamedTuples in translation and iteration (#161)
    Source code(tar.gz)
    Source code(zip)
  • v2.3.3(Jul 2, 2021)

    Previously, if a default value was provided for a field in a settings class, we wouldn't lookup field in the environment. This was counter-intuitive. Now, if a default value is provided, we will defer to the environment before using the provided default.

    Source code(tar.gz)
    Source code(zip)
  • v2.3.2(Jul 2, 2021)

  • v2.3.1(Jul 2, 2021)

  • v2.3.0(Jun 8, 2021)

  • 2.2.0(Jun 7, 2021)

    Features

    • Add UPPER-KEBAB-CASE and UPPER.DOT.CASE (#153) as field-name output options.
    • Add support for custom encoders and decoders (#156) (Documentation here and here).
    • Updated translator to support SQLAlchemy 1.4.

    Chores

    • Cleaned up some dead code-paths.
    • Cleaned up some unwieldy interfaces.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.3(Apr 27, 2021)

    Types defined within the local namespace of a function could not be resolved if they were forward references. This change introduces support for these types. (Resolves #150)

    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Mar 3, 2021)

    When determining whether a Union of types has a tag which may be used in deserialization, member descriptors should not be considered valid.

    Additionally, drop usage of guard_recursion() in the slotted() decorator.

    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Feb 10, 2021)

Owner
Sean
New-York based Pythonista ? and Software Engineer ?‍?
Sean
Optional static typing for Python 3 and 2 (PEP 484)

Mypy: Optional Static Typing for Python Got a question? Join us on Gitter! We don't have a mailing list; but we are always happy to answer questions o

Python 12.1k Jan 16, 2022
Typing-toolbox for Python 3 _and_ 2.7 w.r.t. PEP 484.

Welcome to the pytypes project pytypes is a typing toolbox w.r.t. PEP 484 (PEP 526 on the road map, later also 544 if it gets accepted). Its main feat

Stefan Richthofer 172 Jan 10, 2022
Unbearably fast O(1) runtime type-checking in pure Python.

Look for the bare necessities, the simple bare necessities. Forget about your worries and your strife. — The Jungle Book.

null 1k Jan 21, 2022
pycallgraph is a Python module that creates call graphs for Python programs.

Project Abandoned Many apologies. I've stopped maintaining this project due to personal time constraints. Blog post with more information. I'm happy t

gak 1.6k Jan 14, 2022
Turn your Python and Javascript code into DOT flowcharts

Notes from 2017 This is an older project which I am no longer working on. It was built before ES6 existed and before Python 3 had much usage. While it

Scott Rogowski 1.7k Jan 20, 2022
Inspects Python source files and provides information about type and location of classes, methods etc

prospector About Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and comple

Python Code Quality Authority 1.6k Jan 15, 2022
Find dead Python code

Vulture - Find dead code Vulture finds unused code in Python programs. This is useful for cleaning up and finding errors in large code bases. If you r

Jendrik Seipp 2k Jan 17, 2022
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Kirill Klenov 849 Jan 18, 2022
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

wemake.services 1.7k Jan 20, 2022
The uncompromising Python code formatter

The Uncompromising Code Formatter “Any color you like.” Black is the uncompromising Python code formatter. By using it, you agree to cede control over

Python Software Foundation 24.4k Jan 22, 2022
A Python utility / library to sort imports.

Read Latest Documentation - Browse GitHub Code Repository isort your imports, so you don't have to. isort is a Python utility / library to sort import

Python Code Quality Authority 4.4k Jan 14, 2022
A formatter for Python files

YAPF Introduction Most of the current formatters for Python --- e.g., autopep8, and pep8ify --- are made to remove lint errors from code. This has som

Google 12.3k Jan 14, 2022
Performant type-checking for python.

Pyre is a performant type checker for Python compliant with PEP 484. Pyre can analyze codebases with millions of lines of code incrementally – providi

Facebook 5.7k Jan 22, 2022
Collection of library stubs for Python, with static types

typeshed About Typeshed contains external type annotations for the Python standard library and Python builtins, as well as third party packages as con

Python 2.6k Jan 14, 2022
A system for Python that generates static type annotations by collecting runtime types

MonkeyType MonkeyType collects runtime types of function arguments and return values, and can automatically generate stub files or even add draft type

Instagram 3.6k Jan 20, 2022
A static type analyzer for Python code

pytype - ? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Google 3.6k Jan 13, 2022
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 7.6k Jan 22, 2022
A static analysis tool for Python

pyanalyze Pyanalyze is a tool for programmatically detecting common mistakes in Python code, such as references to undefined variables and some catego

Quora 155 Jan 20, 2022
Run-time type checker for Python

This library provides run-time type checking for functions defined with PEP 484 argument (and return) type annotations. Four principal ways to do type

Alex Grönholm 780 Jan 14, 2022