diff --git a/ruff_defaults.toml b/ruff_defaults.toml index 1ac6c03b3..65eca34dd 100644 --- a/ruff_defaults.toml +++ b/ruff_defaults.toml @@ -317,6 +317,7 @@ select = [ "PLW2901", "PLW3201", "PLW3301", + "PT001", "PT002", "PT003", "PT006", @@ -336,6 +337,7 @@ select = [ "PT020", "PT021", "PT022", + "PT023", "PT024", "PT025", "PT026", @@ -598,3 +600,7 @@ ban-relative-imports = "all" [lint.isort] known-first-party = ["hatch"] + +[lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false diff --git a/scripts/update_ruff.py b/scripts/update_ruff.py index 44fc2badf..8e64d8bf9 100644 --- a/scripts/update_ruff.py +++ b/scripts/update_ruff.py @@ -28,7 +28,7 @@ # Conflicts with type checking 'RET501', 'RET502', # Under review https://github.com/astral-sh/ruff/issues/8796 - 'PT001', 'PT004', 'PT005', 'PT023', + 'PT004', 'PT005', # Buggy https://github.com/astral-sh/ruff/issues/4845 'ERA001', # Too prone to false positives and might be removed https://github.com/astral-sh/ruff/issues/4045 diff --git a/src/hatch/env/internal/fmt.py b/src/hatch/env/internal/fmt.py index aae67b339..4c3a403aa 100644 --- a/src/hatch/env/internal/fmt.py +++ b/src/hatch/env/internal/fmt.py @@ -108,6 +108,10 @@ def construct_config_file(self, *, preview: bool | None) -> str: '', '[lint.isort]', f'known-first-party = ["{self.metadata.name.replace("-", "_")}"]', + '', + '[lint.flake8-pytest-style]', + 'fixture-parentheses = false', + 'mark-parentheses = false', ) ) @@ -438,6 +442,7 @@ def get_config(self, section: str) -> dict[str, Any]: 'PLW1510', 'PLW2901', 'PLW3301', + 'PT001', 'PT002', 'PT003', 'PT006', @@ -457,6 +462,7 @@ def get_config(self, section: str) -> dict[str, Any]: 'PT020', 'PT021', 'PT022', + 'PT023', 'PT024', 'PT025', 'PT026', diff --git a/tests/backend/builders/plugin/test_interface.py b/tests/backend/builders/plugin/test_interface.py index 0fed01b62..16afe2d24 100644 --- a/tests/backend/builders/plugin/test_interface.py +++ b/tests/backend/builders/plugin/test_interface.py @@ -146,7 +146,7 @@ def test_unknown(self, isolation): class TestDirectoryRecursion: - @pytest.mark.requires_unix() + @pytest.mark.requires_unix def test_infinite_loop_prevention(self, temp_dir): project_dir = temp_dir / 'project' project_dir.ensure_dir_exists() diff --git a/tests/backend/builders/test_wheel.py b/tests/backend/builders/test_wheel.py index 2dc9ee4df..4fd7014d3 100644 --- a/tests/backend/builders/test_wheel.py +++ b/tests/backend/builders/test_wheel.py @@ -1833,7 +1833,7 @@ def initialize(self, version, build_data): ) helpers.assert_files(extraction_directory, expected_files) - @pytest.mark.requires_unix() + @pytest.mark.requires_unix def test_default_symlink(self, hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['src-layout'] = False config_file.save() @@ -2185,7 +2185,7 @@ def test_editable_default_force_include_option(self, hatch, helpers, temp_dir): zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) - @pytest.mark.requires_unix() + @pytest.mark.requires_unix def test_editable_default_symlink(self, hatch, helpers, temp_dir): project_name = 'My.App' @@ -3066,7 +3066,7 @@ def initialize(self, version, build_data): ) helpers.assert_files(extraction_directory, expected_files) - @pytest.mark.requires_macos() + @pytest.mark.requires_macos def test_macos_max_compat(self, hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['src-layout'] = False config_file.save() diff --git a/tests/backend/dep/test_core.py b/tests/backend/dep/test_core.py index bc1d1441a..b04c30702 100644 --- a/tests/backend/dep/test_core.py +++ b/tests/backend/dep/test_core.py @@ -17,14 +17,14 @@ def test_dependency_not_found(platform): assert not dependencies_in_sync([Requirement('binary')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_dependency_found(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'binary'], check=True, capture_output=True) assert dependencies_in_sync([Requirement('binary')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_version_unmet(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'binary'], check=True, capture_output=True) @@ -41,36 +41,36 @@ def test_marker_unmet(platform): assert not dependencies_in_sync([Requirement('binary; python_version > "1"')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_extra_no_dependencies(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'binary'], check=True, capture_output=True) assert not dependencies_in_sync([Requirement('binary[foo]')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_unknown_extra(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'requests[security]==2.25.1'], check=True, capture_output=True) assert not dependencies_in_sync([Requirement('requests[foo]')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_extra_unmet(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'requests==2.25.1'], check=True, capture_output=True) assert not dependencies_in_sync([Requirement('requests[security]==2.25.1')], venv.sys_path) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_extra_met(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command(['pip', 'install', 'requests[security]==2.25.1'], check=True, capture_output=True) assert dependencies_in_sync([Requirement('requests[security]==2.25.1')], venv.sys_path) -@pytest.mark.requires_internet() -@pytest.mark.requires_git() +@pytest.mark.requires_internet +@pytest.mark.requires_git def test_dependency_git(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command( @@ -79,8 +79,8 @@ def test_dependency_git(platform): assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests')], venv.sys_path) -@pytest.mark.requires_internet() -@pytest.mark.requires_git() +@pytest.mark.requires_internet +@pytest.mark.requires_git def test_dependency_git_revision(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command( @@ -89,8 +89,8 @@ def test_dependency_git_revision(platform): assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests@main')], venv.sys_path) -@pytest.mark.requires_internet() -@pytest.mark.requires_git() +@pytest.mark.requires_internet +@pytest.mark.requires_git def test_dependency_git_commit(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command( diff --git a/tests/cli/env/test_create.py b/tests/cli/env/test_create.py index 9b7fa6a7b..e584246f0 100644 --- a/tests/cli/env/test_create.py +++ b/tests/cli/env/test_create.py @@ -884,7 +884,7 @@ def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): assert env_dirs[0].name == 'test.42' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_install_project_default_dev_mode( hatch, helpers, temp_dir, platform, config_file, extract_installed_requirements ): @@ -944,7 +944,7 @@ def test_install_project_default_dev_mode( assert requirements[0].lower() == f'-e {str(project_path).lower()}' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_install_project_no_dev_mode(hatch, helpers, temp_dir, platform, config_file, extract_installed_requirements): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1003,7 +1003,7 @@ def test_install_project_no_dev_mode(hatch, helpers, temp_dir, platform, config_ assert requirements[0].startswith('my-app @') -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_pre_install_commands(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1081,7 +1081,7 @@ def test_pre_install_commands_error(hatch, helpers, temp_dir, config_file): ) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_post_install_commands(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1123,7 +1123,7 @@ def test_post_install_commands(hatch, helpers, temp_dir, config_file): assert (project_path / 'test.txt').is_file() -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_post_install_commands_error(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1161,7 +1161,7 @@ def test_post_install_commands_error(hatch, helpers, temp_dir, config_file): ) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_sync_dependencies(hatch, helpers, temp_dir, platform, config_file, extract_installed_requirements): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1232,7 +1232,7 @@ def test_sync_dependencies(hatch, helpers, temp_dir, platform, config_file, extr assert requirements[1].lower() == f'-e {str(project_path).lower()}' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_features(hatch, helpers, temp_dir, platform, config_file, extract_installed_requirements): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1295,7 +1295,7 @@ def test_features(hatch, helpers, temp_dir, platform, config_file, extract_insta assert requirements[1].lower() == f'-e {str(project_path).lower()}' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_sync_dynamic_dependencies(hatch, helpers, temp_dir, platform, config_file, extract_installed_requirements): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1394,7 +1394,7 @@ def update(self, metadata): assert requirements[3].lower() == f'my-app1 @ {path_to_uri(project_path).lower()}/../my-app1' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_unknown_dynamic_feature(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() diff --git a/tests/cli/fmt/test_fmt.py b/tests/cli/fmt/test_fmt.py index e4bb2346c..a1c4c90d4 100644 --- a/tests/cli/fmt/test_fmt.py +++ b/tests/cli/fmt/test_fmt.py @@ -33,6 +33,10 @@ def construct_ruff_defaults_file(rules: tuple[str, ...]) -> str: '', '[lint.isort]', 'known-first-party = ["my_app"]', + '', + '[lint.flake8-pytest-style]', + 'fixture-parentheses = false', + 'mark-parentheses = false', ) ) diff --git a/tests/cli/new/test_new.py b/tests/cli/new/test_new.py index bd8dc9918..5fb306f59 100644 --- a/tests/cli/new/test_new.py +++ b/tests/cli/new/test_new.py @@ -134,7 +134,7 @@ def test_default_empty_plugins_table(hatch, helpers, config_file, temp_dir): ) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_default_no_license_cache(hatch, helpers, temp_dir): project_name = 'My.App' cache_dir = temp_dir / 'cache' diff --git a/tests/cli/project/test_metadata.py b/tests/cli/project/test_metadata.py index 32f776176..1d05bf756 100644 --- a/tests/cli/project/test_metadata.py +++ b/tests/cli/project/test_metadata.py @@ -140,7 +140,7 @@ def test_field_complex(self, hatch, temp_dir, helpers): class TestBuildDependenciesMissing: - @pytest.mark.allow_backend_process() + @pytest.mark.allow_backend_process def test_incompatible_environment(self, hatch, temp_dir, helpers): project_name = 'My.App' diff --git a/tests/cli/publish/test_publish.py b/tests/cli/publish/test_publish.py index 69d81cb86..2e9b83b4a 100644 --- a/tests/cli/publish/test_publish.py +++ b/tests/cli/publish/test_publish.py @@ -21,7 +21,7 @@ def keyring_store(mocker): return mock_store -@pytest.fixture() +@pytest.fixture def published_project_name(): return f'c4880cdbe05de9a28415fbad{secrets.choice(range(100))}' diff --git a/tests/cli/python/conftest.py b/tests/cli/python/conftest.py index 25a11719e..c5c6aea7c 100644 --- a/tests/cli/python/conftest.py +++ b/tests/cli/python/conftest.py @@ -25,6 +25,6 @@ def disable_path_detectors(mocker): mocker.patch('userpath.in_new_path', return_value=False) -@pytest.fixture() +@pytest.fixture def dist_name(compatible_python_distributions): return secrets.choice(compatible_python_distributions) diff --git a/tests/cli/python/test_install.py b/tests/cli/python/test_install.py index 2bb79d8ae..98a0d15f0 100644 --- a/tests/cli/python/test_install.py +++ b/tests/cli/python/test_install.py @@ -59,7 +59,7 @@ def test_incompatible_all(hatch, helpers, path_append, platform, mocker): path_append.assert_not_called() -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_installation( hatch, helpers, temp_dir_data, platform, path_append, default_shells, compatible_python_distributions ): diff --git a/tests/cli/run/test_run.py b/tests/cli/run/test_run.py index 62fe201b1..ff19e4146 100644 --- a/tests/cli/run/test_run.py +++ b/tests/cli/run/test_run.py @@ -145,7 +145,7 @@ def test_enter_project_directory(hatch, config_file, helpers, temp_dir): assert str(env_path) in str(output_file.read_text()) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_sync_dependencies(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -220,7 +220,7 @@ def test_sync_dependencies(hatch, helpers, temp_dir, config_file): assert str(output_file.read_text()) == "(1.0, 'KiB')" -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -293,7 +293,7 @@ def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): assert str(output_file.read_text()) == "(1.0, 'KiB')" -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_sync_project_features(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -372,7 +372,7 @@ def test_sync_project_features(hatch, helpers, temp_dir, config_file): assert str(output_file.read_text()) == "(1.0, 'KiB')" -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -586,7 +586,7 @@ def test_scripts_specific_environment(hatch, helpers, temp_dir, config_file): assert env_var_value == 'bar' -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_scripts_no_environment(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1865,7 +1865,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p assert str(env_path) in str(output_file.read_text()) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_install_python_specific(hatch, helpers, temp_dir, config_file, mocker, available_python_version): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -1932,7 +1932,7 @@ def test_install_python_specific(hatch, helpers, temp_dir, config_file, mocker, assert list(manager.get_installed()) == [available_python_version] -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_update_python_specific(hatch, helpers, temp_dir, config_file, mocker, available_python_version): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -2002,7 +2002,7 @@ def test_update_python_specific(hatch, helpers, temp_dir, config_file, mocker, a assert str(env_path) in str(output_file.read_text()) -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_install_python_max_compatible(hatch, helpers, temp_dir, config_file, mocker, available_python_version): config_file.model.template.plugins['default']['tests'] = False config_file.save() @@ -2065,7 +2065,7 @@ def test_install_python_max_compatible(hatch, helpers, temp_dir, config_file, mo assert list(manager.get_installed()) == [available_python_version] -@pytest.mark.requires_internet() +@pytest.mark.requires_internet def test_update_python_max_compatible(hatch, helpers, temp_dir, config_file, mocker, available_python_version): config_file.model.template.plugins['default']['tests'] = False config_file.save() diff --git a/tests/conftest.py b/tests/conftest.py index 043689efb..2cbd4ba06 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -130,13 +130,13 @@ def uri_slash_prefix(): return '//' if os.sep == '/' else '///' -@pytest.fixture() +@pytest.fixture def temp_dir() -> Generator[Path, None, None]: with temp_directory() as d: yield d -@pytest.fixture() +@pytest.fixture def temp_dir_data(temp_dir) -> Generator[Path, None, None]: data_path = temp_dir / 'data' data_path.mkdir() @@ -145,7 +145,7 @@ def temp_dir_data(temp_dir) -> Generator[Path, None, None]: yield temp_dir -@pytest.fixture() +@pytest.fixture def temp_dir_cache(temp_dir) -> Generator[Path, None, None]: cache_path = temp_dir / 'cache' cache_path.mkdir() @@ -283,7 +283,7 @@ def devpi(tmp_path_factory, worker_id): shutil.rmtree(devpi_data) -@pytest.fixture() +@pytest.fixture def mock_backend_process(request, mocker): if 'allow_backend_process' in request.keywords: yield False @@ -319,7 +319,7 @@ def mock_process(command, **kwargs): yield True -@pytest.fixture() +@pytest.fixture def mock_backend_process_output(request, mocker): if 'allow_backend_process' in request.keywords: yield False @@ -360,7 +360,7 @@ def mock_process(command, **kwargs): yield True -@pytest.fixture() +@pytest.fixture def mock_plugin_installation(mocker): subprocess_run = subprocess.run mocked_subprocess_run = mocker.MagicMock(returncode=0) @@ -377,7 +377,7 @@ def _mock(command, **kwargs): return mocked_subprocess_run -@pytest.fixture() +@pytest.fixture def local_backend_process(mock_backend_process, mocker): if mock_backend_process: mocker.patch('hatch.env.virtual.VirtualEnvironment.build_environment') @@ -385,7 +385,7 @@ def local_backend_process(mock_backend_process, mocker): return mock_backend_process -@pytest.fixture() +@pytest.fixture def local_backend_process_output(mock_backend_process_output, mocker): if mock_backend_process_output: mocker.patch('hatch.env.virtual.VirtualEnvironment.build_environment') diff --git a/tests/python/test_core.py b/tests/python/test_core.py index f1c788b1d..475c8640f 100644 --- a/tests/python/test_core.py +++ b/tests/python/test_core.py @@ -7,7 +7,7 @@ from hatch.python.resolve import get_distribution -@pytest.mark.requires_internet() +@pytest.mark.requires_internet @pytest.mark.parametrize('name', ORDERED_DISTRIBUTIONS) def test_installation(temp_dir, platform, name): # Ensure the source and any parent directories get created diff --git a/tests/utils/test_platform.py b/tests/utils/test_platform.py index 31e2259eb..86389f18d 100644 --- a/tests/utils/test_platform.py +++ b/tests/utils/test_platform.py @@ -8,7 +8,7 @@ from hatch.utils.structures import EnvVars -@pytest.mark.requires_windows() +@pytest.mark.requires_windows class TestWindows: def test_tag(self): assert Platform().windows is True @@ -45,7 +45,7 @@ def test_populate_default_popen_kwargs_executable(self): assert kwargs['executable'] == 'foo' -@pytest.mark.requires_macos() +@pytest.mark.requires_macos class TestMacOS: def test_tag(self): assert Platform().macos is True @@ -85,7 +85,7 @@ def test_populate_default_popen_kwargs_executable(self, temp_dir): assert kwargs['executable'] == str(executable) -@pytest.mark.requires_linux() +@pytest.mark.requires_linux class TestLinux: def test_tag(self): assert Platform().linux is True