diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68b2c953..fd3a9508 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: args: ["--drop-empty-cells", "--extra-keys 'metadata.language_info.version cell.metadata.jp-MarkdownHeadingCollapsed cell.metadata.pycharm'"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.0 + rev: v0.4.2 hooks: - id: ruff-format types_or: [ python, pyi ] diff --git a/docs/user-guide/testing.ipynb b/docs/user-guide/testing.ipynb index 4547bf1b..278d019b 100644 --- a/docs/user-guide/testing.ipynb +++ b/docs/user-guide/testing.ipynb @@ -482,7 +482,7 @@ " if real_client is None:\n", " pytest.skip(\"Backend tests disabled\")\n", " # or do something else\n", - " \n", + "\n", " # do the actual tests" ] }, diff --git a/pyproject.toml b/pyproject.toml index c631e1b9..edca6d62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,14 +94,14 @@ extend-include = ["*.ipynb"] extend-exclude = [".*", "__pycache__", "build", "dist", "venv"] [tool.ruff.lint] -select = ["B", "D", "E", "F", "G", "I", "S", "T20", "UP", "PGH", "FBT003", "RUF"] +select = ["B", "C4", "D", "DTZ", "E", "F", "G", "I", "FBT003", "PERF", "PGH", "PT", "PYI", "RUF", "S", "T20", "W"] ignore = [ - "B905", # `zip()` without an explicit `strict=` parameter - "S324", # insecure hsh function; we don't use hashing for security - "E741", "E742", "E743", # do not use names ‘l’, ‘O’, or ‘I’; they are not a problem with a proper font + "D105", # most magic methods don't need docstrings as their purpose is always the same + "E741", "E742", "E743", # do not use names ‘l’, ‘O’, or ‘I’; they are not a problem with a proper font "UP038", # does not seem to work and leads to slower code - "E111", "E114", "E117", "D206", "D300", # conflict with ruff format - "D105", + # Conflict with ruff format, see + # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "COM812", "COM819", "D206", "D300", "E111", "E114", "E117", "ISC001", "ISC002", "Q000", "Q001", "Q002", "Q003", "W191", ] fixable = ["I001"] isort.known-first-party = ["scitacean"] @@ -111,6 +111,7 @@ pydocstyle.convention = "numpy" "tests/*" = [ "S101", # asserts are fine in tests "D10", # no docstrings required in tests + "S324", # insecure hsh function; we don't use hashing for security ] "docs/*" = [ "D", "E402", "F811", "F841", "RUF015", "S101", "T201", diff --git a/src/scitacean/dataset.py b/src/scitacean/dataset.py index a63c7773..0862d8ad 100644 --- a/src/scitacean/dataset.py +++ b/src/scitacean/dataset.py @@ -432,7 +432,7 @@ def _get_or_add_orig_datablock(self, key: int | str | PID) -> OrigDatablock: def make_upload_model(self) -> UploadDerivedDataset | UploadRawDataset: """Construct a SciCat upload model from self.""" - model: type[UploadRawDataset] | type[UploadDerivedDataset] = ( + model: type[UploadRawDataset | UploadDerivedDataset] = ( UploadRawDataset if self.type == DatasetType.RAW else UploadDerivedDataset ) # Datablocks are not included here because they are handled separately @@ -517,8 +517,8 @@ def keys(self) -> Iterable[str]: """ from itertools import chain - all_fields = set(field.name for field in self.fields()) - my_fields = set(field.name for field in self.fields(dataset_type=self.type)) + all_fields = {field.name for field in self.fields()} + my_fields = {field.name for field in self.fields(dataset_type=self.type)} other_fields = all_fields - my_fields invalid_fields = ( f_name for f_name in other_fields if getattr(self, f_name) is not None diff --git a/src/scitacean/file.py b/src/scitacean/file.py index 952075a2..946b9220 100644 --- a/src/scitacean/file.py +++ b/src/scitacean/file.py @@ -379,13 +379,13 @@ def uploaded( """ if remote_creation_time is None: remote_creation_time = datetime.now().astimezone(timezone.utc) - args = dict( - remote_path=RemotePath(remote_path) if remote_path is not None else None, - remote_gid=remote_gid, - remote_uid=remote_uid, - remote_perm=remote_perm, - _remote_creation_time=remote_creation_time, - ) + args = { + "remote_path": RemotePath(remote_path) if remote_path is not None else None, + "remote_gid": remote_gid, + "remote_uid": remote_uid, + "remote_perm": remote_perm, + "_remote_creation_time": remote_creation_time, + } return dataclasses.replace( self, _remote_size=remote_size if remote_size is not None else self.size, diff --git a/src/scitacean/testing/backend/fixtures.py b/src/scitacean/testing/backend/fixtures.py index 631c30c5..79875ec9 100644 --- a/src/scitacean/testing/backend/fixtures.py +++ b/src/scitacean/testing/backend/fixtures.py @@ -111,7 +111,7 @@ def client(request, scicat_backend) -> Client | FakeClient: @pytest.fixture() -def require_scicat_backend(request, scicat_backend) -> None: +def require_scicat_backend(request, scicat_backend) -> None: # noqa: PT004 """Fixture to declare that a test needs a local scicat backend. Like :func:`scitacean.testing.backend.scicat_backend` diff --git a/src/scitacean/testing/sftp/_sftp.py b/src/scitacean/testing/sftp/_sftp.py index ee128390..7336f510 100644 --- a/src/scitacean/testing/sftp/_sftp.py +++ b/src/scitacean/testing/sftp/_sftp.py @@ -61,7 +61,7 @@ def _seed_files() -> Iterable[tuple[str, str]]: def local_access() -> SFTPAccess: config = _docker_compose_file() service = config["services"]["scitacean-test-sftp-server"] - env = {k: v for k, v in map(lambda s: s.split("="), service["environment"])} + env = dict(map(lambda s: s.split("="), service["environment"])) return SFTPAccess( host="localhost", port=service["ports"][0].split(":")[0], diff --git a/src/scitacean/testing/sftp/fixtures.py b/src/scitacean/testing/sftp/fixtures.py index a9796d1f..6f865f61 100644 --- a/src/scitacean/testing/sftp/fixtures.py +++ b/src/scitacean/testing/sftp/fixtures.py @@ -76,7 +76,7 @@ def sftp_data_dir(sftp_base_dir: Path | None) -> Path | None: @pytest.fixture() -def require_sftp_fileserver(request, sftp_fileserver) -> None: +def require_sftp_fileserver(request, sftp_fileserver) -> None: # noqa: PT004 """Fixture to declare that a test needs a local SFTP server. Like :func:`scitacean.testing.sftp.sftp_fileserver` diff --git a/src/scitacean/testing/transfer.py b/src/scitacean/testing/transfer.py index b319c650..183044d3 100644 --- a/src/scitacean/testing/transfer.py +++ b/src/scitacean/testing/transfer.py @@ -37,7 +37,7 @@ def download_file(self, *, remote: RemotePath, local: Path) -> None: def download_files(self, *, remote: list[RemotePath], local: list[Path]) -> None: """Download multiple files.""" - for r, l in zip(remote, local): + for r, l in zip(remote, local, strict=True): self.download_file(remote=r, local=l) diff --git a/src/scitacean/transfer/link.py b/src/scitacean/transfer/link.py index 5fb461b7..fe2bee49 100644 --- a/src/scitacean/transfer/link.py +++ b/src/scitacean/transfer/link.py @@ -22,7 +22,7 @@ class LinkDownloadConnection: def download_files(self, *, remote: list[RemotePath], local: list[Path]) -> None: """Download files from the given remote path.""" - for r, l in zip(remote, local): + for r, l in zip(remote, local, strict=True): self.download_file(remote=r, local=l) def download_file(self, *, remote: RemotePath, local: Path) -> None: diff --git a/src/scitacean/transfer/sftp.py b/src/scitacean/transfer/sftp.py index f4935e85..2ba784d2 100644 --- a/src/scitacean/transfer/sftp.py +++ b/src/scitacean/transfer/sftp.py @@ -31,7 +31,7 @@ def __init__(self, *, sftp_client: SFTPClient, host: str) -> None: def download_files(self, *, remote: list[RemotePath], local: list[Path]) -> None: """Download files from the given remote path.""" - for r, l in zip(remote, local): + for r, l in zip(remote, local, strict=True): self.download_file(remote=r, local=l) def download_file(self, *, remote: RemotePath, local: Path) -> None: diff --git a/tests/client/attachment_client_test.py b/tests/client/attachment_client_test.py index b91d3102..3c52b747 100644 --- a/tests/client/attachment_client_test.py +++ b/tests/client/attachment_client_test.py @@ -136,14 +136,14 @@ def test_get_attachments_for_dataset_no_attachments(scicat_client): assert attachments == [] -@pytest.mark.parametrize("key", ("raw", "derived")) +@pytest.mark.parametrize("key", ["raw", "derived"]) def test_get_dataset_does_not_initialise_attachments(client, key): dset = INITIAL_DATASETS["derived"] downloaded = client.get_dataset(dset.pid) assert downloaded.attachments is None -@pytest.mark.parametrize("key", ("raw", "derived")) +@pytest.mark.parametrize("key", ["raw", "derived"]) def test_download_attachments_for_dataset(client, key): dset = INITIAL_DATASETS[key] downloaded = client.get_dataset(dset.pid) @@ -155,7 +155,7 @@ def test_download_attachments_for_dataset(client, key): assert with_attachments.attachments == expected -@pytest.mark.parametrize("key", ("raw", "derived")) +@pytest.mark.parametrize("key", ["raw", "derived"]) def test_get_dataset_with_attachments(client, key): dset = INITIAL_DATASETS[key] downloaded = client.get_dataset(dset.pid, attachments=True) diff --git a/tests/client/client_test.py b/tests/client/client_test.py index 7e78c801..d11acfd2 100644 --- a/tests/client/client_test.py +++ b/tests/client/client_test.py @@ -66,9 +66,9 @@ def test_connection_error_does_not_contain_token(): ) try: client.get_dataset("does not exist") - assert False, "There must be an exception" # noqa: B011 + pytest.fail("There must be an exception") except Exception as exc: - assert "the token/which_must-be.kept secret" not in str(exc) + assert "the token/which_must-be.kept secret" not in str(exc) # noqa: PT017 for arg in exc.args: assert "the token/which_must-be.kept secret" not in str(arg) diff --git a/tests/client/datablock_client_test.py b/tests/client/datablock_client_test.py index 74946947..0c53b989 100644 --- a/tests/client/datablock_client_test.py +++ b/tests/client/datablock_client_test.py @@ -55,7 +55,7 @@ def orig_datablock(scicat_access): ) -@pytest.mark.parametrize("key", ("raw", "derived")) +@pytest.mark.parametrize("key", ["raw", "derived"]) def test_get_orig_datablock(scicat_client, key): dblock = INITIAL_ORIG_DATABLOCKS[key][0] downloaded = scicat_client.get_orig_datablocks(dblock.datasetId) diff --git a/tests/client/dataset_client_test.py b/tests/client/dataset_client_test.py index 5460ef5b..0380619b 100644 --- a/tests/client/dataset_client_test.py +++ b/tests/client/dataset_client_test.py @@ -40,7 +40,7 @@ def derived_dataset(scicat_access): ) -@pytest.mark.parametrize("key", ("raw", "derived")) +@pytest.mark.parametrize("key", ["raw", "derived"]) def test_get_dataset_model(scicat_client, key): dset = INITIAL_DATASETS[key] downloaded = scicat_client.get_dataset_model(dset.pid) @@ -68,7 +68,7 @@ def test_create_dataset_model(scicat_client, derived_dataset): def test_validate_dataset_model(real_client, require_scicat_backend, derived_dataset): real_client.scicat.validate_dataset_model(derived_dataset) derived_dataset.contactEmail = "NotAnEmail" - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="validation in SciCat"): real_client.scicat.validate_dataset_model(derived_dataset) @@ -83,7 +83,9 @@ def test_get_dataset(client): assert downloaded.meta["temperature"] == dset.scientificMetadata["temperature"] assert downloaded.meta["data_type"] == dset.scientificMetadata["data_type"] - for dset_file, expected_file in zip(downloaded.files, dblock.dataFileList): + for dset_file, expected_file in zip( + downloaded.files, dblock.dataFileList, strict=True + ): assert dset_file.local_path is None assert dset_file.size == expected_file.size assert dset_file.creation_time == expected_file.time @@ -100,7 +102,9 @@ def test_can_get_public_dataset_without_login(require_scicat_backend, scicat_acc assert downloaded.creation_time == dset.creationTime assert downloaded.access_groups == dset.accessGroups - for dset_file, expected_file in zip(downloaded.files, dblock.dataFileList): + for dset_file, expected_file in zip( + downloaded.files, dblock.dataFileList, strict=True + ): assert dset_file.local_path is None assert dset_file.size == expected_file.size assert dset_file.creation_time == expected_file.time diff --git a/tests/common/files.py b/tests/common/files.py index 2c532889..f3c92235 100644 --- a/tests/common/files.py +++ b/tests/common/files.py @@ -25,9 +25,9 @@ def make_file( # and avoids potential difficulties of querying the file system. creation_time = datetime.now().astimezone(timezone.utc) - return dict( - path=path, - creation_time=creation_time, - checksum=checksum_digest, - size=len(contents), - ) + return { + "path": path, + "creation_time": creation_time, + "checksum": checksum_digest, + "size": len(contents), + } diff --git a/tests/dataset_fields_test.py b/tests/dataset_fields_test.py index 0e705197..84e090bc 100644 --- a/tests/dataset_fields_test.py +++ b/tests/dataset_fields_test.py @@ -36,7 +36,7 @@ def test_init_dataset_with_only_type(): @pytest.mark.parametrize( - "typ", ("raw", "derived", DatasetType.RAW, DatasetType.DERIVED) + "typ", ["raw", "derived", DatasetType.RAW, DatasetType.DERIVED] ) def test_init_dataset_accepted_types(typ): dset = Dataset(type=typ) @@ -44,7 +44,7 @@ def test_init_dataset_accepted_types(typ): def test_init_dataset_raises_for_bad_type(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="DatasetType"): Dataset(type="bad-type") # type: ignore[arg-type] @@ -420,8 +420,7 @@ def test_make_raw_model_raises_if_derived_field_set(field, data): ) val = data.draw(st.from_type(field.type)) assume(val is not None) - setattr(dset, field.name, val) - with pytest.raises(ValueError): + with pytest.raises(pydantic.ValidationError): dset.make_upload_model() @@ -451,11 +450,11 @@ def test_make_derived_model_raises_if_raw_field_set(field, data): val = data.draw(st.from_type(field.type)) assume(val is not None) setattr(dset, field.name, val) - with pytest.raises(ValueError): + with pytest.raises(pydantic.ValidationError): dset.make_upload_model() -@pytest.mark.parametrize("field", ("contact_email", "owner_email")) +@pytest.mark.parametrize("field", ["contact_email", "owner_email"]) def test_email_validation(field): dset = Dataset( type="raw", @@ -473,11 +472,11 @@ def test_email_validation(field): @pytest.mark.parametrize( "good_orcid", - ( + [ "https://orcid.org/0000-0002-3761-3201", "https://orcid.org/0000-0001-2345-6789", "https://orcid.org/0000-0003-2818-0368", - ), + ], ) def test_orcid_validation_valid(good_orcid): dset = Dataset( @@ -496,12 +495,12 @@ def test_orcid_validation_valid(good_orcid): @pytest.mark.parametrize( "bad_orcid", - ( + [ "0000-0002-3761-3201", "https://not-orcid.eu/0000-0002-3761-3201", "https://orcid.org/0010-0002-3765-3201", "https://orcid.org/0000-0002-3761-320X", - ), + ], ) def test_orcid_validation_missing_url(bad_orcid): dset = Dataset( diff --git a/tests/dataset_test.py b/tests/dataset_test.py index 92999768..f93fdc2a 100644 --- a/tests/dataset_test.py +++ b/tests/dataset_test.py @@ -186,7 +186,7 @@ def test_from_download_models_does_not_initialize_wrong_fields(dataset_download_ assert getattr(dset, field.name) is None -@pytest.mark.parametrize("typ", (DatasetType.RAW, DatasetType.DERIVED)) +@pytest.mark.parametrize("typ", [DatasetType.RAW, DatasetType.DERIVED]) def test_new_dataset_has_no_files(typ): dset = Dataset(type=typ) assert len(list(dset.files)) == 0 @@ -196,7 +196,7 @@ def test_new_dataset_has_no_files(typ): assert dset.size == 0 -@pytest.mark.parametrize("typ", (DatasetType.RAW, DatasetType.DERIVED)) +@pytest.mark.parametrize("typ", [DatasetType.RAW, DatasetType.DERIVED]) def test_add_local_file_to_new_dataset(typ, fs): file_data = make_file(fs, "local/folder/data.dat") @@ -221,7 +221,7 @@ def test_add_local_file_to_new_dataset(typ, fs): assert abs(file_data["creation_time"] - f.make_model().time) < timedelta(seconds=1) -@pytest.mark.parametrize("typ", (DatasetType.RAW, DatasetType.DERIVED)) +@pytest.mark.parametrize("typ", [DatasetType.RAW, DatasetType.DERIVED]) def test_add_multiple_local_files_to_new_dataset(typ, fs): file_data0 = make_file(fs, "common/location1/data.dat") file_data1 = make_file(fs, "common/song.mp3") @@ -254,7 +254,7 @@ def test_add_multiple_local_files_to_new_dataset(typ, fs): assert f1.checksum_algorithm == "blake2b" -@pytest.mark.parametrize("typ", (DatasetType.RAW, DatasetType.DERIVED)) +@pytest.mark.parametrize("typ", [DatasetType.RAW, DatasetType.DERIVED]) def test_add_multiple_local_files_to_new_dataset_with_base_path(typ, fs): file_data0 = make_file(fs, "common/location1/data.dat") file_data1 = make_file(fs, "common/song.mp3") @@ -289,8 +289,8 @@ def test_add_multiple_local_files_to_new_dataset_with_base_path(typ, fs): assert f1.checksum_algorithm == "blake2b" -@pytest.mark.parametrize("typ", (DatasetType.RAW, DatasetType.DERIVED)) -@pytest.mark.parametrize("algorithm", ("sha256", None)) +@pytest.mark.parametrize("typ", [DatasetType.RAW, DatasetType.DERIVED]) +@pytest.mark.parametrize("algorithm", ["sha256", None]) def test_can_set_default_checksum_algorithm(typ, algorithm, fs): make_file(fs, "local/data.dat") @@ -333,7 +333,11 @@ def test_make_scicat_models_datablock_without_files(dataset): @settings(max_examples=10) def test_make_scicat_models_datablock_with_one_file(dataset): file_model = model.DownloadDataFile( - path="path", size=6163, chk="8450ac0", gid="group", time=datetime.now() + path="path", + size=6163, + chk="8450ac0", + gid="group", + time=datetime.now(tz=timezone.utc), ) dataset.add_files(File.from_download_model(local_path=None, model=file_model)) @@ -438,7 +442,9 @@ def test_eq_self(dset): dset.add_files( File.from_download_model( local_path=None, - model=model.DownloadDataFile(path="path", size=94571, time=datetime.now()), + model=model.DownloadDataFile( + path="path", size=94571, time=datetime.now(tz=timezone.utc) + ), ) ) dset.attachments.append( @@ -481,14 +487,16 @@ def test_neq_single_mismatched_file(initial): File.from_download_model( local_path=None, model=model.DownloadDataFile( - path="path", size=51553312, time=datetime.now() + path="path", size=51553312, time=datetime.now(tz=timezone.utc) ), ) ) initial.add_files( File.from_download_model( local_path=None, - model=model.DownloadDataFile(path="path", size=94571, time=datetime.now()), + model=model.DownloadDataFile( + path="path", size=94571, time=datetime.now(tz=timezone.utc) + ), ) ) assert modified != initial @@ -503,7 +511,7 @@ def test_neq_extra_file(initial): File.from_download_model( local_path="/local", model=model.DownloadDataFile( - path="path", size=51553312, time=datetime.now() + path="path", size=51553312, time=datetime.now(tz=timezone.utc) ), ) ) @@ -638,7 +646,9 @@ def test_replace_does_not_change_files_no_input_files(initial): def test_replace_does_not_change_files_with_input_files(initial): file = File.from_download_model( local_path=None, - model=model.DownloadDataFile(path="path", size=6163, time=datetime.now()), + model=model.DownloadDataFile( + path="path", size=6163, time=datetime.now(tz=timezone.utc) + ), ) initial.add_files(file) replaced = initial.replace(owner="a-new-owner") @@ -678,7 +688,7 @@ def test_replace_remove_meta(initial): @pytest.mark.parametrize( "attachments", - (None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]), + [None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]], ) @given(initial=sst.datasets()) @settings(max_examples=1) @@ -690,11 +700,11 @@ def test_replace_preserves_attachments(initial, attachments): @pytest.mark.parametrize( "attachments", - (None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]), + [None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]], ) @pytest.mark.parametrize( "target_attachments", - (None, [], [model.Attachment(caption="Attachment 2", owner_group="owner")]), + [None, [], [model.Attachment(caption="Attachment 2", owner_group="owner")]], ) @given(initial=sst.datasets()) @settings(max_examples=1) @@ -780,13 +790,13 @@ def test_derive_keep_nothing(initial): @given(sst.datasets(pid=None)) @settings(max_examples=5) def test_derive_requires_pid(initial): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="pid"): initial.derive() @pytest.mark.parametrize( "attachments", - (None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]), + [None, [], [model.Attachment(caption="Attachment 1", owner_group="owner")]], ) @given(initial=sst.datasets(pid=PID(pid="some-id"))) @settings(max_examples=1) @@ -808,9 +818,9 @@ def invalid_field_example(my_type): @given(initial=sst.datasets(for_upload=True)) @settings(max_examples=10) def test_dataset_dict_like_keys_per_type(initial: Dataset) -> None: - my_names = set( + my_names = { field.name for field in Dataset._FIELD_SPEC if field.used_by(initial.type) - ) + } assert set(initial.keys()) == my_names @@ -819,9 +829,9 @@ def test_dataset_dict_like_keys_per_type(initial: Dataset) -> None: def test_dataset_dict_like_keys_including_invalid_field(initial): invalid_name, invalid_value = invalid_field_example(initial.type) - my_names = set( + my_names = { field.name for field in Dataset._FIELD_SPEC if field.used_by(initial.type) - ) + } assert invalid_name not in my_names my_names.add(invalid_name) @@ -833,7 +843,7 @@ def test_dataset_dict_like_keys_including_invalid_field(initial): @given(initial=sst.datasets(for_upload=True)) @settings(max_examples=10) def test_dataset_dict_like_values(initial: Dataset) -> None: - for key, value in zip(initial.keys(), initial.values()): + for key, value in zip(initial.keys(), initial.values(), strict=True): assert value == getattr(initial, key) @@ -841,7 +851,7 @@ def test_dataset_dict_like_values(initial: Dataset) -> None: @settings(max_examples=10) def test_dataset_dict_like_values_with_invalid_field(initial: Dataset) -> None: setattr(initial, *invalid_field_example(initial.type)) - for key, value in zip(initial.keys(), initial.values()): + for key, value in zip(initial.keys(), initial.values(), strict=True): assert value == getattr(initial, key) @@ -860,7 +870,7 @@ def test_dataset_dict_like_getitem(initial): @pytest.mark.parametrize( - ("is_attr", "wrong_field"), ((True, "size"), (False, "OBVIOUSLYWRONGNAME")) + ("is_attr", "wrong_field"), [(True, "size"), (False, "OBVIOUSLYWRONGNAME")] ) @given(initial=sst.datasets(for_upload=True)) @settings(max_examples=10) @@ -895,7 +905,7 @@ def test_dataset_dict_like_setitem_invalid_field(initial: Dataset) -> None: @pytest.mark.parametrize( ("is_attr", "wrong_field", "wrong_value"), - ((True, "size", 10), (False, "OBVIOUSLYWRONGNAME", "OBVIOUSLYWRONGVALUE")), + [(True, "size", 10), (False, "OBVIOUSLYWRONGNAME", "OBVIOUSLYWRONGVALUE")], ) @given(initial=sst.datasets(for_upload=True)) @settings(max_examples=10) diff --git a/tests/download_test.py b/tests/download_test.py index b25760e5..64c70cd9 100644 --- a/tests/download_test.py +++ b/tests/download_test.py @@ -22,7 +22,7 @@ def _checksum(data: bytes) -> str: return checksum.hexdigest() -@pytest.fixture +@pytest.fixture() def data_files(): contents = { "file1.dat": b"contents-of-file1", @@ -41,7 +41,7 @@ def data_files(): return files, contents -@pytest.fixture +@pytest.fixture() def dataset_and_files(data_files): model = DownloadDataset( contactEmail="p.stibbons@uu.am", diff --git a/tests/file_test.py b/tests/file_test.py index 68f41481..718e2ea8 100644 --- a/tests/file_test.py +++ b/tests/file_test.py @@ -16,7 +16,7 @@ from .common.files import make_file -@pytest.fixture +@pytest.fixture() def fake_file(fs): return make_file(fs, path=Path("local", "dir", "events.nxs")) @@ -88,7 +88,7 @@ def test_file_from_local_set_many_args(fake_file): assert abs(fake_file["creation_time"] - file.creation_time) < timedelta(seconds=1) -@pytest.mark.parametrize("alg", ("md5", "sha256", "blake2s")) +@pytest.mark.parametrize("alg", ["md5", "sha256", "blake2s"]) def test_file_from_local_select_checksum_algorithm(fake_file, alg): file = replace(File.from_local(fake_file["path"]), checksum_algorithm=alg) expected = checksum_of_file(fake_file["path"], algorithm=alg) @@ -377,7 +377,7 @@ def test_validate_after_download_detects_size_mismatch(fake_file, caplog): assert "does not match size reported in dataset" in caplog.text -@pytest.mark.parametrize("chk", ("sha256", None)) +@pytest.mark.parametrize("chk", ["sha256", None]) def test_local_is_not_up_to_date_for_remote_file(chk): file = File.from_download_model( DownloadDataFile( diff --git a/tests/filesystem_test.py b/tests/filesystem_test.py index 955f9735..f4d6e24d 100644 --- a/tests/filesystem_test.py +++ b/tests/filesystem_test.py @@ -39,20 +39,20 @@ def test_remote_path_init_requires_path_like(): RemotePath(["folder", "file.dat"]) # type: ignore[arg-type] -@pytest.mark.parametrize("local_type", (PurePath, Path)) +@pytest.mark.parametrize("local_type", [PurePath, Path]) def test_remote_path_rejects_os_path(local_type): with pytest.raises(TypeError): RemotePath(local_type("dir", "file.csv")) -@pytest.mark.parametrize("local_type", (PurePath, PurePosixPath, PureWindowsPath)) +@pytest.mark.parametrize("local_type", [PurePath, PurePosixPath, PureWindowsPath]) def test_remote_path_from_local(local_type): local_path = local_type("dir", "folder", "file.csv") remote_path = RemotePath.from_local(local_path) assert remote_path == RemotePath("dir/folder/file.csv") -@pytest.mark.parametrize("local_type", (PurePath, PurePosixPath, PureWindowsPath)) +@pytest.mark.parametrize("local_type", [PurePath, PurePosixPath, PureWindowsPath]) def test_remote_path_posix_uses_forward_slashes(local_type): local_path = local_type("dir", "folder", "file.csv") remote_path = RemotePath.from_local(local_path) @@ -75,7 +75,7 @@ def test_remote_path_to_local(): @pytest.mark.parametrize( - "types", ((RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)) + "types", [(RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)] ) def test_remote_path_eq(types): ta, tb = types @@ -83,7 +83,7 @@ def test_remote_path_eq(types): @pytest.mark.parametrize( - "types", ((RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)) + "types", [(RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)] ) def test_remote_path_neq(types): ta, tb = types @@ -91,7 +91,7 @@ def test_remote_path_neq(types): @pytest.mark.parametrize( - "types", ((RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)) + "types", [(RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)] ) def test_remote_path_join(types): ta, tb = types @@ -102,7 +102,7 @@ def test_remote_path_join(types): @pytest.mark.parametrize( - "types", ((RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)) + "types", [(RemotePath, RemotePath), (RemotePath, str), (str, RemotePath)] ) def test_remote_path_join_url(types): ta, tb = types @@ -162,7 +162,7 @@ def test_remote_path_truncated(): assert RemotePath("file.longextension").truncated(9) == "f.longext" -@pytest.mark.parametrize("size", (0, 1, 57121)) +@pytest.mark.parametrize("size", [0, 1, 57121]) def test_file_size(fs, size): fs.create_file("image.tiff", st_size=size) assert file_size(Path("image.tiff")) == size @@ -178,7 +178,7 @@ def test_file_modification_time(fs): @pytest.mark.parametrize( "contents", - (b"small file contents", b"large contents " * 100000), + [b"small file contents", b"large contents " * 100000], ids=("small", "large"), ) def test_checksum_of_file(fs, contents): @@ -194,7 +194,7 @@ def test_checksum_of_file(fs, contents): ) -@pytest.mark.parametrize("path_type", (str, Path, RemotePath)) +@pytest.mark.parametrize("path_type", [str, Path, RemotePath]) def test_escape_path_returns_same_type_as_input(path_type): assert isinstance(escape_path(path_type("x")), path_type) diff --git a/tests/model_test.py b/tests/model_test.py index 6dbcf27a..d662e4f3 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -33,11 +33,11 @@ def build_user_model_for_upload(cls: type[T]) -> st.SearchStrategy[T]: @given(data=st.data()) @pytest.mark.parametrize( "model_types", - ( + [ (model.Attachment, model.UploadAttachment), (model.Technique, model.UploadTechnique), (model.Relationship, model.UploadRelationship), - ), + ], ) # Cannot test (model.Sample, model.UploadSample) because hypothesis # cannot handle fields with type Any. @@ -61,7 +61,7 @@ def test_upload_attachment_fields(attachment): @given(st.builds(model.Attachment)) def test_upload_model_rejects_non_upload_fields(attachment): attachment._created_by = "the-creator" - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="field.*upload"): attachment.make_upload_model() @@ -69,14 +69,14 @@ def test_upload_model_rejects_non_upload_fields(attachment): @given(data=st.data()) @pytest.mark.parametrize( "model_types", - ( + [ (model.Attachment, model.DownloadAttachment), (model.Lifecycle, model.DownloadLifecycle), (model.Technique, model.DownloadTechnique), (model.History, model.DownloadHistory), (model.Instrument, model.DownloadInstrument), (model.Relationship, model.DownloadRelationship), - ), + ], ) def test_can_make_from_download_model(model_types, data): user_model_type, download_model_type = model_types diff --git a/tests/transfer/link_test.py b/tests/transfer/link_test.py index 27d6ea69..02590e41 100644 --- a/tests/transfer/link_test.py +++ b/tests/transfer/link_test.py @@ -3,7 +3,7 @@ import hashlib import sys -from datetime import datetime +from datetime import datetime, timezone import pytest @@ -75,7 +75,7 @@ def test_client_with_link(tmp_path): accessGroups=["group1"], contactEmail="p.stibbons@uu.am", creationLocation="UU", - creationTime=datetime(2023, 6, 23, 10, 0, 0), + creationTime=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), numberOfFiles=1, numberOfFilesArchived=0, owner="PonderStibbons", @@ -92,7 +92,7 @@ def test_client_with_link(tmp_path): path="file1.txt", size=len(content), chk=checksum, - time=datetime(2023, 6, 23, 10, 0, 0), + time=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), ) ], datasetId=ds.pid, @@ -134,7 +134,7 @@ def test_client_with_link_local_file_exists(tmp_path): accessGroups=["group1"], contactEmail="p.stibbons@uu.am", creationLocation="UU", - creationTime=datetime(2023, 6, 23, 10, 0, 0), + creationTime=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), numberOfFiles=1, numberOfFilesArchived=0, owner="PonderStibbons", @@ -151,7 +151,7 @@ def test_client_with_link_local_file_exists(tmp_path): path="file1.txt", size=len(content), chk=checksum, - time=datetime(2023, 6, 23, 10, 0, 0), + time=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), ) ], datasetId=ds.pid, @@ -195,7 +195,7 @@ def test_client_with_link_local_file_exists_clashing_content(tmp_path): accessGroups=["group1"], contactEmail="p.stibbons@uu.am", creationLocation="UU", - creationTime=datetime(2023, 6, 23, 10, 0, 0), + creationTime=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), numberOfFiles=1, numberOfFilesArchived=0, owner="PonderStibbons", @@ -212,7 +212,7 @@ def test_client_with_link_local_file_exists_clashing_content(tmp_path): path="file1.txt", size=len(content), chk=checksum, - time=datetime(2023, 6, 23, 10, 0, 0), + time=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), ) ], datasetId=ds.pid, diff --git a/tests/transfer/sftp_test.py b/tests/transfer/sftp_test.py index 0ed83701..f255747d 100644 --- a/tests/transfer/sftp_test.py +++ b/tests/transfer/sftp_test.py @@ -23,7 +23,7 @@ @pytest.fixture(scope="session", autouse=True) -def server(request, sftp_fileserver): +def _server(request, sftp_fileserver): skip_if_not_sftp(request) @@ -401,7 +401,7 @@ def test_client_with_sftp( access_groups=["group1"], contact_email="p.stibbons@uu.am", creation_location="UU", - creation_time=datetime(2023, 6, 23, 10, 0, 0), + creation_time=datetime(2023, 6, 23, 10, 0, 0, tzinfo=timezone.utc), owner="PonderStibbons", owner_group="uu", principal_investigator="MustrumRidcully", diff --git a/tests/upload_test.py b/tests/upload_test.py index c6ddad86..6e8a9f50 100644 --- a/tests/upload_test.py +++ b/tests/upload_test.py @@ -29,7 +29,7 @@ def get_file_transfer(client: Client) -> FakeFileTransfer: return client.file_transfer # type: ignore[return-value] -@pytest.fixture +@pytest.fixture() def dataset(): return Dataset( access_groups=["group1", "2nd_group"], @@ -51,7 +51,7 @@ def dataset(): ) -@pytest.fixture +@pytest.fixture() def dataset_with_files(dataset, fs): make_file(fs, path="file.nxs", contents=b"contents of file.nxs") make_file(fs, path="the_log_file.log", contents=b"this is a log file") @@ -59,7 +59,7 @@ def dataset_with_files(dataset, fs): return dataset -@pytest.fixture +@pytest.fixture() def attachments(): return [ Attachment( @@ -75,7 +75,7 @@ def attachments(): ] -@pytest.fixture +@pytest.fixture() def client(fs, scicat_access): return FakeClient.from_credentials( url="", @@ -171,10 +171,10 @@ def test_upload_with_only_remote_files_does_not_need_file_transfer(dataset): def test_upload_with_both_remote_and_local_files(client, dataset_with_files): - original_file_names = set( + original_file_names = { dataset_with_files.source_folder / file.remote_path for file in dataset_with_files.files - ) + } dataset_with_files.add_files( File.from_remote( remote_path="file1.h5", size=6123, creation_time="2019-09-09T19:29:39Z" @@ -284,7 +284,7 @@ def test_upload_does_not_create_dataset_if_validation_fails(dataset_with_files, disable={"validate_dataset_model": ValueError("Validation failed")}, file_transfer=FakeFileTransfer(fs=fs), ) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Validation"): client.upload_new_dataset_now(dataset_with_files) assert not client.datasets diff --git a/tests/util/formatter_test.py b/tests/util/formatter_test.py index 268efc6c..5400ab72 100644 --- a/tests/util/formatter_test.py +++ b/tests/util/formatter_test.py @@ -59,5 +59,5 @@ def test_dataset_formatter_preserves_path_separators(): def test_dataset_formatter_does_not_allow_none(): dset = Dataset(type="raw", owner=None) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="format path"): DatasetPathFormatter().format("{owner}", dset=dset)