Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.6
rev: v0.14.8
hooks:
# Run the linter.
- id: ruff-check
Expand Down
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"recommendations": [
"golang.go"
"charliermarsh.ruff"
]
}
10 changes: 4 additions & 6 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
Pytest configuration and fixtures for the OpenTDF Python SDK tests.
"""Pytest configuration and fixtures for the OpenTDF Python SDK tests.

This module contains pytest hooks and fixtures that will be automatically
loaded by pytest when running tests.
Expand All @@ -14,13 +13,13 @@

@pytest.fixture(scope="session")
def project_root(request) -> Path:
"""Get project root directory."""
return request.config.rootpath # Project root


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
Hook that runs after each test phase (setup, call, teardown).
"""Collect server logs when test fails after each test phase.

This hook automatically collects server logs when a test fails.
"""
Expand Down Expand Up @@ -53,8 +52,7 @@ def pytest_runtest_makereport(item, call):

@pytest.fixture
def collect_server_logs():
"""
Fixture that provides a function to manually collect server logs.
"""Fixture that provides a function to manually collect server logs.

Usage:
def test_something(collect_server_logs):
Expand Down
6 changes: 2 additions & 4 deletions otdf-python-proto/scripts/generate_connect_proto.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
"""
Enhanced script to generate Python Connect RPC clients from .proto definitions.
"""Enhanced script to generate Python Connect RPC clients from .proto definitions.

This script:
1. Downloads the latest proto files from OpenTDF platform
Expand Down Expand Up @@ -200,8 +199,7 @@ def create_init_files(generated_dir: Path) -> None:


def _fix_ignore_if_default_value(proto_files_dir):
"""
TODO: Fix buf validation: Updated the proto files to use the correct enum value:
"""TODO: Fix buf validation: Updated the proto files to use the correct enum value:

Changed IGNORE_IF_DEFAULT_VALUE → IGNORE_IF_ZERO_VALUE in:
attributes.proto
Expand Down
50 changes: 28 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ norecursedirs = ["otdf-python-proto"]

[tool.ruff]
line-length = 88
target-version = "py310"

# See https://docs.astral.sh/ruff/rules/
# for rule information.
Expand All @@ -80,28 +81,33 @@ lint.ignore = [
"E501",
]
lint.select = [
# pycodestyle checks.
"E",
"W",
# pyflakes checks.
"F",
# flake8-bugbear checks.
"B",
# flake8-comprehensions checks.
"C4",
# McCabe complexity
"C90",
# isort
"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)
"RUF", # Ruff-specific rules
"FURB", # refurb (FURB)
"PT018", # flake8-pytest-style (pytest style)
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"C90", # McCabe complexity
"D", # pydocstyle
"DOC", # pydoclint
"E", # pycodestyle errors
"F", # pyflakes
"FURB", # refurb
"I", # isort
"PERF", # performance
"PT018", # pytest style
"PTH", # pathlib
"Q", # flake8-quotes
"RUF", # ruff-specific
"SIM", # flake8-simplify
"UP", # pyupgrade
"W", # pycodestyle warnings
]
# Ignore generated files
extend-exclude = ["otdf-python-proto/src/"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["D100", "D101", "D102", "D103", "D107", "D400", "D401", "D415"]
"otdf-python-proto/**" = ["D"] # Ignore all D (docstring) rules for generated proto files

# TODO: Remaining work - 4 buckets to fix (140 errors remaining):
# Bucket #1: D102 (missing method docstrings) - 98 errors
# Bucket #2: D105 (missing magic method docstrings) - 23 errors
# Bucket #3: D205 (blank line formatting), D103, D417, D104 - 19 errors
"src/**" = ["D102", "D105", "D205"]
3 changes: 1 addition & 2 deletions src/otdf_python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
OpenTDF Python SDK
"""OpenTDF Python SDK.

A Python implementation of the OpenTDF SDK for working with Trusted Data Format (TDF) files.
Provides both programmatic APIs and command-line interface for encryption and decryption.
Expand Down
3 changes: 1 addition & 2 deletions src/otdf_python/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
"""
Main entry point for running otdf_python as a module.
"""Main entry point for running otdf_python as a module.

This allows the package to be run with `python -m otdf_python` and properly
handles the CLI interface without import conflicts.
Expand Down
8 changes: 3 additions & 5 deletions src/otdf_python/address_normalizer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
Address normalization utilities for OpenTDF.
"""
"""Address normalization utilities for OpenTDF."""

import logging
import re
Expand All @@ -12,8 +10,7 @@


def normalize_address(url_string: str, use_plaintext: bool) -> str:
"""
Normalize a URL address to ensure it has the correct scheme and port.
"""Normalize a URL address to ensure it has the correct scheme and port.

Args:
url_string: The URL string to normalize
Expand All @@ -24,6 +21,7 @@ def normalize_address(url_string: str, use_plaintext: bool) -> str:

Raises:
SDKException: If there's an error parsing or creating the URL

"""
scheme = "http" if use_plaintext else "https"

Expand Down
8 changes: 8 additions & 0 deletions src/otdf_python/aesgcm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"""AES-GCM encryption and decryption functionality."""

import os

from cryptography.hazmat.primitives.ciphers.aead import AESGCM


class AesGcm:
"""AES-GCM encryption and decryption operations."""

GCM_NONCE_LENGTH = 12
GCM_TAG_LENGTH = 16

def __init__(self, key: bytes):
"""Initialize AES-GCM cipher with key."""
if not key or len(key) not in (16, 24, 32):
raise ValueError("Invalid key size for GCM encryption")
self.key = key
Expand All @@ -17,7 +22,10 @@ def get_key(self) -> bytes:
return self.key

class Encrypted:
"""Encrypted data with initialization vector and ciphertext."""

def __init__(self, iv: bytes, ciphertext: bytes):
"""Initialize encrypted data."""
self.iv = iv
self.ciphertext = ciphertext

Expand Down
21 changes: 21 additions & 0 deletions src/otdf_python/assertion_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Assertion configuration for TDF."""

from enum import Enum, auto
from typing import Any


class Type(Enum):
"""Assertion type enumeration."""

HANDLING_ASSERTION = "handling"
BASE_ASSERTION = "base"

Expand All @@ -11,6 +15,8 @@ def __str__(self):


class Scope(Enum):
"""Assertion scope enumeration."""

TRUSTED_DATA_OBJ = "tdo"
PAYLOAD = "payload"

Expand All @@ -19,12 +25,16 @@ def __str__(self):


class AssertionKeyAlg(Enum):
"""Assertion key algorithm enumeration."""

RS256 = auto()
HS256 = auto()
NOT_DEFINED = auto()


class AppliesToState(Enum):
"""Assertion applies-to state enumeration."""

ENCRYPTED = "encrypted"
UNENCRYPTED = "unencrypted"

Expand All @@ -33,14 +43,19 @@ def __str__(self):


class BindingMethod(Enum):
"""Assertion binding method enumeration."""

JWS = "jws"

def __str__(self):
return self.value


class AssertionKey:
"""Assertion signing key configuration."""

def __init__(self, alg: AssertionKeyAlg, key: Any):
"""Initialize assertion key."""
self.alg = alg
self.key = key

Expand All @@ -49,7 +64,10 @@ def is_defined(self):


class Statement:
"""Assertion statement with format, schema, and value."""

def __init__(self, format: str, schema: str, value: str):
"""Initialize assertion statement."""
self.format = format
self.schema = schema
self.value = value
Expand All @@ -67,6 +85,8 @@ def __hash__(self):


class AssertionConfig:
"""TDF assertion configuration."""

def __init__(
self,
id: str,
Expand All @@ -76,6 +96,7 @@ def __init__(
statement: Statement,
signing_key: AssertionKey | None = None,
):
"""Initialize assertion configuration."""
self.id = id
self.type = type
self.scope = scope
Expand Down
Loading
Loading