#2183 pylint: better detect test-files
Merged 4 months ago by frostyx. Opened 4 months ago by praiskup.
copr/ praiskup/copr pylint-test-files-ignore-docstring  into  main

@@ -4,18 +4,79 @@ 

  needed granularity.

  """

  

+ import os

+ import subprocess

  from astroid import MANAGER, scoped_nodes, nodes, extract_node

  

  def register(_linter):

      """ required pylint entrypoint """

  

- def is_test_method(method):

-     """ ignore missing-function-docstring in tests """

-     if method.name.startswith("test_"):

-         return True

-     if method.name in ["setup_method", "teardown_method"]:

-         return True

-     return False

+ class Cache:

+     """

+     Some rather expensive checks cached (as a class to avoid using globals).

+     """

+     _gitroot = None

+     _test_files = {

+         # Some modules have None in the file argument:

+         # ipdb> function

+         # <FunctionDef.cmp_to_key l.None at 0x7febf4adcb20>

+         # ipdb> function.parent

+         # <Module._functools l.0 at 0x7febf4adcbb0>

+         # ipdb> function.parent.file is None

+         # True

+         None: False,

+     }

+     test_paths = {

+         "cli/tests",

+         "common/tests",

+         "dist-git/tests",

+         "frontend/coprs_frontend/tests",

+         "keygen/tests",

+         "messaging/copr_messaging/tests",

+         "python/copr/test",

+         "rpmbuild/tests",

+     }

+ 

+     @classmethod

+     @property

+     def gitroot(cls):

+         """ Obtain the git-root of the current directory, and cache """

+         if cls._gitroot:

+             return cls._gitroot

+         cls._gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()

+         return cls.gitroot

+ 

+     @classmethod

+     def _slow_is_test_file(cls, test_file):

+         if not test_file.startswith(cls.gitroot + os.sep):

+             return False

+         relpath = os.path.relpath(test_file, cls.gitroot)

+         for test_path in cls.test_paths:

+             if relpath.startswith(test_path):

+                 return True

+         return False

+ 

+     @classmethod

+     def is_test_file(cls, file_path):

+         """

+         Check if file_path (path relative to the gitroot) is a test-file (per

+         cls.test_paths configuration).

+         """

+         cached = cls._test_files.get(file_path)

+         if cached is None:

+             cls._test_files[file_path] = cls._slow_is_test_file(file_path)

+         return cls._test_files[file_path]

+ 

+ 

+ def module_path(node):

+     """

+     Filename where the node (e.g. method) is defined.

+     """

+     while node:

+         if isinstance(node, scoped_nodes.Module):

+             return node.file

+         node = node.parent

+     return None

  

  

  def add_fake_docs(the_object):
@@ -31,6 +92,9 @@ 

      """

      Transformate some function definitions so pylint doesn't object.

      """

+ 

+     filename = module_path(function)

+ 

      if function.name == 'logger':

          for prop in ['debug', 'info', 'warning', 'error', 'exception']:

              function.instance_attrs[prop] = extract_node('def {name}(arg): return'.format(name=prop))
@@ -43,18 +107,25 @@ 

          # behave step definition

          add_fake_docs(function)

  

-     if is_test_method(function):

+     if Cache.is_test_file(filename):

          add_fake_docs(function)

  

  def transform_classes(classdef):

      """

-     Transformate some function definitions so pylint doesn't object.

+     Transform Class definitions that don't need to have docstrings.

      """

-     if classdef.name.startswith("Test"):

-         # ignore missing-function-docstring in migrations

-         classdef.doc = "fake docs"

-         classdef.doc_node = nodes.Const("fake docs")

+     filename = module_path(classdef)

+     if Cache.is_test_file(filename):

+         add_fake_docs(classdef)

+ 

+ def transform_modules(moduledef):

+     """

+     Testing modules don't nave to have the doc-strings either.

+     """

+     if Cache.is_test_file(moduledef.file):

+         add_fake_docs(moduledef)

  

  

  MANAGER.register_transform(scoped_nodes.FunctionDef, transform_functions)

  MANAGER.register_transform(scoped_nodes.ClassDef, transform_classes)

+ MANAGER.register_transform(scoped_nodes.Module, transform_modules)

We tolerate docstring related warnings in test-files. Let's detect the
test-file per the module file-names, not by just the method/class
name. This is more deterministic.

While on it, ignore the missing module doc-strings for test files.

Build succeeded.

Thank you for the PR.
I am merging without waiting several days because it is only a pylint-related change.

Pull-Request has been merged by frostyx

4 months ago
Metadata