Metadata-Version: 2.4
Name: IronHammer
Version: 1.0.1
Summary: A drop-in replacement for Anvil Data Tables API
Author-email: its_me_abi <keralaboypypi@keralaboy.anonaddy.com>
Maintainer-email: its_me_abi <keralaboypypi@keralaboy.anonaddy.com>
License: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/its-me-abi/IronHammer
Project-URL: Documentation, https://github.com/its-me-abi/IronHammer
Project-URL: Repository, https://github.com/its-me-abi/IronHammer
Project-URL: Issues, https://github.com/its-me-abi/IronHammer/issues
Project-URL: Changelog, https://github.com/its-me-abi/IronHammer/blob/main/CHANGELOG.md
Keywords: database,anvil,data-tables,orm,sqlite,postgresql,mysql,storage
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Operating System :: OS Independent
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: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typing-extensions>=4.5.0
Provides-Extra: sqlite
Requires-Dist: IronHammer[core]; extra == "sqlite"
Provides-Extra: postgresql
Requires-Dist: IronHammer[core]; extra == "postgresql"
Requires-Dist: psycopg[binary]>=3.1.0; extra == "postgresql"
Requires-Dist: psycopg-pool>=3.1.0; extra == "postgresql"
Provides-Extra: mysql
Requires-Dist: IronHammer[core]; extra == "mysql"
Requires-Dist: pymysql>=1.1.0; extra == "mysql"
Requires-Dist: cryptography>=41.0.0; extra == "mysql"
Provides-Extra: s3
Requires-Dist: IronHammer[core]; extra == "s3"
Requires-Dist: boto3>=1.28.0; extra == "s3"
Provides-Extra: r2
Requires-Dist: IronHammer[core]; extra == "r2"
Requires-Dist: boto3>=1.28.0; extra == "r2"
Provides-Extra: all
Requires-Dist: IronHammer[mysql,postgresql,r2,s3,sqlite]; extra == "all"
Provides-Extra: dev
Requires-Dist: IronHammer[all]; extra == "dev"
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-mock>=3.11.0; extra == "dev"
Requires-Dist: black>=23.7.0; extra == "dev"
Requires-Dist: ruff>=0.0.280; extra == "dev"
Requires-Dist: mypy>=1.5.0; extra == "dev"
Requires-Dist: pylint>=2.17.0; extra == "dev"
Requires-Dist: sphinx>=7.1.0; extra == "dev"
Requires-Dist: sphinx-rtd-theme>=1.3.0; extra == "dev"
Requires-Dist: sphinx-autodoc-typehints>=1.24.0; extra == "dev"
Provides-Extra: test
Requires-Dist: IronHammer[all]; extra == "test"
Requires-Dist: pytest>=7.4.0; extra == "test"
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
Requires-Dist: pytest-mock>=3.11.0; extra == "test"
Provides-Extra: core
Requires-Dist: typing-extensions>=4.5.0; extra == "core"
Dynamic: license-file

# IronHammer

A production-ready Python library that provides a drop-in replacement for Anvil Data Tables API.

## Features

- **95%+ API Compatibility** with Anvil Data Tables server-side API
- **Multiple Storage Backends**: SQLite, PostgreSQL, MySQL, and in-memory
- **Flexible Media Storage**: Filesystem, Cloudflare R2, Amazon S3, MinIO
- **Advanced Query Engine**: Full support for complex queries with boolean expressions
- **Relationships**: One-to-one, one-to-many, many-to-many with cascade delete
- **Transactions**: ACID compliance with nested transactions and savepoints
- **Schema Management**: Automatic migrations with versioning
- **Indexing**: Unique and composite indexes for performance optimization
- **Serialization**: JSON, CSV, YAML import/export
- **Permissions**: Role-based access control with custom authentication hooks
- **Thread-Safe**: Safe for concurrent access in multi-threaded environments
- **Type Hints**: Full type annotations for IDE support

## Installation

```bash
pip install IronHammer
```

## Quick Start

```python
from IronHammer.apptables import app_tables
import IronHammer.apptables.query as q

# Add a row
user = app_tables.users.add_row(
    name="John",
    age=25
)

# Get a row
user = app_tables.users.get(name="John")

# Search with queries
rows = app_tables.users.search(
    age=q.greater_than(18)
)

for row in rows:
    print(row["name"])

# Update a row
user["age"] = 30
user.update(active=True)

# Delete a row
user.delete()
```

## Configuration

### SQLite Backend (Default)

```python
from IronHammer.apptables import app_tables

app_tables.configure(
    backend="sqlite",
    database_path="my_data.db"
)
```

### PostgreSQL Backend

```python
app_tables.configure(
    backend="postgresql",
    host="localhost",
    port=5432,
    database="mydb",
    user="postgres",
    password="secret"
)
```

### MySQL Backend

```python
app_tables.configure(
    backend="mysql",
    host="localhost",
    port=3306,
    database="mydb",
    user="root",
    password="secret"
)
```

### In-Memory Backend

```python
app_tables.configure(
    backend="memory"
)
```

## Table Operations

```python
# Create a table
app_tables.create_table("users", columns={
    "name": "string",
    "age": "number",
    "active": "bool"
})

# List tables
tables = app_tables.list_tables()

# Delete a table
app_tables.delete_table("users")

# Rename a table
app_tables.rename_table("users", "people")
```

## Query API

```python
import IronHammer.apptables.query as q

# Basic comparisons
app_tables.users.search(age=q.equal_to(25))
app_tables.users.search(age=q.not_equal_to(25))
app_tables.users.search(age=q.greater_than(18))
app_tables.users.search(age=q.greater_than_or_equal_to(18))
app_tables.users.search(age=q.less_than(65))
app_tables.users.search(age=q.less_than_or_equal_to(65))

# String operations
app_tables.users.search(name=q.contains("John"))
app_tables.users.search(name=q.startswith("J"))
app_tables.users.search(name=q.endswith("n"))
app_tables.users.search(name=q.like("J%"))
app_tables.users.search(name=q.ilike("j%"))
app_tables.users.search(name=q.regexp(r"^J.*n$"))

# Null checks
app_tables.users.search(email=q.is_none())
app_tables.users.search(email=q.not_none())

# List operations
app_tables.users.search(age=q.in_list([18, 25, 30]))
app_tables.users.search(age=q.not_in_list([10, 15, 20]))

# Range queries
app_tables.users.search(age=q.between(18, 65))

# Boolean expressions
app_tables.users.search(
    q.all_of(
        age=q.greater_than(18),
        active=True
    )
)

app_tables.users.search(
    q.any_of(
        name=q.contains("John"),
        name=q.contains("Jane")
    )
)

app_tables.users.search(
    q.not_(
        age=q.less_than(18)
    )
)

# Sorting
app_tables.users.search(
    age=q.greater_than(18),
    order_by="name",
    ascending=True
)
```

## Transactions

```python
from IronHammer.apptables import transaction

with transaction():
    user = app_tables.users.add_row(name="John", age=25)
    app_tables.orders.add_row(user_id=user.get_id(), total=100.0)
    # If an exception occurs, changes are rolled back automatically
```

### Nested Transactions

```python
with transaction() as outer:
    user = app_tables.users.add_row(name="John", age=25)
    
    with transaction(savepoint=True):
        app_tables.orders.add_row(user_id=user.get_id(), total=100.0)
        # Can rollback to this savepoint
```

## Relationships

```python
# Define relationships in schema
app_tables.create_table("users", columns={
    "name": "string",
    "email": "string"
})

app_tables.create_table("posts", columns={
    "title": "string",
    "content": "text",
    "user_id": "reference:users"
})

# Access related data
user = app_tables.users.get(name="John")
posts = user["posts"]  # One-to-many relationship

post = app_tables.posts.get(title="My Post")
author = post["user"]  # Many-to-one relationship
```

## Media Storage

```python
from IronHammer.apptables.media import Media

# Configure media storage
app_tables.configure_media(
    backend="filesystem",
    path="/path/to/media"
)

# Cloudflare R2
app_tables.configure_media(
    backend="r2",
    account_id="your-account-id",
    access_key="your-access-key",
    secret_key="your-secret-key",
    bucket="my-bucket"
)

# Amazon S3
app_tables.configure_media(
    backend="s3",
    access_key="your-access-key",
    secret_key="your-secret-key",
    bucket="my-bucket",
    region="us-east-1"
)

# Use media objects
media = Media.from_file("image.png")
app_tables.users.add_row(name="John", avatar=media)
```

## Serialization

```python
# Export to JSON
app_tables.users.export_to_json("users.json")

# Import from JSON
app_tables.users.import_from_json("users.json")

# Export to CSV
app_tables.users.export_to_csv("users.csv")

# Export to YAML
app_tables.users.export_to_yaml("users.yaml")
```

## Bulk Operations

```python
# Bulk insert
data = [
    {"name": "John", "age": 25},
    {"name": "Jane", "age": 30},
    {"name": "Bob", "age": 35}
]
app_tables.users.bulk_add(data)

# Bulk update
app_tables.users.bulk_update(
    ids=[id1, id2, id3],
    updates={"active": True}
)

# Bulk delete
app_tables.users.bulk_delete([id1, id2, id3])
```

## Permissions

```python
# Configure permissions
app_tables.configure_permissions({
    "users": {
        "read": ["admin", "user"],
        "write": ["admin"],
        "delete": ["admin"]
    }
})

# Custom authentication hook
def authenticate(role, table, operation):
    # Your custom authentication logic
    return True

app_tables.set_auth_hook(authenticate)
```

## Compatibility Mode

For existing Anvil projects, use the compatibility package:

```python
# Instead of:
# from anvil.tables import app_tables
# import anvil.tables.query as q

# Use:
from IronHammer.apptables.compat import app_tables
import IronHammer.apptables.compat.query as q
```

## API Reference

See the [docs](docs/) directory for complete API documentation.

## Testing

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=IronHammer --cov-report=html

# Run specific test file
pytest tests/test_table.py
```

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

## License

AGPL-3.0-or-later - see [LICENSE](LICENSE) for details.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for version history.
