TypeDAL 4.6.1__tar.gz → 4.6.3__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-4.6.1 → typedal-4.6.3}/CHANGELOG.md +12 -0
- {typedal-4.6.1 → typedal-4.6.3}/PKG-INFO +1 -1
- {typedal-4.6.1 → typedal-4.6.3}/pyproject.toml +8 -1
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/__about__.py +1 -1
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/cli.py +1 -2
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/core.py +4 -1
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/fields.py +1 -3
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/mixins.py +25 -10
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/rows.py +2 -2
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/tables.py +1 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/types.py +3 -1
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_mixins.py +46 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_row.py +20 -0
- {typedal-4.6.1 → typedal-4.6.3}/.crush/.gitignore +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.crush/crush.db-shm +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.crush/crush.db-wal +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.crush/init +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.crush/logs/crush.log +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.github/workflows/su6.yml +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.gitignore +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/.readthedocs.yml +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/README.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/coverage.svg +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/10_advanced_apis.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/1_getting_started.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/2_defining_tables.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/3_building_queries.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/4_relationships.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/5_py4web.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/6_migrations.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/7_configuration.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/8_mixins.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/9_memoization.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/css/code_blocks.css +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/index.md +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/docs/requirements.txt +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/example_new.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/example_old.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/mkdocs.yml +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/__init__.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/caching.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/config.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/constants.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/define.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/for_py4web.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/for_web2py.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/helpers.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/py.typed +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/query_builder.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/relationships.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tasks.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/__init__.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/configs/simple.toml +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/configs/valid.env +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/configs/valid.toml +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/py314_tests.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_cli.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_config.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_docs_examples.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_helpers.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_json.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_main.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_mypy.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_orm.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_py4web.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_query_builder.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_relationships.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_stats.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_table.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_web2py.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/test_xx_others.py +0 -0
- {typedal-4.6.1 → typedal-4.6.3}/tests/timings.py +0 -0
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.6.3 (2026-03-19)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* `as_dict()` and similar changed behavior after doing `.render()` ([`8241503`](https://github.com/trialandsuccess/TypeDAL/commit/8241503073c5a9fd76cc898048f6c7796de3ce7a))
|
|
10
|
+
|
|
11
|
+
## v4.6.2 (2026-03-13)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
* Move pydantic visibility/lazy-load filtering into schema-converter path used by FastAPI ([`63c2e2c`](https://github.com/trialandsuccess/TypeDAL/commit/63c2e2c2e3c78f8aa817911dd269f650f5ba3d7d))
|
|
16
|
+
|
|
5
17
|
## v4.6.1 (2026-03-13)
|
|
6
18
|
|
|
7
19
|
### Fix
|
|
@@ -183,6 +183,7 @@ select = [
|
|
|
183
183
|
# "COM", # comma's - NO: annoying
|
|
184
184
|
# "PTH", # use pathlib - NO: annoying
|
|
185
185
|
"RUF", # ruff rules
|
|
186
|
+
# "D", # docs
|
|
186
187
|
]
|
|
187
188
|
unfixable = [
|
|
188
189
|
# Don't touch unused imports
|
|
@@ -191,13 +192,19 @@ unfixable = [
|
|
|
191
192
|
extend-ignore = [
|
|
192
193
|
# db.field == None should NOT be fixed to db.field is None
|
|
193
194
|
"E711",
|
|
195
|
+
"D200",
|
|
196
|
+
"D212",
|
|
197
|
+
"D418",
|
|
194
198
|
]
|
|
195
199
|
|
|
196
|
-
|
|
197
200
|
ignore = [
|
|
198
201
|
"RUF013" # implicit optional
|
|
199
202
|
]
|
|
200
203
|
|
|
204
|
+
|
|
205
|
+
[tool.ruff.lint.pydocstyle]
|
|
206
|
+
convention = "google"
|
|
207
|
+
|
|
201
208
|
[tool.bandit]
|
|
202
209
|
# bandit -c pyproject.toml -r .
|
|
203
210
|
exclude_dirs = [".bak", "venv"]
|
|
@@ -390,8 +390,7 @@ def fake_migrations(
|
|
|
390
390
|
|
|
391
391
|
previously_migrated = (
|
|
392
392
|
db(
|
|
393
|
-
db.ewh_implemented_features.name.belongs(to_fake)
|
|
394
|
-
& (db.ewh_implemented_features.installed == True) # noqa E712
|
|
393
|
+
db.ewh_implemented_features.name.belongs(to_fake) & (db.ewh_implemented_features.installed == True) # noqa E712
|
|
395
394
|
)
|
|
396
395
|
.select(db.ewh_implemented_features.name)
|
|
397
396
|
.column("name")
|
|
@@ -4,6 +4,7 @@ Core functionality of TypeDAL.
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
# noinspection PyUnusedImports
|
|
7
8
|
import datetime as dt
|
|
8
9
|
import sys
|
|
9
10
|
import typing as t
|
|
@@ -21,7 +22,9 @@ from .helpers import (
|
|
|
21
22
|
sql_expression,
|
|
22
23
|
to_snake,
|
|
23
24
|
)
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
# noinspection PyUnusedImports
|
|
27
|
+
from .types import CacheStatus, Field, Template
|
|
25
28
|
|
|
26
29
|
try:
|
|
27
30
|
# python 3.14+
|
|
@@ -373,9 +373,7 @@ def UploadField(**kw: t.Unpack[FieldSettings]) -> TypedField[str]:
|
|
|
373
373
|
Upload = UploadField
|
|
374
374
|
|
|
375
375
|
|
|
376
|
-
def ReferenceField[
|
|
377
|
-
T_subclass: (TypedTable, Table)
|
|
378
|
-
](
|
|
376
|
+
def ReferenceField[T_subclass: (TypedTable, Table)](
|
|
379
377
|
other_table: str | t.Type[TypedTable] | TypedTable | Table | T_subclass,
|
|
380
378
|
**kw: t.Unpack[FieldSettings],
|
|
381
379
|
) -> TypedField[int]:
|
|
@@ -376,6 +376,12 @@ class PydanticMixin(Mixin):
|
|
|
376
376
|
field_name: cls._unwrap_pydantic_field_type(field_type) for field_name, field_type in annotations.items()
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
# Respect pyDAL visibility: unreadable DB fields should not be part of pydantic output/schema.
|
|
380
|
+
for field_name in list(fields):
|
|
381
|
+
model_attr = full_dict.get(field_name, getattr(cls, field_name, None))
|
|
382
|
+
if hasattr(model_attr, "readable") and not getattr(model_attr, "readable"):
|
|
383
|
+
fields.pop(field_name, None)
|
|
384
|
+
|
|
379
385
|
for field_name, field_type in fields.items():
|
|
380
386
|
cls._ensure_pydantic_compatible_type(field_name, field_type)
|
|
381
387
|
|
|
@@ -420,8 +426,9 @@ class PydanticMixin(Mixin):
|
|
|
420
426
|
)
|
|
421
427
|
|
|
422
428
|
@staticmethod
|
|
423
|
-
def _make_instance_converter(
|
|
429
|
+
def _make_instance_converter(model_cls: type, fields: dict[str, t.Any]) -> t.Callable[[t.Any], t.Any]:
|
|
424
430
|
_PRIMITIVES = (str, float, bool, bytes)
|
|
431
|
+
relationship_names = set(model_cls.get_relationships()) if hasattr(model_cls, "get_relationships") else set()
|
|
425
432
|
|
|
426
433
|
def convert(value: t.Any) -> t.Any:
|
|
427
434
|
if isinstance(value, dict):
|
|
@@ -432,7 +439,16 @@ class PydanticMixin(Mixin):
|
|
|
432
439
|
if isinstance(value, _PRIMITIVES) or value is None:
|
|
433
440
|
return value
|
|
434
441
|
# Handles both TypedTable instances and raw pydal Row objects
|
|
435
|
-
|
|
442
|
+
values_dict = getattr(value, "__dict__", {})
|
|
443
|
+
result: dict[str, t.Any] = {}
|
|
444
|
+
for field_name in fields:
|
|
445
|
+
# Never trigger lazy-loads during pydantic conversion.
|
|
446
|
+
if field_name in relationship_names and field_name not in values_dict:
|
|
447
|
+
continue
|
|
448
|
+
|
|
449
|
+
result[field_name] = getattr(value, field_name, None)
|
|
450
|
+
|
|
451
|
+
return result
|
|
436
452
|
|
|
437
453
|
return convert
|
|
438
454
|
|
|
@@ -529,11 +545,9 @@ class PydanticMixin(Mixin):
|
|
|
529
545
|
if field_name in _required_fields:
|
|
530
546
|
# Always computed — keep required and non-nullable for clean TS types
|
|
531
547
|
return core_schema.typed_dict_field(inner, required=True)
|
|
532
|
-
# DB fields / relationships: TypeDAL can return partial rows, so allow None
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
required=False,
|
|
536
|
-
)
|
|
548
|
+
# DB fields / relationships: TypeDAL can return partial rows, so allow None when present,
|
|
549
|
+
# but keep missing fields absent (don't auto-fill null/default values).
|
|
550
|
+
return core_schema.typed_dict_field(core_schema.nullable_schema(inner), required=False)
|
|
537
551
|
|
|
538
552
|
schema_fields = {field_name: make_field(field_name, field_type) for field_name, field_type in fields.items()}
|
|
539
553
|
|
|
@@ -586,14 +600,15 @@ class PydanticMixin(Mixin):
|
|
|
586
600
|
def model_dump(self, mode: str = "python", *, _shallow: bool = False) -> dict[str, t.Any]:
|
|
587
601
|
"""Serialize this model to a dict, with optional shallow nested output."""
|
|
588
602
|
cls = type(self)
|
|
603
|
+
relationship_names = set(cls.get_relationships())
|
|
589
604
|
data: dict[str, t.Any] = {}
|
|
590
605
|
for field_name in self._pydantic_fields(
|
|
591
606
|
include_relationships=not _shallow,
|
|
592
607
|
include_properties=not _shallow,
|
|
593
608
|
):
|
|
594
|
-
#
|
|
595
|
-
|
|
596
|
-
if
|
|
609
|
+
# Only include relationship data that was already selected/joined.
|
|
610
|
+
# This prevents model_dump from triggering lazy-loading queries.
|
|
611
|
+
if field_name in relationship_names and field_name not in self.__dict__:
|
|
597
612
|
continue
|
|
598
613
|
|
|
599
614
|
data[field_name] = getattr(self, field_name, None)
|
|
@@ -421,8 +421,8 @@ class TypedRows(t.Collection[T_MetaInstance], Rows):
|
|
|
421
421
|
fields: list[Field] | None = None,
|
|
422
422
|
) -> t.Generator[T_MetaInstance, None, None] | T_MetaInstance:
|
|
423
423
|
"""
|
|
424
|
-
Takes an index and returns a copy of the indexed row with values
|
|
425
|
-
|
|
424
|
+
Takes an index and returns a copy of the indexed row with values
|
|
425
|
+
transformed via the "represent" attributes of the associated fields.
|
|
426
426
|
|
|
427
427
|
Args:
|
|
428
428
|
i: index. If not specified, a generator is returned for iteration
|
|
@@ -1104,6 +1104,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1104
1104
|
# then create a new (more empty) row object:
|
|
1105
1105
|
state["_row"] = Row(json.loads(state["_row"]))
|
|
1106
1106
|
self.__dict__ |= state
|
|
1107
|
+
self._setup_instance_methods()
|
|
1107
1108
|
|
|
1108
1109
|
@classmethod
|
|
1109
1110
|
def _sql(cls) -> str:
|
|
@@ -35,7 +35,9 @@ except ImportError:
|
|
|
35
35
|
# Internal references
|
|
36
36
|
if t.TYPE_CHECKING:
|
|
37
37
|
from .fields import TypedField
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
# noinspection PyUnusedImports
|
|
40
|
+
from .tables import TypedTable, _TypedTable
|
|
39
41
|
|
|
40
42
|
# ---------------------------------------------------------------------------
|
|
41
43
|
# Aliases
|
|
@@ -237,6 +237,11 @@ class PydanticHiddenTypedField(TypedTable, PydanticMixin):
|
|
|
237
237
|
hidden = TypedField(str, readable=False)
|
|
238
238
|
|
|
239
239
|
|
|
240
|
+
class PydanticHiddenRelationshipHost(TypedTable, PydanticMixin):
|
|
241
|
+
name: str
|
|
242
|
+
with_hidden = relationship(list[PydanticHiddenTypedField], lambda self, other: self.id == other.id, lazy="allow")
|
|
243
|
+
|
|
244
|
+
|
|
240
245
|
@pytest.fixture
|
|
241
246
|
def pydantic_db():
|
|
242
247
|
db = TypeDAL("sqlite:memory")
|
|
@@ -247,6 +252,7 @@ def pydantic_db():
|
|
|
247
252
|
db.define(PydanticGenericResolvedRelationship)
|
|
248
253
|
db.define(PydanticGenericUnresolvedRelationship)
|
|
249
254
|
db.define(PydanticHiddenTypedField)
|
|
255
|
+
db.define(PydanticHiddenRelationshipHost)
|
|
250
256
|
db.define(NonPydanticAuthor)
|
|
251
257
|
yield db
|
|
252
258
|
|
|
@@ -460,6 +466,46 @@ def test_pydantic_skips_unreadable_typedfield_in_model_dump(pydantic_db):
|
|
|
460
466
|
assert data == {"id": row.id}
|
|
461
467
|
|
|
462
468
|
|
|
469
|
+
def test_pydantic_skips_unreadable_typedfield_in_nested_list_relationship_dump(pydantic_db):
|
|
470
|
+
hidden = PydanticHiddenTypedField.insert(visible="show", hidden="hide")
|
|
471
|
+
host = PydanticHiddenRelationshipHost.insert(name="Host")
|
|
472
|
+
assert hidden.id == host.id
|
|
473
|
+
|
|
474
|
+
joined = PydanticHiddenRelationshipHost.where(id=host.id).join("with_hidden").first()
|
|
475
|
+
assert joined is not None
|
|
476
|
+
|
|
477
|
+
data = joined.model_dump(mode="json")
|
|
478
|
+
assert data["with_hidden"] == [{"id": hidden.id, "visible": "show"}]
|
|
479
|
+
assert joined.with_hidden[0].hidden == "hide"
|
|
480
|
+
assert [item.model_dump() for item in joined.with_hidden] == [{"id": hidden.id, "visible": "show"}]
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def test_pydantic_model_dump_never_lazy_loads_unjoined_relationships(pydantic_db):
|
|
484
|
+
hidden = PydanticHiddenTypedField.insert(visible="show", hidden="hide")
|
|
485
|
+
host = PydanticHiddenRelationshipHost.insert(name="Host")
|
|
486
|
+
assert hidden.id == host.id
|
|
487
|
+
|
|
488
|
+
data = host.model_dump(mode="json")
|
|
489
|
+
assert "with_hidden" not in data
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def test_pydantic_type_adapter_skips_unreadable_fields(pydantic_db):
|
|
493
|
+
row = PydanticHiddenTypedField.insert(visible="show", hidden="hide")
|
|
494
|
+
ta = pydantic.TypeAdapter(PydanticHiddenTypedField)
|
|
495
|
+
data = ta.validate_python(row)
|
|
496
|
+
assert data == {"id": row.id, "visible": "show"}
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def test_pydantic_type_adapter_never_lazy_loads_unjoined_relationships(pydantic_db):
|
|
500
|
+
hidden = PydanticHiddenTypedField.insert(visible="show", hidden="hide")
|
|
501
|
+
host = PydanticHiddenRelationshipHost.insert(name="Host")
|
|
502
|
+
assert hidden.id == host.id
|
|
503
|
+
|
|
504
|
+
ta = pydantic.TypeAdapter(PydanticHiddenRelationshipHost)
|
|
505
|
+
data = ta.validate_python(host)
|
|
506
|
+
assert "with_hidden" not in data
|
|
507
|
+
|
|
508
|
+
|
|
463
509
|
def test_pydantic_compatibility_non_type_and_missing_relationship_type():
|
|
464
510
|
# ForwardRef is not a runtime type; this should be a no-op, not a crash.
|
|
465
511
|
PydanticMixin._ensure_pydantic_compatible_type("x", typing.ForwardRef("Anything"))
|
|
@@ -266,6 +266,15 @@ def test_render():
|
|
|
266
266
|
assert rendered_two.normal == "123"
|
|
267
267
|
assert rendered_two.list_field == "abc, def"
|
|
268
268
|
assert rendered_two.related.also_normal == "321"
|
|
269
|
+
assert json.loads(rendered_two.as_json()) == {
|
|
270
|
+
"id": rendered_two.id,
|
|
271
|
+
"normal": "123",
|
|
272
|
+
"list_field": "abc, def",
|
|
273
|
+
"related": {
|
|
274
|
+
"id": rendered_two.related.id,
|
|
275
|
+
"also_normal": "321",
|
|
276
|
+
},
|
|
277
|
+
}
|
|
269
278
|
|
|
270
279
|
# test list:
|
|
271
280
|
|
|
@@ -287,3 +296,14 @@ def test_render():
|
|
|
287
296
|
assert rendered_four.normal == "123"
|
|
288
297
|
assert rendered_four.list_field == "abc, def"
|
|
289
298
|
assert rendered_four.related_list[0].also_normal == "321"
|
|
299
|
+
assert json.loads(rendered_four.as_json()) == {
|
|
300
|
+
"id": rendered_four.id,
|
|
301
|
+
"normal": "123",
|
|
302
|
+
"list_field": "abc, def",
|
|
303
|
+
"related_list": [
|
|
304
|
+
{
|
|
305
|
+
"id": rendered_four.related_list[0].id,
|
|
306
|
+
"also_normal": "321",
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
}
|
|
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
|
|
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
|