From db2b6d04319ae4e61463b0a3fbf8fee9e2cb12e1 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 12:39:40 -0400
Subject: [PATCH 02/12] bump cache versions
---
.github/workflows/checks.yaml | 2 +-
.github/workflows/primer-test.yaml | 2 +-
.github/workflows/primer_comment.yaml | 2 +-
.github/workflows/primer_run_main.yaml | 2 +-
.github/workflows/primer_run_pr.yaml | 2 +-
.github/workflows/tests.yaml | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml
index a98e69466b..9abf3944b5 100644
--- a/.github/workflows/checks.yaml
+++ b/.github/workflows/checks.yaml
@@ -9,7 +9,7 @@ on:
env:
# Also change CACHE_VERSION in the other workflows
- CACHE_VERSION: 8
+ CACHE_VERSION: 9
DEFAULT_PYTHON: 3.8
PRE_COMMIT_CACHE: ~/.cache/pre-commit
diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml
index a20f6a3e08..f3ad747f63 100644
--- a/.github/workflows/primer-test.yaml
+++ b/.github/workflows/primer-test.yaml
@@ -14,7 +14,7 @@ on:
env:
# Also change CACHE_VERSION in the CI workflow
- CACHE_VERSION: 6
+ CACHE_VERSION: 7
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
diff --git a/.github/workflows/primer_comment.yaml b/.github/workflows/primer_comment.yaml
index 88bada7267..0b1e5abaee 100644
--- a/.github/workflows/primer_comment.yaml
+++ b/.github/workflows/primer_comment.yaml
@@ -13,7 +13,7 @@ on:
- completed
env:
- CACHE_VERSION: 2
+ CACHE_VERSION: 3
permissions:
contents: read
diff --git a/.github/workflows/primer_run_main.yaml b/.github/workflows/primer_run_main.yaml
index 4cb3fec7b4..077f039ace 100644
--- a/.github/workflows/primer_run_main.yaml
+++ b/.github/workflows/primer_run_main.yaml
@@ -15,7 +15,7 @@ concurrency:
cancel-in-progress: true
env:
- CACHE_VERSION: 2
+ CACHE_VERSION: 3
jobs:
run-primer:
diff --git a/.github/workflows/primer_run_pr.yaml b/.github/workflows/primer_run_pr.yaml
index 96ada3cdd1..7b0e77c3a4 100644
--- a/.github/workflows/primer_run_pr.yaml
+++ b/.github/workflows/primer_run_pr.yaml
@@ -24,7 +24,7 @@ concurrency:
cancel-in-progress: true
env:
- CACHE_VERSION: 2
+ CACHE_VERSION: 3
jobs:
run-primer:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 6712359125..83f17706bf 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -11,7 +11,7 @@ on:
env:
# Also change CACHE_VERSION in the other workflows
- CACHE_VERSION: 7
+ CACHE_VERSION: 8
jobs:
tests-linux:
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 4dc469495e..fa7d96d99d 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1137,9 +1137,24 @@ def visit_classdef(self, node: nodes.ClassDef) -> None:
"""Visit class: update consumption analysis variable."""
self._to_consume.append(NamesConsumer(node, "class"))
- def leave_classdef(self, _: nodes.ClassDef) -> None:
+ def leave_classdef(self, node: nodes.ClassDef) -> None:
"""Leave class: update consumption analysis variable."""
- # do not check for not used locals here (no sense)
+ # Check for hidden ancestor names
+ # e.g. "six" in: Class X(six.with_metaclass(ABCMeta, object)):
+ for name_node in node.nodes_of_class(nodes.Name):
+ if (
+ isinstance(name_node.parent, nodes.Call)
+ and isinstance(name_node.parent.func, nodes.Attribute)
+ and isinstance(name_node.parent.func.expr, nodes.Name)
+ ):
+ hidden_name_node = name_node.parent.func.expr
+ for consumer in self._to_consume:
+ if hidden_name_node.name in consumer.to_consume:
+ consumer.mark_as_consumed(
+ hidden_name_node.name,
+ consumer.to_consume[hidden_name_node.name],
+ )
+ break
self._to_consume.pop()
def visit_lambda(self, node: nodes.Lambda) -> None:
diff --git a/tests/functional/u/unused/unused_import.py b/tests/functional/u/unused/unused_import.py
index 143ee7cf3a..46907b47c5 100644
--- a/tests/functional/u/unused/unused_import.py
+++ b/tests/functional/u/unused/unused_import.py
@@ -3,6 +3,7 @@
import xml.etree # [unused-import]
import xml.sax # [unused-import]
import os.path as test # [unused-import]
+from abc import ABCMeta
from sys import argv as test2 # [unused-import]
from sys import flags # [unused-import]
# +1:[unused-import,unused-import]
@@ -10,6 +11,7 @@
import re, html.parser # [unused-import]
DATA = Counter()
# pylint: disable=self-assigning-variable
+import six
from fake import SomeName, SomeOtherName # [unused-import]
class SomeClass(object):
SomeName = SomeName # https://bitbucket.org/logilab/pylint/issue/475
@@ -87,3 +89,6 @@ def blop(self):
TYPE_CHECKING = False
if TYPE_CHECKING:
import zoneinfo
+
+class WithMetaclass(six.with_metaclass(ABCMeta, object)):
+ pass
diff --git a/tests/functional/u/unused/unused_import.txt b/tests/functional/u/unused/unused_import.txt
index 059405388e..cad621a599 100644
--- a/tests/functional/u/unused/unused_import.txt
+++ b/tests/functional/u/unused/unused_import.txt
@@ -1,14 +1,14 @@
unused-import:3:0:3:16::Unused import xml.etree:UNDEFINED
unused-import:4:0:4:14::Unused import xml.sax:UNDEFINED
unused-import:5:0:5:22::Unused os.path imported as test:UNDEFINED
-unused-import:6:0:6:29::Unused argv imported from sys as test2:UNDEFINED
-unused-import:7:0:7:21::Unused flags imported from sys:UNDEFINED
-unused-import:9:0:9:51::Unused OrderedDict imported from collections:UNDEFINED
-unused-import:9:0:9:51::Unused deque imported from collections:UNDEFINED
-unused-import:10:0:10:22::Unused import re:UNDEFINED
-unused-import:13:0:13:40::Unused SomeOtherName imported from fake:UNDEFINED
-unused-import:48:0:48:9::Unused import os:UNDEFINED
-unused-import:79:4:79:19::Unused import unittest:UNDEFINED
-unused-import:81:4:81:15::Unused import uuid:UNDEFINED
-unused-import:83:4:83:19::Unused import warnings:UNDEFINED
-unused-import:85:4:85:21::Unused import compileall:UNDEFINED
+unused-import:7:0:7:29::Unused argv imported from sys as test2:UNDEFINED
+unused-import:8:0:8:21::Unused flags imported from sys:UNDEFINED
+unused-import:10:0:10:51::Unused OrderedDict imported from collections:UNDEFINED
+unused-import:10:0:10:51::Unused deque imported from collections:UNDEFINED
+unused-import:11:0:11:22::Unused import re:UNDEFINED
+unused-import:15:0:15:40::Unused SomeOtherName imported from fake:UNDEFINED
+unused-import:50:0:50:9::Unused import os:UNDEFINED
+unused-import:81:4:81:19::Unused import unittest:UNDEFINED
+unused-import:83:4:83:15::Unused import uuid:UNDEFINED
+unused-import:85:4:85:19::Unused import warnings:UNDEFINED
+unused-import:87:4:87:21::Unused import compileall:UNDEFINED
From ca091b923c52697312b210c1ab2366f63c46827e Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 13:45:29 -0400
Subject: [PATCH 04/12] Update `test_regression_parallel_mode_without_filepath`
This regression test originally just targeted a crash.
---
tests/test_self.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_self.py b/tests/test_self.py
index d745161c54..22c9ad93a3 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -1060,7 +1060,7 @@ def test_regression_parallel_mode_without_filepath(self) -> None:
path = join(
HERE, "regrtest_data", "regression_missing_init_3564", "subdirectory/"
)
- self._test_output([path, "-j2"], expected_output="No such file or directory")
+ self._test_output([path, "-j2"], expected_output="")
def test_output_file_valid_path(self, tmpdir: LocalPath) -> None:
path = join(HERE, "regrtest_data", "unused_variable.py")
From 0b9dfea6f9cc7d3f639e251b3dd4aedf384c2704 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 13:54:09 -0400
Subject: [PATCH 05/12] Update docs and tests for namespace package changes
---
doc/user_guide/usage/run.rst | 13 ++++++++-----
tests/test_self.py | 23 -----------------------
3 files changed, 11 insertions(+), 28 deletions(-)
diff --git a/doc/user_guide/usage/run.rst b/doc/user_guide/usage/run.rst
index 9a15d04a38..9984a45d81 100644
--- a/doc/user_guide/usage/run.rst
+++ b/doc/user_guide/usage/run.rst
@@ -9,22 +9,25 @@ Pylint is meant to be called from the command line. The usage is ::
pylint [options] modules_or_packages
-By default the ``pylint`` command only accepts a list of python modules and packages. Using a
-directory which is not a package results in an error::
+By default the ``pylint`` command only accepts a list of python modules and packages.
+On versions below 2.15, specifying a directory that is not an explicit package
+(with ``__init__.py``) results in an error::
pylint mydir
************* Module mydir
mydir/__init__.py:1:0: F0010: error while code parsing: Unable to load file mydir/__init__.py:
[Errno 2] No such file or directory: 'mydir/__init__.py' (parse-error)
-When ``--recursive=y`` option is used, modules and packages are also accepted as parameters::
+When the ``--recursive=y`` option is used, modules and packages are also accepted as parameters::
pylint --recursive=y mydir mymodule mypackage
This option makes ``pylint`` attempt to discover all modules (files ending with ``.py`` extension)
-and all packages (all directories containing a ``__init__.py`` file).
+and all explicit packages (all directories containing a ``__init__.py`` file).
+As of version 2.15, ``pylint`` will also import namespace packages but for some edge
+cases that are still under development (where you might still find ``--recursive=y`` useful).
-Pylint **will not import** this package or module, though uses Python internals
+Pylint **will not import** this package or module, but it does use Python internals
to locate them and as such is subject to the same rules and configuration.
You should pay attention to your ``PYTHONPATH``, since it is a common error
to analyze an installed version of a module instead of the development version.
diff --git a/tests/test_self.py b/tests/test_self.py
index 22c9ad93a3..53e88d5ffb 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -1228,13 +1228,6 @@ def test_max_inferred_for_complicated_class_hierarchy() -> None:
# Error code should not include bit-value 1 for crash
assert not ex.value.code % 2
- def test_regression_recursive(self):
- """Tests if error is raised when linter is executed over directory not using --recursive=y"""
- self._test_output(
- [join(HERE, "regrtest_data", "directory", "subdirectory"), "--recursive=n"],
- expected_output="No such file or directory",
- )
-
def test_recursive(self):
"""Tests if running linter over directory using --recursive=y"""
self._runtest(
@@ -1351,22 +1344,6 @@ def test_ignore_path_recursive_current_dir(self) -> None:
code=0,
)
- def test_regression_recursive_current_dir(self):
- with _test_sys_path():
- # pytest is including directory HERE/regrtest_data to sys.path which causes
- # astroid to believe that directory is a package.
- sys.path = [
- path
- for path in sys.path
- if not os.path.basename(path) == "regrtest_data"
- ]
- with _test_cwd():
- os.chdir(join(HERE, "regrtest_data", "directory"))
- self._test_output(
- ["."],
- expected_output="No such file or directory",
- )
-
class TestCallbackOptions:
"""Test for all callback options we support."""
From 1abbbef641a6f8b45c06a907ad7ca23c3cf0ba97 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 15:57:14 -0400
Subject: [PATCH 06/12] bump another cache number
---
.github/workflows/primer-test.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml
index f3ad747f63..9e9209de48 100644
--- a/.github/workflows/primer-test.yaml
+++ b/.github/workflows/primer-test.yaml
@@ -14,7 +14,7 @@ on:
env:
# Also change CACHE_VERSION in the CI workflow
- CACHE_VERSION: 7
+ CACHE_VERSION: 8
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
From 9f67550f4417941d38725420d244c39b0564bca8 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 19:51:03 -0400
Subject: [PATCH 07/12] Adjust `pyreverse` test results accounting for `tests`
as a namespace package
---
tests/pyreverse/test_diadefs.py | 28 ++++++++++++++--------------
tests/pyreverse/test_inspector.py | 16 ++++++++--------
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/tests/pyreverse/test_diadefs.py b/tests/pyreverse/test_diadefs.py
index 01457802c4..e33b55e0f3 100644
--- a/tests/pyreverse/test_diadefs.py
+++ b/tests/pyreverse/test_diadefs.py
@@ -93,10 +93,10 @@ def test_default_values() -> None:
class TestDefaultDiadefGenerator:
_should_rels = [
- ("association", "DoNothing", "Ancestor"),
- ("association", "DoNothing", "Specialization"),
- ("association", "DoNothing2", "Specialization"),
- ("implements", "Ancestor", "Interface"),
+ # ("association", "DoNothing", "Ancestor"),
+ # ("association", "DoNothing", "Specialization"),
+ # ("association", "DoNothing2", "Specialization"),
+ # ("implements", "Ancestor", "Interface"),
("specialization", "Specialization", "Ancestor"),
]
@@ -134,10 +134,10 @@ def test_known_values1(HANDLER: DiadefsHandler, PROJECT: Project) -> None:
assert pd.title == "packages No Name"
modules = sorted((isinstance(m.node, nodes.Module), m.title) for m in pd.objects)
assert modules == [
- (True, "data"),
- (True, "data.clientmodule_test"),
- (True, "data.property_pattern"),
- (True, "data.suppliermodule_test"),
+ (True, "tests.data"),
+ (True, "tests.data.clientmodule_test"),
+ (True, "tests.data.property_pattern"),
+ (True, "tests.data.suppliermodule_test"),
]
cd = dd[1]
assert cd.title == "classes No Name"
@@ -155,7 +155,7 @@ def test_known_values1(HANDLER: DiadefsHandler, PROJECT: Project) -> None:
def test_known_values2(HANDLER: DiadefsHandler, get_project: Callable) -> None:
- project = get_project("data.clientmodule_test")
+ project = get_project("tests.data.clientmodule_test")
dd = DefaultDiadefGenerator(Linker(project), HANDLER).visit(project)
assert len(dd) == 1
keys = [d.TYPE for d in dd]
@@ -169,15 +169,15 @@ def test_known_values2(HANDLER: DiadefsHandler, get_project: Callable) -> None:
def test_known_values3(HANDLER: DiadefsHandler, PROJECT: Project) -> None:
HANDLER.config.classes = ["Specialization"]
cdg = ClassDiadefGenerator(Linker(PROJECT), HANDLER)
- special = "data.clientmodule_test.Specialization"
+ special = "tests.data.clientmodule_test.Specialization"
cd = cdg.class_diagram(PROJECT, special)
assert cd.title == special
classes = _process_classes(cd.objects)
assert classes == [
- (True, "data.clientmodule_test.Ancestor"),
- (True, special),
(True, "data.suppliermodule_test.DoNothing"),
(True, "data.suppliermodule_test.DoNothing2"),
+ (True, "tests.data.clientmodule_test.Ancestor"),
+ (True, special),
]
@@ -185,9 +185,9 @@ def test_known_values4(HANDLER: DiadefsHandler, PROJECT: Project) -> None:
HANDLER.config.classes = ["Specialization"]
HANDLER.config.module_names = False
cd = ClassDiadefGenerator(Linker(PROJECT), HANDLER).class_diagram(
- PROJECT, "data.clientmodule_test.Specialization"
+ PROJECT, "tests.data.clientmodule_test.Specialization"
)
- assert cd.title == "data.clientmodule_test.Specialization"
+ assert cd.title == "tests.data.clientmodule_test.Specialization"
classes = _process_classes(cd.objects)
assert classes == [
(True, "Ancestor"),
diff --git a/tests/pyreverse/test_inspector.py b/tests/pyreverse/test_inspector.py
index 0fd54e8f81..6b22450bb1 100644
--- a/tests/pyreverse/test_inspector.py
+++ b/tests/pyreverse/test_inspector.py
@@ -28,7 +28,7 @@ def project(get_project: Callable) -> Project:
def test_class_implements(project: Project) -> None:
- klass = project.get_module("data.clientmodule_test")["Ancestor"]
+ klass = project.get_module("tests.data.clientmodule_test")["Ancestor"]
assert hasattr(klass, "implements")
assert len(klass.implements) == 1
assert isinstance(klass.implements[0], nodes.ClassDef)
@@ -36,13 +36,13 @@ def test_class_implements(project: Project) -> None:
def test_class_implements_specialization(project: Project) -> None:
- klass = project.get_module("data.clientmodule_test")["Specialization"]
+ klass = project.get_module("tests.data.clientmodule_test")["Specialization"]
assert hasattr(klass, "implements")
assert len(klass.implements) == 0
def test_locals_assignment_resolution(project: Project) -> None:
- klass = project.get_module("data.clientmodule_test")["Specialization"]
+ klass = project.get_module("tests.data.clientmodule_test")["Specialization"]
assert hasattr(klass, "locals_type")
type_dict = klass.locals_type
assert len(type_dict) == 2
@@ -55,7 +55,7 @@ def test_locals_assignment_resolution(project: Project) -> None:
def test_instance_attrs_resolution(project: Project) -> None:
- klass = project.get_module("data.clientmodule_test")["Specialization"]
+ klass = project.get_module("tests.data.clientmodule_test")["Specialization"]
assert hasattr(klass, "instance_attrs_type")
type_dict = klass.instance_attrs_type
assert len(type_dict) == 3
@@ -124,10 +124,10 @@ def test_from_directory(project: Project) -> None:
def test_project_node(project: Project) -> None:
expected = [
- "data",
- "data.clientmodule_test",
- "data.property_pattern",
- "data.suppliermodule_test",
+ "tests.data",
+ "tests.data.clientmodule_test",
+ "tests.data.property_pattern",
+ "tests.data.suppliermodule_test",
]
assert sorted(project.keys()) == expected
From d8d93d60f3881a368c6446db1c16a794855cee46 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 20:15:47 -0400
Subject: [PATCH 08/12] Update expected result for `func_w0401_package`
---
tests/messages/func_w0401_package.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/messages/func_w0401_package.txt b/tests/messages/func_w0401_package.txt
index ca05124482..f712cf79e8 100644
--- a/tests/messages/func_w0401_package.txt
+++ b/tests/messages/func_w0401_package.txt
@@ -1,2 +1,2 @@
-R: 1: Cyclic import (input.func_w0401_package.all_the_things -> input.func_w0401_package.thing2)
+R: 1: Cyclic import (tests.input.func_w0401_package.all_the_things -> tests.input.func_w0401_package.thing2)
W: 8: Using a conditional statement with a constant value
From 75ff2c421b4c3f655519655b9ecc602308c99cd3 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sat, 9 Jul 2022 20:18:02 -0400
Subject: [PATCH 09/12] don't say "import"
---
doc/user_guide/usage/run.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/user_guide/usage/run.rst b/doc/user_guide/usage/run.rst
index 9984a45d81..0c4357e724 100644
--- a/doc/user_guide/usage/run.rst
+++ b/doc/user_guide/usage/run.rst
@@ -24,7 +24,7 @@ When the ``--recursive=y`` option is used, modules and packages are also accepte
This option makes ``pylint`` attempt to discover all modules (files ending with ``.py`` extension)
and all explicit packages (all directories containing a ``__init__.py`` file).
-As of version 2.15, ``pylint`` will also import namespace packages but for some edge
+As of version 2.15, ``pylint`` will also discover namespace packages but for some edge
cases that are still under development (where you might still find ``--recursive=y`` useful).
Pylint **will not import** this package or module, but it does use Python internals
From 5eccc10b8d38a8c93887949e2002e5e30443e758 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sun, 10 Jul 2022 08:29:07 -0400
Subject: [PATCH 11/12] bump cache versions after 2.12.1
---
.github/workflows/checks.yaml | 2 +-
.github/workflows/primer-test.yaml | 2 +-
.github/workflows/tests.yaml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml
index 9abf3944b5..8e3c71522f 100644
--- a/.github/workflows/checks.yaml
+++ b/.github/workflows/checks.yaml
@@ -9,7 +9,7 @@ on:
env:
# Also change CACHE_VERSION in the other workflows
- CACHE_VERSION: 9
+ CACHE_VERSION: 10
DEFAULT_PYTHON: 3.8
PRE_COMMIT_CACHE: ~/.cache/pre-commit
diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml
index 9e9209de48..8eac5d61bb 100644
--- a/.github/workflows/primer-test.yaml
+++ b/.github/workflows/primer-test.yaml
@@ -14,7 +14,7 @@ on:
env:
# Also change CACHE_VERSION in the CI workflow
- CACHE_VERSION: 8
+ CACHE_VERSION: 9
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 83f17706bf..95e9ba2206 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -11,7 +11,7 @@ on:
env:
# Also change CACHE_VERSION in the other workflows
- CACHE_VERSION: 8
+ CACHE_VERSION: 9
jobs:
tests-linux:
From ef3eb765c6c6fb4cbdd72091ce3815b5ffe7fc22 Mon Sep 17 00:00:00 2001
From: Jacob Walls <jacobtylerwalls@gmail.com>
Date: Sun, 10 Jul 2022 10:27:16 -0400
Subject: [PATCH 12/12] Update `contributors-txt` to 0.9.0
---
requirements_test.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements_test.txt b/requirements_test.txt
index 997960d005..c454f73e47 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -4,7 +4,7 @@ coveralls~=3.3
coverage~=6.4
pre-commit~=2.19
tbump~=6.9.0
-contributors-txt>=0.7.3
+contributors-txt>=0.9.0
pytest-cov~=3.0
pytest-profiling~=1.7
pytest-xdist~=2.5