Skip to main content

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 via Annotated[T, 'qualifier'].

  • Component Lifecycle - Components support Singleton and Prototype scopes, with post_construct() and pre_destroy() lifecycle hooks.

  • REST Controllers - Declarative route definition using @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, and @PatchMapping decorators with full FastAPI parameter support.

  • Properties Management - Type-safe configuration via Pydantic-based Properties classes, loaded from a JSON properties file and automatically injected into components.

  • Bean Collections - Factory-style bean registration using BeanCollection classes with create_* methods and return type annotations.

  • Middleware Support - Custom middleware via the Middleware base class and MiddlewareConfiguration for controlling registration order through MiddlewareRegistry.

  • Event System - Publish/subscribe event architecture using ApplicationEventPublisher, ApplicationEvent, and the @EventListener decorator.

  • Graceful Shutdown - Configurable graceful shutdown handling with GracefulShutdownHandler, supporting SIGINT/SIGTERM signals with timeout management.

  • Exception Handlers - Global exception handling with the @ExceptionHandler decorator.

  • Starter Modules - Extensible plugin system via PySpringStarter, supporting manual registration and auto-discovery through pyspring.starters entry 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py_spring_core-0.3.6.tar.gz (87.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_spring_core-0.3.6-py3-none-any.whl (52.2 kB view details)

Uploaded Python 3

File details

Details for the file py_spring_core-0.3.6.tar.gz.

File metadata

  • Download URL: py_spring_core-0.3.6.tar.gz
  • Upload date:
  • Size: 87.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for py_spring_core-0.3.6.tar.gz
Algorithm Hash digest
SHA256 81b042a86fb96521249ad720d196992e706e249ec3200bfd2c6dcc99f00c3c93
MD5 9f4da09199ed265f54cd7f67563fea54
BLAKE2b-256 c4429c616e36d6830dd3a8e665de3aff107a20951668ae572d63f7d93a567b82

See more details on using hashes here.

File details

Details for the file py_spring_core-0.3.6-py3-none-any.whl.

File metadata

  • Download URL: py_spring_core-0.3.6-py3-none-any.whl
  • Upload date:
  • Size: 52.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for py_spring_core-0.3.6-py3-none-any.whl
Algorithm Hash digest
SHA256 165310f73a4f93c1a71a3f8fbc045c00a6b7b4269f9b7c8c24c3984d3530b3e3
MD5 8a6d037fee986220de37f0cbd750eebe
BLAKE2b-256 9470aee6e70ec0fa1517d39d281944e37216d68d10b6c25dc60109001f1d7014

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page