From 4559f5880b98f403ec33f5b4e6fa759415469289 Mon Sep 17 00:00:00 2001 From: Kess Plasmeier Date: Tue, 12 May 2026 16:41:35 -0700 Subject: [PATCH] chore: remove Black, use ruff for formatting --- .github/workflows/lint.yml | 1 + Makefile | 10 ++++++---- pyproject.toml | 6 ------ src/s3_encryption/instruction_file.py | 2 +- src/s3_encryption/materials/kms_keyring.py | 6 ++---- src/s3_encryption/pipelines.py | 5 +---- test/integration/test_i_security.py | 21 ++++++++++----------- test/performance/generate_report.py | 22 +++++++++++----------- test/test_stream.py | 4 ---- 9 files changed, 32 insertions(+), 45 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b374a9a7..a1ef5e2d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,4 +25,5 @@ jobs: - name: Install dependencies and run linting run: | make install + make format-check make lint diff --git a/Makefile b/Makefile index e764a496..d01d75a3 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,16 @@ install: # Run linting checks lint: - uv run black --check src/ test/ - # Enforce ruff checks on src/ but allow test/ to fail uv run ruff check src/ uv run ruff check test/ || true -# Format code with Black and Ruff +# Check formatting (no changes, just verify) +format-check: + uv run ruff format --check src/ test/ + +# Format code format: - uv run black src/ test/ + uv run ruff format src/ test/ uv run ruff check --fix src/ test/ # Run all tests with combined coverage diff --git a/pyproject.toml b/pyproject.toml index 5e94ee4d..7fb8a58e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,6 @@ test = [ "pytest-cov>=7.1.0", ] dev = [ - "black>=26.3.1", "ruff>=0.15.12", "boto3-stubs~=1.43.6", ] @@ -32,11 +31,6 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/s3_encryption"] -[tool.black] -line-length = 100 -target-version = ["py311"] -include = '\.pyi?$' - [tool.ruff] line-length = 100 target-version = "py311" diff --git a/src/s3_encryption/instruction_file.py b/src/s3_encryption/instruction_file.py index 61f9b167..61df766f 100644 --- a/src/s3_encryption/instruction_file.py +++ b/src/s3_encryption/instruction_file.py @@ -47,7 +47,7 @@ def parse_instruction_file(instruction_data: bytes, key: str) -> dict[str, Any]: # Validate that it's a dictionary if not isinstance(metadata, dict): raise S3EncryptionClientError( - f"Instruction file must contain a JSON object, " f"got {type(metadata).__name__}: {key}" + f"Instruction file must contain a JSON object, got {type(metadata).__name__}: {key}" ) # Validate that all keys are S3EC metadata keys diff --git a/src/s3_encryption/materials/kms_keyring.py b/src/s3_encryption/materials/kms_keyring.py index abd6fad4..8eb8b7d2 100644 --- a/src/s3_encryption/materials/kms_keyring.py +++ b/src/s3_encryption/materials/kms_keyring.py @@ -164,8 +164,7 @@ def on_decrypt(self, dec_materials, encrypted_data_keys=None): ##% context. if KMS_CONTEXT_DEFAULT_KEY in encryption_context_from_request: raise S3EncryptionClientError( - f"{KMS_CONTEXT_DEFAULT_KEY} is a reserved key for the " - f"S3 encryption client" + f"{KMS_CONTEXT_DEFAULT_KEY} is a reserved key for the S3 encryption client" ) ##= specification/s3-encryption/materials/s3-kms-keyring.md#kms-context @@ -183,8 +182,7 @@ def on_decrypt(self, dec_materials, encrypted_data_keys=None): if encryption_context_stored_copy != encryption_context_from_request: # TODO: modeled error raise S3EncryptionClientError( - "Provided encryption context does not match information " - "retrieved from S3" + "Provided encryption context does not match information retrieved from S3" ) ##= specification/s3-encryption/materials/s3-kms-keyring.md#decryptdatakey diff --git a/src/s3_encryption/pipelines.py b/src/s3_encryption/pipelines.py index 2b1fe061..23a169ae 100644 --- a/src/s3_encryption/pipelines.py +++ b/src/s3_encryption/pipelines.py @@ -260,7 +260,6 @@ def decrypt( # Check if we need to fetch instruction file if metadata.should_use_instruction_file(): - if self.instruction_file_config.disable_get_object: raise S3EncryptionClientError( "Exception encountered while fetching Instruction File. " @@ -371,9 +370,7 @@ def decrypt( ##% [legacy unauthenticated algorithm suites](#legacy-decryption) is NOT enabled, ##% the S3EC MUST throw an error which details that client was ##% not configured to decrypt objects with ALG_AES_256_CBC_IV16_NO_KDF. - if ( - algorithm_suite.is_legacy and not self.enable_legacy_unauthenticated_modes - ): # noqa: SIM102 + if algorithm_suite.is_legacy and not self.enable_legacy_unauthenticated_modes: # noqa: SIM102 ##= specification/s3-encryption/decryption.md#legacy-decryption ##= type=implementation ##% The S3EC MUST NOT decrypt objects encrypted using legacy unauthenticated algorithm suites diff --git a/test/integration/test_i_security.py b/test/integration/test_i_security.py index 67782c67..3d871e1f 100644 --- a/test/integration/test_i_security.py +++ b/test/integration/test_i_security.py @@ -81,9 +81,9 @@ def test_v3_downgrade_wrap_alg_to_kms_rejected_without_legacy(self): plain_s3 = boto3.client("s3") head = plain_s3.head_object(Bucket=bucket, Key=key) original_metadata = head["Metadata"] - assert ( - original_metadata.get("x-amz-w") == "12" - ), f"Expected x-amz-w='12', got {original_metadata.get('x-amz-w')}" + assert original_metadata.get("x-amz-w") == "12", ( + f"Expected x-amz-w='12', got {original_metadata.get('x-amz-w')}" + ) tampered_metadata = original_metadata.copy() tampered_metadata["x-amz-w"] = "kms" @@ -348,9 +348,9 @@ def test_v2_downgrade_wrap_alg_to_kms_mismatched_context(self): plain_s3 = boto3.client("s3") head = plain_s3.head_object(Bucket=bucket, Key=key) original_metadata = head["Metadata"] - assert ( - original_metadata.get("x-amz-wrap-alg") == "kms+context" - ), f"Expected x-amz-wrap-alg='kms+context', got {original_metadata.get('x-amz-wrap-alg')}" + assert original_metadata.get("x-amz-wrap-alg") == "kms+context", ( + f"Expected x-amz-wrap-alg='kms+context', got {original_metadata.get('x-amz-wrap-alg')}" + ) tampered_metadata = original_metadata.copy() tampered_metadata["x-amz-wrap-alg"] = "kms" @@ -476,14 +476,13 @@ def test_wrong_key_and_tampered_ciphertext_produce_same_error(self): # Both MUST produce the same error message assert str(exc1.value) == str(exc2.value), ( - f"Error messages differ: wrong_key={str(exc1.value)!r}, " - f"tampered={str(exc2.value)!r}" + f"Error messages differ: wrong_key={str(exc1.value)!r}, tampered={str(exc2.value)!r}" ) # Neither message should contain details about the underlying failure - assert ( - "padding" not in str(exc1.value).lower() - ), f"Error message leaks padding information: {str(exc1.value)!r}" + assert "padding" not in str(exc1.value).lower(), ( + f"Error message leaks padding information: {str(exc1.value)!r}" + ) def test_truncated_ciphertext_produces_same_error(self): """Truncated ciphertext MUST produce the same error as padding failure. diff --git a/test/performance/generate_report.py b/test/performance/generate_report.py index 14030657..aee53864 100644 --- a/test/performance/generate_report.py +++ b/test/performance/generate_report.py @@ -76,7 +76,7 @@ def _bar_chart_svg(chart_id, title, groups, sizes, width=700, bar_h=28, gap=6): lx = label_col_w for g in groups: svg.append(f'') - svg.append(f'{g["label"]}') + svg.append(f'{g["label"]}') lx += len(g["label"]) * 7 + 30 y = 58 @@ -150,7 +150,7 @@ def _histogram_svg(chart_id, title, series_list, width=700, height=220, n_bins=1 lx = margin_l for s in series_list: svg.append(f'') - svg.append(f'{s["label"]}') + svg.append(f'{s["label"]}') lx += len(s["label"]) * 6 + 24 # Axes @@ -302,14 +302,14 @@ def _build_table(results): durations_str = ", ".join(_fmt(v) for v in d) rows += f""" - {r['test']} - {r['size_mb']} MB - {r['rounds']} + {r["test"]} + {r["size_mb"]} MB + {r["rounds"]} {_fmt(med)} - {_fmt(r['mean_s'])} + {_fmt(r["mean_s"])} {_fmt(p95)} - {_fmt(r['min_s'])} - {_fmt(r['max_s'])} + {_fmt(r["min_s"])} + {_fmt(r["max_s"])} {durations_str} """ @@ -366,9 +366,9 @@ def generate_html(data: dict) -> str:

S3 Encryption Client — Performance Report

Generated: {timestamp}
- Rounds per test: {config['num_rounds']} · - Object sizes: {', '.join(str(s) + ' MB' for s in sizes)} · - Bucket: {config['bucket']} · Region: {config['region']} + Rounds per test: {config["num_rounds"]} · + Object sizes: {", ".join(str(s) + " MB" for s in sizes)} · + Bucket: {config["bucket"]} · Region: {config["region"]}
diff --git a/test/test_stream.py b/test/test_stream.py index dc0f2eb9..692c8b00 100644 --- a/test/test_stream.py +++ b/test/test_stream.py @@ -124,7 +124,6 @@ def spy_finalize(data): class TestDelayedAuthCBCDecryption: - def test_roundtrip(self): plaintext = b"hello world, this is a CBC test!!" ciphertext, key, iv = _encrypt_cbc(plaintext) @@ -242,7 +241,6 @@ def test_empty_ciphertext(self): class TestBufferedDecryptingStream: - def test_full_read(self): plaintext = os.urandom(1024) ct, key, nonce = _encrypt_gcm(plaintext) @@ -379,7 +377,6 @@ def test_idempotent_decrypt(self): class TestDelayedAuthGCMDecryption: - def test_full_read(self): plaintext = os.urandom(1024) ct, key, nonce = _encrypt_gcm(plaintext) @@ -511,7 +508,6 @@ def test_large_data(self): class TestEdgeCasePlaintextLengths: - @pytest.mark.parametrize("length", EDGE_CASE_LENGTHS) def test_buffered_gcm(self, length): plaintext = os.urandom(length)