sqlmesh 0.187.6.dev7__py3-none-any.whl → 0.187.6.dev8__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.187.6.dev7'
21
- __version_tuple__ = version_tuple = (0, 187, 6, 'dev7')
20
+ __version__ = version = '0.187.6.dev8'
21
+ __version_tuple__ = version_tuple = (0, 187, 6, 'dev8')
sqlmesh/core/context.py CHANGED
@@ -932,19 +932,22 @@ class GenericContext(BaseContext, t.Generic[C]):
932
932
 
933
933
  return snapshot
934
934
 
935
- def config_for_path(self, path: Path) -> Config:
935
+ def config_for_path(self, path: Path) -> t.Tuple[Config, Path]:
936
+ """Returns the config and path of the said project for a given file path."""
936
937
  for config_path, config in self.configs.items():
937
938
  try:
938
939
  path.relative_to(config_path)
939
- return config
940
+ return config, config_path
940
941
  except ValueError:
941
942
  pass
942
- return self.config
943
+ return self.config, self.path
943
944
 
944
945
  def config_for_node(self, node: str | Model | Audit) -> Config:
945
946
  if isinstance(node, str):
946
- return self.config_for_path(self.get_snapshot(node, raise_if_missing=True).node._path) # type: ignore
947
- return self.config_for_path(node._path) # type: ignore
947
+ return self.config_for_path(self.get_snapshot(node, raise_if_missing=True).node._path)[
948
+ 0
949
+ ] # type: ignore
950
+ return self.config_for_path(node._path)[0] # type: ignore
948
951
 
949
952
  @property
950
953
  def models(self) -> MappingProxyType[str, Model]:
sqlmesh/lsp/reference.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from lsprotocol.types import Range, Position
2
2
  import typing as t
3
+ from pathlib import Path
3
4
 
5
+ from sqlmesh.core.audit import StandaloneAudit
4
6
  from sqlmesh.core.dialect import normalize_model_name
5
7
  from sqlmesh.core.model.definition import SqlModel
6
8
  from sqlmesh.lsp.context import LSPContext, ModelTarget, AuditTarget
@@ -10,6 +12,10 @@ from sqlglot.optimizer.scope import build_scope
10
12
  from sqlmesh.lsp.uri import URI
11
13
  from sqlmesh.utils.pydantic import PydanticModel
12
14
  from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
15
+ import ast
16
+ from sqlmesh.core.model import Model
17
+ from sqlmesh import macro
18
+ import inspect
13
19
 
14
20
 
15
21
  class Reference(PydanticModel):
@@ -72,6 +78,11 @@ def get_references(
72
78
  A list of references at the given position
73
79
  """
74
80
  references = get_model_definitions_for_a_path(lint_context, document_uri)
81
+
82
+ # Get macro references before filtering by position
83
+ macro_references = get_macro_definitions_for_a_path(lint_context, document_uri)
84
+ references.extend(macro_references)
85
+
75
86
  filtered_references = list(filter(by_position(position), references))
76
87
  return filtered_references
77
88
 
@@ -290,3 +301,180 @@ def _range_from_token_position_details(
290
301
  start=Position(line=start_line_0, character=start_col_0),
291
302
  end=Position(line=end_line_0, character=end_col_0),
292
303
  )
304
+
305
+
306
+ def get_macro_definitions_for_a_path(
307
+ lsp_context: LSPContext, document_uri: URI
308
+ ) -> t.List[Reference]:
309
+ """
310
+ Get macro references for a given path.
311
+
312
+ This function finds all macro invocations (e.g., @ADD_ONE, @MULTIPLY) in a SQL file
313
+ and creates references to their definitions in the Python macro files.
314
+
315
+ Args:
316
+ lsp_context: The LSP context containing macro definitions
317
+ document_uri: The URI of the document to search for macro invocations
318
+
319
+ Returns:
320
+ A list of Reference objects for each macro invocation found
321
+ """
322
+ path = document_uri.to_path()
323
+ if path.suffix != ".sql":
324
+ return []
325
+
326
+ # Get the file info from the context map
327
+ if path not in lsp_context.map:
328
+ return []
329
+
330
+ file_info = lsp_context.map[path]
331
+ # Process based on whether it's a model or standalone audit
332
+ if isinstance(file_info, ModelTarget):
333
+ # It's a model
334
+ target: t.Optional[t.Union[Model, StandaloneAudit]] = lsp_context.context.get_model(
335
+ model_or_snapshot=file_info.names[0], raise_if_missing=False
336
+ )
337
+ if target is None or not isinstance(target, SqlModel):
338
+ return []
339
+ query = target.query
340
+ file_path = target._path
341
+ elif isinstance(file_info, AuditTarget):
342
+ # It's a standalone audit
343
+ target = lsp_context.context.standalone_audits.get(file_info.name)
344
+ if target is None:
345
+ return []
346
+ query = target.query
347
+ file_path = target._path
348
+ else:
349
+ return []
350
+
351
+ references = []
352
+ config_for_model, config_path = lsp_context.context.config_for_path(
353
+ file_path,
354
+ )
355
+
356
+ with open(file_path, "r", encoding="utf-8") as file:
357
+ read_file = file.readlines()
358
+
359
+ for node in query.find_all(exp.Anonymous):
360
+ macro_name = node.name.lower()
361
+ reference = get_macro_reference(
362
+ node=node,
363
+ target=target,
364
+ read_file=read_file,
365
+ config_path=config_path,
366
+ macro_name=macro_name,
367
+ )
368
+ if reference is not None:
369
+ references.append(reference)
370
+
371
+ return references
372
+
373
+
374
+ def get_macro_reference(
375
+ target: t.Union[Model, StandaloneAudit],
376
+ read_file: t.List[str],
377
+ config_path: t.Optional[Path],
378
+ node: exp.Expression,
379
+ macro_name: str,
380
+ ) -> t.Optional[Reference]:
381
+ # Get the file path where the macro is defined
382
+ try:
383
+ # Get the position of the macro invocation in the source file first
384
+ if hasattr(node, "meta") and node.meta:
385
+ token_details = TokenPositionDetails.from_meta(node.meta)
386
+ macro_range = _range_from_token_position_details(token_details, read_file)
387
+
388
+ # Check if it's a built-in method
389
+ if builtin := get_built_in_macro_reference(macro_name, macro_range):
390
+ return builtin
391
+ else:
392
+ # Skip if we can't get the position
393
+ return None
394
+
395
+ # Find the macro definition information
396
+ macro_def = target.python_env.get(macro_name)
397
+ if macro_def is None:
398
+ return None
399
+
400
+ function_name = macro_def.name
401
+ if not function_name:
402
+ return None
403
+ if not macro_def.path:
404
+ return None
405
+ if not config_path:
406
+ return None
407
+ path = Path(config_path).joinpath(macro_def.path)
408
+
409
+ # Parse the Python file to find the function definition
410
+ with open(path, "r") as f:
411
+ tree = ast.parse(f.read())
412
+ with open(path, "r") as f:
413
+ output_read_line = f.readlines()
414
+
415
+ # Find the function definition by name
416
+ start_line = None
417
+ end_line = None
418
+ get_length_of_end_line = None
419
+ docstring = None
420
+ for ast_node in ast.walk(tree):
421
+ if isinstance(ast_node, ast.FunctionDef) and ast_node.name == function_name:
422
+ start_line = ast_node.lineno
423
+ end_line = ast_node.end_lineno
424
+ get_length_of_end_line = (
425
+ len(output_read_line[end_line - 1])
426
+ if end_line is not None and end_line - 1 < len(read_file)
427
+ else 0
428
+ )
429
+ # Extract docstring if present
430
+ docstring = ast.get_docstring(ast_node)
431
+ break
432
+
433
+ if start_line is None or end_line is None or get_length_of_end_line is None:
434
+ return None
435
+
436
+ # Create a reference to the macro definition
437
+ macro_uri = URI.from_path(path)
438
+
439
+ return Reference(
440
+ uri=macro_uri.value,
441
+ range=macro_range,
442
+ target_range=Range(
443
+ start=Position(line=start_line - 1, character=0),
444
+ end=Position(line=end_line - 1, character=get_length_of_end_line),
445
+ ),
446
+ markdown_description=docstring,
447
+ )
448
+ except Exception:
449
+ return None
450
+
451
+
452
+ def get_built_in_macro_reference(macro_name: str, macro_range: Range) -> t.Optional[Reference]:
453
+ """
454
+ Get a reference to a built-in macro by its name.
455
+
456
+ Args:
457
+ macro_name: The name of the built-in macro (e.g., 'each', 'sql_literal')
458
+ macro_range: The range of the macro invocation in the source file
459
+ """
460
+ built_in_macros = macro.get_registry()
461
+ built_in_macro = built_in_macros.get(macro_name)
462
+ if built_in_macro is None:
463
+ return None
464
+
465
+ func = built_in_macro.func
466
+ filename = inspect.getfile(func)
467
+ source_lines, line_number = inspect.getsourcelines(func)
468
+
469
+ # Calculate the end line number by counting the number of source lines
470
+ end_line_number = line_number + len(source_lines) - 1
471
+
472
+ return Reference(
473
+ uri=URI.from_path(Path(filename)).value,
474
+ range=macro_range,
475
+ target_range=Range(
476
+ start=Position(line=line_number - 1, character=0),
477
+ end=Position(line=end_line_number - 1, character=0),
478
+ ),
479
+ markdown_description=func.__doc__ if func.__doc__ else None,
480
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.187.6.dev7
3
+ Version: 0.187.6.dev8
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=f5wMd1D-q0Tfmeyk6jhWew59Zap6CmONqdGQFj7Q2wY,528
2
+ sqlmesh/_version.py,sha256=t-q_qJgc2Z8fYDdUBhH1EGGu4syoJaadC7IgtdmizXI,528
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
@@ -13,7 +13,7 @@ sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
14
  sqlmesh/core/console.py,sha256=ZAaGLOWBJGBxPzLV7KZZ9akPExgD5m5AboCM8O0Q0nY,150944
15
15
  sqlmesh/core/constants.py,sha256=oqt6zjepUhQoWlgapKn_HpG7Pu9mrFwepdX5caNfJUI,2499
16
- sqlmesh/core/context.py,sha256=gOwToFkXSQjb71Ki3cEqfxYjS64p2T2XKfZPVBtjGQA,115624
16
+ sqlmesh/core/context.py,sha256=qdnbqhTJsxYXs8RcqSq8KRBOlxN6BL0XMFCmHHz0XTc,115784
17
17
  sqlmesh/core/context_diff.py,sha256=s9toP17d4tJfOpFH91LcjGK2CM64PVuB0MPkeFgF4hQ,20415
18
18
  sqlmesh/core/dialect.py,sha256=fPTV517IOS0nWMxYznNNQnXkQkFFrPOSKe8ghsQ0o8A,49759
19
19
  sqlmesh/core/environment.py,sha256=Fsih6WZ7Tx4ec0XIlmIIspUWcmijPNW5uHPRQSWuhA8,12315
@@ -161,7 +161,7 @@ sqlmesh/lsp/context.py,sha256=nCWg2uVH28-IJC2uDYuCbiXTh1lUFElSpNu4Q-fDWi4,2918
161
161
  sqlmesh/lsp/custom.py,sha256=1YVcQz0y-4cdsvTdOQvlhUQXwOqPBWi3v_u1X2EqQxE,901
162
162
  sqlmesh/lsp/description.py,sha256=4QIGC9ix8IzmWF6DVUNhY4m1Crk3sNQA6hZ1RbyqU78,827
163
163
  sqlmesh/lsp/main.py,sha256=Lg_XHxXNScStf0aiOUptijZnBLOIjcmmhsFgM5w2rNo,27405
164
- sqlmesh/lsp/reference.py,sha256=c9iof2QN13T5b-XFNuI8ABlOCDkhfBGTR6yNK8i9LyA,10569
164
+ sqlmesh/lsp/reference.py,sha256=-HkMMzRzq4ike5mSkoWajanYNMAwhQhlPaqw62451ac,16887
165
165
  sqlmesh/lsp/uri.py,sha256=mvSLbZGmMTHPpliThOtVWiFcHnfJzp1w39q9l-xD2SI,802
166
166
  sqlmesh/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
167
  sqlmesh/migrations/v0001_init.py,sha256=h8FwMYJj9_O8yGpk12guaSaGwX5A3we2GiKeHrXi15o,2048
@@ -270,7 +270,7 @@ sqlmesh/utils/pydantic.py,sha256=I898jAFqa3qU6lG2uPpRebVFncOUo8ZrLWM_R3K3DHU,116
270
270
  sqlmesh/utils/rich.py,sha256=xNZyPCqEc4TAlJlsEthg-5eYUWpaizz0QjBzO7a1Yv4,1509
271
271
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
272
272
  sqlmesh/utils/yaml.py,sha256=np-WVcwmwsKghCgAj1OBBMsK1SXSRn4wR-H37cGiFr8,2282
273
- sqlmesh-0.187.6.dev7.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
273
+ sqlmesh-0.187.6.dev8.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
274
274
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
275
275
  web/client/dist/index.html,sha256=8vySM6CmjEU7pX2XGNeYyHR4PwjYGMsYT_RJn3p8Qt8,5472
276
276
  web/client/dist/assets/Audits-C6gUfP0Y.js,sha256=fgwKzn8EkoySQW3vumBtag39Tf7tKZ1CAjrtgiV6nVg,1041
@@ -373,7 +373,7 @@ web/server/main.py,sha256=9PaDa8sigo1z0AxtI3sQ3fHmDqs6uXwP-_IvoMwkznw,3199
373
373
  web/server/models.py,sha256=NUI2xqDcav20uuC2BZ9xDL2x0v16-gpVT1Sb-WHfAcg,15518
374
374
  web/server/openapi.py,sha256=NOg__YQg7arVdajQv5CvOA5KLTQCfGZFvwnjARuM6iE,586
375
375
  web/server/settings.py,sha256=PuWrOmn5LpJMBTymCMQreqMsL93tClTJ6wlcAZXjgCo,3755
376
- web/server/utils.py,sha256=WodQI75kQTXVCO_g1g9Sf0_b-9yoYFieb_xcKRpZ470,3986
376
+ web/server/utils.py,sha256=xqfzyqzsFSZu9Q9QHBCNvULUpAC6viInsznOJwcU6ig,3988
377
377
  web/server/watcher.py,sha256=5biJ8gbHmGNUrooMFWRmw00XDVg0bMx6Hub571cs6TI,5246
378
378
  web/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
379
379
  web/server/api/endpoints/__init__.py,sha256=FcXl56-kPqbKgCZKOx0kjE50hn37q1m-nwOCxI9tRcc,932
@@ -381,15 +381,15 @@ web/server/api/endpoints/commands.py,sha256=MVlxC-QG6VMEcboHYeuTdtsd7WgPpqJcQDa1
381
381
  web/server/api/endpoints/directories.py,sha256=Aa8U4pTs3Saj5DnJ9TDnX9mZois-EXgpcBm5GpNNM_s,1822
382
382
  web/server/api/endpoints/environments.py,sha256=g3YC6j4LLlG_wjJqn-Hrz78BWmgS7vhUhzaycsiez00,2124
383
383
  web/server/api/endpoints/events.py,sha256=tjhOhGe9jt7L1mOLLapl5qTvicDRnP5lPZHSq9OnSVI,932
384
- web/server/api/endpoints/files.py,sha256=HogdDH35tHBPLu_4vwj2aXrAqikA2UdkCOLv5QXf0Ng,6720
384
+ web/server/api/endpoints/files.py,sha256=H309MNDakLDBG12xtP1_J6URTSN3o2lq2rB9qQA5bRg,6731
385
385
  web/server/api/endpoints/lineage.py,sha256=XU99RI-6Q9SidItGM5WLQMje8NhM5nZWHbtWxDwWeFE,6050
386
386
  web/server/api/endpoints/meta.py,sha256=QKWWrHewGh5JS8mCSWXTKCIQwXw-TQfaT9tWt_SsNY4,1032
387
387
  web/server/api/endpoints/models.py,sha256=ud-lRwriyI_OYg9tZchrBgeiXELiRTLNEodrafB7Ndk,4697
388
388
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
389
389
  web/server/api/endpoints/plan.py,sha256=OYuQ_4dgbhG9ibTcX1a8mvYgH3ANxw9Fhbb4wg6QWqc,9222
390
390
  web/server/api/endpoints/table_diff.py,sha256=Wd5YxeKbZB7AkrDvqFgvV9ZQlnKYOF1n5W_w2EHE_Zw,2085
391
- sqlmesh-0.187.6.dev7.dist-info/METADATA,sha256=hUNr3g8uYQ-_rlFlkauSRlPcLF9VA6VmWu8ffRSR58w,26493
392
- sqlmesh-0.187.6.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
393
- sqlmesh-0.187.6.dev7.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
394
- sqlmesh-0.187.6.dev7.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
395
- sqlmesh-0.187.6.dev7.dist-info/RECORD,,
391
+ sqlmesh-0.187.6.dev8.dist-info/METADATA,sha256=U4RAMCMoF9gc7XvPkD6zIwq5XxFRxwV7PE_KkCBNFhs,26493
392
+ sqlmesh-0.187.6.dev8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
393
+ sqlmesh-0.187.6.dev8.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
394
+ sqlmesh-0.187.6.dev8.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
395
+ sqlmesh-0.187.6.dev8.dist-info/RECORD,,
@@ -60,7 +60,7 @@ async def write_file(
60
60
  replace_file(settings.project_path / path, settings.project_path / path_or_new_path)
61
61
  else:
62
62
  full_path = settings.project_path / path
63
- config = context.config_for_path(Path(path_or_new_path)) if context else None
63
+ config, _ = context.config_for_path(Path(path_or_new_path)) if context else (None, None)
64
64
  if (
65
65
  config
66
66
  and config.ui.format_on_save
web/server/utils.py CHANGED
@@ -64,11 +64,10 @@ def validate_path(path: str, settings: Settings = Depends(get_settings)) -> str:
64
64
  if any(
65
65
  full_path.match(pattern)
66
66
  for pattern in (
67
- context.config_for_path(Path(path)).ignore_patterns if context else c.IGNORE_PATTERNS
67
+ context.config_for_path(Path(path))[0].ignore_patterns if context else c.IGNORE_PATTERNS
68
68
  )
69
69
  ):
70
70
  raise HTTPException(status_code=HTTP_404_NOT_FOUND)
71
-
72
71
  return path
73
72
 
74
73