Skip to content

Commit

Permalink
Add ignore-paths configuration directive
Browse files Browse the repository at this point in the history
List of regex matching against the full path

Close pylint-dev#2541
  • Loading branch information
bernardn authored and fdamken committed May 26, 2021
1 parent 0b5a443 commit 49b710f
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,5 @@ contributors:
* ruro: contributor

* David Liu (david-yz-liu): contributor

* Bernard Nauwelaerts: contributor
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,10 @@ What's New in Pylint 2.6.0?

Release date: 2020-08-20

* Added ``ignore-paths`` behaviour. Defined regex patterns are matched against full file path.

Close #2541

* Fix various scope-related bugs in ``undefined-variable`` checker

Close #1082, #3434, #3461
Expand Down
16 changes: 15 additions & 1 deletion pylint/lint/pylinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ def make_options():
" skipped. The regex matches against base names, not paths.",
},
),
(
"ignore-paths",
{
"type": "regexp_csv",
"metavar": "<pattern>[,<pattern>...]",
"dest": "black_list_paths_re",
"default": (),
"help": "Add files or directories matching the regex patterns to the"
" blacklist. The regex matches against paths.",
},
),
(
"persistent",
{
Expand Down Expand Up @@ -1046,7 +1057,10 @@ def _iterate_file_descrs(self, files_or_modules):
def _expand_files(self, modules):
"""get modules and errors from a list of modules and handle errors"""
result, errors = expand_modules(
modules, self.config.black_list, self.config.black_list_re
modules,
self.config.black_list,
self.config.black_list_re,
self.config.black_list_paths_re,
)
for error in errors:
message = modname = error["mod"]
Expand Down
1 change: 1 addition & 0 deletions pylint/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
IsortDriver,
_check_csv,
_format_option_value,
_is_in_blacklist_re,
_splitstrip,
_unquote,
decoding_stream,
Expand Down
143 changes: 143 additions & 0 deletions pylint/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,149 @@ def tokenize_module(module):
return list(tokenize.tokenize(readline))


def _is_in_blacklist_re(element, black_list_re):
"""Determines if the element is matched in a regex blacklist
:param str element: The element to be checked.
:param list black_list_re: A collection of regex patterns to match against.
Successful matches are blacklisted.
:returns: `True` if the basename is blacklisted, `False` otherwise.
:rtype: bool
"""
for file_pattern in black_list_re:
if file_pattern.match(element):
return True
return False


def _modpath_from_file(filename, is_namespace, path=None):
def _is_package_cb(path, parts):
return modutils.check_modpath_has_init(path, parts) or is_namespace

return modutils.modpath_from_file_with_callback(
filename, path=path, is_package_cb=_is_package_cb
)


def get_python_path(filepath):
dirname = os.path.realpath(os.path.expanduser(filepath))
if not os.path.isdir(dirname):
dirname = os.path.dirname(dirname)
while True:
if not os.path.exists(os.path.join(dirname, "__init__.py")):
return dirname
old_dirname = dirname
dirname = os.path.dirname(dirname)
if old_dirname == dirname:
return os.getcwd()
return None


def expand_modules(files_or_modules, black_list, black_list_re, black_list_paths_re):
"""take a list of files/modules/packages and return the list of tuple
(file, module name) which have to be actually checked
"""
result = []
errors = []
path = sys.path.copy()

for something in files_or_modules:
if os.path.basename(something) in black_list:
continue

if _is_in_blacklist_re(os.path.basename(something), black_list_re):
continue

if _is_in_blacklist_re(something, black_list_paths_re):
continue

module_path = get_python_path(something)
additional_search_path = [".", module_path] + path
if os.path.exists(something):
# this is a file or a directory
try:
modname = ".".join(
modutils.modpath_from_file(something, path=additional_search_path)
)
except ImportError:
modname = os.path.splitext(os.path.basename(something))[0]
if os.path.isdir(something):
filepath = os.path.join(something, "__init__.py")
else:
filepath = something
else:
# suppose it's a module or package
modname = something
try:
filepath = modutils.file_from_modpath(
modname.split("."), path=additional_search_path
)
if filepath is None:
continue
except (ImportError, SyntaxError) as ex:
# The SyntaxError is a Python bug and should be
# removed once we move away from imp.find_module: https://bugs.python.org/issue10588
errors.append({"key": "fatal", "mod": modname, "ex": ex})
continue

filepath = os.path.normpath(filepath)
modparts = (modname or something).split(".")

try:
spec = modutils.file_info_from_modpath(
modparts, path=additional_search_path
)
except ImportError:
# Might not be acceptable, don't crash.
is_namespace = False
is_directory = os.path.isdir(something)
else:
is_namespace = modutils.is_namespace(spec)
is_directory = modutils.is_directory(spec)

if not is_namespace:
result.append(
{
"path": filepath,
"name": modname,
"isarg": True,
"basepath": filepath,
"basename": modname,
}
)

has_init = (
not (modname.endswith(".__init__") or modname == "__init__")
and os.path.basename(filepath) == "__init__.py"
)
if has_init or is_namespace or is_directory:
for subfilepath in modutils.get_module_files(
os.path.dirname(filepath), black_list, list_all=is_namespace
):
if filepath == subfilepath:
continue
if _is_in_blacklist_re(os.path.basename(subfilepath), black_list_re):
continue
if _is_in_blacklist_re(subfilepath, black_list_paths_re):
continue

modpath = _modpath_from_file(
subfilepath, is_namespace, path=additional_search_path
)
submodname = ".".join(modpath)
result.append(
{
"path": subfilepath,
"name": submodname,
"isarg": False,
"basepath": filepath,
"basename": modname,
}
)
return result, errors


def register_plugins(linter, directory):
"""load all module and package in the given directory, looking for a
'register' function in each one, used to register pylint checkers
Expand Down
12 changes: 12 additions & 0 deletions tests/utils/unittest_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
from pylint.utils import utils


def test__is_in_blacklist_re_match():
patterns = [re.compile(".*enchilada.*"), re.compile("unittest_.*")]
assert utils._is_in_blacklist_re("unittest_utils.py", patterns)
assert utils._is_in_blacklist_re("cheese_enchiladas.xml", patterns)


def test__is_in_blacklist_re_nomatch():
patterns = [re.compile(".*enchilada.*"), re.compile("unittest_.*")]
assert not utils._is_in_blacklist_re("test_utils.py", patterns)
assert not utils._is_in_blacklist_re("enchilad.py", patterns)


def test_decoding_stream_unknown_encoding():
"""decoding_stream should fall back to *some* decoding when given an
unknown encoding.
Expand Down

0 comments on commit 49b710f

Please sign in to comment.