sqlmesh 0.186.2.dev10__py3-none-any.whl → 0.186.2.dev12__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.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.186.2.dev10'
21
- __version_tuple__ = version_tuple = (0, 186, 2, 'dev10')
20
+ __version__ = version = '0.186.2.dev12'
21
+ __version_tuple__ = version_tuple = (0, 186, 2, 'dev12')
sqlmesh/core/loader.py CHANGED
@@ -4,7 +4,6 @@ import abc
4
4
  import glob
5
5
  import itertools
6
6
  import linecache
7
- import logging
8
7
  import os
9
8
  import re
10
9
  import typing as t
@@ -12,6 +11,7 @@ from collections import Counter, defaultdict
12
11
  from dataclasses import dataclass
13
12
  from pathlib import Path
14
13
  from pydantic import ValidationError
14
+ import concurrent.futures
15
15
 
16
16
  from sqlglot.errors import SqlglotError
17
17
  from sqlglot import exp
@@ -19,6 +19,7 @@ from sqlglot.helper import subclasses
19
19
 
20
20
  from sqlmesh.core import constants as c
21
21
  from sqlmesh.core.audit import Audit, ModelAudit, StandaloneAudit, load_multiple_audits
22
+ from sqlmesh.core.console import Console
22
23
  from sqlmesh.core.dialect import parse
23
24
  from sqlmesh.core.environment import EnvironmentStatements
24
25
  from sqlmesh.core.linter.rule import Rule
@@ -28,7 +29,6 @@ from sqlmesh.core.metric import Metric, MetricMeta, expand_metrics, load_metric_
28
29
  from sqlmesh.core.model import (
29
30
  Model,
30
31
  ModelCache,
31
- SeedModel,
32
32
  create_external_model,
33
33
  load_sql_based_models,
34
34
  )
@@ -41,6 +41,7 @@ from sqlmesh.utils.errors import ConfigError
41
41
  from sqlmesh.utils.jinja import JinjaMacroRegistry, MacroExtractor
42
42
  from sqlmesh.utils.metaprogramming import import_python_file
43
43
  from sqlmesh.utils.pydantic import validation_error_message
44
+ from sqlmesh.utils.process import create_process_pool_executor
44
45
  from sqlmesh.utils.yaml import YAML, load as yaml_load
45
46
 
46
47
 
@@ -48,8 +49,6 @@ if t.TYPE_CHECKING:
48
49
  from sqlmesh.core.context import GenericContext
49
50
 
50
51
 
51
- logger = logging.getLogger(__name__)
52
-
53
52
  GATEWAY_PATTERN = re.compile(r"gateway:\s*([^\s]+)")
54
53
 
55
54
 
@@ -73,17 +72,113 @@ class CacheBase(abc.ABC):
73
72
  self, target_path: Path, loader: t.Callable[[], t.List[Model]]
74
73
  ) -> t.List[Model]:
75
74
  """Get or load all models from cache."""
75
+ pass
76
+
77
+ @abc.abstractmethod
78
+ def put(self, models: t.List[Model], path: Path) -> bool:
79
+ """Store models in the cache associated with the given path.
80
+
81
+ Args:
82
+ models: List of models to cache
83
+ path: File path to associate with the cached models
84
+
85
+ Returns:
86
+ True if the models were successfully cached,
87
+ False otherwise (empty list, not a list, unsupported model types)
88
+ """
89
+ pass
90
+
91
+ @abc.abstractmethod
92
+ def get(self, path: Path) -> t.List[Model]:
93
+ """Retrieve models from the cache for a given path.
94
+
95
+ Args:
96
+ path: File path to look up in the cache
97
+
98
+ Returns:
99
+ List of cached models associated with the path, an empty list if no cache entry exists
100
+ """
101
+ pass
102
+
103
+
104
+ _defaults: t.Optional[t.Dict[str, t.Any]] = None
105
+ _cache: t.Optional[CacheBase] = None
106
+ _config_essentials: t.Optional[t.Dict[str, t.Any]] = None
107
+ _selected_gateway: t.Optional[str] = None
108
+
109
+
110
+ def _init_model_defaults(
111
+ config_essentials: t.Dict[str, t.Any],
112
+ selected_gateway: t.Optional[str],
113
+ model_loading_defaults: t.Optional[t.Dict[str, t.Any]] = None,
114
+ cache: t.Optional[CacheBase] = None,
115
+ console: t.Optional[Console] = None,
116
+ ) -> None:
117
+ global _defaults, _cache, _config_essentials, _selected_gateway
118
+ _defaults = model_loading_defaults
119
+ _cache = cache
120
+ _config_essentials = config_essentials
121
+ _selected_gateway = selected_gateway
122
+
123
+ # Set the console passed from the parent process
124
+ if console is not None:
125
+ from sqlmesh.core.console import set_console
126
+
127
+ set_console(console)
128
+
129
+
130
+ def load_sql_models(path: Path) -> t.List[Model]:
131
+ assert _defaults
132
+ assert _cache
133
+
134
+ with open(path, "r", encoding="utf-8") as file:
135
+ expressions = parse(file.read(), default_dialect=_defaults["dialect"])
136
+ models = load_sql_based_models(expressions, path=Path(path).absolute(), **_defaults)
137
+
138
+ return [] if _cache.put(models, path) else models
139
+
140
+
141
+ def get_variables(gateway_name: t.Optional[str] = None) -> t.Dict[str, t.Any]:
142
+ assert _config_essentials
143
+
144
+ gateway_name = gateway_name or _selected_gateway
145
+
146
+ try:
147
+ gateway = _config_essentials["gateways"].get(gateway_name)
148
+ except ConfigError:
149
+ from sqlmesh.core.console import get_console
150
+
151
+ get_console().log_warning(
152
+ f"Gateway '{gateway_name}' not found in project '{_config_essentials['project']}'."
153
+ )
154
+ gateway = None
155
+
156
+ return {
157
+ **_config_essentials["variables"],
158
+ **(gateway.variables if gateway else {}),
159
+ c.GATEWAY: gateway_name,
160
+ }
76
161
 
77
162
 
78
163
  class Loader(abc.ABC):
79
164
  """Abstract base class to load macros and models for a context"""
80
165
 
81
166
  def __init__(self, context: GenericContext, path: Path) -> None:
167
+ from sqlmesh.core.console import get_console
168
+
82
169
  self._path_mtimes: t.Dict[Path, float] = {}
83
170
  self.context = context
84
171
  self.config_path = path
85
172
  self.config = self.context.configs[self.config_path]
86
173
  self._variables_by_gateway: t.Dict[str, t.Dict[str, t.Any]] = {}
174
+ self._console = get_console()
175
+
176
+ self.config_essentials = {
177
+ "project": self.config.project,
178
+ "variables": self.config.variables,
179
+ "gateways": self.config.gateways,
180
+ }
181
+ _init_model_defaults(self.config_essentials, self.context.selected_gateway)
87
182
 
88
183
  def load(self) -> LoadedProject:
89
184
  """
@@ -243,19 +338,21 @@ class Loader(abc.ABC):
243
338
  for row in YAML().load(file.read())
244
339
  ]
245
340
  except Exception as ex:
246
- self._raise_failed_to_load_model_error(path, ex)
247
- raise
341
+ raise ConfigError(self._failed_to_load_model_error(path, ex))
248
342
 
249
343
  for path in paths_to_load:
250
344
  self._track_file(path)
251
345
 
252
346
  external_models = cache.get_or_load_models(path, lambda: _load(path))
347
+
253
348
  # external models with no explicit gateway defined form the base set
254
349
  for model in external_models:
255
350
  if model.gateway is None:
256
351
  if model.fqn in models:
257
- self._raise_failed_to_load_model_error(
258
- path, f"Duplicate external model name: '{model.name}'."
352
+ raise ConfigError(
353
+ self._failed_to_load_model_error(
354
+ path, f"Duplicate external model name: '{model.name}'."
355
+ )
259
356
  )
260
357
  models[model.fqn] = model
261
358
 
@@ -264,8 +361,10 @@ class Loader(abc.ABC):
264
361
  for model in external_models:
265
362
  if model.gateway == gateway:
266
363
  if model.fqn in models and models[model.fqn].gateway == gateway:
267
- self._raise_failed_to_load_model_error(
268
- path, f"Duplicate external model name: '{model.name}'."
364
+ raise ConfigError(
365
+ self._failed_to_load_model_error(
366
+ path, f"Duplicate external model name: '{model.name}'."
367
+ )
269
368
  )
270
369
  models.update({model.fqn: model})
271
370
 
@@ -352,33 +451,11 @@ class Loader(abc.ABC):
352
451
  """Project file to track for modifications"""
353
452
  self._path_mtimes[path] = path.stat().st_mtime
354
453
 
355
- def _get_variables(self, gateway_name: t.Optional[str] = None) -> t.Dict[str, t.Any]:
356
- gateway_name = gateway_name or self.context.selected_gateway
357
-
358
- if gateway_name not in self._variables_by_gateway:
359
- try:
360
- gateway = self.config.get_gateway(gateway_name)
361
- except ConfigError:
362
- from sqlmesh.core.console import get_console
363
-
364
- get_console().log_warning(
365
- f"Gateway '{gateway_name}' not found in project '{self.config.project}'."
366
- )
367
- gateway = None
368
-
369
- self._variables_by_gateway[gateway_name] = {
370
- **self.config.variables,
371
- **(gateway.variables if gateway else {}),
372
- c.GATEWAY: gateway_name,
373
- }
374
-
375
- return self._variables_by_gateway[gateway_name]
376
-
377
- def _raise_failed_to_load_model_error(self, path: Path, error: t.Union[str, Exception]) -> None:
454
+ def _failed_to_load_model_error(self, path: Path, error: t.Union[str, Exception]) -> str:
378
455
  base_message = f"Failed to load model definition at '{path}':"
379
456
  if isinstance(error, ValidationError):
380
- raise ConfigError(validation_error_message(error, base_message))
381
- raise ConfigError(f"{base_message}\n {error}")
457
+ return validation_error_message(error, base_message)
458
+ return f"{base_message}\n {error}"
382
459
 
383
460
 
384
461
  class SqlMeshLoader(Loader):
@@ -442,14 +519,15 @@ class SqlMeshLoader(Loader):
442
519
  audits into a Dict and creates the dag
443
520
  """
444
521
  cache = SqlMeshLoader._Cache(self, self.config_path)
445
- sql_models = self._load_sql_models(macros, jinja_macros, audits, signals, cache)
522
+
523
+ sql_models = self._load_sql_models(macros, jinja_macros, audits, signals, cache, gateway)
446
524
  external_models = self._load_external_models(audits, cache, gateway)
447
525
  python_models = self._load_python_models(macros, jinja_macros, audits, signals)
448
526
 
449
527
  all_model_names = list(sql_models) + list(external_models) + list(python_models)
450
528
  duplicates = [name for name, count in Counter(all_model_names).items() if count > 1]
451
529
  if duplicates:
452
- raise ValueError(f"Duplicate model name(s) found: {', '.join(duplicates)}.")
530
+ raise ConfigError(f"Duplicate model name(s) found: {', '.join(duplicates)}.")
453
531
 
454
532
  return UniqueKeyDict("models", **sql_models, **external_models, **python_models)
455
533
 
@@ -460,9 +538,12 @@ class SqlMeshLoader(Loader):
460
538
  audits: UniqueKeyDict[str, ModelAudit],
461
539
  signals: UniqueKeyDict[str, signal],
462
540
  cache: CacheBase,
541
+ gateway: t.Optional[str],
463
542
  ) -> UniqueKeyDict[str, Model]:
464
543
  """Loads the sql models into a Dict"""
465
544
  models: UniqueKeyDict[str, Model] = UniqueKeyDict("models")
545
+ paths: t.Set[Path] = set()
546
+ cached_paths: UniqueKeyDict[Path, t.List[Model]] = UniqueKeyDict("cached_paths")
466
547
 
467
548
  for path in self._glob_paths(
468
549
  self.config_path / c.MODELS,
@@ -473,48 +554,63 @@ class SqlMeshLoader(Loader):
473
554
  continue
474
555
 
475
556
  self._track_file(path)
557
+ paths.add(path)
558
+ if cached_models := cache.get(path):
559
+ cached_paths[path] = cached_models
476
560
 
477
- def _load() -> t.List[Model]:
478
- try:
479
- with open(path, "r", encoding="utf-8") as file:
480
- expressions = parse(
481
- file.read(), default_dialect=self.config.model_defaults.dialect
482
- )
483
-
484
- return load_sql_based_models(
485
- expressions,
486
- self._get_variables,
487
- defaults=self.config.model_defaults.dict(),
488
- macros=macros,
489
- jinja_macros=jinja_macros,
490
- audit_definitions=audits,
491
- default_audits=self.config.model_defaults.audits,
492
- path=Path(path).absolute(),
493
- module_path=self.config_path,
494
- dialect=self.config.model_defaults.dialect,
495
- time_column_format=self.config.time_column_format,
496
- physical_schema_mapping=self.config.physical_schema_mapping,
497
- project=self.config.project,
498
- default_catalog=self.context.default_catalog,
499
- infer_names=self.config.model_naming.infer_names,
500
- signal_definitions=signals,
501
- default_catalog_per_gateway=self.context.default_catalog_per_gateway,
502
- )
503
- except Exception as ex:
504
- self._raise_failed_to_load_model_error(path, ex)
505
- raise
506
-
507
- for model in cache.get_or_load_models(path, _load):
508
- if model.fqn in models:
509
- self._raise_failed_to_load_model_error(
510
- path, f"Duplicate SQL model name: '{model.name}'."
511
- )
561
+ for path, cached_models in cached_paths.items():
562
+ paths.remove(path)
563
+ for model in cached_models:
512
564
  if model.enabled:
513
565
  models[model.fqn] = model
514
566
 
515
- if isinstance(model, SeedModel):
516
- seed_path = model.seed_path
517
- self._track_file(seed_path)
567
+ if paths:
568
+ model_loading_defaults = dict(
569
+ get_variables=get_variables,
570
+ defaults=self.config.model_defaults.dict(),
571
+ macros=macros,
572
+ jinja_macros=jinja_macros,
573
+ audit_definitions=audits,
574
+ default_audits=self.config.model_defaults.audits,
575
+ module_path=self.config_path,
576
+ dialect=self.config.model_defaults.dialect,
577
+ time_column_format=self.config.time_column_format,
578
+ physical_schema_mapping=self.config.physical_schema_mapping,
579
+ project=self.config.project,
580
+ default_catalog=self.context.default_catalog,
581
+ infer_names=self.config.model_naming.infer_names,
582
+ signal_definitions=signals,
583
+ default_catalog_per_gateway=self.context.default_catalog_per_gateway,
584
+ )
585
+
586
+ with create_process_pool_executor(
587
+ initializer=_init_model_defaults,
588
+ initargs=(
589
+ self.config_essentials,
590
+ gateway,
591
+ model_loading_defaults,
592
+ cache,
593
+ self._console,
594
+ ),
595
+ max_workers=c.MAX_FORK_WORKERS,
596
+ ) as pool:
597
+ futures_to_paths = {pool.submit(load_sql_models, path): path for path in paths}
598
+ for future in concurrent.futures.as_completed(futures_to_paths):
599
+ path = futures_to_paths[future]
600
+ try:
601
+ loaded = future.result()
602
+ for model in loaded or cache.get(path):
603
+ if model.fqn in models:
604
+ raise ConfigError(
605
+ self._failed_to_load_model_error(
606
+ path, f"Duplicate SQL model name: '{model.name}'."
607
+ )
608
+ )
609
+ elif model.enabled:
610
+ model._path = path
611
+ models[model.fqn] = model
612
+ except Exception as ex:
613
+ raise ConfigError(self._failed_to_load_model_error(path, ex))
518
614
 
519
615
  return models
520
616
 
@@ -548,7 +644,7 @@ class SqlMeshLoader(Loader):
548
644
  registered |= new
549
645
  for name in new:
550
646
  for model in registry[name].models(
551
- self._get_variables,
647
+ get_variables,
552
648
  path=path,
553
649
  module_path=self.config_path,
554
650
  defaults=self.config.model_defaults.dict(),
@@ -566,7 +662,7 @@ class SqlMeshLoader(Loader):
566
662
  if model.enabled:
567
663
  models[model.fqn] = model
568
664
  except Exception as ex:
569
- self._raise_failed_to_load_model_error(path, ex)
665
+ raise ConfigError(self._failed_to_load_model_error(path, ex))
570
666
 
571
667
  finally:
572
668
  model_registry._dialect = None
@@ -616,7 +712,7 @@ class SqlMeshLoader(Loader):
616
712
  """Loads all the model audits."""
617
713
  audits_by_name: UniqueKeyDict[str, Audit] = UniqueKeyDict("audits")
618
714
  audits_max_mtime: t.Optional[float] = None
619
- variables = self._get_variables()
715
+ variables = get_variables()
620
716
 
621
717
  for path in self._glob_paths(
622
718
  self.config_path / c.AUDITS,
@@ -692,7 +788,7 @@ class SqlMeshLoader(Loader):
692
788
  module_path=self.config_path,
693
789
  jinja_macro_references=None,
694
790
  macros=macros,
695
- variables=self._get_variables(),
791
+ variables=get_variables(),
696
792
  path=self.config_path,
697
793
  )
698
794
 
@@ -727,7 +823,7 @@ class SqlMeshLoader(Loader):
727
823
  gateway_line = GATEWAY_PATTERN.search(source)
728
824
  gateway = YAML().load(gateway_line.group(0))["gateway"] if gateway_line else None
729
825
 
730
- contents = yaml_load(source, variables=self._get_variables(gateway))
826
+ contents = yaml_load(source, variables=get_variables(gateway))
731
827
 
732
828
  for test_name, value in contents.items():
733
829
  model_test_metadata[test_name] = ModelTestMetadata(
@@ -785,11 +881,30 @@ class SqlMeshLoader(Loader):
785
881
  self._model_cache_entry_id(target_path),
786
882
  loader=loader,
787
883
  )
884
+
788
885
  for model in models:
789
886
  model._path = target_path
790
887
 
791
888
  return models
792
889
 
890
+ def put(self, models: t.List[Model], path: Path) -> bool:
891
+ return self._model_cache.put(
892
+ models,
893
+ self._cache_entry_name(path),
894
+ self._model_cache_entry_id(path),
895
+ )
896
+
897
+ def get(self, path: Path) -> t.List[Model]:
898
+ models = self._model_cache.get(
899
+ self._cache_entry_name(path),
900
+ self._model_cache_entry_id(path),
901
+ )
902
+
903
+ for model in models:
904
+ model._path = path
905
+
906
+ return models
907
+
793
908
  def _cache_entry_name(self, target_path: Path) -> str:
794
909
  return "__".join(target_path.relative_to(self.config_path).parts).replace(
795
910
  target_path.suffix, ""
@@ -1,9 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- import multiprocessing as mp
5
4
  import typing as t
6
- from concurrent.futures import ProcessPoolExecutor
7
5
  from pathlib import Path
8
6
 
9
7
  from sqlglot import exp
@@ -15,7 +13,7 @@ from sqlmesh.core import constants as c
15
13
  from sqlmesh.core.model.definition import ExternalModel, Model, SqlModel, _Model
16
14
  from sqlmesh.utils.cache import FileCache
17
15
  from sqlmesh.utils.hashing import crc32
18
- from sqlmesh.utils.windows import IS_WINDOWS
16
+ from sqlmesh.utils.process import PoolExecutor, create_process_pool_executor
19
17
 
20
18
  from dataclasses import dataclass
21
19
 
@@ -46,12 +44,10 @@ class ModelCache:
46
44
  self, name: str, entry_id: str = "", *, loader: t.Callable[[], t.List[Model]]
47
45
  ) -> t.List[Model]:
48
46
  """Returns an existing cached model definition or loads and caches a new one.
49
-
50
47
  Args:
51
48
  name: The name of the entry.
52
49
  entry_id: The unique entry identifier. Used for cache invalidation.
53
50
  loader: Used to load a new model definition when no cached instance was found.
54
-
55
51
  Returns:
56
52
  The model definition.
57
53
  """
@@ -66,9 +62,22 @@ class ModelCache:
66
62
  model.full_depends_on
67
63
 
68
64
  self._file_cache.put(name, entry_id, value=models)
69
-
70
65
  return models
71
66
 
67
+ def put(self, models: t.List[Model], name: str, entry_id: str = "") -> bool:
68
+ if models and isinstance(seq_get(models, 0), (SqlModel, ExternalModel)):
69
+ # make sure we preload full_depends_on
70
+ for model in models:
71
+ model.full_depends_on
72
+
73
+ self._file_cache.put(name, entry_id, value=models)
74
+ return True
75
+
76
+ return False
77
+
78
+ def get(self, name: str, entry_id: str = "") -> t.List[Model]:
79
+ return self._file_cache.get(name, entry_id) or []
80
+
72
81
 
73
82
  @dataclass
74
83
  class OptimizedQueryCacheEntry:
@@ -149,11 +158,8 @@ class OptimizedQueryCache:
149
158
  return f"{model.name}_{crc32(hash_data)}"
150
159
 
151
160
 
152
- def optimized_query_cache_pool(optimized_query_cache: OptimizedQueryCache) -> ProcessPoolExecutor:
153
- # fork doesnt work on Windows. ref: https://docs.python.org/3/library/multiprocessing.html#multiprocessing-start-methods
154
- context_type = "spawn" if IS_WINDOWS else "fork"
155
- return ProcessPoolExecutor(
156
- mp_context=mp.get_context(context_type),
161
+ def optimized_query_cache_pool(optimized_query_cache: OptimizedQueryCache) -> PoolExecutor:
162
+ return create_process_pool_executor(
157
163
  initializer=_init_optimized_query_cache,
158
164
  initargs=(optimized_query_cache,),
159
165
  max_workers=c.MAX_FORK_WORKERS,
@@ -28,10 +28,7 @@ def update_model_schemas(
28
28
  schema = MappingSchema(normalize=False)
29
29
  optimized_query_cache: OptimizedQueryCache = OptimizedQueryCache(context_path / c.CACHE)
30
30
 
31
- if c.MAX_FORK_WORKERS == 1:
32
- _update_model_schemas_sequential(dag, models, schema, optimized_query_cache)
33
- else:
34
- _update_model_schemas_parallel(dag, models, schema, optimized_query_cache)
31
+ _update_model_schemas(dag, models, schema, optimized_query_cache)
35
32
 
36
33
 
37
34
  def _update_schema_with_model(schema: MappingSchema, model: Model) -> None:
@@ -49,25 +46,7 @@ def _update_schema_with_model(schema: MappingSchema, model: Model) -> None:
49
46
  raise
50
47
 
51
48
 
52
- def _update_model_schemas_sequential(
53
- dag: DAG[str],
54
- models: UniqueKeyDict[str, Model],
55
- schema: MappingSchema,
56
- optimized_query_cache: OptimizedQueryCache,
57
- ) -> None:
58
- for name in dag.sorted:
59
- model = models.get(name)
60
-
61
- # External models don't exist in the context, so we need to skip them
62
- if not model:
63
- continue
64
-
65
- model.update_schema(schema)
66
- optimized_query_cache.with_optimized_query(model)
67
- _update_schema_with_model(schema, model)
68
-
69
-
70
- def _update_model_schemas_parallel(
49
+ def _update_model_schemas(
71
50
  dag: DAG[str],
72
51
  models: UniqueKeyDict[str, Model],
73
52
  schema: MappingSchema,
@@ -107,12 +86,16 @@ def _update_model_schemas_parallel(
107
86
 
108
87
  while futures:
109
88
  for future in as_completed(futures):
110
- futures.remove(future)
111
- fqn, entry_name, data_hash, metadata_hash, mapping_schema = future.result()
112
- model = models[fqn]
113
- model._data_hash = data_hash
114
- model._metadata_hash = metadata_hash
115
- model.set_mapping_schema(mapping_schema)
116
- optimized_query_cache.with_optimized_query(model, entry_name)
117
- _update_schema_with_model(schema, model)
118
- process_models(completed_model=model)
89
+ try:
90
+ futures.remove(future)
91
+ fqn, entry_name, data_hash, metadata_hash, mapping_schema = future.result()
92
+ model = models[fqn]
93
+ model._data_hash = data_hash
94
+ model._metadata_hash = metadata_hash
95
+ if model.mapping_schema != mapping_schema:
96
+ model.set_mapping_schema(mapping_schema)
97
+ optimized_query_cache.with_optimized_query(model, entry_name)
98
+ _update_schema_with_model(schema, model)
99
+ process_models(completed_model=model)
100
+ except Exception as ex:
101
+ raise SchemaError(f"Failed to update model schemas\n\n{ex}")
@@ -55,20 +55,19 @@ class SnapshotCache:
55
55
  for snapshot in loaded_snapshots:
56
56
  snapshots[snapshot.snapshot_id] = snapshot
57
57
 
58
- if c.MAX_FORK_WORKERS != 1:
59
- with optimized_query_cache_pool(self._optimized_query_cache) as executor:
60
- for key, entry_name in executor.map(
61
- load_optimized_query,
62
- (
63
- (snapshot.model, s_id)
64
- for s_id, snapshot in snapshots.items()
65
- if snapshot.is_model
66
- ),
67
- ):
68
- if entry_name:
69
- self._optimized_query_cache.with_optimized_query(
70
- snapshots[key].model, entry_name
71
- )
58
+ with optimized_query_cache_pool(self._optimized_query_cache) as executor:
59
+ for key, entry_name in executor.map(
60
+ load_optimized_query,
61
+ (
62
+ (snapshot.model, s_id)
63
+ for s_id, snapshot in snapshots.items()
64
+ if snapshot.is_model
65
+ ),
66
+ ):
67
+ if entry_name:
68
+ self._optimized_query_cache.with_optimized_query(
69
+ snapshots[key].model, entry_name
70
+ )
72
71
 
73
72
  for snapshot in snapshots.values():
74
73
  self._update_node_hash_cache(snapshot)
sqlmesh/dbt/loader.py CHANGED
@@ -355,6 +355,19 @@ class DbtLoader(Loader):
355
355
 
356
356
  return models
357
357
 
358
+ def put(self, models: t.List[Model], path: Path) -> bool:
359
+ return self._model_cache.put(
360
+ models,
361
+ self._cache_entry_name(path),
362
+ self._cache_entry_id(path),
363
+ )
364
+
365
+ def get(self, path: Path) -> t.List[Model]:
366
+ return self._model_cache.get(
367
+ self._cache_entry_name(path),
368
+ self._cache_entry_id(path),
369
+ )
370
+
358
371
  def _cache_entry_name(self, target_path: Path) -> str:
359
372
  try:
360
373
  path_for_name = target_path.absolute().relative_to(
sqlmesh/lsp/context.py CHANGED
@@ -3,6 +3,10 @@ from pathlib import Path
3
3
  from sqlmesh.core.context import Context
4
4
  import typing as t
5
5
 
6
+ from sqlmesh.core.model.definition import SqlModel
7
+ from sqlmesh.lsp.custom import RenderModelEntry
8
+ from sqlmesh.lsp.uri import URI
9
+
6
10
 
7
11
  @dataclass
8
12
  class ModelTarget:
@@ -49,3 +53,37 @@ class LSPContext:
49
53
  **model_map,
50
54
  **audit_map,
51
55
  }
56
+
57
+
58
+ def render_model(context: LSPContext, uri: URI) -> t.Iterator[RenderModelEntry]:
59
+ target = context.map[uri.to_path()]
60
+ if isinstance(target, AuditTarget):
61
+ audit = context.context.standalone_audits[target.name]
62
+ definition = audit.render_definition(
63
+ include_python=False,
64
+ render_query=True,
65
+ )
66
+ rendered_query = [render.sql(dialect=audit.dialect, pretty=True) for render in definition]
67
+ yield RenderModelEntry(
68
+ name=audit.name,
69
+ fqn=audit.fqn,
70
+ description=audit.description,
71
+ rendered_query="\n\n".join(rendered_query),
72
+ )
73
+ if isinstance(target, ModelTarget):
74
+ for name in target.names:
75
+ model = context.context.get_model(name)
76
+ if isinstance(model, SqlModel):
77
+ rendered_query = [
78
+ render.sql(dialect=model.dialect, pretty=True)
79
+ for render in model.render_definition(
80
+ include_python=False,
81
+ render_query=True,
82
+ )
83
+ ]
84
+ yield RenderModelEntry(
85
+ name=model.name,
86
+ fqn=model.fqn,
87
+ description=model.description,
88
+ rendered_query="\n\n".join(rendered_query),
89
+ )
sqlmesh/lsp/custom.py CHANGED
@@ -20,3 +20,29 @@ class AllModelsResponse(PydanticModel):
20
20
 
21
21
  models: t.List[str]
22
22
  keywords: t.List[str]
23
+
24
+
25
+ RENDER_MODEL_FEATURE = "sqlmesh/render_model"
26
+
27
+
28
+ class RenderModelRequest(PydanticModel):
29
+ textDocumentUri: str
30
+
31
+
32
+ class RenderModelEntry(PydanticModel):
33
+ """
34
+ An entry in the rendered model.
35
+ """
36
+
37
+ name: str
38
+ fqn: str
39
+ description: str
40
+ rendered_query: str
41
+
42
+
43
+ class RenderModelResponse(PydanticModel):
44
+ """
45
+ Response to render a model.
46
+ """
47
+
48
+ models: t.List[RenderModelEntry]
sqlmesh/lsp/main.py CHANGED
@@ -20,8 +20,15 @@ from sqlmesh.lsp.api import (
20
20
  ApiResponseGetModels,
21
21
  )
22
22
  from sqlmesh.lsp.completions import get_sql_completions
23
- from sqlmesh.lsp.context import LSPContext, ModelTarget
24
- from sqlmesh.lsp.custom import ALL_MODELS_FEATURE, AllModelsRequest, AllModelsResponse
23
+ from sqlmesh.lsp.context import LSPContext, ModelTarget, render_model as render_model_context
24
+ from sqlmesh.lsp.custom import (
25
+ ALL_MODELS_FEATURE,
26
+ RENDER_MODEL_FEATURE,
27
+ AllModelsRequest,
28
+ AllModelsResponse,
29
+ RenderModelRequest,
30
+ RenderModelResponse,
31
+ )
25
32
  from sqlmesh.lsp.reference import (
26
33
  get_references,
27
34
  )
@@ -88,6 +95,12 @@ class SQLMeshLanguageServer:
88
95
  except Exception as e:
89
96
  return get_sql_completions(None, uri)
90
97
 
98
+ @self.server.feature(RENDER_MODEL_FEATURE)
99
+ def render_model(ls: LanguageServer, params: RenderModelRequest) -> RenderModelResponse:
100
+ uri = URI(params.textDocumentUri)
101
+ context = self._context_get_or_load(uri)
102
+ return RenderModelResponse(models=list(render_model_context(context, uri)))
103
+
91
104
  @self.server.feature(API_FEATURE)
92
105
  def api(ls: LanguageServer, request: ApiRequest) -> t.Dict[str, t.Any]:
93
106
  ls.log_trace(f"API request: {request}")
@@ -0,0 +1,73 @@
1
+ # mypy: disable-error-code=no-untyped-def
2
+
3
+ from concurrent.futures import Future, ProcessPoolExecutor
4
+ import typing as t
5
+ import multiprocessing as mp
6
+ from sqlmesh.utils.windows import IS_WINDOWS
7
+
8
+
9
+ class SynchronousPoolExecutor:
10
+ """A mock implementation of the ProcessPoolExecutor for synchronous use.
11
+
12
+ This executor runs functions synchronously in the same process, avoiding the issues
13
+ with forking in test environments or when forking isn't possible (non-posix).
14
+ """
15
+
16
+ def __init__(self, max_workers=None, mp_context=None, initializer=None, initargs=()):
17
+ if initializer is not None:
18
+ try:
19
+ initializer(*initargs)
20
+ except BaseException as ex:
21
+ raise RuntimeError(f"Exception in initializer: {ex}")
22
+
23
+ def __enter__(self):
24
+ return self
25
+
26
+ def __exit__(self, *args):
27
+ self.shutdown(wait=True)
28
+ return False
29
+
30
+ def shutdown(self, wait=True, cancel_futures=False):
31
+ """No-op method to match ProcessPoolExecutor API.
32
+
33
+ Since this executor runs synchronously, there are no background processes
34
+ or resources to shut down and all futures will have completed already.
35
+ """
36
+ pass
37
+
38
+ def submit(self, fn, *args, **kwargs):
39
+ """Execute the function synchronously and return a Future with the result."""
40
+ future = Future()
41
+ try:
42
+ result = fn(*args, **kwargs)
43
+ future.set_result(result)
44
+ except Exception as e:
45
+ future.set_exception(e)
46
+ return future
47
+
48
+ def map(self, fn, *iterables, timeout=None, chunksize=1):
49
+ """Synchronous implementation of ProcessPoolExecutor.map.
50
+
51
+ This executes the function for each set of inputs from the iterables in the
52
+ current process using Python's built-in map, rather than distributing work.
53
+ """
54
+ return map(fn, *iterables)
55
+
56
+
57
+ PoolExecutor = t.Union[SynchronousPoolExecutor, ProcessPoolExecutor]
58
+
59
+
60
+ def create_process_pool_executor(
61
+ initializer: t.Callable, initargs: t.Tuple, max_workers: t.Optional[int]
62
+ ) -> PoolExecutor:
63
+ if max_workers == 1 or IS_WINDOWS:
64
+ return SynchronousPoolExecutor(
65
+ initializer=initializer,
66
+ initargs=initargs,
67
+ )
68
+ return ProcessPoolExecutor(
69
+ mp_context=mp.get_context("fork"),
70
+ initializer=initializer,
71
+ initargs=initargs,
72
+ max_workers=max_workers,
73
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.186.2.dev10
3
+ Version: 0.186.2.dev12
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=69xw6bTmi4cW90M-sl3g6wLDgxVcLHcKg1q8GS3lqyA,7466
2
- sqlmesh/_version.py,sha256=4uMhCBuhIRrG8deugXpflEuX2HS2TJC4pIJnUdcIplA,530
2
+ sqlmesh/_version.py,sha256=-p5hKFEr6HTn4HheIsijmPg5yWrVg3DTRSdgBNmX0G8,530
3
3
  sqlmesh/magics.py,sha256=JHorAZNtezf123jmTyNSoUC9yHmv-08hMP205PJmmIo,39750
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,7 +18,7 @@ sqlmesh/core/context_diff.py,sha256=s9toP17d4tJfOpFH91LcjGK2CM64PVuB0MPkeFgF4hQ,
18
18
  sqlmesh/core/dialect.py,sha256=fPTV517IOS0nWMxYznNNQnXkQkFFrPOSKe8ghsQ0o8A,49759
19
19
  sqlmesh/core/environment.py,sha256=0TTQf4C4LXwf37fm-dfYv1qyG7vtZfQI5rizgm5g0j0,11442
20
20
  sqlmesh/core/lineage.py,sha256=zjB0Zfamo2Fja2r5SSZPMbrEKTXr1WjozZPVqvxdybI,3143
21
- sqlmesh/core/loader.py,sha256=5ty4qY4qQFQuinhtNRzB-l5yvU4TgdPPW_Y_9VrnS2U,32244
21
+ sqlmesh/core/loader.py,sha256=exNCHmw06-CRBtOk-Hk5Ivw3gCowDWbxcvhMJVxpDd8,35887
22
22
  sqlmesh/core/macros.py,sha256=wSKkFDZ2eIvhPENIsQ_7j27zVMHIgT-2juRjGvDeEKM,59620
23
23
  sqlmesh/core/node.py,sha256=r4_1MRTPcU0MAlwZ5KzrykLT58B7dkKvHzPCsJmEKD8,14497
24
24
  sqlmesh/core/notification_target.py,sha256=PPGoDrgbRKxr27vJEu03XqNTQLYTw0ZF_b0yAapxGeI,16158
@@ -84,20 +84,20 @@ sqlmesh/core/metric/__init__.py,sha256=H1HmoD5IwN4YWe9iJXyueLYNmTQFZwok5nSWNJcZI
84
84
  sqlmesh/core/metric/definition.py,sha256=Yd5aVgsZCDPJ43aGP7WqtzZOuuSUtB8uJGVA6Jw9x9M,7201
85
85
  sqlmesh/core/metric/rewriter.py,sha256=GiSTHfn2kinqCfNPYgZPRk93JFLzVaaejHtHDQ0yXZI,7326
86
86
  sqlmesh/core/model/__init__.py,sha256=C8GRZ53xuXEA9hQv3BQS9pNNyd9rZ06R_B96UYGhDu4,1644
87
- sqlmesh/core/model/cache.py,sha256=s-aEFuUmUKITaWHvYbOyRarsjsgoA6vfASK-f-MwJ9k,7257
87
+ sqlmesh/core/model/cache.py,sha256=WJdn54xmA3NtX0dlX-DZnYlrWDhj4ycoq_4FD4MK2yg,7502
88
88
  sqlmesh/core/model/common.py,sha256=vbcXFhWE22-FeKYAVIlKWvTb-wkeC8XISjU_TIlak0c,16252
89
89
  sqlmesh/core/model/decorator.py,sha256=v_sVudNROGrtj5cZ3vgcOFnwPdzwoUGY6REAkQpIMQc,8927
90
90
  sqlmesh/core/model/definition.py,sha256=QBwGNL7Jqg8BGgpZfTtKciRJA7axkI5FShJJjnUDQ4w,108645
91
91
  sqlmesh/core/model/kind.py,sha256=i6_AMBarXPGPoMdF9kzBkyqS9tu__a2339-BoX7TgCM,36010
92
92
  sqlmesh/core/model/meta.py,sha256=osJgfSj6maIIdWAHzDVCkXXL--__gnKvqkzxM6Nuq6g,20779
93
- sqlmesh/core/model/schema.py,sha256=QzM1cOfukuK3L-cEE9MtxKeYec3x5JvutdZAnczJSiQ,3858
93
+ sqlmesh/core/model/schema.py,sha256=OClbAeA8LpW-IiSiLT_TlDWkEqmIIvSk0GXq8SWI-GQ,3443
94
94
  sqlmesh/core/model/seed.py,sha256=ctUHH6CmeEnBVJyIAZznx0xJBm09W7VIZ-76Cfwfq28,5004
95
95
  sqlmesh/core/plan/__init__.py,sha256=ctpnu9mK3uTujs0It8qRMqcj9GdObXDnvSgn4rD2zqc,450
96
96
  sqlmesh/core/plan/builder.py,sha256=Fd5raxq6r6b_3mOZndiI2U7SmliVYevcrpxWNqRV_OE,38471
97
97
  sqlmesh/core/plan/definition.py,sha256=SI3zi2poPkKJKTZogYv60Q7eIOhksaAcFUcu-J2-XqI,13218
98
98
  sqlmesh/core/plan/evaluator.py,sha256=RAHJgQoV5Y5SIG_pV2O_Bn0VFlD1yzMC0UrUU6eaB_E,26842
99
99
  sqlmesh/core/snapshot/__init__.py,sha256=Z5HTetNCZGoKF1WAKXk4LWL9dc8EC8lwS2O4TtoBmt4,1317
100
- sqlmesh/core/snapshot/cache.py,sha256=MRYi_koDvp2yeQ5NDGoNFzhuJTw7Vh7mI0oJyeVbqio,4022
100
+ sqlmesh/core/snapshot/cache.py,sha256=bgqCR2hyf6r2A_8QP1EnXFK25gDX37-Zg0YeMuETWxg,3934
101
101
  sqlmesh/core/snapshot/categorizer.py,sha256=qGG5870mu1jR2whKisSBH_GFbsyh96RnzJDgkN7DV9g,2482
102
102
  sqlmesh/core/snapshot/definition.py,sha256=PKzBLiQ1yIycGbmslEoQyJEwtfnJHsQuY_sl1xkBcvk,84081
103
103
  sqlmesh/core/snapshot/evaluator.py,sha256=Y01dkakdX-Jvz3t8DAzJPLVt3eL2DRqz7SabgZ7ME7g,89430
@@ -127,7 +127,7 @@ sqlmesh/dbt/builtin.py,sha256=fyzv4qR1isfnID1oWz-r46oDQvS90DN5mMUEWgapvN8,15153
127
127
  sqlmesh/dbt/column.py,sha256=Kvj_N3D_QegcoNQG7VqRiqy-FRfupud-6ilhlykBOgE,1873
128
128
  sqlmesh/dbt/common.py,sha256=g4PAL-sEdlnfb_zT-WW3xh-oamyCUoK8dUiDEMVwDWI,7804
129
129
  sqlmesh/dbt/context.py,sha256=dxClM-G1IeMbP0v5yxC_va8QNU-uKghW2dBlSZHJOk0,10331
130
- sqlmesh/dbt/loader.py,sha256=rpK6EUIZQdQ0igdgI7KNM9MIA6TGSTQbZQBxNej9Stc,15588
130
+ sqlmesh/dbt/loader.py,sha256=dXnqs_MmbP5u1CzoCviQpA_i0XCaEvm_L3kJTWOmFJ8,16024
131
131
  sqlmesh/dbt/manifest.py,sha256=iKfUWiqydIW3YnX6hqWAzRc-TmhiXfVFS7e29XJaS6E,22906
132
132
  sqlmesh/dbt/model.py,sha256=-TqeldZAqW-4g84Xi_16DpUD3atImk_9OHMbATIKYb8,26328
133
133
  sqlmesh/dbt/package.py,sha256=WM5Ve6o0QPW3rZxhw1xbr3yX2619I5KtHOKK1qe5mfU,4291
@@ -157,10 +157,10 @@ sqlmesh/integrations/github/cicd/config.py,sha256=tRhMZaLf1p6AWXvi67n85Q-lptJNGt
157
157
  sqlmesh/integrations/github/cicd/controller.py,sha256=EXFB_AHR_B4gTXMjX3CE6jZSvlENJ2BiI-Y7Wg6eGq4,41667
158
158
  sqlmesh/lsp/api.py,sha256=LJdECSaEZoPbu_Xl4KscVG-WLt7_g4dY-zKGO68dH14,2162
159
159
  sqlmesh/lsp/completions.py,sha256=Uo8lUzPBeoB58Eu2642YoAtNI0pgIFWFSvwmuJALTv4,3583
160
- sqlmesh/lsp/context.py,sha256=Ff09vrsawdYS-7YcXbGZW_RU-tj_-k8WV7maCghpYp0,1438
161
- sqlmesh/lsp/custom.py,sha256=GvNAiW7imPI8xmIx3qLWN5FljGPHFJKh0N5b6aZnwRo,489
160
+ sqlmesh/lsp/context.py,sha256=nCWg2uVH28-IJC2uDYuCbiXTh1lUFElSpNu4Q-fDWi4,2918
161
+ sqlmesh/lsp/custom.py,sha256=1YVcQz0y-4cdsvTdOQvlhUQXwOqPBWi3v_u1X2EqQxE,901
162
162
  sqlmesh/lsp/description.py,sha256=4QIGC9ix8IzmWF6DVUNhY4m1Crk3sNQA6hZ1RbyqU78,827
163
- sqlmesh/lsp/main.py,sha256=x2rPSDdYtB3S6baXMYUl0XOvvAtyGJhQgVLp_Ql7uPk,18087
163
+ sqlmesh/lsp/main.py,sha256=5EF1xA1rESYq4sTKDJPPG9Zl6mx3xfy6dbzWBbDee0M,18553
164
164
  sqlmesh/lsp/reference.py,sha256=bO2edkI2avmEYayQI11_OsDX1gjp36kq9ANwHczWSC0,8285
165
165
  sqlmesh/lsp/uri.py,sha256=mvSLbZGmMTHPpliThOtVWiFcHnfJzp1w39q9l-xD2SI,802
166
166
  sqlmesh/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -265,11 +265,12 @@ sqlmesh/utils/jinja.py,sha256=6Iob2Pv88L2rCaHyl_V90b5GKsG9hI10Ua_FRWhQIl0,23628
265
265
  sqlmesh/utils/metaprogramming.py,sha256=i1rxWi-oxdh1VwKoBh5Qvy0GgHm9GaDZMvIrv3QXGt4,23083
266
266
  sqlmesh/utils/migration.py,sha256=R7Ck6FX-LfcujGy7sgS0kDWlK-xAEBODr3gqIQ8gC08,835
267
267
  sqlmesh/utils/pandas.py,sha256=uKI-ygEtMcGDRzXdBu0RkGmMBkYxwu5O2Wabho841Ls,2196
268
+ sqlmesh/utils/process.py,sha256=vpXcpJ1eIbivs7gZdwTKSJI7OS6kKBebnaBzz75ykvs,2428
268
269
  sqlmesh/utils/pydantic.py,sha256=I898jAFqa3qU6lG2uPpRebVFncOUo8ZrLWM_R3K3DHU,11685
269
270
  sqlmesh/utils/rich.py,sha256=xNZyPCqEc4TAlJlsEthg-5eYUWpaizz0QjBzO7a1Yv4,1509
270
271
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
271
272
  sqlmesh/utils/yaml.py,sha256=np-WVcwmwsKghCgAj1OBBMsK1SXSRn4wR-H37cGiFr8,2282
272
- sqlmesh-0.186.2.dev10.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
273
+ sqlmesh-0.186.2.dev12.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
273
274
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
274
275
  web/client/dist/index.html,sha256=M50OuwjW63dRCS0Dz40Yecgvw9ecLFqizbTa6gv5Nd4,5472
275
276
  web/client/dist/assets/Audits-BPlxzqeG.js,sha256=TCx1qCjCGgmUSvH6HoPr5t8ztbRXMlOxrcW4o7hRjlA,1041
@@ -387,8 +388,8 @@ web/server/api/endpoints/models.py,sha256=ud-lRwriyI_OYg9tZchrBgeiXELiRTLNEodraf
387
388
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
388
389
  web/server/api/endpoints/plan.py,sha256=OYuQ_4dgbhG9ibTcX1a8mvYgH3ANxw9Fhbb4wg6QWqc,9222
389
390
  web/server/api/endpoints/table_diff.py,sha256=Wd5YxeKbZB7AkrDvqFgvV9ZQlnKYOF1n5W_w2EHE_Zw,2085
390
- sqlmesh-0.186.2.dev10.dist-info/METADATA,sha256=OQgQ8YFS_p9Nx6sC-Q5iQtr8pJjXLz0z-KxyFFx22DI,25524
391
- sqlmesh-0.186.2.dev10.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
392
- sqlmesh-0.186.2.dev10.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
393
- sqlmesh-0.186.2.dev10.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
394
- sqlmesh-0.186.2.dev10.dist-info/RECORD,,
391
+ sqlmesh-0.186.2.dev12.dist-info/METADATA,sha256=L3VQwV-bEeW1fq32TnOoXLWAyDZvZJGLKMcH-qllkyw,25524
392
+ sqlmesh-0.186.2.dev12.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
393
+ sqlmesh-0.186.2.dev12.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
394
+ sqlmesh-0.186.2.dev12.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
395
+ sqlmesh-0.186.2.dev12.dist-info/RECORD,,