Skip to content

Commit

Permalink
Merge 7898fbb into 30ff97f
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim Avanov committed Apr 23, 2019
2 parents 30ff97f + 7898fbb commit 297fa10
Show file tree
Hide file tree
Showing 18 changed files with 478 additions and 378 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 Makefile
@@ -0,0 +1,7 @@
PROJECT=typeit

test:
pytest -s tests/

typecheck:
mypy --config-file setup.cfg --package $(PROJECT)
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']
15 changes: 8 additions & 7 deletions typeit/cli/run.py
Expand Up @@ -2,36 +2,37 @@
import json
import sys
from pathlib import Path
from typing import Dict

from .. import codegen as cg


def setup(subparsers):
def setup(subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser:
sub = subparsers.add_parser('gen', help='Generate hints for structured data (JSON, YAML)')
sub.add_argument('-s', '--source', help="Path to structured data (JSON, YAML). "
"If not specified, then the data will be read from stdin.")
sub.set_defaults(func=main)
return sub


def main(args: argparse.Namespace):
def main(args: argparse.Namespace) -> None:
""" $ typeit gen <source> <target>
"""
try:
with Path(args.source).open('r') as f:
struct = _read_data(f)
dict_struct = _read_data(f)
except TypeError:
# source is None, read from stdin
struct = _read_data(sys.stdin)
dict_struct = _read_data(sys.stdin)

struct, overrides = cg.typeit(struct)
struct, overrides = cg.typeit(dict_struct)
python_src, __ = cg.codegen_py(struct, overrides)
sys.stdout.write(python_src)
sys.stdout.write('\n')
sys.exit(0)


def _read_data(fd):
def _read_data(fd) -> Dict:
buf = fd.read() # because stdin does not support seek
try:
struct = json.loads(buf)
Expand All @@ -45,5 +46,5 @@ def _read_data(fd):
"dependency, or use the `third_party` extra tag with typeit:\n\n"
"$ pip install typeit[third_party]"
)
struct = yaml.load(buf, Loader=yaml.FullLoader)
struct = yaml.full_load(buf)
return struct
10 changes: 5 additions & 5 deletions typeit/codegen.py
Expand Up @@ -140,7 +140,7 @@ def literal_for_type(typ: Type[iface.IType]) -> str:
try:
return BUILTIN_LITERALS_FOR_TYPES[typ](typ)
except KeyError:
if typ.__class__ is List.__class__:
if typ.__class__ is List.__class__: # type: ignore
sub_type = literal_for_type(typ.__args__[0])
return f'List[{sub_type}]'
# typ: NamedTuple
Expand Down Expand Up @@ -265,7 +265,7 @@ def _clarify_field_type_list(field_name: str,


def construct_type(name: str,
fields: List[FieldDefinition]) -> Tuple[Type[iface.IType], OverridesT]:
fields: List[FieldDefinition]) -> Tuple[Type, OverridesT]:
""" Generates a NamedTuple type structure out of provided
field definitions.
Expand All @@ -282,8 +282,8 @@ def construct_type(name: str,
if c.field_name != c.source_name:
overrides[c.field_name] = c.source_name

typ = NamedTuple(inflection.camelize(name), type_fields)
type_overrides = pmap({
typ = NamedTuple(inflection.camelize(name), type_fields) # type: ignore
type_overrides: PMap[property, str] = pmap({
getattr(typ, k): v for k, v in overrides.items()
})
return typ, type_overrides
return typ, type_overrides # type: ignore
11 changes: 1 addition & 10 deletions typeit/definitions.py
@@ -1,4 +1,4 @@
from typing import NamedTuple, Union, Any, Mapping, Type
from typing import NamedTuple, Union, Any, Type

from pyrsistent import pmap
from pyrsistent.typing import PMap
Expand All @@ -19,15 +19,6 @@
]


OverrideT = Union[
# flag override
_Flag,
# new type extension
Type,
Mapping[property, str],
]


NO_OVERRIDES: OverridesT = pmap()


Expand Down

0 comments on commit 297fa10

Please sign in to comment.