pytrilogy 0.0.2.19__tar.gz → 0.0.2.21__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.

Files changed (106) hide show
  1. {pytrilogy-0.0.2.19/pytrilogy.egg-info → pytrilogy-0.0.2.21}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_models.py +6 -2
  4. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/__init__.py +1 -1
  5. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/constants.py +1 -0
  6. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/enums.py +1 -0
  7. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/models.py +98 -47
  8. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/select_node.py +2 -1
  9. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/query_processor.py +3 -0
  10. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/parse_engine.py +7 -2
  11. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/LICENSE.md +0 -0
  12. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/README.md +0 -0
  13. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pyproject.toml +0 -0
  14. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pytrilogy.egg-info/SOURCES.txt +0 -0
  15. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pytrilogy.egg-info/dependency_links.txt +0 -0
  16. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pytrilogy.egg-info/entry_points.txt +0 -0
  17. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pytrilogy.egg-info/requires.txt +0 -0
  18. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/pytrilogy.egg-info/top_level.txt +0 -0
  19. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/setup.cfg +0 -0
  20. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/setup.py +0 -0
  21. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_datatypes.py +0 -0
  22. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_declarations.py +0 -0
  23. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_derived_concepts.py +0 -0
  24. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_discovery_nodes.py +0 -0
  25. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_environment.py +0 -0
  26. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_functions.py +0 -0
  27. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_imports.py +0 -0
  28. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_metadata.py +0 -0
  29. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_multi_join_assignments.py +0 -0
  30. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_parsing.py +0 -0
  31. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_partial_handling.py +0 -0
  32. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_query_processing.py +0 -0
  33. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_select.py +0 -0
  34. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_statements.py +0 -0
  35. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_undefined_concept.py +0 -0
  36. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/tests/test_where_clause.py +0 -0
  37. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/compiler.py +0 -0
  38. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/constants.py +0 -0
  39. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/__init__.py +0 -0
  40. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/env_processor.py +0 -0
  41. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/environment_helpers.py +0 -0
  42. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/ergonomics.py +0 -0
  43. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/exceptions.py +0 -0
  44. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/functions.py +0 -0
  45. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/graph_models.py +0 -0
  46. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/internal.py +0 -0
  47. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimization.py +0 -0
  48. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimizations/__init__.py +0 -0
  49. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimizations/base_optimization.py +0 -0
  50. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimizations/inline_constant.py +0 -0
  51. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimizations/inline_datasource.py +0 -0
  52. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  53. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/__init__.py +0 -0
  54. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  55. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/graph_utils.py +0 -0
  56. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/__init__.py +0 -0
  57. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  58. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/common.py +0 -0
  59. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  60. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/group_node.py +0 -0
  61. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  62. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  63. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  64. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  65. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  66. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  67. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/node_generators/window_node.py +0 -0
  68. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/__init__.py +0 -0
  69. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/base_node.py +0 -0
  70. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/filter_node.py +0 -0
  71. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/group_node.py +0 -0
  72. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/merge_node.py +0 -0
  73. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  74. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  75. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/nodes/window_node.py +0 -0
  76. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/core/processing/utility.py +0 -0
  77. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/__init__.py +0 -0
  78. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/base.py +0 -0
  79. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/bigquery.py +0 -0
  80. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/common.py +0 -0
  81. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/config.py +0 -0
  82. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/duckdb.py +0 -0
  83. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/enums.py +0 -0
  84. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/postgres.py +0 -0
  85. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/presto.py +0 -0
  86. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/snowflake.py +0 -0
  87. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/dialect/sql_server.py +0 -0
  88. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/engine.py +0 -0
  89. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/executor.py +0 -0
  90. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/hooks/__init__.py +0 -0
  91. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/hooks/base_hook.py +0 -0
  92. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/hooks/graph_hook.py +0 -0
  93. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/hooks/query_debugger.py +0 -0
  94. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/metadata/__init__.py +0 -0
  95. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parser.py +0 -0
  96. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/__init__.py +0 -0
  97. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/common.py +0 -0
  98. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/config.py +0 -0
  99. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/exceptions.py +0 -0
  100. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/helpers.py +0 -0
  101. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/render.py +0 -0
  102. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/parsing/trilogy.lark +0 -0
  103. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/py.typed +0 -0
  104. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/scripts/__init__.py +0 -0
  105. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/scripts/trilogy.py +0 -0
  106. {pytrilogy-0.0.2.19 → pytrilogy-0.0.2.21}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.19
3
+ Version: 0.0.2.21
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.19
3
+ Version: 0.0.2.21
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -100,12 +100,16 @@ def test_conditional(test_environment, test_environment_graph):
100
100
  merged = condition_a + condition_b
101
101
  assert merged == condition_a
102
102
 
103
- test_concept_two = list(test_environment.concepts.values())[-2]
103
+ test_concept_two = [
104
+ x
105
+ for x in test_environment.concepts.values()
106
+ if x.address != test_concept.address
107
+ ].pop()
104
108
  condition_c = Conditional(
105
109
  left=test_concept, right=test_concept_two, operator=BooleanOperator.AND
106
110
  )
107
111
  merged_two = condition_a + condition_c
108
- assert merged_two.left == condition_a
112
+ assert merged_two.left == condition_a, f"{str(merged_two.left), str(condition_a)}"
109
113
  assert merged_two.right == condition_c
110
114
  assert merged_two.operator == BooleanOperator.AND
111
115
 
@@ -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.19"
7
+ __version__ = "0.0.2.21"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1,3 +1,4 @@
1
1
  CONSTANT_DATASET: str = "preql_internal_constant_dataset"
2
2
  ALL_ROWS_CONCEPT = "all_rows"
3
3
  INTERNAL_NAMESPACE = "__preql_internal"
4
+ PERSISTED_CONCEPT_PREFIX = "__pre_persist"
@@ -12,6 +12,7 @@ class UnnestMode(Enum):
12
12
 
13
13
  class ConceptSource(Enum):
14
14
  MANUAL = "manual"
15
+ PERSIST_STATEMENT = "persist_statement"
15
16
  AUTO_DERIVED = "auto_derived"
16
17
 
17
18
 
@@ -44,6 +44,7 @@ from trilogy.core.constants import (
44
44
  ALL_ROWS_CONCEPT,
45
45
  INTERNAL_NAMESPACE,
46
46
  CONSTANT_DATASET,
47
+ PERSISTED_CONCEPT_PREFIX,
47
48
  )
48
49
  from trilogy.core.enums import (
49
50
  InfiniteFunctionArgs,
@@ -559,19 +560,16 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
559
560
  grain = ",".join([str(c.address) for c in self.grain.components])
560
561
  return f"{self.namespace}.{self.name}<{grain}>"
561
562
 
562
- @property
563
+ @cached_property
563
564
  def address(self) -> str:
564
- if not self._address_cache:
565
- self._address_cache = f"{self.namespace}.{self.name}"
566
- return self._address_cache
567
-
568
- @address.setter
569
- def address(self, address: str) -> None:
570
- self._address_cache = address
565
+ return f"{self.namespace}.{self.name}"
571
566
 
572
567
  def set_name(self, name: str):
573
568
  self.name = name
574
- self.address = f"{self.namespace}.{self.name}"
569
+ try:
570
+ del self.address
571
+ except AttributeError:
572
+ pass
575
573
 
576
574
  @property
577
575
  def output(self) -> "Concept":
@@ -915,10 +913,10 @@ class Grain(Mergeable, BaseModel):
915
913
  )
916
914
 
917
915
  @cached_property
918
- def set(self):
916
+ def set(self) -> set[str]:
919
917
  base = []
920
918
  for x in self.components_copy:
921
- if x.derivation == PurposeLineage.ROWSET:
919
+ if isinstance(x.lineage, RowsetItem):
922
920
  base.append(x.lineage.content.address)
923
921
  else:
924
922
  base.append(x.address)
@@ -3134,6 +3132,12 @@ class EnvironmentConceptDict(dict):
3134
3132
  def values(self) -> ValuesView[Concept]: # type: ignore
3135
3133
  return super().values()
3136
3134
 
3135
+ def get(self, key: str, default: Concept | None = None) -> Concept | None: # type: ignore
3136
+ try:
3137
+ return self.__getitem__(key)
3138
+ except UndefinedConceptException:
3139
+ return default
3140
+
3137
3141
  def __getitem__(
3138
3142
  self, key, line_no: int | None = None
3139
3143
  ) -> Concept | UndefinedConcept:
@@ -3260,34 +3264,62 @@ class Environment(BaseModel):
3260
3264
  for datasource in self.datasources.values():
3261
3265
  for concept in datasource.output_concepts:
3262
3266
  concrete_addresses.add(concept.address)
3263
- current_mat = [x.address for x in self.materialized_concepts]
3264
- self.materialized_concepts = [
3265
- c for c in self.concepts.values() if c.address in concrete_addresses
3266
- ]
3267
- # include aliased concepts
3268
- self.materialized_concepts += [
3269
- c
3270
- for c in self.alias_origin_lookup.values()
3271
- if c.address in concrete_addresses
3272
- ]
3273
- new = [
3274
- x.address
3275
- for x in self.materialized_concepts
3276
- if x.address not in current_mat
3277
- ]
3278
- if new:
3279
- logger.debug(f"Environment added new materialized concepts {new}")
3267
+ self.materialized_concepts = unique(
3268
+ [c for c in self.concepts.values() if c.address in concrete_addresses]
3269
+ + [
3270
+ c
3271
+ for c in self.alias_origin_lookup.values()
3272
+ if c.address in concrete_addresses
3273
+ ],
3274
+ "address",
3275
+ )
3280
3276
 
3281
- def validate_concept(self, lookup: str, meta: Meta | None = None):
3277
+ def validate_concept(self, new_concept: Concept, meta: Meta | None = None):
3278
+ lookup = new_concept.address
3282
3279
  existing: Concept = self.concepts.get(lookup) # type: ignore
3283
3280
  if not existing:
3284
3281
  return
3285
- elif existing and self.environment_config.allow_duplicate_declaration:
3282
+
3283
+ def handle_persist():
3284
+ deriv_lookup = (
3285
+ f"{existing.namespace}.{PERSISTED_CONCEPT_PREFIX}_{existing.name}"
3286
+ )
3287
+
3288
+ alt_source = self.alias_origin_lookup.get(deriv_lookup)
3289
+ if not alt_source:
3290
+ return None
3291
+ # if the new concept binding has no lineage
3292
+ # nothing to cause us to think a persist binding
3293
+ # needs to be invalidated
3294
+ if not new_concept.lineage:
3295
+ return existing
3296
+ if str(alt_source.lineage) == str(new_concept.lineage):
3297
+ logger.info(
3298
+ f"Persisted concept {existing.address} matched redeclaration, keeping current persistence binding."
3299
+ )
3300
+ return existing
3301
+ logger.warning(
3302
+ f"Persisted concept {existing.address} lineage {str(alt_source.lineage)} did not match redeclaration {str(new_concept.lineage)}, overwriting and invalidating persist binding."
3303
+ )
3304
+ for k, datasource in self.datasources.items():
3305
+ if existing.address in datasource.output_concepts:
3306
+ datasource.columns = [
3307
+ x
3308
+ for x in datasource.columns
3309
+ if x.concept.address != existing.address
3310
+ ]
3311
+ return None
3312
+
3313
+ if existing and self.environment_config.allow_duplicate_declaration:
3314
+ if existing.metadata.concept_source == ConceptSource.PERSIST_STATEMENT:
3315
+ return handle_persist()
3286
3316
  return
3287
3317
  elif existing.metadata:
3318
+ if existing.metadata.concept_source == ConceptSource.PERSIST_STATEMENT:
3319
+ return handle_persist()
3288
3320
  # if the existing concept is auto derived, we can overwrite it
3289
3321
  if existing.metadata.concept_source == ConceptSource.AUTO_DERIVED:
3290
- return
3322
+ return None
3291
3323
  elif meta and existing.metadata:
3292
3324
  raise ValueError(
3293
3325
  f"Assignment to concept '{lookup}' on line {meta.line} is a duplicate"
@@ -3400,39 +3432,58 @@ class Environment(BaseModel):
3400
3432
  meta: Meta | None = None,
3401
3433
  force: bool = False,
3402
3434
  add_derived: bool = True,
3435
+ _ignore_cache: bool = False,
3403
3436
  ):
3404
3437
  if not force:
3405
- self.validate_concept(concept.address, meta=meta)
3438
+ existing = self.validate_concept(concept, meta=meta)
3439
+ if existing:
3440
+ concept = existing
3406
3441
  if concept.namespace == DEFAULT_NAMESPACE:
3407
3442
  self.concepts[concept.name] = concept
3408
- else:
3409
- self.concepts[concept.address] = concept
3443
+ self.concepts[concept.address] = concept
3410
3444
  from trilogy.core.environment_helpers import generate_related_concepts
3411
3445
 
3412
3446
  generate_related_concepts(concept, self, meta=meta, add_derived=add_derived)
3413
- self.gen_concept_list_caches()
3447
+ if not _ignore_cache:
3448
+ self.gen_concept_list_caches()
3414
3449
  return concept
3415
3450
 
3416
3451
  def add_datasource(
3417
3452
  self,
3418
3453
  datasource: Datasource,
3419
3454
  meta: Meta | None = None,
3455
+ _ignore_cache: bool = False,
3420
3456
  ):
3421
-
3422
3457
  self.datasources[datasource.env_label] = datasource
3423
- for column in datasource.columns:
3424
- current_concept = column.concept
3458
+ for current_concept in datasource.output_concepts:
3425
3459
  current_derivation = current_concept.derivation
3460
+ # TODO: refine this section;
3461
+ # too hacky for maintainability
3426
3462
  if current_derivation not in (PurposeLineage.ROOT, PurposeLineage.CONSTANT):
3427
- new_concept = current_concept.model_copy(deep=True)
3428
- new_concept.set_name("_pre_persist_" + current_concept.name)
3429
- # remove the associated lineage
3430
- current_concept.lineage = None
3431
- self.add_concept(new_concept, meta=meta, force=True)
3432
- self.add_concept(current_concept, meta=meta, force=True)
3433
- self.merge_concept(new_concept, current_concept, [])
3434
-
3435
- self.gen_concept_list_caches()
3463
+ persisted = f"{PERSISTED_CONCEPT_PREFIX}_" + current_concept.name
3464
+ # override the current concept source to reflect that it's now coming from a datasource
3465
+ if (
3466
+ current_concept.metadata.concept_source
3467
+ != ConceptSource.PERSIST_STATEMENT
3468
+ ):
3469
+ new_concept = current_concept.model_copy(deep=True)
3470
+ new_concept.set_name(persisted)
3471
+ self.add_concept(
3472
+ new_concept, meta=meta, force=True, _ignore_cache=True
3473
+ )
3474
+ current_concept.metadata.concept_source = (
3475
+ ConceptSource.PERSIST_STATEMENT
3476
+ )
3477
+ # remove the associated lineage
3478
+ # to make this a root for discovery purposes
3479
+ # as it now "exists" in a table
3480
+ current_concept.lineage = None
3481
+ self.add_concept(
3482
+ current_concept, meta=meta, force=True, _ignore_cache=True
3483
+ )
3484
+ self.merge_concept(new_concept, current_concept, [])
3485
+ if not _ignore_cache:
3486
+ self.gen_concept_list_caches()
3436
3487
  return datasource
3437
3488
 
3438
3489
  def delete_datasource(
@@ -39,9 +39,10 @@ def gen_select_node(
39
39
  ]
40
40
  )
41
41
  if materialized_lcl != all_lcl:
42
+ missing = all_lcl.difference(materialized_lcl)
42
43
  logger.info(
43
44
  f"{padding(depth)}{LOGGER_PREFIX} Skipping select node generation for {concept.address}"
44
- f" as it + optional includes non-materialized concepts (looking for all {all_lcl}) "
45
+ f" as it + optional includes non-materialized concepts (looking for all {all_lcl}, missing {missing}) "
45
46
  )
46
47
  if fail_if_not_found:
47
48
  raise NoDatasourceException(f"No datasource exists for {concept}")
@@ -11,6 +11,7 @@ from trilogy.core.models import (
11
11
  Concept,
12
12
  Environment,
13
13
  PersistStatement,
14
+ ConceptDeclarationStatement,
14
15
  SelectStatement,
15
16
  MultiSelectStatement,
16
17
  CTE,
@@ -393,6 +394,8 @@ def process_auto(
393
394
  return process_persist(environment, statement, hooks)
394
395
  elif isinstance(statement, SelectStatement):
395
396
  return process_query(environment, statement, hooks)
397
+ elif isinstance(statement, ConceptDeclarationStatement):
398
+ return None
396
399
  raise ValueError(f"Do not know how to process {type(statement)}")
397
400
 
398
401
 
@@ -814,14 +814,19 @@ class ParseToObjects(Transformer):
814
814
  raise ImportError(f"Unable to import file {target}, parsing error: {e}")
815
815
 
816
816
  for _, concept in nparser.environment.concepts.items():
817
- self.environment.add_concept(concept.with_namespace(alias))
817
+ self.environment.add_concept(
818
+ concept.with_namespace(alias), _ignore_cache=True
819
+ )
818
820
 
819
821
  for _, datasource in nparser.environment.datasources.items():
820
- self.environment.add_datasource(datasource.with_namespace(alias))
822
+ self.environment.add_datasource(
823
+ datasource.with_namespace(alias), _ignore_cache=True
824
+ )
821
825
  imps = ImportStatement(
822
826
  alias=alias, path=Path(args[0]), environment=nparser.environment
823
827
  )
824
828
  self.environment.imports[alias] = imps
829
+ self.environment.gen_concept_list_caches()
825
830
  return imps
826
831
 
827
832
  @v_args(meta=True)
File without changes
File without changes
File without changes
File without changes