Skip to content

Commit

Permalink
🔖 Release 1.1.1
Browse files Browse the repository at this point in the history
## Changed
- Bumped `pyo3` to version 0.20.3

## Fixed
- Certifi fallback loading
- Exception if the underlying rust library could not access the OS store
  • Loading branch information
Ousret committed Apr 29, 2024
1 parent cfb12bf commit fc5d7e3
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 59 deletions.
50 changes: 33 additions & 17 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
# This file is autogenerated by maturin v1.2.3
# To update, run
#
# maturin generate-ci github
#
name: CI

on:
Expand Down Expand Up @@ -34,7 +29,7 @@ jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest ] # windows-latest
os: [ubuntu-latest, macos-12, windows-latest ] # windows-latest
python_version: ['3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10']
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -75,15 +70,18 @@ jobs:
- test
- lint
strategy:
fail-fast: false
matrix:
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le, ppc64, i686]
python_version: ['3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10']
manylinux: ['auto', 'musllinux_1_1']
exclude:
- manylinux: musllinux_1_1
target: ppc64le
- manylinux: musllinux_1_1
target: s390x
- manylinux: musllinux_1_1
target: ppc64
- manylinux: musllinux_1_1
target: ppc64le

steps:
- uses: actions/checkout@v3
Expand All @@ -109,15 +107,31 @@ jobs:
- lint
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
target: [x64,]
target: [x64, aarch64, x86]
python_version: ['3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10']
exclude:
- target: aarch64
python_version: 'pypy-3.7'
- target: aarch64
python_version: 'pypy-3.8'
- target: aarch64
python_version: 'pypy-3.9'
- target: aarch64
python_version: 'pypy-3.10'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
if: matrix.target != 'x86'
with:
python-version: ${{ matrix.python_version }}
architecture: ${{ matrix.target }}
architecture: x64
- uses: actions/setup-python@v4
if: matrix.target == 'x86'
with:
python-version: ${{ matrix.python_version }}
architecture: x86
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
Expand All @@ -134,10 +148,11 @@ jobs:
needs:
- test
- lint
runs-on: macos-latest
runs-on: macos-12
strategy:
fail-fast: false
matrix:
target: [x86_64, aarch64]
target: [x86_64, aarch64, universal2]
python_version: ['3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10']
steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -197,7 +212,7 @@ jobs:
path: dist/*.whl

checksum:
name: Compute hashes
name: compute hashes
runs-on: ubuntu-latest
needs: [linux, windows, macos, sdist, universal]
if: "startsWith(github.ref, 'refs/tags/')"
Expand Down Expand Up @@ -231,18 +246,19 @@ jobs:
upload-assets: true

release:
name: Release
name: release
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/')"
needs: provenance
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing *
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to wassima will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## 1.1.1 (2024-04-29)

## Changed
- Bumped `pyo3` to version 0.20.3

## Fixed
- Certifi fallback loading
- Exception if the underlying rust library could not access the OS store

## 1.1.0 (2024-02-20)

## Changed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wassima"
version = "1.1.0"
version = "1.1.1"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
13 changes: 8 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
use pyo3::{prelude::*, types::PyBytes};
use pyo3::exceptions::PyRuntimeError;
use rustls_native_certs;

/// Retrieve a list of system DER root CAs
#[pyfunction]
fn root_der_certificates(py: Python) -> PyResult<Vec<&PyBytes>> {
let mut roots = Vec::new();
let certs = rustls_native_certs::load_native_certs();

for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
let vec_to_string = cert.as_ref().to_vec();
if certs.is_err() {
return Err(PyRuntimeError::new_err("unable to extract root certificates"));
}

let py_bytes = PyBytes::new(py, &vec_to_string);
roots.push(py_bytes);
for cert in certs.unwrap() {
roots.push(PyBytes::new(py, &cert.as_ref().to_vec()));
}

Ok(roots)
return Ok(roots);
}

#[pymodule]
Expand Down
110 changes: 80 additions & 30 deletions wassima/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"""
from __future__ import annotations

import contextlib
import os
import ssl
import typing
from functools import lru_cache
from os import environ
from ssl import DER_cert_to_PEM_cert
from threading import RLock

from ._version import VERSION, __version__
Expand All @@ -22,30 +23,85 @@
#: Lock for shared register-ca
_USER_APPEND_CA_LOCK = RLock()


def _split_certifi_bundle(data: bytes) -> list[str]:
line_ending = b"\n" if b"-----\r\n" not in data else b"\r\n"
boundary = b"-----END CERTIFICATE-----" + line_ending

certificates = []

for chunk in data.split(boundary):
if chunk:
start_marker = chunk.find(b"-----BEGIN CERTIFICATE-----" + line_ending)
if start_marker == -1:
break
pem_reconstructed = b"".join([chunk[start_marker:], boundary]).decode(
"ascii"
)
certificates.append(pem_reconstructed)

return certificates


@contextlib.contextmanager
def _shelve_environment(*keys: str) -> typing.Generator[None, None, None]:
ctx = {}

for key in keys:
try:
ctx[key] = os.environ.pop(key)
except KeyError:
...

try:
yield
finally:
for key in ctx:
os.environ[key] = ctx[key]


def _certifi_fallback() -> list[bytes]:
import certifi # type: ignore

certs: list[bytes] = []

try:
with open(certifi.where(), "rb") as fp:
for pem_cert in _split_certifi_bundle(fp.read()):
certs.append(ssl.PEM_cert_to_DER_cert(pem_cert))
except (OSError, PermissionError) as e:
warnings.warn(
"Unable to fallback on Certifi due to an error trying to read the vendored CA bundle. "
f"{str(e)}"
)
return certs

return certs


try:
from ._rustls import root_der_certificates as _root_der_certificates

@lru_cache()
def root_der_certificates() -> list[bytes]:
try:
bck = environ.pop("SSL_CERT_FILE")
except KeyError:
bck = None

try:
with _shelve_environment("SSL_CERT_FILE", "SSL_CERT_DIR"):
with _USER_APPEND_CA_LOCK:
return _root_der_certificates() + _MANUALLY_REGISTERED_CA
finally:
if bck is not None:
environ["SSL_CERT_FILE"] = bck
try:
return _root_der_certificates() + _MANUALLY_REGISTERED_CA
except RuntimeError:
try:
fallback_certificates = _certifi_fallback()
except ImportError:
fallback_certificates = []

return fallback_certificates + _MANUALLY_REGISTERED_CA

RUSTLS_LOADED = True
except ImportError:
RUSTLS_LOADED = False
from ssl import PEM_cert_to_DER_cert

try:
import certifi # type: ignore
import certifi
except ImportError:
certifi = None

Expand All @@ -54,27 +110,21 @@ def root_der_certificates() -> list[bytes]:
import warnings

warnings.warn(
f"""Unable to access your system root CAs. Your particular interpreter and/or
operating system ({platform.python_implementation()}, {platform.uname()}, {platform.python_version()})
is not supported. While it is not ideal, you may circumvent that warning by having certifi
installed in your environment. Run `python -m pip install certifi`.
You may also open an issue at https://github.com/jawah/wassima/issues to get your platform supported.""",
"Unable to access your system root CAs. Your particular interpreter and/or "
f"operating system ({platform.python_implementation()}, {platform.uname()}, {platform.python_version()}) "
"is not supported. While it is not ideal, you may circumvent that warning by having certifi "
"installed in your environment. Run `python -m pip install certifi`. "
"You may also open an issue at https://github.com/jawah/wassima/issues to get your platform supported.",
RuntimeWarning,
)

@lru_cache()
def root_der_certificates() -> list[bytes]:
if certifi is None:
try:
return _certifi_fallback()
except ImportError:
return []

certs: list[bytes] = []

with open(certifi.where(), encoding="utf-8") as fp:
for pem_cert in fp.read().split("\n\n"):
certs.append(PEM_cert_to_DER_cert(pem_cert))

return certs


@lru_cache()
def root_pem_certificates() -> list[str]:
Expand All @@ -85,7 +135,7 @@ def root_pem_certificates() -> list[str]:
pem_certs = []

for bin_cert in root_der_certificates():
pem_certs.append(DER_cert_to_PEM_cert(bin_cert))
pem_certs.append(ssl.DER_cert_to_PEM_cert(bin_cert))

return pem_certs

Expand All @@ -104,7 +154,7 @@ def register_ca(pem_or_der_certificate: bytes | str) -> None:
"""
with _USER_APPEND_CA_LOCK:
if isinstance(pem_or_der_certificate, str):
pem_or_der_certificate = PEM_cert_to_DER_cert(pem_or_der_certificate)
pem_or_der_certificate = ssl.PEM_cert_to_DER_cert(pem_or_der_certificate)

if pem_or_der_certificate not in _MANUALLY_REGISTERED_CA:
_MANUALLY_REGISTERED_CA.append(pem_or_der_certificate)
Expand Down
5 changes: 1 addition & 4 deletions wassima/_rustls.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from __future__ import annotations

import typing

def root_der_certificates() -> typing.List[bytes]:
def root_der_certificates() -> list[bytes]:
"""
Retrieve a list of root certificate from your operating system trust store.
They will be DER (binary) encoded.
"""
...
2 changes: 1 addition & 1 deletion wassima/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

__version__ = "1.1.0"
__version__ = "1.1.1"
VERSION = __version__.split(".")

0 comments on commit fc5d7e3

Please sign in to comment.