From 8792c30872e7e49c2cf2076c5765238b2e92c729 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 22 Feb 2016 21:47:43 -0600 Subject: [PATCH] Add utility functions around filename matching We add utils.fnmatch and utils.filenames_for in anticipation of our checker manager creating file checkers for each file. We also include tests for these functions and a couple previously untested utility functions. --- flake8/utils.py | 55 ++++++++++++++++++++++++++++++++++++++++ tests/unit/test_utils.py | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/flake8/utils.py b/flake8/utils.py index e642d26..54c1990 100644 --- a/flake8/utils.py +++ b/flake8/utils.py @@ -1,4 +1,5 @@ """Utility methods for flake8.""" +import fnmatch as _fnmatch import io import os import sys @@ -89,3 +90,57 @@ def is_using_stdin(paths): bool """ return '-' in paths + + +def _default_predicate(*args): + return False + + +def filenames_from(arg, predicate=None): + # type: (str) -> Generator + """Generate filenames from an argument. + + :param str arg: + Parameter from the command-line. + :param callable predicate: + Predicate to use to filter out filenames. If the predicate + returns ``True`` we will exclude the filename, otherwise we + will yield it. + :returns: + Generator of paths + """ + if predicate is None: + predicate = _default_predicate + if os.path.isdir(arg): + for root, sub_directories, files in os.walk(arg): + for filename in files: + joined = os.path.join(root, filename) + if predicate(filename) or predicate(joined): + continue + yield joined + # NOTE(sigmavirus24): os.walk() will skip a directory if you + # remove it from the list of sub-directories. + for directory in sub_directories: + if predicate(directory): + sub_directories.remove(directory) + else: + yield arg + + +def fnmatch(filename, patterns, default=True): + # type: (str, List[str], bool) -> bool + """Wrap :func:`fnmatch.fnmatch` to add some functionality. + + :param str filename: + Name of the file we're trying to match. + :param list patterns: + Patterns we're using to try to match the filename. + :param bool default: + The default value if patterns is empty + :returns: + True if a pattern matches the filename, False if it doesn't. + ``default`` if patterns is empty. + """ + if not patterns: + return default + return any(_fnmatch.fnmatch(filename, pattern) for pattern in patterns) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index a6096d4..34b4909 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,5 +1,6 @@ """Tests for flake8's utils module.""" import os +import mock import pytest @@ -40,3 +41,48 @@ def test_normalize_path(value, expected): def test_normalize_paths(value, expected): """Verify we normalize comma-separated paths provided to the tool.""" assert utils.normalize_paths(value) == expected + + +def test_is_windows_checks_for_nt(): + """Verify that we correctly detect Windows.""" + with mock.patch.object(os, 'name', 'nt'): + assert utils.is_windows() is True + + with mock.patch.object(os, 'name', 'posix'): + assert utils.is_windows() is False + + +@pytest.mark.parametrize('filename,patterns,expected', [ + ('foo.py', [], True), + ('foo.py', ['*.pyc'], False), + ('foo.pyc', ['*.pyc'], True), + ('foo.pyc', ['*.swp', '*.pyc', '*.py'], True), +]) +def test_fnmatch(filename, patterns, expected): + """Verify that our fnmatch wrapper works as expected.""" + assert utils.fnmatch(filename, patterns) is expected + + +def test_filenames_from_a_directory(): + """Verify that filenames_from walks a directory.""" + filenames = list(utils.filenames_from('flake8/')) + assert len(filenames) > 2 + assert 'flake8/__init__.py' in filenames + + +def test_filenames_from_a_directory_with_a_predicate(): + """Verify that predicates filter filenames_from.""" + filenames = list(utils.filenames_from( + arg='flake8/', + predicate=lambda filename: filename == 'flake8/__init__.py', + )) + assert len(filenames) > 2 + assert 'flake8/__init__.py' not in filenames + + +def test_filenames_from_a_single_file(): + """Verify that we simply yield that filename.""" + filenames = list(utils.filenames_from('flake8/__init__.py')) + + assert len(filenames) == 1 + assert ['flake8/__init__.py'] == filenames