class IntersectionType(node.Node('type_list')): """An intersection type that contains all types in self.type_list.""" __slots__ = () # NOTE: type_list is kept as a tuple, to preserve the original order # even though in most respects it acts like a frozenset. # It also flattens the input, such that printing without # parentheses gives the same result. def __new__(cls, type_list): flattened = itertools.chain.from_iterable( t.type_list if isinstance(t, IntersectionType) else [t] for t in type_list) return super(IntersectionType, cls).__new__(cls, tuple(flattened)) def __hash__(self): # See __eq__ - order doesn't matter, so use frozenset return hash(frozenset(self.type_list)) def __eq__(self, other): if self is other: return True if isinstance(other, IntersectionType): # equality doesn't care about the ordering of the type_list return frozenset(self.type_list) == frozenset(other.type_list) return NotImplemented def __ne__(self, other): return not self == other
class NodeWithVisit(node.Node("x", "y")): """A node with its own Visit function.""" def Visit(self, visitor): """Allow a visitor to modify our children. Returns modified node.""" # only visit x, not y x = self.x.Visit(visitor) return NodeWithVisit(x, self.y)
class NothingType(node.Node()): """An "impossible" type, with no instances ('nothing' in pytd). Also known as the "uninhabited" type. For representing empty lists, and functions that never return. """ __slots__ = ()
class ClassType(node.Node('name')): """A type specified through an existing class node.""" # This type is different from normal nodes: # (a) It's mutable, and there are functions (parse/visitors.py:FillInClasses) # that modify a tree in place. # (b) Because it's mutable, it's not actually using the tuple/Node interface # to store things (in particular, the pointer to the existing class). # (c) Visitors will not process the "children" of this node. Since we point # to classes that are back at the top of the tree, that would generate # cycles. __slots__ = () def __new__(cls, name, clsref=None): self = super(ClassType, cls).__new__(cls, name) self.cls = clsref # potentially filled in later (by visitors.FillInClasses) return self # __eq__ is inherited (using tuple equality + requiring the two classes # be the same) def __str__(self): return str(self.cls.name) if self.cls else self.name def __repr__(self): return '{type}{cls}({name})'.format( type=type(self).__name__, name=self.name, cls='<unresolved>' if self.cls is None else '')
class StrictType(node.Node("name")): """A type that doesn't allow sub- or superclasses to match. For example, "int" is considered a valid argument for a function that accepts "object", but StrictType("int") is not. """ pass
class Function(node.Node('name', 'signatures')): """A function or a method. Attributes: name: The name of this function. signatures: Possible list of parameter type combinations for this function. """ __slots__ = ()
class Parameter(node.Node('name', 'type')): """Represents a parameter of a function definition. Attributes: name: The name of the parameter. type: The type of the parameter. """ __slots__ = ()
class MutableParameter(node.Node('name', 'type', 'new_type')): """Represents a parameter that's modified by the function. Attributes: name: The name of the parameter. type: The type of the parameter. new_type: The type the parameter will have after the function is called. """ __slots__ = ()
class TypeParameter(node.Node('name')): """Represents a type parameter. A type parameter is a bound variable in the context of a function or class definition. It specifies an equivalence between types. For example, this defines a identity function: def f<T>(x: T) -> T """ __slots__ = ()
class GenericType(node.Node('base_type', 'parameters')): """Generic type. Takes a base type and type paramters. This corresponds to the syntax: type<type1,>, type<type1, type2> (etc.). Attributes: base_type: The base type. Instance of Type. parameters: Type paramters. Tuple of instances of Type. """ __slots__ = ()
class Signature( node.Node('params', 'return_type', 'exceptions', 'template', 'has_optional')): """Represents an individual signature of a function. For overloaded functions, this is one specific combination of parameters. For non-overloaded functions, there is a 1:1 correspondence between function and signature. Attributes: name: The name of this function. params: The list of parameters for this function definition. return_type: The return type of this function. exceptions: List of exceptions for this function definition. template: names for bindings for bounded types in params/return_type has_optional: Do we have optional parameters ("...")? """ __slots__ = ()
class TypeDeclUnit( node.Node('name', 'constants', 'classes', 'functions', 'modules')): """Module node. Holds module contents (classes / functions) and submodules. Attributes: name: Name of this module, or None for the top-level module. constants: Iterable of module-level constants. functions: Iterable of functions defined in this type decl unit. classes: Iterable of classes defined in this type decl unit. modules: Iterable of submodules of the current module. """ __slots__ = () def Lookup(self, name): """Convenience function: Look up a given name in the global namespace. Tries to find a constant, function or class by this name. Args: name: Name to look up. Returns: A Constant, Function or Class. Raises: KeyError: if this identifier doesn't exist. """ # TODO: Remove. Change constants, classes and functions to dict. try: return self._name2item[name] except AttributeError: self._name2item = {} for x in self.constants + self.functions + self.classes + self.modules: self._name2item[x.name] = x return self._name2item[name] def __hash__(self): return id(self) def __eq__(self, other): return id(self) == id(other) def __ne__(self, other): return id(self) != id(other)
class TemplateItem(node.Node('type_param', 'within_type')): """Represents "template name extends bounded_type". This is used for classes and signatures. The 'template' field of both is a list of TemplateItems. Note that *using* the template happens through TypeParameters. E.g. in: class A<T>: def f(T x) -> T both the "T"s in the definition of f() are using pytd.TypeParameter to refer to the TemplateItem in class A's template. Attributes: type_param: the TypeParameter instance used. This is the actual instance that's used wherever this type parameter appears, e.g. within a class. within_type: the "extends" type for this name (e.g., NamedType('object')) """ __slots__ = () @property def name(self): return self.type_param.name
class Class(node.Node('name', 'parents', 'methods', 'constants', 'template')): """Represents a class declaration. Used as dict/set key, so all components must be hashable. Attributes: name: Class name (string) parents: The super classes of this class (instances of Type). methods: Tuple of class methods (instances of Function). constants: Tuple of constant class attributes (instances of Constant). template: Tuple of TemplateItem instances. """ # TODO: Rename "parents" to "bases". "Parents" is confusing since we're # in a tree. __slots__ = () def Lookup(self, name): """Convenience function: Look up a given name in the class namespace. Tries to find a method or constant by this name in the class. Args: name: Name to look up. Returns: A Constant or Function instance. Raises: KeyError: if this identifier doesn't exist in this class. """ # TODO: Remove this. Make methods and constants dictionaries. try: return self._name2item[name] except AttributeError: self._name2item = {} for x in self.methods + self.constants: self._name2item[x.name] = x return self._name2item[name]
class NativeType(node.Node('python_type')): """A type specified by a native Python type. Used during runtime checking.""" __slots__ = ()
class Y(node.Node("c", "d")): """Inner node 'Y', with two children. See testVisitor[...]() below.""" pass
class XY(node.Node("x", "y")): """Inner node 'XY', with two children. See testVisitor[...]() below.""" pass
class V(node.Node("x")): """Inner node 'V', with one child. See testVisitor[...]() below.""" pass
class X(node.Node("a", "b")): """Inner node 'X', with two children. See testVisitor[...]() below.""" pass
class Node3(node.Node("x", "y")): """For equality testing: Same attributes as Node2.""" pass
class Data(node.Node("d1", "d2", "d3")): """'Data' node. Visitor tests use this to store numbers in leafs.""" pass
class Node1(node.Node("a", "b")): """Simple node for equality testing. Not equal to anything else.""" pass
class AnythingType(node.Node()): """A type we know nothing about yet ('?' in pytd).""" __slots__ = ()
class NamedType(node.Node('name')): """A type specified by name.""" __slots__ = () def __str__(self): return str(self.name)
class Constant(node.Node('name', 'type')): __slots__ = ()
class Scalar(node.Node('value')): __slots__ = ()