# 1Password CLI Python Wrapper

[![PyPI version](https://badge.fury.io/py/1password-cli.svg)](https://pypi.org/project/1password-cli/)
[![Python Support](https://img.shields.io/pypi/pyversions/1password-cli.svg)](https://pypi.org/project/1password-cli/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI/CD](https://github.com/scottzach1/1Password-Python/actions/workflows/pipeline.yml/badge.svg)](https://github.com/scottzach1/1Password-Python/actions)
[![Coverage](https://scottzach1.github.io/1Password-Python/coverage.svg)](https://github.com/scottzach1/1Password-Python)

Python wrapper for 1Password CLI with Pydantic Settings integration. Automatically injects secrets from `op://` references while preserving types.

## Features

- Automatic secret injection with `op://` reference detection
- Pydantic Settings integration with pre-validation injection
- Type preservation (int, float, bool, None, lists)
- Full type hints with `py.typed` marker
- Performance optimized (skips CLI calls when no secrets detected)
- Timeout protection and clear error messages

## Installation

```bash
pip install 1password-cli
```

**Prerequisites:** [1Password CLI](https://developer.1password.com/docs/cli/get-started/) installed and authenticated (`op signin`)

## Usage

**String injection:**
```python
from scottzach1.onepassword_cli import inject_string_1password

password = inject_string_1password("op://vault/database/password")
```

**Dictionary injection:**
```python
from scottzach1.onepassword_cli import inject_dictionary_1password

config = {
    "database_url": "op://vault/database/url",
    "api_key": "op://vault/api/key",
    "port": 5432,  # Non-secret values preserved
}
injected = inject_dictionary_1password(config)
```

**Pydantic Settings:**
```python
from scottzach1.onepassword_cli import OnePasswordSettings

class AppSettings(OnePasswordSettings):
    database_url: str = "op://vault/database/url"
    api_key: str = "op://vault/api/key"
    port: int = 8080

settings = AppSettings()  # Secrets injected automatically
```

## API

- **`inject_string_1password(value, check_cli=True)`** - Inject secrets into a string
- **`inject_dictionary_1password(data, check_cli=True, in_place=False)`** - Inject secrets into a dictionary
- **`OnePasswordSettings`** - Pydantic Settings base class with automatic injection

See docstrings for parameters and exceptions.

## Development

```bash
# Setup
uv sync --dev && uv run pre-commit install

# Test
uv run pytest tests/ -v -m "not integration"
uv run coverage run -m pytest tests/ -m "not integration"

# Lint/Format
uv run ruff check src/ tests/ --fix
uv run ruff format src/ tests/
```

## License

MIT License - see [LICENSE](LICENSE) file.
