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.
@@ -0,0 +1,168 @@
1
+ """
2
+ Template engine for rendering HTML templates
3
+
4
+ Integrates Jinja2 with Spring Boot-style patterns.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional
8
+ from pathlib import Path
9
+ import os
10
+
11
+
12
+ try:
13
+ from jinja2 import Environment, FileSystemLoader, select_autoescape, Template
14
+ JINJA2_AVAILABLE = True
15
+ except ImportError:
16
+ JINJA2_AVAILABLE = False
17
+ Environment = None
18
+ FileSystemLoader = None
19
+
20
+
21
+ class TemplateEngine:
22
+ """
23
+ Template rendering engine
24
+
25
+ Wraps Jinja2 with Spring Boot-style configuration.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ template_dir: str = "templates",
31
+ auto_reload: bool = True,
32
+ cache_size: int = 400
33
+ ):
34
+ """
35
+ Initialize template engine
36
+
37
+ Args:
38
+ template_dir: Directory containing templates
39
+ auto_reload: Whether to reload templates on change
40
+ cache_size: Template cache size
41
+ """
42
+ if not JINJA2_AVAILABLE:
43
+ raise ImportError(
44
+ "Jinja2 is required for template rendering. "
45
+ "Install it with: pip install jinja2"
46
+ )
47
+
48
+ self.template_dir = template_dir
49
+
50
+ # Create template directory if it doesn't exist
51
+ Path(template_dir).mkdir(parents=True, exist_ok=True)
52
+
53
+ # Initialize Jinja2 environment
54
+ self.env = Environment(
55
+ loader=FileSystemLoader(template_dir),
56
+ autoescape=select_autoescape(['html', 'xml']),
57
+ auto_reload=auto_reload,
58
+ cache_size=cache_size
59
+ )
60
+
61
+ # Add custom filters and functions
62
+ self._setup_filters()
63
+
64
+ def _setup_filters(self):
65
+ """Setup custom Jinja2 filters"""
66
+ # Add custom filters here
67
+ self.env.filters['format_date'] = self._format_date
68
+ self.env.filters['format_currency'] = self._format_currency
69
+
70
+ def _format_date(self, value, format='%Y-%m-%d'):
71
+ """Format datetime objects"""
72
+ if value is None:
73
+ return ''
74
+ try:
75
+ return value.strftime(format)
76
+ except:
77
+ return str(value)
78
+
79
+ def _format_currency(self, value, currency='$'):
80
+ """Format currency values"""
81
+ if value is None:
82
+ return ''
83
+ try:
84
+ return f"{currency}{value:,.2f}"
85
+ except:
86
+ return str(value)
87
+
88
+ def render(self, template_name: str, context: Dict[str, Any] = None) -> str:
89
+ """
90
+ Render a template
91
+
92
+ Args:
93
+ template_name: Template file name
94
+ context: Template context variables
95
+
96
+ Returns:
97
+ Rendered HTML string
98
+ """
99
+ context = context or {}
100
+ template = self.env.get_template(template_name)
101
+ return template.render(**context)
102
+
103
+ def render_string(self, template_string: str, context: Dict[str, Any] = None) -> str:
104
+ """
105
+ Render a template from string
106
+
107
+ Args:
108
+ template_string: Template content as string
109
+ context: Template context variables
110
+
111
+ Returns:
112
+ Rendered HTML string
113
+ """
114
+ context = context or {}
115
+ template = self.env.from_string(template_string)
116
+ return template.render(**context)
117
+
118
+ def add_global(self, name: str, value: Any):
119
+ """
120
+ Add a global variable to all templates
121
+
122
+ Args:
123
+ name: Variable name
124
+ value: Variable value
125
+ """
126
+ self.env.globals[name] = value
127
+
128
+ def add_filter(self, name: str, func: callable):
129
+ """
130
+ Add a custom filter
131
+
132
+ Args:
133
+ name: Filter name
134
+ func: Filter function
135
+ """
136
+ self.env.filters[name] = func
137
+
138
+
139
+ # Global template engine instance
140
+ _template_engine: Optional[TemplateEngine] = None
141
+
142
+
143
+ def get_template_engine() -> TemplateEngine:
144
+ """Get the global template engine instance"""
145
+ global _template_engine
146
+ if _template_engine is None:
147
+ _template_engine = TemplateEngine()
148
+ return _template_engine
149
+
150
+
151
+ def set_template_engine(engine: TemplateEngine):
152
+ """Set the global template engine instance"""
153
+ global _template_engine
154
+ _template_engine = engine
155
+
156
+
157
+ def render_template(template_name: str, context: Dict[str, Any] = None) -> str:
158
+ """
159
+ Convenience function to render a template
160
+
161
+ Args:
162
+ template_name: Template file name
163
+ context: Template context variables
164
+
165
+ Returns:
166
+ Rendered HTML string
167
+ """
168
+ return get_template_engine().render(template_name, context)
@@ -0,0 +1,69 @@
1
+ """
2
+ Template engine integration
3
+
4
+ Provides Jinja2 template rendering with Spring Boot-style patterns.
5
+ """
6
+
7
+ from typing import Dict, Any, Optional
8
+ from pathlib import Path
9
+ import os
10
+
11
+
12
+ class ModelAndView:
13
+ """
14
+ Container for model and view name
15
+
16
+ Similar to Spring MVC's ModelAndView.
17
+
18
+ Example:
19
+ @GetMapping("/users")
20
+ def list_users(self) -> ModelAndView:
21
+ users = self.user_service.find_all()
22
+ return ModelAndView("users/list.html", {"users": users})
23
+ """
24
+
25
+ def __init__(self, view_name: str, model: Optional[Dict[str, Any]] = None):
26
+ """
27
+ Initialize ModelAndView
28
+
29
+ Args:
30
+ view_name: Template file name
31
+ model: Dictionary of model attributes
32
+ """
33
+ self.view_name = view_name
34
+ self.model = model or {}
35
+
36
+ def add_object(self, key: str, value: Any) -> 'ModelAndView':
37
+ """
38
+ Add an object to the model
39
+
40
+ Args:
41
+ key: Attribute name
42
+ value: Attribute value
43
+
44
+ Returns:
45
+ Self for method chaining
46
+ """
47
+ self.model[key] = value
48
+ return self
49
+
50
+ def add_all_objects(self, objects: Dict[str, Any]) -> 'ModelAndView':
51
+ """
52
+ Add multiple objects to the model
53
+
54
+ Args:
55
+ objects: Dictionary of attributes
56
+
57
+ Returns:
58
+ Self for method chaining
59
+ """
60
+ self.model.update(objects)
61
+ return self
62
+
63
+ def get_model(self) -> Dict[str, Any]:
64
+ """Get the model dictionary"""
65
+ return self.model
66
+
67
+ def get_view_name(self) -> str:
68
+ """Get the view name"""
69
+ return self.view_name
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: starspring
3
+ Version: 0.1.0
4
+ Summary: A Spring Boot-inspired Python web framework built on Starlette
5
+ Author: StarSpring Contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/starspring
8
+ Project-URL: Documentation, https://github.com/yourusername/starspring#readme
9
+ Project-URL: Repository, https://github.com/yourusername/starspring
10
+ Keywords: web,framework,starlette,spring-boot,dependency-injection
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
19
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: starlette>=0.27.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Requires-Dist: uvicorn[standard]>=0.23.0
25
+ Requires-Dist: python-multipart>=0.0.6
26
+ Requires-Dist: pyyaml>=6.0
27
+ Requires-Dist: httpx>=0.24.0
28
+ Requires-Dist: jinja2>=3.1.6
29
+ Requires-Dist: sqlalchemy>=2.0.46
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
32
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
33
+ Requires-Dist: black>=23.7.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
35
+ Provides-Extra: sqlalchemy
36
+ Requires-Dist: sqlalchemy>=2.0.0; extra == "sqlalchemy"
37
+
38
+ # StarSpring Framework
39
+
40
+ [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
41
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
42
+ [![Status](https://img.shields.io/badge/status-alpha-orange)](https://pypi.org/project/starspring/)
43
+
44
+ **StarSpring** is a production-grade, asynchronous web framework for Python that brings the robust architectural patterns of **Spring Boot** to the modern Python ecosystem. Built on top of **Starlette** and **SQLAlchemy**, it combines enterprise structure with Pythonic elegance.
45
+
46
+ ---
47
+
48
+ ## 🌟 Features
49
+
50
+ * **Dependency Injection (IoC)**: Fully typed, automatic constructor injection. No more global state or manual wiring.
51
+ * **Declarative Routing**: Use `@GetMapping`, `@PostMapping` decorators for clean, readable controllers.
52
+ * **Enterprise ORM**: Built on **SQLAlchemy** with **Imperative Mapping**. Define simple Python classes, and they become powerful database entities automatically.
53
+ * **Magic Repositories**: Define interfaces, and StarSpring implements the queries for you (e.g., `find_by_email_and_active(email, True)`).
54
+ * **Robust Transaction Management**: `@Transactional` decorators with support for robust nested transactions (`SAVEPOINT`s).
55
+ * **Production Ready**: Built-in support for CORS, Exception Handling, Logging, and Configuration Management (`application.yaml`).
56
+
57
+ ---
58
+
59
+ ## 📦 Installation
60
+
61
+ StarSpring requires Python 3.10+.
62
+
63
+ ### Using pip
64
+ ```bash
65
+ pip install starspring
66
+ ```
67
+
68
+ ### Using uv (Recommended for speed)
69
+ ```bash
70
+ uv pip install starspring
71
+ ```
72
+
73
+ ### Database Drivers
74
+ StarSpring uses SQLAlchemy. Install the driver for your database:
75
+ ```bash
76
+ # SQLite (Standard)
77
+ pip install starspring
78
+
79
+ # PostgreSQL
80
+ pip install psycopg2-binary
81
+ # or
82
+ pip install asyncpg
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 🚀 Building Your First Application
88
+
89
+ Here is a complete walkthrough of building a User Management API.
90
+
91
+ ### 1. Project Structure
92
+ We recommend a standard layered architecture:
93
+ ```text
94
+ my_app/
95
+ ├── __init__.py
96
+ ├── main.py # Entry point
97
+ ├── application.yaml # Configuration
98
+ ├── entities.py # Database Models
99
+ ├── repositories.py # Data Access
100
+ ├── services.py # Business Logic
101
+ └── controllers.py # REST Endpoints
102
+ ```
103
+
104
+ ### 2. Configuration (`application.yaml`)
105
+ Configure your server and database connection.
106
+
107
+ ```yaml
108
+ server:
109
+ port: 8000
110
+ host: 0.0.0.0
111
+
112
+ database:
113
+ url: "sqlite:///app.db" # or postgresql://user:pass@localhost/db
114
+ ddl-auto: "create-if-not-exists" # Auto-creates tables from entities
115
+ ```
116
+
117
+ ### 3. Entities (`entities.py`)
118
+ Define your database models using standard Python classes. StarSpring maps them automatically.
119
+
120
+ ```python
121
+ from starspring import Entity, BaseEntity, Column, Id, GeneratedValue
122
+ from datetime import datetime
123
+
124
+ @Entity(table_name="users")
125
+ class User(BaseEntity):
126
+ # BaseEntity automatically adds 'id', 'created_at', 'updated_at'
127
+
128
+ username: str = Column(unique=True, length=50, nullable=False)
129
+ email: str = Column(unique=True, nullable=False)
130
+ is_active: bool = Column(default=True)
131
+ role: str = Column(default="USER")
132
+ ```
133
+
134
+ ### 4. Repositories (`repositories.py`)
135
+ Create an interface for data access. Inherit from `StarRepository`.
136
+
137
+ ```python
138
+ from starspring import Repository, StarRepository
139
+ from my_app.entities import User
140
+
141
+ @Repository
142
+ class UserRepository(StarRepository[User, int]):
143
+ # StarSpring automatically implements standard CRUD (save, find_by_id, delete, etc.)
144
+
145
+ # Define custom finders just by naming them!
146
+ async def find_by_username(self, username: str) -> User | None:
147
+ ...
148
+
149
+ async def find_by_email_and_is_active(self, email: str, is_active: bool) -> User | None:
150
+ ...
151
+ ```
152
+
153
+ ### 5. Services (`services.py`)
154
+ Encapsulate your business logic here. Use `@Transactional` to ensure data integrity.
155
+
156
+ ```python
157
+ from starspring import Service, Transactional
158
+ from my_app.repositories import UserRepository
159
+ from my_app.entities import User
160
+
161
+ @Service
162
+ class UserService:
163
+ # Dependency Injection: Just mention the type in the constructor!
164
+ def __init__(self, user_repo: UserRepository):
165
+ self.user_repo = user_repo
166
+
167
+ @Transactional
168
+ async def register_user(self, username: str, email: str) -> User:
169
+ # Check if exists
170
+ existing = await self.user_repo.find_by_email_and_is_active(email, True)
171
+ if existing:
172
+ raise ValueError("User already exists")
173
+
174
+ # Create new user
175
+ new_user = User(username=username, email=email)
176
+ return await self.user_repo.save(new_user)
177
+ ```
178
+
179
+ ### 6. Controllers (`controllers.py`)
180
+ Expose your logic as a REST API.
181
+
182
+ ```python
183
+ from starspring import RestController, GetMapping, PostMapping
184
+ from starspring.web.response import ResponseEntity
185
+ from my_app.services import UserService
186
+
187
+ @RestController("/api/users")
188
+ class UserController:
189
+
190
+ def __init__(self, user_service: UserService):
191
+ self.user_service = user_service
192
+
193
+ @GetMapping("/{username}")
194
+ async def get_user(self, username: str) -> dict:
195
+ # You can return dicts, lists, or Entities directly
196
+ user = await self.user_service.user_repo.find_by_username(username)
197
+ return user.to_dict() if user else ResponseEntity.not_found()
198
+
199
+ @PostMapping("/register")
200
+ async def register(self, username: str, email: str) -> dict:
201
+ # Arguments are automatically extracted from JSON body or Query params
202
+ try:
203
+ user = await self.user_service.register_user(username, email)
204
+ return {"status": "success", "user_id": user.id}
205
+ except ValueError as e:
206
+ return ResponseEntity.bad_request(str(e))
207
+ ```
208
+
209
+ ### 7. Main Entry Point (`main.py`)
210
+ Bootstrap the application.
211
+
212
+ ```python
213
+ from starspring import StarSpringApplication
214
+
215
+ # Initialize App
216
+ app = StarSpringApplication(
217
+ title="My User API",
218
+ config_path="application.yaml"
219
+ )
220
+
221
+ # Scan for all your components (Controllers, Services, Repositories)
222
+ app.scan_components("my_app")
223
+
224
+ if __name__ == "__main__":
225
+ # Runs the server (default: localhost:8000)
226
+ app.run()
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 📚 Core Concepts
232
+
233
+ ### Dependency Injection (DI)
234
+ StarSpring manages the lifecycle of your objects. When you ask for a `UserRepository` in your `UserService` constructor, the framework:
235
+ 1. Finds the `UserRepository` class.
236
+ 2. Creates an instance of it (Singleton by default).
237
+ 3. Passes it to your `UserService`.
238
+
239
+ This makes testing easier (you can mock repositories) and code cleaner.
240
+
241
+ ### Database & ORM
242
+ We use a **Code-First** approach.
243
+ 1. Define Python classes (`@Entity`).
244
+ 2. StarSpring tells SQLAlchemy to map these classes to tables.
245
+ 3. If `database.ddl-auto` is set to `create`, the framework creates the tables for you on startup.
246
+
247
+ ### Transaction Management
248
+ Use the `@Transactional` decorator on any method (usually in Services).
249
+ * **Success**: The transaction commits automatically.
250
+ * **Reference Counter**: If you nest `@Transactional` methods, the inner one joins the outer transaction.
251
+ * **Error**: If an exception occurs, the entire transaction rolls back.
252
+
253
+ ---
254
+
255
+ ## 🛠 Advanced Configuration
256
+
257
+ You can tune every part of the framework via `application.yaml`.
258
+
259
+ ```yaml
260
+ server:
261
+ port: 8080
262
+ cors:
263
+ allowed-origins: ["*"]
264
+ allowed-methods: ["GET", "POST", "PUT", "DELETE"]
265
+
266
+ logging:
267
+ level: INFO
268
+ format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
269
+ ```
270
+
271
+ ---
272
+
273
+ ## 🤝 Contributing
274
+
275
+ We welcome contributions!
276
+ 1. Fork the repository.
277
+ 2. Create a feature branch.
278
+ 3. Submit a Pull Request.
279
+
280
+ Please ensure all tests pass before submitting.
281
+
282
+ ## 📄 License
283
+
284
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,36 @@
1
+ starspring/__init__.py,sha256=erdKp-lAqhw9iFeIcVamGQAFLVhITCibPvHQFtgha5Q,3411
2
+ starspring/application.py,sha256=J_xMTqwtEBhccTznJA2Be4j-qjh-tJhsOMLMHcE7bf4,14113
3
+ starspring/client/__init__.py,sha256=Bomyz-O3w14OMozOLMa337TvRyEevj5m9bzkiswHnzk,29
4
+ starspring/client/rest_client.py,sha256=ZoecH1eEor3aN9udxqWzjBsfPJcTuRPTTMBG22PzUSI,7298
5
+ starspring/config/__init__.py,sha256=_OSjmqPwe35219tdibkRE7fdbWirb2G-Cz-CWPNC9rY,43
6
+ starspring/config/environment.py,sha256=wSKMYvukbKccB_DVz6fk-BjtQsbnK1EtS9CAUbjLOco,2316
7
+ starspring/config/properties.py,sha256=NEZcmGCSAJL9r69HybsoFeE75h00--M_uWPdGsOw7zI,4878
8
+ starspring/core/__init__.py,sha256=u81P23ha-vTNh50kLwWZMSLinbE-DxSBluYaYIsn2Gk,33
9
+ starspring/core/context.py,sha256=Ho6cLAhDlgCi5l3_mfvNsBUTQn6Z_3xd8C3UiXxvgsg,5914
10
+ starspring/core/controller.py,sha256=IqRCTvKbebeRt5_HzIufK5jEO0qaRJ1ybvtvSytadD0,1459
11
+ starspring/core/exceptions.py,sha256=9cu21pohqA1blzGN5dD5MBmggFc3em4M4vm36Hi1mx4,2879
12
+ starspring/core/response.py,sha256=BMQxL6AcdTevHNQeszqVW0yHN1YNctUKa14AnA_NydA,4717
13
+ starspring/data/__init__.py,sha256=0lBfm1wIj1caWDRF4bZGFtSDYJovMv6VwdmWhP6paBY,1102
14
+ starspring/data/database_config.py,sha256=pQ5ChPap-0m8Kq1sissTjd9vw56yJ80BwuSI6waGzNQ,3656
15
+ starspring/data/entity.py,sha256=gn6rH4VCPzn_oNK_TLxTiKMRKm-gI7vB3JrhYY0hBPU,12404
16
+ starspring/data/orm_gateway.py,sha256=LZfASLvnpdFYguK7_LtV1OnuhqkMwkAmlelLREqqvEI,8558
17
+ starspring/data/query_builder.py,sha256=1ef2S8oNa8tPpZ8qazsbVRglro2oJ7_7u2QhNCKeHCo,11690
18
+ starspring/data/repository.py,sha256=8gjoZ4iImz3J4b_ADs8qQottbkTjNyd9mZ1iZ91j3ko,10075
19
+ starspring/data/schema_generator.py,sha256=r6OqYjsW-HeDVAdlfKb2wAPC6XbedH-0E11pNmynKms,4834
20
+ starspring/data/transaction.py,sha256=kFVrv3xA2EhOXnDcN88QPhLq6rYX9UXC_2EUG_sQGzg,1613
21
+ starspring/decorators/__init__.py,sha256=WSJHi2c_4I1ZX6TzhGCvEgBkFWnCk9NbXKIyjH9ErqY,63
22
+ starspring/decorators/components.py,sha256=wB2CMNnqoJ2VPhQ1Er9_heFQyPLjna7KYKpdmmym1Yw,5288
23
+ starspring/decorators/configuration.py,sha256=XUkzZCusmaoGdQpm17d3x3zVTViXeC231RCfkfr35Ok,2901
24
+ starspring/decorators/routing.py,sha256=rENfRuRkBlpFLn6KSZGIoqnDpMqR0IwriPB5rX9iIC8,10560
25
+ starspring/decorators/validation.py,sha256=wsqvVA_IuGZprwLqbkoZ7Xg3qCK-om_Rxc4RqDU0Nhw,817
26
+ starspring/middleware/__init__.py,sha256=jzho4eY5jLPyUH-kUm6K--RFIF_X1z2oA-I_8EZZ43o,29
27
+ starspring/middleware/cors.py,sha256=74mmat0jYkvLi9x5iytxj4S-QupdH16r2r7k_Cw0iQk,2609
28
+ starspring/middleware/exception.py,sha256=Fk5X5vS-BKNBn0wPzXKStH30Mi8wq1qXGhVjY56-JMI,2685
29
+ starspring/middleware/logging.py,sha256=GXwAujaHcgMWigeS9Vv-cvEIw2E3G5hv15pebepfjmM,1667
30
+ starspring/template/__init__.py,sha256=aNwQaFo8sG-p-Q4bNOHPi4s67jOUIcH845_xggki3MU,387
31
+ starspring/template/engine.py,sha256=9xBcZDRnMdQN1I-ZWf_5tHkpuJhvYlsv-bo2f5boqEk,4794
32
+ starspring/template/model_and_view.py,sha256=qEQblzl3WotfnFVWCF3IcB6AOpCJHS6Hh998m-YFkLU,1799
33
+ starspring-0.1.0.dist-info/METADATA,sha256=R8MiBfqNbAcTL_AVlVzC0InqwD77EvLE1LmN65PCdEM,9572
34
+ starspring-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
35
+ starspring-0.1.0.dist-info/top_level.txt,sha256=FWPAl1oRZXxoFa3ePRyY7lmXpde2KMYRX7CBTNDuZtI,11
36
+ starspring-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ starspring