TypeDAL 4.4.6__tar.gz → 4.5.0__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.4.6 → typedal-4.5.0}/CHANGELOG.md +6 -0
- {typedal-4.4.6 → typedal-4.5.0}/PKG-INFO +1 -1
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/__about__.py +1 -1
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/cli.py +2 -1
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/fields.py +2 -2
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/mixins.py +3 -6
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/query_builder.py +3 -3
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/relationships.py +1 -1
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/rows.py +17 -1
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/tables.py +63 -12
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/types.py +2 -2
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_mypy.py +24 -0
- {typedal-4.4.6 → typedal-4.5.0}/.github/workflows/su6.yml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/.gitignore +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/.readthedocs.yml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/README.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/coverage.svg +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/10_advanced_apis.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/1_getting_started.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/2_defining_tables.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/3_building_queries.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/4_relationships.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/5_py4web.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/6_migrations.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/7_configuration.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/8_mixins.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/9_memoization.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/css/code_blocks.css +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/index.md +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/docs/requirements.txt +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/example_new.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/example_old.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/mkdocs.yml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/pyproject.toml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/__init__.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/caching.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/config.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/constants.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/core.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/define.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/for_py4web.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/for_web2py.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/helpers.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/py.typed +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tasks.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/__init__.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/configs/simple.toml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/configs/valid.env +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/configs/valid.toml +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/py314_tests.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_cli.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_config.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_docs_examples.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_helpers.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_json.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_main.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_mixins.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_orm.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_py4web.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_query_builder.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_relationships.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_row.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_stats.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_table.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_web2py.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/test_xx_others.py +0 -0
- {typedal-4.4.6 → typedal-4.5.0}/tests/timings.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.5.0 (2026-03-06)
|
|
6
|
+
|
|
7
|
+
### Feature
|
|
8
|
+
|
|
9
|
+
* **typing:** Support mixin-typed table class APIs without MRO conflicts ([`fe0c99e`](https://github.com/trialandsuccess/TypeDAL/commit/fe0c99eb2790baff8ddf4de3ec1f23c53e4b3a11))
|
|
10
|
+
|
|
5
11
|
## v4.4.6 (2026-03-05)
|
|
6
12
|
|
|
7
13
|
### Fix
|
|
@@ -392,7 +392,8 @@ def fake_migrations(
|
|
|
392
392
|
|
|
393
393
|
previously_migrated = (
|
|
394
394
|
db(
|
|
395
|
-
db.ewh_implemented_features.name.belongs(to_fake)
|
|
395
|
+
db.ewh_implemented_features.name.belongs(to_fake)
|
|
396
|
+
& (db.ewh_implemented_features.installed == True) # noqa E712
|
|
396
397
|
)
|
|
397
398
|
.select(db.ewh_implemented_features.name)
|
|
398
399
|
.column("name")
|
|
@@ -31,7 +31,7 @@ from .types import (
|
|
|
31
31
|
|
|
32
32
|
if t.TYPE_CHECKING:
|
|
33
33
|
# will be imported for real later:
|
|
34
|
-
from .tables import TypedTable
|
|
34
|
+
from .tables import TypedTable, _TypedTable
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
## general
|
|
@@ -79,7 +79,7 @@ class TypedField(Expression, t.Generic[T_Value]): # pragma: no cover
|
|
|
79
79
|
"""
|
|
80
80
|
|
|
81
81
|
@t.overload
|
|
82
|
-
def __get__(self, instance: None, owner: "t.Type[
|
|
82
|
+
def __get__(self, instance: None, owner: "t.Type[_TypedTable]") -> "TypedField[T_Value]": # pragma: no cover
|
|
83
83
|
"""
|
|
84
84
|
Table.field -> Field.
|
|
85
85
|
"""
|
|
@@ -16,21 +16,18 @@ from slugify import slugify
|
|
|
16
16
|
|
|
17
17
|
from .core import TypeDAL
|
|
18
18
|
from .fields import DatetimeField, StringField
|
|
19
|
-
from .tables import _TypedTable
|
|
19
|
+
from .tables import TableMeta, _TypedTable
|
|
20
20
|
from .types import OpRow, Set, T_MetaInstance
|
|
21
21
|
|
|
22
|
-
if t.TYPE_CHECKING:
|
|
23
|
-
from .tables import TypedTable # noqa: F401
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
class Mixin(_TypedTable):
|
|
23
|
+
class Mixin(_TypedTable, metaclass=TableMeta):
|
|
27
24
|
"""
|
|
28
25
|
A mixin should be derived from this class.
|
|
29
26
|
|
|
30
27
|
The mixin base class itself doesn't do anything,
|
|
31
28
|
but using it makes sure the mixin fields are placed AFTER the table's normal fields (instead of before)
|
|
32
29
|
|
|
33
|
-
During runtime, mixin should not
|
|
30
|
+
During runtime, mixin should not inherit from TypedTable to prevent MRO issues
|
|
34
31
|
('inconsistent method resolution' or 'metaclass conflicts')
|
|
35
32
|
"""
|
|
36
33
|
|
|
@@ -23,7 +23,7 @@ from .helpers import (
|
|
|
23
23
|
normalize_table_keys,
|
|
24
24
|
throw,
|
|
25
25
|
)
|
|
26
|
-
from .tables import TableMeta, TypedTable
|
|
26
|
+
from .tables import TableMeta, TypedTable, _TypedTable
|
|
27
27
|
from .types import (
|
|
28
28
|
CacheMetadata,
|
|
29
29
|
Condition,
|
|
@@ -698,7 +698,7 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
|
|
|
698
698
|
return joins
|
|
699
699
|
|
|
700
700
|
def _build_inner_joins_recursive(
|
|
701
|
-
self, relation: Relationship[t.Any], parent_table: t.Type[
|
|
701
|
+
self, relation: Relationship[t.Any], parent_table: t.Type[_TypedTable], key: str, parent_key: str = ""
|
|
702
702
|
) -> list[t.Any]:
|
|
703
703
|
"""Recursively build inner joins for a relationship and its nested relationships."""
|
|
704
704
|
db = self._get_db()
|
|
@@ -764,7 +764,7 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
|
|
|
764
764
|
key: str,
|
|
765
765
|
select_args: list[t.Any],
|
|
766
766
|
left_joins: list[Expression],
|
|
767
|
-
parent_table: t.Type[
|
|
767
|
+
parent_table: t.Type[_TypedTable],
|
|
768
768
|
parent_key: str = "",
|
|
769
769
|
) -> list[t.Any]:
|
|
770
770
|
"""Process a single relationship for left join and field selection."""
|
|
@@ -400,11 +400,27 @@ class TypedRows(t.Collection[T_MetaInstance], Rows):
|
|
|
400
400
|
self.__dict__.update(state)
|
|
401
401
|
# db etc. set after undill by caching.py
|
|
402
402
|
|
|
403
|
+
@t.overload
|
|
403
404
|
def render(
|
|
404
405
|
self,
|
|
405
|
-
i:
|
|
406
|
+
i: None = None,
|
|
406
407
|
fields: list[Field] | None = None,
|
|
407
408
|
) -> t.Generator[T_MetaInstance, None, None]:
|
|
409
|
+
"""With no index, yield rendered rows as a generator."""
|
|
410
|
+
|
|
411
|
+
@t.overload
|
|
412
|
+
def render(
|
|
413
|
+
self,
|
|
414
|
+
i: int,
|
|
415
|
+
fields: list[Field] | None = None,
|
|
416
|
+
) -> T_MetaInstance:
|
|
417
|
+
"""With an index, return one rendered row instance."""
|
|
418
|
+
|
|
419
|
+
def render(
|
|
420
|
+
self,
|
|
421
|
+
i: int | None = None,
|
|
422
|
+
fields: list[Field] | None = None,
|
|
423
|
+
) -> t.Generator[T_MetaInstance, None, None] | T_MetaInstance:
|
|
408
424
|
"""
|
|
409
425
|
Takes an index and returns a copy of the indexed row with values \
|
|
410
426
|
transformed via the "represent" attributes of the associated fields.
|
|
@@ -650,7 +650,7 @@ class TableMeta(type):
|
|
|
650
650
|
return reorder_fields(cls._table, fields, keep_others=keep_others)
|
|
651
651
|
|
|
652
652
|
|
|
653
|
-
class _TypedTable:
|
|
653
|
+
class _TypedTable(metaclass=TableMeta):
|
|
654
654
|
"""
|
|
655
655
|
This class is a final shared parent between TypedTable and Mixins.
|
|
656
656
|
|
|
@@ -661,6 +661,9 @@ class _TypedTable:
|
|
|
661
661
|
-> Setting 'TypedTable' as the parent for Mixin does not work at runtime (and works semi at type check time)
|
|
662
662
|
"""
|
|
663
663
|
|
|
664
|
+
# This class contains weird typing glue to dodge MRO headaches without losing editor/mypy table methods
|
|
665
|
+
# you can safely ignore it when changing runtime behavior; touch it only for typing/mypy issues
|
|
666
|
+
|
|
664
667
|
id: "TypedField[int]"
|
|
665
668
|
|
|
666
669
|
_before_insert: list[t.Callable[[t.Self], t.Optional[bool]] | t.Callable[[OpRow], t.Optional[bool]]]
|
|
@@ -671,6 +674,8 @@ class _TypedTable:
|
|
|
671
674
|
_after_update: list[t.Callable[[Set, t.Self], t.Optional[bool]] | t.Callable[[Set, OpRow], t.Optional[bool]]]
|
|
672
675
|
_before_delete: list[t.Callable[[Set], t.Optional[bool]]]
|
|
673
676
|
_after_delete: list[t.Callable[[Set], t.Optional[bool]]]
|
|
677
|
+
_rows: tuple[Row, ...]
|
|
678
|
+
_with: list[str]
|
|
674
679
|
|
|
675
680
|
@classmethod
|
|
676
681
|
def __on_define__(cls, db: TypeDAL) -> None:
|
|
@@ -681,16 +686,49 @@ class _TypedTable:
|
|
|
681
686
|
where you need a reference to the current database, which may not exist yet when defining the model.
|
|
682
687
|
"""
|
|
683
688
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
689
|
+
def __new__(cls, *_args: t.Any, **_kwargs: t.Any) -> t.Self:
|
|
690
|
+
"""
|
|
691
|
+
Shared constructor signature for typing.
|
|
692
|
+
|
|
693
|
+
TypedTable provides the concrete behavior; this base only keeps static typing happy
|
|
694
|
+
for generic classmethod flows that instantiate `self(...)`.
|
|
695
|
+
"""
|
|
696
|
+
return super().__new__(cls)
|
|
697
|
+
|
|
698
|
+
# Add an abstract placeholder here only when generic code is typed against
|
|
699
|
+
# `T_MetaInstance` (bound to `_TypedTable`) and directly calls/accesses that member.
|
|
700
|
+
# If a member is only used on concrete `TypedTable` paths, it should stay on `TypedTable`.
|
|
701
|
+
def _ensure_matching_row(self) -> Row:
|
|
702
|
+
# Typed on the shared base so generic instance helpers can call into row access safely.
|
|
703
|
+
raise NotImplementedError # pragma: no cover
|
|
704
|
+
|
|
705
|
+
def _update(self: t.Self, **fields: t.Any) -> t.Self:
|
|
706
|
+
# Declared here for generic update flows; real behavior is implemented in TypedTable.
|
|
707
|
+
raise NotImplementedError # pragma: no cover
|
|
708
|
+
|
|
709
|
+
def _update_record(self: t.Self, **fields: t.Any) -> t.Self:
|
|
710
|
+
# Declared here for generic update flows; real behavior is implemented in TypedTable.
|
|
711
|
+
raise NotImplementedError # pragma: no cover
|
|
712
|
+
|
|
713
|
+
def update_record(self: t.Self, **fields: t.Any) -> t.Self:
|
|
714
|
+
# Declared here for generic update flows; real behavior is implemented in TypedTable.
|
|
715
|
+
raise NotImplementedError # pragma: no cover
|
|
716
|
+
|
|
717
|
+
def as_dict(self, *args: t.Any, **kwargs: t.Any) -> AnyDict:
|
|
718
|
+
# Broad signature keeps class/instance serialization overrides LSP-compatible.
|
|
719
|
+
raise NotImplementedError # pragma: no cover
|
|
720
|
+
|
|
721
|
+
def render(self: t.Self, *fields: t.Any, **kwargs: t.Any) -> t.Self:
|
|
722
|
+
# Rows/QueryBuilder treat render as model-preserving, so this returns Self for typing.
|
|
723
|
+
raise NotImplementedError # pragma: no cover
|
|
724
|
+
|
|
725
|
+
def __getitem__(self, key: str) -> t.Any:
|
|
726
|
+
# Relationship collection writes into model instances via dict-style access.
|
|
727
|
+
raise NotImplementedError # pragma: no cover
|
|
728
|
+
|
|
729
|
+
def __setitem__(self, key: str, value: t.Any) -> None:
|
|
730
|
+
# Relationship collection writes into model instances via dict-style access.
|
|
731
|
+
raise NotImplementedError # pragma: no cover
|
|
694
732
|
|
|
695
733
|
|
|
696
734
|
class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
@@ -702,8 +740,21 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
702
740
|
_row: Row | None = None
|
|
703
741
|
_rows: tuple[Row, ...] = ()
|
|
704
742
|
|
|
743
|
+
id: "TypedField[int]"
|
|
744
|
+
|
|
705
745
|
_with: list[str]
|
|
706
746
|
|
|
747
|
+
@classproperty
|
|
748
|
+
def _hooks(cls) -> dict[str, list[t.Callable[..., t.Optional[bool]]]]:
|
|
749
|
+
return {
|
|
750
|
+
"before_insert": cls._before_insert,
|
|
751
|
+
"after_insert": cls._after_insert,
|
|
752
|
+
"before_update": cls._before_update,
|
|
753
|
+
"after_update": cls._after_update,
|
|
754
|
+
"before_delete": cls._before_delete,
|
|
755
|
+
"after_delete": cls._after_delete,
|
|
756
|
+
}
|
|
757
|
+
|
|
707
758
|
def _setup_instance_methods(self) -> None:
|
|
708
759
|
self.as_dict = self._as_dict # type: ignore
|
|
709
760
|
self.__json__ = self.as_json = self._as_json # type: ignore
|
|
@@ -990,7 +1041,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
990
1041
|
def _update_record(self: T_MetaInstance, **fields: t.Any) -> T_MetaInstance:
|
|
991
1042
|
row = self._ensure_matching_row()
|
|
992
1043
|
new_row = row.update_record(**fields)
|
|
993
|
-
self.
|
|
1044
|
+
self._update(**new_row)
|
|
994
1045
|
return self
|
|
995
1046
|
|
|
996
1047
|
def update_record(self: T_MetaInstance, **fields: t.Any) -> T_MetaInstance: # pragma: no cover
|
|
@@ -35,7 +35,7 @@ except ImportError:
|
|
|
35
35
|
# Internal references
|
|
36
36
|
if t.TYPE_CHECKING:
|
|
37
37
|
from .fields import TypedField
|
|
38
|
-
from .tables import TypedTable
|
|
38
|
+
from .tables import TypedTable, _TypedTable
|
|
39
39
|
|
|
40
40
|
# ---------------------------------------------------------------------------
|
|
41
41
|
# Aliases
|
|
@@ -297,7 +297,7 @@ T = t.TypeVar("T", bound=t.Any)
|
|
|
297
297
|
P = t.ParamSpec("P")
|
|
298
298
|
R = t.TypeVar("R")
|
|
299
299
|
|
|
300
|
-
T_MetaInstance = t.TypeVar("T_MetaInstance", bound="
|
|
300
|
+
T_MetaInstance = t.TypeVar("T_MetaInstance", bound="_TypedTable")
|
|
301
301
|
T_Query = t.Union[
|
|
302
302
|
"Table",
|
|
303
303
|
Query,
|
|
@@ -10,6 +10,7 @@ from typedal import ( # todo: why does src.typedal not work anymore?
|
|
|
10
10
|
TypedRows,
|
|
11
11
|
TypedTable,
|
|
12
12
|
)
|
|
13
|
+
from typedal.mixins import Mixin
|
|
13
14
|
from typedal.types import CacheFn, CacheTuple, OpRow, Reference, Rows
|
|
14
15
|
|
|
15
16
|
db = TypeDAL("sqlite:memory")
|
|
@@ -29,6 +30,14 @@ class OtherTable(TypedTable): ...
|
|
|
29
30
|
class LaterDefine(TypedTable): ...
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
class SearchMixin(Mixin): ...
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@db.define
|
|
37
|
+
class SearchableTable(TypedTable, SearchMixin):
|
|
38
|
+
title: str
|
|
39
|
+
|
|
40
|
+
|
|
32
41
|
old_style = db.define_table("old_table")
|
|
33
42
|
|
|
34
43
|
|
|
@@ -152,6 +161,21 @@ def mypy_test_query() -> None:
|
|
|
152
161
|
reveal_type(MyTable.where().column(MyTable.fancy)) # R: builtins.list[builtins.str]
|
|
153
162
|
|
|
154
163
|
|
|
164
|
+
@pytest.mark.mypy_testing
|
|
165
|
+
def mypy_test_rows_render_overload() -> None:
|
|
166
|
+
rows = MyTable.where().collect()
|
|
167
|
+
reveal_type(rows.render()) # R: typing.Generator[tests.test_mypy.MyTable, None, None]
|
|
168
|
+
reveal_type(rows.render(1)) # R: tests.test_mypy.MyTable
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@pytest.mark.mypy_testing
|
|
172
|
+
def mypy_test_mixin_typed_table_argument() -> None:
|
|
173
|
+
def using_mixin(table: type[SearchMixin]) -> None:
|
|
174
|
+
reveal_type(table.where()) # R: typedal.query_builder.QueryBuilder[tests.test_mypy.SearchMixin]
|
|
175
|
+
|
|
176
|
+
using_mixin(SearchableTable)
|
|
177
|
+
|
|
178
|
+
|
|
155
179
|
@pytest.mark.mypy_testing
|
|
156
180
|
def mypy_test_cachefn() -> None:
|
|
157
181
|
def cache_model(key: str, fn: CacheFn, expire: int) -> Rows:
|
|
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
|