TypeDAL 5.1.0__tar.gz → 5.1.2__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.
- {typedal-5.1.0 → typedal-5.1.2}/PKG-INFO +1 -1
- {typedal-5.1.0 → typedal-5.1.2}/pyproject.toml +3 -3
- {typedal-5.1.0 → typedal-5.1.2}/pyproject.toml.orig +3 -3
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/cli.py +1 -1
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/core.py +15 -13
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/query_builder.py +42 -7
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/relationships.py +4 -2
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/rows.py +2 -3
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/serializers/typescript.py +1 -1
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/tables.py +1 -1
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/warnings.py +4 -0
- {typedal-5.1.0 → typedal-5.1.2}/README.md +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/__about__.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/__init__.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/asynchronous.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/caching.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/config.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/constants.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/define.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/enum_helpers.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/fields.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/for_py4web.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/for_web2py.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/helpers.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/mixins.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/py.typed +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/serializers/as_json.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/types.py +0 -0
- {typedal-5.1.0 → typedal-5.1.2}/src/typedal/web2py_py4web_shared.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
|
6
6
|
name = "TypeDAL"
|
|
7
7
|
description = "Typing support for PyDAL"
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
version = "5.1.
|
|
9
|
+
version = "5.1.2"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
license-expression = "MIT"
|
|
12
12
|
keywords = []
|
|
@@ -164,7 +164,7 @@ badge = true
|
|
|
164
164
|
mypy = "--disable-error-code misc"
|
|
165
165
|
|
|
166
166
|
[tool.black]
|
|
167
|
-
target-version = ["
|
|
167
|
+
target-version = ["py312"]
|
|
168
168
|
line-length = 120
|
|
169
169
|
extend-exclude = '''
|
|
170
170
|
# A regex preceded with ^/ will apply only to files and directories
|
|
@@ -199,7 +199,7 @@ exclude = [
|
|
|
199
199
|
]
|
|
200
200
|
|
|
201
201
|
[tool.ruff]
|
|
202
|
-
target-version = "
|
|
202
|
+
target-version = "py312"
|
|
203
203
|
line-length = 120
|
|
204
204
|
extend-exclude = [
|
|
205
205
|
"*.bak/",
|
|
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
|
6
6
|
name = "TypeDAL"
|
|
7
7
|
description = 'Typing support for PyDAL'
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
version = "5.1.
|
|
9
|
+
version = "5.1.2"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
license-expression = "MIT"
|
|
12
12
|
keywords = []
|
|
@@ -181,7 +181,7 @@ badge = true
|
|
|
181
181
|
mypy = "--disable-error-code misc"
|
|
182
182
|
|
|
183
183
|
[tool.black]
|
|
184
|
-
target-version = ["
|
|
184
|
+
target-version = ["py312"]
|
|
185
185
|
line-length = 120
|
|
186
186
|
# 'extend-exclude' excludes files or directories in addition to the defaults
|
|
187
187
|
extend-exclude = '''
|
|
@@ -229,7 +229,7 @@ strict = true
|
|
|
229
229
|
exclude = ["venv", ".bak"]
|
|
230
230
|
|
|
231
231
|
[tool.ruff]
|
|
232
|
-
target-version = "
|
|
232
|
+
target-version = "py312"
|
|
233
233
|
line-length = 120
|
|
234
234
|
|
|
235
235
|
extend-exclude = ["*.bak/", "venv*/", "tests/", "example_*.py"]
|
|
@@ -227,7 +227,7 @@ def setup(
|
|
|
227
227
|
data.pop("connection", None)
|
|
228
228
|
|
|
229
229
|
# ignore any None:
|
|
230
|
-
old_contents["tool"]["typedal"] = {k: v for k, v in data.items() if v is not None}
|
|
230
|
+
old_contents["tool"]["typedal"] = {k: v for k, v in data.items() if v is not None}
|
|
231
231
|
|
|
232
232
|
with toml_path.open("w") as f:
|
|
233
233
|
tomlkit.dump(old_contents, f)
|
|
@@ -34,7 +34,7 @@ from .helpers import (
|
|
|
34
34
|
from .serializers.typescript import TypedDictRegistry
|
|
35
35
|
|
|
36
36
|
# noinspection PyUnusedImports
|
|
37
|
-
from .types import CacheStatus, Field, Template
|
|
37
|
+
from .types import CacheStatus, Expression, Field, Template
|
|
38
38
|
|
|
39
39
|
try:
|
|
40
40
|
# python 3.14+
|
|
@@ -46,10 +46,10 @@ except ImportError: # pragma: no cover
|
|
|
46
46
|
if t.TYPE_CHECKING:
|
|
47
47
|
from .fields import TypedField
|
|
48
48
|
from .query_builder import QueryBuilder
|
|
49
|
-
from .types import AnyDict, DefineKwargs,
|
|
49
|
+
from .types import AnyDict, DefineKwargs, Rows, Set, T_Query, Table
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def _expression_subclasses() -> t.Iterator[type]:
|
|
52
|
+
def _expression_subclasses() -> t.Iterator[type[Expression]]:
|
|
53
53
|
"""
|
|
54
54
|
Yield Expression and every (nested) subclass currently loaded, e.g. Field and TypedField.
|
|
55
55
|
"""
|
|
@@ -100,7 +100,7 @@ def _purge_dialect_expressions(adapter: t.Any) -> None:
|
|
|
100
100
|
# because then they will have different globals and it breaks!
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
def evaluate_forward_reference_312(fw_ref: ForwardRef, namespace:
|
|
103
|
+
def evaluate_forward_reference_312(fw_ref: ForwardRef, namespace: t.Mapping[str, type]) -> type: # pragma: no cover
|
|
104
104
|
"""
|
|
105
105
|
Extract the original type from a forward reference string.
|
|
106
106
|
|
|
@@ -110,13 +110,13 @@ def evaluate_forward_reference_312(fw_ref: ForwardRef, namespace: dict[str, type
|
|
|
110
110
|
type,
|
|
111
111
|
fw_ref._evaluate( # ty: ignore[deprecated]
|
|
112
112
|
localns=locals(),
|
|
113
|
-
globalns=globals() | namespace,
|
|
113
|
+
globalns=globals() | dict(namespace),
|
|
114
114
|
recursive_guard=frozenset(),
|
|
115
115
|
),
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def evaluate_forward_reference_313(fw_ref: ForwardRef, namespace:
|
|
119
|
+
def evaluate_forward_reference_313(fw_ref: ForwardRef, namespace: t.Mapping[str, type]) -> type: # pragma: no cover
|
|
120
120
|
"""
|
|
121
121
|
Extract the original type from a forward reference string.
|
|
122
122
|
|
|
@@ -126,14 +126,14 @@ def evaluate_forward_reference_313(fw_ref: ForwardRef, namespace: dict[str, type
|
|
|
126
126
|
type,
|
|
127
127
|
fw_ref._evaluate( # ty: ignore[deprecated]
|
|
128
128
|
localns=locals(),
|
|
129
|
-
globalns=globals() | namespace,
|
|
129
|
+
globalns=globals() | dict(namespace),
|
|
130
130
|
recursive_guard=frozenset(),
|
|
131
131
|
type_params=(), # suggested since 3.13 (warning) and not supported before. Mandatory after 1.15!
|
|
132
132
|
),
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
def evaluate_forward_reference_314(fw_ref: ForwardRef, namespace:
|
|
136
|
+
def evaluate_forward_reference_314(fw_ref: ForwardRef, namespace: t.Mapping[str, type]) -> type: # pragma: no cover
|
|
137
137
|
"""
|
|
138
138
|
Extract the original type from a forward reference string.
|
|
139
139
|
|
|
@@ -143,7 +143,7 @@ def evaluate_forward_reference_314(fw_ref: ForwardRef, namespace: dict[str, type
|
|
|
143
143
|
type,
|
|
144
144
|
fw_ref.evaluate(
|
|
145
145
|
locals=locals(),
|
|
146
|
-
globals=globals() | namespace,
|
|
146
|
+
globals=globals() | dict(namespace),
|
|
147
147
|
type_params=(),
|
|
148
148
|
),
|
|
149
149
|
)
|
|
@@ -151,7 +151,7 @@ def evaluate_forward_reference_314(fw_ref: ForwardRef, namespace: dict[str, type
|
|
|
151
151
|
|
|
152
152
|
def evaluate_forward_reference(
|
|
153
153
|
fw_ref: ForwardRef,
|
|
154
|
-
namespace:
|
|
154
|
+
namespace: t.Mapping[str, type] | None = None,
|
|
155
155
|
) -> type: # pragma: no cover
|
|
156
156
|
"""
|
|
157
157
|
Extract the original type from a forward reference string.
|
|
@@ -166,7 +166,7 @@ def evaluate_forward_reference(
|
|
|
166
166
|
return evaluate_forward_reference_314(fw_ref, namespace=namespace or {})
|
|
167
167
|
|
|
168
168
|
|
|
169
|
-
def resolve_annotation_313(ftype: str, namespace:
|
|
169
|
+
def resolve_annotation_313(ftype: str, namespace: t.Mapping[str, type] | None = None) -> type: # pragma: no cover
|
|
170
170
|
"""
|
|
171
171
|
Resolve an annotation that's in string representation.
|
|
172
172
|
|
|
@@ -176,7 +176,7 @@ def resolve_annotation_313(ftype: str, namespace: dict[str, type] | None = None)
|
|
|
176
176
|
return evaluate_forward_reference(fw_ref, namespace=namespace)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
def resolve_annotation_314(ftype: str, namespace:
|
|
179
|
+
def resolve_annotation_314(ftype: str, namespace: t.Mapping[str, type] | None = None) -> type: # pragma: no cover
|
|
180
180
|
"""
|
|
181
181
|
Resolve an annotation that's in string representation.
|
|
182
182
|
|
|
@@ -186,7 +186,7 @@ def resolve_annotation_314(ftype: str, namespace: dict[str, type] | None = None)
|
|
|
186
186
|
return evaluate_forward_reference(fw_ref, namespace=namespace)
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
def resolve_annotation(ftype: str, namespace:
|
|
189
|
+
def resolve_annotation(ftype: str, namespace: t.Mapping[str, type] | None = None) -> type: # pragma: no cover
|
|
190
190
|
"""
|
|
191
191
|
Resolve an annotation that's in string representation.
|
|
192
192
|
|
|
@@ -220,6 +220,8 @@ if t.TYPE_CHECKING:
|
|
|
220
220
|
|
|
221
221
|
def has_representer(self, field_type: str) -> bool: ...
|
|
222
222
|
|
|
223
|
+
def represent(self, name: str, *args: t.Any, **kwargs: t.Any) -> str: ...
|
|
224
|
+
|
|
223
225
|
# pydal exposes dynamic table attributes like `db.my_table`.
|
|
224
226
|
# this keeps type checkers from flagging these as missing attributes.
|
|
225
227
|
def __getattr__(self, item: str) -> "Table": ...
|
|
@@ -46,7 +46,19 @@ from .types import (
|
|
|
46
46
|
merge_permissions,
|
|
47
47
|
require_permission,
|
|
48
48
|
)
|
|
49
|
-
from .warnings import UnusedWindowWarning
|
|
49
|
+
from .warnings import NoopQueryWarning, UnusedWindowWarning
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def warn_noop(method: str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Warn that `method` was called without arguments, which has no effect.
|
|
55
|
+
"""
|
|
56
|
+
# stacklevel: 1 = here, 2 = the query builder method, 3 = the caller.
|
|
57
|
+
warnings.warn(
|
|
58
|
+
f"`.{method}()` without arguments does nothing. You can remove this call.",
|
|
59
|
+
stacklevel=3,
|
|
60
|
+
category=NoopQueryWarning,
|
|
61
|
+
)
|
|
50
62
|
|
|
51
63
|
|
|
52
64
|
class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
@@ -175,6 +187,10 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
175
187
|
"""
|
|
176
188
|
Return a clone of this builder with permission overrides merged in.
|
|
177
189
|
"""
|
|
190
|
+
if not permissions and self: # ty: ignore[redundant-condition]
|
|
191
|
+
warn_noop("permissions")
|
|
192
|
+
return self
|
|
193
|
+
|
|
178
194
|
return self._extend(permissions=permissions)
|
|
179
195
|
|
|
180
196
|
def _normalize_select_option(
|
|
@@ -216,8 +232,15 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
216
232
|
join: othertable.on(query) - do an INNER JOIN. Using TypeDAL relationships with .join() is recommended!
|
|
217
233
|
left: othertable.on(query) - do a LEFT JOIN. Using TypeDAL relationships with .join() is recommended!
|
|
218
234
|
cache: cache the query result to speed up repeated queries; e.g. (cache=(cache.ram, 3600), cacheable=True)
|
|
235
|
+
|
|
236
|
+
Calling this without any fields or options on a builder that already has settings
|
|
237
|
+
does nothing and emits a NoopQueryWarning.
|
|
219
238
|
"""
|
|
220
239
|
|
|
240
|
+
if not fields and not options and self:
|
|
241
|
+
warn_noop("select")
|
|
242
|
+
return self
|
|
243
|
+
|
|
221
244
|
for key in ("distinct",):
|
|
222
245
|
if options.get(key):
|
|
223
246
|
options[key] = self._normalize_select_option(options[key])
|
|
@@ -282,7 +305,15 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
282
305
|
.where(lambda table: table.id == 5).where(lambda table: table.id == 6) == (table.id == 5) & (table.id=6)
|
|
283
306
|
When passing multiple queries to a single .where, they will be ORed:
|
|
284
307
|
.where(lambda table: table.id == 5, lambda table: table.id == 6) == (table.id == 5) | (table.id=6)
|
|
308
|
+
|
|
309
|
+
Calling this without any arguments on a builder that already has settings
|
|
310
|
+
does nothing and emits a NoopQueryWarning.
|
|
311
|
+
Starting an empty builder (e.g. `Model.where()`) is allowed and stays silent.
|
|
285
312
|
"""
|
|
313
|
+
if not queries_or_lambdas and not filters and self:
|
|
314
|
+
warn_noop("where")
|
|
315
|
+
return self
|
|
316
|
+
|
|
286
317
|
new_query = self.query
|
|
287
318
|
table = self._ensure_table_defined()
|
|
288
319
|
|
|
@@ -1268,7 +1299,8 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1268
1299
|
def __count(
|
|
1269
1300
|
self,
|
|
1270
1301
|
db: TypeDAL,
|
|
1271
|
-
distinct: t.
|
|
1302
|
+
distinct: bool | Field | TypedField[t.Any] = False,
|
|
1303
|
+
*,
|
|
1272
1304
|
include_left_for_distinct: bool = True,
|
|
1273
1305
|
) -> Query:
|
|
1274
1306
|
# internal, shared logic between .count and ._count
|
|
@@ -1292,9 +1324,11 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1292
1324
|
|
|
1293
1325
|
return query
|
|
1294
1326
|
|
|
1295
|
-
def count(self, distinct: t.
|
|
1327
|
+
def count(self, distinct: bool | Field | TypedField[t.Any] = False) -> int:
|
|
1296
1328
|
"""
|
|
1297
1329
|
Return the amount of rows matching the current query.
|
|
1330
|
+
|
|
1331
|
+
Passing a field counts its distinct values.
|
|
1298
1332
|
"""
|
|
1299
1333
|
require_permission(self._permissions, "read")
|
|
1300
1334
|
db = self._get_db()
|
|
@@ -1302,7 +1336,7 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1302
1336
|
|
|
1303
1337
|
return db(query).count(distinct)
|
|
1304
1338
|
|
|
1305
|
-
def _count(self, distinct: t.
|
|
1339
|
+
def _count(self, distinct: bool | Field | TypedField[t.Any] = False) -> str:
|
|
1306
1340
|
"""
|
|
1307
1341
|
Return the SQL for .count().
|
|
1308
1342
|
"""
|
|
@@ -1328,8 +1362,9 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1328
1362
|
return self.count()
|
|
1329
1363
|
|
|
1330
1364
|
db = self._get_db()
|
|
1331
|
-
|
|
1332
|
-
|
|
1365
|
+
distinct = t.cast(TypedField[int], self.model.id)
|
|
1366
|
+
query = self.__count(db, distinct=distinct, include_left_for_distinct=False)
|
|
1367
|
+
return db(query).count(distinct)
|
|
1333
1368
|
|
|
1334
1369
|
def __paginate(
|
|
1335
1370
|
self,
|
|
@@ -1523,7 +1558,7 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1523
1558
|
column = t.cast(t.Callable[..., list[T]], self.column)
|
|
1524
1559
|
return await run_async(self._get_db(), column, field, **options)
|
|
1525
1560
|
|
|
1526
|
-
async def count_async(self, distinct: t.
|
|
1561
|
+
async def count_async(self, distinct: bool | Field | TypedField[t.Any] = False) -> int:
|
|
1527
1562
|
"""
|
|
1528
1563
|
Async twin of `count()`.
|
|
1529
1564
|
"""
|
|
@@ -253,10 +253,12 @@ class Relationship[To_Type]:
|
|
|
253
253
|
# relationship queried on class, that's allowed
|
|
254
254
|
return self
|
|
255
255
|
|
|
256
|
+
db = instance._db
|
|
257
|
+
|
|
256
258
|
# instance: TypedTable instance
|
|
257
259
|
# owner: TypedTable class
|
|
258
260
|
|
|
259
|
-
if not self.name: # pragma: no cover
|
|
261
|
+
if not (self.name and db): # pragma: no cover
|
|
260
262
|
raise ValueError("Relationship does not seem to be connected to a table field.")
|
|
261
263
|
|
|
262
264
|
# Handle different lazy policies
|
|
@@ -284,7 +286,7 @@ class Relationship[To_Type]:
|
|
|
284
286
|
|
|
285
287
|
# For "tolerate" and "allow", we fetch the data
|
|
286
288
|
try:
|
|
287
|
-
resolved_table = self.get_table(
|
|
289
|
+
resolved_table = self.get_table(db)
|
|
288
290
|
|
|
289
291
|
builder = owner.where(id=instance.id).join(self.name)
|
|
290
292
|
if issubclass(resolved_table, TypedTable) or isinstance(resolved_table, Table):
|
|
@@ -10,6 +10,7 @@ import typing as t
|
|
|
10
10
|
|
|
11
11
|
from .asynchronous import run_async
|
|
12
12
|
from .core import TypeDAL
|
|
13
|
+
from .fields import TypedField
|
|
13
14
|
from .helpers import mktable
|
|
14
15
|
from .query_builder import QueryBuilder
|
|
15
16
|
from .serializers import as_json
|
|
@@ -563,7 +564,7 @@ class TypedSet(Set): # pragma: no cover
|
|
|
563
564
|
This class is not actually used, only 'cast' by TypeDAL.__call__
|
|
564
565
|
"""
|
|
565
566
|
|
|
566
|
-
def count(self, distinct: bool |
|
|
567
|
+
def count(self, distinct: bool | Field | TypedField[t.Any] = False, cache: AnyDict | None = None) -> int:
|
|
567
568
|
"""
|
|
568
569
|
Count returns an int.
|
|
569
570
|
"""
|
|
@@ -585,5 +586,3 @@ class TypedSet(Set): # pragma: no cover
|
|
|
585
586
|
|
|
586
587
|
|
|
587
588
|
# note: these imports exist at the bottom of this file to prevent circular import issues:
|
|
588
|
-
|
|
589
|
-
from .fields import TypedField # noqa: E402
|
|
@@ -31,7 +31,7 @@ class TypedDictRegistry(Singleton):
|
|
|
31
31
|
def __init__(self) -> None:
|
|
32
32
|
"""Initialize the singleton registry and optional shared typtyp world."""
|
|
33
33
|
self._types: dict[type, type[dict[str, t.Any]]] = {}
|
|
34
|
-
self._world = typtyp.World() if typtyp else None #
|
|
34
|
+
self._world = typtyp.World() if typtyp else None # ty: ignore[redundant-condition]
|
|
35
35
|
self._names: set[str] = set()
|
|
36
36
|
|
|
37
37
|
@property
|
|
@@ -1152,7 +1152,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1152
1152
|
"after_delete": cls._after_delete,
|
|
1153
1153
|
}
|
|
1154
1154
|
|
|
1155
|
-
def _setup_instance_methods(self) -> None:
|
|
1155
|
+
def _setup_instance_methods(self: t.Self | type[t.Self]) -> None:
|
|
1156
1156
|
# use setattr instead of self.x = y to make the typecheckers happier
|
|
1157
1157
|
setattr(self, "as_dict", self._as_dict)
|
|
1158
1158
|
setattr(self, "__json__", self._as_json)
|
|
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
|