Metadata-Version: 2.4
Name: viveka-kosha
Version: 0.1.1
Summary: Spring JPA-inspired async database library for Python — built on SQLAlchemy async and Redis
License: VIVEKASUTRA PROPRIETARY SOFTWARE LICENSE
        
        Copyright (c) 2025 VivekaSutra. All rights reserved.
        https://vivekasutra.com/
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        IMPORTANT: READ THIS LICENSE CAREFULLY BEFORE DOWNLOADING,
        INSTALLING, OR USING THIS SOFTWARE.
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        By downloading, installing, or using this software ("viveka-kosha"),
        you agree to be bound by the terms of this license agreement.
        If you do not agree, do not download, install, or use this software.
        
        
        1. GRANT OF LICENSE
           VivekaSutra grants you a limited, non-exclusive, non-transferable,
           royalty-free license to download and use this software solely for
           your personal or internal business purposes.
        
        
        2. RESTRICTIONS
           You may NOT:
           a) Copy, modify, merge, or create derivative works of this software.
           b) Distribute, sell, sublicense, rent, lease, or transfer this
              software or any rights in it to any third party.
           c) Reverse engineer, decompile, disassemble, or attempt to derive
              the source code of this software.
           d) Remove, alter, or obscure any proprietary notices, labels,
              or markings on this software.
           e) Use this software to build a competing product or service.
        
        
        3. INTELLECTUAL PROPERTY
           This software and all copies thereof are proprietary to VivekaSutra.
           All title, ownership rights, and intellectual property rights in and
           to this software remain exclusively with VivekaSutra. This license
           does not grant you any rights to trademarks, service marks, or trade
           names of VivekaSutra.
        
        
        4. NO WARRANTY
           THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
           EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
           NON-INFRINGEMENT.
        
           VIVEKASUTRA DOES NOT WARRANT THAT:
           a) THE SOFTWARE WILL MEET YOUR REQUIREMENTS.
           b) THE SOFTWARE WILL OPERATE UNINTERRUPTED OR ERROR-FREE.
           c) ANY ERRORS IN THE SOFTWARE WILL BE CORRECTED.
           d) THE SOFTWARE IS COMPATIBLE WITH YOUR SYSTEMS OR ENVIRONMENT.
        
           YOU ASSUME ALL RISK ASSOCIATED WITH THE USE OF THIS SOFTWARE.
        
        
        5. LIMITATION OF LIABILITY
           TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
           VIVEKASUTRA, ITS DIRECTORS, EMPLOYEES, PARTNERS, AGENTS, SUPPLIERS,
           OR AFFILIATES BE LIABLE FOR ANY:
        
           a) DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
              CONSEQUENTIAL DAMAGES.
           b) LOSS OF PROFITS, REVENUE, DATA, BUSINESS, OR GOODWILL.
           c) BUSINESS INTERRUPTION OR LOSS OF BUSINESS OPPORTUNITY.
           d) COST OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES.
           e) ANY OTHER COMMERCIAL OR ECONOMIC LOSS.
        
           THIS LIMITATION APPLIES REGARDLESS OF WHETHER SUCH DAMAGES ARISE
           FROM CONTRACT, TORT, NEGLIGENCE, STRICT LIABILITY, OR ANY OTHER
           LEGAL THEORY, AND EVEN IF VIVEKASUTRA HAS BEEN ADVISED OF THE
           POSSIBILITY OF SUCH DAMAGES.
        
        
        6. INDEMNIFICATION
           You agree to indemnify, defend, and hold harmless VivekaSutra and
           its affiliates from and against any claims, liabilities, damages,
           losses, and expenses (including legal fees) arising out of or in
           connection with your use of this software or your violation of
           this license.
        
        
        7. TERMINATION
           This license is effective until terminated. Your rights under this
           license will terminate automatically and without notice from
           VivekaSutra if you fail to comply with any of its terms and
           conditions. Upon termination, you must immediately cease all use
           of this software and destroy all copies in your possession.
        
        
        8. GOVERNING LAW
           This license shall be governed by and construed in accordance with
           applicable laws. Any disputes arising under this license shall be
           subject to the exclusive jurisdiction of the competent courts.
        
        
        9. ENTIRE AGREEMENT
           This license constitutes the entire agreement between you and
           VivekaSutra with respect to this software and supersedes all prior
           or contemporaneous understandings, agreements, or representations.
        
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        For licensing inquiries or permissions beyond the scope of this
        license, please contact us at: https://vivekasutra.com/
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        VivekaSutra
        https://vivekasutra.com/
        Copyright (c) 2025. All rights reserved.
        
Project-URL: Homepage, https://vivekasutra.com/
Project-URL: Source, https://github.com/vivekasutra/viveka-kosha
Keywords: database,orm,sqlalchemy,async,fastapi,jpa,repository,transactional
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
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: Framework :: AsyncIO
Classifier: Framework :: FastAPI
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: sqlalchemy[asyncio]>=2.0
Requires-Dist: redis>=5.0
Requires-Dist: starlette>=0.35
Requires-Dist: viveka-grantha>=0.1.0
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.29; extra == "postgres"
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.2; extra == "mysql"
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Dynamic: license-file

# viveka-kosha

A Spring JPA-inspired async database library for Python. Provides declarative entity mapping, generic repositories, transaction management, and cache decorators — built on SQLAlchemy async and Redis.

---

## Design Philosophy

viveka-kosha maps Spring JPA concepts to Python idioms:

| Spring JPA | viveka-kosha |
|---|---|
| `@Entity` + `@Table` | `@entity` + `@table` |
| `JpaRepository<T, ID>` | `DbRepo[T]` |
| `@Repository` | `@repository` |
| `@Transactional` | `@transactional` |
| `@Cacheable` | `@cacheable` |
| `@CacheEvict` | `@cache_evict` |
| `OpenSessionInView` filter | `DbMiddleware` |
| `ThreadLocal` session | `ContextVar` session |

The library is **async-first**. Every repository method, cache call, and session operation is `async/await`. This makes it a natural fit for FastAPI / Starlette services.

---

## Architecture

```
viveka-kosha/
└── viveka_db/
    ├── base/
    │   ├── db_base.py        # VivekaDbBase — shared SQLAlchemy declarative base
    │   └── db_repo.py        # DbRepo[T] — generic CRUD repository
    ├── decorator/
    │   └── db_decorator.py   # @entity, @table, @repository, @transactional,
    │                         # @cacheable, @cache_evict
    ├── session/
    │   └── db_session.py     # DatabaseSessionFactory — singleton engine + session factory
    ├── middleware/
    │   └── db_middleware.py  # DbMiddleware — per-request session lifecycle
    ├── context/
    │   └── db_context.py     # ContextVar session store (async-safe ThreadLocal equivalent)
    └── cache/
        └── db_cache.py       # RedisCache — async Redis wrapper
```

### Session Lifecycle

viveka-kosha manages sessions in two distinct paths:

```
HTTP Request
    │
    ▼
DbMiddleware.dispatch()
    │  creates AsyncSession
    │  stores in ContextVar
    ▼
Route Handler → Service → @transactional method
    │               detects session in ContextVar
    │               uses it directly (no new session)
    ▼
DbMiddleware
    │  commit on success
    │  rollback on exception
    └  close + clear ContextVar


Background Task / Async Worker
    │
    ▼
@transactional method
    │  no session in ContextVar
    │  opens a new session via DatabaseSessionFactory
    │  commit on success / rollback on exception / close
    └  logs a warning so async task usage is visible
```

This means `@transactional` works transparently in both HTTP and non-HTTP contexts with no configuration change.

### Entity Registration

`@entity` registers the class in a global `_entity_registry`. `@table(name)` sets `__tablename__` and triggers SQLAlchemy's declarative mapping. `VivekaDbBase.__init_subclass__` marks any subclass without a `__tablename__` as `__abstract__` so SQLAlchemy skips unmapped intermediary classes.

---

## Installation

Add to your service's `requirements.txt`:

```
viveka-kosha
sqlalchemy[asyncio]
asyncpg          # for PostgreSQL
aiomysql         # for MySQL
redis            # for cache decorators
starlette        # for DbMiddleware
```

---

## Configuration

viveka-kosha reads from your `ConfigService` (from `viveka-grantha`).

`config.ini`:

```ini
[database]
url          = postgresql+asyncpg://user:pass@localhost/mydb
pool_size    = 10
max_overflow = 20
echo         = false

[cache]
url          = redis://localhost:6379/0
enabled      = true
```

---

## Usage

### 1. Initialize the session factory at startup

```python
from viveka_db.session.db_session import DatabaseSessionFactory
from viveka_common.config.config_service import ConfigService

config = ConfigService()
DatabaseSessionFactory(config_service=config)
```

### 2. Register the middleware (FastAPI / Starlette)

```python
from fastapi import FastAPI
from viveka_db.middleware.db_middleware import DbMiddleware
from viveka_db.session.db_session import DatabaseSessionFactory

app = FastAPI()
engine = DatabaseSessionFactory.get_instance().engine
app.add_middleware(DbMiddleware, engine=engine)
```

### 3. Define an entity

```python
from sqlalchemy import Column, Integer, String
from viveka_db.base.db_base import VivekaDbBase
from viveka_db.decorator.db_decorator import entity, table

@entity
@table("users")
class User(VivekaDbBase):
    id       = Column(Integer, primary_key=True)
    name     = Column(String(100), nullable=False)
    email    = Column(String(255), unique=True, nullable=False)
```

### 4. Define a repository

```python
from viveka_db.base.db_repo import DbRepo
from viveka_db.decorator.db_decorator import repository, transactional, cacheable, cache_evict

@repository
class UserRepo(DbRepo[User]):

    def __init__(self):
        super().__init__(User)

    @transactional
    async def save(self, user: User) -> User:
        result = await self.insert_all([user])
        return result[0]

    @transactional
    @cacheable(key_prefix="user", ttl=3600)
    async def get(self, user_id: int):
        return await self.get_by_id(user_id)

    @transactional
    @cache_evict(key_prefix="user")
    async def remove(self, user_id: int) -> bool:
        return await self.delete(user_id)

    @transactional
    async def find_by_email(self, email: str):
        return await self.find_by_field("email", email)
```

### 5. Use the repository in a service

```python
class UserService:

    def __init__(self):
        self.user_repo = UserRepo()

    async def register(self, name: str, email: str) -> User:
        user = User(name=name, email=email)
        return await self.user_repo.save(user)

    async def get_user(self, user_id: int) -> User:
        return await self.user_repo.get(user_id)
```

---

## Built-in Repository Methods

`DbRepo[T]` provides these methods out of the box — all require `@transactional` on the calling method:

| Method | Description |
|---|---|
| `insert_all(entities)` | Bulk insert, returns saved entities |
| `get_by_id(id)` | Fetch by primary key |
| `get_all(skip, limit)` | Paginated fetch (default: 0, 100) |
| `update(entity)` | Merge and flush |
| `delete(id)` | Delete by primary key, returns `bool` |
| `delete_entity(entity)` | Delete a loaded entity instance |
| `exists(id)` | Check existence by primary key |
| `count()` | Total row count |
| `find_by_field(field, value)` | First match on any field |
| `find_all_by_field(field, value)` | All matches on any field |

---

## Decorators Reference

### `@entity`
Marks a class as a mapped entity. Registers it in the global entity registry. The class must inherit from `VivekaDbBase`.

### `@table(name)`
Sets the database table name and activates SQLAlchemy mapping. Must be used together with `@entity`.

```python
@entity
@table("orders")
class Order(VivekaDbBase):
    ...
```

### `@repository`
Marks a class as a repository for auto-discovery. No runtime behaviour beyond flagging.

### `@transactional`
Injects the active `AsyncSession` into `self.db` before the method runs.

- **HTTP context**: reuses the session created by `DbMiddleware` — middleware commits/rolls back.
- **Background task context**: opens a dedicated session, commits on success, rolls back on exception.

```python
@transactional
async def do_something(self):
    # self.db is an AsyncSession here
    result = await self.db.execute(select(MyModel))
```

### `@cacheable(key_prefix, ttl=3600)`
Cache-aside read. Checks cache before calling the method. Caches the result on a miss.
Cache key is `{key_prefix}:{first_arg}`.

```python
@transactional
@cacheable(key_prefix="product", ttl=1800)
async def get_product(self, product_id: int):
    return await self.get_by_id(product_id)
```

### `@cache_evict(key_prefix)`
Runs the method first, then deletes all keys matching `{key_prefix}:*` from cache.

```python
@transactional
@cache_evict(key_prefix="product")
async def update_product(self, product: Product):
    return await self.update(product)
```

---

## Dependencies

| Package | Role |
|---|---|
| `sqlalchemy[asyncio]` | ORM, async engine, session |
| `asyncpg` / `aiomysql` | Async DB driver (PostgreSQL / MySQL) |
| `redis` | Async Redis client (`redis.asyncio`) |
| `starlette` | `BaseHTTPMiddleware` for `DbMiddleware` |
| `viveka-grantha` | `ConfigService`, `VivekaLogManager`, `VivekaCacheManager` |

---

## License

Copyright (c) 2025 VivekaSutra. All rights reserved.

This software is distributed under the **VivekaSutra Proprietary Software License**.

- Free to download and use for personal or commercial purposes
- Modification, redistribution, and reverse engineering are not permitted
- Provided "AS IS" — no warranty of any kind
- VivekaSutra is not liable for any damages arising from use

See [LICENSE.txt](LICENSE.txt) for the full license text.
For licensing inquiries visit [vivekasutra.com](https://vivekasutra.com/)
