From 4742fccfa7b94e50637685c9e97ec1d6ed5b6efe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:25:00 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.12 → v0.13.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.12...v0.13.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index caffd53433..3bb7c372f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - id: sort-simple-yaml - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.12.12' + rev: 'v0.13.0' hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] From 950e9c45a51eaa48e27223419f9e9cda6d316ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saugat=20Pachhai=20=28=E0=A4=B8=E0=A5=8C=E0=A4=97=E0=A4=BE?= =?UTF-8?q?=E0=A4=A4=29?= Date: Sat, 20 Sep 2025 10:13:16 +0545 Subject: [PATCH 2/2] fix ruff errors --- dvc/api/dataset.py | 1 + dvc/commands/check_ignore.py | 2 +- dvc/commands/dataset.py | 3 +- dvc/config.py | 1 + dvc/database.py | 2 +- dvc/dvcfile.py | 2 +- dvc/exceptions.py | 2 +- dvc/logger.py | 2 +- dvc/output.py | 5 +- dvc/parsing/context.py | 6 +- dvc/parsing/interpolate.py | 1 + dvc/render/converter/vega.py | 2 +- dvc/repo/experiments/exceptions.py | 2 +- dvc/repo/fetch.py | 1 + dvc/repo/install.py | 2 +- dvc/repo/scm_context.py | 4 +- dvc/scm.py | 2 +- dvc/stage/__init__.py | 2 +- dvc/stage/exceptions.py | 2 +- dvc/ui/__init__.py | 6 +- dvc/updater.py | 2 +- pyproject.toml | 14 ++++- tests/func/api/test_experiments.py | 4 +- tests/func/experiments/test_remove.py | 24 ++++---- tests/func/experiments/test_save.py | 6 +- tests/func/experiments/test_set_params.py | 4 +- tests/func/metrics/test_show.py | 4 +- tests/func/plots/test_show.py | 4 +- tests/func/test_import.py | 2 +- tests/func/test_import_db.py | 2 +- tests/func/test_remove.py | 3 +- tests/func/test_stage.py | 7 ++- tests/func/test_update.py | 4 +- tests/func/utils/test_hydra.py | 5 +- tests/unit/command/test_diff.py | 8 +-- tests/unit/command/test_plots.py | 6 +- tests/unit/repo/test_scm_context.py | 2 +- tests/unit/test_api.py | 10 +++- tests/unit/test_tabular_data.py | 6 +- tests/unit/utils/test_collections.py | 28 +++++----- tests/unit/utils/test_utils.py | 67 ++++++++++++----------- 41 files changed, 149 insertions(+), 113 deletions(-) diff --git a/dvc/api/dataset.py b/dvc/api/dataset.py index 2a2378c289..7fecead960 100644 --- a/dvc/api/dataset.py +++ b/dvc/api/dataset.py @@ -64,3 +64,4 @@ def get(name: str) -> Union[DatachainDataset, DVCDataset, URLDataset]: for file in dataset.lock.files ] return URLDataset(type="url", files=files, path=versioned_path) + raise AssertionError("unreachable") diff --git a/dvc/commands/check_ignore.py b/dvc/commands/check_ignore.py index 3398f8ef1b..fac432e95a 100644 --- a/dvc/commands/check_ignore.py +++ b/dvc/commands/check_ignore.py @@ -16,7 +16,7 @@ def __init__(self, args): def _show_results(self, result: "CheckIgnoreResult"): if not result.match and not self.args.details: - return + return None if self.args.details: pattern_infos = result.pattern_infos diff --git a/dvc/commands/dataset.py b/dvc/commands/dataset.py index 66b8117a94..166b67e74a 100644 --- a/dvc/commands/dataset.py +++ b/dvc/commands/dataset.py @@ -83,7 +83,7 @@ def display(self, name: str, dataset: "Dataset", new: "Dataset"): return CmdDatasetAdd.display(name, new, action) if dataset == new: ui.write("[yellow]Nothing to update[/]", styled=True) - return + return None assert new.lock @@ -112,6 +112,7 @@ def display(self, name: str, dataset: "Dataset", new: "Dataset"): assert new.type == "url" stats = diff_files(dataset.lock.files, new.lock.files) log_changes(stats) + return None def run(self): from difflib import get_close_matches diff --git a/dvc/config.py b/dvc/config.py index 5c8a31ad23..f8a402b73c 100644 --- a/dvc/config.py +++ b/dvc/config.py @@ -139,6 +139,7 @@ def get_dir(cls, level): return global_config_dir() if level == "system": return system_config_dir() + return None @cached_property def files(self) -> dict[str, str]: diff --git a/dvc/database.py b/dvc/database.py index 01e038e4e0..a5eaa9863b 100644 --- a/dvc/database.py +++ b/dvc/database.py @@ -84,7 +84,7 @@ def test_connection(self, onerror: Optional[Callable[[], Any]] = None) -> None: except Exception as exc: if callable(onerror): onerror() - logger.exception( + logger.exception( # noqa: LOG007 "Could not connect to the database. " "Check your database credentials and try again.", exc_info=False, diff --git a/dvc/dvcfile.py b/dvc/dvcfile.py index c84ff85743..1e0ef6367d 100644 --- a/dvc/dvcfile.py +++ b/dvc/dvcfile.py @@ -203,7 +203,7 @@ def dump(self, stage, **kwargs) -> None: def dump_stages(self, stages, **kwargs) -> None: if not stages: - return + return None assert len(stages) == 1, "SingleStageFile can only dump one stage." return self.dump(stages[0], **kwargs) diff --git a/dvc/exceptions.py b/dvc/exceptions.py index ef72049d75..d41ad26efd 100644 --- a/dvc/exceptions.py +++ b/dvc/exceptions.py @@ -9,7 +9,7 @@ from dvc.stage import Stage -class DvcException(Exception): +class DvcException(Exception): # noqa: N818 """Base class for all dvc exceptions.""" def __init__(self, msg, *args): diff --git a/dvc/logger.py b/dvc/logger.py index 00555c4fbb..3d9e7f6e3f 100644 --- a/dvc/logger.py +++ b/dvc/logger.py @@ -55,7 +55,7 @@ def log_to_root(message, *args, **kwargs): setattr(logging, method_name, log_to_root) -class LoggingException(Exception): +class LoggingException(Exception): # noqa: N818 def __init__(self, record): msg = f"failed to log {record!s}" super().__init__(msg) diff --git a/dvc/output.py b/dvc/output.py index 485bfe4787..9164c23bd5 100644 --- a/dvc/output.py +++ b/dvc/output.py @@ -1473,12 +1473,13 @@ def merge_version_meta( ): """Merge version meta for files which are unchanged from other.""" if not self.hash_info: - return + return None if self.hash_info.isdir: return self._merge_dir_version_meta(old_hi, old_obj) if self.hash_info != old_hi: - return + return None self.meta = old_meta + return None def _merge_dir_version_meta( self, old_hi: "HashInfo", old_obj: Optional[Union["HashFile", "Tree"]] diff --git a/dvc/parsing/context.py b/dvc/parsing/context.py index 0a5d096218..4abdc6994e 100644 --- a/dvc/parsing/context.py +++ b/dvc/parsing/context.py @@ -66,7 +66,7 @@ class ParamsLoadError(ContextError): pass -class KeyNotInContext(ContextError, KeyError): +class KeyNotInContext(ContextError, KeyError): # noqa: N818 def __init__(self, key: str) -> None: self.key: str = key super().__init__(f"Could not find '{key}'") @@ -75,7 +75,7 @@ def __str__(self): return self.msg -class VarsAlreadyLoaded(ContextError): +class VarsAlreadyLoaded(ContextError): # noqa: N818 pass @@ -272,7 +272,7 @@ def __setitem__(self, key, value): if not isinstance(key, str): # limitation for the interpolation # ignore other kinds of keys - return + return None return super().__setitem__(key, value) def merge_update(self, other, overwrite=False): diff --git a/dvc/parsing/interpolate.py b/dvc/parsing/interpolate.py index 8948d045a8..6fa284592c 100644 --- a/dvc/parsing/interpolate.py +++ b/dvc/parsing/interpolate.py @@ -202,6 +202,7 @@ def validate_value(value, key): if isinstance(value, dict) and key == "cmd": return True raise ParseError(f"Cannot interpolate data of type '{type(value).__name__}'") + return None def str_interpolate( diff --git a/dvc/render/converter/vega.py b/dvc/render/converter/vega.py index 6a60e8716d..50d6bc8c80 100644 --- a/dvc/render/converter/vega.py +++ b/dvc/render/converter/vega.py @@ -167,7 +167,7 @@ def infer_y_label(properties): if isinstance(y, list): return "y" if not isinstance(y, dict): - return + return None fields = {field for _, field in _file_field(y)} if len(fields) == 1: diff --git a/dvc/repo/experiments/exceptions.py b/dvc/repo/experiments/exceptions.py index 23c83a774e..47a3916115 100644 --- a/dvc/repo/experiments/exceptions.py +++ b/dvc/repo/experiments/exceptions.py @@ -50,7 +50,7 @@ def __init__(self, rev, ref_infos): self.ref_infos = ref_infos -class AmbiguousExpRefInfo(InvalidArgumentError): +class AmbiguousExpRefInfo(InvalidArgumentError): # noqa: N818 def __init__(self, exp_name: str, exp_ref_list: Iterable["ExpRefInfo"]): msg = [ ( diff --git a/dvc/repo/fetch.py b/dvc/repo/fetch.py index 60571bbacd..ee11ec88d8 100644 --- a/dvc/repo/fetch.py +++ b/dvc/repo/fetch.py @@ -20,6 +20,7 @@ def _make_index_onerror(onerror, rev): def _onerror(entry, exc): if onerror: return onerror(rev, entry, exc) + return None return _onerror diff --git a/dvc/repo/install.py b/dvc/repo/install.py index ddf274d3ad..1024ef4b25 100644 --- a/dvc/repo/install.py +++ b/dvc/repo/install.py @@ -75,7 +75,7 @@ def install(self: "Repo", use_pre_commit_tool: bool = False) -> None: scm = self.scm if not isinstance(scm, Git): - return + return None driver = "dvc git-hook merge-driver --ancestor %O --our %A --their %B " scm.install_merge_driver("dvc", "DVC merge driver", driver) diff --git a/dvc/repo/scm_context.py b/dvc/repo/scm_context.py index c3979cec49..e822a1ad94 100644 --- a/dvc/repo/scm_context.py +++ b/dvc/repo/scm_context.py @@ -58,7 +58,7 @@ def add(self, paths: Union[str, Iterable[str]]) -> None: def track_changed_files(self) -> None: """Stage files that have changed.""" if not self.files_to_track: - return + return None logger.debug("Staging files: %s", self.files_to_track) return self.add(self.files_to_track) @@ -76,6 +76,7 @@ def ignore(self, path: str) -> None: logger.debug("Added '%s' to gitignore file.", path) self.track_file(relpath(gitignore_file)) return self.ignored_paths.append(path) + return None def ignore_remove(self, path: str) -> None: from scmrepo.exceptions import FileNotInRepoError @@ -90,6 +91,7 @@ def ignore_remove(self, path: str) -> None: if gitignore_file: return self.track_file(relpath(gitignore_file)) + return None @contextmanager def __call__( diff --git a/dvc/scm.py b/dvc/scm.py index c1d747670e..c451a501eb 100644 --- a/dvc/scm.py +++ b/dvc/scm.py @@ -45,7 +45,7 @@ def __init__(self): super().__init__(msg) -class InvalidRemoteSCMRepo(SCMError): +class InvalidRemoteSCMRepo(SCMError): # noqa: N818 pass diff --git a/dvc/stage/__init__.py b/dvc/stage/__init__.py index fa1270d6ca..c534961458 100644 --- a/dvc/stage/__init__.py +++ b/dvc/stage/__init__.py @@ -121,7 +121,7 @@ def restore_fields(stage: "Stage") -> None: old_outs = {out.def_path: out for out in old.outs} for out in stage.outs: - old_out = old_outs.get(out.def_path, None) + old_out = old_outs.get(out.def_path) if old_out is not None: out.restore_fields(old_out) diff --git a/dvc/stage/exceptions.py b/dvc/stage/exceptions.py index 1921440c1a..76bfabb6cc 100644 --- a/dvc/stage/exceptions.py +++ b/dvc/stage/exceptions.py @@ -82,7 +82,7 @@ def __init__(self, path: str): super().__init__(f"data source changed: {path}") -class StageNotFound(DvcException, KeyError): +class StageNotFound(DvcException, KeyError): # noqa: N818 def __init__(self, file, name): self.file = file.relpath self.name = name diff --git a/dvc/ui/__init__.py b/dvc/ui/__init__.py index 94d4dc4aa4..260e945fc3 100644 --- a/dvc/ui/__init__.py +++ b/dvc/ui/__init__.py @@ -193,7 +193,7 @@ def write( sep = " " if sep is None else sep end = "\n" if end is None else end if not self._enabled and not force: - return + return None file = file or (sys.stderr if stderr else sys.stdout) with Tqdm.external_write_mode(file=file): @@ -305,7 +305,7 @@ def table( from dvc.ui import table as t if not data and not markdown: - return + return None if not markdown and rich_table: if force or self._enabled: @@ -319,7 +319,7 @@ def table( borders=borders, ) - return + return None return t.plain_table( self, diff --git a/dvc/updater.py b/dvc/updater.py index ffe3a6b2ff..517768d3a9 100644 --- a/dvc/updater.py +++ b/dvc/updater.py @@ -120,7 +120,7 @@ def _notify(self, latest: str, pkg: Optional[str] = PKG) -> None: from dvc.ui import ui if not ui.isatty(): - return + return None message = self._get_message(latest, pkg=pkg) return ui.error_write(message, styled=True) diff --git a/pyproject.toml b/pyproject.toml index c7c6c2ed46..6c904c7270 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -265,8 +265,16 @@ show-fixes = true [tool.ruff.lint] ignore = [ - "N818", "S101", "A005", "PT007", "RET502", "RET503", "SIM105", "SIM108", "SIM117", - "TRY003", "TRY300", "PERF203", "PLC0415", "PLR2004", "PLW2901", "LOG007", + "PERF203", # try-except-in-loop + "PLC0415", # import-outside-top-level + "PLR2004", # magic-value-comparison + "PLW2901", # redefined-loop-name + "S101", # assert + "SIM105", # suppressible-exception + "SIM108", # if-else-block-instead-of-if-exp + "SIM117", # multiple-with-statements + "TRY003", # raise-vanilla-args + "TRY300", # try-consider-else ] select = [ "F", "E", "W", "C90", "I", "N", "UP", "YTT", "ASYNC", "S", "BLE", "B", "A", "C4", "DTZ", "T10", @@ -304,4 +312,4 @@ max-args = 10 "dvc/commands/**" = ["N806"] "dvc/testing/**" = ["ARG002"] "dvc/testing/benchmarks/**" = ["ARG001"] -"tests/**" = ["S", "ARG001", "ARG002", "TRY002", "TRY301", "PERF"] +"tests/**" = ["S", "ARG001", "ARG002", "TRY002", "TRY301", "PERF", "PLR2004"] diff --git a/tests/func/api/test_experiments.py b/tests/func/api/test_experiments.py index 924996f889..56894cca54 100644 --- a/tests/func/api/test_experiments.py +++ b/tests/func/api/test_experiments.py @@ -1,3 +1,5 @@ +import re + import pytest from dvc import api @@ -13,7 +15,7 @@ def test_exp_save(tmp_dir, dvc, scm): api.exp_save("foo") with pytest.raises( ExperimentExistsError, - match="Experiment conflicts with existing experiment 'foo'.", + match=re.escape("Experiment conflicts with existing experiment 'foo'."), ): api.exp_save("foo") api.exp_save("foo", force=True) diff --git a/tests/func/experiments/test_remove.py b/tests/func/experiments/test_remove.py index a172eccfd2..d86ef2ca74 100644 --- a/tests/func/experiments/test_remove.py +++ b/tests/func/experiments/test_remove.py @@ -204,10 +204,10 @@ def test_remove_multi_rev(tmp_dir, scm, dvc, exp_stage): @pytest.mark.parametrize( "keep, expected_removed", [ - [["exp1"], ["exp2", "exp3"]], - [["exp1", "exp2"], ["exp3"]], - [["exp1", "exp2", "exp3"], []], - [[], []], # remove does nothing if no experiments are specified + (["exp1"], ["exp2", "exp3"]), + (["exp1", "exp2"], ["exp3"]), + (["exp1", "exp2", "exp3"], []), + ([], []), # remove does nothing if no experiments are specified ], ) def test_keep_selected_by_name(tmp_dir, scm, dvc, exp_stage, keep, expected_removed): @@ -239,14 +239,14 @@ def test_keep_selected_by_nonexistent_name(tmp_dir, scm, dvc, exp_stage): @pytest.mark.parametrize( "num_exps, rev, num, expected_removed", [ - [2, "exp1", 1, ["exp2"]], - [3, "exp3", 1, ["exp1", "exp2"]], - [3, "exp3", 2, ["exp1"]], - [3, "exp3", 3, []], - [3, "exp2", 2, ["exp3"]], - [4, "exp2", 2, ["exp3", "exp4"]], - [4, "exp4", 2, ["exp1", "exp2"]], - [1, None, 1, []], # remove does nothing if no experiments are specified + (2, "exp1", 1, ["exp2"]), + (3, "exp3", 1, ["exp1", "exp2"]), + (3, "exp3", 2, ["exp1"]), + (3, "exp3", 3, []), + (3, "exp2", 2, ["exp3"]), + (4, "exp2", 2, ["exp3", "exp4"]), + (4, "exp4", 2, ["exp1", "exp2"]), + (1, None, 1, []), # remove does nothing if no experiments are specified ], ) def test_keep_selected_by_rev( diff --git a/tests/func/experiments/test_save.py b/tests/func/experiments/test_save.py index 9fe7715c00..f7af217152 100644 --- a/tests/func/experiments/test_save.py +++ b/tests/func/experiments/test_save.py @@ -18,7 +18,7 @@ def test_exp_save_unchanged(tmp_dir, dvc, scm): dvc.experiments.save() -@pytest.mark.parametrize("name", (None, "test")) +@pytest.mark.parametrize("name", [None, "test"]) def test_exp_save(tmp_dir, dvc, scm, name): setup_stage(tmp_dir, dvc, scm) baseline = scm.get_rev() @@ -47,13 +47,13 @@ def test_exp_save_overwrite_experiment(tmp_dir, dvc, scm): @pytest.mark.parametrize( "name", - ( + [ "invalid/name", "invalid..name", "invalid~name", "invalid?name", "invalidname.", - ), + ], ) def test_exp_save_invalid_name(tmp_dir, dvc, scm, name): setup_stage(tmp_dir, dvc, scm) diff --git a/tests/func/experiments/test_set_params.py b/tests/func/experiments/test_set_params.py index a479b2ce59..b3bfa204a0 100644 --- a/tests/func/experiments/test_set_params.py +++ b/tests/func/experiments/test_set_params.py @@ -7,8 +7,8 @@ @pytest.mark.parametrize( "changes, expected", [ - [["foo=baz"], "foo: baz\ngoo:\n bag: 3.0\nlorem: false"], - [["params.yaml:foo=baz"], "foo: baz\ngoo:\n bag: 3.0\nlorem: false"], + (["foo=baz"], "foo: baz\ngoo:\n bag: 3.0\nlorem: false"), + (["params.yaml:foo=baz"], "foo: baz\ngoo:\n bag: 3.0\nlorem: false"), ], ) def test_modify_params(params_repo, dvc, changes, expected): diff --git a/tests/func/metrics/test_show.py b/tests/func/metrics/test_show.py index 6962a8db94..5ae6cc316e 100644 --- a/tests/func/metrics/test_show.py +++ b/tests/func/metrics/test_show.py @@ -289,14 +289,14 @@ def test_metrics_show_overlap(tmp_dir, dvc, run_copy_metrics, clear_before_run): @pytest.mark.parametrize( "file,error_path,err_type", - ( + [ (PROJECT_FILE, ["workspace", "error", "type"], "YAMLSyntaxError"), ( "metrics.yaml", ["workspace", "data", "metrics.yaml", "error", "type"], "YAMLFileCorruptedError", ), - ), + ], ) def test_log_errors( tmp_dir, scm, dvc, capsys, run_copy_metrics, file, error_path, err_type diff --git a/tests/func/plots/test_show.py b/tests/func/plots/test_show.py index 2bacbb4e8b..9982ff771c 100644 --- a/tests/func/plots/test_show.py +++ b/tests/func/plots/test_show.py @@ -300,13 +300,13 @@ def test_ignore_parsing_error(tmp_dir, dvc, run_copy_metrics): @pytest.mark.parametrize( "file,path_kwargs", - ( + [ (PROJECT_FILE, {"revision": "workspace", "endkey": "error"}), ( "plot.yaml", {"revision": "workspace", "file": "plot.yaml", "endkey": "error"}, ), - ), + ], ) def test_log_errors(tmp_dir, scm, dvc, run_copy_metrics, file, path_kwargs, capsys): metric = [{"val": 2}, {"val": 3}] diff --git a/tests/func/test_import.py b/tests/func/test_import.py index 68b97d64ca..754cd4d9e8 100644 --- a/tests/func/test_import.py +++ b/tests/func/test_import.py @@ -628,7 +628,7 @@ def test_chained_import(tmp_dir, dvc, make_tmp_dir, erepo_dir, local_cloud): assert (dst / "bar").read_text() == "bar" -@pytest.mark.parametrize("paths", ([], ["dir"])) +@pytest.mark.parametrize("paths", [[], ["dir"]]) def test_parameterized_repo(tmp_dir, dvc, scm, erepo_dir, paths): path = erepo_dir.joinpath(*paths) path.mkdir(parents=True, exist_ok=True) diff --git a/tests/func/test_import_db.py b/tests/func/test_import_db.py index 51b7584cf5..f4ddae5300 100644 --- a/tests/func/test_import_db.py +++ b/tests/func/test_import_db.py @@ -38,7 +38,7 @@ def load_data(file, output_format): return pd.read_csv(file) -@pytest.mark.parametrize("output_format", ("csv", "json")) +@pytest.mark.parametrize("output_format", ["csv", "json"]) @pytest.mark.parametrize( "args,file_name", [ diff --git a/tests/func/test_remove.py b/tests/func/test_remove.py index 9dd6ce85ba..deb8a020d3 100644 --- a/tests/func/test_remove.py +++ b/tests/func/test_remove.py @@ -1,4 +1,5 @@ import os +import re import pytest @@ -37,7 +38,7 @@ def test_remove_file_target(tmp_dir, dvc): with pytest.raises( StageFileIsNotDvcFileError, - match="'foo' is not a .dvc file. Do you mean 'foo.dvc'?", + match=re.escape("'foo' is not a .dvc file. Do you mean 'foo.dvc'?"), ): dvc.remove("foo") diff --git a/tests/func/test_stage.py b/tests/func/test_stage.py index c8beb7bafd..e36152789c 100644 --- a/tests/func/test_stage.py +++ b/tests/func/test_stage.py @@ -1,4 +1,5 @@ import os +import re import pytest @@ -228,7 +229,7 @@ def test_parent_repo_collect_stages(tmp_dir, scm, dvc): assert deep_subrepo_stages != [] -@pytest.mark.parametrize("with_deps", (False, True)) +@pytest.mark.parametrize("with_deps", [False, True]) def test_collect_symlink(tmp_dir, dvc, with_deps): from dvc.exceptions import StageNotFoundError @@ -339,6 +340,8 @@ def test_stage_add_duplicated_output(tmp_dir, dvc): with pytest.raises( OutputDuplicationError, - match="Use `dvc remove foo.dvc` to stop tracking the overlapping output.", + match=re.escape( + "Use `dvc remove foo.dvc` to stop tracking the overlapping output." + ), ): dvc.stage.add(name="duplicated", cmd="echo bar > foo", outs=["foo"]) diff --git a/tests/func/test_update.py b/tests/func/test_update.py index 4e6d668777..7de4e641a3 100644 --- a/tests/func/test_update.py +++ b/tests/func/test_update.py @@ -156,7 +156,7 @@ def test_update_unchanged(tmp_dir, dvc, erepo_dir, mocker): assert not spy.called -@pytest.mark.parametrize("outs_exist", (False, True)) +@pytest.mark.parametrize("outs_exist", [False, True]) def test_update_no_download(tmp_dir, dvc, erepo_dir, outs_exist, mocker): with erepo_dir.chdir(): erepo_dir.dvc_gen("file", "file content", commit="add file") @@ -204,7 +204,7 @@ def test_update_import_url(tmp_dir, dvc, workspace): assert dst.read_text() == "updated file content" -@pytest.mark.parametrize("outs_exist", (False, True)) +@pytest.mark.parametrize("outs_exist", [False, True]) def test_update_import_url_no_download(tmp_dir, dvc, workspace, outs_exist, mocker): workspace.gen("file", "file content") diff --git a/tests/func/utils/test_hydra.py b/tests/func/utils/test_hydra.py index b2c947c25b..cec87d5da6 100644 --- a/tests/func/utils/test_hydra.py +++ b/tests/func/utils/test_hydra.py @@ -1,3 +1,4 @@ +import re from contextlib import nullcontext as does_not_raise import pytest @@ -214,7 +215,9 @@ def hydra_setup_dir_basic(tmp_dir, config_subdir, config_name, config_content): None, pytest.raises( ValueError, - match="Either `config_dir` or `config_module` should be provided.", + match=re.escape( + "Either `config_dir` or `config_module` should be provided." + ), ), ), ], diff --git a/tests/unit/command/test_diff.py b/tests/unit/command/test_diff.py index a18b414c51..2967481773 100644 --- a/tests/unit/command/test_diff.py +++ b/tests/unit/command/test_diff.py @@ -206,20 +206,20 @@ def test_diff_show_markdown_and_hash(mocker, show_hash, dvc): @pytest.mark.parametrize( "opts", - ( + [ [], ["a_rev", "b_rev"], ["--targets", "."], ["--hide-missing"], - ), + ], ) @pytest.mark.parametrize( "show, expected", - ( + [ ([], ""), (["--json"], "{}"), (["--md"], "| Status | Path |\n|----------|--------|"), - ), + ], ) def test_no_changes(mocker, capsys, opts, show, expected, dvc): args = parse_args(["diff", *opts, *show]) diff --git a/tests/unit/command/test_plots.py b/tests/unit/command/test_plots.py index 37bc82e79c..b6ce3e1f54 100644 --- a/tests/unit/command/test_plots.py +++ b/tests/unit/command/test_plots.py @@ -260,7 +260,7 @@ def test_should_pass_template_dir(tmp_dir, dvc, mocker, capsys): ) -@pytest.mark.parametrize("output", ("some_out", os.path.join("to", "subdir"), None)) +@pytest.mark.parametrize("output", ["some_out", os.path.join("to", "subdir"), None]) def test_should_call_render(tmp_dir, mocker, capsys, plots_data, output): cli_args = parse_args(["plots", "diff", "--targets", "plots.csv", "--out", output]) cmd = cli_args.func(cli_args) @@ -322,7 +322,7 @@ def test_plots_diff_json(dvc, mocker, capsys): @pytest.mark.parametrize( "target,expected_out,expected_rtn", - (("t1", "\"{'t1'}\"", 0), (None, "t1\nt2", 0), ("t3", "", 1)), + [("t1", "\"{'t1'}\"", 0), (None, "t1\nt2", 0), ("t3", "", 1)], ) def test_plots_templates(dvc, mocker, capsys, target, expected_out, expected_rtn): t1 = mocker.Mock() @@ -352,7 +352,7 @@ def test_plots_templates(dvc, mocker, capsys, target, expected_out, expected_rtn assert rtn == expected_rtn -@pytest.mark.parametrize("split", (True, False)) +@pytest.mark.parametrize("split", [True, False]) def test_show_json(split, mocker, capsys): import dvc.commands.plots diff --git a/tests/unit/repo/test_scm_context.py b/tests/unit/repo/test_scm_context.py index d9ff258e35..f23bcd1941 100644 --- a/tests/unit/repo/test_scm_context.py +++ b/tests/unit/repo/test_scm_context.py @@ -77,7 +77,7 @@ def test_scm_context_autostage_changed_files(scm_context): def test_scm_context_clears_ignores_on_error(scm_context): - class CustomException(Exception): + class CustomException(Exception): # noqa: N818 pass with pytest.raises(CustomException), scm_context(): # noqa: PT012 diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 28cd8ecf54..f5a8e7344e 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,3 +1,5 @@ +import re + import pytest from dvc import api @@ -7,13 +9,17 @@ def test_open_raises_error_if_no_context(tmp_dir, dvc): tmp_dir.dvc_gen("foo", "foo-text") fd = api.open("foo") - with pytest.raises(AttributeError, match="should be used in a with statement."): + with pytest.raises( + AttributeError, match=re.escape("should be used in a with statement.") + ): fd.read() def test_open_rev_raises_error_on_wrong_mode(tmp_dir, dvc): tmp_dir.dvc_gen("foo", "foo-text") - with pytest.raises(ValueError, match="Only reading `mode` is supported."): + with pytest.raises( + ValueError, match=re.escape("Only reading `mode` is supported.") + ): with api.open("foo", mode="w"): pass diff --git a/tests/unit/test_tabular_data.py b/tests/unit/test_tabular_data.py index e3a2a3e2f2..5587f0990f 100644 --- a/tests/unit/test_tabular_data.py +++ b/tests/unit/test_tabular_data.py @@ -1,3 +1,5 @@ +import re + import pytest from dvc.compare import TabularData @@ -365,12 +367,12 @@ def test_drop_duplicates_subset(axis, subset, expected): def test_dropna_invalid_axis(): td = TabularData(["col-1", "col-2", "col-3"]) - with pytest.raises(ValueError, match="Invalid 'axis' value foo."): + with pytest.raises(ValueError, match=re.escape("Invalid 'axis' value foo.")): td.dropna("foo") def test_drop_duplicates_invalid_axis(): td = TabularData(["col-1", "col-2", "col-3"]) - with pytest.raises(ValueError, match="Invalid 'axis' value foo."): + with pytest.raises(ValueError, match=re.escape("Invalid 'axis' value foo.")): td.drop_duplicates("foo") diff --git a/tests/unit/utils/test_collections.py b/tests/unit/utils/test_collections.py index 6f26abce91..8d342d35b3 100644 --- a/tests/unit/utils/test_collections.py +++ b/tests/unit/utils/test_collections.py @@ -86,32 +86,32 @@ class CustomList(list): @pytest.mark.parametrize( "changes, expected", [ - [{"foo": "baz"}, {"foo": "baz", "goo": {"bag": 3}, "lorem": False}], - [ + ({"foo": "baz"}, {"foo": "baz", "goo": {"bag": 3}, "lorem": False}), + ( {"foo": "baz", "goo": "bar"}, {"foo": "baz", "goo": "bar", "lorem": False}, - ], - [ + ), + ( {"goo": {"bag": 4}}, {"foo": {"bar": 1, "baz": 2}, "goo": {"bag": 4}, "lorem": False}, - ], - [ + ), + ( {"foo": {"bar": 1, "baz": 2, 0: "bar"}}, { "foo": {"bar": 1, "baz": 2, 0: "bar"}, "goo": {"bag": 3}, "lorem": False, }, - ], - [ + ), + ( {"lorem": {"ipsum": 3}}, { "foo": {"bar": 1, "baz": 2}, "goo": {"bag": 3}, "lorem": {"ipsum": 3}, }, - ], - [{}, {"foo": {"bar": 1, "baz": 2}, "goo": {"bag": 3}, "lorem": False}], + ), + ({}, {"foo": {"bar": 1, "baz": 2}, "goo": {"bag": 3}, "lorem": False}), ], ) def test_merge_dicts(changes, expected): @@ -125,12 +125,12 @@ def test_merge_dicts(changes, expected): @pytest.mark.parametrize( "changes, expected", [ - [{"foo": "baz"}, {"foo": {"baz": 2}}], - [ + ({"foo": "baz"}, {"foo": {"baz": 2}}), + ( {"foo": "baz", "goo": "bag"}, {"foo": {"baz": 2}, "goo": {"bag": 3}}, - ], - [{}, {}], + ), + ({}, {}), ], ) def test_remove_missing_keys(changes, expected): diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index a29e49a2d0..f2a1346ca8 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -1,4 +1,5 @@ import os +import re import pytest @@ -52,19 +53,19 @@ def test_relpath_windows(): @pytest.mark.parametrize( "inp,out,is_dir,expected", [ - ["target", None, False, "target"], - ["target", "dir", True, os.path.join("dir", "target")], - ["target", "file_target", False, "file_target"], - [ + ("target", None, False, "target"), + ("target", "dir", True, os.path.join("dir", "target")), + ("target", "file_target", False, "file_target"), + ( "target", os.path.join("dir", "subdir"), True, os.path.join("dir", "subdir", "target"), - ], - ["dir/", None, False, "dir"], - ["dir", None, False, "dir"], - ["dir", "other_dir", False, "other_dir"], - ["dir", "other_dir", True, os.path.join("other_dir", "dir")], + ), + ("dir/", None, False, "dir"), + ("dir", None, False, "dir"), + ("dir", "other_dir", False, "other_dir"), + ("dir", "other_dir", True, os.path.join("other_dir", "dir")), ], ) def test_resolve_output(inp, out, is_dir, expected, mocker): @@ -76,40 +77,40 @@ def test_resolve_output(inp, out, is_dir, expected, mocker): @pytest.mark.parametrize( "inp,out, default", [ - ["dvc.yaml", ("dvc.yaml", None), None], - ["dvc.yaml:name", ("dvc.yaml", "name"), None], - [":name", ("dvc.yaml", "name"), None], - ["stage.dvc", ("stage.dvc", None), None], - ["../models/stage.dvc", ("../models/stage.dvc", None), "def"], - [":name", ("default", "name"), "default"], - ["something.dvc:name", ("something.dvc", "name"), None], - ["../something.dvc:name", ("../something.dvc", "name"), None], - ["file", (None, "file"), None], - ["build@15", (None, "build@15"), None], - ["build@{'level': 35}", (None, "build@{'level': 35}"), None], - [":build@15", ("dvc.yaml", "build@15"), None], - [":build@{'level': 35}", ("dvc.yaml", "build@{'level': 35}"), None], - ["dvc.yaml:build@15", ("dvc.yaml", "build@15"), None], - [ + ("dvc.yaml", ("dvc.yaml", None), None), + ("dvc.yaml:name", ("dvc.yaml", "name"), None), + (":name", ("dvc.yaml", "name"), None), + ("stage.dvc", ("stage.dvc", None), None), + ("../models/stage.dvc", ("../models/stage.dvc", None), "def"), + (":name", ("default", "name"), "default"), + ("something.dvc:name", ("something.dvc", "name"), None), + ("../something.dvc:name", ("../something.dvc", "name"), None), + ("file", (None, "file"), None), + ("build@15", (None, "build@15"), None), + ("build@{'level': 35}", (None, "build@{'level': 35}"), None), + (":build@15", ("dvc.yaml", "build@15"), None), + (":build@{'level': 35}", ("dvc.yaml", "build@{'level': 35}"), None), + ("dvc.yaml:build@15", ("dvc.yaml", "build@15"), None), + ( "dvc.yaml:build@{'level': 35}", ("dvc.yaml", "build@{'level': 35}"), None, - ], - [ + ), + ( "build2@{'level': [1, 2, 3]}", (None, "build2@{'level': [1, 2, 3]}"), None, - ], - [ + ), + ( ":build2@{'level': [1, 2, 3]}", ("dvc.yaml", "build2@{'level': [1, 2, 3]}"), None, - ], - [ + ), + ( "dvc.yaml:build2@{'level': [1, 2, 3]}", ("dvc.yaml", "build2@{'level': [1, 2, 3]}"), None, - ], + ), ], ) def test_parse_target(inp, out, default): @@ -117,7 +118,9 @@ def test_parse_target(inp, out, default): def test_hint_on_lockfile(): - with pytest.raises(Exception, match="Did you mean: `dvc.yaml:name`?") as e: + with pytest.raises( + Exception, match=re.escape("Did you mean: `dvc.yaml:name`?") + ) as e: assert parse_target("dvc.lock:name") assert "dvc.yaml:name" in str(e.value)