diff --git a/.github/check_entitlements.sh b/.github/check_entitlements.sh index 401f7bf..58590ac 100755 --- a/.github/check_entitlements.sh +++ b/.github/check_entitlements.sh @@ -17,7 +17,6 @@ echo "" get_token() { curl -k --location "$TOKEN_URL" \ - --header "X-VirtruPubKey;" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=client_credentials" \ --data-urlencode "client_id=$OTDF_CLIENT" \ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 620ed7d..677eb43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.12.12 + rev: v0.13.1 hooks: # Run the linter. - id: ruff-check diff --git a/README.md b/README.md index 60be913..719a221 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,6 @@ decrypted_data = tdf_reader.payload with open("decrypted.txt", "wb") as f: f.write(decrypted_data) -# Don't forget to close the SDK when done -sdk.close() ``` ## Project Structure diff --git a/otdf-python-proto/README.md b/otdf-python-proto/README.md index 2e4c102..4e04f07 100644 --- a/otdf-python-proto/README.md +++ b/otdf-python-proto/README.md @@ -17,7 +17,7 @@ See [CONNECT_RPC.md](../docs/CONNECT_RPC.md) for additional information. ## Structure - `proto-files/`: Contains the raw .proto files downloaded from the OpenTDF platform -- `generated/`: Contains the generated Python protobuf and Connect RPC client files +- `src/otdf_python_proto/`: Contains the generated Python protobuf and Connect RPC client files - `scripts/`: Contains build scripts for generating protobuf and Connect RPC files - `buf.yaml`: Buf configuration for proto validation and management - `buf.gen.yaml`: Buf generation configuration for Connect RPC and protobuf @@ -40,10 +40,10 @@ Or use the convenience script: ``` This generates: -- `generated/*_connect.py` - Connect RPC clients (preferred) -- `generated/*_pb2.py` - Standard protobuf classes -- `generated/*_pb2.pyi` - Type stubs for better IDE support -- `generated/legacy_grpc/*_pb2_grpc.py` - Legacy gRPC clients (backward compatibility) +- `src/otdf_python_proto/**/*_connect.py` - Connect RPC clients (preferred) +- `src/otdf_python_proto/**/*_pb2.py` - Standard protobuf classes +- `src/otdf_python_proto/**/*_pb2.pyi` - Type stubs for better IDE support +- `src/otdf_python_proto/legacy_grpc/**/*_pb2_grpc.py` - Legacy gRPC clients (backward compatibility) ### Legacy gRPC Generation diff --git a/otdf-python-proto/scripts/generate_connect_proto.py b/otdf-python-proto/scripts/generate_connect_proto.py index ff0d0d8..111cd81 100644 --- a/otdf-python-proto/scripts/generate_connect_proto.py +++ b/otdf-python-proto/scripts/generate_connect_proto.py @@ -96,10 +96,10 @@ def copy_opentdf_proto_files(proto_gen_dir: Path) -> bool: print(f" Copying {relative_path}...") # Copy the file content - with open(proto_file) as src: + with proto_file.open() as src: content = src.read() - with open(dest_path, "w") as dst: + with dest_path.open("w") as dst: dst.write(content) copied_files += 1 @@ -155,7 +155,7 @@ def run_buf_generate(proto_gen_dir: Path) -> bool: # Update buf.gen.yaml with the correct path buf_gen_path = proto_gen_dir / "buf.gen.yaml" - with open(buf_gen_path) as f: + with buf_gen_path.open() as f: content = f.read() # Replace the local plugin path @@ -163,7 +163,7 @@ def run_buf_generate(proto_gen_dir: Path) -> bool: "- local: protoc-gen-connect_python", f"- local: {connect_plugin_path}" ) - with open(buf_gen_path, "w") as f: + with buf_gen_path.open("w") as f: f.write(updated_content) # Run buf generate @@ -221,7 +221,7 @@ def _fix_ignore_if_default_value(proto_files_dir): # Iterate all .proto files in the directory for proto_file in proto_files_dir.glob("**/*.proto"): try: - with open(proto_file, "r") as file: # noqa: UP015 + with proto_file.open("r") as file: content = file.read() # Replace the old enum value with the new one @@ -230,7 +230,7 @@ def _fix_ignore_if_default_value(proto_files_dir): ) # Write the updated content back to the file - with open(proto_file, "w") as file: + with proto_file.open("w") as file: file.write(updated_content) print(f"Updated {proto_file.name} to use IGNORE_IF_ZERO_VALUE") diff --git a/pyproject.toml b/pyproject.toml index 5a7cb92..e82c17e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ lint.select = [ "I", # Performance-related rules "PERF", # Ruff's performance rules + "PTH", # pathlib (path handling) # Additional useful rules "UP", # pyupgrade (modern Python features) "SIM", # flake8-simplify (simplifications) diff --git a/src/otdf_python/cli.py b/src/otdf_python/cli.py index 48a1019..863d006 100644 --- a/src/otdf_python/cli.py +++ b/src/otdf_python/cli.py @@ -88,7 +88,7 @@ def load_client_credentials(creds_file_path: str) -> tuple[str, str]: "CRITICAL", f"Credentials file does not exist: {creds_file_path}" ) - with open(creds_path) as f: + with creds_path.open() as f: creds = json.load(f) client_id = creds.get("clientId") @@ -223,13 +223,13 @@ def cmd_encrypt(args): try: # Read input file - with open(input_path, "rb") as input_file: + with input_path.open("rb") as input_file: payload = input_file.read() # Determine output if args.output: output_path = Path(args.output) - with open(output_path, "wb") as output_file: + with output_path.open("wb") as output_file: try: # Create appropriate config based on container type container_type = getattr(args, "container_type", "tdf") @@ -247,7 +247,7 @@ def cmd_encrypt(args): logger.debug("Creating TDF") config = create_tdf_config(sdk, args) output_stream = BytesIO() - manifest, size, _ = sdk.create_tdf( + _manifest, size, _ = sdk.create_tdf( BytesIO(payload), config, output_stream ) output_file.write(output_stream.getvalue()) @@ -274,7 +274,7 @@ def cmd_encrypt(args): logger.debug("Creating TDF") config = create_tdf_config(sdk, args) output_stream = BytesIO() - manifest, size, _ = sdk.create_tdf( + _manifest, size, _ = sdk.create_tdf( BytesIO(payload), config, output_stream ) output_file.write(output_stream.getvalue()) @@ -296,13 +296,13 @@ def cmd_decrypt(args): try: # Read encrypted file - with open(input_path, "rb") as input_file: + with input_path.open("rb") as input_file: encrypted_data = input_file.read() # Determine output if args.output: output_path = Path(args.output) - with open(output_path, "wb") as output_file: + with output_path.open("wb") as output_file: try: # Try to determine if it's a NanoTDF or regular TDF # NanoTDFs have a specific header format, regular TDFs are ZIP files @@ -359,7 +359,7 @@ def cmd_inspect(args): try: # Read encrypted file - with open(input_path, "rb") as input_file: + with input_path.open("rb") as input_file: encrypted_data = input_file.read() if encrypted_data.startswith(b"PK"): @@ -400,7 +400,7 @@ def cmd_inspect(args): except Exception as e: # If we can't inspect due to auth issues, show what we can logger.warning(f"Limited inspection due to: {e}") - with open(input_path, "rb") as input_file: + with input_path.open("rb") as input_file: encrypted_data = input_file.read() file_type = "TDF" if encrypted_data.startswith(b"PK") else "NanoTDF" diff --git a/src/otdf_python/header.py b/src/otdf_python/header.py index df7186d..2f6654d 100644 --- a/src/otdf_python/header.py +++ b/src/otdf_python/header.py @@ -51,7 +51,7 @@ def peek_length(buffer: bytes) -> int: # MAGIC_NUMBER_AND_VERSION (3 bytes) offset += 3 # ResourceLocator - kas_locator, kas_size = ResourceLocator.from_bytes_with_size(buffer[offset:]) + _kas_locator, kas_size = ResourceLocator.from_bytes_with_size(buffer[offset:]) offset += kas_size # ECC mode (1 byte) ecc_mode = ECCMode(buffer[offset]) @@ -59,7 +59,7 @@ def peek_length(buffer: bytes) -> int: # Payload config (1 byte) offset += 1 # PolicyInfo - policy_info, policy_size = PolicyInfo.from_bytes_with_size( + _policy_info, policy_size = PolicyInfo.from_bytes_with_size( buffer[offset:], ecc_mode ) offset += policy_size diff --git a/src/otdf_python/nanotdf.py b/src/otdf_python/nanotdf.py index d8a063e..15d7725 100644 --- a/src/otdf_python/nanotdf.py +++ b/src/otdf_python/nanotdf.py @@ -300,7 +300,7 @@ def create_nano_tdf( iv, ciphertext = self._encrypt_payload(payload, key) # Wrap key if needed - wrapped_key, kas_public_key = self._wrap_key_if_needed(key, config) + wrapped_key, _kas_public_key = self._wrap_key_if_needed(key, config) # Compose the complete NanoTDF: [IV][CIPHERTEXT][WRAPPED_KEY][WRAPPED_KEY_LEN] if wrapped_key: diff --git a/src/otdf_python/sdk_builder.py b/src/otdf_python/sdk_builder.py index ead42f5..9f4f5e4 100644 --- a/src/otdf_python/sdk_builder.py +++ b/src/otdf_python/sdk_builder.py @@ -4,9 +4,9 @@ """ import logging -import os import ssl from dataclasses import dataclass +from pathlib import Path from typing import Any import httpx @@ -77,9 +77,10 @@ def ssl_context_from_directory(self, certs_dir_path: str) -> "SDKBuilder": self.cert_paths = [] # Find all .pem and .crt files in the directory - for filename in os.listdir(certs_dir_path): - if filename.endswith(".pem") or filename.endswith(".crt"): - self.cert_paths.append(os.path.join(certs_dir_path, filename)) + certs_path = Path(certs_dir_path) + for cert_file in certs_path.iterdir(): + if cert_file.suffix in (".pem", ".crt"): + self.cert_paths.append(str(cert_file)) # Create SSL context with these certificates if self.cert_paths: diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 88525ef..8cf0d77 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -20,7 +20,7 @@ def temp_credentials_file(): with tempfile.TemporaryDirectory() as temp_dir: creds_file = Path(temp_dir) / "creds.json" creds_data = {"clientId": "opentdf", "clientSecret": "secret"} - with open(creds_file, "w") as f: + with creds_file.open("w") as f: json.dump(creds_data, f) yield creds_file diff --git a/tests/integration/otdfctl_only/test_otdfctl_generated_fixtures.py b/tests/integration/otdfctl_only/test_otdfctl_generated_fixtures.py index ccff47c..112d804 100644 --- a/tests/integration/otdfctl_only/test_otdfctl_generated_fixtures.py +++ b/tests/integration/otdfctl_only/test_otdfctl_generated_fixtures.py @@ -48,7 +48,7 @@ def test_sample_file_contents(sample_input_files): # Check text file has content text_file = sample_input_files["text"] assert text_file.exists(), f"Text file should exist: {text_file}" - with open(text_file) as f: + with text_file.open() as f: content = f.read() assert "Hello, World!" in content assert len(content) > 0 @@ -66,7 +66,7 @@ def test_sample_file_contents(sample_input_files): # Check attributes file has content attr_file = sample_input_files["with_attributes"] assert attr_file.exists(), f"Attributes file should exist: {attr_file}" - with open(attr_file) as f: + with attr_file.open() as f: content = f.read() assert "Classification: SECRET" in content diff --git a/tests/integration/otdfctl_to_python/test_cli_comparison.py b/tests/integration/otdfctl_to_python/test_cli_comparison.py index d5d428a..cc7fd54 100644 --- a/tests/integration/otdfctl_to_python/test_cli_comparison.py +++ b/tests/integration/otdfctl_to_python/test_cli_comparison.py @@ -32,7 +32,7 @@ def test_otdfctl_encrypt_python_decrypt( # Create input file input_file = temp_path / "input.txt" input_content = "Hello, World! This is a test for otdfctl decrypt comparison." - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define TDF file created by otdfctl @@ -115,7 +115,7 @@ def test_otdfctl_encrypt_otdfctl_decrypt(collect_server_logs, temp_credentials_f input_content = ( "Hello, World! This is a test for otdfctl roundtrip encryption/decryption." ) - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define TDF file and decrypted output diff --git a/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py b/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py index 5ff6118..457b25f 100644 --- a/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py +++ b/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py @@ -33,7 +33,7 @@ def test_read_otdfctl_created_tdf_structure( # Create input file input_file = temp_path / "input.txt" input_content = "Hello, World! This is test data for TDFReader integration." - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define output files @@ -60,7 +60,7 @@ def test_read_otdfctl_created_tdf_structure( assert otdfctl_output.stat().st_size > 0, "otdfctl created empty TDF file" # Test that TDFReader can open and read the structure - with open(otdfctl_output, "rb") as f: + with otdfctl_output.open("rb") as f: tdf_data = f.read() # Initialize TDFReader @@ -116,7 +116,7 @@ def test_read_otdfctl_tdf_with_attributes( # Create input file input_file = temp_path / "input.txt" input_content = "This is input data for testing attributes." - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define output file @@ -143,7 +143,7 @@ def test_read_otdfctl_tdf_with_attributes( assert otdfctl_output.exists(), "otdfctl did not create TDF file" # Test that TDFReader can read the file with attributes - with open(otdfctl_output, "rb") as f: + with otdfctl_output.open("rb") as f: tdf_data = f.read() reader = TDFReader(io.BytesIO(tdf_data)) @@ -209,10 +209,10 @@ def test_read_multiple_otdfctl_files( # Create input file input_file = temp_path / f"{test_case['name']}.txt" if isinstance(test_case["content"], bytes): - with open(input_file, "wb") as f: + with input_file.open("wb") as f: f.write(test_case["content"]) else: - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(test_case["content"]) # Define output file @@ -235,7 +235,7 @@ def test_read_multiple_otdfctl_files( ) # Test TDFReader on this file - with open(output_file, "rb") as f: + with output_file.open("rb") as f: tdf_data = f.read() reader = TDFReader(io.BytesIO(tdf_data)) diff --git a/tests/integration/test_cli_integration.py b/tests/integration/test_cli_integration.py index 9201e8e..f830e69 100644 --- a/tests/integration/test_cli_integration.py +++ b/tests/integration/test_cli_integration.py @@ -35,7 +35,7 @@ def test_cli_decrypt_otdfctl_tdf( # Create input file input_file = temp_path / "input.txt" input_content = "Hello, World! This is a test for decryption." - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define TDF file created by otdfctl @@ -99,7 +99,7 @@ def test_otdfctl_decrypt_comparison( # Create input file input_file = temp_path / "input.txt" input_content = "Hello, World! This is a test for otdfctl decrypt comparison." - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define TDF file created by otdfctl @@ -183,7 +183,7 @@ def test_otdfctl_encrypt_decrypt_roundtrip(collect_server_logs, temp_credentials input_content = ( "Hello, World! This is a test for otdfctl roundtrip encryption/decryption." ) - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define TDF file and decrypted output @@ -258,7 +258,7 @@ def test_cli_encrypt_integration( # Create input file input_file = temp_path / "input.txt" input_content = "Hello, World" - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(input_content) # Define output files diff --git a/tests/integration/test_cli_tdf_validation.py b/tests/integration/test_cli_tdf_validation.py index ff6dfd5..c484cca 100644 --- a/tests/integration/test_cli_tdf_validation.py +++ b/tests/integration/test_cli_tdf_validation.py @@ -28,7 +28,7 @@ def _create_test_input_file(temp_path: Path, content: str) -> Path: """Create a test input file with the given content.""" input_file = temp_path / "input.txt" - with open(input_file, "w") as f: + with input_file.open("w") as f: f.write(content) return input_file @@ -471,7 +471,7 @@ def test_different_content_types( # Create input file input_file = temp_path / filename # Use binary mode for consistent handling of all content types - with open(input_file, "w", encoding="utf-8") as f: + with input_file.open("w", encoding="utf-8") as f: f.write(content) # Test with Python CLI @@ -528,7 +528,7 @@ def test_different_content_types_empty( # Create input file input_file = temp_path / filename # Use binary mode for consistent handling of all content types - with open(input_file, "w", encoding="utf-8") as f: + with input_file.open("w", encoding="utf-8") as f: f.write(content) # Test with Python CLI diff --git a/tests/integration/test_pe_interaction.py b/tests/integration/test_pe_interaction.py index e9828a3..3758811 100644 --- a/tests/integration/test_pe_interaction.py +++ b/tests/integration/test_pe_interaction.py @@ -22,7 +22,7 @@ def decrypt(input_path: Path, output_path: Path, sdk: SDK): # Determine output - with open(input_path, "rb") as infile, open(output_path, "wb") as outfile: + with input_path.open("rb") as infile, output_path.open("wb") as outfile: try: logger.debug("Decrypting TDF") tdf_reader = sdk.load_tdf(infile.read()) @@ -57,7 +57,7 @@ def test_single_attribute_encryption_decryption(): input_path = Path(INPUT_FILE) output_path = input_path.with_suffix(input_path.suffix + ".tdf") - with open(input_path, "rb") as infile, open(output_path, "wb") as outfile: + with input_path.open("rb") as infile, output_path.open("wb") as outfile: sdk.create_tdf(infile.read(), config, output_stream=outfile) TDF_FILE = output_path @@ -69,7 +69,7 @@ def test_single_attribute_encryption_decryption(): DECRYPTED_FILE_SDK.touch() # Ensure the file exists decrypt(TDF_FILE, DECRYPTED_FILE_SDK, sdk) - with open(INPUT_FILE, "rb") as f1, open(DECRYPTED_FILE_SDK, "rb") as f2: + with INPUT_FILE.open("rb") as f1, DECRYPTED_FILE_SDK.open("rb") as f2: assert f1.read() == f2.read(), "SDK decrypted output does not match input" # # Decrypt with otdfctl diff --git a/tests/support_common.py b/tests/support_common.py index c3bcf5c..153cf9e 100644 --- a/tests/support_common.py +++ b/tests/support_common.py @@ -55,7 +55,7 @@ def validate_tdf3_file(tdf_path: Path, tool_name: str) -> None: assert zipfile.is_zipfile(tdf_path), f"{tool_name} output is not a valid ZIP file" # Verify TDF file has correct ZIP signature - with open(tdf_path, "rb") as f: + with tdf_path.open("rb") as f: tdf_header = f.read(4) assert tdf_header == b"PK\x03\x04", f"{tool_name} output is not a valid ZIP file" assert tdf_path.suffix == ".tdf", f"File should have .tdf extension: {tdf_path}" @@ -68,7 +68,7 @@ def validate_plaintext_file_created( assert path.exists(), f"{scenario=} did not create decrypted file" assert path.stat().st_size > 0, f"{scenario=} created empty decrypted file" # Verify scenario produces the expected decrypted content - with open(path) as f: + with path.open() as f: decrypted_content = f.read() assert decrypted_content == expected_content, ( diff --git a/tests/test_cli.py b/tests/test_cli.py index abd8cc3..b1d8b4d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,7 +2,6 @@ Test CLI functionality """ -import os import subprocess import sys import tempfile @@ -37,7 +36,7 @@ def test_cli_version(project_root): assert result.returncode == 0 assert "OpenTDF Python SDK" in result.stdout - with open(Path(__file__).parent.parent / "pyproject.toml", "rb") as f: + with (Path(__file__).parent.parent / "pyproject.toml").open("rb") as f: # Use tomli for Python < 3.11, tomllib for 3.11+ if sys.version_info < (3, 11): import tomli @@ -110,7 +109,7 @@ def test_cli_encrypt_missing_auth(project_root): assert "Authentication required" in result.stderr assert "--with-client-creds-file" in result.stderr finally: - os.unlink(temp_file) + Path(temp_file).unlink() def test_cli_encrypt_missing_creds_file(project_root): @@ -137,7 +136,7 @@ def test_cli_encrypt_missing_creds_file(project_root): assert result.returncode == 1 assert "Credentials file does not exist" in result.stderr finally: - os.unlink(temp_file) + Path(temp_file).unlink() def test_cli_encrypt_invalid_creds_file(project_root): @@ -168,8 +167,8 @@ def test_cli_encrypt_invalid_creds_file(project_root): assert result.returncode == 1 assert "must contain 'clientId' and 'clientSecret' fields" in result.stderr finally: - os.unlink(temp_file) - os.unlink(creds_file) + Path(temp_file).unlink() + Path(creds_file).unlink() def test_cli_decrypt_missing_file(project_root): diff --git a/tests/test_crypto_utils.py b/tests/test_crypto_utils.py index 2a802f5..c495208 100644 --- a/tests/test_crypto_utils.py +++ b/tests/test_crypto_utils.py @@ -27,7 +27,7 @@ def test_ec_keypair(self): self.assertIn("BEGIN PRIVATE KEY", priv_pem) def test_rsa_key_type_check(self): - priv, pub = CryptoUtils.generate_rsa_keypair() + _priv, _pub = CryptoUtils.generate_rsa_keypair() with self.assertRaises(ValueError): CryptoUtils.get_rsa_public_key_pem("notakey") with self.assertRaises(ValueError): diff --git a/tests/test_manifest_format.py b/tests/test_manifest_format.py index 674aa26..7be368d 100644 --- a/tests/test_manifest_format.py +++ b/tests/test_manifest_format.py @@ -24,7 +24,7 @@ def test_manifest_field_format(): test_data = b"Hello World" tdf_instance = TDF() config.policy_object = {"uuid": "test-uuid", "body": {"dissem": ["test"]}} - manifest, size, output_stream = tdf_instance.create_tdf( + manifest, _size, _output_stream = tdf_instance.create_tdf( payload=test_data, config=config ) @@ -82,7 +82,7 @@ def test_manifest_roundtrip_serialization(): test_data = b"Hello World" tdf_instance = TDF() config.policy_object = {"uuid": "test-uuid", "body": {"dissem": ["test"]}} - manifest, size, output_stream = tdf_instance.create_tdf( + manifest, _size, _output_stream = tdf_instance.create_tdf( payload=test_data, config=config ) diff --git a/tests/test_sdk_builder.py b/tests/test_sdk_builder.py index 0e9f835..0fae7ae 100644 --- a/tests/test_sdk_builder.py +++ b/tests/test_sdk_builder.py @@ -2,8 +2,8 @@ Tests for the SDKBuilder class. """ -import os import tempfile +from pathlib import Path from unittest.mock import MagicMock, patch import pytest @@ -103,10 +103,11 @@ def test_ssl_context_from_directory(): # Create temporary directory with cert files with tempfile.TemporaryDirectory() as tmpdirname: # Create dummy cert files + tmpdir_path = Path(tmpdirname) with ( - open(os.path.join(tmpdirname, "cert1.pem"), "w") as f1, - open(os.path.join(tmpdirname, "cert2.crt"), "w") as f2, - open(os.path.join(tmpdirname, "not_a_cert.txt"), "w") as f3, + (tmpdir_path / "cert1.pem").open("w") as f1, + (tmpdir_path / "cert2.crt").open("w") as f2, + (tmpdir_path / "not_a_cert.txt").open("w") as f3, ): f1.write("dummy cert") f2.write("dummy cert") diff --git a/tests/test_sdk_tdf_integration.py b/tests/test_sdk_tdf_integration.py index eaedafe..85b2a85 100644 --- a/tests/test_sdk_tdf_integration.py +++ b/tests/test_sdk_tdf_integration.py @@ -14,7 +14,7 @@ def test_sdk_create_tdf_with_builder(): from otdf_python.kas_info import KASInfo # Generate key pair for testing - kas_private_key, kas_public_key = generate_rsa_keypair() + _kas_private_key, kas_public_key = generate_rsa_keypair() # Create SDK with builder sdk = SDKBuilder().set_platform_endpoint("https://example.kas.com").build() @@ -37,7 +37,7 @@ def test_sdk_create_tdf_with_builder(): output = io.BytesIO() # This should not raise an AttributeError or ValueError - manifest, size, out_stream = sdk.create_tdf(payload, config, output_stream=output) + _manifest, size, out_stream = sdk.create_tdf(payload, config, output_stream=output) # Basic validations assert size > 0 @@ -50,7 +50,7 @@ def test_validate_otdf_python_script(): from otdf_python.kas_info import KASInfo # Generate key pair for testing - kas_private_key, kas_public_key = generate_rsa_keypair() + _kas_private_key, kas_public_key = generate_rsa_keypair() # Create SDK with builder sdk = SDKBuilder().set_platform_endpoint("https://default.kas.example.com").build() @@ -74,7 +74,7 @@ def test_validate_otdf_python_script(): output = io.BytesIO() # This should not raise an AttributeError or ValueError - manifest, size, out_stream = sdk.create_tdf(payload, config, output_stream=output) + _manifest, size, out_stream = sdk.create_tdf(payload, config, output_stream=output) # Basic validations assert size > 0 diff --git a/tests/test_tdf.py b/tests/test_tdf.py index 699ba32..1dcc8c9 100644 --- a/tests/test_tdf.py +++ b/tests/test_tdf.py @@ -49,7 +49,7 @@ def test_tdf_multi_kas_roundtrip(): kas2 = KASInfo(url="https://kas2.example.com", public_key=pub2, kid="kas2") config = TDFConfig(kas_info_list=[kas1, kas2]) - manifest, size, out = tdf.create_tdf(payload, config) + _manifest, _size, out = tdf.create_tdf(payload, config) data = out.getvalue() if hasattr(out, "getvalue") else out.read() # Should be able to decrypt with either KAS private key for priv in (priv1, priv2): diff --git a/tests/test_tdf_reader.py b/tests/test_tdf_reader.py index 5e7c634..663ccea 100644 --- a/tests/test_tdf_reader.py +++ b/tests/test_tdf_reader.py @@ -78,7 +78,7 @@ def test_init_no_payload(self, mock_zip_reader): def test_manifest(self, mock_zip_reader): """Test getting the manifest content.""" - mock_reader, manifest_data, _ = mock_zip_reader + mock_reader, _manifest_data, _ = mock_zip_reader manifest_content = json.dumps({"test": "manifest"}) reader = TDFReader(io.BytesIO(b"fake tdf data")) @@ -108,7 +108,7 @@ def test_read_payload_bytes(self, mock_zip_reader): @patch("otdf_python.tdf_reader.Manifest") def test_read_policy_object(self, mock_manifest, mock_zip_reader): """Test reading the policy object from the manifest.""" - mock_reader, manifest_data, _ = mock_zip_reader + mock_reader, _manifest_data, _ = mock_zip_reader # Create a realistic manifest with a base64 encoded policy import base64 diff --git a/tests/test_url_normalization.py b/tests/test_url_normalization.py index 6c6eab9..a5757fc 100644 --- a/tests/test_url_normalization.py +++ b/tests/test_url_normalization.py @@ -6,12 +6,6 @@ the use_plaintext setting when converting URLs. """ -# Allow importing from src directory -import os -import sys - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) - from src.otdf_python.kas_client import KASClient diff --git a/tests/test_validate_otdf_python.py b/tests/test_validate_otdf_python.py index 369b40f..1ac0818 100644 --- a/tests/test_validate_otdf_python.py +++ b/tests/test_validate_otdf_python.py @@ -40,7 +40,7 @@ def encrypt_file(input_path: Path) -> Path: sdk, tdf_config = _get_sdk_and_tdf_config() output_path = input_path.with_suffix(input_path.suffix + ".tdf") - with open(input_path, "rb") as infile, open(output_path, "wb") as outfile: + with input_path.open("rb") as infile, output_path.open("wb") as outfile: sdk.create_tdf(infile.read(), tdf_config, output_stream=outfile) return output_path @@ -50,7 +50,7 @@ def decrypt_file(encrypted_path: Path) -> Path: sdk = get_sdk() output_path = encrypted_path.with_suffix(".decrypted") - with open(encrypted_path, "rb") as infile, open(output_path, "wb") as outfile: + with encrypted_path.open("rb") as infile, output_path.open("wb") as outfile: # Use KAS client for key unwrapping reader = sdk.load_tdf(infile.read()) # TDFReader is a dataclass with payload attribute @@ -140,9 +140,9 @@ def verify_encrypt_decrypt_file() -> None: assert decrypted_path.exists() # Validate content - with open(input_file, "rb") as f: + with input_file.open("rb") as f: original = f.read() - with open(decrypted_path, "rb") as f: + with decrypted_path.open("rb") as f: decrypted = f.read() assert original == decrypted, "Decrypted content doesn't match original" diff --git a/tests/test_zip_reader.py b/tests/test_zip_reader.py index 11af01c..cde645e 100644 --- a/tests/test_zip_reader.py +++ b/tests/test_zip_reader.py @@ -1,6 +1,7 @@ import io import random import unittest +from pathlib import Path from otdf_python.zip_reader import ZipReader from otdf_python.zip_writer import ZipWriter @@ -33,7 +34,7 @@ def test_extract(self): reader = ZipReader(io.BytesIO(data)) with tempfile.TemporaryDirectory() as tmpdir: out_path = reader.extract("baz.txt", tmpdir) - with open(out_path, "rb") as f: + with Path(out_path).open("rb") as f: self.assertEqual(f.read(), b"baz") reader.close() diff --git a/uv.lock b/uv.lock index dce9354..9367acb 100644 --- a/uv.lock +++ b/uv.lock @@ -1063,28 +1063,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.10" +version = "0.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" }, - { url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" }, - { url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" }, - { url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" }, - { url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" }, - { url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" }, - { url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" }, - { url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" }, - { url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" }, - { url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" }, - { url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" }, - { url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" }, - { url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" }, - { url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" }, + { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" }, + { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" }, + { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" }, + { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" }, + { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" }, + { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" }, + { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" }, + { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" }, + { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" }, + { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" }, ] [[package]]