pytrilogy 0.0.3.100__tar.gz → 0.0.3.101__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.100/pytrilogy.egg-info → pytrilogy-0.0.3.101}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parsing.py +1 -1
- pytrilogy-0.0.3.101/tests/test_validators.py +209 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/exceptions.py +1 -1
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/functions.py +5 -2
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/core.py +3 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/group_node.py +1 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/node_merge_node.py +0 -3
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/datasource.py +30 -6
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/base.py +1 -1
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/render.py +5 -3
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/trilogy.lark +6 -3
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/README.md +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/setup.cfg +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/setup.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_node_factory.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/common.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/concept.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/environment.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/fix.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/metadata.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/parse_engine.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/metric.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/utility.py +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from trilogy.core.models.core import (
|
|
5
|
+
ArrayType,
|
|
6
|
+
DataType,
|
|
7
|
+
MapType,
|
|
8
|
+
NumericType,
|
|
9
|
+
StructType,
|
|
10
|
+
TraitDataType,
|
|
11
|
+
)
|
|
12
|
+
from trilogy.core.validation.datasource import type_check
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_type_check():
|
|
16
|
+
# Basic string tests
|
|
17
|
+
assert type_check("hello", DataType.STRING)
|
|
18
|
+
assert not type_check(123, DataType.STRING)
|
|
19
|
+
assert type_check("", DataType.STRING) # empty string
|
|
20
|
+
|
|
21
|
+
# Integer tests
|
|
22
|
+
assert type_check(123, DataType.INTEGER)
|
|
23
|
+
assert type_check(0, DataType.INTEGER)
|
|
24
|
+
assert type_check(-123, DataType.INTEGER)
|
|
25
|
+
assert not type_check("123", DataType.INTEGER)
|
|
26
|
+
assert not type_check(123.0, DataType.INTEGER) # float should not match int
|
|
27
|
+
|
|
28
|
+
# BIGINT tests (same as INTEGER in implementation)
|
|
29
|
+
assert type_check(123, DataType.BIGINT)
|
|
30
|
+
assert type_check(2**63 - 1, DataType.BIGINT) # large integer
|
|
31
|
+
assert not type_check("123", DataType.BIGINT)
|
|
32
|
+
assert not type_check(123.0, DataType.BIGINT)
|
|
33
|
+
|
|
34
|
+
# Float tests
|
|
35
|
+
assert type_check(123.45, DataType.FLOAT)
|
|
36
|
+
assert type_check(123, DataType.FLOAT) # int should match float
|
|
37
|
+
assert type_check(0.0, DataType.FLOAT)
|
|
38
|
+
assert type_check(-123.45, DataType.FLOAT)
|
|
39
|
+
assert not type_check("123.45", DataType.FLOAT)
|
|
40
|
+
|
|
41
|
+
# Decimal support in float
|
|
42
|
+
decimal_val = Decimal("123.45")
|
|
43
|
+
assert type_check(decimal_val, DataType.FLOAT)
|
|
44
|
+
|
|
45
|
+
# NumericType tests
|
|
46
|
+
numeric_type = NumericType() # Assuming NumericType can be instantiated
|
|
47
|
+
assert type_check(123.45, numeric_type)
|
|
48
|
+
assert type_check(123, numeric_type)
|
|
49
|
+
assert type_check(decimal_val, numeric_type)
|
|
50
|
+
assert not type_check("123", numeric_type)
|
|
51
|
+
|
|
52
|
+
# NUMBER and NUMERIC tests (both handle int, float, Decimal)
|
|
53
|
+
assert type_check(123, DataType.NUMBER)
|
|
54
|
+
assert type_check(123.45, DataType.NUMBER)
|
|
55
|
+
assert type_check(decimal_val, DataType.NUMBER)
|
|
56
|
+
assert not type_check("123", DataType.NUMBER)
|
|
57
|
+
|
|
58
|
+
assert type_check(123, DataType.NUMERIC)
|
|
59
|
+
assert type_check(123.45, DataType.NUMERIC)
|
|
60
|
+
assert type_check(decimal_val, DataType.NUMERIC)
|
|
61
|
+
assert not type_check("123", DataType.NUMERIC)
|
|
62
|
+
|
|
63
|
+
# Boolean tests
|
|
64
|
+
assert type_check(True, DataType.BOOL)
|
|
65
|
+
assert type_check(False, DataType.BOOL)
|
|
66
|
+
assert not type_check(1, DataType.BOOL) # int should not match bool
|
|
67
|
+
assert not type_check(0, DataType.BOOL)
|
|
68
|
+
assert not type_check("true", DataType.BOOL)
|
|
69
|
+
|
|
70
|
+
# Date tests
|
|
71
|
+
test_date = date(2023, 12, 25)
|
|
72
|
+
assert type_check(test_date, DataType.DATE)
|
|
73
|
+
assert not type_check("2023-12-25", DataType.DATE)
|
|
74
|
+
assert not type_check(
|
|
75
|
+
datetime.now(), DataType.DATE
|
|
76
|
+
) # datetime should not match date
|
|
77
|
+
|
|
78
|
+
# DateTime and Timestamp tests
|
|
79
|
+
test_datetime = datetime(2023, 12, 25, 15, 30, 45)
|
|
80
|
+
assert type_check(test_datetime, DataType.DATETIME)
|
|
81
|
+
assert type_check(test_datetime, DataType.TIMESTAMP)
|
|
82
|
+
assert not type_check("2023-12-25 15:30:45", DataType.DATETIME)
|
|
83
|
+
assert not type_check(
|
|
84
|
+
test_date, DataType.DATETIME
|
|
85
|
+
) # date should not match datetime
|
|
86
|
+
|
|
87
|
+
# Unix seconds tests
|
|
88
|
+
assert type_check(1640995200, DataType.UNIX_SECONDS) # int timestamp
|
|
89
|
+
assert type_check(1640995200.123, DataType.UNIX_SECONDS) # float timestamp
|
|
90
|
+
assert not type_check("1640995200", DataType.UNIX_SECONDS)
|
|
91
|
+
|
|
92
|
+
# Date part tests
|
|
93
|
+
assert type_check("year", DataType.DATE_PART)
|
|
94
|
+
assert type_check("month", DataType.DATE_PART)
|
|
95
|
+
assert type_check("day", DataType.DATE_PART)
|
|
96
|
+
assert not type_check(2023, DataType.DATE_PART)
|
|
97
|
+
|
|
98
|
+
# Array tests
|
|
99
|
+
assert type_check([1, 2, 3], DataType.ARRAY)
|
|
100
|
+
assert type_check([], DataType.ARRAY) # empty array
|
|
101
|
+
assert type_check(["a", "b"], DataType.ARRAY)
|
|
102
|
+
assert not type_check("not a list", DataType.ARRAY)
|
|
103
|
+
assert not type_check({"key": "value"}, DataType.ARRAY)
|
|
104
|
+
|
|
105
|
+
# ArrayType tests
|
|
106
|
+
array_type = ArrayType(type=DataType.INTEGER)
|
|
107
|
+
assert type_check([1, 2, 3], array_type)
|
|
108
|
+
assert not type_check("not a list", array_type)
|
|
109
|
+
|
|
110
|
+
# Map tests
|
|
111
|
+
assert type_check({"key": "value"}, DataType.MAP)
|
|
112
|
+
assert type_check({}, DataType.MAP) # empty dict
|
|
113
|
+
assert type_check({"a": 1, "b": 2}, DataType.MAP)
|
|
114
|
+
assert not type_check([1, 2, 3], DataType.MAP)
|
|
115
|
+
assert not type_check("not a dict", DataType.MAP)
|
|
116
|
+
|
|
117
|
+
# MapType tests
|
|
118
|
+
map_type = MapType(key_type=DataType.STRING, value_type=DataType.STRING)
|
|
119
|
+
assert type_check({"key": "value"}, map_type)
|
|
120
|
+
assert not type_check([1, 2, 3], map_type)
|
|
121
|
+
|
|
122
|
+
# Struct tests
|
|
123
|
+
assert type_check({"field1": "value1"}, DataType.STRUCT)
|
|
124
|
+
assert type_check({}, DataType.STRUCT)
|
|
125
|
+
assert not type_check([1, 2, 3], DataType.STRUCT)
|
|
126
|
+
|
|
127
|
+
# StructType tests
|
|
128
|
+
struct_type = StructType(
|
|
129
|
+
fields=[DataType.STRING, DataType.STRING],
|
|
130
|
+
fields_map={"field1": DataType.STRING, "field2": DataType.STRING},
|
|
131
|
+
)
|
|
132
|
+
assert not type_check("not a dict", struct_type)
|
|
133
|
+
|
|
134
|
+
# NULL tests
|
|
135
|
+
assert type_check(None, DataType.NULL)
|
|
136
|
+
assert not type_check("", DataType.NULL)
|
|
137
|
+
assert not type_check(0, DataType.NULL)
|
|
138
|
+
assert not type_check(False, DataType.NULL)
|
|
139
|
+
|
|
140
|
+
# UNKNOWN tests (should accept anything)
|
|
141
|
+
assert type_check("anything", DataType.UNKNOWN)
|
|
142
|
+
assert type_check(123, DataType.UNKNOWN)
|
|
143
|
+
assert type_check(None, DataType.UNKNOWN)
|
|
144
|
+
assert type_check([1, 2, 3], DataType.UNKNOWN)
|
|
145
|
+
assert type_check({"key": "value"}, DataType.UNKNOWN)
|
|
146
|
+
|
|
147
|
+
# Nullable tests
|
|
148
|
+
assert type_check(None, DataType.STRING) # nullable by default
|
|
149
|
+
assert type_check(None, DataType.INTEGER)
|
|
150
|
+
assert type_check(None, DataType.FLOAT)
|
|
151
|
+
assert type_check(None, DataType.BOOL)
|
|
152
|
+
|
|
153
|
+
# Non-nullable tests
|
|
154
|
+
assert not type_check(None, DataType.STRING, nullable=False)
|
|
155
|
+
assert not type_check(None, DataType.INTEGER, nullable=False)
|
|
156
|
+
assert not type_check(None, DataType.FLOAT, nullable=False)
|
|
157
|
+
assert not type_check(None, DataType.BOOL, nullable=False)
|
|
158
|
+
|
|
159
|
+
# TraitDataType tests (recursive handling)
|
|
160
|
+
# Assuming TraitDataType wraps another DataType
|
|
161
|
+
trait_string_type = TraitDataType(type=DataType.STRING, traits=[])
|
|
162
|
+
assert type_check("hello", trait_string_type)
|
|
163
|
+
assert not type_check(123, trait_string_type)
|
|
164
|
+
assert type_check(None, trait_string_type) # nullable by default
|
|
165
|
+
assert not type_check(None, trait_string_type, nullable=False)
|
|
166
|
+
|
|
167
|
+
# Nested TraitDataType
|
|
168
|
+
nested_trait_type = TraitDataType(type=DataType.STRING, traits=[])
|
|
169
|
+
assert type_check("hello", nested_trait_type)
|
|
170
|
+
assert not type_check(123, nested_trait_type)
|
|
171
|
+
|
|
172
|
+
# Edge cases and invalid types
|
|
173
|
+
# Test with unsupported/custom types should return False
|
|
174
|
+
class CustomType:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
custom_obj = CustomType()
|
|
178
|
+
# These should all return False as they don't match any known type
|
|
179
|
+
assert not type_check(custom_obj, DataType.STRING)
|
|
180
|
+
assert not type_check(custom_obj, DataType.INTEGER)
|
|
181
|
+
assert not type_check(custom_obj, DataType.ARRAY)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_type_check_edge_cases():
|
|
185
|
+
"""Additional edge case tests"""
|
|
186
|
+
|
|
187
|
+
# Test very large numbers
|
|
188
|
+
large_int = 2**100
|
|
189
|
+
assert type_check(large_int, DataType.INTEGER)
|
|
190
|
+
assert type_check(large_int, DataType.BIGINT)
|
|
191
|
+
assert type_check(large_int, DataType.FLOAT) # int should match float
|
|
192
|
+
|
|
193
|
+
# Test special float values
|
|
194
|
+
assert type_check(float("inf"), DataType.FLOAT)
|
|
195
|
+
assert type_check(float("-inf"), DataType.FLOAT)
|
|
196
|
+
# Note: NaN might need special handling depending on requirements
|
|
197
|
+
# assert type_check(float('nan'), DataType.FLOAT)
|
|
198
|
+
|
|
199
|
+
# Test empty collections
|
|
200
|
+
assert type_check([], DataType.ARRAY)
|
|
201
|
+
assert type_check({}, DataType.MAP)
|
|
202
|
+
assert type_check({}, DataType.STRUCT)
|
|
203
|
+
|
|
204
|
+
# Test nested collections
|
|
205
|
+
nested_list = [[1, 2], [3, 4]]
|
|
206
|
+
nested_dict = {"outer": {"inner": "value"}}
|
|
207
|
+
assert type_check(nested_list, DataType.ARRAY)
|
|
208
|
+
assert type_check(nested_dict, DataType.MAP)
|
|
209
|
+
assert type_check(nested_dict, DataType.STRUCT)
|
|
@@ -69,7 +69,7 @@ class DatasourceColumnBindingData:
|
|
|
69
69
|
actual_modifiers: List[Modifier]
|
|
70
70
|
|
|
71
71
|
def format_failure(self):
|
|
72
|
-
return f"Concept {self.address} value '{self.value}' with type {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
|
|
72
|
+
return f"Concept {self.address} value '{self.value}' with type {self.value_type} and {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
|
|
73
73
|
|
|
74
74
|
def is_modifier_issue(self) -> bool:
|
|
75
75
|
return len(self.value_modifiers) > 0 and any(
|
|
@@ -18,6 +18,7 @@ from trilogy.core.models.author import (
|
|
|
18
18
|
AggregateWrapper,
|
|
19
19
|
Concept,
|
|
20
20
|
ConceptRef,
|
|
21
|
+
Conditional,
|
|
21
22
|
Function,
|
|
22
23
|
Parenthetical,
|
|
23
24
|
UndefinedConcept,
|
|
@@ -129,8 +130,8 @@ def validate_case_output(
|
|
|
129
130
|
def create_struct_output(
|
|
130
131
|
args: list[Any],
|
|
131
132
|
) -> StructType:
|
|
132
|
-
zipped = dict(zip(args[::2], args[
|
|
133
|
-
types = [arg_to_datatype(x) for x in args[
|
|
133
|
+
zipped = dict(zip(args[1::2], args[::2]))
|
|
134
|
+
types = [arg_to_datatype(x) for x in args[::2]]
|
|
134
135
|
return StructType(fields=types, fields_map=zipped)
|
|
135
136
|
|
|
136
137
|
|
|
@@ -997,6 +998,8 @@ def argument_to_purpose(arg) -> Purpose:
|
|
|
997
998
|
return argument_to_purpose(arg.content)
|
|
998
999
|
elif isinstance(arg, WindowItem):
|
|
999
1000
|
return Purpose.PROPERTY
|
|
1001
|
+
elif isinstance(arg, Conditional):
|
|
1002
|
+
return Purpose.PROPERTY
|
|
1000
1003
|
elif isinstance(arg, Concept):
|
|
1001
1004
|
base = arg.purpose
|
|
1002
1005
|
if (
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
from collections import UserDict, UserList
|
|
5
5
|
from datetime import date, datetime
|
|
6
|
+
from decimal import Decimal
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from typing import (
|
|
8
9
|
Any,
|
|
@@ -448,6 +449,8 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
|
|
|
448
449
|
return DataType.STRING
|
|
449
450
|
elif isinstance(arg, float):
|
|
450
451
|
return DataType.FLOAT
|
|
452
|
+
elif isinstance(arg, Decimal):
|
|
453
|
+
return DataType.NUMERIC
|
|
451
454
|
elif isinstance(arg, DataType):
|
|
452
455
|
return arg
|
|
453
456
|
elif isinstance(arg, NumericType):
|
|
@@ -164,9 +164,6 @@ def reinject_common_join_keys_v2(
|
|
|
164
164
|
reduced = BuildGrain.from_concepts(concrete_concepts).components
|
|
165
165
|
existing_addresses = set()
|
|
166
166
|
for concrete in concrete_concepts:
|
|
167
|
-
logger.debug(
|
|
168
|
-
f"looking at column {concrete.address} with pseudonyms {concrete.pseudonyms}"
|
|
169
|
-
)
|
|
170
167
|
cnode = concept_to_node(concrete.with_default_grain())
|
|
171
168
|
if cnode in final.nodes:
|
|
172
169
|
existing_addresses.add(concrete.address)
|
|
@@ -36,31 +36,51 @@ def type_check(
|
|
|
36
36
|
) -> bool:
|
|
37
37
|
if input is None and nullable:
|
|
38
38
|
return True
|
|
39
|
+
|
|
39
40
|
target_type = expected_type
|
|
40
41
|
while isinstance(target_type, TraitDataType):
|
|
41
42
|
return type_check(input, target_type.data_type, nullable)
|
|
43
|
+
|
|
42
44
|
if target_type == DataType.STRING:
|
|
43
45
|
return isinstance(input, str)
|
|
44
46
|
if target_type == DataType.INTEGER:
|
|
45
47
|
return isinstance(input, int)
|
|
48
|
+
if target_type == DataType.BIGINT:
|
|
49
|
+
return isinstance(input, int) # or check for larger int if needed
|
|
46
50
|
if target_type == DataType.FLOAT or isinstance(target_type, NumericType):
|
|
47
51
|
return (
|
|
48
52
|
isinstance(input, float)
|
|
49
53
|
or isinstance(input, int)
|
|
50
54
|
or isinstance(input, Decimal)
|
|
51
55
|
)
|
|
56
|
+
if target_type == DataType.NUMBER:
|
|
57
|
+
return isinstance(input, (int, float, Decimal))
|
|
58
|
+
if target_type == DataType.NUMERIC:
|
|
59
|
+
return isinstance(input, (int, float, Decimal))
|
|
52
60
|
if target_type == DataType.BOOL:
|
|
53
61
|
return isinstance(input, bool)
|
|
54
62
|
if target_type == DataType.DATE:
|
|
55
|
-
return isinstance(input, date)
|
|
63
|
+
return isinstance(input, date) and not isinstance(input, datetime)
|
|
56
64
|
if target_type == DataType.DATETIME:
|
|
57
65
|
return isinstance(input, datetime)
|
|
66
|
+
if target_type == DataType.TIMESTAMP:
|
|
67
|
+
return isinstance(input, datetime) # or timestamp type if you have one
|
|
68
|
+
if target_type == DataType.UNIX_SECONDS:
|
|
69
|
+
return isinstance(input, (int, float)) # Unix timestamps are numeric
|
|
70
|
+
if target_type == DataType.DATE_PART:
|
|
71
|
+
return isinstance(
|
|
72
|
+
input, str
|
|
73
|
+
) # assuming date parts are strings like "year", "month"
|
|
58
74
|
if target_type == DataType.ARRAY or isinstance(target_type, ArrayType):
|
|
59
75
|
return isinstance(input, list)
|
|
60
76
|
if target_type == DataType.MAP or isinstance(target_type, MapType):
|
|
61
77
|
return isinstance(input, dict)
|
|
62
78
|
if target_type == DataType.STRUCT or isinstance(target_type, StructType):
|
|
63
79
|
return isinstance(input, dict)
|
|
80
|
+
if target_type == DataType.NULL:
|
|
81
|
+
return input is None
|
|
82
|
+
if target_type == DataType.UNKNOWN:
|
|
83
|
+
return True
|
|
64
84
|
return False
|
|
65
85
|
|
|
66
86
|
|
|
@@ -125,15 +145,19 @@ def validate_datasource(
|
|
|
125
145
|
rval = row[actual_address]
|
|
126
146
|
passed = type_check(rval, col.concept.datatype, col.is_nullable)
|
|
127
147
|
if not passed:
|
|
148
|
+
value_type = (
|
|
149
|
+
arg_to_datatype(rval) if rval is not None else col.concept.datatype
|
|
150
|
+
)
|
|
151
|
+
traits = None
|
|
152
|
+
if isinstance(col.concept.datatype, TraitDataType):
|
|
153
|
+
traits = col.concept.datatype.traits
|
|
154
|
+
if traits and not isinstance(value_type, TraitDataType):
|
|
155
|
+
value_type = TraitDataType(type=value_type, traits=traits)
|
|
128
156
|
failures.append(
|
|
129
157
|
DatasourceColumnBindingData(
|
|
130
158
|
address=col.concept.address,
|
|
131
159
|
value=rval,
|
|
132
|
-
value_type=
|
|
133
|
-
arg_to_datatype(rval)
|
|
134
|
-
if rval is not None
|
|
135
|
-
else col.concept.datatype
|
|
136
|
-
),
|
|
160
|
+
value_type=value_type,
|
|
137
161
|
value_modifiers=[Modifier.NULLABLE] if rval is None else [],
|
|
138
162
|
actual_type=col.concept.datatype,
|
|
139
163
|
actual_modifiers=col.concept.modifiers,
|
|
@@ -349,7 +349,8 @@ class Renderer:
|
|
|
349
349
|
else:
|
|
350
350
|
output = f"{concept.purpose.value} {namespace}{concept.name} <- {self.to_string(concept.lineage)};"
|
|
351
351
|
if base_description:
|
|
352
|
-
|
|
352
|
+
lines = "\n#".join(base_description.split("\n"))
|
|
353
|
+
output += f" #{lines}"
|
|
353
354
|
return output
|
|
354
355
|
|
|
355
356
|
@to_string.register
|
|
@@ -439,7 +440,7 @@ class Renderer:
|
|
|
439
440
|
|
|
440
441
|
@to_string.register
|
|
441
442
|
def _(self, arg: "Conditional"):
|
|
442
|
-
return f"
|
|
443
|
+
return f"{self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)}"
|
|
443
444
|
|
|
444
445
|
@to_string.register
|
|
445
446
|
def _(self, arg: "SubselectComparison"):
|
|
@@ -451,7 +452,8 @@ class Renderer:
|
|
|
451
452
|
|
|
452
453
|
@to_string.register
|
|
453
454
|
def _(self, arg: "Comment"):
|
|
454
|
-
|
|
455
|
+
lines = "\n#".join(arg.text.split("\n"))
|
|
456
|
+
return f"{lines}"
|
|
455
457
|
|
|
456
458
|
@to_string.register
|
|
457
459
|
def _(self, arg: "WindowItem"):
|
|
@@ -134,10 +134,12 @@
|
|
|
134
134
|
metadata: "metadata" "(" IDENTIFIER "=" string_lit ")"
|
|
135
135
|
|
|
136
136
|
limit: "LIMIT"i /[0-9]+/
|
|
137
|
+
|
|
138
|
+
_order_atom: expr ordering
|
|
137
139
|
|
|
138
|
-
order_list:
|
|
140
|
+
order_list: _order_atom ("," _order_atom)* ","?
|
|
139
141
|
|
|
140
|
-
over_list: concept_lit
|
|
142
|
+
over_list: (concept_lit ",")* concept_lit ","?
|
|
141
143
|
|
|
142
144
|
ORDERING_DIRECTION: /ASC|DESC/i
|
|
143
145
|
|
|
@@ -433,7 +435,8 @@
|
|
|
433
435
|
map_lit: "{" (literal ":" literal ",")* literal ":" literal ","? "}"
|
|
434
436
|
|
|
435
437
|
_STRUCT.1: "struct("i
|
|
436
|
-
|
|
438
|
+
_BINDING.1: "->"
|
|
439
|
+
struct_lit: _STRUCT expr _BINDING IDENTIFIER ( "," expr _BINDING IDENTIFIER )* ","? ")"
|
|
437
440
|
|
|
438
441
|
!bool_lit: "True"i | "False"i
|
|
439
442
|
|
|
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
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/predicate_pushdown.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/concept_strategies_v3.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_node_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/common.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/constant_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/synonym_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/union_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|