Skip to content

Commit

Permalink
Bug pylint 3342 (#1148)
Browse files Browse the repository at this point in the history
* Adds some type hints

* Adds a logger module

* Remove this useless module

* It is probably not a good idea to apply transforms on module which have been authorized to be directly imported

* Adds a function named is_module_name_part_of_extension_package_whitelist which returns True if the beginning of a module dotted name is part of the package whitelist in argument

* Adds the extension_package_whitelist variable to the brainless manager in order unittest dealing with this managert to be ok

* Adds two tests that check the behavior of the is_module_name_part_of_extension_package_whitelist function

* Updating Changelog

* Adds explanation to the ChangeLog entry

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
  • Loading branch information
hippo91 and Pierre-Sassoulas committed Aug 29, 2021
1 parent e90016f commit 8049834
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 11 deletions.
7 changes: 7 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ What's New in astroid 2.8.0?
============================
Release date: TBA

* The transforms related to a module are applied only if this module has not been explicitly authorized to be imported
(i.e is not in AstroidManager.extension_package_whitelist). Solves the following issues if numpy is authorized to be imported
through the `extension-pkg-allow-list` option.

Closes PyCQA/pylint#3342
Closes PyCQA/pylint#4326

* Fixed bug in attribute inference from inside method calls.

Closes PyCQA/pylint#400
Expand Down
11 changes: 9 additions & 2 deletions astroid/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"""
import os
import textwrap
import types
from tokenize import detect_encoding
from typing import List, Union

Expand Down Expand Up @@ -79,7 +80,9 @@ def __init__(self, manager=None, apply_transforms=True):
super().__init__(manager)
self._apply_transforms = apply_transforms

def module_build(self, module, modname=None):
def module_build(
self, module: types.ModuleType, modname: str = None
) -> nodes.Module:
"""Build an astroid from a living module instance."""
node = None
path = getattr(module, "__file__", None)
Expand Down Expand Up @@ -157,6 +160,10 @@ def _post_build(self, module, encoding):

# Visit the transforms
if self._apply_transforms:
if modutils.is_module_name_part_of_extension_package_whitelist(
module.name, self._manager.extension_package_whitelist
):
return module
module = self._manager.visit_transforms(module)
return module

Expand Down Expand Up @@ -260,7 +267,7 @@ def delayed_assattr(self, node):
pass


def build_namespace_package_module(name, path):
def build_namespace_package_module(name, path: str) -> nodes.Module:
return nodes.Module(name, doc="", path=path, package=True)


Expand Down
12 changes: 6 additions & 6 deletions astroid/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"""

import os
import types
import zipimport
from typing import ClassVar

Expand All @@ -37,6 +38,7 @@
NoSourceFile,
file_info_from_modpath,
get_source_file,
is_module_name_part_of_extension_package_whitelist,
is_python_source,
is_standard_module,
load_module_from_name,
Expand Down Expand Up @@ -138,15 +140,13 @@ def _build_namespace_module(self, modname, path):

return build_namespace_package_module(modname, path)

def _can_load_extension(self, modname):
def _can_load_extension(self, modname: str) -> bool:
if self.always_load_extensions:
return True
if is_standard_module(modname):
return True
parts = modname.split(".")
return any(
".".join(parts[:x]) in self.extension_package_whitelist
for x in range(1, len(parts) + 1)
return is_module_name_part_of_extension_package_whitelist(
modname, self.extension_package_whitelist
)

def ast_from_module_name(self, modname, context_file=None):
Expand Down Expand Up @@ -263,7 +263,7 @@ def file_from_module_name(self, modname, contextfile):
raise value.with_traceback(None)
return value

def ast_from_module(self, module, modname=None):
def ast_from_module(self, module: types.ModuleType, modname: str = None):
"""given an imported module, return the astroid object"""
modname = modname or module.__name__
if modname in self.astroid_cache:
Expand Down
19 changes: 18 additions & 1 deletion astroid/modutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
import os
import platform
import sys
import types
from distutils.errors import DistutilsPlatformError # pylint: disable=import-error
from distutils.sysconfig import get_python_lib # pylint: disable=import-error
from typing import Set

from astroid.interpreter._import import spec, util

Expand Down Expand Up @@ -197,7 +199,7 @@ def _cache_normalize_path(path):
return result


def load_module_from_name(dotted_name):
def load_module_from_name(dotted_name: str) -> types.ModuleType:
"""Load a Python module from its name.
:type dotted_name: str
Expand Down Expand Up @@ -648,3 +650,18 @@ def is_namespace(specobj):

def is_directory(specobj):
return specobj.type == spec.ModuleType.PKG_DIRECTORY


def is_module_name_part_of_extension_package_whitelist(
module_name: str, package_whitelist: Set[str]
) -> bool:
"""
Returns True if one part of the module name is in the package whitelist
>>> is_module_name_part_of_extension_package_whitelist('numpy.core.umath', {'numpy'})
True
"""
parts = module_name.split(".")
return any(
".".join(parts[:x]) in package_whitelist for x in range(1, len(parts) + 1)
)
6 changes: 4 additions & 2 deletions astroid/raw_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def attach_import_node(node, modname, membername):
_attach_local_node(node, from_node, membername)


def build_module(name, doc=None):
def build_module(name: str, doc: str = None) -> nodes.Module:
"""create and initialize an astroid Module node"""
node = nodes.Module(name, doc, pure_python=False)
node.package = False
Expand Down Expand Up @@ -304,7 +304,9 @@ def __init__(self, manager_instance=None):
self._done = {}
self._module = None

def inspect_build(self, module, modname=None, path=None):
def inspect_build(
self, module: types.ModuleType, modname: str = None, path: str = None
) -> nodes.Module:
"""build astroid from a living module (i.e. using inspect)
this is used when there is no python source code available (either
because it's a built-in module or because the .py is not available)
Expand Down
1 change: 1 addition & 0 deletions astroid/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ def brainless_manager():
m.astroid_cache = {}
m._mod_file_cache = {}
m._transform = transforms.TransformVisitor()
m.extension_package_whitelist = {}
return m
38 changes: 38 additions & 0 deletions tests/unittest_modutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,5 +361,43 @@ def test_load_module_set_attribute(self):
self.assertTrue(m is xml.etree.ElementTree)


class ExtensionPackageWhitelistTest(unittest.TestCase):
def test_is_module_name_part_of_extension_package_whitelist_true(self):
"""Test that the is_module_name_part_of_extension_package_whitelist function returns True when needed"""
self.assertTrue(
modutils.is_module_name_part_of_extension_package_whitelist(
"numpy", {"numpy"}
)
)
self.assertTrue(
modutils.is_module_name_part_of_extension_package_whitelist(
"numpy.core", {"numpy"}
)
)
self.assertTrue(
modutils.is_module_name_part_of_extension_package_whitelist(
"numpy.core.umath", {"numpy"}
)
)

def test_is_module_name_part_of_extension_package_whitelist_success(self):
"""Test that the is_module_name_part_of_extension_package_whitelist function returns False when needed"""
self.assertFalse(
modutils.is_module_name_part_of_extension_package_whitelist(
"numpy", {"numpy.core"}
)
)
self.assertFalse(
modutils.is_module_name_part_of_extension_package_whitelist(
"numpy.core", {"numpy.core.umath"}
)
)
self.assertFalse(
modutils.is_module_name_part_of_extension_package_whitelist(
"core.umath", {"numpy"}
)
)


if __name__ == "__main__":
unittest.main()

0 comments on commit 8049834

Please sign in to comment.