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
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)
def remove_aliases_from_api(api): # Resolve nested aliases from each namespace first. This way, when we replace an alias with # its source later on, it too is alias free. for namespace in api.namespaces.values(): for alias in namespace.aliases: # This loops through each alias type chain, resolving each (nested) alias # to its underlying type at the end of the chain (see resolve_aliases fn). # # It will continue until it no longer encounters a type # with a data_type attribute - this ensures it resolves aliases # that are subtypes of composites e.g. Lists curr_type = alias while hasattr(curr_type, 'data_type'): curr_type.data_type = resolve_aliases(curr_type.data_type) curr_type = curr_type.data_type # Remove alias layers from each data type for namespace in api.namespaces.values(): for data_type in namespace.data_types: for field in data_type.fields: strip_alias(field) for route in namespace.routes: # Strip inner aliases strip_alias(route.arg_data_type) strip_alias(route.result_data_type) strip_alias(route.error_data_type) # Strip top-level aliases 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
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: return prefix_with_ns_if_necessary(name, data_type.namespace, ns) return name
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
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
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_map_type(dt): v = generate_func_call( 'bv.Map', args=[ generate_validator_constructor(ns, dt.key_data_type), generate_validator_constructor(ns, dt.value_data_type), ] ) 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
def map_stone_type_to_python_type(ns, data_type, override_dict=None): # type: (ApiNamespace, DataType, typing.Optional[OverrideDefaultTypesDict]) -> typing.Text """ 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): string_override = override_dict.get(String, None) if string_override: return string_override(ns, data_type, override_dict) 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_map_type(data_type): map_type = cast(Map, data_type) if Map in override_dict: return override_dict[Map](ns, data_type, override_dict) return 'dict of [{}:{}]'.format( map_stone_type_to_python_type(ns, map_type.key_data_type, override_dict), map_stone_type_to_python_type(ns, map_type.value_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)
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_map_type(data_type): map_type = cast(Map, data_type) if Map in override_dict: return override_dict[Map]( ns, data_type, override_dict ) return 'dict of [{}:{}]'.format( map_stone_type_to_python_type(ns, map_type.key_data_type, override_dict), map_stone_type_to_python_type(ns, map_type.value_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)