Skip to content

Commit

Permalink
Add support for typing.Sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim Avanov committed Jun 23, 2018
1 parent 35e166b commit 063a023
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 9 deletions.
16 changes: 15 additions & 1 deletion tests/test_parser.py
@@ -1,5 +1,5 @@
import json
from typing import NamedTuple, Dict, Any
from typing import NamedTuple, Dict, Any, Sequence

import colander
import pytest
Expand All @@ -18,6 +18,20 @@ def test_typeit():
typeit(x)


def test_type_with_sequence():
""" Create a type with an explicit dictionary value
that can hold any kv pairs
"""
class X(NamedTuple):
x: int
y: Sequence[Any]

MkX = p.type_constructor(X)

x: X = MkX({'x': 1, 'y': []})
assert x.y == []


def test_type_with_dict():
""" Create a type with an explicit dictionary value
that can hold any kv pairs
Expand Down
36 changes: 28 additions & 8 deletions typeit/parser.py
@@ -1,8 +1,8 @@
import re
from typing import (
Type, Tuple, Optional, Any, Union, List,
Dict, NamedTuple, Callable
)
Dict, NamedTuple, Callable,
Sequence)

import inflection
import colander as col
Expand Down Expand Up @@ -141,21 +141,41 @@ def clarify_field_type_list(field_name: str,
}


NORMALIZATION_PREFIX = 'normalized__'

RESERVED_WORDS = {
'and', 'del', 'from',
'not', 'while','as',
'elif', 'global', 'or',
'with','assert', 'else',
'if', 'pass', 'yield',
'break', 'except', 'import',
'print', 'class', 'exec',
'in', 'raise', 'continue',
'finally', 'is', 'return',
'def', 'for', 'lambda', 'try',
}

NORMALIZED_RESERVED_WORDS = {
f'{NORMALIZATION_PREFIX}{x}' for x in RESERVED_WORDS
}


def normalize_name(name: str,
pattern=re.compile('^([_0-9]+).*$')) -> Tuple[str, bool]:
""" Some field name patterns are not allowed in NamedTuples
https://docs.python.org/3.7/library/collections.html#collections.namedtuple
"""
if pattern.match(name):
return f'normalized__{name}', True
if name in RESERVED_WORDS or pattern.match(name):
return f'{NORMALIZATION_PREFIX}{name}', True
return name, False


def denormalize_name(name: str) -> Tuple[str, bool]:
""" Undo normalize_name()
"""
if name.startswith('normalized__'):
return name[len('normalized__'):], True
if name in NORMALIZED_RESERVED_WORDS or name.startswith(NORMALIZATION_PREFIX):
return name[len(NORMALIZATION_PREFIX):], True
return name, False


Expand Down Expand Up @@ -199,9 +219,9 @@ def _maybe_node_for_optional(typ) -> Optional[col.SchemaNode]:

def _maybe_node_for_list(typ) -> Optional[col.SequenceSchema]:
# typ is List[T] where T is either unknown Any or a concrete type
if typ is List[Any]:
if typ in (List[Any], Sequence[Any]):
return col.SequenceSchema(col.SchemaNode(col.Str(allow_empty=True)))
elif insp.get_origin(typ) is List:
elif insp.get_origin(typ) in (List, Sequence):
inner = insp.get_last_args(typ)[0]
return col.SequenceSchema(decide_node_type(inner))
return None
Expand Down

0 comments on commit 063a023

Please sign in to comment.