Skip to content

Commit 06c94c9

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent ae9adee commit 06c94c9

File tree

13 files changed

+1229
-215
lines changed

13 files changed

+1229
-215
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
version: 2
77
updates:
8-
- package-ecosystem: "pip" # See documentation for possible values
8+
- package-ecosystem: "uv" # See documentation for possible values
99
directory: "/" # Location of package manifests
1010
schedule:
11-
interval: "daily"
11+
interval: "weekly"

.github/workflows/lint.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
workflow_dispatch: # Allows manual triggering
9+
10+
jobs:
11+
lint:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
16+
steps:
17+
- uses: actions/checkout@main
18+
19+
- name: Install the latest version of uv
20+
uses: astral-sh/setup-uv@v5
21+
with:
22+
enable-cache: true
23+
24+
- name: Lint with ruff
25+
run: |
26+
uvx --with cryptography ruff check
27+
28+
- name: Type check with mypy
29+
run: |
30+
uvx --with cryptography mypy httpx_pkcs12

.github/workflows/publish.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
2+
3+
on: push
4+
5+
jobs:
6+
build:
7+
name: Build distribution 📦
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v4
12+
with:
13+
persist-credentials: false
14+
- name: Set up Python
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.x"
18+
- name: Install pypa/build
19+
run: >-
20+
python3 -m
21+
pip install
22+
build
23+
--user
24+
- name: Build a binary wheel and a source tarball
25+
run: python3 -m build
26+
- name: Store the distribution packages
27+
uses: actions/upload-artifact@v4
28+
with:
29+
name: python-package-distributions
30+
path: dist/
31+
32+
publish-to-pypi:
33+
name: >-
34+
Publish Python 🐍 distribution 📦 to PyPI
35+
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
36+
needs:
37+
- build
38+
runs-on: ubuntu-latest
39+
environment:
40+
name: pypi
41+
url: https://pypi.org/p/httpx-pkcs12
42+
permissions:
43+
id-token: write # IMPORTANT: mandatory for trusted publishing
44+
45+
steps:
46+
- name: Download all the dists
47+
uses: actions/download-artifact@v4
48+
with:
49+
name: python-package-distributions
50+
path: dist/
51+
- name: Publish distribution 📦 to PyPI
52+
uses: pypa/gh-action-pypi-publish@release/v1
53+
54+
github-release:
55+
name: >-
56+
Sign the Python 🐍 distribution 📦 with Sigstore
57+
and upload them to GitHub Release
58+
needs:
59+
- publish-to-pypi
60+
runs-on: ubuntu-latest
61+
62+
permissions:
63+
contents: write # IMPORTANT: mandatory for making GitHub Releases
64+
id-token: write # IMPORTANT: mandatory for sigstore
65+
66+
steps:
67+
- name: Download all the dists
68+
uses: actions/download-artifact@v4
69+
with:
70+
name: python-package-distributions
71+
path: dist/
72+
- name: Sign the dists with Sigstore
73+
uses: sigstore/gh-action-sigstore-python@v3.0.0
74+
with:
75+
inputs: >-
76+
./dist/*.tar.gz
77+
./dist/*.whl
78+
- name: Create GitHub Release
79+
env:
80+
GITHUB_TOKEN: ${{ github.token }}
81+
run: >-
82+
gh release create
83+
"$GITHUB_REF_NAME"
84+
--repo "$GITHUB_REPOSITORY"
85+
--notes ""
86+
- name: Upload artifact signatures to GitHub Release
87+
env:
88+
GITHUB_TOKEN: ${{ github.token }}
89+
# Upload to GitHub Release using the `gh` CLI.
90+
# `dist/` contains the built packages, and the
91+
# sigstore-produced signatures and certificates.
92+
run: >-
93+
gh release upload
94+
"$GITHUB_REF_NAME" dist/**
95+
--repo "$GITHUB_REPOSITORY"

.github/workflows/python-publish.yml

Lines changed: 0 additions & 28 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
workflow_dispatch: # Allows manual triggering
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
python-version: ["3.10", "3.11", "3.12"]
17+
18+
steps:
19+
- uses: actions/checkout@main
20+
21+
- name: Install the latest version of uv
22+
uses: astral-sh/setup-uv@v5
23+
with:
24+
enable-cache: true
25+
python-version: ${{ matrix.python-version }}
26+
27+
- name: Install dependencies
28+
run: |
29+
uv sync --frozen --extra dev
30+
31+
- name: Test with pytest
32+
run: |
33+
uv run pytest --cov=httpx_pkcs12 --cov-report=xml
34+
35+
- name: Upload coverage to Codecov
36+
uses: codecov/codecov-action@v3
37+
with:
38+
file: ./coverage.xml
39+
fail_ci_if_error: false

.gitignore

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
.vscode
22
.idea
3-
dist
3+
dist
4+
.venv
5+
__pycache__
6+
.pytest_cache
7+
.ruff_cache
8+
*.egg-info
9+
*.egg
10+
*.pdb
11+
*.log
12+
*.coverage
13+
*.cover
14+
*.hypothesis
15+
coverage*
16+
htmlcov

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12

README.md

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,119 @@
1-
## httpx-pkcs12
1+
# httpx-pkcs12
22

3-
Addon which activates PKCS12 certificates usage with HTTPX client.
3+
Enhanced PKCS12 (PFX/P12) certificate support for the HTTPX Python client. This package allows you to easily use PKCS12 certificates with HTTPX for client certificate authentication.
4+
5+
## Features
6+
7+
- Simple API for loading PKCS12 certificates
8+
- Support for certificate validation
9+
- Extract certificate information
10+
- Works with both file paths and certificate data
11+
- Accepts passwords as strings or bytes
12+
- Proper cleanup of temporary certificate files
13+
- Comprehensive type hints
14+
15+
## Installation
16+
17+
```bash
18+
pip install httpx-pkcs12
19+
```
20+
21+
Or with uv:
22+
23+
```bash
24+
uv add httpx-pkcs12
25+
```
426

527
## Usage
28+
29+
### Basic Usage
30+
631
```python
7-
with open('path/to/your/cert', 'rb') as f:
8-
cert_contents = f.read()
9-
password = 'your-secret-password'
32+
import httpx
33+
from httpx_pkcs12 import create_ssl_context
1034

11-
context = create_ssl_context(cert_contents, password)
35+
# Load certificate from file
36+
context = create_ssl_context(
37+
'path/to/your/cert.p12',
38+
password='your-secret-password'
39+
)
1240

13-
# async version
41+
# With async client
1442
async with httpx.AsyncClient(verify=context) as client:
15-
response = ...
43+
response = await client.get('https://api.example.com')
44+
45+
# With sync client
46+
with httpx.Client(verify=context) as client:
47+
response = client.get('https://api.example.com')
48+
49+
# Or for a one-off request
50+
response = httpx.get('https://api.example.com', verify=context)
51+
```
52+
53+
### Advanced Usage
54+
55+
```python
56+
from httpx_pkcs12 import create_ssl_context, get_certificate_info
57+
58+
# Load certificate directly from bytes
59+
with open('path/to/cert.p12', 'rb') as f:
60+
cert_data = f.read()
61+
62+
# Create context without validation (for expired certs)
63+
context = create_ssl_context(
64+
cert_data,
65+
password='your-password',
66+
validate=False
67+
)
68+
69+
# Get certificate information
70+
not_before, not_after, common_name, alt_names = get_certificate_info(
71+
'path/to/cert.p12',
72+
password='your-password'
73+
)
74+
75+
print(f"Certificate: {common_name}")
76+
print(f"Valid from: {not_before}")
77+
print(f"Valid until: {not_after}")
78+
print(f"Alternative names: {', '.join(alt_names)}")
79+
```
80+
81+
## API Reference
82+
83+
### `create_ssl_context(pkcs12_data, password=None, validate=True)`
84+
85+
Creates an SSL context from PKCS12 data.
86+
87+
- **pkcs12_data**: The PKCS12 certificate data as bytes or a path to the file
88+
- **password**: Password to decrypt the PKCS12 data (string or bytes)
89+
- **validate**: Whether to validate certificate expiration
90+
- **Returns**: An SSLContext object configured with the certificate
91+
92+
### `load_pkcs12_from_file(certificate_path, password=None, validate=True)`
93+
94+
Convenience function to create an SSL context from a PKCS12 certificate file.
95+
96+
- **certificate_path**: Path to the PKCS12 certificate file
97+
- **password**: Password to decrypt the PKCS12 data
98+
- **validate**: Whether to validate certificate expiration
99+
- **Returns**: An SSLContext object configured with the certificate
100+
101+
### `get_certificate_info(pkcs12_data, password=None)`
102+
103+
Extracts and returns information about the certificate.
104+
105+
- **pkcs12_data**: The PKCS12 certificate data or file path
106+
- **password**: Password to decrypt the PKCS12 data
107+
- **Returns**: Tuple with (not_valid_before, not_valid_after, common_name, alt_names)
108+
109+
## Error Handling
110+
111+
The package raises the following exceptions:
112+
113+
- `CertificateError`: For certificate validation issues
114+
- `ValueError`: For invalid input data
115+
- `IOError`: For file operation failures
116+
117+
## License
16118

17-
# or sync version
18-
response = httpx.get(..., verify=context)
19-
```
119+
MIT

0 commit comments

Comments
 (0)