Example #1
0
 def _python_type_mapping(self, ns, data_type):
     """Map Stone data types to their most natural equivalent in Python
     for documentation purposes."""
     if is_string_type(data_type):
         return 'str'
     elif is_bytes_type(data_type):
         return 'bytes'
     elif is_boolean_type(data_type):
         return 'bool'
     elif is_float_type(data_type):
         return 'float'
     elif is_integer_type(data_type):
         return 'long'
     elif is_void_type(data_type):
         return 'None'
     elif is_timestamp_type(data_type):
         return 'datetime.datetime'
     elif is_alias(data_type):
         return self._python_type_mapping(ns, data_type.data_type)
     elif is_user_defined_type(data_type):
         class_name = class_name_for_data_type(data_type)
         if data_type.namespace.name != ns.name:
             return '%s.%s_validator' % (
                 data_type.namespace.name, class_name)
         else:
             return class_name
     elif is_list_type(data_type):
         # PyCharm understands this description format for a list
         return 'list of [{}]'.format(self._python_type_mapping(
             ns, data_type.data_type))
     elif is_nullable_type(data_type):
         return 'Optional[{}]'.format(
             self._python_type_mapping(ns, data_type.data_type))
     else:
         raise TypeError('Unknown data type %r' % data_type)
Example #2
0
 def _python_type_mapping(self, ns, data_type):
     """Map Stone data types to their most natural equivalent in Python
     for documentation purposes."""
     if is_string_type(data_type):
         return 'str'
     elif is_bytes_type(data_type):
         return 'bytes'
     elif is_boolean_type(data_type):
         return 'bool'
     elif is_float_type(data_type):
         return 'float'
     elif is_integer_type(data_type):
         return 'long'
     elif is_void_type(data_type):
         return 'None'
     elif is_timestamp_type(data_type):
         return 'datetime.datetime'
     elif is_alias(data_type):
         return self._python_type_mapping(ns, data_type.data_type)
     elif is_user_defined_type(data_type):
         class_name = class_name_for_data_type(data_type)
         if data_type.namespace.name != ns.name:
             return '%s.%s_validator' % (
                 data_type.namespace.name, class_name)
         else:
             return class_name
     elif is_list_type(data_type):
         # PyCharm understands this description format for a list
         return 'list of [{}]'.format(self._python_type_mapping(
             ns, data_type.data_type))
     elif is_nullable_type(data_type):
         return 'Optional[{}]'.format(
             self._python_type_mapping(ns, data_type.data_type))
     else:
         raise TypeError('Unknown data type %r' % data_type)
def map_stone_type_to_python_type(ns, data_type, override_dict=None):
    # type: (ApiNamespace, DataType, typing.Optional[OverrideDefaultTypesDict]) -> str
    """
    Args:
        override_dict: lets you override the default behavior for a given type by hooking into
            a callback. (Currently only hooked up for stone's List and Nullable)
    """
    override_dict = override_dict or {}

    if is_string_type(data_type):
        return 'str'
    elif is_bytes_type(data_type):
        return 'bytes'
    elif is_boolean_type(data_type):
        return 'bool'
    elif is_float_type(data_type):
        return 'float'
    elif is_integer_type(data_type):
        return 'long'
    elif is_void_type(data_type):
        return 'None'
    elif is_timestamp_type(data_type):
        timestamp_override = override_dict.get(Timestamp, None)
        if timestamp_override:
            return timestamp_override(ns, data_type, override_dict)
        return 'datetime.datetime'
    elif is_alias(data_type):
        alias_type = cast(Alias, data_type)
        return map_stone_type_to_python_type(ns, alias_type.data_type,
                                             override_dict)
    elif is_user_defined_type(data_type):
        user_defined_type = cast(UserDefined, data_type)
        class_name = class_name_for_data_type(user_defined_type)
        if user_defined_type.namespace.name != ns.name:
            return '%s.%s_validator' % (user_defined_type.namespace.name,
                                        class_name)
        else:
            return class_name
    elif is_list_type(data_type):
        list_type = cast(List, data_type)
        if List in override_dict:
            return override_dict[List](ns, list_type.data_type, override_dict)

        # PyCharm understands this description format for a list
        return 'list of [{}]'.format(
            map_stone_type_to_python_type(ns, list_type.data_type,
                                          override_dict))
    elif is_nullable_type(data_type):
        nullable_type = cast(Nullable, data_type)
        if Nullable in override_dict:
            return override_dict[Nullable](ns, nullable_type.data_type,
                                           override_dict)

        return 'Optional[{}]'.format(
            map_stone_type_to_python_type(ns, nullable_type.data_type,
                                          override_dict))
    else:
        raise TypeError('Unknown data type %r' % data_type)
Example #4
0
def generate_validator_constructor(ns, data_type):
    """
    Given a Stone data type, returns a string that can be used to construct
    the appropriate validation object in Python.
    """
    dt, nullable_dt = unwrap_nullable(data_type)
    if is_list_type(dt):
        v = generate_func_call(
            'bv.List',
            args=[
                generate_validator_constructor(ns, dt.data_type)],
            kwargs=[
                ('min_items', dt.min_items),
                ('max_items', dt.max_items)],
        )
    elif is_numeric_type(dt):
        v = generate_func_call(
            'bv.{}'.format(dt.name),
            kwargs=[
                ('min_value', dt.min_value),
                ('max_value', dt.max_value)],
        )
    elif is_string_type(dt):
        pattern = None
        if dt.pattern is not None:
            pattern = repr(dt.pattern)
        v = generate_func_call(
            'bv.String',
            kwargs=[
                ('min_length', dt.min_length),
                ('max_length', dt.max_length),
                ('pattern', pattern)],
        )
    elif is_timestamp_type(dt):
        v = generate_func_call(
            'bv.Timestamp',
            args=[repr(dt.format)],
        )
    elif is_user_defined_type(dt):
        v = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            v = '{}.{}'.format(dt.namespace.name, v)
    elif is_alias(dt):
        # Assume that the alias has already been declared elsewhere.
        name = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            name = '{}.{}'.format(dt.namespace.name, name)
        v = name
    elif is_boolean_type(dt) or is_bytes_type(dt) or is_void_type(dt):
        v = generate_func_call('bv.{}'.format(dt.name))
    else:
        raise AssertionError('Unsupported data type: %r' % dt)

    if nullable_dt:
        return generate_func_call('bv.Nullable', args=[v])
    else:
        return v
Example #5
0
def generate_validator_constructor(ns, data_type):
    """
    Given a Stone data type, returns a string that can be used to construct
    the appropriate validation object in Python.
    """
    dt, nullable_dt = unwrap_nullable(data_type)
    if is_list_type(dt):
        v = generate_func_call(
            'bv.List',
            args=[
                generate_validator_constructor(ns, dt.data_type)],
            kwargs=[
                ('min_items', dt.min_items),
                ('max_items', dt.max_items)],
        )
    elif is_numeric_type(dt):
        v = generate_func_call(
            'bv.{}'.format(dt.name),
            kwargs=[
                ('min_value', dt.min_value),
                ('max_value', dt.max_value)],
        )
    elif is_string_type(dt):
        pattern = None
        if dt.pattern is not None:
            pattern = repr(dt.pattern)
        v = generate_func_call(
            'bv.String',
            kwargs=[
                ('min_length', dt.min_length),
                ('max_length', dt.max_length),
                ('pattern', pattern)],
        )
    elif is_timestamp_type(dt):
        v = generate_func_call(
            'bv.Timestamp',
            args=[repr(dt.format)],
        )
    elif is_user_defined_type(dt):
        v = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            v = '{}.{}'.format(dt.namespace.name, v)
    elif is_alias(dt):
        # Assume that the alias has already been declared elsewhere.
        name = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            name = '{}.{}'.format(dt.namespace.name, name)
        v = name
    elif is_boolean_type(dt) or is_bytes_type(dt) or is_void_type(dt):
        v = generate_func_call('bv.{}'.format(dt.name))
    else:
        raise AssertionError('Unsupported data type: %r' % dt)

    if nullable_dt:
        return generate_func_call('bv.Nullable', args=[v])
    else:
        return v
Example #6
0
File: api.py Project: scobbe/stone
 def add_alias(alias):
     if alias in seen_aliases:
         return
     elif alias.namespace != self:
         return
     if is_alias(alias.data_type):
         add_alias(alias.data_type)
     linearized_aliases.append(alias)
     seen_aliases.add(alias)
Example #7
0
def remove_aliases_from_api(api):
    for namespace in api.namespaces.values():
        # Important: Even if this namespace has no aliases, it may reference
        # an alias in an imported namespace.

        # Remove nested aliases first. This way, when we replace an alias with
        # its source later on, it too is alias free.
        for alias in namespace.aliases:
            data_type = alias
            while True:
                # For better or for worse, all non-user-defined types that
                # reference other types do so with a 'data_type' attribute.
                if hasattr(data_type, 'data_type'):
                    if is_alias(data_type.data_type):
                        # Skip the alias (this looks so terrible...)
                        data_type.data_type = data_type.data_type.data_type
                    data_type = data_type.data_type
                else:
                    break

        for data_type in namespace.data_types:
            for field in data_type.fields:
                data_type = field
                while True:
                    if hasattr(data_type, 'data_type'):
                        if is_alias(data_type.data_type):
                            data_type.data_type = data_type.data_type.data_type
                        data_type = data_type.data_type
                    else:
                        break

        for route in namespace.routes:
            if is_alias(route.arg_data_type):
                route.arg_data_type = route.arg_data_type.data_type
            if is_alias(route.result_data_type):
                route.result_data_type = route.result_data_type.data_type
            if is_alias(route.error_data_type):
                route.error_data_type = route.error_data_type.data_type

        # Clear aliases
        namespace.aliases = []
        namespace.alias_by_name = {}

    return api
Example #8
0
def remove_aliases_from_api(api):
    for namespace in api.namespaces.values():
        # Important: Even if this namespace has no aliases, it may reference
        # an alias in an imported namespace.

        # Remove nested aliases first. This way, when we replace an alias with
        # its source later on, it too is alias free.
        for alias in namespace.aliases:
            data_type = alias
            while True:
                # For better or for worse, all non-user-defined types that
                # reference other types do so with a 'data_type' attribute.
                if hasattr(data_type, 'data_type'):
                    if is_alias(data_type.data_type):
                        # Skip the alias (this looks so terrible...)
                        data_type.data_type = data_type.data_type.data_type
                    data_type = data_type.data_type
                else:
                    break

        for data_type in namespace.data_types:
            for field in data_type.fields:
                data_type = field
                while True:
                    if hasattr(data_type, 'data_type'):
                        if is_alias(data_type.data_type):
                            data_type.data_type = data_type.data_type.data_type
                        data_type = data_type.data_type
                    else:
                        break

        for route in namespace.routes:
            if is_alias(route.arg_data_type):
                route.arg_data_type = route.arg_data_type.data_type
            if is_alias(route.result_data_type):
                route.result_data_type = route.result_data_type.data_type
            if is_alias(route.error_data_type):
                route.error_data_type = route.error_data_type.data_type

        # Clear aliases
        namespace.aliases = []
        namespace.alias_by_name = {}

    return api
Example #9
0
 def _generate_type(self, data_type, indent_spaces, extra_args):
     """
     Generates a TypeScript type for the given type.
     """
     if is_alias(data_type):
         self._generate_alias_type(data_type)
     elif is_struct_type(data_type):
         self._generate_struct_type(data_type, indent_spaces, extra_args)
     elif is_union_type(data_type):
         self._generate_union_type(data_type, indent_spaces)
Example #10
0
 def add_alias(alias):
     # type: (Alias) -> None
     if alias in seen_aliases:
         return
     elif alias.namespace != self:
         return
     if is_alias(alias.data_type):
         add_alias(alias.data_type)
     linearized_aliases.append(alias)
     seen_aliases.add(alias)
Example #11
0
def fmt_type_name(data_type, inside_namespace=None):
    """
    Produces a TypeScript type name for the given data type.
    inside_namespace should be set to the namespace that the reference
    occurs in, or None if this parameter is not relevant.
    """
    if is_user_defined_type(data_type) or is_alias(data_type):
        if data_type.namespace == inside_namespace:
            return data_type.name
        else:
            return '%s.%s' % (data_type.namespace.name, data_type.name)
    else:
        fmted_type = _base_type_table.get(data_type.__class__, 'Object')
        if is_list_type(data_type):
            fmted_type += '<' + fmt_type(data_type.data_type, inside_namespace) + '>'
        return fmted_type
Example #12
0
def class_name_for_data_type(data_type, ns=None):
    """
    Returns the name of the Python class that maps to a user-defined type.
    The name is identical to the name in the spec.

    If ``ns`` is set to a Namespace and the namespace of `data_type` does
    not match, then a namespace prefix is added to the returned name.
    For example, ``foreign_ns.TypeName``.
    """
    assert is_user_defined_type(data_type) or is_alias(data_type), \
        'Expected composite type, got %r' % type(data_type)
    name = fmt_class(data_type.name)
    if ns and data_type.namespace != ns:
        # If from an imported namespace, add a namespace prefix.
        name = '{}.{}'.format(data_type.namespace.name, name)
    return name
Example #13
0
def class_name_for_data_type(data_type, ns=None):
    """
    Returns the name of the Python class that maps to a user-defined type.
    The name is identical to the name in the spec.

    If ``ns`` is set to a Namespace and the namespace of `data_type` does
    not match, then a namespace prefix is added to the returned name.
    For example, ``foreign_ns.TypeName``.
    """
    assert is_user_defined_type(data_type) or is_alias(data_type), \
        'Expected composite type, got %r' % type(data_type)
    name = fmt_class(data_type.name)
    if ns and data_type.namespace != ns:
        # If from an imported namespace, add a namespace prefix.
        name = '{}.{}'.format(data_type.namespace.name, name)
    return name
Example #14
0
def fmt_type_name(data_type, inside_namespace=None):
    """
    Produces a TypeScript type name for the given data type.
    inside_namespace should be set to the namespace that the reference
    occurs in, or None if this parameter is not relevant.
    """
    if is_user_defined_type(data_type) or is_alias(data_type):
        if data_type.namespace == inside_namespace:
            return data_type.name
        else:
            return '%s.%s' % (data_type.namespace.name, data_type.name)
    else:
        fmted_type = _base_type_table.get(data_type.__class__, 'Object')
        if is_list_type(data_type):
            fmted_type += '<' + fmt_type(data_type.data_type,
                                         inside_namespace) + '>'
        return fmted_type
Example #15
0
File: api.py Project: scobbe/stone
    def get_route_io_data_types(self):
        """
        Returns a list of all user-defined data types that are referenced as
        either an argument, result, or error of a route. If a List or Nullable
        data type is referenced, then the contained data type is returned
        assuming it's a user-defined type.
        """
        data_types = set()
        for route in self.routes:
            for dtype in (route.arg_data_type, route.result_data_type,
                          route.error_data_type):
                while is_list_type(dtype) or is_nullable_type(dtype):
                    dtype = dtype.data_type
                if is_composite_type(dtype) or is_alias(dtype):
                    data_types.add(dtype)

        return sorted(data_types, key=lambda dt: dt.name)
Example #16
0
    def get_route_io_data_types(self):
        # type: () -> List[UserDefined]
        """
        Returns a list of all user-defined data types that are referenced as
        either an argument, result, or error of a route. If a List or Nullable
        data type is referenced, then the contained data type is returned
        assuming it's a user-defined type.
        """
        data_types = set()  # type: Set[UserDefined]
        for route in self.routes:
            for dtype in (route.arg_data_type, route.result_data_type,
                          route.error_data_type):
                while is_list_type(dtype) or is_nullable_type(dtype):
                    dtype_as_list = cast(Union[DataTypeList, Nullable], dtype)
                    dtype = dtype_as_list.data_type
                if is_composite_type(dtype) or is_alias(dtype):
                    data_types.add(dtype)

        return sorted(data_types, key=lambda dt: dt.name)