sunstone-data-platform 1.0.0__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.
Files changed (66) hide show
  1. sunstone_data/__init__.py +36 -0
  2. sunstone_data/_version.py +24 -0
  3. sunstone_data/auth/__init__.py +43 -0
  4. sunstone_data/auth/client_creds.py +239 -0
  5. sunstone_data/auth/pkce.py +307 -0
  6. sunstone_data/auth/store.py +97 -0
  7. sunstone_data/catalog.py +175 -0
  8. sunstone_data/cli/__init__.py +5 -0
  9. sunstone_data/cli/_app.py +26 -0
  10. sunstone_data/cli/_errors.py +31 -0
  11. sunstone_data/cli/commands/__init__.py +22 -0
  12. sunstone_data/cli/commands/data_auth.py +120 -0
  13. sunstone_data/cli/commands/data_import.py +157 -0
  14. sunstone_data/cli/commands/data_info.py +166 -0
  15. sunstone_data/cli/commands/data_publish.py +69 -0
  16. sunstone_data/cli/commands/ls.py +49 -0
  17. sunstone_data/cli/commands/prov.py +58 -0
  18. sunstone_data/cli/commands/refs.py +142 -0
  19. sunstone_data/cli/commands/show.py +139 -0
  20. sunstone_data/cli/commands/snapshots.py +77 -0
  21. sunstone_data/cli/commands/sparql.py +68 -0
  22. sunstone_data/cli/main.py +30 -0
  23. sunstone_data/config.py +250 -0
  24. sunstone_data/discovery.py +317 -0
  25. sunstone_data/env_commands.py +97 -0
  26. sunstone_data/env_section.py +86 -0
  27. sunstone_data/graph/__init__.py +36 -0
  28. sunstone_data/graph/client.py +328 -0
  29. sunstone_data/graph/dataset.py +438 -0
  30. sunstone_data/graph/terms.py +34 -0
  31. sunstone_data/identifiers.py +155 -0
  32. sunstone_data/import_/__init__.py +28 -0
  33. sunstone_data/import_/fetcher.py +259 -0
  34. sunstone_data/import_/owner.py +130 -0
  35. sunstone_data/import_/parser.py +492 -0
  36. sunstone_data/import_/pipeline.py +275 -0
  37. sunstone_data/import_/storage.py +135 -0
  38. sunstone_data/metadata/__init__.py +41 -0
  39. sunstone_data/metadata/builder.py +276 -0
  40. sunstone_data/metadata/models.py +87 -0
  41. sunstone_data/metadata/provenance.py +171 -0
  42. sunstone_data/metadata/provenance_rdf.py +102 -0
  43. sunstone_data/plugin.py +62 -0
  44. sunstone_data/publish.py +134 -0
  45. sunstone_data/query.py +624 -0
  46. sunstone_data/resolve.py +178 -0
  47. sunstone_data/url.py +78 -0
  48. sunstone_data/utils/__init__.py +12 -0
  49. sunstone_data/utils/checksum.py +14 -0
  50. sunstone_data/utils/encoding.py +16 -0
  51. sunstone_data/utils/naming.py +38 -0
  52. sunstone_data/versioning/__init__.py +17 -0
  53. sunstone_data/versioning/schema_evolution.py +180 -0
  54. sunstone_data_platform-1.0.0.dist-info/METADATA +302 -0
  55. sunstone_data_platform-1.0.0.dist-info/RECORD +66 -0
  56. sunstone_data_platform-1.0.0.dist-info/WHEEL +4 -0
  57. sunstone_data_platform-1.0.0.dist-info/entry_points.txt +2 -0
  58. sunstone_pipeline/__init__.py +1 -0
  59. sunstone_pipeline/assets/__init__.py +11 -0
  60. sunstone_pipeline/assets/mirrors.py +123 -0
  61. sunstone_pipeline/assets/transforms.py +74 -0
  62. sunstone_pipeline/definitions.py +25 -0
  63. sunstone_pipeline/io_manager.py +56 -0
  64. sunstone_pipeline/resources/__init__.py +6 -0
  65. sunstone_pipeline/resources/iceberg.py +29 -0
  66. sunstone_pipeline/resources/refs.py +88 -0
@@ -0,0 +1,6 @@
1
+ """Dagster resources wrapping sunstone_data services."""
2
+
3
+ from sunstone_pipeline.resources.iceberg import IcebergResource
4
+ from sunstone_pipeline.resources.refs import RefsResource
5
+
6
+ __all__ = ["IcebergResource", "RefsResource"]
@@ -0,0 +1,29 @@
1
+ """Dagster resource wrapping sunstone_data Iceberg catalog access."""
2
+
3
+ import dagster as dg
4
+
5
+ from sunstone_data.catalog import get_catalog
6
+
7
+
8
+ class IcebergResource(dg.ConfigurableResource):
9
+ """Provides access to the Iceberg catalog via sunstone_data.
10
+
11
+ Wraps get_catalog() so assets receive catalog access as an
12
+ injectable resource. All data state lives in Iceberg — this
13
+ resource is a thin pass-through.
14
+ """
15
+
16
+ branch: str = "main"
17
+
18
+ def get_table(self, table_identifier: str):
19
+ """Load an Iceberg table by identifier (e.g., 'oslo.population')."""
20
+ catalog = get_catalog(self.branch)
21
+ return catalog.load_table(table_identifier)
22
+
23
+ def get_table_property(self, table_identifier: str, key: str) -> str | None:
24
+ """Read a single property from an Iceberg table's metadata.
25
+
26
+ Returns None if the property doesn't exist.
27
+ """
28
+ table = self.get_table(table_identifier)
29
+ return table.metadata.properties.get(key)
@@ -0,0 +1,88 @@
1
+ """Dagster resource for per-run branch isolation on graph-server refs.
2
+
3
+ Branches are graph-server refs (``graph.branch``). The Iceberg REST catalog
4
+ serves each branch at ``{catalog_uri}/iceberg/{branch}/``, so a branch created
5
+ here is both a graph ref and an isolated Iceberg catalog view. Branch CRUD goes
6
+ through :class:`sunstone_data.graph.client.GraphClient`; catalog access on the
7
+ branch goes through :func:`sunstone_data.catalog.get_catalog`.
8
+ """
9
+
10
+ import dagster as dg
11
+ from pyiceberg.catalog.rest import RestCatalog
12
+
13
+ from sunstone_data.catalog import get_catalog
14
+ from sunstone_data.graph.client import get_graph_client
15
+
16
+
17
+ class RefsResource(dg.ConfigurableResource):
18
+ """Manages graph-server branches for pipeline run isolation.
19
+
20
+ A pipeline run creates an isolated branch, runs its transforms against
21
+ the Iceberg catalog on that branch, and abandons the branch on failure.
22
+ """
23
+
24
+ parent: str = "main"
25
+
26
+ @staticmethod
27
+ def branch_name(pipeline_name: str, run_id: str) -> str:
28
+ """Return the branch name for a run.
29
+
30
+ graph-server and iceberg-catalog both take the branch as a single URL
31
+ path segment, so the name uses ``-`` as separator and rejects ``/``.
32
+
33
+ Raises:
34
+ ValueError: If ``pipeline_name`` or ``run_id`` contains ``/``.
35
+ """
36
+ if "/" in pipeline_name or "/" in run_id:
37
+ raise ValueError(f"pipeline name and run id must not contain '/': {pipeline_name!r}, {run_id!r}")
38
+ return f"pipeline-{pipeline_name}-{run_id}"
39
+
40
+ def create_pipeline_branch(self, pipeline_name: str, run_id: str) -> str:
41
+ """Create an isolated branch for a pipeline run, forked from ``parent``.
42
+
43
+ Args:
44
+ pipeline_name: Name of the pipeline (e.g. ``trusthere``).
45
+ run_id: Unique run identifier.
46
+
47
+ Returns:
48
+ Branch name (e.g. ``pipeline-trusthere-run-1``).
49
+
50
+ Raises:
51
+ RuntimeError: If graph-server rejects the request.
52
+ """
53
+ name = self.branch_name(pipeline_name, run_id)
54
+ try:
55
+ get_graph_client().create_branch(name, self.parent)
56
+ except Exception as e:
57
+ raise RuntimeError(f"Failed to create branch '{name}': {e}") from e
58
+ return name
59
+
60
+ def catalog(self, branch_name: str) -> RestCatalog:
61
+ """Return a PyIceberg catalog scoped to ``branch_name``."""
62
+ return get_catalog(branch_name)
63
+
64
+ def merge_to_main(self, branch_name: str) -> None:
65
+ """Merge a pipeline branch into ``parent``.
66
+
67
+ graph-server has no branch-merge endpoint yet (its ref API is create,
68
+ list and delete), so this always fails. Pipelines that need their
69
+ output on ``main`` must write there directly until the server grows a
70
+ merge.
71
+
72
+ Raises:
73
+ NotImplementedError: Always.
74
+ """
75
+ raise NotImplementedError(
76
+ f"graph-server has no branch merge; cannot merge '{branch_name}' into '{self.parent}'"
77
+ )
78
+
79
+ def abandon_branch(self, branch_name: str) -> None:
80
+ """Delete a pipeline branch without merging (run failed).
81
+
82
+ Raises:
83
+ RuntimeError: If graph-server rejects the request.
84
+ """
85
+ try:
86
+ get_graph_client().delete_branch(branch_name)
87
+ except Exception as e:
88
+ raise RuntimeError(f"Failed to delete branch '{branch_name}': {e}") from e