diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc3717204..be4b16d61 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,12 +42,12 @@ repos: - id: setup-cfg-fmt # TODO: when upgrading setup-cfg-fmt this can be removed args: [--max-py-version=3.9] -- repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.780 # NOTE: keep this in sync with setup.cfg. - hooks: - - id: mypy - files: ^(src/|testing/) - args: [] + #- repo: https://github.com/pre-commit/mirrors-mypy + #rev: v0.780 # NOTE: keep this in sync with setup.cfg. + #hooks: + #- id: mypy + #files: ^(src/|testing/) + #args: [] - repo: local hooks: - id: rst diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 6e26bf15c..887744a03 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1088,13 +1088,26 @@ class Config: if not required_plugins: return + # Imported lazily to improve start-up time. + from packaging.version import Version + from packaging.requirements import InvalidRequirement, Requirement + plugin_info = self.pluginmanager.list_plugin_distinfo() - plugin_dist_names = [dist.project_name for _, dist in plugin_info] + plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} missing_plugins = [] - for plugin in required_plugins: - if plugin not in plugin_dist_names: - missing_plugins.append(plugin) + for required_plugin in required_plugins: + spec = None + try: + spec = Requirement(required_plugin) + except InvalidRequirement: + missing_plugins.append(required_plugin) + continue + + if spec.name not in plugin_dist_info: + missing_plugins.append(required_plugin) + elif Version(plugin_dist_info[spec.name]) not in spec.specifier: + missing_plugins.append(required_plugin) if missing_plugins: fail( diff --git a/testing/test_config.py b/testing/test_config.py index a10ac41dd..420fa06f8 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -250,6 +250,63 @@ class TestParseIni: ), ( """ + [pytest] + required_plugins = pytest-xdist + """, + "", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist==1.32.0 + """, + "", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist~=1.32.0 pytest-xdist==1.32.0 pytest-xdist!=0.0.1 pytest-xdist<=99.99.0 + pytest-xdist>=1.32.0 pytest-xdist<9.9.9 pytest-xdist>1.30.0 pytest-xdist===1.32.0 + """, + "", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist>9.9.9 pytest-xdist==1.32.0 pytest-xdist==8.8.8 + """, + "Missing required plugins: pytest-xdist==8.8.8, pytest-xdist>9.9.9", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist==aegsrgrsgs + """, + "Missing required plugins: pytest-xdist==aegsrgrsgs", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist==-1 + """, + "Missing required plugins: pytest-xdist==-1", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist== pytest-xdist<= + """, + "Missing required plugins: pytest-xdist<=, pytest-xdist==", + ), + ( + """ + [pytest] + required_plugins = pytest-xdist= pytest-xdist< + """, + "Missing required plugins: pytest-xdist<, pytest-xdist=", + ), + ( + """ [some_other_header] required_plugins = wont be triggered [pytest]