コード例 #1
0
 def test_builtin_lookup(self):
     self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ())
     intstmts = scoped_nodes.builtin_lookup('int')[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.ClassDef)
     self.assertEqual(intstmts[0].name, 'int')
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #2
0
ファイル: unittest_lookup.py プロジェクト: GymWenFLL/tpp_libs
 def test_builtin_lookup(self):
     self.assertEqual(builtin_lookup("__dict__")[1], ())
     intstmts = builtin_lookup("int")[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.Class)
     self.assertEqual(intstmts[0].name, "int")
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #3
0
def apply_type_shim(cls, _context=None):
    """
    Morphs model fields to representative type
    """
    if cls.name in ['IntField', 'SmallIntField']:
        base_nodes = scoped_nodes.builtin_lookup('int')
    elif cls.name in ['CharField', 'TextField']:
        base_nodes = scoped_nodes.builtin_lookup('str')
    elif cls.name == 'BooleanField':
        base_nodes = scoped_nodes.builtin_lookup('bool')
    elif cls.name == 'FloatField':
        base_nodes = scoped_nodes.builtin_lookup('float')
    elif cls.name == 'DecimalField':
        base_nodes = MANAGER.ast_from_module_name('decimal').lookup('Decimal')
    elif cls.name == 'DatetimeField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup(
            'datetime')
    elif cls.name == 'DateField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date')
    elif cls.name == 'ForeignKeyField':
        base_nodes = MANAGER.ast_from_module_name('tortoise.fields').lookup(
            'BackwardFKRelation')
    elif cls.name == 'ManyToManyField':
        base_nodes = MANAGER.ast_from_module_name('tortoise.fields').lookup(
            'ManyToManyRelationManager')
    else:
        return iter([cls])

    return iter([cls] + base_nodes[1])
コード例 #4
0
def apply_type_shim(cls, _context=None) -> Iterator:
    """
    Morphs model fields to representative type
    """
    if cls.name in ["IntField", "SmallIntField"]:
        base_nodes = scoped_nodes.builtin_lookup("int")
    elif cls.name in ["CharField", "TextField"]:
        base_nodes = scoped_nodes.builtin_lookup("str")
    elif cls.name == "BooleanField":
        base_nodes = scoped_nodes.builtin_lookup("bool")
    elif cls.name == "FloatField":
        base_nodes = scoped_nodes.builtin_lookup("float")
    elif cls.name == "DecimalField":
        base_nodes = MANAGER.ast_from_module_name("decimal").lookup("Decimal")
    elif cls.name == "DatetimeField":
        base_nodes = MANAGER.ast_from_module_name("datetime").lookup("datetime")
    elif cls.name == "DateField":
        base_nodes = MANAGER.ast_from_module_name("datetime").lookup("date")
    elif cls.name == "ForeignKeyField":
        base_nodes = MANAGER.ast_from_module_name("tortoise.fields").lookup("BackwardFKRelation")
    elif cls.name == "ManyToManyField":
        base_nodes = MANAGER.ast_from_module_name("tortoise.fields").lookup(
            "ManyToManyRelationManager"
        )
    else:
        return iter([cls])

    return iter([cls] + base_nodes[1])
コード例 #5
0
ファイル: fields.py プロジェクト: davinirjr/pylint-django
def apply_type_shim(cls, context=None):

    if cls.name in _STR_FIELDS:
        base_node = scoped_nodes.builtin_lookup('str')
    elif cls.name in _INT_FIELDS:
        base_node = scoped_nodes.builtin_lookup('int')
    elif cls.name in _BOOL_FIELDS:
        base_node = scoped_nodes.builtin_lookup('bool')
    elif cls.name == 'FloatField':
        base_node = scoped_nodes.builtin_lookup('float')
    elif cls.name == 'DecimalField':
        base_node = MANAGER.ast_from_module_name('decimal').lookup('Decimal')
    elif cls.name in ('SplitDateTimeField', 'DateTimeField'):
        base_node = MANAGER.ast_from_module_name('datetime').lookup('datetime')
    elif cls.name == 'TimeField':
        base_node = MANAGER.ast_from_module_name('datetime').lookup('time')
    elif cls.name == 'DateField':
        base_node = MANAGER.ast_from_module_name('datetime').lookup('date')
    elif cls.name == 'ManyToManyField':
        base_node = MANAGER.ast_from_module_name('django.db.models.query').lookup('QuerySet')
    elif cls.name in ('ImageField', 'FileField'):
        base_node = MANAGER.ast_from_module_name('django.core.files.base').lookup('File')
    else:
        return iter([cls])

    return iter([cls] + base_node[1])
コード例 #6
0
 def test_builtin_lookup(self):
     self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ())
     intstmts = scoped_nodes.builtin_lookup('int')[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.ClassDef)
     self.assertEqual(intstmts[0].name, 'int')
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #7
0
 def test_builtin_lookup(self):
     self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ())
     intstmts = scoped_nodes.builtin_lookup('int')[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.ClassDef)
     self.assertEqual(intstmts[0].name, 'int')
     # pylint: disable=no-member; union type in const_factory, this shouldn't happen
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #8
0
ファイル: unittest_lookup.py プロジェクト: my88899/astroid
 def test_builtin_lookup(self):
     self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ())
     intstmts = scoped_nodes.builtin_lookup('int')[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.ClassDef)
     self.assertEqual(intstmts[0].name, 'int')
     # pylint: disable=no-member; union type in const_factory, this shouldn't happen
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #9
0
ファイル: unittest_lookup.py プロジェクト: tcwz/astroid
 def test_builtin_lookup(self):
     self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ())
     intstmts = scoped_nodes.builtin_lookup('int')[1]
     self.assertEqual(len(intstmts), 1)
     self.assertIsInstance(intstmts[0], nodes.ClassDef)
     self.assertEqual(intstmts[0].name, 'int')
     # pylint: disable=no-member; Infers two potential values
     self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
コード例 #10
0
def transform(cls):
    if cls.name in CLASS_NAME_BLACKLIST:
        return

    if cls.name.endswith('API') or 'schema' in cls.locals:
        # This is a class which defines attributes in "schema" variable using json schema.
        # Those attributes are then assigned during run time inside the constructor
        fqdn = cls.qname()
        module_name, class_name = fqdn.rsplit('.', 1)

        module = __import__(module_name, fromlist=[class_name])
        actual_cls = getattr(module, class_name)

        schema = actual_cls.schema

        if not isinstance(schema, dict):
            # Not a class we are interested in
            return

        properties = schema.get('properties', {})
        for property_name, property_data in six.iteritems(properties):
            property_name = property_name.replace(
                '-', '_')  # Note: We do the same in Python code
            property_type = property_data.get('type', None)

            if isinstance(property_type, (list, tuple)):
                # Hack for attributes with multiple types (e.g. string, null)
                property_type = property_type[0]

            if property_type == 'object':
                node = nodes.Dict()
            elif property_type == 'array':
                node = nodes.List()
            elif property_type == 'integer':
                node = scoped_nodes.builtin_lookup('int')[1][0]
            elif property_type == 'number':
                node = scoped_nodes.builtin_lookup('float')[1][0]
            elif property_type == 'string':
                node = scoped_nodes.builtin_lookup('str')[1][0]
            elif property_type == 'boolean':
                node = scoped_nodes.builtin_lookup('bool')[1][0]
            elif property_type == 'null':
                node = scoped_nodes.builtin_lookup('None')[1][0]
            else:
                # Unknown type
                node = scoped_nodes.Class(property_name, None)

            cls.locals[property_name] = [node]
コード例 #11
0
def transform(cls):
    if cls.name in CLASS_NAME_BLACKLIST:
        return

    if cls.name.endswith("API") or "schema" in cls.locals:
        # This is a class which defines attributes in "schema" variable using json schema.
        # Those attributes are then assigned during run time inside the constructor
        fqdn = cls.qname()
        module_name, class_name = fqdn.rsplit(".", 1)

        module = __import__(module_name, fromlist=[class_name])
        actual_cls = getattr(module, class_name)

        schema = actual_cls.schema

        if not isinstance(schema, dict):
            # Not a class we are interested in
            return

        properties = schema.get("properties", {})
        for property_name, property_data in six.iteritems(properties):
            property_name = property_name.replace(
                "-", "_")  # Note: We do the same in Python code
            property_type = property_data.get("type", None)

            if isinstance(property_type, (list, tuple)):
                # Hack for attributes with multiple types (e.g. string, null)
                property_type = property_type[0]

            if property_type == "object":
                node = nodes.Dict()
            elif property_type == "array":
                node = nodes.List()
            elif property_type == "integer":
                node = scoped_nodes.builtin_lookup("int")[1][0]
            elif property_type == "number":
                node = scoped_nodes.builtin_lookup("float")[1][0]
            elif property_type == "string":
                node = scoped_nodes.builtin_lookup("str")[1][0]
            elif property_type == "boolean":
                node = scoped_nodes.builtin_lookup("bool")[1][0]
            elif property_type == "null":
                node = scoped_nodes.builtin_lookup("None")[1][0]
            else:
                # Unknown type
                node = astroid.ClassDef(property_name, None)

            cls.locals[property_name] = [node]
コード例 #12
0
ファイル: pylint_plugin.py プロジェクト: sigiesec/conan
def transform_conanfile(node):
    """Transform definition of ConanFile class so dynamic fields are visible to pylint"""

    str_class = scoped_nodes.builtin_lookup("str")
    info_class = MANAGER.ast_from_module_name("conans.model.info").lookup(
        "ConanInfo")
    build_requires_class = MANAGER.ast_from_module_name(
        "conans.client.graph.graph_manager").lookup("_RecipeBuildRequires")
    file_copier_class = MANAGER.ast_from_module_name(
        "conans.client.file_copier").lookup("FileCopier")
    file_importer_class = MANAGER.ast_from_module_name(
        "conans.client.importer").lookup("_FileImporter")

    dynamic_fields = {
        "source_folder": str_class,
        "build_folder": str_class,
        "package_folder": str_class,
        "build_requires": build_requires_class,
        "info_build": info_class,
        "info": info_class,
        "copy": file_copier_class,
        "copy_deps": file_importer_class,
    }

    for f, t in dynamic_fields.items():
        node.locals[f] = [t]
コード例 #13
0
ファイル: api_models.py プロジェクト: AlexeyDeyneko/st2
def transform(cls):
    if cls.name in CLASS_NAME_BLACKLIST:
        return

    if cls.name.endswith('API') or 'schema' in cls.locals:
        # This is a class which defines attributes in "schema" variable using json schema.
        # Those attributes are then assigned during run time inside the constructor
        fqdn = cls.qname()
        module_name, class_name = fqdn.rsplit('.', 1)

        module = __import__(module_name, fromlist=[class_name])
        actual_cls = getattr(module, class_name)

        schema = actual_cls.schema

        if not isinstance(schema, dict):
            # Not a class we are interested in
            return

        properties = schema.get('properties', {})
        for property_name, property_data in six.iteritems(properties):
            property_name = property_name.replace('-', '_')  # Note: We do the same in Python code
            property_type = property_data.get('type', None)

            if isinstance(property_type, (list, tuple)):
                # Hack for attributes with multiple types (e.g. string, null)
                property_type = property_type[0]

            if property_type == 'object':
                node = nodes.Dict()
            elif property_type == 'array':
                node = nodes.List()
            elif property_type == 'integer':
                node = scoped_nodes.builtin_lookup('int')[1][0]
            elif property_type == 'number':
                node = scoped_nodes.builtin_lookup('float')[1][0]
            elif property_type == 'string':
                node = scoped_nodes.builtin_lookup('str')[1][0]
            elif property_type == 'boolean':
                node = scoped_nodes.builtin_lookup('bool')[1][0]
            elif property_type == 'null':
                node = scoped_nodes.builtin_lookup('None')[1][0]
            else:
                # Unknown type
                node = scoped_nodes.Class(property_name, None)

            cls.locals[property_name] = [node]
コード例 #14
0
def apply_type_shim(cls, context=None):

    if cls.name in _STR_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('str')
    elif cls.name in _INT_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('int')
    elif cls.name in _BOOL_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('bool')
    elif cls.name == 'FloatField':
        base_nodes = scoped_nodes.builtin_lookup('float')
    elif cls.name == 'DecimalField':
        if sys.version_info >= (3, 5):
            # I dunno, I'm tired and this works :(
            base_nodes = MANAGER.ast_from_module_name('_decimal').lookup(
                'Decimal')
        else:
            base_nodes = MANAGER.ast_from_module_name('decimal').lookup(
                'Decimal')
    elif cls.name in ('SplitDateTimeField', 'DateTimeField'):
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup(
            'datetime')
    elif cls.name == 'TimeField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('time')
    elif cls.name == 'DateField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date')
    elif cls.name == 'ManyToManyField':
        base_nodes = MANAGER.ast_from_module_name(
            'django.db.models.query').lookup('QuerySet')
    elif cls.name in ('ImageField', 'FileField'):
        base_nodes = MANAGER.ast_from_module_name(
            'django.core.files.base').lookup('File')
    else:
        return iter([cls])

    # XXX: for some reason, with python3, this particular line triggers a
    # check in the StdlibChecker for deprecated methods; one of these nodes
    # is an ImportFrom which has no qname() method, causing the checker
    # to die...
    if utils.PY3:
        base_nodes = [
            n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)
        ]
    else:
        base_nodes = list(base_nodes[1])

    return iter([cls] + base_nodes)
コード例 #15
0
ファイル: fields.py プロジェクト: Coldwings/pylint-django
def apply_type_shim(cls, context=None):

    if cls.name in _STR_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('str')
    elif cls.name in _INT_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('int')
    elif cls.name in _BOOL_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('bool')
    elif cls.name == 'FloatField':
        base_nodes = scoped_nodes.builtin_lookup('float')
    elif cls.name == 'DecimalField':
        if sys.version_info >= (3, 5):
            # I dunno, I'm tired and this works :(
            base_nodes = MANAGER.ast_from_module_name('_decimal').lookup('Decimal')
        else:
            base_nodes = MANAGER.ast_from_module_name('decimal').lookup('Decimal')
    elif cls.name in ('SplitDateTimeField', 'DateTimeField'):
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('datetime')
    elif cls.name == 'TimeField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('time')
    elif cls.name == 'DateField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date')
    elif cls.name == 'ManyToManyField':
        base_nodes = MANAGER.ast_from_module_name('django.db.models.query').lookup('QuerySet')
    elif cls.name in ('ImageField', 'FileField'):
        base_nodes = MANAGER.ast_from_module_name('django.core.files.base').lookup('File')
    else:
        return iter([cls])

    # XXX: for some reason, with python3, this particular line triggers a
    # check in the StdlibChecker for deprecated methods; one of these nodes
    # is an ImportFrom which has no qname() method, causing the checker
    # to die...
    if utils.PY3:
        base_nodes = [n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)]
    else:
        base_nodes = list(base_nodes[1])

    return iter([cls] + base_nodes)
コード例 #16
0
def apply_type_shim(cls, _context=None):  # noqa

    if cls.name in _STR_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('str')
    elif cls.name in _INT_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('int')
    elif cls.name in _BOOL_FIELDS:
        base_nodes = scoped_nodes.builtin_lookup('bool')
    elif cls.name == 'FloatField':
        base_nodes = scoped_nodes.builtin_lookup('float')
    elif cls.name == 'DecimalField':
        try:
            base_nodes = MANAGER.ast_from_module_name('_decimal').lookup(
                'Decimal')
        except AstroidImportError:
            base_nodes = MANAGER.ast_from_module_name('_pydecimal').lookup(
                'Decimal')
    elif cls.name in ('SplitDateTimeField', 'DateTimeField'):
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup(
            'datetime')
    elif cls.name == 'TimeField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('time')
    elif cls.name == 'DateField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date')
    elif cls.name == 'DurationField':
        base_nodes = MANAGER.ast_from_module_name('datetime').lookup(
            'timedelta')
    elif cls.name == 'UUIDField':
        base_nodes = MANAGER.ast_from_module_name('uuid').lookup('UUID')
    elif cls.name == 'ManyToManyField':
        base_nodes = MANAGER.ast_from_module_name(
            'django.db.models.query').lookup('QuerySet')
    elif cls.name in ('ImageField', 'FileField'):
        base_nodes = MANAGER.ast_from_module_name(
            'django.core.files.base').lookup('File')
    elif cls.name == 'ArrayField':
        base_nodes = scoped_nodes.builtin_lookup('list')
    elif cls.name in ('HStoreField', 'JSONField'):
        base_nodes = scoped_nodes.builtin_lookup('dict')
    elif cls.name in _RANGE_FIELDS:
        base_nodes = MANAGER.ast_from_module_name('psycopg2._range').lookup(
            'Range')
    else:
        return iter([cls])

    # XXX: for some reason, with python3, this particular line triggers a
    # check in the StdlibChecker for deprecated methods; one of these nodes
    # is an ImportFrom which has no qname() method, causing the checker
    # to die...
    if utils.PY3:
        base_nodes = [
            n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)
        ]
    else:
        base_nodes = list(base_nodes[1])

    return iter([cls] + base_nodes)
コード例 #17
0
ファイル: api_models.py プロジェクト: wingiti/st2
def transform(cls: nodes.ClassDef):
    """
    Astroid (used by pylint) calls this function on each class definition it discovers.
    cls is an Astroid AST representation of that class.

    Our purpose here is to extract the schema dict from API model classes
    so that we can inform pylint about all of the attributes on those models.
    We do this by injecting attributes on the class for each property in the schema.
    """

    # This is a class which defines attributes in "schema" variable using json schema.
    # Those attributes are then assigned during run time inside the constructor

    # Get the value node for the "schema =" assignment
    schema_dict_node = next(cls.igetattr("schema"))

    extra_schema_properties = {}

    # If the "schema =" assignment's value node is not a simple type (like a dictionary),
    # then pylint cannot infer exactly what it does. Most of the time, this is actually
    # a function call to copy the schema from another class. So, let's find the dictionary.
    if schema_dict_node is astroid.Uninferable:
        # the assignment probably looks like this:
        # schema = copy.deepcopy(ActionAPI.schema)

        # so far we only have the value, but we need the actual assignment
        assigns = [
            n for n in cls.get_children() if isinstance(n, nodes.Assign)
        ]
        schema_assign_name_node = cls.local_attr("schema")[0]
        schema_assign_node = next(
            assign for assign in assigns
            if assign.targets[0] == schema_assign_name_node)
        assigns.remove(schema_assign_node)

        # We only care about "schema = copy.deepcopy(...)"
        schema_dict_node = infer_copy_deepcopy(schema_assign_node.value)
        if not schema_dict_node:
            # This is not an API model class, as it doesn't have
            # something we can resolve to a dictionary.
            return

        # OK, now we need to look for any properties that dynamically modify
        # the dictionary that was just copied from somewhere else.
        # See the note below for why we only care about "properties" here.
        for assign_node in assigns:
            # we're looking for assignments like this:
            # schema["properties"]["ttl"] = {...}
            target = assign_node.targets[0]
            try:
                if (isinstance(target, nodes.Subscript)
                        and target.value.value.name == "schema"
                        and target.value.slice.value.value == "properties"):
                    property_name_node = target.slice.value
                else:
                    # not schema["properties"]
                    continue
            except AttributeError:
                continue

            # schema["properties"]["execution"] = copy.deepcopy(ActionExecutionAPI.schema)
            inferred_value = infer_copy_deepcopy(assign_node.value)

            extra_schema_properties[property_name_node] = (
                inferred_value if inferred_value else assign_node.value)

    if not isinstance(schema_dict_node, nodes.Dict):
        # Not a class we are interested in (like BaseAPI)
        return

    # We only care about "properties" in the schema because that's the only part of the schema
    # that gets translated into dynamic attributes on the model API class.
    properties_dict_node = None
    for key_node, value_node in schema_dict_node.items:
        if key_node.value == "properties":
            properties_dict_node = value_node
            break

    if not properties_dict_node and not extra_schema_properties:
        # Not a class we can do anything with
        return

    # Hooray! We have the schema properties dict now, so we can start processing
    # each property and add an attribute for each one to the API model class node.
    for property_name_node, property_data_node in properties_dict_node.items + list(
            extra_schema_properties.items()):
        property_name = property_name_node.value.replace(
            "-", "_")  # Note: We do the same in Python code

        # Despite the processing above to extract the schema properties dictionary
        # each property in the dictionary might also reference other variables,
        # so we still need to resolve these to figure out each property's type.

        # an indirect reference to copy.deepcopy() as in:
        #   REQUIRED_ATTR_SCHEMAS = {"action": copy.deepcopy(ActionAPI.schema)}
        #   schema = {"properties": {"action": REQUIRED_ATTR_SCHEMAS["action"]}}
        if isinstance(property_data_node, nodes.Subscript):
            var_name = property_data_node.value.name
            subscript = property_data_node.slice.value.value

            # lookup var by name (assume its at module level)
            var_node = next(cls.root().igetattr(var_name))

            # assume it is a dict at this point
            data_node = None
            for key_node, value_node in var_node.items:
                if key_node.value == subscript:
                    # infer will resolve a Dict
                    data_node = next(value_node.infer())
                    if data_node is astroid.Uninferable:
                        data_node = infer_copy_deepcopy(value_node)
                    break
            if data_node:
                property_data_node = data_node

        if not isinstance(property_data_node, nodes.Dict):
            # if infer_copy_deepcopy already ran, we may need to resolve the dict
            data_node = next(property_data_node.infer())
            if data_node is not astroid.Uninferable:
                property_data_node = data_node

        property_type_node = None
        if isinstance(property_data_node, nodes.Dict):
            # We have a property schema, but we only care about the property's type.
            for property_key_node, property_value_node in property_data_node.items:
                if property_key_node.value == "type":
                    property_type_node = next(property_value_node.infer())
                    break

        if property_type_node is None and isinstance(property_data_node,
                                                     nodes.Attribute):
            # reference schema from another file like this:
            #   from ... import TriggerAPI
            #   schema = {"properties": {"trigger": TriggerAPI.schema}}
            # We only pull a schema from another file when it is an "object" (a dict).
            # So, we do not need to do any difficult cross-file processing.
            property_type = "object"
        elif property_type_node is None:
            property_type = None
        elif isinstance(property_type_node, nodes.Const):
            property_type = property_type_node.value
        elif isinstance(property_type_node, (nodes.List, nodes.Tuple)):
            # Hack for attributes with multiple types (e.g. string, null)
            property_type = property_type_node.elts[
                0].value  # elts has "elements" in the list/tuple
        else:
            # We should only hit this if someone has used a different approach
            # for dynamically constructing the property's schema.
            # Expose the AST at this point to facilitate handling that approach.
            raise Exception(property_type_node.repr_tree())

        # Hooray! We've got a property's name at this point.
        # And we have the property's type, if that type was defined in the schema.
        # Now, we can construct the AST node that we'll add to the API model class.

        if property_type == "object":
            node = nodes.Dict()
        elif property_type == "array":
            node = nodes.List()
        elif property_type == "integer":
            node = scoped_nodes.builtin_lookup("int")[1][0]
        elif property_type == "number":
            node = scoped_nodes.builtin_lookup("float")[1][0]
        elif property_type == "string":
            node = scoped_nodes.builtin_lookup("str")[1][0]
        elif property_type == "boolean":
            node = scoped_nodes.builtin_lookup("bool")[1][0]
        elif property_type == "null":
            node = scoped_nodes.builtin_lookup("None")[1][0]
        else:
            # Unknown type
            node = astroid.ClassDef(property_name, None)

        # Create a "property = node" assign node
        assign_node = nodes.Assign(parent=cls)
        assign_name_node = nodes.AssignName(property_name, parent=assign_node)
        assign_node.postinit(targets=[assign_name_node], value=node)

        # Finally, add the property node as an attribute on the class.
        cls.locals[property_name] = [assign_name_node]