def register(_): """ Registers this plugin to pylint pylint calls this function when loading """ MANAGER.register_transform(nodes.Module, pytest_transform) MANAGER.register_transform(nodes.Module, redbaron_transform)
def register_builtin_transform(transform, builtin_name): """Register a new transform function for the given *builtin_name*. The transform function must accept two parameters, a node and an optional context. """ def _transform_wrapper(node, context=None): result = transform(node, context=context) if result: if not result.parent: # Let the transformation function determine # the parent for its result. Otherwise, # we set it to be the node we transformed from. result.parent = node if result.lineno is None: result.lineno = node.lineno if result.col_offset is None: result.col_offset = node.col_offset return iter([result]) MANAGER.register_transform( nodes.Call, inference_tip(_transform_wrapper), partial(_builtin_filter_predicate, builtin_name=builtin_name), )
def tearDown(self): # Since we may have created a brainless manager, leading # to a new cache builtin module and proxy classes in the constants, # clear out the global manager cache. MANAGER.clear_cache() sys.path.pop(0) sys.path_importer_cache.pop(resources.find('data'), None)
def tearDown(self): # Since we may have created a brainless manager, leading # to a new cache builtin module and proxy classes in the constants, # clear out the global manager cache. MANAGER.clear_cache(self._builtins) MANAGER.always_load_extensions = False sys.path.pop(0) sys.path_importer_cache.pop(resources.find("data"), None)
def register(linter): """ Register new linter checkers and transformers Called when loaded by pylint's load-plugins option. We register our tranformation function here. """ MANAGER.register_transform(nodes.Module, transform)
def register(_): # pragma: no cover """ An entrypoint that pylint uses to search for and register plugins with the given ``linter`` """ functions = \ functions_in_file(FUNCTIONS_HEADER) | functions_in_file(SOURCE_MAIN) constants = constants_in_file(CONSTANTS_HEADER) MANAGER.register_transform( scoped_nodes.Class, partial(transform, constants=constants, functions=functions), predicate=lambda node: node.name == "FFILibrary")
def _add_transform(package_name, *class_names): transforms_dir = os.path.join(os.path.dirname(__file__), 'transforms') fake_module_path = os.path.join(transforms_dir, '%s.py' % re.sub(r'\.', '_', package_name)) with open(fake_module_path) as modulefile: fake_module = modulefile.read() fake = AstroidBuilder(MANAGER).string_build(fake_module) def set_fake_locals(module): if module.name != package_name: return for class_name in class_names: module.locals[class_name] = fake.locals[class_name] MANAGER.register_transform(nodes.Module, set_fake_locals)
def _six_fail_hook(modname): """Fix six.moves imports due to the dynamic nature of this class. Construct a pseudo-module which contains all the necessary imports for six :param modname: Name of failed module :type modname: str :return: An astroid module :rtype: nodes.Module """ attribute_of = (modname != "six.moves" and modname.startswith("six.moves")) if modname != 'six.moves' and not attribute_of: raise AstroidBuildingError(modname=modname) module = AstroidBuilder(MANAGER).string_build(_IMPORTS) module.name = 'six.moves' if attribute_of: # Facilitate import of submodules in Moves start_index = len(module.name) attribute = modname[start_index:].lstrip(".").replace(".", "_") try: import_attr = module.getattr(attribute)[0] except AttributeInferenceError: raise AstroidBuildingError(modname=modname) if isinstance(import_attr, nodes.Import): submodule = MANAGER.ast_from_module_name(import_attr.names[0][0]) return submodule # Let dummy submodule imports pass through # This will cause an Uninferable result, which is okay return module
def test_regex_flags(self): import re names = [name for name in dir(re) if name.isupper()] re_ast = MANAGER.ast_from_module_name('re') for name in names: self.assertIn(name, re_ast) self.assertEqual(next(re_ast[name].infer()).value, getattr(re, name))
def get_project(module, name=None): """return a astroid project representation""" # flush cache MANAGER._modules_by_name = {} def _astroid_wrapper(func, modname): return func(modname) return MANAGER.project_from_files([module], _astroid_wrapper, project_name=name)
def register_builtin_transform(transform, builtin_name): """Register a new transform function for the given *builtin_name*. The transform function must accept two parameters, a node and an optional context. """ def _transform_wrapper(node, context=None): result = transform(node, context=context) if result: result.parent = node result.lineno = node.lineno result.col_offset = node.col_offset return iter([result]) MANAGER.register_transform(nodes.CallFunc, inference_tip(_transform_wrapper), lambda n: (isinstance(n.func, nodes.Name) and n.func.name == builtin_name))
def _add_transform(package_name, *class_names): """Transform package's classes.""" transforms_dir = os.path.join(os.path.dirname(__file__), 'transforms') fake_module_path = os.path.join(transforms_dir, '%s.py' % re.sub(r'\.', '_', package_name)) with open(fake_module_path) as modulefile: fake_module = modulefile.read() fake = AstroidBuilder(MANAGER).string_build(fake_module) def set_fake_locals(module): """Set fake locals for package.""" if module.name != package_name: return for class_name in class_names: module._locals[class_name] = fake._locals[class_name] # pylint: disable=protected-access MANAGER.register_transform(nodes.Module, set_fake_locals)
def _check(module_name='', level='all', local_config='', output=None): """Check a module for problems, printing a report. The `module_name` can take several inputs: - string of a directory, or file to check (`.py` extension optional). - list of strings of directories or files -- can have multiple. - no argument -- checks the python file containing the function call. `level` is used to specify which checks should be made. `local_config` is a dict of config options or string (config file name). `output` is an absolute path to capture pyta data output. Default std out. """ MANAGER.clear_cache() # Add reporters to an internal pylint data structure, for use with setting # custom pyta options in a Tuple, before (re)setting reporter. for reporter in REPORTERS: VALIDATORS[reporter.__name__] = reporter linter = reset_linter(config=local_config) current_reporter = reset_reporter(linter, output) patch_all() # Monkeypatch pylint (override certain methods) # Try to check file, issue error message for invalid files. try: for locations in _get_valid_files_to_check(current_reporter, module_name): for file_py in get_file_paths(locations): if not _verify_pre_check(file_py): continue # Check the other files # Load config file in user location. Construct new linter each # time, so config options don't bleed to unintended files. linter = reset_linter(config=local_config, file_linted=file_py) # Assume the local config will NOT set a new reporter. linter.set_reporter(current_reporter) current_reporter.register_file(file_py) linter.check(file_py) # Lint ! current_reporter.print_messages(level) current_reporter.reset_messages() # Clear lists for any next file. current_reporter.output_blob() return current_reporter except Exception as e: print('[ERROR] Unexpected error encountered! Please report this to your instructor (and attach the code that caused the error).') print('[ERROR] Error message: "{}"'.format(e)) raise e
def transform_model_class(cls): if cls.is_subtype_of('django.db.models.base.Model'): core_exceptions = MANAGER.ast_from_module_name('django.core.exceptions') # add DoesNotExist exception DoesNotExist = Class('DoesNotExist', None) DoesNotExist.bases = core_exceptions.lookup('ObjectDoesNotExist')[1] cls.locals['DoesNotExist'] = [DoesNotExist] # add MultipleObjectsReturned exception MultipleObjectsReturned = Class('MultipleObjectsReturned', None) MultipleObjectsReturned.bases = core_exceptions.lookup( 'MultipleObjectsReturned')[1] cls.locals['MultipleObjectsReturned'] = [MultipleObjectsReturned] # add objects manager if 'objects' not in cls.locals: try: Manager = MANAGER.ast_from_module_name( 'django.db.models.manager').lookup('Manager')[1][0] QuerySet = MANAGER.ast_from_module_name( 'django.db.models.query').lookup('QuerySet')[1][0] except IndexError: pass else: if isinstance(Manager.body[0], Pass): # for django >= 1.7 for func_name, func_list in QuerySet.locals.items(): if (not func_name.startswith('_') and func_name not in Manager.locals): func = func_list[0] if (isinstance(func, Function) and 'queryset_only' not in func.instance_attrs): f = Function(func_name, None) f.args = Arguments() Manager.locals[func_name] = [f] cls.locals['objects'] = [Instance(Manager)] # add id field if 'id' not in cls.locals: try: AutoField = MANAGER.ast_from_module_name( 'django.db.models.fields').lookup('AutoField')[1][0] except IndexError: pass else: cls.locals['id'] = [Instance(AutoField)]
def get_ast(self, filepath, modname): """return a ast(roid) representation for a module""" try: return MANAGER.ast_from_file(filepath, modname, source=True) except SyntaxError as ex: self.add_message('E0001', line=ex.lineno, args=ex.msg) except AstroidBuildingException as ex: self.add_message('F0010', args=ex) except Exception as ex: import traceback traceback.print_exc() self.add_message('F0002', args=(ex.__class__, ex))
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])
def test_hashlib(self): """Tests that brain extensions for hashlib work.""" hashlib_module = MANAGER.ast_from_module_name('hashlib') for class_name in ['md5', 'sha1']: class_obj = hashlib_module[class_name] self.assertIn('update', class_obj) self.assertIn('digest', class_obj) self.assertIn('hexdigest', class_obj) self.assertEqual(len(class_obj['__init__'].args.args), 2) self.assertEqual(len(class_obj['__init__'].args.defaults), 1) self.assertEqual(len(class_obj['update'].args.args), 2) self.assertEqual(len(class_obj['digest'].args.args), 1) self.assertEqual(len(class_obj['hexdigest'].args.args), 1)
def _add_transform(package_name, *class_names): """Transform package's classes.""" transforms_dir = os.path.join(os.path.dirname(__file__), 'transforms') fake_module_path = os.path.join(transforms_dir, '%s.py' % re.sub(r'\.', '_', package_name)) with open(fake_module_path) as modulefile: fake_module = modulefile.read() fake = AstroidBuilder(MANAGER).string_build(fake_module) def set_fake_locals(module): """Set fake locals for package.""" if module.name != package_name: return for class_name in class_names: # This changed from locals to _locals between astroid 1.3 and 1.4 if hasattr(module, '_locals'): module._locals[class_name].extend(fake._locals[class_name]) # pylint: disable=protected-access else: module.locals[class_name].extend(fake.locals[class_name]) MANAGER.register_transform(nodes.Module, set_fake_locals)
def register(dummy_linter): """Pylint calls this hook to actually activate the plugin""" checker = ImportRewriterVisitor() MANAGER.register_transform(nodes.Import, checker.visit_import) MANAGER.register_transform(nodes.From, checker.visit_from) MANAGER.register_transform(nodes.Module, checker.visit_module)
def test_hashlib(self): """Tests that brain extensions for hashlib work.""" hashlib_module = MANAGER.ast_from_module_name("hashlib") for class_name in ["md5", "sha1"]: class_obj = hashlib_module[class_name] self.assertIn("update", class_obj) self.assertIn("digest", class_obj) self.assertIn("hexdigest", class_obj) self.assertIn("block_size", class_obj) self.assertIn("digest_size", class_obj) self.assertEqual(len(class_obj["__init__"].args.args), 2) self.assertEqual(len(class_obj["__init__"].args.defaults), 1) self.assertEqual(len(class_obj["update"].args.args), 2) self.assertEqual(len(class_obj["digest"].args.args), 1) self.assertEqual(len(class_obj["hexdigest"].args.args), 1)
def test_pylint_config_attr(self): try: from pylint import lint except ImportError: self.skipTest('pylint not available') mod = MANAGER.ast_from_module_name('pylint.lint') pylinter = mod['PyLinter'] expect = ['OptionsManagerMixIn', 'object', 'MessagesHandlerMixIn', 'ReportsHandlerMixIn', 'BaseTokenChecker', 'BaseChecker', 'OptionsProviderMixIn'] self.assertListEqual([c.name for c in pylinter.ancestors()], expect) self.assertTrue(list(Instance(pylinter).getattr('config'))) infered = list(Instance(pylinter).igetattr('config')) self.assertEqual(len(infered), 1) self.assertEqual(infered[0].root().name, 'optparse') self.assertEqual(infered[0].name, 'Values')
def test_pylint_config_attr(self): try: from pylint import lint except ImportError: self.skipTest("pylint not available") mod = MANAGER.ast_from_module_name("pylint.lint") pylinter = mod["PyLinter"] expect = [ "OptionsManagerMixIn", "object", "MessagesHandlerMixIn", "ReportsHandlerMixIn", "BaseTokenChecker", "BaseChecker", "OptionsProviderMixIn", ] self.assertListEqual([c.name for c in pylinter.ancestors()], expect) self.assertTrue(list(Instance(pylinter).getattr("config"))) infered = list(Instance(pylinter).igetattr("config")) self.assertEqual(len(infered), 1) self.assertEqual(infered[0].root().name, "optparse") self.assertEqual(infered[0].name, "Values")
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)
python_requires_class = MANAGER.ast_from_module_name( "conans.client.graph.python_requires").lookup("PyRequires") dynamic_fields = { "conan_data": str_class, "build_requires": build_requires_class, "info_build": info_class, "info": info_class, "copy": file_copier_class, "copy_deps": file_importer_class, "python_requires": [str_class, python_requires_class], } for f, t in dynamic_fields.items(): node.locals[f] = [t] MANAGER.register_transform( astroid.ClassDef, transform_conanfile, lambda node: node.qname() == "conans.model.conan_file.ConanFile") def _python_requires_member(): return astroid.parse(""" from conans.client.graph.python_requires import ConanPythonRequire python_requires = ConanPythonRequire() """) astroid.register_module_extender(astroid.MANAGER, "conans", _python_requires_member)
def dataclass_transform(node): """Rewrite a dataclass to be easily understood by pylint""" for assign_node in node.body: if not isinstance(assign_node, (astroid.AnnAssign, astroid.Assign)): continue if (isinstance(assign_node, astroid.AnnAssign) and isinstance(assign_node.annotation, astroid.Subscript) and (isinstance(assign_node.annotation.value, astroid.Name) and assign_node.annotation.value.name == "ClassVar" or isinstance(assign_node.annotation.value, astroid.Attribute) and assign_node.annotation.value.attrname == "ClassVar")): continue targets = (assign_node.targets if hasattr(assign_node, "targets") else [assign_node.target]) for target in targets: rhs_node = astroid.Unknown( lineno=assign_node.lineno, col_offset=assign_node.col_offset, parent=assign_node, ) node.instance_attrs[target.name] = [rhs_node] node.locals[target.name] = [rhs_node] MANAGER.register_transform(astroid.ClassDef, dataclass_transform, is_decorated_with_dataclass)
# this method is expected by pylint for plugins, however we don't # want to register any checkers pass MODULE_TRANSFORMS = {} def transform(module): try: tr = MODULE_TRANSFORMS[module.name] except KeyError: pass else: tr(module) MANAGER.register_transform(nodes.Module, transform) def celery_transform(module): fake = AstroidBuilder(MANAGER).string_build(''' class task_dummy(object): def __call__(self): pass ''') module.locals['task'] = fake.locals['task_dummy'] MODULE_TRANSFORMS['celery'] = celery_transform
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] MANAGER.register_transform(astroid.ClassDef, transform)
MODULE_TRANSFORMS = {} PY3K = sys.version_info > (3, 0) # module specific transformation functions ##################################### def transform(module): try: tr = MODULE_TRANSFORMS[module.name] except KeyError: pass else: tr(module) MANAGER.register_transform(nodes.Module, transform) # module specific transformation functions ##################################### def hashlib_transform(module): template = ''' class %s(object): def __init__(self, value=''): pass def digest(self): return u'' def update(self, value): pass def hexdigest(self): return u'' '''
def test_hashlib_py36(self): hashlib_module = MANAGER.ast_from_module_name('hashlib') for class_name in ['sha3_224', 'sha3_512', 'shake_128']: class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj)
if "id" not in cls.locals: cls.locals["id"] = [nodes.ClassDef("id", None)] def is_model_field(cls: ClassDef) -> bool: """ Guard to apply this transform to Model Fields only """ type_name = "tortoise.fields.base.Field" return cls.is_subtype_of(type_name) and cls.qname() != type_name def apply_type_shim(cls: ClassDef, _context=None) -> Iterator[ClassDef]: """ Morphs model fields to representative type """ base_nodes: List[ClassDef] = [cls] # Use the type inference standard try: base_nodes.extend(list(cls.getattr("field_type")[0].infer())) except AstroidError: pass return iter(base_nodes) MANAGER.register_transform(nodes.ClassDef, inference_tip(apply_type_shim), is_model_field) MANAGER.register_transform(nodes.ClassDef, transform_model, is_model)
def infer_key_classes(node, context=None): keyword_args = [] if node.keywords: keyword_args = [kw.value for kw in node.keywords if kw.arg == 'to'] all_args = chain(node.args, keyword_args) for arg in all_args: # typically the class of the foreign key will # be the first argument, so we'll go from left to right if isinstance(arg, (nodes.Name, nodes.Attribute)): try: key_cls = None for inferred in arg.infer(context=context): key_cls = inferred break except InferenceError: continue else: if key_cls is not None: break elif isinstance(arg, nodes.Const): try: # can be 'self' , 'Model' or 'app.Model' if arg.value == 'self': module_name = '' # for relations with `to` first parent be Keyword(arg='to') # and we need to go deeper in parent tree to get model name if isinstance(arg.parent, nodes.Keyword) and arg.parent.arg == 'to': model_name = arg.parent.parent.parent.parent.name else: model_name = arg.parent.parent.parent.name else: module_name, _, model_name = arg.value.rpartition('.') except AttributeError: break # when ForeignKey is specified only by class name we assume that # this class must be found in the current module if not module_name: current_module = node.frame() while not isinstance(current_module, nodes.Module): current_module = current_module.parent.frame() module_name = current_module.name elif not module_name.endswith('models'): # otherwise Django allows specifying an app name first, e.g. # ForeignKey('auth.User') so we try to convert that to # 'auth.models', 'User' which works nicely with the `endswith()` # comparison below module_name += '.models' # ensure that module is loaded in astroid_cache, for cases when models is a package if module_name not in MANAGER.astroid_cache: MANAGER.ast_from_module_name(module_name) # create list from dict_values, because it may be modified in a loop for module in list(MANAGER.astroid_cache.values()): # only load model classes from modules which match the module in # which *we think* they are defined. This will prevent infering # other models of the same name which are found elsewhere! if model_name in module.locals and module.name.endswith( module_name): class_defs = _get_model_class_defs_from_module( module, model_name, module_name) if class_defs: return iter([class_defs[0].instantiate_class()]) else: raise UseInferenceDefault return iter([key_cls.instantiate_class()])
raise astroid.UseInferenceDefault try: elts = random.sample(inferred_sequence.elts, length.value) except ValueError as exc: raise astroid.UseInferenceDefault from exc new_node = astroid.List(lineno=node.lineno, col_offset=node.col_offset, parent=node.scope()) new_elts = [ _clone_node_with_lineno(elt, parent=new_node, lineno=new_node.lineno) for elt in elts ] new_node.postinit(new_elts) return iter((new_node, )) def _looks_like_random_sample(node): func = node.func if isinstance(func, astroid.Attribute): return func.attrname == "sample" if isinstance(func, astroid.Name): return func.name == "sample" return False MANAGER.register_transform(astroid.Call, astroid.inference_tip(infer_random_sample), _looks_like_random_sample)
if not isinstance(elt, (nodes.List, nodes.Tuple)): raise UseInferenceDefault if len(elt.elts) != 2: raise UseInferenceDefault names.append(elt.elts[0].as_string()) typename = node.args[0].as_string() if names: field_names = "({},)".format(",".join(names)) else: field_names = "''" node = extract_node(f"namedtuple({typename}, {field_names})") return infer_named_tuple(node, context) MANAGER.register_transform(nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple) MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), _looks_like_enum) MANAGER.register_transform( nodes.ClassDef, infer_enum_class, predicate=lambda cls: any(basename for basename in cls.basenames if basename in ENUM_BASE_NAMES), ) MANAGER.register_transform(nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base) MANAGER.register_transform( nodes.FunctionDef, inference_tip(infer_typing_namedtuple_function), lambda node: node.name == "NamedTuple" and getattr(node.root(), "name",
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER """Astroid hooks for the UUID module.""" from astroid import MANAGER from astroid import nodes def _patch_uuid_class(node): # The .int member is patched using __dict__ node.locals['int'] = [nodes.Const(0, parent=node)] MANAGER.register_transform(nodes.ClassDef, _patch_uuid_class, lambda node: node.qname() == 'uuid.UUID')
module.name = 'six.moves' return module def transform_six_add_metaclass(node): """Check if the given class node is decorated with *six.add_metaclass* If so, inject its argument as the metaclass of the underlying class. """ if not node.decorators: return for decorator in node.decorators.nodes: if not isinstance(decorator, nodes.Call): continue try: func = next(decorator.func.infer()) except InferenceError: continue if func.qname() == SIX_ADD_METACLASS and decorator.args: metaclass = decorator.args[0] node._metaclass = metaclass return node register_module_extender(MANAGER, 'six', six_moves_transform) register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', six_moves_transform) MANAGER.register_failed_import_hook(_six_fail_hook) MANAGER.register_transform(nodes.ClassDef, transform_six_add_metaclass)
def register(_): """Register plugins with pylint.""" MANAGER.register_transform(astroid.FunctionDef, pylint_sqlalchemy.dml_no_argument.strip_dml)
def transform_model(cls): """ Anything that uses the ModelMeta needs _meta and id. Also keep track of relationships and make them in the related model class. """ if cls.name != 'Model': appname = 'models' for mcls in cls.get_children(): if isinstance(mcls, ClassDef): for attr in mcls.get_children(): if isinstance(attr, Assign): if attr.targets[0].name == 'app': appname = attr.value.value mname = '{}.{}'.format(appname, cls.name) MODELS[mname] = cls for relname, relval in FUTURE_RELATIONS.get(mname, []): cls.locals[relname] = relval for attr in cls.get_children(): if isinstance(attr, Assign): try: attrname = attr.value.func.attrname except AttributeError: pass else: if attrname in ['ForeignKeyField', 'ManyToManyField']: tomodel = attr.value.args[0].value relname = '' if attr.value.keywords: for keyword in attr.value.keywords: if keyword.arg == 'related_name': relname = keyword.value.value if relname: # Injected model attributes need to also have the relation manager if attrname == 'ManyToManyField': relval = [ attr.value.func, MANAGER.ast_from_module_name( 'tortoise.fields').lookup( 'ManyToManyRelationManager')[1][0] ] else: relval = [ attr.value.func, MANAGER.ast_from_module_name( 'tortoise.fields').lookup( 'RelationQueryContainer')[1][0] ] if tomodel in MODELS: MODELS[tomodel].locals[relname] = relval else: FUTURE_RELATIONS.setdefault(tomodel, []).append( (relname, relval)) cls.locals['_meta'] = [ MANAGER.ast_from_module_name('tortoise.models').lookup('MetaInfo')[1] [0].instantiate_class() ] if 'id' not in cls.locals: cls.locals['id'] = [nodes.ClassDef('id', None)]
def transform_model(cls: ClassDef) -> None: """ Anything that uses the ModelMeta needs _meta and id. Also keep track of relationships and make them in the related model class. """ if cls.name != "Model": appname = "models" for mcls in cls.get_children(): if isinstance(mcls, ClassDef): for attr in mcls.get_children(): if isinstance(attr, Assign): if attr.targets[0].name == "app": appname = attr.value.value mname = f"{appname}.{cls.name}" MODELS[mname] = cls for relname, relval in FUTURE_RELATIONS.get(mname, []): cls.locals[relname] = relval for attr in cls.get_children(): if isinstance(attr, (Assign, AnnAssign)): try: attrname = attr.value.func.attrname except AttributeError: pass else: if attrname in [ "OneToOneField", "ForeignKeyField", "ManyToManyField" ]: tomodel = attr.value.args[0].value relname = "" if attr.value.keywords: for keyword in attr.value.keywords: if keyword.arg == "related_name": relname = keyword.value.value if not relname: relname = cls.name.lower() + "s" # Injected model attributes need to also have the relation manager if attrname == "ManyToManyField": relval = [ # attr.value.func, MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "ManyToManyFieldInstance")[1][0], MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "ManyToManyRelation")[1][0], ] elif attrname == "ForeignKeyField": relval = [ MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "ForeignKeyFieldInstance")[1][0], MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "ReverseRelation")[1][0], ] elif attrname == "OneToOneField": relval = [ MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "OneToOneFieldInstance")[1][0], MANAGER.ast_from_module_name( "tortoise.fields.relational").lookup( "OneToOneRelation")[1][0], ] if tomodel in MODELS: MODELS[tomodel].locals[relname] = relval else: FUTURE_RELATIONS.setdefault(tomodel, []).append( (relname, relval)) cls.locals["_meta"] = [ MANAGER.ast_from_module_name("tortoise.models").lookup("MetaInfo")[1] [0].instantiate_class() ] if "id" not in cls.locals: cls.locals["id"] = [nodes.ClassDef("id", None)]
def test_hashlib(self): """Tests that brain extensions for hashlib work.""" hashlib_module = MANAGER.ast_from_module_name('hashlib') for class_name in ['md5', 'sha1']: class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj)
def register(linter): """Register transform with the linter.""" MANAGER.register_transform(astroid.ClassDef, mutable_record_transform)
if isinstance(func, nodes.Attribute): if func.attrname != "require_version": return False if isinstance(func.expr, nodes.Name) and func.expr.name == "gi": return True return False if isinstance(func, nodes.Name): return func.name == "require_version" return False def _register_require_version(node): # Load the gi.require_version locally try: import gi gi.require_version(node.args[0].value, node.args[1].value) except Exception: pass return node MANAGER.register_failed_import_hook(_import_gi_module) MANAGER.register_transform( nodes.Call, _register_require_version, _looks_like_require_version )
from astroid import MANAGER from astroid import scoped_nodes NAME_DEF_GET_PLUGIN_RESOURCE = 'getPluginResources' NAME_DEF_GET_PLUGIN_INFO = 'getPluginInfo' def transform(cls): if NAME_DEF_GET_PLUGIN_RESOURCE not in cls.locals \ or NAME_DEF_GET_PLUGIN_INFO not in cls.locals: print '======= Module: ' + cls.name + ' =======' if NAME_DEF_GET_PLUGIN_RESOURCE not in cls.locals: print 'No required function ' + NAME_DEF_GET_PLUGIN_RESOURCE if NAME_DEF_GET_PLUGIN_INFO not in cls.locals: print 'No required function ' + NAME_DEF_GET_PLUGIN_INFO def register(_): pass MANAGER.register_transform(scoped_nodes.Module, transform)
for decorator_attribute in node.decorators.nodes: if decorator_attribute.as_string() in decorator_names: return True return False def attr_attributes_transform(node): """Given that the ClassNode has an attr decorator, rewrite class attributes as instance attributes """ for cdefbodynode in node.body: if not isinstance(cdefbodynode, astroid.Assign): continue if isinstance(cdefbodynode.value, astroid.Call): if cdefbodynode.value.func.as_string() != ATTR_IB: continue for target in cdefbodynode.targets: rhs_node = astroid.Unknown( lineno=cdefbodynode.lineno, col_offset=cdefbodynode.col_offset, parent=cdefbodynode ) node.locals[target.name] = [rhs_node] MANAGER.register_transform( astroid.Class, attr_attributes_transform, is_decorated_with_attrs)
return _build_dict_with_elements([]) # Builtins inference register_builtin_transform(infer_bool, "bool") register_builtin_transform(infer_super, "super") register_builtin_transform(infer_callable, "callable") register_builtin_transform(infer_property, "property") register_builtin_transform(infer_getattr, "getattr") register_builtin_transform(infer_hasattr, "hasattr") register_builtin_transform(infer_tuple, "tuple") register_builtin_transform(infer_set, "set") register_builtin_transform(infer_list, "list") register_builtin_transform(infer_dict, "dict") register_builtin_transform(infer_frozenset, "frozenset") register_builtin_transform(infer_type, "type") register_builtin_transform(infer_slice, "slice") register_builtin_transform(infer_isinstance, "isinstance") register_builtin_transform(infer_issubclass, "issubclass") register_builtin_transform(infer_len, "len") register_builtin_transform(infer_str, "str") register_builtin_transform(infer_int, "int") register_builtin_transform(infer_dict_fromkeys, "dict.fromkeys") # Infer object.__new__ calls MANAGER.register_transform( nodes.ClassDef, inference_tip(_infer_object__new__decorator), _infer_object__new__decorator_check, )
# Replace all the assignments with our mocked class. classdef = dedent(''' class %(name)s(%(types)s): @property def value(self): # Not the best return. return None @property def name(self): return %(name)r ''' % { 'name': target.name, 'types': ', '.join(node.basenames) }) fake = AstroidBuilder(MANAGER).string_build(classdef)[ target.name] fake.parent = target.parent for method in node.mymethods(): fake.locals[method.name] = [method] new_targets.append(fake.instantiate_class()) node.locals[local] = new_targets break return node MANAGER.register_transform(nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple) MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), _looks_like_enum) MANAGER.register_transform(nodes.ClassDef, infer_enum_class)
def get_ast(self, filepath, modname): """return a ast(roid) representation for a module""" try: return MANAGER.ast_from_file(filepath, modname, source=True) except SyntaxError, ex: self.add_message('E0001', line=ex.lineno, args=ex.msg)
def get_ast(self, filepath, modname): """return a ast(roid) representation for a module""" try: return MANAGER.ast_from_file(filepath, modname, source=True) except SyntaxError, ex: self.add_message('syntax-error', line=ex.lineno, args=ex.msg)
# pylint plugin to suppress false positives of type "method x has no y member". # Based on: https://stackoverflow.com/a/51678586/1014208 from astroid import MANAGER, extract_node, scoped_nodes FUNCTION_TO_WHITELISTED_PROPS = { 'logger': ('debug', 'info', 'warning', 'error', 'addHandler', 'handlers', 'setLevel'), } def register(_): pass def transform(f): for prop in FUNCTION_TO_WHITELISTED_PROPS.get(f.name, []): f.instance_attrs[prop] = extract_node( 'def {name}(arg): return'.format(name=prop)) MANAGER.register_transform(scoped_nodes.FunctionDef, transform)
def register(linter): pass def transform(cls): 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', {}).keys() for property_name in properties: property_name = property_name.replace('-', '_') # Note: We do the same in Python code cls.locals[property_name] = [scoped_nodes.Class(property_name, None)] MANAGER.register_transform(scoped_nodes.Class, transform)
func = "{}.{}".format(import_from.modname, base.func.name) except (AttributeError, KeyError, IndexError): return False return func == SIX_WITH_METACLASS def transform_six_with_metaclass(node): """Check if the given class node is defined with *six.with_metaclass* If so, inject its argument as the metaclass of the underlying class. """ call = node.bases[0] node._metaclass = call.args[0] node.bases = call.args[1:] register_module_extender(MANAGER, "six", six_moves_transform) register_module_extender(MANAGER, "requests.packages.urllib3.packages.six", six_moves_transform) MANAGER.register_failed_import_hook(_six_fail_hook) MANAGER.register_transform( nodes.ClassDef, transform_six_add_metaclass, _looks_like_decorated_with_six_add_metaclass, ) MANAGER.register_transform( nodes.ClassDef, transform_six_with_metaclass, _looks_like_nested_from_six_with_metaclass, )
class_def = nodes.ClassDef( name=node.args[0].attrname, lineno=0, col_offset=0, parent=node.parent, ) class_def.postinit( bases=[], body=[], decorators=None, metaclass=create_typing_metaclass() ) return class_def return None MANAGER.register_transform( nodes.Call, inference_tip(infer_typing_typevar_or_newtype), looks_like_typing_typevar_or_newtype, ) MANAGER.register_transform( nodes.Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript ) if PY39: MANAGER.register_transform( nodes.FunctionDef, infer_typedDict, _looks_like_typedDict ) if PY37: MANAGER.register_transform(nodes.Call, infer_typing_alias, _looks_like_typing_alias)
] }, 'replicas', 'clients', 'ad_domains', ] } def fix_ipa_classes(cls): class_name_with_module = "{}.{}".format(cls.root().name, cls.name) if class_name_with_module in ipa_class_members: fake_class(cls, ipa_class_members[class_name_with_module]) MANAGER.register_transform(scoped_nodes.Class, fix_ipa_classes) def pytest_config_transform(): """pylint.config attribute """ return AstroidBuilder(MANAGER).string_build( textwrap.dedent(''' from _pytest.config import get_config config = get_config() ''')) register_module_extender(MANAGER, 'pytest', pytest_config_transform)
## Usage: # export PYTHONPATH=`pwd`:$PYTHONPATH # pylint --load-plugins ignoretest examples/kulan.py from astroid import MANAGER from astroid import scoped_nodes def register(linter): pass def transform(modu): for m in list(modu): try: if m.startswith("test_") and modu[m].is_function: print("Ignore function: %s" % m) modu.body.remove(modu[m]) except: continue MANAGER.register_transform(scoped_nodes.Module, transform)
def transform_pyqt_signal(node): module = parse(''' class pyqtSignal(object): def connect(self, slot, type=None, no_receiver_check=False): pass def disconnect(self, slot): pass def emit(self, *args): pass ''') signal_cls = module['pyqtSignal'] node._instance_attrs['emit'] = signal_cls['emit'] node._instance_attrs['disconnect'] = signal_cls['disconnect'] node._instance_attrs['connect'] = signal_cls['connect'] def pyqt4_qtcore_transform(): return AstroidBuilder(MANAGER).string_build(''' def SIGNAL(signal_name): pass class QObject(object): def emit(self, signal): pass ''') register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform) MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, _looks_like_signal)
""" Pylint_ plugin to fix invalid errors about the :mod:`logging` module. .. _Pylint: https://pypi.python.org/pypi/pylint """ from astroid import MANAGER, scoped_nodes, nodes def register(linter): """No-op (required by Pylint).""" def verboselogs_class_transform(cls): """Make Pylint aware of ``RootLogger.verbose()`` and ``RootLogger.spam()``.""" if cls.name == 'RootLogger': for meth in ['verbose', 'spam']: cls.locals[meth] = [scoped_nodes.Function(meth, None)] def verboselogs_module_transform(mod): """Make Pylint aware of ``logging.VERBOSE`` and ``logging.SPAM``.""" if mod.name == 'logging': for const in ['VERBOSE', 'SPAM']: mod.locals[const] = [nodes.Const(const)] # Register the above methods with Pylint. MANAGER.register_transform(scoped_nodes.Class, verboselogs_class_transform) MANAGER.register_transform(scoped_nodes.Module, verboselogs_module_transform)
def register(_): 'Register web2py transformer, called by pylint' MANAGER.register_transform(scoped_nodes.Module, web2py_transform)
def register(linter): '''Register the transform function during plugin registration.''' MANAGER.register_transform(nodes.Module, transform)
if not node.decorators: return False for decorator in node.decorators.nodes: if not isinstance(decorator, astroid.Call): continue if _looks_like_functools_member(decorator, "lru_cache"): return True return False def _looks_like_functools_member(node, member): """Check if the given Call node is a functools.partial call""" if isinstance(node.func, astroid.Name): return node.func.name == member elif isinstance(node.func, astroid.Attribute): return (node.func.attrname == member and isinstance(node.func.expr, astroid.Name) and node.func.expr.name == "functools") _looks_like_partial = partial(_looks_like_functools_member, member="partial") MANAGER.register_transform(astroid.FunctionDef, _transform_lru_cache, _looks_like_lru_cache) MANAGER.register_transform( astroid.Call, astroid.inference_tip(_functools_partial_inference), _looks_like_partial, )