starspring 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- starspring/__init__.py +150 -0
- starspring/application.py +421 -0
- starspring/client/__init__.py +1 -0
- starspring/client/rest_client.py +220 -0
- starspring/config/__init__.py +1 -0
- starspring/config/environment.py +81 -0
- starspring/config/properties.py +146 -0
- starspring/core/__init__.py +1 -0
- starspring/core/context.py +180 -0
- starspring/core/controller.py +47 -0
- starspring/core/exceptions.py +82 -0
- starspring/core/response.py +147 -0
- starspring/data/__init__.py +47 -0
- starspring/data/database_config.py +113 -0
- starspring/data/entity.py +365 -0
- starspring/data/orm_gateway.py +256 -0
- starspring/data/query_builder.py +345 -0
- starspring/data/repository.py +324 -0
- starspring/data/schema_generator.py +151 -0
- starspring/data/transaction.py +58 -0
- starspring/decorators/__init__.py +1 -0
- starspring/decorators/components.py +179 -0
- starspring/decorators/configuration.py +102 -0
- starspring/decorators/routing.py +306 -0
- starspring/decorators/validation.py +30 -0
- starspring/middleware/__init__.py +1 -0
- starspring/middleware/cors.py +90 -0
- starspring/middleware/exception.py +83 -0
- starspring/middleware/logging.py +60 -0
- starspring/template/__init__.py +19 -0
- starspring/template/engine.py +168 -0
- starspring/template/model_and_view.py +69 -0
- starspring-0.1.0.dist-info/METADATA +284 -0
- starspring-0.1.0.dist-info/RECORD +36 -0
- starspring-0.1.0.dist-info/WHEEL +5 -0
- starspring-0.1.0.dist-info/top_level.txt +1 -0
starspring/__init__.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StarSpring - Spring Boot-inspired Python Web Framework
|
|
3
|
+
|
|
4
|
+
A Python web framework built on Starlette that provides Spring Boot-inspired
|
|
5
|
+
syntax and patterns, including dependency injection, decorator-based routing,
|
|
6
|
+
repository patterns, ORM, template engine, and comprehensive middleware support.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from starspring.application import StarSpringApplication, create_application
|
|
10
|
+
|
|
11
|
+
# Core components
|
|
12
|
+
from starspring.core.context import ApplicationContext, BeanScope
|
|
13
|
+
from starspring.core.controller import BaseController
|
|
14
|
+
from starspring.core.response import ResponseEntity, ApiResponse
|
|
15
|
+
from starspring.core.exceptions import (
|
|
16
|
+
StarSpringException,
|
|
17
|
+
NotFoundException,
|
|
18
|
+
BadRequestException,
|
|
19
|
+
UnauthorizedException,
|
|
20
|
+
ForbiddenException,
|
|
21
|
+
ValidationException,
|
|
22
|
+
InternalServerException
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Decorators
|
|
26
|
+
from starspring.decorators.components import (
|
|
27
|
+
Controller,
|
|
28
|
+
TemplateController,
|
|
29
|
+
Service,
|
|
30
|
+
Component,
|
|
31
|
+
Repository,
|
|
32
|
+
Autowired
|
|
33
|
+
)
|
|
34
|
+
from starspring.decorators.routing import (
|
|
35
|
+
GetMapping,
|
|
36
|
+
PostMapping,
|
|
37
|
+
PutMapping,
|
|
38
|
+
DeleteMapping,
|
|
39
|
+
PatchMapping,
|
|
40
|
+
RequestMapping
|
|
41
|
+
)
|
|
42
|
+
from starspring.decorators.configuration import (
|
|
43
|
+
Configuration,
|
|
44
|
+
Bean,
|
|
45
|
+
Value
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Data layer
|
|
49
|
+
from starspring.data.repository import Repository as RepositoryBase, CrudRepository, StarRepository
|
|
50
|
+
from starspring.data.entity import (
|
|
51
|
+
Entity,
|
|
52
|
+
Column,
|
|
53
|
+
Id,
|
|
54
|
+
GeneratedValue,
|
|
55
|
+
ManyToOne,
|
|
56
|
+
OneToMany,
|
|
57
|
+
ManyToMany,
|
|
58
|
+
BaseEntity,
|
|
59
|
+
GenerationType
|
|
60
|
+
)
|
|
61
|
+
from starspring.data.transaction import Transactional
|
|
62
|
+
|
|
63
|
+
# Template engine
|
|
64
|
+
from starspring.template import ModelAndView, TemplateEngine, render_template
|
|
65
|
+
|
|
66
|
+
# Middleware
|
|
67
|
+
from starspring.middleware.cors import enable_cors, CORSConfig
|
|
68
|
+
|
|
69
|
+
# Configuration
|
|
70
|
+
from starspring.config.properties import ApplicationProperties
|
|
71
|
+
from starspring.config.environment import Environment
|
|
72
|
+
|
|
73
|
+
# Client
|
|
74
|
+
from starspring.client.rest_client import RestTemplate
|
|
75
|
+
|
|
76
|
+
__version__ = "0.1.0"
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
# Application
|
|
80
|
+
'StarSpringApplication',
|
|
81
|
+
'create_application',
|
|
82
|
+
|
|
83
|
+
# Core
|
|
84
|
+
'ApplicationContext',
|
|
85
|
+
'BeanScope',
|
|
86
|
+
'BaseController',
|
|
87
|
+
'ResponseEntity',
|
|
88
|
+
'ApiResponse',
|
|
89
|
+
|
|
90
|
+
# Exceptions
|
|
91
|
+
'StarSpringException',
|
|
92
|
+
'NotFoundException',
|
|
93
|
+
'BadRequestException',
|
|
94
|
+
'UnauthorizedException',
|
|
95
|
+
'ForbiddenException',
|
|
96
|
+
'ValidationException',
|
|
97
|
+
'InternalServerException',
|
|
98
|
+
|
|
99
|
+
# Component decorators
|
|
100
|
+
'Controller',
|
|
101
|
+
'TemplateController',
|
|
102
|
+
'Service',
|
|
103
|
+
'Component',
|
|
104
|
+
'Repository',
|
|
105
|
+
'Autowired',
|
|
106
|
+
|
|
107
|
+
# Routing decorators
|
|
108
|
+
'GetMapping',
|
|
109
|
+
'PostMapping',
|
|
110
|
+
'PutMapping',
|
|
111
|
+
'DeleteMapping',
|
|
112
|
+
'PatchMapping',
|
|
113
|
+
'RequestMapping',
|
|
114
|
+
|
|
115
|
+
# Configuration decorators
|
|
116
|
+
'Configuration',
|
|
117
|
+
'Bean',
|
|
118
|
+
'Value',
|
|
119
|
+
|
|
120
|
+
# Data layer
|
|
121
|
+
'RepositoryBase',
|
|
122
|
+
'CrudRepository',
|
|
123
|
+
'StarRepository',
|
|
124
|
+
'Entity',
|
|
125
|
+
'Column',
|
|
126
|
+
'Id',
|
|
127
|
+
'GeneratedValue',
|
|
128
|
+
'ManyToOne',
|
|
129
|
+
'OneToMany',
|
|
130
|
+
'ManyToMany',
|
|
131
|
+
'BaseEntity',
|
|
132
|
+
'GenerationType',
|
|
133
|
+
'Transactional',
|
|
134
|
+
|
|
135
|
+
# Template engine
|
|
136
|
+
'ModelAndView',
|
|
137
|
+
'TemplateEngine',
|
|
138
|
+
'render_template',
|
|
139
|
+
|
|
140
|
+
# Middleware
|
|
141
|
+
'enable_cors',
|
|
142
|
+
'CORSConfig',
|
|
143
|
+
|
|
144
|
+
# Configuration
|
|
145
|
+
'ApplicationProperties',
|
|
146
|
+
'Environment',
|
|
147
|
+
|
|
148
|
+
# Client
|
|
149
|
+
'RestTemplate',
|
|
150
|
+
]
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StarSpring Application
|
|
3
|
+
|
|
4
|
+
Main application class that bootstraps the framework.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import inspect
|
|
8
|
+
import logging
|
|
9
|
+
from typing import List, Type, Optional, Callable
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from starlette.applications import Starlette
|
|
13
|
+
from starlette.routing import Route, Mount
|
|
14
|
+
from starlette.staticfiles import StaticFiles
|
|
15
|
+
from starlette.middleware import Middleware
|
|
16
|
+
|
|
17
|
+
from starspring.core.context import ApplicationContext, get_application_context, set_application_context
|
|
18
|
+
from starspring.config.properties import ApplicationProperties, set_properties
|
|
19
|
+
from starspring.config.environment import Environment, set_environment
|
|
20
|
+
from starspring.middleware.exception import ExceptionHandlerMiddleware
|
|
21
|
+
from starspring.middleware.cors import CORSConfig, create_cors_middleware
|
|
22
|
+
from starspring.middleware.logging import LoggingMiddleware
|
|
23
|
+
from starspring.decorators.routing import create_route_handler
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StarSpringApplication:
|
|
30
|
+
"""
|
|
31
|
+
Main application class
|
|
32
|
+
|
|
33
|
+
Bootstraps the StarSpring framework and creates a Starlette ASGI application.
|
|
34
|
+
Similar to Spring Boot's SpringApplication.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
app = StarSpringApplication()
|
|
38
|
+
app.scan_components("myapp.controllers")
|
|
39
|
+
app.add_cors(enable_cors())
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
app.run()
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
config_path: Optional[str] = None,
|
|
48
|
+
debug: bool = None,
|
|
49
|
+
title: str = "StarSpring Application",
|
|
50
|
+
version: str = "1.0.0"
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Initialize the application
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
config_path: Path to configuration file
|
|
57
|
+
debug: Debug mode (auto-detected from environment if None)
|
|
58
|
+
title: Application title
|
|
59
|
+
version: Application version
|
|
60
|
+
"""
|
|
61
|
+
# Initialize environment
|
|
62
|
+
self.environment = Environment()
|
|
63
|
+
set_environment(self.environment)
|
|
64
|
+
|
|
65
|
+
# Set debug mode
|
|
66
|
+
if debug is None:
|
|
67
|
+
debug = self.environment.is_development()
|
|
68
|
+
self.debug = debug
|
|
69
|
+
|
|
70
|
+
# Initialize application context (reuse existing if present)
|
|
71
|
+
existing_context = None
|
|
72
|
+
try:
|
|
73
|
+
existing_context = get_application_context()
|
|
74
|
+
except:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
if existing_context:
|
|
78
|
+
self.context = existing_context
|
|
79
|
+
else:
|
|
80
|
+
self.context = ApplicationContext()
|
|
81
|
+
set_application_context(self.context)
|
|
82
|
+
|
|
83
|
+
# Initialize properties
|
|
84
|
+
self.properties = ApplicationProperties()
|
|
85
|
+
if config_path:
|
|
86
|
+
self.properties.load(config_path)
|
|
87
|
+
set_properties(self.properties)
|
|
88
|
+
|
|
89
|
+
# Initialize database if configured
|
|
90
|
+
self._init_database()
|
|
91
|
+
|
|
92
|
+
# Application metadata
|
|
93
|
+
self.title = title
|
|
94
|
+
self.version = version
|
|
95
|
+
|
|
96
|
+
# Middleware stack
|
|
97
|
+
self._middleware: List[Middleware] = []
|
|
98
|
+
|
|
99
|
+
# Routes
|
|
100
|
+
self._routes: List[Route] = []
|
|
101
|
+
|
|
102
|
+
# Static files
|
|
103
|
+
self._static_mounts: List[Mount] = []
|
|
104
|
+
|
|
105
|
+
# Lifecycle hooks
|
|
106
|
+
self._startup_hooks: List[Callable] = []
|
|
107
|
+
self._shutdown_hooks: List[Callable] = []
|
|
108
|
+
|
|
109
|
+
# Add default middleware
|
|
110
|
+
self._add_default_middleware()
|
|
111
|
+
|
|
112
|
+
# Starlette app (created on demand)
|
|
113
|
+
self._app: Optional[Starlette] = None
|
|
114
|
+
|
|
115
|
+
def _add_default_middleware(self):
|
|
116
|
+
"""Add default middleware"""
|
|
117
|
+
# Exception handler (should be first)
|
|
118
|
+
self._middleware.append(
|
|
119
|
+
Middleware(ExceptionHandlerMiddleware, debug=self.debug)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Logging middleware
|
|
123
|
+
self._middleware.append(
|
|
124
|
+
Middleware(LoggingMiddleware)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def _init_database(self):
|
|
128
|
+
"""Initialize database connection if configured"""
|
|
129
|
+
try:
|
|
130
|
+
# Check if database URL is configured
|
|
131
|
+
db_url = self.properties.get("database.url")
|
|
132
|
+
if not db_url:
|
|
133
|
+
logger.info("No database configured (database.url not found)")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# Import database modules
|
|
137
|
+
from starspring.data.database_config import DatabaseConfig
|
|
138
|
+
from starspring.data.orm_gateway import SQLAlchemyGateway, set_orm_gateway
|
|
139
|
+
|
|
140
|
+
# Initialize database configuration
|
|
141
|
+
db_config = DatabaseConfig()
|
|
142
|
+
|
|
143
|
+
# Create and set ORM gateway
|
|
144
|
+
gateway = SQLAlchemyGateway(db_config.SessionFactory)
|
|
145
|
+
set_orm_gateway(gateway)
|
|
146
|
+
|
|
147
|
+
logger.info("Database initialized successfully")
|
|
148
|
+
|
|
149
|
+
except ImportError as e:
|
|
150
|
+
logger.warning(f"Database dependencies not installed: {e}")
|
|
151
|
+
logger.warning("Install with: pip install sqlalchemy")
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error(f"Failed to initialize database: {e}")
|
|
154
|
+
if self.debug:
|
|
155
|
+
raise
|
|
156
|
+
|
|
157
|
+
def _auto_create_tables_if_enabled(self):
|
|
158
|
+
"""
|
|
159
|
+
Automatically create database tables from entity metadata
|
|
160
|
+
|
|
161
|
+
Similar to Spring Boot's ddl-auto feature.
|
|
162
|
+
Called after component scanning when entity classes are available.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
from starspring.data.orm_gateway import get_orm_gateway
|
|
166
|
+
from starspring.data.entity import mapper_registry
|
|
167
|
+
|
|
168
|
+
# Check if ddl-auto is enabled
|
|
169
|
+
ddl_auto = self.properties.get("database.ddl-auto", "create-if-not-exists")
|
|
170
|
+
if ddl_auto not in ["create", "create-if-not-exists", "update"]:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Get ORM gateway
|
|
174
|
+
try:
|
|
175
|
+
gateway = get_orm_gateway()
|
|
176
|
+
except RuntimeError:
|
|
177
|
+
# No database configured
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
# Use SQLAlchemy's metadata.create_all to create tables
|
|
181
|
+
# This uses the imperative mappings we set up in the @Entity decorator
|
|
182
|
+
# We get the engine from the gateway's session
|
|
183
|
+
engine = gateway.session.get_bind()
|
|
184
|
+
mapper_registry.metadata.create_all(engine)
|
|
185
|
+
|
|
186
|
+
logger.info("Auto-created tables using SQLAlchemy metadata")
|
|
187
|
+
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.error(f"Failed to auto-create tables: {e}")
|
|
190
|
+
if self.debug:
|
|
191
|
+
raise
|
|
192
|
+
|
|
193
|
+
def add_cors(self, config: CORSConfig) -> 'StarSpringApplication':
|
|
194
|
+
"""
|
|
195
|
+
Add CORS middleware
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
config: CORS configuration
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Self for chaining
|
|
202
|
+
"""
|
|
203
|
+
# CORS should be added early in the middleware stack
|
|
204
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
205
|
+
self._middleware.insert(1, Middleware(
|
|
206
|
+
CORSMiddleware,
|
|
207
|
+
**config.to_middleware_kwargs()
|
|
208
|
+
))
|
|
209
|
+
return self
|
|
210
|
+
|
|
211
|
+
def add_middleware(self, middleware_class, **options) -> 'StarSpringApplication':
|
|
212
|
+
"""
|
|
213
|
+
Add custom middleware
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
middleware_class: Middleware class
|
|
217
|
+
**options: Middleware options
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Self for chaining
|
|
221
|
+
"""
|
|
222
|
+
self._middleware.append(Middleware(middleware_class, **options))
|
|
223
|
+
return self
|
|
224
|
+
|
|
225
|
+
def scan_components(self, *module_paths: str) -> 'StarSpringApplication':
|
|
226
|
+
"""
|
|
227
|
+
Scan and register components from modules
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
*module_paths: Module paths to scan (e.g., "myapp.controllers")
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Self for chaining
|
|
234
|
+
"""
|
|
235
|
+
import importlib
|
|
236
|
+
import pkgutil
|
|
237
|
+
|
|
238
|
+
# Initialize scanned modules list if not exists
|
|
239
|
+
if not hasattr(self, '_scanned_modules'):
|
|
240
|
+
self._scanned_modules = []
|
|
241
|
+
|
|
242
|
+
for module_path in module_paths:
|
|
243
|
+
try:
|
|
244
|
+
# Import the module
|
|
245
|
+
module = importlib.import_module(module_path)
|
|
246
|
+
self._scanned_modules.append(module) # Track module
|
|
247
|
+
print(f"DEBUG: Scanning module {module_path}")
|
|
248
|
+
print(f"DEBUG: Current beans: {list(self.context._beans.keys())}")
|
|
249
|
+
|
|
250
|
+
# Scan for controllers and register routes
|
|
251
|
+
for name, obj in inspect.getmembers(module, inspect.isclass):
|
|
252
|
+
if hasattr(obj, '_is_controller'):
|
|
253
|
+
print(f"DEBUG: Found controller: {name}")
|
|
254
|
+
self._register_controller(obj)
|
|
255
|
+
|
|
256
|
+
# If it's a package, scan submodules
|
|
257
|
+
if hasattr(module, '__path__'):
|
|
258
|
+
for importer, modname, ispkg in pkgutil.walk_packages(
|
|
259
|
+
path=module.__path__,
|
|
260
|
+
prefix=module.__name__ + '.',
|
|
261
|
+
):
|
|
262
|
+
submodule = importlib.import_module(modname)
|
|
263
|
+
self._scanned_modules.append(submodule) # Track submodule
|
|
264
|
+
for name, obj in inspect.getmembers(submodule, inspect.isclass):
|
|
265
|
+
if hasattr(obj, '_is_controller'):
|
|
266
|
+
self._register_controller(obj)
|
|
267
|
+
|
|
268
|
+
except ImportError as e:
|
|
269
|
+
print(f"Warning: Could not import module {module_path}: {e}")
|
|
270
|
+
|
|
271
|
+
# Auto-create database tables from entities
|
|
272
|
+
self._auto_create_tables_if_enabled()
|
|
273
|
+
|
|
274
|
+
return self
|
|
275
|
+
|
|
276
|
+
def _register_controller(self, controller_class: Type):
|
|
277
|
+
"""Register a controller and its routes"""
|
|
278
|
+
# Get controller instance from context
|
|
279
|
+
controller = self.context.get_bean(controller_class)
|
|
280
|
+
|
|
281
|
+
# Get controller prefix
|
|
282
|
+
prefix = getattr(controller_class, '_controller_prefix', '')
|
|
283
|
+
|
|
284
|
+
# Scan for route methods
|
|
285
|
+
for name, method in inspect.getmembers(controller_class, inspect.isfunction):
|
|
286
|
+
if hasattr(method, '_route_path'):
|
|
287
|
+
path = method._route_path
|
|
288
|
+
methods = method._route_methods
|
|
289
|
+
|
|
290
|
+
# Combine prefix and path
|
|
291
|
+
full_path = f"{prefix}{path}".replace('//', '/')
|
|
292
|
+
|
|
293
|
+
# Get the bound method and wrap it with request handling
|
|
294
|
+
bound_method = getattr(controller, name)
|
|
295
|
+
handler = create_route_handler(bound_method)
|
|
296
|
+
|
|
297
|
+
# Create route
|
|
298
|
+
route = Route(
|
|
299
|
+
full_path,
|
|
300
|
+
endpoint=handler,
|
|
301
|
+
methods=methods
|
|
302
|
+
)
|
|
303
|
+
self._routes.append(route)
|
|
304
|
+
print(f"DEBUG: Registered route: {full_path} -> {methods}")
|
|
305
|
+
|
|
306
|
+
def add_static_files(
|
|
307
|
+
self,
|
|
308
|
+
path: str,
|
|
309
|
+
directory: str,
|
|
310
|
+
name: str = "static"
|
|
311
|
+
) -> 'StarSpringApplication':
|
|
312
|
+
"""
|
|
313
|
+
Add static file serving
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
path: URL path prefix
|
|
317
|
+
directory: Directory to serve files from
|
|
318
|
+
name: Mount name
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Self for chaining
|
|
322
|
+
"""
|
|
323
|
+
self._static_mounts.append(
|
|
324
|
+
Mount(path, StaticFiles(directory=directory), name=name)
|
|
325
|
+
)
|
|
326
|
+
return self
|
|
327
|
+
|
|
328
|
+
def on_startup(self, func: Callable) -> 'StarSpringApplication':
|
|
329
|
+
"""
|
|
330
|
+
Register a startup hook
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
func: Function to call on startup
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Self for chaining
|
|
337
|
+
"""
|
|
338
|
+
self._startup_hooks.append(func)
|
|
339
|
+
return self
|
|
340
|
+
|
|
341
|
+
def on_shutdown(self, func: Callable) -> 'StarSpringApplication':
|
|
342
|
+
"""
|
|
343
|
+
Register a shutdown hook
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
func: Function to call on shutdown
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Self for chaining
|
|
350
|
+
"""
|
|
351
|
+
self._shutdown_hooks.append(func)
|
|
352
|
+
return self
|
|
353
|
+
|
|
354
|
+
def build(self) -> Starlette:
|
|
355
|
+
"""
|
|
356
|
+
Build the Starlette application
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Configured Starlette application
|
|
360
|
+
"""
|
|
361
|
+
if self._app is None:
|
|
362
|
+
# Combine routes and static mounts
|
|
363
|
+
all_routes = self._routes + self._static_mounts
|
|
364
|
+
|
|
365
|
+
# Create Starlette app
|
|
366
|
+
self._app = Starlette(
|
|
367
|
+
debug=self.debug,
|
|
368
|
+
routes=all_routes,
|
|
369
|
+
middleware=self._middleware,
|
|
370
|
+
on_startup=self._startup_hooks,
|
|
371
|
+
on_shutdown=self._shutdown_hooks,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return self._app
|
|
375
|
+
|
|
376
|
+
def run(
|
|
377
|
+
self,
|
|
378
|
+
host: str = "0.0.0.0",
|
|
379
|
+
port: int = 8000,
|
|
380
|
+
**uvicorn_options
|
|
381
|
+
):
|
|
382
|
+
"""
|
|
383
|
+
Run the application with Uvicorn
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
host: Host to bind to
|
|
387
|
+
port: Port to bind to
|
|
388
|
+
**uvicorn_options: Additional Uvicorn options
|
|
389
|
+
"""
|
|
390
|
+
import uvicorn
|
|
391
|
+
|
|
392
|
+
app = self.build()
|
|
393
|
+
|
|
394
|
+
uvicorn.run(
|
|
395
|
+
app,
|
|
396
|
+
host=host,
|
|
397
|
+
port=port,
|
|
398
|
+
**uvicorn_options
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def app(self) -> Starlette:
|
|
403
|
+
"""Get the Starlette application"""
|
|
404
|
+
return self.build()
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def create_application(
|
|
408
|
+
config_path: Optional[str] = None,
|
|
409
|
+
**kwargs
|
|
410
|
+
) -> StarSpringApplication:
|
|
411
|
+
"""
|
|
412
|
+
Factory function to create a StarSpring application
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
config_path: Path to configuration file
|
|
416
|
+
**kwargs: Additional application options
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
StarSpring application instance
|
|
420
|
+
"""
|
|
421
|
+
return StarSpringApplication(config_path=config_path, **kwargs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""API client components"""
|