Metadata-Version: 2.4
Name: cullinan
Version: 0.80
Summary: Cullinan is written based on tornado and Sqlalchemy to help the project quickly build web application
Home-page: https://github.com/plumeink/Cullinan
Author: plumeink
Author-email: official@plumeink.com
License: MIT
Project-URL: Source, https://github.com/plumeink/Cullinan
Project-URL: Wiki, https://github.com/plumeink/Cullinan/wiki
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
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: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tornado
Requires-Dist: python-dotenv
Requires-Dist: contextvars
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

![Python version](https://img.shields.io/badge/python-3.7%20|%203.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue)
![PyPI version](https://img.shields.io/pypi/v/cullinan.svg?style=flat&logo=pypi&color=green)
![PyPI downloads](https://img.shields.io/pypi/dm/cullinan.svg?style=flat&logo=pypi&color=blue)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/plumeink/Cullinan)
![GitHub stars](https://img.shields.io/github/stars/plumeink/cullinan.svg?style=flat&logo=github&color=white)
![License](https://img.shields.io/github/license/plumeink/cullinan.svg?style=flat&color=white)


```                                              
   _____      _ _ _                      
  / ____|    | | (_)                     
 | |    _   _| | |_ _ __   __ _ _ __     
 | |   | | | | | | | '_ \ / _` | '_ \    
 | |___| |_| | | | | | | | (_| | | | |   
 \_____\__, _|_|_|_|_| |_|\__,_|_| |_|  
```

# Cullinan

**A lightweight, modular Python web framework with enhanced architecture**

Cullinan is built on Tornado and SQLAlchemy, designed to help you quickly build production-ready web applications with modern patterns like dependency injection, lifecycle management, and unified registry.

---

## ✨ Features

### Core Framework
- 🚀 **Easy to Use** - Simple decorator-based routing and intuitive API
- 🏗️ **Modular Architecture** - Core module with registry, DI, and lifecycle management
- 🔌 **Dependency Injection** - Optional DI system for complex applications
- ♻️ **Lifecycle Hooks** - Proper initialization and cleanup with `on_init`/`on_destroy`
- 🧪 **Test-Friendly** - Built-in testing utilities with mocks and test registries

### Services & WebSocket
- 🎯 **Enhanced Service Layer** - Service registry with dependency resolution
- 🌐 **WebSocket Support** - Full WebSocket integration with registry pattern
- 📋 **Unified Registry** - Consistent registration across services, handlers, and WebSockets
- 🔄 **Request Context** - Thread-safe request-scoped data management

### Deployment & Production
- 📦 **Packaging Ready** - Full Nuitka & PyInstaller support
- ⚙️ **Flexible Configuration** - Multiple configuration options
- 🌍 **Cross-Platform** - Windows, Linux, macOS
- 🏭 **Production Ready** - Built on battle-tested Tornado
- 🗄️ **SQLAlchemy Integration** - Built-in ORM support
- 🔧 **Smart Module Loading** - Auto-discovery for development, configurable for packaging

---

## 📚 Documentation

### Language / 语言

- **[English Documentation](docs/README.md)** - Complete English documentation
- **[中文文档](docs/zh/README.md)** - 完整中文文档

### Current Version

- **v0.7x** - Enhanced architecture with modern features
  - Core module with registry, DI, and lifecycle management
  - Enhanced service layer with dependency injection
  - WebSocket support with unified registry
  - Request context management

### ⚠️ Important Notice (v0.7x)

**Deprecated modules have been removed:**
- `cullinan.registry` → Use `cullinan.handler` and `cullinan.controller`
- `cullinan.websocket` → Use `cullinan.websocket_registry`

**Migration required:** See [API Migration Guide](docs/API_MIGRATION_GUIDE.md) for details.

---

## 🚀 Quick Start

## 📦 Installation

### From PyPI (Stable Release)

```bash
pip install cullinan
```

### Basic Application

```python
# app.py
from cullinan import configure, application
from cullinan.controller import controller, get_api

# Configure for packaging (optional in development)
configure(user_packages=['your_app'])

@controller(url='/api')
class HelloController:
    @get_api(url='/hello')
    def hello(self, query_params):
        name = query_params.get('name', 'World')
        return self.response_build(
            status=200, 
            message=f"Hello, {name}!"
        )

if __name__ == '__main__':
    application.run()
```

### Run

```bash
python app.py
# Visit: http://localhost:4080/api/hello?name=Cullinan
```

---

## 💡 Full Example with Services

### Project Structure

```
my_app/
├── main.py              # Entry point
├── controllers/         # Controllers
│   ├── __init__.py
│   └── api.py
├── services/            # Services
│   ├── __init__.py
│   ├── user.py
│   └── email.py
└── models/              # Models
    ├── __init__.py
    └── user.py
```

### Service Layer (`services/email.py`)

```python
from cullinan import service, Service

@service
class EmailService(Service):
    """Service for sending emails."""
    
    def on_init(self):
        """Lifecycle hook - called when service is registered."""
        print("EmailService initialized")
    
    def send_email(self, to, subject, body):
        print(f"Sending email to {to}: {subject}")
        # Your email logic here
        return True
```

### Service with Dependencies (`services/user.py`)

```python
from cullinan import service, Service
from cullinan.core import InjectByName

@service
class UserService(Service):
    """Service for user management with email dependency."""
    
    # Modern dependency injection using InjectByName
    email_service = InjectByName('EmailService')
    
    def on_init(self):
        """Initialization hook - dependencies are already injected."""
        print("UserService initialized")
    
    def create_user(self, name, email):
        user = {'name': name, 'email': email}
        # Use injected service directly - no need for self.dependencies
        self.email_service.send_email(email, "Welcome", f"Welcome {name}!")
        return user
    
    def on_destroy(self):
        """Cleanup hook - called on shutdown."""
        print("UserService shutting down")
```

**Alternative: Type-based Injection with IDE Support**

```python
from cullinan import service, Service
from cullinan.core import Inject
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from services.email import EmailService

@service
class UserService(Service):
    """Service with type hints for better IDE support."""
    
    # Type-based injection with IDE autocomplete
    email_service: 'EmailService' = Inject()
    
    def create_user(self, name, email):
        # IDE now provides autocomplete for email_service methods
        self.email_service.send_email(email, "Welcome", f"Welcome {name}!")
        return {"name": name, "email": email}
```

### Controller (`controllers/api.py`)

```python
from cullinan.controller import controller, get_api, post_api
from cullinan.core import InjectByName

@controller(url='/api')
class UserController:
    """Controller with automatic service injection."""
    
    # Inject UserService using InjectByName
    user_service = InjectByName('UserService')
    
    @get_api(url='/users', query_params=['id'])
    def get_user(self, query_params):
        user_id = query_params.get('id')
        return self.response_build(
            status=200, 
            message="User fetched successfully",
            data={"user_id": user_id}
        )
    
    @post_api(url='/users', body_params=['name', 'email'])
    def create_user(self, body_params):
        # Use injected service directly - it's the actual UserService instance
        user = self.user_service.create_user(
            body_params['name'],
            body_params['email']
        )
        return self.response_build(
            status=201, 
            message="User created successfully",
            data=user
        )
```

**Note:** With InjectByName, `self.user_service` is the actual UserService instance, not a proxy. You can call all methods directly.

### WebSocket Support (`controllers/chat.py`)

```python
from cullinan import websocket_handler
from cullinan.core import InjectByName

@websocket_handler(url='/ws/chat')
class ChatHandler:
    """WebSocket handler with dependency injection support."""
    
    # Optional: Inject services if needed
    # user_service = InjectByName('UserService')
    
    def on_open(self):
        """Called when WebSocket connection opens."""
        self.clients = getattr(ChatHandler, 'clients', set())
        if not hasattr(ChatHandler, 'clients'):
            ChatHandler.clients = set()
        self.clients.add(self)
        print(f"Client connected: {len(self.clients)} total")
    
    def on_message(self, message):
        """Handle incoming WebSocket message."""
        # Broadcast to all clients
        for client in self.clients:
            try:
                client.write_message(f"Broadcast: {message}")
            except:
                pass
    
    def on_close(self):
        """Called when WebSocket connection closes."""
        if hasattr(self, 'clients'):
            self.clients.discard(self)
            print(f"Client disconnected: {len(self.clients)} remaining")
```

### Application (`main.py`)

```python
from cullinan import configure, application

# Configure for packaging
configure(
    user_packages=['my_app'],
    verbose=True  # Enable logging
)

def main():
    application.run()

if __name__ == '__main__':
    main()
```

---

## 📖 Additional Documentation

For detailed guides on advanced features, configuration, and deployment, please refer to the [complete documentation](docs/README.md).

Key topics include:
- **Configuration** - Environment setup and configuration options
- **Packaging** - Building standalone executables with Nuitka/PyInstaller
- **Service Layer** - Advanced service patterns and dependency injection
- **Registry Pattern** - Understanding the unified registry system
- **Testing** - Writing tests with mock services and test registries
- **Troubleshooting** - Common issues and solutions

---

## 🔗 Links

- **Documentation**: [docs/README.md](docs/README.md)
- **GitHub**: https://github.com/plumeink/Cullinan
- **PyPI**: https://pypi.org/project/cullinan/
- **Issues**: https://github.com/plumeink/Cullinan/issues
- **Discussions**: https://github.com/plumeink/Cullinan/discussions

---

## 📄 License

MIT License - see [LICENSE](LICENSE) for details.

---

## 🙏 Acknowledgments

- Built on [Tornado](https://www.tornadoweb.org/)
- ORM powered by [SQLAlchemy](https://www.sqlalchemy.org/)
- Packaging powered by [Nuitka](https://nuitka.net/) and [PyInstaller](https://pyinstaller.org/)

---

## 💻 Maintainer

[<img src="https://avatars.githubusercontent.com/u/104434649?v=4" width = "40" height = "40"/>](https://github.com/plumeink)
