pytrilogy 0.0.2.27__py3-none-any.whl → 0.0.2.28__py3-none-any.whl

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.27
3
+ Version: 0.0.2.28
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,8 +1,8 @@
1
- trilogy/__init__.py,sha256=0dmRt4UwzuYJgQIl-qXbs6u3dziHijxpsqFSoJ53myE,291
1
+ trilogy/__init__.py,sha256=Oj4rApJpgEd7VNBVDA5Sy1tHnI0ISyXQcfpiH-fv6UY,291
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=KiyYnctoZen4Hzv8WG2jeN-IE-dfQbWHdVCUeTZYjBg,1270
4
4
  trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
5
- trilogy/executor.py,sha256=Gd9KRT1rNAQyF1oDtKMcidg6XWqGMBhPnErrzFpf7Ew,12139
5
+ trilogy/executor.py,sha256=VcZ2U3RUU2al_VJ75AKVwmCJQLltYouxlgTjq4oxPB0,12577
6
6
  trilogy/parser.py,sha256=UtuqSiGiCjpMAYgo1bvNq-b7NSzCA5hzbUW31RXaMII,281
7
7
  trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
@@ -16,7 +16,7 @@ trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,56
16
16
  trilogy/core/functions.py,sha256=IhVpt3n6wEanKHnGu3oA2w6-hKIlxWpEyz7fHN66mpo,10720
17
17
  trilogy/core/graph_models.py,sha256=mameUTiuCajtihDw_2-W218xyJlvTusOWrEKP1yAWgk,2003
18
18
  trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
19
- trilogy/core/models.py,sha256=TUZtT3oqVMFTuIl2SvfzDO_MgVNzgxK6tP3CaOLBe6s,158789
19
+ trilogy/core/models.py,sha256=W_0ZfIIEuyHfYsSXGMJOJPNJf7vSljSrRm42nLyiL8w,159702
20
20
  trilogy/core/optimization.py,sha256=od_60A9F8J8Nj24MHgrxl4vwRwmBFH13TMdoMQvgVKs,7717
21
21
  trilogy/core/query_processor.py,sha256=mbcZlgjChrRjDHkdmMbKe-T70UpbBkJhS09MyU5a6UY,17785
22
22
  trilogy/core/optimizations/__init__.py,sha256=bWQecbeiwiDx9LJnLsa7dkWxdbl2wcnkcTN69JyP8iI,356
@@ -27,7 +27,7 @@ trilogy/core/optimizations/predicate_pushdown.py,sha256=1l9WnFOSv79e341typG3tTdk
27
27
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  trilogy/core/processing/concept_strategies_v3.py,sha256=7MT_x6QFHrbSDmjz21pYdQB5ux419ES4QS-8lO16eyw,36091
29
29
  trilogy/core/processing/graph_utils.py,sha256=aq-kqk4Iado2HywDxWEejWc-7PGO6Oa-ZQLAM6XWPHw,1199
30
- trilogy/core/processing/utility.py,sha256=KzmlNnJ4or7_0weqtzfFw8WsUqGXtzEO-FeuV7Oz_HQ,17461
30
+ trilogy/core/processing/utility.py,sha256=xlaKqnoWg-mEwTF-erBF9QXnXZtESrTuYrK2RQb7Wi4,17411
31
31
  trilogy/core/processing/node_generators/__init__.py,sha256=-mzYkRsaRNa_dfTckYkKVFSR8h8a3ihEiPJDU_tAmDo,672
32
32
  trilogy/core/processing/node_generators/basic_node.py,sha256=WQNgJ1MwrMS_BQ-b3XwGGB6eToDykelAVj_fesJuqe0,2069
33
33
  trilogy/core/processing/node_generators/common.py,sha256=eslHTTPFTkmwHwKIuUsbFn54jxj-Avtt-QScqtNwzdg,8945
@@ -75,9 +75,9 @@ trilogy/parsing/render.py,sha256=B9J2GrYQcE76kddMQSeAmvAPX-9pv39mpeSHZ10SNj8,146
75
75
  trilogy/parsing/trilogy.lark,sha256=_z5px2N-e8oLUf7SpPMXXNqbAykDkZOvP4_lPgf5-Uk,12245
76
76
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
78
- pytrilogy-0.0.2.27.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
79
- pytrilogy-0.0.2.27.dist-info/METADATA,sha256=FwOit8tU80ZUP3yP3IpzIyUV30J9z3JlJi5dfawRtTQ,8403
80
- pytrilogy-0.0.2.27.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
81
- pytrilogy-0.0.2.27.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
82
- pytrilogy-0.0.2.27.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
83
- pytrilogy-0.0.2.27.dist-info/RECORD,,
78
+ pytrilogy-0.0.2.28.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
79
+ pytrilogy-0.0.2.28.dist-info/METADATA,sha256=Ftsu-RyQ2c7b05KV4JZm7J9f1DEMup2xjMfLUA-PfWQ,8403
80
+ pytrilogy-0.0.2.28.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
81
+ pytrilogy-0.0.2.28.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
82
+ pytrilogy-0.0.2.28.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
83
+ pytrilogy-0.0.2.28.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.executor import Executor
4
4
  from trilogy.parser import parse
5
5
  from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.2.27"
7
+ __version__ = "0.0.2.28"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
trilogy/core/models.py CHANGED
@@ -606,6 +606,8 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
606
606
  return self.grain.components_copy if self.grain else []
607
607
 
608
608
  def with_namespace(self, namespace: str) -> "Concept":
609
+ if namespace == self.namespace:
610
+ return self
609
611
  return self.__class__(
610
612
  name=self.name,
611
613
  datatype=self.datatype,
@@ -3253,7 +3255,6 @@ class EnvironmentConceptDict(dict):
3253
3255
  )
3254
3256
  self.undefined[key] = undefined
3255
3257
  return undefined
3256
-
3257
3258
  matches = self._find_similar_concepts(key)
3258
3259
  message = f"Undefined concept: {key}."
3259
3260
  if matches:
@@ -3263,8 +3264,15 @@ class EnvironmentConceptDict(dict):
3263
3264
  raise UndefinedConceptException(f"line: {line_no}: " + message, matches)
3264
3265
  raise UndefinedConceptException(message, matches)
3265
3266
 
3266
- def _find_similar_concepts(self, concept_name):
3267
- matches = difflib.get_close_matches(concept_name, self.keys())
3267
+ def _find_similar_concepts(self, concept_name: str):
3268
+ def strip_local(input: str):
3269
+ if input.startswith(f"{DEFAULT_NAMESPACE}."):
3270
+ return input[len(DEFAULT_NAMESPACE) + 1 :]
3271
+ return input
3272
+
3273
+ matches = difflib.get_close_matches(
3274
+ strip_local(concept_name), [strip_local(x) for x in self.keys()]
3275
+ )
3268
3276
  return matches
3269
3277
 
3270
3278
  def items(self) -> ItemsView[str, Concept]: # type: ignore
@@ -3325,7 +3333,6 @@ class Environment(BaseModel):
3325
3333
 
3326
3334
  materialized_concepts: List[Concept] = Field(default_factory=list)
3327
3335
  alias_origin_lookup: Dict[str, Concept] = Field(default_factory=dict)
3328
- canonical_map: Dict[str, str] = Field(default_factory=dict)
3329
3336
  _parse_count: int = 0
3330
3337
 
3331
3338
  @classmethod
@@ -3444,14 +3451,38 @@ class Environment(BaseModel):
3444
3451
  exists = True
3445
3452
  imp_stm = ImportStatement(alias=alias, path=Path(source.working_path))
3446
3453
 
3454
+ same_namespace = alias == self.namespace
3455
+
3447
3456
  if not exists:
3448
3457
  self.imports[alias].append(imp_stm)
3449
3458
 
3450
- for _, concept in source.concepts.items():
3451
- self.add_concept(concept.with_namespace(alias), _ignore_cache=True)
3459
+ for k, concept in source.concepts.items():
3460
+ if same_namespace:
3461
+ new = self.add_concept(concept, _ignore_cache=True)
3462
+ else:
3463
+ new = self.add_concept(
3464
+ concept.with_namespace(alias), _ignore_cache=True
3465
+ )
3466
+
3467
+ k = address_with_namespace(k, alias)
3468
+ # set this explicitly, to handle aliasing
3469
+ self.concepts[k] = new
3452
3470
 
3453
3471
  for _, datasource in source.datasources.items():
3454
- self.add_datasource(datasource.with_namespace(alias), _ignore_cache=True)
3472
+ if same_namespace:
3473
+ self.add_datasource(datasource, _ignore_cache=True)
3474
+ else:
3475
+ self.add_datasource(
3476
+ datasource.with_namespace(alias), _ignore_cache=True
3477
+ )
3478
+ for key, val in source.alias_origin_lookup.items():
3479
+ if same_namespace:
3480
+ self.alias_origin_lookup[key] = val
3481
+ else:
3482
+ self.alias_origin_lookup[address_with_namespace(key, alias)] = (
3483
+ val.with_namespace(alias)
3484
+ )
3485
+
3455
3486
  self.gen_concept_list_caches()
3456
3487
  return self
3457
3488
 
@@ -3542,8 +3573,6 @@ class Environment(BaseModel):
3542
3573
  existing = self.validate_concept(concept, meta=meta)
3543
3574
  if existing:
3544
3575
  concept = existing
3545
- if concept.namespace == DEFAULT_NAMESPACE:
3546
- self.concepts[concept.name] = concept
3547
3576
  self.concepts[concept.address] = concept
3548
3577
  from trilogy.core.environment_helpers import generate_related_concepts
3549
3578
 
@@ -3631,7 +3660,6 @@ class Environment(BaseModel):
3631
3660
  v.pseudonyms.add(source.address)
3632
3661
  if v.address == source.address:
3633
3662
  replacements[k] = target
3634
- self.canonical_map[k] = target.address
3635
3663
  v.pseudonyms.add(target.address)
3636
3664
  # we need to update keys and grains of all concepts
3637
3665
  else:
@@ -66,9 +66,6 @@ def resolve_join_order_v2(
66
66
  ) -> list[JoinOrderOutput]:
67
67
  datasources = [x for x in g.nodes if x.startswith("ds~")]
68
68
  concepts = [x for x in g.nodes if x.startswith("c~")]
69
- # from trilogy.hooks.graph_hook import GraphHook
70
-
71
- # GraphHook().query_graph_built(g)
72
69
 
73
70
  output: list[JoinOrderOutput] = []
74
71
  pivot_map = {
@@ -78,7 +75,7 @@ def resolve_join_order_v2(
78
75
  pivots = list(
79
76
  sorted(
80
77
  [x for x in pivot_map if len(pivot_map[x]) > 1],
81
- key=lambda x: len(pivot_map[x]),
78
+ key=lambda x: (len(pivot_map[x]), len(x), x),
82
79
  )
83
80
  )
84
81
  solo = [x for x in pivot_map if len(pivot_map[x]) == 1]
@@ -95,7 +92,7 @@ def resolve_join_order_v2(
95
92
  root = pivots.pop()
96
93
 
97
94
  # sort so less partials is last and eligible lefts are
98
- def score_key(x: str) -> int:
95
+ def score_key(x: str) -> tuple[int, int, str]:
99
96
  base = 1
100
97
  # if it's left, higher weight
101
98
  if x in eligible_left:
@@ -103,7 +100,7 @@ def resolve_join_order_v2(
103
100
  # if it has the concept as a partial, lower weight
104
101
  if root in partials.get(x, []):
105
102
  base -= 1
106
- return base
103
+ return (base, len(x), x)
107
104
 
108
105
  # get remainig un-joined datasets
109
106
  to_join = sorted(
trilogy/executor.py CHANGED
@@ -276,6 +276,20 @@ class Executor(object):
276
276
  output.append(compiled_sql)
277
277
  return output
278
278
 
279
+ def parse_file(self, file: str | Path, persist: bool = False) -> Generator[
280
+ ProcessedQuery
281
+ | ProcessedQueryPersist
282
+ | ProcessedShowStatement
283
+ | ProcessedRawSQLStatement
284
+ | ProcessedCopyStatement,
285
+ None,
286
+ None,
287
+ ]:
288
+ file = Path(file)
289
+ with open(file, "r") as f:
290
+ command = f.read()
291
+ return self.parse_text_generator(command, persist=persist)
292
+
279
293
  def parse_text(
280
294
  self, command: str, persist: bool = False
281
295
  ) -> List[
@@ -319,9 +333,11 @@ class Executor(object):
319
333
  x = self.generator.generate_queries(
320
334
  self.environment, [t], hooks=self.hooks
321
335
  )[0]
336
+
337
+ yield x
338
+
322
339
  if persist and isinstance(x, ProcessedQueryPersist):
323
340
  self.environment.add_datasource(x.datasource)
324
- yield x
325
341
 
326
342
  def execute_raw_sql(
327
343
  self, command: str, variables: dict | None = None