This release is a pre-release and may not be stable for production use.
jentic-openapi-common
Common utilities for OpenAPI tools packages. This package provides shared functionality using PEP 420 namespace packages as contribution points.
Installation
uv add jentic-openapi-common
Modules
uri
URI/URL/path utilities for working with OpenAPI document references.
Available functions:
is_uri_like(s: str | None) -> bool- Check if a string looks like a URI/URL/pathis_http_https_url(s: str | None) -> bool- Check if string is an HTTP(S) URLis_file_uri(s: str | None) -> bool- Check if string is a file:// URIis_path(s: str | None) -> bool- Check if string is a filesystem path (not a URL)resolve_to_absolute(uri: str, base_uri: str | None = None) -> str- Resolve relative URIs to absolute
Exceptions:
URIResolutionError- Raised when URI resolution fails
path_security
Path security utilities for validating and securing filesystem access. Provides defense-in-depth protection against path traversal attacks, directory escapes, and unauthorized file access.
Available functions:
validate_path(path, *, allowed_base=None, allowed_extensions=None, resolve_symlinks=True, as_string=True) -> str | Path- Validate and canonicalize a filesystem path with security checks. Returnsstrby default, orPathwhenas_string=False
Exceptions:
PathSecurityError- Base exception for path security violationsPathTraversalError- Path attempts to escape allowed base directoryInvalidExtensionError- Path has disallowed file extensionSymlinkSecurityError- Path contains symlinks when not allowed or symlink escapes boundary
subproc
Subprocess execution utilities with enhanced error handling and cross-platform support.
version_detection
OpenAPI/Swagger version detection utilities. Provides functions to detect and extract version information from OpenAPI documents in text (YAML/JSON) or Mapping formats.
Available functions:
get_version(document: str | Mapping[str, Any]) -> str | None- Extract version string from document (e.g., "3.0.4", "2.0")is_openapi_20(document: str | Mapping[str, Any]) -> bool- Check if document is OpenAPI 2.0 (Swagger 2.0)is_openapi_30(document: str | Mapping[str, Any]) -> bool- Check if document is OpenAPI 3.0.xis_openapi_31(document: str | Mapping[str, Any]) -> bool- Check if document is OpenAPI 3.1.xis_openapi_32(document: str | Mapping[str, Any]) -> bool- Check if document is OpenAPI 3.2.x
Version Detection Behavior:
- Text input: Validates against regex patterns, only returns/matches valid versions per specification
- Mapping input:
get_version()returns whatever version string is present (for extraction/inspection)is_openapi_*()validates against patterns (for version checking)
Usage Examples
URI Utilities
from jentic.apitools.openapi.common.uri import (
is_uri_like,
is_http_https_url,
is_file_uri,
is_path,
resolve_to_absolute,
URIResolutionError,
)
# Check URI types
is_uri_like("https://example.com/spec.yaml") # True
is_http_https_url("https://example.com/spec.yaml") # True
is_file_uri("file:///home/user/spec.yaml") # True
is_path("/home/user/spec.yaml") # True
is_path("https://example.com/spec.yaml") # False
# Resolve relative URIs
absolute = resolve_to_absolute("../spec.yaml", "/home/user/project/docs/")
# Returns: "/home/user/project/spec.yaml"
absolute = resolve_to_absolute("spec.yaml") # Resolves against current working directory
Path Security
from pathlib import Path
from jentic.apitools.openapi.common.path_security import (
validate_path,
PathSecurityError,
PathTraversalError,
InvalidExtensionError,
SymlinkSecurityError,
)
# Basic validation - converts to absolute path (returns string by default)
safe_path = validate_path("./specs/openapi.yaml")
print(safe_path) # '/current/working/dir/specs/openapi.yaml'
print(type(safe_path)) # <class 'str'>
# Request Path object with as_string=False
safe_path_obj = validate_path("./specs/openapi.yaml", as_string=False)
print(safe_path_obj) # Path('/current/working/dir/specs/openapi.yaml')
print(type(safe_path_obj)) # <class 'pathlib.Path'>
# Return type control with as_string parameter
# - as_string=True (default): Returns str - best for subprocess commands
# - as_string=False: Returns Path - best for file operations with pathlib
# Example: Using with subprocess commands (default string return)
import subprocess
doc_path = validate_path("./specs/openapi.yaml")
subprocess.run(["cat", doc_path]) # Works directly, no str() conversion needed
# Example: Using with pathlib operations (Path return)
from pathlib import Path
doc_path = validate_path("./specs/openapi.yaml", as_string=False)
if doc_path.exists():
content = doc_path.read_text() # Path methods available
# Boundary enforcement - restrict access to specific directory
try:
safe_path = validate_path(
"/var/app/data/spec.yaml",
allowed_base="/var/app",
)
print(f"Access granted: {safe_path}")
except PathTraversalError as e:
print(f"Access denied: {e}")
# Block directory traversal attacks
try:
safe_path = validate_path(
"/var/app/../../../etc/passwd",
allowed_base="/var/app",
)
except PathTraversalError:
print("Path traversal attack blocked!")
# Extension validation - whitelist approach
try:
safe_path = validate_path(
"spec.yaml",
allowed_extensions=(".yaml", ".yml", ".json"),
)
print(f"Valid extension: {safe_path}")
except InvalidExtensionError:
print("Invalid file extension")
# Combined security checks (recommended for web services)
try:
safe_path = validate_path(
user_provided_path,
allowed_base="/var/app/uploads",
allowed_extensions=(".yaml", ".yml", ".json"),
resolve_symlinks=True, # Default: resolve and check symlinks
)
# Safe to use safe_path for file operations
with open(safe_path) as f:
content = f.read()
except PathSecurityError as e:
print(f"Security validation failed: {e}")
Subprocess Execution
Basic Command Execution
from jentic.apitools.openapi.common.subproc import run_subprocess
# Simple command
result = run_subprocess(["echo", "hello"])
print(result.stdout) # "hello\n"
print(result.returncode) # 0
# Command with working directory
result = run_subprocess(["pwd"], cwd="/tmp")
print(result.stdout.strip()) # "/tmp"
Error Handling
from jentic.apitools.openapi.common.subproc import (
run_subprocess,
SubprocessExecutionError
)
# Handle errors manually
result = run_subprocess(["false"]) # Command that exits with code 1
if result.returncode != 0:
print(f"Command failed with code {result.returncode}")
# Automatic error handling
try:
result = run_subprocess(["false"], fail_on_error=True)
except SubprocessExecutionError as e:
print(f"Command {e.cmd} failed: {e}")
Advanced Usage
from jentic.apitools.openapi.common.subproc import (
run_subprocess,
SubprocessExecutionError
)
# Timeout handling
try:
result = run_subprocess(["sleep", "10"], timeout=1)
except SubprocessExecutionError as e:
print("Command timed out")
# Custom encoding
result = run_subprocess(["python", "-c", "print('ñ')"], encoding="utf-8")
print(result.stdout) # "ñ\n"
# Custom environment variables (replaces inherited environment)
import os
custom_env = {**os.environ, "MY_VAR": "value"}
result = run_subprocess(["printenv", "MY_VAR"], env=custom_env)
print(result.stdout.strip()) # "value"
Version Detection
from jentic.apitools.openapi.common.version_detection import (
get_version,
is_openapi_20,
is_openapi_30,
is_openapi_31,
is_openapi_32,
)
# Extract version from text (YAML/JSON)
yaml_doc = """
openapi: 3.0.4
info:
title: Pet Store API
version: 1.0.0
"""
version = get_version(yaml_doc)
print(version) # "3.0.4"
json_doc = '{"openapi": "3.1.2", "info": {"title": "API", "version": "1.0.0"}}'
version = get_version(json_doc)
print(version) # "3.1.2"
# Extract version from Mapping (returns any version string, even if unsupported)
doc = {"openapi": "3.0.4"}
version = get_version(doc)
print(version) # "3.0.4"
# Even unsupported versions are returned from Mapping
doc = {"openapi": "3.0.4-rc1"}
version = get_version(doc)
print(version) # "3.0.4-rc1" (suffix returned as-is)
# But text input validates with regex
version = get_version("openapi: 3.0.4-rc1")
print(version) # None (suffix doesn't match pattern)
# Version checking with predicates (validates for both text and Mapping)
doc_20 = {"swagger": "2.0"}
print(is_openapi_20(doc_20)) # True
print(is_openapi_30(doc_20)) # False
doc_30 = {"openapi": "3.0.4"}
print(is_openapi_30(doc_30)) # True
print(is_openapi_31(doc_30)) # False
doc_31 = {"openapi": "3.1.2"}
print(is_openapi_31(doc_31)) # True
print(is_openapi_32(doc_31)) # False
doc_32 = {"openapi": "3.2.0"}
print(is_openapi_32(doc_32)) # True
# Predicates validate strictly
doc_suffix = {"openapi": "3.0.4-rc1"}
print(is_openapi_30(doc_suffix)) # False (suffix rejected)
doc_unsupported = {"openapi": "3.3.0"}
print(is_openapi_32(doc_unsupported)) # False (unsupported version)
# Works with YAML text too
yaml_text = "openapi: 3.0.4\ninfo:\n title: API"
print(is_openapi_30(yaml_text)) # True
# Works with JSON text
json_text = '{"openapi": "3.1.2"}'
print(is_openapi_31(json_text)) # True
Testing
Run the test suite:
uv run --package jentic-openapi-common pytest packages/jentic-openapi-common -v
Metadata
Release files for jentic-openapi-common 1.0.0a57
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jentic_openapi_common-1.0.0a57.tar.gz | 15.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jentic_openapi_common-1.0.0a57-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.3 kB
Release files / jentic_openapi_common-1.0.0a57.tar.gz
| Download URL | jentic_openapi_common-1.0.0a57.tar.gz |
|---|---|
| Size | 15.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6940edec224c8f56e069e653be29959e36d6f58c5d3eb06519db689298845175
|
|
BLAKE2b-256 checksum How to use checksums |
c65660b07720ef30c76287e9d31cdeedd2f8daa1017b981e013522f004408755
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency logRelease files / jentic_openapi_common-1.0.0a57-py3-none-any.whl
| Download URL | jentic_openapi_common-1.0.0a57-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ce61a00b43f0eb7b978495eab28d7136ef13d2e70aca6b1438624b74653dcfc5
|
|
BLAKE2b-256 checksum How to use checksums |
a63ad24e71cc4546201fd5c3229652872d0af8dd547b0cf6a47c0441ecb7468c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency log