duckrun 0.2.9.dev1__tar.gz → 0.2.9.dev3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duckrun
3
- Version: 0.2.9.dev1
3
+ Version: 0.2.9.dev3
4
4
  Summary: Lakehouse task runner powered by DuckDB for Microsoft Fabric
5
5
  Author: mim
6
6
  License: MIT
@@ -2,7 +2,7 @@
2
2
 
3
3
  from duckrun.core import Duckrun
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = "0.2.9.dev3"
6
6
 
7
7
  # Expose unified connect method at module level
8
8
  connect = Duckrun.connect
@@ -123,6 +123,12 @@ def get_fabric_api_token() -> Optional[str]:
123
123
  Returns:
124
124
  Fabric API token string or None if authentication fails
125
125
  """
126
+ # Check if we already have a cached Fabric API token
127
+ fabric_token_env = os.environ.get("FABRIC_API_TOKEN")
128
+ if fabric_token_env:
129
+ print("✅ Using cached Fabric API token")
130
+ return fabric_token_env
131
+
126
132
  print("🔐 Getting Fabric API token...")
127
133
 
128
134
  # Try Fabric notebook environment first
@@ -130,6 +136,7 @@ def get_fabric_api_token() -> Optional[str]:
130
136
  import notebookutils # type: ignore
131
137
  print("📓 Microsoft Fabric notebook detected - using notebookutils")
132
138
  token = notebookutils.credentials.getToken("pbi")
139
+ os.environ["FABRIC_API_TOKEN"] = token
133
140
  print("✅ Fabric API token obtained!")
134
141
  return token
135
142
  except ImportError:
@@ -158,6 +165,7 @@ def get_fabric_api_token() -> Optional[str]:
158
165
  print("🔐 Trying Azure CLI for Fabric API...")
159
166
  credential = AzureCliCredential()
160
167
  token_obj = credential.get_token("https://api.fabric.microsoft.com/.default")
168
+ os.environ["FABRIC_API_TOKEN"] = token_obj.token
161
169
  print("✅ Fabric API token obtained via Azure CLI!")
162
170
  return token_obj.token
163
171
  except Exception as cli_error:
@@ -167,6 +175,7 @@ def get_fabric_api_token() -> Optional[str]:
167
175
  credential = InteractiveBrowserCredential()
168
176
 
169
177
  token_obj = credential.get_token("https://api.fabric.microsoft.com/.default")
178
+ os.environ["FABRIC_API_TOKEN"] = token_obj.token
170
179
  print("✅ Fabric API token obtained!")
171
180
  return token_obj.token
172
181
 
@@ -727,8 +727,8 @@ class Duckrun:
727
727
 
728
728
  # Call the deployment function (DirectLake only)
729
729
  return deploy_semantic_model(
730
- workspace_name=self.workspace,
731
- lakehouse_name=self.lakehouse_name,
730
+ workspace_name_or_id=self.workspace,
731
+ lakehouse_name_or_id=self.lakehouse_name,
732
732
  schema_name=self.schema,
733
733
  dataset_name=dataset_name,
734
734
  bim_url=bim_url,
@@ -43,31 +43,66 @@ class FabricRestClient:
43
43
  return response
44
44
 
45
45
 
46
- def get_workspace_id(workspace_name, client):
47
- """Get workspace ID by name"""
46
+ def get_workspace_id(workspace_name_or_id, client):
47
+ """Get workspace ID by name or validate if already a GUID"""
48
+ import re
49
+
50
+ # Check if input is already a GUID
51
+ guid_pattern = re.compile(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', re.IGNORECASE)
52
+ if guid_pattern.match(workspace_name_or_id):
53
+ # It's already a GUID, verify it exists
54
+ try:
55
+ response = client.get(f"/v1/workspaces/{workspace_name_or_id}")
56
+ workspace_name = response.json().get('displayName', workspace_name_or_id)
57
+ print(f"✓ Found workspace: {workspace_name}")
58
+ return workspace_name_or_id
59
+ except:
60
+ raise ValueError(f"Workspace with ID '{workspace_name_or_id}' not found")
61
+
62
+ # It's a name, search for it
48
63
  response = client.get("/v1/workspaces")
49
64
  workspaces = response.json().get('value', [])
50
65
 
51
- workspace_match = next((ws for ws in workspaces if ws.get('displayName') == workspace_name), None)
66
+ workspace_match = next((ws for ws in workspaces if ws.get('displayName') == workspace_name_or_id), None)
52
67
  if not workspace_match:
53
- raise ValueError(f"Workspace '{workspace_name}' not found")
68
+ raise ValueError(f"Workspace '{workspace_name_or_id}' not found")
54
69
 
55
70
  workspace_id = workspace_match['id']
56
- print(f"✓ Found workspace: {workspace_name}")
71
+ print(f"✓ Found workspace: {workspace_name_or_id}")
57
72
  return workspace_id
58
73
 
59
74
 
60
- def get_lakehouse_id(lakehouse_name, workspace_id, client):
61
- """Get lakehouse ID by name"""
75
+ def get_lakehouse_id(lakehouse_name_or_id, workspace_id, client):
76
+ """Get lakehouse ID by name or validate if already a GUID"""
77
+ import re
78
+
79
+ # Check if input is already a GUID
80
+ guid_pattern = re.compile(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', re.IGNORECASE)
81
+ if guid_pattern.match(lakehouse_name_or_id):
82
+ # It's already a GUID, verify it exists
83
+ try:
84
+ response = client.get(f"/v1/workspaces/{workspace_id}/lakehouses")
85
+ items = response.json().get('value', [])
86
+ lakehouse_match = next((item for item in items if item.get('id') == lakehouse_name_or_id), None)
87
+ if lakehouse_match:
88
+ lakehouse_name = lakehouse_match.get('displayName', lakehouse_name_or_id)
89
+ print(f"✓ Found lakehouse: {lakehouse_name}")
90
+ return lakehouse_name_or_id
91
+ else:
92
+ raise ValueError(f"Lakehouse with ID '{lakehouse_name_or_id}' not found")
93
+ except Exception as e:
94
+ raise ValueError(f"Lakehouse with ID '{lakehouse_name_or_id}' not found: {e}")
95
+
96
+ # It's a name, search for it
62
97
  response = client.get(f"/v1/workspaces/{workspace_id}/lakehouses")
63
98
  items = response.json().get('value', [])
64
99
 
65
- lakehouse_match = next((item for item in items if item.get('displayName') == lakehouse_name), None)
100
+ lakehouse_match = next((item for item in items if item.get('displayName') == lakehouse_name_or_id), None)
66
101
  if not lakehouse_match:
67
- raise ValueError(f"Lakehouse '{lakehouse_name}' not found")
102
+ raise ValueError(f"Lakehouse '{lakehouse_name_or_id}' not found")
68
103
 
69
104
  lakehouse_id = lakehouse_match['id']
70
- print(f"✓ Found lakehouse: {lakehouse_name}")
105
+ print(f"✓ Found lakehouse: {lakehouse_name_or_id}")
71
106
  return lakehouse_id
72
107
 
73
108
 
@@ -266,14 +301,14 @@ def create_dataset_from_bim(dataset_name, bim_content, workspace_id, client):
266
301
  raise Exception(f"Operation timed out")
267
302
 
268
303
 
269
- def deploy_semantic_model(workspace_name, lakehouse_name, schema_name, dataset_name,
304
+ def deploy_semantic_model(workspace_name_or_id, lakehouse_name_or_id, schema_name, dataset_name,
270
305
  bim_url, wait_seconds=5):
271
306
  """
272
307
  Deploy a semantic model using DirectLake mode.
273
308
 
274
309
  Args:
275
- workspace_name: Name of the target workspace
276
- lakehouse_name: Name of the lakehouse
310
+ workspace_name_or_id: Name or GUID of the target workspace
311
+ lakehouse_name_or_id: Name or GUID of the lakehouse
277
312
  schema_name: Schema name (e.g., 'dbo', 'staging')
278
313
  dataset_name: Name for the semantic model
279
314
  bim_url: URL to the BIM file
@@ -295,7 +330,7 @@ def deploy_semantic_model(workspace_name, lakehouse_name, schema_name, dataset_n
295
330
  try:
296
331
  # Step 1: Get workspace ID
297
332
  print("\n[Step 1/6] Getting workspace information...")
298
- workspace_id = get_workspace_id(workspace_name, client)
333
+ workspace_id = get_workspace_id(workspace_name_or_id, client)
299
334
 
300
335
  # Step 2: Check if dataset exists
301
336
  print(f"\n[Step 2/6] Checking if dataset '{dataset_name}' exists...")
@@ -320,7 +355,7 @@ def deploy_semantic_model(workspace_name, lakehouse_name, schema_name, dataset_n
320
355
 
321
356
  # Step 3: Get lakehouse ID
322
357
  print(f"\n[Step 3/6] Finding lakehouse...")
323
- lakehouse_id = get_lakehouse_id(lakehouse_name, workspace_id, client)
358
+ lakehouse_id = get_lakehouse_id(lakehouse_name_or_id, workspace_id, client)
324
359
 
325
360
  # Step 4: Download and update BIM
326
361
  print("\n[Step 4/6] Downloading and configuring BIM file...")
@@ -346,8 +381,8 @@ def deploy_semantic_model(workspace_name, lakehouse_name, schema_name, dataset_n
346
381
  print("🎉 Deployment Completed!")
347
382
  print("=" * 70)
348
383
  print(f"Dataset: {dataset_name}")
349
- print(f"Workspace: {workspace_name}")
350
- print(f"Lakehouse: {lakehouse_name}")
384
+ print(f"Workspace: {workspace_name_or_id}")
385
+ print(f"Lakehouse: {lakehouse_name_or_id}")
351
386
  print(f"Schema: {schema_name}")
352
387
  print("=" * 70)
353
388
 
@@ -359,8 +394,8 @@ def deploy_semantic_model(workspace_name, lakehouse_name, schema_name, dataset_n
359
394
  print("=" * 70)
360
395
  print(f"Error: {str(e)}")
361
396
  print("\n💡 Troubleshooting:")
362
- print(f" - Verify workspace '{workspace_name}' exists")
363
- print(f" - Verify lakehouse '{lakehouse_name}' exists")
397
+ print(f" - Verify workspace '{workspace_name_or_id}' exists")
398
+ print(f" - Verify lakehouse '{lakehouse_name_or_id}' exists")
364
399
  print(f" - Ensure tables exist in '{schema_name}' schema")
365
400
  print(f" - Check tables are in Delta format")
366
401
  print("=" * 70)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duckrun
3
- Version: 0.2.9.dev1
3
+ Version: 0.2.9.dev3
4
4
  Summary: Lakehouse task runner powered by DuckDB for Microsoft Fabric
5
5
  Author: mim
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "duckrun"
7
- version = "0.2.9.dev1"
7
+ version = "0.2.9.dev3"
8
8
  description = "Lakehouse task runner powered by DuckDB for Microsoft Fabric"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes