Skip to content

Commit

Permalink
Release 0.12.0 (#31)
Browse files Browse the repository at this point in the history
* Reorganize typeit.schema

* Add support for pyrsistent PVector and PMap

* Add support for Literals #21

* Release 0.12.0
  • Loading branch information
Maxim Avanov committed Apr 24, 2019
1 parent 30ff97f commit 0000747
Show file tree
Hide file tree
Showing 23 changed files with 639 additions and 393 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -15,7 +15,7 @@ python:

# --source specifies what packages to cover, you probably want to use that option
script:
# - mypy --config-file setup.cfg --package typeit
# - make typecheck
- pytest --cov=typeit

after_success: coveralls
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -2,6 +2,13 @@
CHANGELOG
=========

0.12.0
============

* ``typeit.iter_invalid`` replaces ``typeit.utils.iter_invalid_data``.
* Add support for ``pyrsistent.typing.PVector`` and ``pyrsistent.typing.PMap`` types.
* Add support for ``Literals``.

0.11.0
============

Expand Down
7 changes: 7 additions & 0 deletions Makefile
@@ -0,0 +1,7 @@
PROJECT=typeit

test:
pytest -s tests/

typecheck:
mypy --config-file setup.cfg --package $(PROJECT)
4 changes: 2 additions & 2 deletions docs/conf.py
Expand Up @@ -51,9 +51,9 @@
# built documents.
#
# The short X.Y version.
version = '0.11'
version = '0.12'
# The full version, including alpha/beta/rc tags.
release = '0.11.0'
release = '0.12.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -38,7 +38,7 @@ def requirements(at_path: Path):
# ----------------------------

setup(name='typeit',
version='0.11.0',
version='0.12.0',
description='typeit brings typed data into your project',
long_description=README,
classifiers=[
Expand Down
89 changes: 84 additions & 5 deletions tests/std_types/test_typing.py
@@ -1,8 +1,10 @@
import collections
from typing import Mapping, Any
from typing import Mapping, Any, Union, Optional, Sequence
from typing import NamedTuple
from typing_extensions import Literal

from typeit import type_constructor
import pytest
from typeit import type_constructor, Invalid


def test_mapping():
Expand All @@ -11,19 +13,96 @@ class X(NamedTuple):
y: Mapping[str, Any]
z: collections.abc.Mapping

mk_x, dict_x = type_constructor(X)
mk_x, dict_x = type_constructor ^ X


def test_sequence():
class X(NamedTuple):
xs: collections.abc.Sequence

mk_x, dict_x = type_constructor(X)
mk_x, dict_x = type_constructor ^ X


def test_sets():
class X(NamedTuple):
xs: collections.abc.Set
ys: collections.abc.MutableSet

mk_x, dict_x = type_constructor(X)
mk_x, dict_x = type_constructor ^ X


def test_literals():
class X(NamedTuple):
x: Literal[1]
y: Literal[1, 'a']
z: Literal[None, 1]

mk_x, dict_x = type_constructor ^ X

data = {
'x': 1,
'y': 'a',
'z': None,
}
x = mk_x(data)
assert x.x == 1
assert x.y == 'a'
assert x.z is None
assert dict_x(x) == data

data = {
'x': 1,
'y': 1,
'z': 1
}
x = mk_x(data)
assert x.y == 1
assert x.z == 1

for case in (
{
'x': '1',
'y': 'a',
},
{
'x': 2,
'y': 'a',
},
):
with pytest.raises(Invalid):
mk_x(case)

x = X(None, None, 3)
with pytest.raises(Invalid):
dict_x(x)


def test_literals_included():
class X(NamedTuple):
x: Union[Literal[1], None]
y: Optional[Literal[1]]
z: Sequence[Literal[1]]

mk_x, dict_x = type_constructor ^ X

data = {
'x': None,
'y': None,
'z': [1],
}
x = mk_x(data)
assert x.z == [1]
assert dict_x(x) == data

mk_x({
'x': None,
'y': None,
'z': [1, 1],
})

with pytest.raises(Invalid):
mk_x({
'x': None,
'y': None,
'z': [1, 2],
})
31 changes: 16 additions & 15 deletions tests/test_parser.py
Expand Up @@ -7,6 +7,7 @@
from money.currency import Currency
from money.money import Money

import typeit
from typeit import codegen as cg
from typeit import parser as p
from typeit import flags
Expand Down Expand Up @@ -66,10 +67,10 @@ class X(NamedTuple):
d=1,
)

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
mk_x(data)

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
dict_x(data_X)

assert mk_x_nonstrict(data) == X(
Expand Down Expand Up @@ -154,22 +155,22 @@ class X(NamedTuple):
assert x.b == ()
assert x.b == x.c

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
# 'abc' is not int
x = mk_x({
'a': ['value', 'abc'],
'b': [],
'c': [],
})

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
# .c field is required
x = mk_x({
'a': ['value', 5],
'b': [],
})

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
# .c field is required to be fixed sequence
x = mk_x({
'a': ['value', 'abc'],
Expand Down Expand Up @@ -238,7 +239,7 @@ class X(NamedTuple):
assert isinstance(x.s('value'), Sums)
assert data == dict_x(x)

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
x = mk_x({'e': 'a', 's': None})


Expand Down Expand Up @@ -278,7 +279,7 @@ class X(NamedTuple):
x = mk_x({'x': 1, 'y': variant.value})
assert x.y is variant

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
x = mk_x({'x': 1, 'y': None})


Expand Down Expand Up @@ -313,7 +314,7 @@ class X(NamedTuple):


def test_schema_node():
x = schema.SchemaNode(schema.primitives.Int())
x = schema.nodes.SchemaNode(schema.primitives.Int())
assert x.__repr__().startswith('SchemaNode(<typeit.schema.primitives.Int ')


Expand All @@ -327,10 +328,10 @@ class X(NamedTuple):

mk_x, serializer = p.type_constructor(X)

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
mk_x({})

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
mk_x({'x': 1})

x = mk_x({'x': 1, 'y': {'x': 1}})
Expand Down Expand Up @@ -368,7 +369,7 @@ class X(NamedTuple):

data = {'my-x': 1}

with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
mk_x, dict_x = p.type_constructor ^ X
mk_x(data)

Expand All @@ -382,20 +383,20 @@ def test_extending():
class X(NamedTuple):
x: Money

class MoneySchema(schema.Tuple):
class MoneySchema(schema.types.Tuple):
def deserialize(self, node, cstruct):
r = super().deserialize(node, cstruct)
if r in (colander.null, None):
return r
try:
currency = Currency(r[0])
except ValueError:
raise colander.Invalid(node, f'Invalid currency token in {r}', cstruct)
raise typeit.Invalid(node, f'Invalid currency token in {r}', cstruct)

try:
rv = Money(r[1], currency)
except:
raise colander.Invalid(node, f'Invalid amount in {r}', cstruct)
raise typeit.Invalid(node, f'Invalid amount in {r}', cstruct)

return rv

Expand All @@ -412,7 +413,7 @@ def serialize(self, node, appstruct: Optional[Money]):

mk_x, dict_x = (
p.type_constructor
& MoneySchema[Money] << schema.Enum(Currency) << schema.primitives.NonStrictStr()
& MoneySchema[Money] << schema.types.Enum(Currency) << schema.primitives.NonStrictStr()
^ X
)

Expand Down
34 changes: 34 additions & 0 deletions tests/test_pyrsistent.py
@@ -0,0 +1,34 @@
from typing import NamedTuple, Any, Optional

import pytest

import typeit as ty
import pyrsistent as ps


def test_pyrsistent_types():
class X(NamedTuple):
a: ps.typing.PMap[str, Any]
b: ps.typing.PVector[ps.typing.PMap]
c: Optional[ps.typing.PMap]

mk_x, dict_x = ty.type_constructor ^ X

data = {
'a': {'x': 'x', 'y': 'y'},
'b': [{'x': 'x', 'y': 'y'}],
'c': None
}
x = mk_x(data)
assert isinstance(x.a, ps.PMap)
assert isinstance(x.b, ps.PVector)
assert isinstance(x.b[0], ps.PMap)
assert dict_x(x) == data

data = {
'a': None,
'b': [{'x': 'x', 'y': 'y'}],
'c': {'x': 'x', 'y': 'y'}
}
with pytest.raises(ty.Invalid):
mk_x(data)
6 changes: 3 additions & 3 deletions tests/test_utils.py
@@ -1,8 +1,8 @@
import typeit
from typeit import utils
from typing import NamedTuple, Sequence
from typeit import type_constructor
from enum import Enum
import colander


def test_normalization():
Expand Down Expand Up @@ -40,6 +40,6 @@ class X(NamedTuple):

try:
x = mk_x(data)
except colander.Invalid as e:
for inv in utils.iter_invalid_data(e, data):
except typeit.Invalid as e:
for inv in utils.iter_invalid(e, data):
assert isinstance(inv, utils.InvalidData)
5 changes: 3 additions & 2 deletions tests/unions/test_unions.py
@@ -1,8 +1,9 @@
from typing import NamedTuple, Union, Mapping, Any, Dict
from typing import NamedTuple, Union, Any, Dict

import colander
import pytest

import typeit
from typeit import type_constructor, parser as p, flags
from typeit.compat import PY36

Expand Down Expand Up @@ -31,7 +32,7 @@ class X(NamedTuple):
assert data == dict_x(x)

assert mk_x({'x': None, 'y': 'y'}) == mk_x({'y': 'y'})
with pytest.raises(colander.Invalid):
with pytest.raises(typeit.Invalid):
# this is not the same as mk_x({}),
# the empty structure is passed as attribute x,
# which should match with only an empty named tuple definition,
Expand Down
4 changes: 3 additions & 1 deletion typeit/__init__.py
@@ -1,5 +1,7 @@
from .parser import type_constructor
from .codegen import typeit
from . import flags
from .schema.errors import Invalid
from .utils import iter_invalid

__all__ = ['type_constructor', 'typeit', 'flags']
__all__ = ['type_constructor', 'typeit', 'flags', 'Invalid', 'iter_invalid']

0 comments on commit 0000747

Please sign in to comment.