pytrilogy 0.0.3.8__py3-none-any.whl → 0.0.3.9__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.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.8
3
+ Version: 0.0.3.9
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=OtihFBeuWR7p7GBhiNMfyCpmu-x2kn_W2xSawos5ILM,302
1
+ trilogy/__init__.py,sha256=oZcEJCenpaskC-ViU-OpC-03zsr2chE8ggGCpg2Duws,302
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
4
4
  trilogy/engine.py,sha256=3etkm2RSVKO0IkgPKkrcs33X5gN_fIMyqMNfChcsR1E,1318
5
- trilogy/executor.py,sha256=YgSCeeYVecI9526LGSLVe2apOo7Ddsttvs_nDC9yElQ,17194
5
+ trilogy/executor.py,sha256=9HhdLQoou1Cy9KSDgpdYxK6uyg-UPkgx9jXJBuK5ITc,17271
6
6
  trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
7
7
  trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
@@ -20,8 +20,8 @@ trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
20
20
  trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
21
21
  trilogy/core/query_processor.py,sha256=Do8YpdPBdsbKtl9n37hobzk8SORMGqH-e_zNNxd-BE4,19456
22
22
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- trilogy/core/models/author.py,sha256=S6riJx8Z4_orbohjK3fcZh5Ei6nCPYAS2FGDsI2njHk,69488
24
- trilogy/core/models/build.py,sha256=z2QO7l2E2-1hHimmOGsLl42sTnEb2x9o45zkvOoJYpM,56687
23
+ trilogy/core/models/author.py,sha256=eJOz-p20Am5IQPDeBntgIkncB1nUZGhuwcy7WBSbCmM,70240
24
+ trilogy/core/models/build.py,sha256=bO1qYvuGl6LeNGgsfS6ZHAzZBR2lBPLg-QJymp9hgkU,57235
25
25
  trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
26
26
  trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
27
27
  trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
@@ -85,7 +85,7 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
85
85
  trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
86
86
  trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- trilogy/parsing/common.py,sha256=RwO9CdNYy3KxJCjg5Ta_xJwfZHV2PuRErxg66Cs50ws,20490
88
+ trilogy/parsing/common.py,sha256=IgZ2K3LzJ0usLIwxRCRmS-4luP6uwmM-f1oqGNyGbm0,21306
89
89
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
90
90
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
91
91
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -94,9 +94,9 @@ trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,178
94
94
  trilogy/parsing/trilogy.lark,sha256=wZpqI1louDqm-t-TpmzW749dPA9w2EIAyowyEJIeXAM,12620
95
95
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
96
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
97
- pytrilogy-0.0.3.8.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
98
- pytrilogy-0.0.3.8.dist-info/METADATA,sha256=DAu0XOCyEgXpZj9-Znl0IbtouGXHELjt2EUZnp3IgEs,8983
99
- pytrilogy-0.0.3.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
100
- pytrilogy-0.0.3.8.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
101
- pytrilogy-0.0.3.8.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
102
- pytrilogy-0.0.3.8.dist-info/RECORD,,
97
+ pytrilogy-0.0.3.9.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
98
+ pytrilogy-0.0.3.9.dist-info/METADATA,sha256=8Bv3VIgAPpBlaRGc1UlrWrMPLb0HYNpy-coiUXMzzK0,8983
99
+ pytrilogy-0.0.3.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
100
+ pytrilogy-0.0.3.9.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
101
+ pytrilogy-0.0.3.9.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
102
+ pytrilogy-0.0.3.9.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.8"
7
+ __version__ = "0.0.3.9"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -547,6 +547,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
547
547
  MagicConstants,
548
548
  WindowItem,
549
549
  AggregateWrapper,
550
+ FilterItem,
550
551
  ]
551
552
  right: Union[
552
553
  int,
@@ -566,6 +567,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
566
567
  WindowItem,
567
568
  AggregateWrapper,
568
569
  TupleWrapper,
570
+ FilterItem,
569
571
  ]
570
572
  operator: ComparisonOperator
571
573
 
@@ -1249,7 +1251,7 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
1249
1251
  def with_merge(
1250
1252
  self, source: Concept, target: Concept, modifiers: List[Modifier]
1251
1253
  ) -> "OrderItem":
1252
- return OrderItem(
1254
+ return OrderItem.model_construct(
1253
1255
  expr=(
1254
1256
  self.expr.with_merge(source, target, modifiers)
1255
1257
  if isinstance(self.expr, Mergeable)
@@ -1258,6 +1260,16 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
1258
1260
  order=self.order,
1259
1261
  )
1260
1262
 
1263
+ def with_reference_replacement(self, source, target):
1264
+ return OrderItem.model_construct(
1265
+ expr=(
1266
+ self.expr.with_reference_replacement(source, target)
1267
+ if isinstance(self.expr, Mergeable)
1268
+ else self.expr
1269
+ ),
1270
+ order=self.order,
1271
+ )
1272
+
1261
1273
  @property
1262
1274
  def concept_arguments(self) -> Sequence[ConceptRef]:
1263
1275
  return get_concept_arguments(self.expr)
@@ -1320,6 +1332,17 @@ class WindowItem(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1320
1332
  )
1321
1333
  return output
1322
1334
 
1335
+ def with_reference_replacement(self, source, target):
1336
+ return WindowItem.model_construct(
1337
+ type=self.type,
1338
+ content=self.content.with_reference_replacement(source, target),
1339
+ over=[x.with_reference_replacement(source, target) for x in self.over],
1340
+ order_by=[
1341
+ x.with_reference_replacement(source, target) for x in self.order_by
1342
+ ],
1343
+ index=self.index,
1344
+ )
1345
+
1323
1346
  def with_namespace(self, namespace: str) -> "WindowItem":
1324
1347
  return WindowItem.model_construct(
1325
1348
  type=self.type,
@@ -1515,30 +1538,7 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1515
1538
  List[Set[DataType]],
1516
1539
  ]
1517
1540
  ] = None
1518
- arguments: Sequence[
1519
- Union[
1520
- ConceptRef,
1521
- AggregateWrapper,
1522
- Function,
1523
- Parenthetical,
1524
- CaseWhen,
1525
- CaseElse,
1526
- WindowItem,
1527
- int,
1528
- float,
1529
- str,
1530
- date,
1531
- datetime,
1532
- MapWrapper[Any, Any],
1533
- DataType,
1534
- ListType,
1535
- MapType,
1536
- NumericType,
1537
- DatePart,
1538
- list,
1539
- ListWrapper[Any],
1540
- ]
1541
- ]
1541
+ arguments: Sequence[FuncArgs]
1542
1542
 
1543
1543
  def __init__(self, **kwargs):
1544
1544
  super().__init__(**kwargs)
@@ -2164,6 +2164,7 @@ Expr = (
2164
2164
  | datetime
2165
2165
  | TupleWrapper
2166
2166
  | ListWrapper
2167
+ | MapWrapper
2167
2168
  | WindowItem
2168
2169
  | FilterItem
2169
2170
  | ConceptRef
@@ -2172,4 +2173,30 @@ Expr = (
2172
2173
  | Parenthetical
2173
2174
  | Function
2174
2175
  | AggregateWrapper
2176
+ | CaseWhen
2177
+ | CaseElse
2178
+ )
2179
+
2180
+ FuncArgs = (
2181
+ ConceptRef
2182
+ | AggregateWrapper
2183
+ | Function
2184
+ | Parenthetical
2185
+ | CaseWhen
2186
+ | CaseElse
2187
+ | WindowItem
2188
+ | FilterItem
2189
+ | int
2190
+ | float
2191
+ | str
2192
+ | date
2193
+ | datetime
2194
+ | MapWrapper[Any, Any]
2195
+ | DataType
2196
+ | ListType
2197
+ | MapType
2198
+ | NumericType
2199
+ | DatePart
2200
+ | list
2201
+ | ListWrapper[Any]
2175
2202
  )
@@ -51,6 +51,7 @@ from trilogy.core.models.author import (
51
51
  ConceptRef,
52
52
  Conditional,
53
53
  FilterItem,
54
+ FuncArgs,
54
55
  Function,
55
56
  Grain,
56
57
  HavingClause,
@@ -1498,10 +1499,22 @@ class Factory:
1498
1499
 
1499
1500
  @build.register
1500
1501
  def _(self, base: Function) -> BuildFunction:
1502
+ from trilogy.parsing.common import arbitrary_to_concept
1501
1503
 
1504
+ raw_args: list[Concept | FuncArgs] = []
1505
+ for arg in base.arguments:
1506
+ # to do proper discovery, we need to inject virtual intermediate ocncepts
1507
+ if isinstance(arg, (AggregateWrapper, FilterItem, WindowItem)):
1508
+ narg = arbitrary_to_concept(
1509
+ arg,
1510
+ environment=self.environment,
1511
+ )
1512
+ raw_args.append(narg)
1513
+ else:
1514
+ raw_args.append(arg)
1502
1515
  new = BuildFunction.model_construct(
1503
1516
  operator=base.operator,
1504
- arguments=[self.build(c) for c in base.arguments],
1517
+ arguments=[self.build(c) for c in raw_args],
1505
1518
  output_datatype=base.output_datatype,
1506
1519
  output_purpose=base.output_purpose,
1507
1520
  valid_inputs=base.valid_inputs,
trilogy/executor.py CHANGED
@@ -166,8 +166,11 @@ class Executor(object):
166
166
  )
167
167
 
168
168
  @execute_query.register
169
- def _(self, query: str) -> CursorResult:
170
- return self.execute_text(query)[-1]
169
+ def _(self, query: str) -> CursorResult | None:
170
+ results = self.execute_text(query)
171
+ if results:
172
+ return results[-1]
173
+ return None
171
174
 
172
175
  @execute_query.register
173
176
  def _(self, query: SelectStatement) -> CursorResult:
trilogy/parsing/common.py CHANGED
@@ -84,7 +84,8 @@ def process_function_arg(
84
84
  environment.add_concept(concept, meta=meta)
85
85
  return concept
86
86
  elif isinstance(
87
- arg, (FilterItem, WindowItem, AggregateWrapper, ListWrapper, MapWrapper)
87
+ arg,
88
+ (ListWrapper, MapWrapper),
88
89
  ):
89
90
  id_hash = string_to_hash(str(arg))
90
91
  name = f"{VIRTUAL_CONCEPT_PREFIX}_{id_hash}"
@@ -249,6 +250,24 @@ def concepts_to_grain_concepts(
249
250
  return v2
250
251
 
251
252
 
253
+ def get_relevant_parent_concepts(arg) -> tuple[list[ConceptRef], bool]:
254
+ from trilogy.core.models.author import get_concept_arguments
255
+
256
+ is_metric = False
257
+ if isinstance(arg, Function):
258
+ all = []
259
+ for y in arg.arguments:
260
+ refs, local_flag = get_relevant_parent_concepts(y)
261
+ all += refs
262
+ is_metric = is_metric or local_flag
263
+ return all, is_metric
264
+ elif isinstance(arg, AggregateWrapper) and not arg.by:
265
+ return [], True
266
+ elif isinstance(arg, AggregateWrapper) and arg.by:
267
+ return arg.by, True
268
+ return get_concept_arguments(arg), False
269
+
270
+
252
271
  def function_to_concept(
253
272
  parent: Function,
254
273
  name: str,
@@ -256,14 +275,12 @@ def function_to_concept(
256
275
  namespace: str | None = None,
257
276
  metadata: Metadata | None = None,
258
277
  ) -> Concept:
278
+
259
279
  pkeys: List[Concept] = []
260
280
  namespace = namespace or environment.namespace
261
- concrete_args = [
262
- x
263
- for x in [environment.concepts[c.address] for c in parent.concept_arguments]
264
- if not isinstance(x, UndefinedConcept)
265
- ]
266
-
281
+ is_metric = False
282
+ ref_args, is_metric = get_relevant_parent_concepts(parent)
283
+ concrete_args = [environment.concepts[c.address] for c in ref_args]
267
284
  pkeys += [x for x in concrete_args if not x.derivation == Derivation.CONSTANT]
268
285
  grain: Grain | None = Grain()
269
286
  for x in pkeys:
@@ -274,12 +291,18 @@ def function_to_concept(
274
291
  modifiers = get_upstream_modifiers(pkeys, environment)
275
292
  key_grain: list[str] = []
276
293
  for x in pkeys:
277
- if x.keys:
294
+ # metrics will group to keys, so do no do key traversal
295
+ if is_metric:
296
+ key_grain.append(x.address)
297
+ # otherwse, for row ops, assume keys are transitive
298
+ elif x.keys:
278
299
  key_grain += [*x.keys]
279
300
  else:
280
301
  key_grain.append(x.address)
281
302
  keys = set(key_grain)
282
- if not pkeys:
303
+ if is_metric:
304
+ purpose = Purpose.METRIC
305
+ elif not pkeys:
283
306
  purpose = Purpose.CONSTANT
284
307
  else:
285
308
  purpose = parent.output_purpose