PySpring is a Python web framework inspired by Spring Boot, combining FastAPI, and Pydantic for building scalable web applications with auto dependency injection, configuration management, and a web server.
Project description
PySpring Core
PySpring is a Python framework inspired by Spring Boot. It combines FastAPI for the web layer and Pydantic for data validation, providing a structured approach to building scalable web applications with automatic dependency injection, configuration management, component lifecycle hooks, event-driven architecture, and a built-in web server.
Key Features
-
IoC Container & Dependency Injection - Automatic dependency injection based on type annotations, supporting
Component,BeanCollection,Properties, and qualifier-based resolution viaAnnotated[T, 'qualifier']. -
Component Lifecycle - Components support
SingletonandPrototypescopes, withpost_construct()andpre_destroy()lifecycle hooks. -
REST Controllers - Declarative route definition using
@GetMapping,@PostMapping,@PutMapping,@DeleteMapping, and@PatchMappingdecorators with full FastAPI parameter support. -
Properties Management - Type-safe configuration via Pydantic-based
Propertiesclasses, loaded from a JSON properties file and automatically injected into components. -
Bean Collections - Factory-style bean registration using
BeanCollectionclasses withcreate_*methods and return type annotations. -
Middleware Support - Custom middleware via the
Middlewarebase class andMiddlewareConfigurationfor controlling registration order throughMiddlewareRegistry. -
Event System - Publish/subscribe event architecture using
ApplicationEventPublisher,ApplicationEvent, and the@EventListenerdecorator. -
Graceful Shutdown - Configurable graceful shutdown handling with
GracefulShutdownHandler, supporting SIGINT/SIGTERM signals with timeout management. -
Exception Handlers - Global exception handling with the
@ExceptionHandlerdecorator. -
Starter Modules - Extensible plugin system via
PySpringStarter, supporting manual registration and auto-discovery throughpyspring.startersentry points. -
Logging - Integrated Loguru-based logging with configurable log level, rotation, retention, file output, and JSON format support.
Project Structure
my-pyspring-app/
├── src/ # Application source code (configurable)
│ ├── controllers/
│ ├── components/
│ ├── properties/
│ └── ...
├── app-config.json # Application configuration
├── application-properties.json # Application properties
└── main.py # Application entry point
Getting Started
Prerequisites
- Python >= 3.11, < 3.13
Installation
pip install py-spring-core
Configuration
Create an app-config.json:
{
"app_src_target_dir": "./src",
"server_config": {
"host": "0.0.0.0",
"port": 8080,
"enabled": true
},
"properties_file_path": "./application-properties.json",
"loguru_config": {
"log_file_path": "./logs/app.log",
"log_level": "DEBUG"
},
"shutdown_config": {
"timeout_seconds": 30.0,
"enabled": true
}
}
Create an application-properties.json for your application properties:
{
"database": {
"host": "localhost",
"port": 5432,
"name": "mydb"
}
}
Application Entry Point
from py_spring_core import PySpringApplication
def main():
app = PySpringApplication("./app-config.json")
app.run()
if __name__ == "__main__":
main()
Defining a Component
from py_spring_core import Component
class UserService(Component):
def post_construct(self):
# Called after dependency injection
...
def pre_destroy(self):
# Called during shutdown
...
def get_user(self, user_id: int) -> dict:
return {"id": user_id, "name": "Alice"}
Defining Properties
from py_spring_core import Properties
class DatabaseProperties(Properties):
__key__ = "database"
host: str
port: int
name: str
Defining a REST Controller
from py_spring_core import RestController, GetMapping, PostMapping
class UserController(RestController):
class Config:
prefix = "/api/users"
# Dependencies are injected automatically by type annotation
user_service: UserService
@GetMapping("/{user_id}")
def get_user(self, user_id: int):
return self.user_service.get_user(user_id)
@PostMapping("/")
def create_user(self, name: str):
return {"name": name}
Defining a Bean Collection
from py_spring_core import BeanCollection
class AppBeans(BeanCollection):
def create_http_client(self) -> HttpClient:
return HttpClient(timeout=30)
Using the Event System
from py_spring_core import (
Component, ApplicationEvent, ApplicationEventPublisher, EventListener
)
class UserCreatedEvent(ApplicationEvent):
user_id: int
username: str
class NotificationService(Component):
@EventListener(UserCreatedEvent)
def on_user_created(self, event: UserCreatedEvent):
print(f"User created: {event.username}")
class UserService(Component):
event_publisher: ApplicationEventPublisher
def create_user(self, username: str):
self.event_publisher.publish(UserCreatedEvent(user_id=1, username=username))
Using Middleware
from fastapi import Request, Response
from py_spring_core import Middleware, MiddlewareConfiguration, MiddlewareRegistry
class AuthMiddleware(Middleware):
def should_skip(self, request: Request) -> bool:
return request.url.path == "/health"
async def process_request(self, request: Request) -> Response | None:
token = request.headers.get("Authorization")
if not token:
return Response(status_code=401, content="Unauthorized")
return None # Continue to next middleware/route
class AppMiddlewareConfig(MiddlewareConfiguration):
def configure_middlewares(self, registry: MiddlewareRegistry):
registry.add_middleware(AuthMiddleware)
Using Starters
from py_spring_core import PySpringStarter, PySpringApplication
class MyStarter(PySpringStarter):
def on_configure(self):
self.component_classes.append(MyComponent)
self.properties_classes.append(MyProperties)
def on_initialized(self):
# Called after IoC container is built
...
def on_destroy(self):
# Called during shutdown
...
# Manual registration
app = PySpringApplication("./app-config.json", starters=[MyStarter()])
app.run()
Component Scopes & Qualifiers
from typing import Annotated
from py_spring_core import Component, ComponentScope
class TransientService(Component):
class Config:
scope = ComponentScope.Prototype
class PrimaryCache(Component):
class Config:
name = "redis_cache"
class MyService(Component):
# Qualifier-based injection
cache: Annotated[CacheBase, "redis_cache"]
Graceful Shutdown
from py_spring_core import GracefulShutdownHandler, ShutdownType
class AppShutdownHandler(GracefulShutdownHandler):
def on_shutdown(self, shutdown_type: ShutdownType):
print(f"Shutting down: {shutdown_type}")
def on_timeout(self):
print("Shutdown timed out")
def on_error(self, error: Exception):
print(f"Shutdown error: {error}")
Configuration Reference
app-config.json
| Field | Type | Description |
|---|---|---|
app_src_target_dir |
str |
Directory containing application source code |
server_config.host |
str |
Server host address |
server_config.port |
int |
Server port number |
server_config.enabled |
bool |
Whether to start the web server (default: true) |
exclude_file_patterns |
list[str] |
Regex patterns for files to exclude from scanning (default: [".*/models\\.py$"]) |
properties_file_path |
str |
Path to the properties JSON file |
loguru_config.log_file_path |
str | null |
Log file path (set null to disable file logging) |
loguru_config.log_level |
str |
Log level: TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR, CRITICAL |
loguru_config.log_rotation |
str | null |
Log rotation interval (e.g. "1 day") |
loguru_config.log_retention |
str | null |
Log retention period (e.g. "7 days") |
loguru_config.format |
str |
Log format: text or json |
shutdown_config.timeout_seconds |
float |
Graceful shutdown timeout in seconds (default: 30.0) |
shutdown_config.enabled |
bool |
Whether shutdown timeout is enabled (default: true) |
Dependencies
Key runtime dependencies:
- FastAPI >= 0.136.1
- Pydantic >= 2.11.7
- Uvicorn >= 0.30.5
- Loguru >= 0.7.2
- PyYAML >= 6.0.2
- cachetools >= 5.5.0
For a complete list, see pyproject.toml.
Development
git clone https://github.com/NFUChen/PySpring.git
cd PySpring
pip install -e ".[dev]"
pytest
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_spring_core-0.3.7.tar.gz.
File metadata
- Download URL: py_spring_core-0.3.7.tar.gz
- Upload date:
- Size: 90.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
685f6c52cc26828eaa761184380735d755cc6ea2cddf73d8803659be0cde6ac9
|
|
| MD5 |
5d3c33a968c9529bb7bc0e2a0229cadf
|
|
| BLAKE2b-256 |
1c59a7bb8abe78971a016d38700eb6031f6ce7a07511da82c0b6727aa4a68dfd
|
File details
Details for the file py_spring_core-0.3.7-py3-none-any.whl.
File metadata
- Download URL: py_spring_core-0.3.7-py3-none-any.whl
- Upload date:
- Size: 53.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
faf0cc07cb25b6398b2f79e66f12be6e56b399fc5f56d6d8dd536af33420512b
|
|
| MD5 |
7aa22ecb1d19fef0e1091b9e99177950
|
|
| BLAKE2b-256 |
5f9f7077c448f86906db341f7b5027eadabf3aecce9a992b974a11af866ebe4a
|