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
115 changes: 115 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Python Bindings

on:
push:
branches: [main]
paths:
- 'python/**'
- 'client/**'
- '.github/workflows/python.yml'
pull_request:
branches: [main]
paths:
- 'python/**'
- 'client/**'
- '.github/workflows/python.yml'
release:
types: [published]

jobs:
test:
name: Test Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gnome-keyring dbus-x11

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Python dependencies
working-directory: python
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Build and install Python package
working-directory: python
run: |
maturin build --release
pip install ../target/wheels/oo7_python-*.whl

- name: Start gnome-keyring
run: |
dbus-run-session -- sh -c '
echo "test" | gnome-keyring-daemon --unlock
gnome-keyring-daemon --start --daemonize --components=secrets
export $(gnome-keyring-daemon --start)
cd python && pytest -v
'

lint:
name: Lint Python bindings
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'

- name: Install ruff
run: pip install ruff

- name: Check Python formatting
working-directory: python
run: ruff format --check tests/

- name: Check Python linting
working-directory: python
run: ruff check tests/

publish:
name: Publish to PyPI
runs-on: ubuntu-latest
if: github.event_name == 'release'
needs: [test, lint]
permissions:
id-token: write

steps:
- uses: actions/checkout@v6

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
working-directory: python
command: build
args: --release --out dist --interpreter 3.8 3.9 3.10 3.11 3.12 3.13

- name: Publish to PyPI
uses: PyO3/maturin-action@v1
with:
working-directory: python
command: upload
args: --non-interactive --skip-existing dist/*
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
#Cargo.lock
cli/target
coverage/
python/.venv
python/*.so
python/**/__pycache__
113 changes: 113 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"cli",
"pam",
"portal",
"python",
"server",
]

Expand Down
2 changes: 2 additions & 0 deletions coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ grcov coverage-raw/combined.info \
--branch \
--ignore-not-existing \
--ignore "**/portal/*" \
--ignore "**/python/*" \
--ignore "**/cli/*" \
--ignore "**/pam/*" \
--ignore "**/tests/*" \
Expand All @@ -79,6 +80,7 @@ grcov coverage-raw/combined.info \
--branch \
--ignore-not-existing \
--ignore "**/portal/*" \
--ignore "**/python/*" \
--ignore "**/cli/*" \
--ignore "**/pam/*" \
--ignore "**/tests/*" \
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ allow = [
"Apache-2.0", # rpassword by cli only
"BSD-3-Clause", # used by subtle -> digest
"Unicode-3.0", # used by icu_collections -> url
"Apache-2.0 WITH LLVM-exception", # target-lexicon -> pyo3
]

[sources]
Expand Down
26 changes: 26 additions & 0 deletions python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "oo7-python"
version.workspace = true
edition.workspace = true
authors.workspace = true
keywords.workspace = true
categories.workspace = true
repository.workspace = true
homepage.workspace = true
license.workspace = true
rust-version.workspace = true
description = "Python bindings for oo7"

[lib]
name = "oo7"
crate-type = ["cdylib"]

[dependencies]
oo7_rs = { package = "oo7", path = "../client", version = "0.6" }
pyo3 = { version = "0.24", features = ["extension-module", "abi3-py38"] }
pyo3-async-runtimes = { version = "0.24", features = ["tokio-runtime"] }
tokio = { version = "1", features = ["rt-multi-thread"] }

[profile.release]
lto = true
codegen-units = 1
57 changes: 57 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# oo7 Python Bindings

Python bindings for [oo7](../client/), providing access to Secret Service API on Linux. Automatically uses a file-based keyring when running in a sandboxed environment.

## Installation

```bash
cd python
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
maturin develop
```

## Usage

```python
import asyncio
import oo7

async def main():
# Create keyring
keyring = await oo7.Keyring.new()

# Store a secret
await keyring.create_item(
"My Password",
{"application": "myapp", "username": "alice"},
b"secret-password",
replace=True
)

# Search for items
items = await keyring.search_items({"application": "myapp"})
for item in items:
secret = await item.secret()
print(f"Secret: {secret}")

# Clean up
await keyring.delete({"application": "myapp"})

asyncio.run(main())
```

## Running Tests

```bash
pytest
```

## Examples

See `tests/test_keyring.py` for more examples.

## License

MIT
Loading
Loading