dagster-dbt 0.28.5__tar.gz → 0.28.7__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.
Files changed (67) hide show
  1. {dagster_dbt-0.28.5/dagster_dbt.egg-info → dagster_dbt-0.28.7}/PKG-INFO +4 -3
  2. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/asset_utils.py +3 -0
  3. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/cli_invocation.py +7 -0
  4. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/client.py +91 -18
  5. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/run_handler.py +22 -0
  6. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/sensor_builder.py +1 -1
  7. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dagster_dbt_translator.py +19 -8
  8. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_project.py +36 -0
  9. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/pyproject.toml.jinja +1 -1
  10. dagster_dbt-0.28.7/dagster_dbt/version.py +1 -0
  11. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7/dagster_dbt.egg-info}/PKG-INFO +4 -3
  12. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/requires.txt +1 -1
  13. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/setup.py +3 -2
  14. dagster_dbt-0.28.5/dagster_dbt/version.py +0 -1
  15. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/LICENSE +0 -0
  16. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/MANIFEST.in +0 -0
  17. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/README.md +0 -0
  18. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/__init__.py +0 -0
  19. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/asset_decorator.py +0 -0
  20. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/asset_specs.py +0 -0
  21. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cli/__init__.py +0 -0
  22. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cli/app.py +0 -0
  23. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/__init__.py +0 -0
  24. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/asset_defs.py +0 -0
  25. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/cli.py +0 -0
  26. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/ops.py +0 -0
  27. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/resources.py +0 -0
  28. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/types.py +0 -0
  29. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud/utils.py +0 -0
  30. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/__init__.py +0 -0
  31. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/asset_decorator.py +0 -0
  32. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/resources.py +0 -0
  33. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/cloud_v2/types.py +0 -0
  34. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/compat.py +0 -0
  35. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/components/__init__.py +0 -0
  36. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/components/dbt_project/__init__.py +0 -0
  37. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/components/dbt_project/component.py +0 -0
  38. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/components/dbt_project/scaffolder.py +0 -0
  39. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/__init__.py +0 -0
  40. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/dbt_cli_event.py +0 -0
  41. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/dbt_cli_invocation.py +0 -0
  42. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/dbt_event_iterator.py +0 -0
  43. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/resource.py +0 -0
  44. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/core/utils.py +0 -0
  45. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_core_version.py +0 -0
  46. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_manifest.py +0 -0
  47. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_manifest_asset_selection.py +0 -0
  48. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_project_manager.py +0 -0
  49. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/dbt_version.py +0 -0
  50. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/errors.py +0 -0
  51. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/freshness_builder.py +0 -0
  52. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/__init__.py +0 -0
  53. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/scaffold/__init__.py.jinja +0 -0
  54. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/scaffold/assets.py.jinja +0 -0
  55. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/scaffold/definitions.py.jinja +0 -0
  56. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/scaffold/project.py.jinja +0 -0
  57. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/scaffold/schedules.py.jinja +0 -0
  58. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/include/setup.py.jinja +0 -0
  59. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/metadata_set.py +0 -0
  60. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/py.typed +0 -0
  61. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt/utils.py +0 -0
  62. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/SOURCES.txt +0 -0
  63. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/dependency_links.txt +0 -0
  64. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/entry_points.txt +0 -0
  65. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/not-zip-safe +0 -0
  66. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/dagster_dbt.egg-info/top_level.txt +0 -0
  67. {dagster_dbt-0.28.5 → dagster_dbt-0.28.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dagster-dbt
3
- Version: 0.28.5
3
+ Version: 0.28.7
4
4
  Summary: A Dagster integration for dbt
5
5
  Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-dbt
6
6
  Author: Dagster Labs
@@ -10,11 +10,12 @@ Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Classifier: License :: OSI Approved :: Apache Software License
14
15
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.10,<3.14
16
+ Requires-Python: >=3.10,<3.15
16
17
  License-File: LICENSE
17
- Requires-Dist: dagster==1.12.5
18
+ Requires-Dist: dagster==1.12.7
18
19
  Requires-Dist: dbt-core<1.11,>=1.7
19
20
  Requires-Dist: gitpython
20
21
  Requires-Dist: Jinja2
@@ -572,6 +572,9 @@ def default_metadata_from_dbt_resource_props(
572
572
  dbt_resource_props.get("database"),
573
573
  dbt_resource_props.get("schema"),
574
574
  dbt_resource_props.get("alias"),
575
+ dbt_resource_props.get("name")
576
+ if dbt_resource_props.get("resource_type") == "source"
577
+ else None,
575
578
  ]
576
579
  if relation_part
577
580
  ]
@@ -1,3 +1,4 @@
1
+ import sys
1
2
  from collections.abc import Iterator, Mapping, Sequence
2
3
  from typing import Any, Optional, Union
3
4
 
@@ -54,6 +55,12 @@ class DbtCloudCliInvocation:
54
55
  self, timeout: Optional[float] = None
55
56
  ) -> Iterator[Union[AssetCheckEvaluation, AssetCheckResult, AssetMaterialization, Output]]:
56
57
  run = self.run_handler.wait(timeout=timeout)
58
+
59
+ # Write dbt Cloud run logs to stdout
60
+ logs = self.run_handler.get_run_logs()
61
+ if logs:
62
+ sys.stdout.write(logs)
63
+
57
64
  if "run_results.json" in self.run_handler.list_run_artifacts():
58
65
  run_results = DbtCloudJobRunResults.from_run_results_json(
59
66
  run_results_json=self.run_handler.get_run_results()
@@ -92,7 +92,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
92
92
  data: Optional[Mapping[str, Any]] = None,
93
93
  params: Optional[Mapping[str, Any]] = None,
94
94
  session_attr: str = "_get_session",
95
- ) -> Mapping[str, Any]:
95
+ ) -> requests.Response:
96
96
  url = f"{base_url}/{endpoint}" if endpoint else base_url
97
97
 
98
98
  num_retries = 0
@@ -107,7 +107,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
107
107
  timeout=self.request_timeout,
108
108
  )
109
109
  response.raise_for_status()
110
- return response.json()
110
+ return response
111
111
  except RequestException as e:
112
112
  self._log.error(
113
113
  f"Request to dbt Cloud API failed for url {url} with method {method} : {e}"
@@ -143,7 +143,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
143
143
  """
144
144
  if not description:
145
145
  description = "A job that runs dbt models, sources, and tests."
146
- return self._make_request(
146
+ response = self._make_request(
147
147
  method="post",
148
148
  endpoint="jobs",
149
149
  base_url=self.api_v2_url,
@@ -155,7 +155,8 @@ class DbtCloudWorkspaceClient(DagsterModel):
155
155
  "description": description,
156
156
  "job_type": "other",
157
157
  },
158
- )["data"]
158
+ )
159
+ return response.json()["data"]
159
160
 
160
161
  def list_jobs(
161
162
  self,
@@ -185,7 +186,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
185
186
  "limit": DAGSTER_DBT_CLOUD_LIST_JOBS_INDIVIDUAL_REQUEST_LIMIT,
186
187
  "offset": len(results),
187
188
  },
188
- )["data"]:
189
+ ).json()["data"]:
189
190
  results.extend(jobs)
190
191
  if len(jobs) < DAGSTER_DBT_CLOUD_LIST_JOBS_INDIVIDUAL_REQUEST_LIMIT:
191
192
  break
@@ -201,7 +202,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
201
202
  method="get",
202
203
  endpoint=f"jobs/{job_id}",
203
204
  base_url=self.api_v2_url,
204
- )["data"]
205
+ ).json()["data"]
205
206
 
206
207
  def destroy_job(self, job_id: int) -> Mapping[str, Any]:
207
208
  """Destroys a given dbt Cloud job.
@@ -213,7 +214,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
213
214
  method="delete",
214
215
  endpoint=f"jobs/{job_id}",
215
216
  base_url=self.api_v2_url,
216
- )["data"]
217
+ ).json()["data"]
217
218
 
218
219
  def trigger_job_run(
219
220
  self, job_id: int, steps_override: Optional[Sequence[str]] = None
@@ -237,7 +238,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
237
238
  data={"steps_override": steps_override, "cause": DAGSTER_ADHOC_TRIGGER_CAUSE}
238
239
  if steps_override
239
240
  else {"cause": DAGSTER_ADHOC_TRIGGER_CAUSE},
240
- )["data"]
241
+ ).json()["data"]
241
242
 
242
243
  def get_runs_batch(
243
244
  self,
@@ -278,26 +279,35 @@ class DbtCloudWorkspaceClient(DagsterModel):
278
279
  "finished_at__range": f"""["{finished_at_lower_bound.isoformat()}", "{finished_at_upper_bound.isoformat()}"]""",
279
280
  "order_by": "finished_at",
280
281
  },
281
- )
282
+ ).json()
282
283
  data = cast("Sequence[Mapping[str, Any]]", resp["data"])
283
284
  total_count = resp["extra"]["pagination"]["total_count"]
284
285
  return data, total_count
285
286
 
286
- def get_run_details(self, run_id: int) -> Mapping[str, Any]:
287
+ def get_run_details(
288
+ self, run_id: int, include_related: Optional[Sequence[str]] = None
289
+ ) -> Mapping[str, Any]:
287
290
  """Retrieves the details of a given dbt Cloud Run.
288
291
 
289
292
  Args:
290
- run_id (str): The dbt Cloud Run ID. You can retrieve this value from the
293
+ run_id (int): The dbt Cloud Run ID. You can retrieve this value from the
291
294
  URL of the given run in the dbt Cloud UI.
295
+ include_related (Optional[Sequence[str]]): List of related fields to pull with the run.
296
+ Valid values are "trigger", "job", "debug_logs", and "run_steps".
292
297
 
293
298
  Returns:
294
299
  Dict[str, Any]: Parsed json data representing the API response.
295
300
  """
301
+ params = {}
302
+ if include_related:
303
+ params["include_related"] = ",".join(include_related)
304
+
296
305
  return self._make_request(
297
306
  method="get",
298
307
  endpoint=f"runs/{run_id}",
299
308
  base_url=self.api_v2_url,
300
- )["data"]
309
+ params=params,
310
+ ).json()["data"]
301
311
 
302
312
  def poll_run(
303
313
  self,
@@ -352,21 +362,25 @@ class DbtCloudWorkspaceClient(DagsterModel):
352
362
  endpoint=f"runs/{run_id}/artifacts",
353
363
  base_url=self.api_v2_url,
354
364
  session_attr="_get_artifact_session",
355
- )["data"],
365
+ ).json()["data"],
356
366
  )
357
367
 
358
368
  def get_run_artifact(self, run_id: int, path: str) -> Mapping[str, Any]:
359
369
  """Retrieves an artifact at the given path for a given dbt Cloud Run.
360
370
 
371
+ Args:
372
+ run_id (int): The dbt Cloud Run ID.
373
+ path (str): The path to the artifact (e.g., "run_results.json", "manifest.json").
374
+
361
375
  Returns:
362
- Dict[str, Any]: Parsed json data representing the API response.
376
+ Dict[str, Any]: Parsed json data representing the artifact.
363
377
  """
364
378
  return self._make_request(
365
379
  method="get",
366
380
  endpoint=f"runs/{run_id}/artifacts/{path}",
367
381
  base_url=self.api_v2_url,
368
382
  session_attr="_get_artifact_session",
369
- )
383
+ ).json()
370
384
 
371
385
  def get_run_results_json(self, run_id: int) -> Mapping[str, Any]:
372
386
  """Retrieves the run_results.json artifact of a given dbt Cloud Run.
@@ -384,6 +398,65 @@ class DbtCloudWorkspaceClient(DagsterModel):
384
398
  """
385
399
  return self.get_run_artifact(run_id=run_id, path="manifest.json")
386
400
 
401
+ def get_run_logs(self, run_id: int, max_retries: int = 3, retry_delay: float = 2.0) -> str:
402
+ """Retrieves the stdout/stderr logs from a given dbt Cloud Run.
403
+
404
+ This method fetches logs from the run_steps field by calling get_run_details
405
+ with include_related=["run_steps"]. Each step contains a logs field with
406
+ the stdout/stderr output for that step.
407
+
408
+ Note: There can be a slight delay between when a run completes and when the logs
409
+ are fully populated in the API. This method will retry a few times if it detects
410
+ completed steps with empty logs.
411
+
412
+ Args:
413
+ run_id (int): The dbt Cloud Run ID.
414
+ max_retries (int): Maximum number of times to retry fetching logs if empty. Defaults to 3.
415
+ retry_delay (float): Time in seconds to wait between retries. Defaults to 2.0.
416
+
417
+ Returns:
418
+ str: The concatenated log text content from all run steps.
419
+ """
420
+ for attempt in range(max_retries):
421
+ run_details = self.get_run_details(run_id=run_id, include_related=["run_steps"])
422
+
423
+ logs_parts = []
424
+ run_steps = run_details.get("run_steps", [])
425
+ completed_steps_with_empty_logs = 0
426
+
427
+ for step in run_steps:
428
+ step_name = step.get("name", "Unknown Step")
429
+ step_logs = step.get("logs", "")
430
+ step_status = step.get("status_humanized", "unknown")
431
+
432
+ # Track completed steps with empty logs
433
+ if step_status == "Success" and not step_logs:
434
+ completed_steps_with_empty_logs += 1
435
+
436
+ if step_logs:
437
+ logs_parts.append(f"=== Step: {step_name} ===")
438
+ logs_parts.append(step_logs)
439
+ logs_parts.append("") # Empty line between steps
440
+
441
+ # If we have completed steps with empty logs and retries left, wait and try again
442
+ if completed_steps_with_empty_logs > 0 and attempt < max_retries - 1:
443
+ self._log.warning(
444
+ f"Found {completed_steps_with_empty_logs} completed steps with empty logs for run {run_id}. "
445
+ f"Retrying in {retry_delay} seconds..."
446
+ )
447
+ time.sleep(retry_delay)
448
+ continue
449
+
450
+ # Either we got all logs or we're out of retries
451
+ if completed_steps_with_empty_logs > 0:
452
+ self._log.warning(
453
+ f"Still missing logs for {completed_steps_with_empty_logs} completed steps after {max_retries} attempts"
454
+ )
455
+
456
+ return "\n".join(logs_parts) if logs_parts else ""
457
+
458
+ return ""
459
+
387
460
  def get_project_details(self, project_id: int) -> Mapping[str, Any]:
388
461
  """Retrieves the details of a given dbt Cloud Project.
389
462
 
@@ -398,7 +471,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
398
471
  method="get",
399
472
  endpoint=f"projects/{project_id}",
400
473
  base_url=self.api_v2_url,
401
- )["data"]
474
+ ).json()["data"]
402
475
 
403
476
  def get_environment_details(self, environment_id: int) -> Mapping[str, Any]:
404
477
  """Retrieves the details of a given dbt Cloud Environment.
@@ -414,7 +487,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
414
487
  method="get",
415
488
  endpoint=f"environments/{environment_id}",
416
489
  base_url=self.api_v2_url,
417
- )["data"]
490
+ ).json()["data"]
418
491
 
419
492
  def get_account_details(self) -> Mapping[str, Any]:
420
493
  """Retrieves the details of the account associated to the dbt Cloud workspace.
@@ -426,7 +499,7 @@ class DbtCloudWorkspaceClient(DagsterModel):
426
499
  method="get",
427
500
  endpoint=None,
428
501
  base_url=self.api_v2_url,
429
- )["data"]
502
+ ).json()["data"]
430
503
 
431
504
  def verify_connection(self) -> None:
432
505
  """Verifies the connection to the dbt Cloud REST API."""
@@ -12,7 +12,9 @@ from dagster import (
12
12
  get_dagster_logger,
13
13
  )
14
14
  from dagster._record import record
15
+ from dagster._time import get_current_timestamp
15
16
  from dateutil import parser
17
+ from requests.exceptions import RequestException
16
18
 
17
19
  from dagster_dbt.asset_utils import build_dbt_specs, get_asset_check_key_for_test
18
20
  from dagster_dbt.cloud_v2.client import DbtCloudWorkspaceClient
@@ -61,8 +63,28 @@ class DbtCloudJobRunHandler:
61
63
  def list_run_artifacts(self) -> Sequence[str]:
62
64
  return self.client.list_run_artifacts(run_id=self.run_id)
63
65
 
66
+ def get_run_logs(self) -> Optional[str]:
67
+ """Retrieves the stdout/stderr logs from the completed dbt Cloud run.
68
+
69
+ This method fetches logs from the run_steps by calling get_run_details
70
+ with include_related=["run_steps"].
71
+
72
+ Returns:
73
+ Optional[str]: The concatenated log text content from all run steps,
74
+ or None if logs are not available.
75
+ """
76
+ try:
77
+ return self.client.get_run_logs(run_id=self.run_id)
78
+ except RequestException as e:
79
+ logger.warning(f"Failed to retrieve logs for run {self.run_id}: {e}")
80
+ return None
81
+
64
82
 
65
83
  def get_completed_at_timestamp(result: Mapping[str, Any]) -> float:
84
+ timing = result["timing"]
85
+ if len(timing) == 0:
86
+ # as a fallback, use the current timestamp
87
+ return get_current_timestamp()
66
88
  # result["timing"] is a list of events in run_results.json
67
89
  # For successful models and passing tests,
68
90
  # the last item of that list includes the timing details of the execution.
@@ -142,7 +142,7 @@ def sorted_asset_events(
142
142
  return [
143
143
  sorted_event[1]
144
144
  for sorted_event in sorted(
145
- materializations_and_timestamps, key=lambda x: (x[0], topo_aks.index(x[1].asset_key))
145
+ materializations_and_timestamps, key=lambda x: (topo_aks.index(x[1].asset_key), x[0])
146
146
  )
147
147
  ]
148
148
 
@@ -62,6 +62,8 @@ class DagsterDbtTranslatorSettings(Resolvable):
62
62
  rather than fully qualified name. Defaults to False.
63
63
  enable_source_tests_as_checks (bool): Whether to load dbt source tests as Dagster asset checks.
64
64
  Defaults to False. If False, asset observations will be emitted for source tests.
65
+ enable_source_metadata (bool): Whether to include metadata on AssetDep objects for dbt sources.
66
+ If set to True, enables the ability to remap upstream asset keys based on table name. Defaults to False.
65
67
  """
66
68
 
67
69
  enable_asset_checks: bool = True
@@ -69,6 +71,7 @@ class DagsterDbtTranslatorSettings(Resolvable):
69
71
  enable_code_references: bool = False
70
72
  enable_dbt_selection_by_name: bool = False
71
73
  enable_source_tests_as_checks: bool = False
74
+ enable_source_metadata: bool = False
72
75
 
73
76
 
74
77
  class DagsterDbtTranslator:
@@ -145,15 +148,23 @@ class DagsterDbtTranslator:
145
148
 
146
149
  # calculate the dependencies for the asset
147
150
  upstream_ids = get_upstream_unique_ids(manifest, resource_props)
148
- deps = [
149
- AssetDep(
150
- asset=self.get_asset_spec(manifest, upstream_id, project).key,
151
- partition_mapping=self.get_partition_mapping(
152
- resource_props, self.get_resource_props(manifest, upstream_id)
153
- ),
151
+ deps: list[AssetDep] = []
152
+ for upstream_id in upstream_ids:
153
+ spec = self.get_asset_spec(manifest, upstream_id, project)
154
+ partition_mapping = self.get_partition_mapping(
155
+ resource_props, self.get_resource_props(manifest, upstream_id)
154
156
  )
155
- for upstream_id in upstream_ids
156
- ]
157
+
158
+ deps.append(
159
+ AssetDep(
160
+ asset=spec.key,
161
+ partition_mapping=partition_mapping,
162
+ metadata=spec.metadata
163
+ if self.settings.enable_source_metadata and upstream_id.startswith("source")
164
+ else None,
165
+ )
166
+ )
167
+
157
168
  self_partition_mapping = self.get_partition_mapping(resource_props, resource_props)
158
169
  if self_partition_mapping and has_self_dependency(resource_props):
159
170
  deps.append(
@@ -132,6 +132,42 @@ class DagsterDbtProjectPreparer(DbtProjectPreparer):
132
132
  .wait()
133
133
  )
134
134
 
135
+ # Remove seed entries from partial_parse to force re-parsing at runtime.
136
+ # This ensures seeds get correct root_path based on current project location.
137
+ self._invalidate_seeds_in_partial_parse(project)
138
+
139
+ def _invalidate_seeds_in_partial_parse(self, project: "DbtProject") -> None:
140
+ """Remove seed entries from partial_parse.msgpack to force re-parsing.
141
+
142
+ Seeds contain root_path which is an absolute path from build time. When state
143
+ is generated in one environment (e.g., CI/CD) and used in another (e.g., deployed
144
+ container), the root_path points to the wrong location and seed loading fails.
145
+
146
+ By removing seed entries from the cache, dbt will re-parse them at runtime with
147
+ the correct current project path. Models keep their cached data for fast loading.
148
+ """
149
+ import msgpack
150
+
151
+ partial_parse_path = project.project_dir / project.target_path / "partial_parse.msgpack"
152
+ if not partial_parse_path.exists():
153
+ return
154
+
155
+ with open(partial_parse_path, "rb") as f:
156
+ data = msgpack.unpack(f, raw=False, strict_map_key=False)
157
+
158
+ # Remove seed nodes
159
+ seed_node_ids = [k for k in data.get("nodes", {}).keys() if k.startswith("seed.")]
160
+ for seed_id in seed_node_ids:
161
+ del data["nodes"][seed_id]
162
+
163
+ # Remove seed file entries (CSVs)
164
+ seed_file_ids = [k for k in data.get("files", {}).keys() if k.lower().endswith(".csv")]
165
+ for file_id in seed_file_ids:
166
+ del data["files"][file_id]
167
+
168
+ with open(partial_parse_path, "wb") as f:
169
+ msgpack.pack(data, f)
170
+
135
171
 
136
172
  @record_custom
137
173
  class DbtProject(IHaveNew):
@@ -3,7 +3,7 @@ name = "{{ project_name }}"
3
3
  version = "0.1.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
- requires-python = ">=3.10,<3.14"
6
+ requires-python = ">=3.10,<3.15"
7
7
  dependencies = [
8
8
  "dagster",
9
9
  "dagster-cloud",
@@ -0,0 +1 @@
1
+ __version__ = "0.28.7"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dagster-dbt
3
- Version: 0.28.5
3
+ Version: 0.28.7
4
4
  Summary: A Dagster integration for dbt
5
5
  Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-dbt
6
6
  Author: Dagster Labs
@@ -10,11 +10,12 @@ Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Classifier: License :: OSI Approved :: Apache Software License
14
15
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.10,<3.14
16
+ Requires-Python: >=3.10,<3.15
16
17
  License-File: LICENSE
17
- Requires-Dist: dagster==1.12.5
18
+ Requires-Dist: dagster==1.12.7
18
19
  Requires-Dist: dbt-core<1.11,>=1.7
19
20
  Requires-Dist: gitpython
20
21
  Requires-Dist: Jinja2
@@ -1,4 +1,4 @@
1
- dagster==1.12.5
1
+ dagster==1.12.7
2
2
  dbt-core<1.11,>=1.7
3
3
  gitpython
4
4
  Jinja2
@@ -32,14 +32,15 @@ setup(
32
32
  "Programming Language :: Python :: 3.11",
33
33
  "Programming Language :: Python :: 3.12",
34
34
  "Programming Language :: Python :: 3.13",
35
+ "Programming Language :: Python :: 3.14",
35
36
  "License :: OSI Approved :: Apache Software License",
36
37
  "Operating System :: OS Independent",
37
38
  ],
38
39
  packages=find_packages(exclude=["dagster_dbt_tests*"]),
39
40
  include_package_data=True,
40
- python_requires=">=3.10,<3.14",
41
+ python_requires=">=3.10,<3.15",
41
42
  install_requires=[
42
- "dagster==1.12.5",
43
+ "dagster==1.12.7",
43
44
  # Follow the version support constraints for dbt Core: https://docs.getdbt.com/docs/dbt-versions/core
44
45
  f"dbt-core>=1.7,<{DBT_CORE_VERSION_UPPER_BOUND}",
45
46
  "gitpython",
@@ -1 +0,0 @@
1
- __version__ = "0.28.5"
File without changes
File without changes
File without changes
File without changes