starspring 0.1.0__tar.gz → 0.1.1__tar.gz

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.
Files changed (41) hide show
  1. {starspring-0.1.0 → starspring-0.1.1}/PKG-INFO +13 -10
  2. {starspring-0.1.0 → starspring-0.1.1}/README.md +8 -5
  3. {starspring-0.1.0 → starspring-0.1.1}/pyproject.toml +5 -5
  4. {starspring-0.1.0 → starspring-0.1.1}/starspring/application.py +9 -1
  5. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/repository.py +52 -5
  6. {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/routing.py +16 -1
  7. {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/PKG-INFO +13 -10
  8. {starspring-0.1.0 → starspring-0.1.1}/setup.cfg +0 -0
  9. {starspring-0.1.0 → starspring-0.1.1}/starspring/__init__.py +0 -0
  10. {starspring-0.1.0 → starspring-0.1.1}/starspring/client/__init__.py +0 -0
  11. {starspring-0.1.0 → starspring-0.1.1}/starspring/client/rest_client.py +0 -0
  12. {starspring-0.1.0 → starspring-0.1.1}/starspring/config/__init__.py +0 -0
  13. {starspring-0.1.0 → starspring-0.1.1}/starspring/config/environment.py +0 -0
  14. {starspring-0.1.0 → starspring-0.1.1}/starspring/config/properties.py +0 -0
  15. {starspring-0.1.0 → starspring-0.1.1}/starspring/core/__init__.py +0 -0
  16. {starspring-0.1.0 → starspring-0.1.1}/starspring/core/context.py +0 -0
  17. {starspring-0.1.0 → starspring-0.1.1}/starspring/core/controller.py +0 -0
  18. {starspring-0.1.0 → starspring-0.1.1}/starspring/core/exceptions.py +0 -0
  19. {starspring-0.1.0 → starspring-0.1.1}/starspring/core/response.py +0 -0
  20. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/__init__.py +0 -0
  21. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/database_config.py +0 -0
  22. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/entity.py +0 -0
  23. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/orm_gateway.py +0 -0
  24. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/query_builder.py +0 -0
  25. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/schema_generator.py +0 -0
  26. {starspring-0.1.0 → starspring-0.1.1}/starspring/data/transaction.py +0 -0
  27. {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/__init__.py +0 -0
  28. {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/components.py +0 -0
  29. {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/configuration.py +0 -0
  30. {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/validation.py +0 -0
  31. {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/__init__.py +0 -0
  32. {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/cors.py +0 -0
  33. {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/exception.py +0 -0
  34. {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/logging.py +0 -0
  35. {starspring-0.1.0 → starspring-0.1.1}/starspring/template/__init__.py +0 -0
  36. {starspring-0.1.0 → starspring-0.1.1}/starspring/template/engine.py +0 -0
  37. {starspring-0.1.0 → starspring-0.1.1}/starspring/template/model_and_view.py +0 -0
  38. {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/SOURCES.txt +0 -0
  39. {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/dependency_links.txt +0 -0
  40. {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/requires.txt +0 -0
  41. {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: starspring
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A Spring Boot-inspired Python web framework built on Starlette
5
- Author: StarSpring Contributors
5
+ Author: Dasun Nethsara
6
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
7
+ Project-URL: Homepage, https://github.com/DasunNethsara-04/starspring
8
+ Project-URL: Documentation, https://github.com/DasunNethsara-04/starspring#readme
9
+ Project-URL: Repository, https://github.com/DasunNethsara-04/starspring
10
10
  Keywords: web,framework,starlette,spring-boot,dependency-injection
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
@@ -118,7 +118,7 @@ database:
118
118
  Define your database models using standard Python classes. StarSpring maps them automatically.
119
119
 
120
120
  ```python
121
- from starspring import Entity, BaseEntity, Column, Id, GeneratedValue
121
+ from starspring import Entity, BaseEntity, Column
122
122
  from datetime import datetime
123
123
 
124
124
  @Entity(table_name="users")
@@ -133,13 +133,14 @@ class User(BaseEntity):
133
133
 
134
134
  ### 4. Repositories (`repositories.py`)
135
135
  Create an interface for data access. Inherit from `StarRepository`.
136
+ Note: Identify the Entity type in the generic (e.g., `[User]`).
136
137
 
137
138
  ```python
138
139
  from starspring import Repository, StarRepository
139
140
  from my_app.entities import User
140
141
 
141
142
  @Repository
142
- class UserRepository(StarRepository[User, int]):
143
+ class UserRepository(StarRepository[User]):
143
144
  # StarSpring automatically implements standard CRUD (save, find_by_id, delete, etc.)
144
145
 
145
146
  # Define custom finders just by naming them!
@@ -180,11 +181,12 @@ class UserService:
180
181
  Expose your logic as a REST API.
181
182
 
182
183
  ```python
183
- from starspring import RestController, GetMapping, PostMapping
184
- from starspring.web.response import ResponseEntity
184
+ # Note: Use @Controller for REST endpoints too.
185
+ # StarSpring automatically serializes dict/list responses to JSON.
186
+ from starspring import Controller, GetMapping, PostMapping, ResponseEntity
185
187
  from my_app.services import UserService
186
188
 
187
- @RestController("/api/users")
189
+ @Controller("/api/users")
188
190
  class UserController:
189
191
 
190
192
  def __init__(self, user_service: UserService):
@@ -194,6 +196,7 @@ class UserController:
194
196
  async def get_user(self, username: str) -> dict:
195
197
  # You can return dicts, lists, or Entities directly
196
198
  user = await self.user_service.user_repo.find_by_username(username)
199
+ # Assuming BaseEntity has .to_dict() or Pydantic serialization
197
200
  return user.to_dict() if user else ResponseEntity.not_found()
198
201
 
199
202
  @PostMapping("/register")
@@ -81,7 +81,7 @@ database:
81
81
  Define your database models using standard Python classes. StarSpring maps them automatically.
82
82
 
83
83
  ```python
84
- from starspring import Entity, BaseEntity, Column, Id, GeneratedValue
84
+ from starspring import Entity, BaseEntity, Column
85
85
  from datetime import datetime
86
86
 
87
87
  @Entity(table_name="users")
@@ -96,13 +96,14 @@ class User(BaseEntity):
96
96
 
97
97
  ### 4. Repositories (`repositories.py`)
98
98
  Create an interface for data access. Inherit from `StarRepository`.
99
+ Note: Identify the Entity type in the generic (e.g., `[User]`).
99
100
 
100
101
  ```python
101
102
  from starspring import Repository, StarRepository
102
103
  from my_app.entities import User
103
104
 
104
105
  @Repository
105
- class UserRepository(StarRepository[User, int]):
106
+ class UserRepository(StarRepository[User]):
106
107
  # StarSpring automatically implements standard CRUD (save, find_by_id, delete, etc.)
107
108
 
108
109
  # Define custom finders just by naming them!
@@ -143,11 +144,12 @@ class UserService:
143
144
  Expose your logic as a REST API.
144
145
 
145
146
  ```python
146
- from starspring import RestController, GetMapping, PostMapping
147
- from starspring.web.response import ResponseEntity
147
+ # Note: Use @Controller for REST endpoints too.
148
+ # StarSpring automatically serializes dict/list responses to JSON.
149
+ from starspring import Controller, GetMapping, PostMapping, ResponseEntity
148
150
  from my_app.services import UserService
149
151
 
150
- @RestController("/api/users")
152
+ @Controller("/api/users")
151
153
  class UserController:
152
154
 
153
155
  def __init__(self, user_service: UserService):
@@ -157,6 +159,7 @@ class UserController:
157
159
  async def get_user(self, username: str) -> dict:
158
160
  # You can return dicts, lists, or Entities directly
159
161
  user = await self.user_service.user_repo.find_by_username(username)
162
+ # Assuming BaseEntity has .to_dict() or Pydantic serialization
160
163
  return user.to_dict() if user else ResponseEntity.not_found()
161
164
 
162
165
  @PostMapping("/register")
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "starspring"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "A Spring Boot-inspired Python web framework built on Starlette"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = {text = "MIT"}
12
12
  authors = [
13
- {name = "StarSpring Contributors"}
13
+ {name = "Dasun Nethsara"}
14
14
  ]
15
15
  keywords = ["web", "framework", "starlette", "spring-boot", "dependency-injection"]
16
16
  classifiers = [
@@ -48,9 +48,9 @@ sqlalchemy = [
48
48
  ]
49
49
 
50
50
  [project.urls]
51
- Homepage = "https://github.com/yourusername/starspring"
52
- Documentation = "https://github.com/yourusername/starspring#readme"
53
- Repository = "https://github.com/yourusername/starspring"
51
+ Homepage = "https://github.com/DasunNethsara-04/starspring"
52
+ Documentation = "https://github.com/DasunNethsara-04/starspring#readme"
53
+ Repository = "https://github.com/DasunNethsara-04/starspring"
54
54
 
55
55
  [tool.setuptools.packages.find]
56
56
  where = ["."]
@@ -181,9 +181,17 @@ class StarSpringApplication:
181
181
  # This uses the imperative mappings we set up in the @Entity decorator
182
182
  # We get the engine from the gateway's session
183
183
  engine = gateway.session.get_bind()
184
+
185
+ # Debug: Log what tables are registered
186
+ table_names = list(mapper_registry.metadata.tables.keys())
187
+ logger.info(f"Tables registered in mapper_registry: {table_names}")
188
+
189
+ if not table_names:
190
+ logger.warning("No tables found in mapper_registry.metadata! Entities may not be imported yet.")
191
+
184
192
  mapper_registry.metadata.create_all(engine)
185
193
 
186
- logger.info("Auto-created tables using SQLAlchemy metadata")
194
+ logger.info(f"Auto-created tables: {table_names}")
187
195
 
188
196
  except Exception as e:
189
197
  logger.error(f"Failed to auto-create tables: {e}")
@@ -216,33 +216,53 @@ class StarRepository(Repository[T]):
216
216
 
217
217
  # If it's a method and it's defined (not just 'pass'), return it
218
218
  if callable(attr) and hasattr(attr, '__func__'):
219
- # Check if method has actual implementation (more than just 'pass')
219
+ # Check if method has actual implementation (more than just 'pass' or '...')
220
220
  source = inspect.getsource(attr.__func__)
221
- if 'pass' not in source or len(source.strip().split('\n')) > 3:
221
+ # If it's just a stub (contains 'pass' or '...' and is short), treat as unimplemented
222
+ is_stub = ('pass' in source or '...' in source) and len(source.strip().split('\n')) <= 3
223
+ if not is_stub:
222
224
  return attr
223
225
  except AttributeError:
224
226
  pass
225
227
 
226
228
  # Check if this is a query method that should be auto-implemented
229
+ # Support both camelCase (findByUsername) and snake_case (find_by_username)
227
230
  if name.startswith(('findBy', 'countBy', 'deleteBy', 'existsBy')):
228
231
  logger.debug(f"Auto-implementing query method: {name}")
229
- return self._create_query_method(name)
232
+ return self._create_query_method(name, original_name=name)
233
+ elif name.startswith(('find_by_', 'count_by_', 'delete_by_', 'exists_by_')):
234
+ # Convert snake_case to camelCase for the parser
235
+ logger.debug(f"Auto-implementing query method (snake_case): {name}")
236
+ camel_name = self._snake_to_camel_method(name)
237
+ # Pass BOTH the camelCase name (for parsing) and original name (for type hints)
238
+ return self._create_query_method(camel_name, original_name=name)
230
239
 
231
240
  # Default behavior for everything else
232
241
  return object.__getattribute__(self, name)
233
242
 
234
- def _create_query_method(self, method_name: str):
243
+ def _snake_to_camel_method(self, snake_name: str) -> str:
244
+ """Convert snake_case method name to camelCase for parser"""
245
+ parts = snake_name.split('_')
246
+ # First part stays lowercase, capitalize rest
247
+ return parts[0] + ''.join(word.capitalize() for word in parts[1:])
248
+
249
+
250
+ def _create_query_method(self, method_name: str, original_name: str = None):
235
251
  """
236
252
  Create a query method dynamically
237
253
 
238
254
  Args:
239
- method_name: Name of the method (e.g., 'findByEmail')
255
+ method_name: Name of the method in camelCase for parsing (e.g., 'findByUsername')
256
+ original_name: Original method name as defined in class (e.g., 'find_by_username')
240
257
 
241
258
  Returns:
242
259
  Async function that executes the query
243
260
  """
244
261
  from starspring.data.query_builder import QueryMethodParser, SQLQueryGenerator, QueryOperation
245
262
 
263
+ # Use original_name if provided, otherwise use method_name
264
+ type_hint_name = original_name if original_name else method_name
265
+
246
266
  # Parse the method name
247
267
  try:
248
268
  parser = QueryMethodParser()
@@ -265,6 +285,26 @@ class StarRepository(Repository[T]):
265
285
  sql, param_names = generator.generate(parsed_query)
266
286
 
267
287
  # Create the async function
288
+ # Helper to check return type
289
+ import typing # Import typing for type hint checking
290
+ is_list_return = True # Default to list if ambiguous
291
+ try:
292
+ # Try to get type hints from the stub method if it exists on the class
293
+ if hasattr(self.__class__, type_hint_name):
294
+ method = getattr(self.__class__, type_hint_name)
295
+ hints = typing.get_type_hints(method)
296
+ if 'return' in hints:
297
+ ret_type = hints['return']
298
+ # Check if it's a List
299
+ origin = getattr(ret_type, '__origin__', None)
300
+ if origin is list or origin is typing.List:
301
+ is_list_return = True
302
+ else:
303
+ # Assume single entity if not List
304
+ is_list_return = False
305
+ except Exception:
306
+ pass
307
+
268
308
  async def query_executor(*args, **kwargs):
269
309
  """Auto-generated query executor"""
270
310
  # Build parameter dictionary
@@ -283,6 +323,13 @@ class StarRepository(Repository[T]):
283
323
  parsed_query.operation
284
324
  )
285
325
 
326
+ # Unwrap list if expecting single result (for find operations)
327
+ if parsed_query.operation == QueryOperation.FIND and not is_list_return:
328
+ if isinstance(result, list):
329
+ if len(result) > 0:
330
+ return result[0]
331
+ return None
332
+
286
333
  return result
287
334
 
288
335
  return query_executor
@@ -148,7 +148,22 @@ def create_route_handler(bound_method: Callable) -> Callable:
148
148
  elif not request_injected:
149
149
  # Don't inject into typed primitives (int, str, bool, float)
150
150
  is_primitive = param_type in (int, str, bool, float)
151
- if not is_primitive:
151
+
152
+ # Try to extract from JSON body for primitives if not found yet
153
+ json_body = None
154
+ if is_primitive or param_type is None:
155
+ try:
156
+ # Only attempt to read body for methods that support it
157
+ if request.method in ["POST", "PUT", "PATCH"]:
158
+ # Use a cached property or similar to avoid re-reading stream if possible?
159
+ # Starlette requests cache .json() result automatically after first await.
160
+ json_body = await request.json()
161
+ except Exception:
162
+ pass
163
+
164
+ if json_body and isinstance(json_body, dict) and param_name in json_body:
165
+ kwargs[param_name] = json_body[param_name]
166
+ elif not is_primitive:
152
167
  kwargs[param_name] = request
153
168
  request_injected = True
154
169
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: starspring
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A Spring Boot-inspired Python web framework built on Starlette
5
- Author: StarSpring Contributors
5
+ Author: Dasun Nethsara
6
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
7
+ Project-URL: Homepage, https://github.com/DasunNethsara-04/starspring
8
+ Project-URL: Documentation, https://github.com/DasunNethsara-04/starspring#readme
9
+ Project-URL: Repository, https://github.com/DasunNethsara-04/starspring
10
10
  Keywords: web,framework,starlette,spring-boot,dependency-injection
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
@@ -118,7 +118,7 @@ database:
118
118
  Define your database models using standard Python classes. StarSpring maps them automatically.
119
119
 
120
120
  ```python
121
- from starspring import Entity, BaseEntity, Column, Id, GeneratedValue
121
+ from starspring import Entity, BaseEntity, Column
122
122
  from datetime import datetime
123
123
 
124
124
  @Entity(table_name="users")
@@ -133,13 +133,14 @@ class User(BaseEntity):
133
133
 
134
134
  ### 4. Repositories (`repositories.py`)
135
135
  Create an interface for data access. Inherit from `StarRepository`.
136
+ Note: Identify the Entity type in the generic (e.g., `[User]`).
136
137
 
137
138
  ```python
138
139
  from starspring import Repository, StarRepository
139
140
  from my_app.entities import User
140
141
 
141
142
  @Repository
142
- class UserRepository(StarRepository[User, int]):
143
+ class UserRepository(StarRepository[User]):
143
144
  # StarSpring automatically implements standard CRUD (save, find_by_id, delete, etc.)
144
145
 
145
146
  # Define custom finders just by naming them!
@@ -180,11 +181,12 @@ class UserService:
180
181
  Expose your logic as a REST API.
181
182
 
182
183
  ```python
183
- from starspring import RestController, GetMapping, PostMapping
184
- from starspring.web.response import ResponseEntity
184
+ # Note: Use @Controller for REST endpoints too.
185
+ # StarSpring automatically serializes dict/list responses to JSON.
186
+ from starspring import Controller, GetMapping, PostMapping, ResponseEntity
185
187
  from my_app.services import UserService
186
188
 
187
- @RestController("/api/users")
189
+ @Controller("/api/users")
188
190
  class UserController:
189
191
 
190
192
  def __init__(self, user_service: UserService):
@@ -194,6 +196,7 @@ class UserController:
194
196
  async def get_user(self, username: str) -> dict:
195
197
  # You can return dicts, lists, or Entities directly
196
198
  user = await self.user_service.user_repo.find_by_username(username)
199
+ # Assuming BaseEntity has .to_dict() or Pydantic serialization
197
200
  return user.to_dict() if user else ResponseEntity.not_found()
198
201
 
199
202
  @PostMapping("/register")
File without changes