pytrilogy 0.0.3.5__py3-none-any.whl → 0.0.3.6__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.5
3
+ Version: 0.0.3.6
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,4 +1,4 @@
1
- trilogy/__init__.py,sha256=7rK8M4Aw3BDqS9a5ou3PjyRkVMzEhaVMf6a7V-8ll4E,302
1
+ trilogy/__init__.py,sha256=R9yJSDmZQvpxjWMcp4mGFnbg7xuxUCiIVrvP8eacKj4,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=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
@@ -25,7 +25,7 @@ trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,
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=c0tGxyH2WwTmAD047tr69U0a6GNVf-ug26H68yii7DA,9257
28
- trilogy/core/models/environment.py,sha256=GU8D3cn6lCFAseVPYfW_a-cnBbD1sYEeDVOkbZSWCxk,25943
28
+ trilogy/core/models/environment.py,sha256=h06y1Dv7naw2GuFFAAyoFZmicG7a7Lu-dRoYPVfrOGo,25967
29
29
  trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
30
30
  trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
31
31
  trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
@@ -88,14 +88,14 @@ trilogy/parsing/common.py,sha256=yAE3x4SyO4PfAb7HhZ_l9sNPYaf_pcM1K8ioEy76SCU,203
88
88
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
89
89
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
90
90
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
91
- trilogy/parsing/parse_engine.py,sha256=uazcnUhY3pERY8Xa116IHl1JhCPSlvQG6i4vP7I4Qpk,54638
91
+ trilogy/parsing/parse_engine.py,sha256=32_yO_SreTjHxCkMziW2re15ilEZn01OUizVAvN9xHo,54656
92
92
  trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,17837
93
93
  trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
94
94
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
95
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
96
- pytrilogy-0.0.3.5.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
97
- pytrilogy-0.0.3.5.dist-info/METADATA,sha256=W8SsLe2qtgpl4SDr_xlL3lOwReWbKYBClHXkjLQSIqk,8983
98
- pytrilogy-0.0.3.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
99
- pytrilogy-0.0.3.5.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
100
- pytrilogy-0.0.3.5.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
101
- pytrilogy-0.0.3.5.dist-info/RECORD,,
96
+ pytrilogy-0.0.3.6.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
97
+ pytrilogy-0.0.3.6.dist-info/METADATA,sha256=PBxZLl7AH82ztlgGbJbdLWwj8r3Wo2_JRsXxgh5y1Gc,8983
98
+ pytrilogy-0.0.3.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
99
+ pytrilogy-0.0.3.6.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
100
+ pytrilogy-0.0.3.6.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
101
+ pytrilogy-0.0.3.6.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.5"
7
+ __version__ = "0.0.3.6"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -426,7 +426,6 @@ class Environment(BaseModel):
426
426
  from trilogy.parsing.parse_engine import (
427
427
  PARSER,
428
428
  ParseToObjects,
429
- gen_cache_lookup,
430
429
  )
431
430
 
432
431
  if isinstance(path, str):
@@ -440,7 +439,8 @@ class Environment(BaseModel):
440
439
  else:
441
440
  target = path
442
441
  if not env:
443
- parse_address = gen_cache_lookup(str(target), alias, str(self.working_path))
442
+ import_keys = ["root", alias]
443
+ parse_address = "-".join(import_keys)
444
444
  try:
445
445
  with open(target, "r", encoding="utf-8") as f:
446
446
  text = f.read()
@@ -454,11 +454,12 @@ class Environment(BaseModel):
454
454
  ),
455
455
  parse_address=parse_address,
456
456
  token_address=target,
457
+ import_keys=import_keys,
457
458
  )
458
459
  nparser.set_text(text)
459
460
  nparser.environment.concepts.fail_on_missing = False
460
461
  nparser.transform(PARSER.parse(text))
461
- nparser.hydrate_missing()
462
+ nparser.run_second_parse_pass()
462
463
  nparser.environment.concepts.fail_on_missing = True
463
464
 
464
465
  except Exception as e:
@@ -129,6 +129,8 @@ CONSTANT_TYPES = (int, float, str, bool, list, ListWrapper, MapWrapper)
129
129
 
130
130
  SELF_LABEL = "root"
131
131
 
132
+ MAX_PARSE_DEPTH = 10
133
+
132
134
 
133
135
  @dataclass
134
136
  class WholeGrainWrapper:
@@ -146,13 +148,6 @@ with open(join(dirname(__file__), "trilogy.lark"), "r") as f:
146
148
  )
147
149
 
148
150
 
149
- def gen_cache_lookup(path: str, alias: str, parent: str) -> str:
150
- # path is the path of the file
151
- # alias is what it's being imported under
152
- # parent is the...direct parnet?
153
- return path + alias + parent
154
-
155
-
156
151
  def parse_concept_reference(
157
152
  name: str, environment: Environment, purpose: Optional[Purpose] = None
158
153
  ) -> Tuple[str, str, str, str | None]:
@@ -226,6 +221,8 @@ class ParseToObjects(Transformer):
226
221
  parsed: dict[str, "ParseToObjects"] | None = None,
227
222
  tokens: dict[Path | str, ParseTree] | None = None,
228
223
  text_lookup: dict[Path | str, str] | None = None,
224
+ environment_lookup: dict[str, Environment] | None = None,
225
+ import_keys: list[str] | None = None,
229
226
  ):
230
227
  Transformer.__init__(self, True)
231
228
  self.environment: Environment = environment
@@ -233,6 +230,7 @@ class ParseToObjects(Transformer):
233
230
  self.token_address: Path | str = token_address or SELF_LABEL
234
231
  self.parsed: dict[str, ParseToObjects] = parsed if parsed is not None else {}
235
232
  self.tokens: dict[Path | str, ParseTree] = tokens if tokens is not None else {}
233
+ self.environments: dict[str, Environment] = environment_lookup or {}
236
234
  self.text_lookup: dict[Path | str, str] = (
237
235
  text_lookup if text_lookup is not None else {}
238
236
  )
@@ -240,6 +238,7 @@ class ParseToObjects(Transformer):
240
238
  # after initial parsing
241
239
  self.parse_pass = ParsePass.INITIAL
242
240
  self.function_factory = FunctionFactory(self.environment)
241
+ self.import_keys: list[str] = import_keys or ["root"]
243
242
 
244
243
  def set_text(self, text: str):
245
244
  self.text_lookup[self.token_address] = text
@@ -255,14 +254,14 @@ class ParseToObjects(Transformer):
255
254
  for _, v in self.parsed.items():
256
255
  v.prepare_parse()
257
256
 
258
- def hydrate_missing(self, force: bool = False):
257
+ def run_second_parse_pass(self, force: bool = False):
259
258
  if self.token_address not in self.tokens:
260
259
  return []
261
260
  self.parse_pass = ParsePass.VALIDATION
262
261
  for _, v in list(self.parsed.items()):
263
- if v.parse_pass == ParsePass.VALIDATION and not force:
262
+ if v.parse_pass == ParsePass.VALIDATION:
264
263
  continue
265
- v.hydrate_missing()
264
+ v.run_second_parse_pass()
266
265
  reparsed = self.transform(self.tokens[self.token_address])
267
266
  self.environment.concepts.undefined = {}
268
267
  return reparsed
@@ -306,11 +305,6 @@ class ParseToObjects(Transformer):
306
305
  def QUOTED_IDENTIFIER(self, args) -> str:
307
306
  return args.value[1:-1]
308
307
 
309
- # @v_args(meta=True)
310
- # def concept_lit(self, meta: Meta, args) -> ConceptRef:
311
- # address = args[0]
312
- # return self.environment.concepts.__getitem__(address, meta.line)
313
- # return ConceptRef(address=address, line_no=meta.line)
314
308
  @v_args(meta=True)
315
309
  def concept_lit(self, meta: Meta, args) -> ConceptRef:
316
310
  address = args[0]
@@ -402,7 +396,6 @@ class ParseToObjects(Transformer):
402
396
  if len(concept_list) > 1:
403
397
  modifiers += concept_list[:-1]
404
398
  concept = concept_list[-1]
405
- assert not self.environment.concepts.fail_on_missing
406
399
  resolved = self.environment.concepts.__getitem__( # type: ignore
407
400
  key=concept, line_no=meta.line, file=self.token_address
408
401
  )
@@ -858,8 +851,10 @@ class ParseToObjects(Transformer):
858
851
  def import_statement(self, args: list[str]) -> ImportStatement:
859
852
  if len(args) == 2:
860
853
  alias = args[-1]
854
+ cache_key = args[-1]
861
855
  else:
862
856
  alias = self.environment.namespace
857
+ cache_key = args[0]
863
858
  path = args[0].split(".")
864
859
 
865
860
  target = join(self.environment.working_path, *path) + ".preql"
@@ -867,10 +862,14 @@ class ParseToObjects(Transformer):
867
862
  # tokens + text are cached by path
868
863
  token_lookup = Path(target)
869
864
 
870
- # cache lookups by the target, the alias, and the file we're importing it from
871
- cache_lookup = gen_cache_lookup(
872
- path=target, alias=alias, parent=str(self.token_address)
873
- )
865
+ # parser + env has to be cached by prior import path + current key
866
+ key_path = self.import_keys + [cache_key]
867
+ cache_lookup = "-".join(key_path)
868
+
869
+ # we don't iterate past the max parse depth
870
+ if len(key_path) > MAX_PARSE_DEPTH:
871
+ return ImportStatement(alias=alias, path=Path(target))
872
+
874
873
  if token_lookup in self.tokens:
875
874
  raw_tokens = self.tokens[token_lookup]
876
875
  text = self.text_lookup[token_lookup]
@@ -886,7 +885,7 @@ class ParseToObjects(Transformer):
886
885
  new_env = nparser.environment
887
886
  if nparser.parse_pass != ParsePass.VALIDATION:
888
887
  # nparser.transform(raw_tokens)
889
- nparser.hydrate_missing()
888
+ nparser.run_second_parse_pass()
890
889
  else:
891
890
  try:
892
891
  new_env = Environment(
@@ -902,6 +901,7 @@ class ParseToObjects(Transformer):
902
901
  parsed=self.parsed,
903
902
  tokens=self.tokens,
904
903
  text_lookup=self.text_lookup,
904
+ import_keys=self.import_keys + [cache_key],
905
905
  )
906
906
  nparser.transform(raw_tokens)
907
907
  self.parsed[cache_lookup] = nparser
@@ -909,6 +909,7 @@ class ParseToObjects(Transformer):
909
909
  raise ImportError(
910
910
  f"Unable to import file {target}, parsing error: {e}"
911
911
  ) from e
912
+
912
913
  parsed_path = Path(args[0])
913
914
  imps = ImportStatement(alias=alias, path=parsed_path)
914
915
 
@@ -1621,7 +1622,7 @@ def parse_text(
1621
1622
  environment = environment or (
1622
1623
  Environment(working_path=root) if root else Environment()
1623
1624
  )
1624
- parser = ParseToObjects(environment=environment)
1625
+ parser = ParseToObjects(environment=environment, import_keys=["root"])
1625
1626
 
1626
1627
  try:
1627
1628
  parser.set_text(text)
@@ -1629,7 +1630,7 @@ def parse_text(
1629
1630
  parser.prepare_parse()
1630
1631
  parser.transform(PARSER.parse(text))
1631
1632
  # this will reset fail on missing
1632
- pass_two = parser.hydrate_missing(force=True)
1633
+ pass_two = parser.run_second_parse_pass()
1633
1634
  output = [v for v in pass_two if v]
1634
1635
  environment.concepts.fail_on_missing = True
1635
1636
  except VisitError as e: