Exemple #1
0
 def _parse_procedure_arguments(self, type_hints, request_json):
     arguments = {}
     version = type_hints.get('_v', 1)
     name_map = type_hints['_names']
     for argument_name, type_ in type_hints.items():
         if argument_name.startswith('_'):
             continue
         if version >= 2:
             type_ = type_()
         behind_name = name_map[argument_name]
         try:
             data = request_json[behind_name]
         except KeyError:
             raise NirumProcedureArgumentRequiredError(
                 "A argument named '{}' is missing, it is required.".format(
                     behind_name))
         try:
             arguments[argument_name] = deserialize_meta(type_, data)
         except ValueError:
             raise NirumProcedureArgumentValueError(
                 "Incorrect type '{0}' for '{1}'. "
                 "expected '{2}'.".format(typing._type_repr(data.__class__),
                                          behind_name,
                                          typing._type_repr(type_)))
     return arguments
Exemple #2
0
 def _parse_procedure_arguments(self, method_facial_name, request_json):
     type_hints = self.service.__nirum_service_methods__[method_facial_name]
     arguments = {}
     version = type_hints.get('_v', 1)
     name_map = type_hints['_names']
     errors = MethodArgumentError()
     for argument_name, type_ in type_hints.items():
         if argument_name.startswith('_'):
             continue
         if version >= 2:
             type_ = type_()
         behind_name = name_map[argument_name]
         try:
             data = request_json[behind_name]
         except KeyError:
             if is_optional_type(type_):
                 arguments[argument_name] = None
             else:
                 errors.on_error('.' + behind_name, 'Expected to exist.')
             continue
         try:
             arguments[argument_name] = deserialize_meta(type_, data)
         except ValueError:
             errors.on_error(
                 '.' + behind_name,
                 'Expected {0}, but {1} was given.'.format(
                     typing._type_repr(type_),
                     typing._type_repr(type(data))))
     errors.raise_if_errored()
     return arguments
Exemple #3
0
def check_tuple(data: typing.Tuple,
                hint: typing.Union[type, typing.TypingMeta]) -> bool:
    """Check argument type & return type of :class:`typing.Tuple`. since it
    raises check :class:`typing.Tuple` using `isinstance`, so compare in
    diffrent way

    :param data: tuple given as a argument
    :param hint: assumed type of given ``data``

    """
    if not isinstance(data, tuple):
        raise TypeError(
            'expected {}, not {}'.format(
                typing._type_repr(hint),
                'None' if data is None else '{}: {!r}'.format(
                    typing._type_repr(type(data)),
                    data
                )
            )
        )
    tuple_param = hint.__tuple_params__
    if len(data) != len(tuple_param):
        raise TypeError('expected tuple size is {}, not {}: '
                        '{!r}'.format(len(tuple_param), len(data), data))
    zipped = itertools.zip_longest(data, tuple_param)
    for i, (v, t) in enumerate(zipped):
        _, correct = check_type(v, t)
        if not correct:
            raise TypeError(
                '{0}th item `{1}` in tuple must be {2!r}, not: {3!r}'.format(
                    i, v, t, v
                )
            )
    return hint, True
Exemple #4
0
def test_readable_error_when_null_returned_from_null_disallowed_method(caplog):
    """Even if the method implementation returns None (FYI Python functions
    return None when it lacks return statement so that service methods are
    prone to return None by mistake) the error message should be readable
    and helpful for debugging.

    """
    expected_message = '''The return type of null-disallowed-method() method \
is not optional (i.e., no trailing question mark), but its server-side \
implementation has tried to return nothing (i.e., null, nil, None).  \
It is an internal server error and should be fixed by server-side.'''
    app = WsgiApp(NullDisallowedMethodServiceImpl(None))
    client = Client(app, Response)
    caplog.handler.records = []  # Clear log records
    response = client.post('/?method=null_disallowed_method',
                           data=json.dumps({}),
                           content_type='application/json')
    assert caplog.record_tuples and caplog.record_tuples[-1] == (
        '{0}.null_disallowed_method'.format(
            typing._type_repr(NullDisallowedMethodServiceImpl)),
        logging.ERROR,
        '''None is an invalid return value for the return type of {0}.\
null_disallowed_method() method.'''.format(
            typing._type_repr(NullDisallowedMethodServiceImpl)),
    )
    assert response.status_code == 500, response.get_data(as_text=True)
    actual = json.loads(response.get_data(as_text=True))
    assert actual == {
        '_type': 'error',
        '_tag': 'internal_server_error',
        'message': expected_message,
    }
Exemple #5
0
def deserialize_union_type(cls, value):
    if '_type' not in value:
        raise ValueError('"_type" field is missing.')
    if '_tag' not in value:
        raise ValueError('"_tag" field is missing.')
    if not hasattr(cls, '__nirum_tag__'):
        if hasattr(cls, '__nirum_tag_classes__'):
            tag = cls.Tag(value['_tag'])
            try:
                cls = cls.__nirum_tag_classes__[tag]
            except KeyError:
                raise ValueError(
                    '{0!r} is not deserialzable tag of {1} ({2!r})'.format(
                        value, typing._type_repr(cls), tag
                    )
                )
        else:
            # FIXME: This fallback for backward compatibility should be removed
            # in the future releases.
            # See also: https://github.com/spoqa/nirum/pull/192
            for sub_cls in cls.__subclasses__():
                if sub_cls.__nirum_tag__.value == value['_tag']:
                    cls = sub_cls
                    break
            else:
                raise ValueError(
                    '{0!r} is not deserialzable tag of {1}'.format(
                        value, typing._type_repr(cls)
                    )
                )
    if not cls.__nirum_union_behind_name__ == value['_type']:
        raise ValueError('{0} expect "_type" equal to'
                         ' "{1.__nirum_union_behind_name__}"'
                         ', but found {2}.'.format(typing._type_repr(cls), cls,
                                                   value['_type']))
    if not cls.__nirum_tag__.value == value['_tag']:
        raise ValueError('{0} expect "_tag" equal to'
                         ' "{1.__nirum_tag__.value}"'
                         ', but found {1}.'.format(typing._type_repr(cls),
                                                   cls, value['_tag']))
    args = {}
    behind_names = cls.__nirum_tag_names__.behind_names
    for attribute_name, item in value.items():
        if attribute_name in {'_type', '_tag'}:
            continue
        if attribute_name in behind_names:
            name = behind_names[attribute_name]
        else:
            name = attribute_name
        tag_types = cls.__nirum_tag_types__
        if callable(tag_types):  # old compiler could generate non-callable map
            tag_types = dict(tag_types())
        args[name] = deserialize_meta(tag_types[name], item)
    return cls(**args)
Exemple #6
0
def test_wsgi_app_error(caplog, fx_test_client):
    # method not allowed
    assert_response(
        fx_test_client.get('/?method=get_music_by_artist_name'), 405, {
            '_type': 'error',
            '_tag': 'method_not_allowed',
            'message': 'The requested URL / was not allowed HTTP method GET.'
        })
    # method missing
    assert_response(
        fx_test_client.post('/'), 400, {
            '_type': 'error',
            '_tag': 'bad_request',
            'message': u'`method` is missing.',
        })
    # invalid procedure name
    assert_response(
        fx_test_client.post('/?method=foo'), 400, {
            '_type': 'error',
            '_tag': 'bad_request',
            'message': 'No service method `foo` found.'
        })
    # invalid json
    assert_response(
        fx_test_client.post('/?method=get_music_by_artist_name',
                            data="!",
                            content_type='application/json'), 400, {
                                '_type': 'error',
                                '_tag': 'bad_request',
                                'message': "Invalid JSON payload: '!'."
                            })
    # incorrect return
    caplog.handler.records = []  # Clear log records
    response = fx_test_client.post('/?method=incorrect_return')
    assert caplog.record_tuples and caplog.record_tuples[-1] == (
        typing._type_repr(MusicServiceImpl) + '.incorrect_return',
        logging.ERROR,
        '''1 is an invalid return value for the return type ({0}) of {1}.\
incorrect_return() method.'''.format('unicode' if PY2 else 'str',
                                     typing._type_repr(MusicServiceImpl)),
    )
    assert_response(
        response, 500, {
            '_type':
            'error',
            '_tag':
            'internal_server_error',
            'message':
            '''The return type of the incorrect-return() method is \
{0}, but its server-side implementation has tried to return a value of \
an invalid type.  It is an internal server error and should be fixed by \
server-side.'''.format('unicode' if PY2 else 'str')
        })
Exemple #7
0
 def __init__(self):
     for method_name in self.__nirum_service_methods__:
         try:
             method = getattr(self, method_name)
         except AttributeError:
             raise InvalidNirumServiceMethodNameError(
                 "{0}.{1} inexist.".format(
                     typing._type_repr(self.__class__), method_name))
         if not callable(method):
             raise InvalidNirumServiceMethodTypeError(
                 "{0}.{1} isn't callable".format(
                     typing._type_repr(self.__class__), method_name))
Exemple #8
0
 def __init__(self):
     for method_name in self.__nirum_service_methods__:
         try:
             method = getattr(self, method_name)
         except AttributeError:
             raise InvalidNirumServiceMethodNameError(
                 '{0}.{1}() method has to be implemented.'.format(
                     typing._type_repr(type(self)), method_name))
         if not callable(method):
             raise InvalidNirumServiceMethodTypeError(
                 '{0}.{1} has to be callable so that is a method'.format(
                     typing._type_repr(type(self)), method_name))
Exemple #9
0
    def _link_type_args(self, obj: Any, reverse_index: Dict[int, str],
                        linker: signature_lib.FormatArguments) -> str:
        """Recurses into typehint object and links known objects to their pages."""
        arg_full_name = reverse_index.get(id(obj), None)
        if arg_full_name is not None:
            return linker.get_link(arg_full_name)

        result = []
        if getattr(obj, '__args__', None):
            for arg in obj.__args__:
                result.append(self._link_type_args(arg, reverse_index, linker))
            origin_str = typing._type_repr(obj.__origin__)  # pylint: disable=protected-access # pytype: disable=module-attr
            result = self._custom_join(result, origin_str)
            return f'{origin_str}[{result}]'
        else:
            return typing._type_repr(obj)  # pylint: disable=protected-access # pytype: disable=module-attr
Exemple #10
0
def deserialize_record_type(cls, value):
    if '_type' not in value:
        raise ValueError('"_type" field is missing.')
    if not cls.__nirum_record_behind_name__ == value['_type']:
        raise ValueError(
            '{0} expect "_type" equal to "{1.__nirum_record_behind_name__}"'
            ', but found {2}.'.format(
                typing._type_repr(cls),
                cls, value['_type']
            )
        )
    args = {}
    behind_names = cls.__nirum_field_names__.behind_names
    field_types = cls.__nirum_field_types__
    if callable(field_types):
        field_types = field_types()
        # old compiler could generate non-callable dictionary
    for attribute_name, item in value.items():
        if attribute_name == '_type':
            continue
        if attribute_name in behind_names:
            name = behind_names[attribute_name]
        else:
            name = attribute_name
        args[name] = deserialize_meta(field_types[name], item)
    return cls(**args)
Exemple #11
0
    def __getitem__(self, params):
        if not isinstance(params, tuple):
            params = (params,)
        if not params:
            raise TypeError("Cannot have empty parameter list")
        params = tuple(params)
        if self.__parameters__ is None:
            for p in params:
                if not isinstance(p, (TypeVar, TypeParam)):
                    raise TypeError("Initial parameters must be " "type variables; got %s" % p)
            if len(set(params)) != len(params):
                raise TypeError("All type variables in Generic[...] must be distinct.")
        else:
            if len(params) != len(self.__parameters__):
                raise TypeError("Cannot change parameter count from %d to %d" % (len(self.__parameters__), len(params)))
            for new, old in zip(params, self.__parameters__):
                if isinstance(old, TypeVar):
                    if not old.__constraints__:
                        # Substituting for an unconstrained TypeVar is OK.
                        continue
                    if issubclass(new, Union[old.__constraints__]):
                        # Specializing a constrained type variable is OK.
                        continue
                if not issubclass(new, old):
                    raise TypeError("Cannot substitute %s for %s in %s" % (_type_repr(new), _type_repr(old), self))

        return self.__class__(
            self.__name__, self.__bases__, dict(self.__dict__), parameters=params, origin=self, extra=self.__extra__
        )
Exemple #12
0
def deserialize_primitive(cls, data):
    if cls is datetime.datetime:
        try:
            d = parse_date(data)
        except iso8601.ParseError:
            raise ValueError("'{}' is not a datetime.".format(data))
    elif cls is datetime.date:
        try:
            d = parse_date(data).date()
        except iso8601.ParseError:
            raise ValueError("'{}' is not a date.".format(data))
    elif cls in {int, float, uuid.UUID, bool}:
        d = cls(data)
    elif cls is numbers.Integral:
        d = data
    elif cls is decimal.Decimal:
        try:
            d = cls(data)
        except decimal.InvalidOperation:
            raise ValueError("'{}' is not a decimal.".format(data))
    elif cls is text_type:
        if not isinstance(data, text_type):
            raise ValueError("'{}' is not a string.".format(data))
        d = cls(data)
    else:
        raise TypeError("'{0}' is not a primitive type.".format(
            typing._type_repr(cls)))
    return d
Exemple #13
0
 def typecheck(self, value) -> None:
     union_types = get_union_types(self.cls)
     cls = self.cls if union_types is None else union_types
     if not isinstance(value, cls):
         raise ConfigTypeError(
             '{0} configuration must be {1}, not {2!r}'.format(
                 self.key, typing._type_repr(self.cls), value))
Exemple #14
0
    def __get__(self, obj, cls: typing.Optional[type] = None):
        if obj is None:
            return self

        if self.cached:
            cache_key = '  cache_{!s}'.format(self.key)
            try:
                instance = getattr(obj, cache_key)
            except AttributeError:
                pass
            else:
                return instance

        default, expression = self.get_raw_value(obj)
        if default:
            return expression

        if not isinstance(expression, collections.abc.Mapping):
            raise ConfigTypeError(
                '{0!r} field must be a mapping, not {1}'.format(
                    self.key, typing._type_repr(type(expression))))
        elif 'class' not in expression:
            raise ConfigValueError('{0!r} field lacks "class" field'.format(
                self.key))
        value = self.evaluate(expression)
        self.typecheck(value)

        if self.cached:
            setattr(obj, cache_key, value)

        return value
Exemple #15
0
 def rpc(self, request, service_method, request_json):
     name_map = self.service.__nirum_method_names__
     try:
         method_facial_name = name_map.behind_names[service_method]
     except KeyError:
         raise ServiceMethodError()
     try:
         func = getattr(self.service, method_facial_name)
     except AttributeError:
         return self.error(400,
                           request,
                           message="Service has no procedure '{}'.".format(
                               service_method))
     if not callable(func):
         return self.error(
             400,
             request,
             message="Remote procedure '{}' is not callable.".format(
                 service_method))
     type_hints = self.service.__nirum_service_methods__[method_facial_name]
     try:
         arguments = self._parse_procedure_arguments(
             type_hints, request_json)
     except (NirumProcedureArgumentValueError,
             NirumProcedureArgumentRequiredError) as e:
         return self.error(400, request, message=str(e))
     method_error_types = self.service.__nirum_method_error_types__
     if not callable(method_error_types):  # generated by older compiler
         method_error_types = method_error_types.get
     method_error = method_error_types(method_facial_name, ())
     try:
         result = func(**arguments)
     except method_error as e:
         return self._raw_response(400, serialize_meta(e))
     return_type = type_hints['_return']
     if type_hints.get('_v', 1) >= 2:
         return_type = return_type()
     if not self._check_return_type(return_type, result):
         return self.error(400,
                           request,
                           message="Incorrect return type '{0}' "
                           "for '{1}'. expected '{2}'.".format(
                               typing._type_repr(result.__class__),
                               service_method,
                               typing._type_repr(return_type)))
     else:
         return self._raw_response(200, serialize_meta(result))
Exemple #16
0
 def _tree_repr(self, tree):
     cls, origin, metadata = tree
     if not isinstance(origin, tuple):
         tp_repr = typing._type_repr(origin)
     else:
         tp_repr = origin[0]._tree_repr(origin)
     metadata_reprs = ", ".join(repr(arg) for arg in metadata)
     return '%s[%s, %s]' % (cls, tp_repr, metadata_reprs)
Exemple #17
0
 def do_request(self, request_url, payload):
     request_tuple = self.make_request(u'POST', request_url, [
         ('Content-type', 'application/json;charset=utf-8'),
         ('Accepts', 'application/json'),
     ], payload)
     if not isinstance(request_tuple, collections.Sequence) and \
             len(request_tuple) == 3:
         raise TypeError(
             'make_request() must return a triple of '
             '(status_code, content, headers): {}'.format(request_tuple))
     http_method, request_url, headers, content = request_tuple
     if not isinstance(request_url, text_type):
         raise TypeError(
             '`request_url` have to be instance of text. not {}'.format(
                 typing._type_repr(type(request_url))))
     if not isinstance(headers, collections.Sequence):
         raise TypeError(
             '`headers` have to be instance of sequence. not {}'.format(
                 typing._type_repr(type(headers))))
     if not isinstance(content, bytes):
         raise TypeError(
             '`content` have to be instance of bytes. not {}'.format(
                 typing._type_repr(type(content))))
     if not isinstance(http_method, text_type):
         raise TypeError(
             '`method` have to be instance of text. not {}'.format(
                 typing._type_repr(type(http_method))))
     http_method = http_method.upper()
     proper_http_method_names = {
         'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'TRACE', 'CONNECT',
         'HEAD'
     }
     if http_method not in proper_http_method_names:
         raise ValueError('`method` have to be one of {!r}.: {}'.format(
             proper_http_method_names, http_method))
     request = urllib.request.Request(request_url, data=content)
     request.get_method = lambda: http_method.upper()
     for header_name, header_content in headers:
         request.add_header(header_name, header_content)
     response = self.opener.open(request, None)
     response_text = response.read()
     if 200 <= response.code < 300:
         return response_text.decode('utf-8')
     else:
         raise UnexpectedNirumResponseError(response_text)
Exemple #18
0
 def typecheck(self, value) -> None:
     union_types = get_union_types(self.cls)
     cls = self.cls if union_types is None else union_types
     if not isinstance(value, cls):
         raise ConfigTypeError(
             '{0} configuration must be {1}, not {2!r}'.format(
                 self.key, typing._type_repr(self.cls), value
             )
         )
Exemple #19
0
def validate_record_type(record):
    for attr, type_ in record.__nirum_field_types__.items():
        data = getattr(record, attr)
        if not validate_type(data, type_):
            raise TypeError('expect {0}.{1} to be {2}'
                            ', but found: {3}'.format(
                                typing._type_repr(record.__class__), attr,
                                type_, type(data)))
    else:
        return record
Exemple #20
0
def validate_union_type(union):
    for attr, type_ in union.__nirum_tag_types__.items():
        data = getattr(union, attr)
        if not validate_type(data, type_):
            raise TypeError('expect {0}.{1} to be {2}'
                            ', but found: {3}'.format(
                                typing._type_repr(union.__class__), attr,
                                type_, type(data)))
    else:
        return union
Exemple #21
0
def _dp_init_subclass(sub_cls, *args, **kwargs):
    # Add function for datapipe instance to reinforce the type
    sub_cls.reinforce_type = reinforce_type

    # TODO:
    # - add global switch for type checking at compile-time

    # Ignore internal type class
    if getattr(sub_cls, '__type_class__', False):
        return

    # Check if the string type is valid
    if isinstance(sub_cls.type.param, ForwardRef):
        base_globals = sys.modules[sub_cls.__module__].__dict__
        try:
            param = _eval_type(sub_cls.type.param, base_globals, locals())
            sub_cls.type.param = param
        except TypeError as e:
            raise TypeError("{} is not supported by Python typing".format(
                sub_cls.type.param.__forward_arg__)) from e

    if '__iter__' in sub_cls.__dict__:
        iter_fn = sub_cls.__dict__['__iter__']
        hints = get_type_hints(iter_fn)
        if 'return' in hints:
            return_hint = hints['return']
            # Plain Return Hint for Python 3.6
            if return_hint == Iterator:
                return
            if not (hasattr(return_hint, '__origin__') and
                    (return_hint.__origin__ == Iterator
                     or return_hint.__origin__ == collections.abc.Iterator)):
                raise TypeError(
                    "Expected 'Iterator' as the return annotation for `__iter__` of {}"
                    ", but found {}".format(sub_cls.__name__,
                                            _type_repr(hints['return'])))
            data_type = return_hint.__args__[0]
            if not issubtype(data_type, sub_cls.type.param):
                raise TypeError(
                    "Expected return type of '__iter__' as a subtype of {}, but found {}"
                    " for {}".format(sub_cls.type, _type_repr(data_type),
                                     sub_cls.__name__))
Exemple #22
0
def _dp_init_subclass(sub_cls, *args, **kwargs):
    # TODO:
    # - add global switch for type checking at compile-time
    if '__iter__' in sub_cls.__dict__:
        iter_fn = sub_cls.__dict__['__iter__']
        hints = get_type_hints(iter_fn)
        if 'return' in hints:
            return_hint = hints['return']
            # Plain Return Hint for Python 3.6
            if return_hint == Iterator:
                return
            if not (hasattr(return_hint, '__origin__') and
                    (return_hint.__origin__ == Iterator or
                     return_hint.__origin__ == collections.abc.Iterator)):
                raise TypeError("Expected 'Iterator' as the return annotation for `__iter__` of {}"
                                ", but found {}".format(sub_cls.__name__, _type_repr(hints['return'])))
            data_type = return_hint.__args__[0]
            if not issubtype(data_type, sub_cls.type.param):
                raise TypeError("Expected return type of '__iter__' is a subtype of {}, but found {}"
                                " for {}".format(sub_cls.type, _type_repr(data_type), sub_cls.__name__))
Exemple #23
0
    def collect_docs(self) -> None:
        """Collect all information necessary to genertate the function page.

    Mainly this is details about the function signature.

    For the type alias signature, the args are extracted and replaced with the
    full_name if the object is present in `parser_config.reverse_index`. They
    are also linkified to point to that symbol's page.

    For example (If generating docs for symbols in TF library):

    ```
    X = Union[int, str, bool, tf.Tensor, np.ndarray]
    ```

    In this case `tf.Tensor` will get linked to that symbol's page.
    Note: In the signature `tf.Tensor` is an object, so it will show up as
    `tensorflow.python.framework.ops.Tensor`. That's why we need to query
    `parser_config.reverse_index` to get the full_name of the object which will
    be `tf.Tensor`. Hence the signature will be:

    ```
    X = Union[int, str, bool, <a href="URL">tf.Tensor</a>, np.ndarray]
    ```
    """
        assert self.signature is None

        linker = signature_lib.FormatArguments(
            parser_config=self.parser_config)

        sig_args = []
        if self.py_object.__origin__:
            for arg_obj in self.py_object.__args__:
                sig_args.append(
                    self._link_type_args(arg_obj,
                                         self.parser_config.reverse_index,
                                         linker))

        sig_args_str = textwrap.indent(',\n'.join(sig_args), '    ')
        if self.py_object.__origin__:
            origin_str = typing._type_repr(self.py_object.__origin__)  # pylint: disable=protected-access # pytype: disable=module-attr
            sig = f'{origin_str}[\n{sig_args_str}\n]'
        else:
            sig = repr(self.py_object)

        # pytype: enable=module-attr

        # Starting in Python 3.7, the __origin__ attribute of typing constructs
        # contains the equivalent runtime class rather than the construct itself
        # (e.g., typing.Callable.__origin__ is collections.abc.Callable).
        self._signature = sig.replace('typing.',
                                      '').replace('collections.abc.', '')
Exemple #24
0
def validate_union_type(union):
    tag_types = union.__nirum_tag_types__
    if not callable(tag_types):  # generated by older compiler
        tag_types = tag_types.items
    for attr, type_ in tag_types():
        data = getattr(union, attr)
        if not validate_type(data, type_):
            raise TypeError('expect {0}.{1} to be {2}'
                            ', but found: {3}'.format(
                                typing._type_repr(union.__class__), attr,
                                type_, type(data)))
    else:
        return union
Exemple #25
0
def validate_record_type(record):
    field_types = record.__nirum_field_types__
    if callable(field_types):
        field_types = field_types()
        # old compiler could generate non-callable dictionary
    for attr, type_ in field_types.items():
        data = getattr(record, attr)
        if not validate_type(data, type_):
            raise TypeError('expect {0}.{1} to be {2}'
                            ', but found: {3}'.format(
                                typing._type_repr(record.__class__), attr,
                                type_, type(data)))
    else:
        return record
Exemple #26
0
def deserialize_union_type(cls, value):
    if '_type' not in value:
        raise ValueError('"_type" field is missing.')
    if '_tag' not in value:
        raise ValueError('"_tag" field is missing.')
    if not hasattr(cls, '__nirum_tag__'):
        for sub_cls in cls.__subclasses__():
            if sub_cls.__nirum_tag__.value == value['_tag']:
                cls = sub_cls
                break
        else:
            raise ValueError('{0!r} is not deserialzable tag of `{1}`.'.format(
                value, typing._type_repr(cls)))
    if not cls.__nirum_union_behind_name__ == value['_type']:
        raise ValueError('{0} expect "_type" equal to'
                         ' "{1.__nirum_union_behind_name__}"'
                         ', but found {2}.'.format(typing._type_repr(cls), cls,
                                                   value['_type']))
    if not cls.__nirum_tag__.value == value['_tag']:
        raise ValueError('{0} expect "_tag" equal to'
                         ' "{1.__nirum_tag__.value}"'
                         ', but found {1}.'.format(typing._type_repr(cls), cls,
                                                   value['_tag']))
    args = {}
    behind_names = cls.__nirum_tag_names__.behind_names
    for attribute_name, item in value.items():
        if attribute_name in {'_type', '_tag'}:
            continue
        if attribute_name in behind_names:
            name = behind_names[attribute_name]
        else:
            name = attribute_name
        tag_types = cls.__nirum_tag_types__
        if callable(tag_types):  # old compiler could generate non-callable map
            tag_types = dict(tag_types())
        args[name] = deserialize_meta(tag_types[name], item)
    return cls(**args)
Exemple #27
0
 def __get__(self, obj, cls: typing.Optional[type] = None):
     if obj is None:
         return self
     default, expression = self.get_raw_value(obj)
     if not default:
         if not isinstance(expression, collections.abc.Mapping):
             raise ConfigTypeError(
                 '{0!r} field must be a mapping, not {1}'.format(
                     self.key, typing._type_repr(type(expression))))
         elif 'class' not in expression:
             raise ConfigValueError(
                 '{0!r} field lacks "class" field'.format(self.key))
         value = self.evaluate(expression)
         self.typecheck(value)
     return value
Exemple #28
0
    def __getitem__(self, params):
        if not isinstance(params, tuple):
            params = (params, )
        if not params:
            raise TypeError("Cannot have empty parameter list")
        params = tuple(params)
        if self.__parameters__ is None:
            for p in params:
                if not isinstance(p, (TypeVar, TypeParam)):
                    raise TypeError("Initial parameters must be "
                                    "type variables; got %s" % p)
            if len(set(params)) != len(params):
                raise TypeError(
                    "All type variables in Generic[...] must be distinct.")
        else:
            if len(params) != len(self.__parameters__):
                raise TypeError("Cannot change parameter count from %d to %d" %
                                (len(self.__parameters__), len(params)))
            for new, old in zip(params, self.__parameters__):
                if isinstance(old, TypeVar):
                    if not old.__constraints__:
                        # Substituting for an unconstrained TypeVar is OK.
                        continue
                    if issubclass(new, Union[old.__constraints__]):
                        # Specializing a constrained type variable is OK.
                        continue
                if not issubclass(new, old):
                    raise TypeError("Cannot substitute %s for %s in %s" %
                                    (_type_repr(new), _type_repr(old), self))

        return self.__class__(self.__name__,
                              self.__bases__,
                              dict(self.__dict__),
                              parameters=params,
                              origin=self,
                              extra=self.__extra__)
Exemple #29
0
 def _raw_response(self, status_code, response_json, **kwargs):
     response_tuple = self.make_response(
         status_code,
         headers=[('Content-type', 'application/json')],
         content=json.dumps(response_json).encode('utf-8'))
     if not (isinstance(response_tuple, collections.Sequence)
             and len(response_tuple) == 3):
         raise TypeError('make_response() must return a triple of '
                         '(status_code, headers, content), not ' +
                         repr(response_tuple))
     status_code, headers, content = response_tuple
     if not isinstance(status_code, integer_types):
         raise TypeError(
             '`status_code` have to be instance of integer. not {}'.format(
                 typing._type_repr(type(status_code))))
     if not isinstance(headers, collections.Sequence):
         raise TypeError(
             '`headers` have to be instance of sequence. not {}'.format(
                 typing._type_repr(type(headers))))
     if not isinstance(content, bytes):
         raise TypeError(
             '`content` have to be instance of bytes. not {}'.format(
                 typing._type_repr(type(content))))
     return Response(content, status_code, headers, **kwargs)
Exemple #30
0
def reinforce_type(self, expected_type):
    r"""
    Reinforce the type for DataPipe instance. And the 'expected_type' is required
    to be a subtype of the original type hint to restrict the type requirement
    of DataPipe instance.
    """
    if isinstance(expected_type, tuple):
        expected_type = Tuple[expected_type]
    _type_check(expected_type, msg="'expected_type' must be a type")

    if not issubtype(expected_type, self.type.param):
        raise TypeError("Expected 'expected_type' as subtype of {}, but found {}"
                        .format(self.type, _type_repr(expected_type)))

    self.type = _DataPipeType(expected_type)
    return self
Exemple #31
0
def test_serializer_deserializer(spec_file):
    with io.open(spec_file, 'r', encoding='utf-8') as f:
        spec = json.load(f)
    print()
    print('Spec:', os.path.relpath(spec_file, test_suite_dir))
    print('Description:', spec['description'])
    print('Type:', spec['type'])
    candidates = list(
        pkg_resources.iter_entry_points('nirum.classes', name=spec['type']))
    assert 'normal' in spec or 'errors' in spec, (
        'The specification must have either "normal" or "errors" field.')
    assert candidates, (
        'Failed to resolve a corresponding Python class to the Nirum '
        'type "{0}".'.format(spec['type']))
    assert len(candidates) < 2, (
        'Too many classes are mapped to the Nirum type name "{0}":\n'
        '{1}'.format(spec['type'], '\n'.join(map(str, candidates))))
    cls = candidates[0].resolve()
    print('Class:', _type_repr(cls))
    print('Input:', dump_json(spec['input']))
    if 'normal' in spec:
        deserialized = cls.__nirum_deserialize__(spec['input'])
        print('Deserialized:', pprint.pformat(deserialized))
        print('Normal:', dump_json(spec['normal']))
        serialized = deserialized.__nirum_serialize__()
        print('Serialized:', dump_json(serialized))
        if 'ignoreOrder' in spec:
            normalize(spec['ignoreOrder'], spec['normal'])
            normalize(spec['ignoreOrder'], serialized)
        assert serialized == spec['normal']
    else:
        print('Expected errors:', dump_json(spec['errors']))
        actual_errors = set()
        deserialized = cls.__nirum_deserialize__(
            spec['input'],
            on_error=lambda field, msg: actual_errors.add((field, msg)))
        print(
            'Actual errors:',
            dump_json([{
                'path': p,
                'message': m
            } for (p, m) in sorted(actual_errors)]))
        if not actual_errors:
            print('Deserialized:', pprint.pformat(deserialized))
        assert actual_errors == frozenset(
            (e['path'], e['message']) for e in spec['errors'])
    print()
Exemple #32
0
 def __get__(self, obj, cls: typing.Optional[type] = None):
     if obj is None:
         return self
     default, expression = self.get_raw_value(obj)
     if not default:
         if not isinstance(expression, collections.abc.Mapping):
             raise ConfigTypeError(
                 '{0!r} field must be a mapping, not {1}'.format(
                     self.key, typing._type_repr(type(expression))
                 )
             )
         elif 'class' not in expression:
             raise ConfigValueError(
                 '{0!r} field lacks "class" field'.format(self.key)
             )
         value = self.evaluate(expression)
         self.typecheck(value)
     return value
Exemple #33
0
 def __repr__(self):
     r = super(_Final, self).__repr__()
     if self.__type__ is not None:
         r += '[{}]'.format(typing._type_repr(self.__type__))
     return r
 def __repr__(self):
     r = super(_Final, self).__repr__()
     if self.__type__ is not None:
         r += '[{}]'.format(typing._type_repr(self.__type__))
     return r
Exemple #35
0
 def __repr__(self):
     return _type_repr(self.param)