Metadata-Version: 2.4
Name: py-pglite
Version: 0.4.0
Summary: Python testing library for PGlite - in-memory PostgreSQL for tests
Project-URL: Homepage, https://github.com/wey-gu/py-pglite
Project-URL: Documentation, https://github.com/wey-gu/py-pglite#readme
Project-URL: Repository, https://github.com/wey-gu/py-pglite
Project-URL: Issues, https://github.com/wey-gu/py-pglite/issues
Author-email: Wey Gu <weyl.gu@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: database,django,pglite,postgresql,pytest,pytest-django,sqlalchemy,sqlmodel,testing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: psutil>=6.0.0
Provides-Extra: all
Requires-Dist: asyncpg>=0.29.0; extra == 'all'
Requires-Dist: bcrypt>=4.3.0; extra == 'all'
Requires-Dist: django>=4.0.0; extra == 'all'
Requires-Dist: fastapi>=0.115.12; extra == 'all'
Requires-Dist: httpx>=0.27.0; extra == 'all'
Requires-Dist: numpy>=1.0.0; extra == 'all'
Requires-Dist: passlib>=1.7.4; extra == 'all'
Requires-Dist: pgvector>=0.4.1; extra == 'all'
Requires-Dist: psycopg>=3.0.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-django>=4.5.0; extra == 'all'
Requires-Dist: python-jose>=3.3.0; extra == 'all'
Requires-Dist: sqlalchemy>=2.0.41; extra == 'all'
Requires-Dist: sqlmodel>=0.0.24; extra == 'all'
Provides-Extra: async
Requires-Dist: asyncpg>=0.29.0; extra == 'async'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'async'
Provides-Extra: asyncpg
Requires-Dist: asyncpg>=0.29.0; extra == 'asyncpg'
Provides-Extra: dev
Requires-Dist: bandit[toml]>=1.8.3; extra == 'dev'
Requires-Dist: build>=1.2.2.post1; extra == 'dev'
Requires-Dist: mypy>=1.16.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.1.1; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.11.12; extra == 'dev'
Requires-Dist: safety>=3.5.2; extra == 'dev'
Requires-Dist: twine>=6.1.0; extra == 'dev'
Requires-Dist: types-psutil>=7.0.0; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.0.0; extra == 'django'
Requires-Dist: pytest-django>=4.5.0; extra == 'django'
Provides-Extra: examples
Requires-Dist: bcrypt>=4.3.0; extra == 'examples'
Requires-Dist: fastapi>=0.115.12; extra == 'examples'
Requires-Dist: httpx>=0.27.0; extra == 'examples'
Requires-Dist: passlib>=1.7.4; extra == 'examples'
Requires-Dist: python-jose>=3.3.0; extra == 'examples'
Requires-Dist: sqlalchemy>=2.0.41; extra == 'examples'
Requires-Dist: sqlmodel>=0.0.24; extra == 'examples'
Provides-Extra: extensions
Requires-Dist: numpy>=1.0.0; extra == 'extensions'
Requires-Dist: pgvector>=0.4.1; extra == 'extensions'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.115.12; extra == 'fastapi'
Requires-Dist: httpx>=0.27.0; extra == 'fastapi'
Requires-Dist: sqlalchemy>=2.0.41; extra == 'fastapi'
Requires-Dist: sqlmodel>=0.0.24; extra == 'fastapi'
Provides-Extra: psycopg
Requires-Dist: psycopg>=3.0.0; extra == 'psycopg'
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy>=2.0.41; extra == 'sqlalchemy'
Provides-Extra: sqlmodel
Requires-Dist: sqlalchemy>=2.0.41; extra == 'sqlmodel'
Requires-Dist: sqlmodel>=0.0.24; extra == 'sqlmodel'
Provides-Extra: test-extensions
Requires-Dist: numpy>=1.0.0; extra == 'test-extensions'
Requires-Dist: pgvector>=0.4.1; extra == 'test-extensions'
Description-Content-Type: text/markdown

# py-pglite

<img src="https://github.com/user-attachments/assets/3c6ef886-5075-4d82-a180-a6b1dafe792b" alt="py-pglite Logo" width="60" align="left" style="margin-right: 16px;"/>

**A Pythonic interface for PGlite - the instant, zero-config PostgreSQL.** ⚡️

`py-pglite` brings the magic of [PGlite](https://github.com/electric-sql/pglite) to Python with a high-level, developer-friendly API. Real PostgreSQL, instant testing.

`pip install py-pglite`

<br clear="all"/>

```python
def test_users(pglite_session):
    user = User(name="Alice")
    pglite_session.add(user)
    pglite_session.commit()
    assert user.id == 1  # It's real PostgreSQL!
```

**That's it.** No Docker, no setup, no config files. Real PostgreSQL, instant testing.

[![CI](https://github.com/wey-gu/py-pglite/actions/workflows/ci.yml/badge.svg)](https://github.com/wey-gu/py-pglite/actions/workflows/ci.yml) [![PyPI](https://badge.fury.io/py/py-pglite.svg)](https://badge.fury.io/py/py-pglite) [![Python](https://img.shields.io/pypi/pyversions/py-pglite.svg)](https://pypi.org/project/py-pglite/)

[![License](https://img.shields.io/pypi/l/py-pglite.svg)](https://github.com/wey-gu/py-pglite/blob/main/LICENSE) [![MyPy](https://img.shields.io/badge/type_checked-mypy-informational.svg)](https://mypy.readthedocs.io/en/stable/introduction.html) [![Ruff](https://img.shields.io/badge/style-ruff-blue?logo=ruff&logoColor=white)](https://github.com/astral-sh/ruff) [![codecov](https://codecov.io/gh/wey-gu/py-pglite/branch/main/graph/badge.svg?token=YOUR_CODECOV_TOKEN)](https://codecov.io/gh/wey-gu/py-pglite)

---

## **Why py-pglite?**

```python
# ❌ Traditional testing
def test_old_way():
    # 1. Install PostgreSQL
    # 2. Configure connection  
    # 3. Manage test databases
    # 4. Handle cleanup
    # 5. Docker containers...
    pass

# ✅ py-pglite way  
def test_new_way(pglite_session):
    User.objects.create(name="Alice")  # Just works!
```

**The magic:**

- 🎯 **Zero config** - No setup, no Docker, no servers
- ⚡ **Instant** - 2-3s startup vs 30-60s Docker
- 🔄 **Isolated** - Fresh database per test
- 🐘 **Real PostgreSQL** - JSON, arrays, window functions
- 🚀 **Any client** - SQLAlchemy, Django, psycopg, asyncpg

---

## **Install**

```bash
# Core (framework-agnostic)
pip install py-pglite

# With your stack
pip install py-pglite[sqlalchemy]  # SQLAlchemy + SQLModel
pip install py-pglite[django]      # Django + pytest-django  
pip install py-pglite[asyncpg]     # Pure async client
pip install py-pglite[all]         # Everything

# Extra Features
pip install py-pglite[extensions]  # pglite extensions, like pgvector, fuzzystrmatch etc.
```

---

## **Quick Start**

### **SQLAlchemy** (Zero imports needed)

```python
def test_sqlalchemy_just_works(pglite_session):
    user = User(name="Alice", email="alice@test.com")  
    pglite_session.add(user)
    pglite_session.commit()
    
    assert user.id is not None
    assert User.query.count() == 1  # Real PostgreSQL!
```

### **Django** (Auto-configured)

```python  
def test_django_just_works(db):
    Post.objects.create(title="Hello", content="World")
    assert Post.objects.count() == 1  # Real PostgreSQL!
```

### **Any PostgreSQL client**

```python
def test_any_client_works(pglite_manager):
    # Extract connection details
    engine = pglite_manager.get_engine()
    host, port, database = str(engine.url.host), engine.url.port, engine.url.database
    
    # Use with any PostgreSQL client
    # conn = psycopg.connect(host=host, port=port, dbname=database)
    # conn = await asyncpg.connect(host=host, port=port, database=database)
    # engine = create_async_engine(f"postgresql+asyncpg://{host}:{port}/{database}")
```

---

## **Examples**

### **FastAPI + SQLModel**

```python
from fastapi.testclient import TestClient

def test_api_endpoint(client: TestClient):
    response = client.post("/users/", json={"name": "Alice"})
    assert response.status_code == 201
    
    response = client.get("/users/")
    assert len(response.json()) == 1
```

### **PostgreSQL Features**

```python
def test_postgresql_power(pglite_session):
    pglite_session.execute(text("""
        CREATE TABLE analytics (
            data JSONB,
            tags TEXT[],
            created TIMESTAMP DEFAULT NOW()
        )
    """))
    
    pglite_session.execute(text("""
        INSERT INTO analytics (data, tags) VALUES 
        ('{"clicks": 100}', ARRAY['web', 'mobile'])
    """))
    
    result = pglite_session.execute(text("""
        SELECT data->>'clicks' as clicks,
               array_length(tags, 1) as tag_count
        FROM analytics 
        WHERE data->>'clicks' > '50'
    """)).fetchone()
    
    assert result.clicks == '100'
```

### **PostgreSQL Extensions**

`py-pglite` supports PostgreSQL extensions, allowing you to test advanced features like vector similarity search for AI/RAG applications.

### **🚀 `pgvector` for RAG Applications**

Enable `pgvector` to test vector embeddings and similarity search directly in your test suite.

**1. Install with the `[extensions]` extra:**

```bash
pip install 'py-pglite[extensions]'
```

**2. Enable `pgvector` in the configuration:**

```python
from py_pglite import PGliteConfig, PGliteManager
from pgvector.psycopg import register_vector
import psycopg
import numpy as np

# Enable the extension
config = PGliteConfig(extensions=["pgvector"])

with PGliteManager(config=config) as db:
    with psycopg.connect(db.get_dsn(), autocommit=True) as conn:
        # Create the extension and register the type
        conn.execute("CREATE EXTENSION IF NOT EXISTS vector")
        register_vector(conn)

        # Create a table and insert a vector
        conn.execute("CREATE TABLE items (embedding vector(3))")
        conn.execute("INSERT INTO items (embedding) VALUES (%s)", (np.array([1, 2, 3]),))
        
        # Perform a similarity search
        result = conn.execute("SELECT * FROM items ORDER BY embedding <-> %s LIMIT 1", (np.array([1, 1, 1]),)).fetchone()
        assert np.array_equal(result[0], np.array([1, 2, 3]))
```

`py-pglite` can support many other extensions available in the underlying [PGlite extensions](https://pglite.dev/extensions/) ♥️.

---

## **Advanced**

<details>
<summary><strong>🔧 Production Configuration</strong></summary>

```python
from py_pglite import PGliteConfig
from py_pglite.sqlalchemy import SQLAlchemyPGliteManager

config = PGliteConfig(
    timeout=60,                    # Extended timeout for CI/CD
    log_level="INFO",              # Balanced logging
    cleanup_on_exit=True,          # Automatic cleanup
    work_dir=Path("./test-data")   # Custom directory
)

with SQLAlchemyPGliteManager(config) as manager:
    engine = manager.get_engine(
        pool_recycle=3600,         # Connection recycling
        echo=False                 # SQL logging
    )
```

</details>

<details>
<summary><strong>🔄 Client Compatibility</strong></summary>

```python
# py-pglite provides a REAL PostgreSQL server - any client works!

with SQLAlchemyPGliteManager() as manager:
    engine = manager.get_engine()
    url = engine.url
    
    # Extract connection details for any PostgreSQL client
    host, port, database = str(url.host), url.port, url.database
    
    # Examples for different clients:
    # psycopg:  psycopg.connect(host=host, port=port, dbname=database)
    # asyncpg:  await asyncpg.connect(host=host, port=port, database=database)
    # Django:   Uses custom py-pglite backend automatically
```

**Installation Matrix:**

| Client | Install | Use Case |
|--------|---------|----------|
| `[sqlalchemy]` | SQLAlchemy + SQLModel | ORM, modern Python |
| `[django]` | Django + pytest-django | Django projects |
| `[psycopg]` | psycopg (sync/async) | Raw SQL, custom |
| `[asyncpg]` | Pure async client | High-performance async |
| `[all]` | Everything | Full compatibility |

</details>

<details>
<summary><strong>🎯 Framework Isolation</strong></summary>

```bash
# Perfect isolation - no framework bleeding
pytest -m sqlalchemy -p no:django     # Pure SQLAlchemy
pytest -m django -p no:sqlalchemy     # Pure Django  
pytest tests/sqlalchemy/              # Directory isolation
```

</details>

---

**Built for developers who want PostgreSQL testing without the complexity.**

🎯 [Examples](examples/) • 📚 [Contributing](CONTRIBUTING.md) • 🐛 [Issues](https://github.com/wey-gu/py-pglite/issues)

---

*py-pglite: Because testing should be simple.* ⚡

Powered by the 🚀 amazing and ♥️ beloved [PGlite](https://github.com/electric-sql/pglite).
