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.
- {starspring-0.1.0 → starspring-0.1.1}/PKG-INFO +13 -10
- {starspring-0.1.0 → starspring-0.1.1}/README.md +8 -5
- {starspring-0.1.0 → starspring-0.1.1}/pyproject.toml +5 -5
- {starspring-0.1.0 → starspring-0.1.1}/starspring/application.py +9 -1
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/repository.py +52 -5
- {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/routing.py +16 -1
- {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/PKG-INFO +13 -10
- {starspring-0.1.0 → starspring-0.1.1}/setup.cfg +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/client/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/client/rest_client.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/config/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/config/environment.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/config/properties.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/core/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/core/context.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/core/controller.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/core/exceptions.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/core/response.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/database_config.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/entity.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/orm_gateway.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/query_builder.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/schema_generator.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/data/transaction.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/components.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/configuration.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/decorators/validation.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/cors.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/exception.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/middleware/logging.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/template/__init__.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/template/engine.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring/template/model_and_view.py +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/SOURCES.txt +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/dependency_links.txt +0 -0
- {starspring-0.1.0 → starspring-0.1.1}/starspring.egg-info/requires.txt +0 -0
- {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.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A Spring Boot-inspired Python web framework built on Starlette
|
|
5
|
-
Author:
|
|
5
|
+
Author: Dasun Nethsara
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Documentation, https://github.com/
|
|
9
|
-
Project-URL: Repository, https://github.com/
|
|
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
|
|
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
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
@
|
|
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
|
|
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
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
@
|
|
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.
|
|
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 = "
|
|
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/
|
|
52
|
-
Documentation = "https://github.com/
|
|
53
|
-
Repository = "https://github.com/
|
|
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
|
|
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
|
-
|
|
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
|
|
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., '
|
|
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
|
-
|
|
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.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A Spring Boot-inspired Python web framework built on Starlette
|
|
5
|
-
Author:
|
|
5
|
+
Author: Dasun Nethsara
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Documentation, https://github.com/
|
|
9
|
-
Project-URL: Repository, https://github.com/
|
|
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
|
|
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
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
@
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|