semantic-link-labs 0.9.2__py3-none-any.whl → 0.9.4__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.

Potentially problematic release.


This version of semantic-link-labs might be problematic. Click here for more details.

Files changed (54) hide show
  1. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/METADATA +10 -6
  2. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/RECORD +54 -44
  3. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/WHEEL +1 -1
  4. sempy_labs/__init__.py +27 -1
  5. sempy_labs/_ai.py +8 -5
  6. sempy_labs/_capacity_migration.py +3 -2
  7. sempy_labs/_connections.py +45 -9
  8. sempy_labs/_dax.py +17 -3
  9. sempy_labs/_delta_analyzer.py +308 -138
  10. sempy_labs/_eventhouses.py +70 -1
  11. sempy_labs/_gateways.py +56 -8
  12. sempy_labs/_generate_semantic_model.py +30 -9
  13. sempy_labs/_helper_functions.py +84 -9
  14. sempy_labs/_job_scheduler.py +226 -2
  15. sempy_labs/_list_functions.py +42 -19
  16. sempy_labs/_ml_experiments.py +1 -1
  17. sempy_labs/_model_bpa.py +17 -2
  18. sempy_labs/_model_bpa_rules.py +20 -8
  19. sempy_labs/_semantic_models.py +117 -0
  20. sempy_labs/_sql.py +73 -6
  21. sempy_labs/_sqldatabase.py +227 -0
  22. sempy_labs/_translations.py +2 -2
  23. sempy_labs/_vertipaq.py +3 -3
  24. sempy_labs/_warehouses.py +1 -1
  25. sempy_labs/admin/__init__.py +49 -8
  26. sempy_labs/admin/_activities.py +166 -0
  27. sempy_labs/admin/_apps.py +143 -0
  28. sempy_labs/admin/_basic_functions.py +32 -652
  29. sempy_labs/admin/_capacities.py +250 -0
  30. sempy_labs/admin/_datasets.py +184 -0
  31. sempy_labs/admin/_domains.py +1 -3
  32. sempy_labs/admin/_items.py +3 -1
  33. sempy_labs/admin/_reports.py +165 -0
  34. sempy_labs/admin/_scanner.py +53 -49
  35. sempy_labs/admin/_shared.py +74 -0
  36. sempy_labs/admin/_tenant.py +489 -0
  37. sempy_labs/directlake/_dl_helper.py +0 -1
  38. sempy_labs/directlake/_update_directlake_partition_entity.py +6 -0
  39. sempy_labs/graph/_teams.py +1 -1
  40. sempy_labs/graph/_users.py +9 -1
  41. sempy_labs/lakehouse/_get_lakehouse_columns.py +2 -2
  42. sempy_labs/lakehouse/_get_lakehouse_tables.py +2 -2
  43. sempy_labs/lakehouse/_lakehouse.py +3 -3
  44. sempy_labs/lakehouse/_shortcuts.py +29 -16
  45. sempy_labs/migration/_migrate_calctables_to_lakehouse.py +2 -2
  46. sempy_labs/migration/_refresh_calc_tables.py +2 -2
  47. sempy_labs/report/__init__.py +3 -1
  48. sempy_labs/report/_download_report.py +4 -1
  49. sempy_labs/report/_export_report.py +272 -0
  50. sempy_labs/report/_report_functions.py +11 -263
  51. sempy_labs/report/_report_rebind.py +1 -1
  52. sempy_labs/tom/_model.py +281 -29
  53. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/LICENSE +0 -0
  54. {semantic_link_labs-0.9.2.dist-info → semantic_link_labs-0.9.4.dist-info}/top_level.txt +0 -0
@@ -7,12 +7,18 @@ from sempy_labs._helper_functions import (
7
7
  _print_success,
8
8
  resolve_item_id,
9
9
  _create_dataframe,
10
+ _conv_b64,
11
+ _decode_b64,
10
12
  )
11
13
  from uuid import UUID
14
+ import sempy_labs._icons as icons
12
15
 
13
16
 
14
17
  def create_eventhouse(
15
- name: str, description: Optional[str] = None, workspace: Optional[str | UUID] = None
18
+ name: str,
19
+ definition: Optional[dict],
20
+ description: Optional[str] = None,
21
+ workspace: Optional[str | UUID] = None,
16
22
  ):
17
23
  """
18
24
  Creates a Fabric eventhouse.
@@ -23,6 +29,8 @@ def create_eventhouse(
23
29
  ----------
24
30
  name: str
25
31
  Name of the eventhouse.
32
+ definition : dict
33
+ The definition (EventhouseProperties.json) of the eventhouse.
26
34
  description : str, default=None
27
35
  A description of the environment.
28
36
  workspace : str | uuid.UUID, default=None
@@ -38,6 +46,20 @@ def create_eventhouse(
38
46
  if description:
39
47
  payload["description"] = description
40
48
 
49
+ if definition is not None:
50
+ if not isinstance(definition, dict):
51
+ raise ValueError(f"{icons.red_dot} The definition must be a dictionary.")
52
+
53
+ payload["definition"] = {
54
+ "parts": [
55
+ {
56
+ "path": "EventhouseProperties.json",
57
+ "payload": _conv_b64(definition),
58
+ "payloadType": "InlineBase64",
59
+ }
60
+ ]
61
+ }
62
+
41
63
  _base_api(
42
64
  request=f"/v1/workspaces/{workspace_id}/eventhouses",
43
65
  method="post",
@@ -123,3 +145,50 @@ def delete_eventhouse(name: str, workspace: Optional[str | UUID] = None):
123
145
  workspace_name=workspace_name,
124
146
  action="deleted",
125
147
  )
148
+
149
+
150
+ def get_eventhouse_definition(
151
+ eventhouse: str | UUID,
152
+ workspace: Optional[str | UUID] = None,
153
+ return_dataframe: bool = False,
154
+ ) -> dict | pd.DataFrame:
155
+ """
156
+ Gets the eventhouse definition.
157
+
158
+ This is a wrapper function for the following API: `Items - Get Eventhouse Definition <https://learn.microsoft.com/rest/api/fabric/eventhouse/items/get-eventhouse-definition>`_.
159
+
160
+ Parameters
161
+ ----------
162
+ eventhouse : str
163
+ Name of the eventhouse.
164
+ workspace : str | uuid.UUID, default=None
165
+ The Fabric workspace name or ID in which the eventhouse resides.
166
+ Defaults to None which resolves to the workspace of the attached lakehouse
167
+ or if no lakehouse attached, resolves to the workspace of the notebook.
168
+ return_dataframe : bool, default=False
169
+ If True, returns a dataframe. If False, returns a json dictionary.
170
+
171
+ Returns
172
+ -------
173
+ dict | pandas.DataFrame
174
+ The eventhouse definition in .json format or as a pandas dataframe.
175
+ """
176
+
177
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
178
+ item_id = resolve_item_id(item=eventhouse, type="Eventhouse", workspace=workspace)
179
+
180
+ result = _base_api(
181
+ request=f"/v1/workspaces/{workspace_id}/eventhouses/{item_id}/getDefinition",
182
+ method="post",
183
+ status_codes=None,
184
+ lro_return_json=True,
185
+ )
186
+
187
+ df = pd.json_normalize(result["definition"]["parts"])
188
+
189
+ if return_dataframe:
190
+ return df
191
+ else:
192
+ df_filt = df[df["path"] == "EventhouseProperties.json"]
193
+ payload = df_filt["payload"].iloc[0]
194
+ return _decode_b64(payload)
sempy_labs/_gateways.py CHANGED
@@ -21,6 +21,8 @@ def list_gateways() -> pd.DataFrame:
21
21
 
22
22
  This is a wrapper function for the following API: `Gateways - List Gateways <https://learn.microsoft.com/rest/api/fabric/core/gateways/list-gateways>`_.
23
23
 
24
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
25
+
24
26
  Returns
25
27
  -------
26
28
  pandas.DataFrame
@@ -41,7 +43,9 @@ def list_gateways() -> pd.DataFrame:
41
43
  }
42
44
  df = _create_dataframe(columns=columns)
43
45
 
44
- responses = _base_api(request="/v1/gateways", uses_pagination=True)
46
+ responses = _base_api(
47
+ request="/v1/gateways", client="fabric_sp", uses_pagination=True
48
+ )
45
49
 
46
50
  for r in responses:
47
51
  for v in r.get("value", []):
@@ -85,6 +89,8 @@ def delete_gateway(gateway: str | UUID):
85
89
 
86
90
  This is a wrapper function for the following API: `Gateways - Delete Gateway <https://learn.microsoft.com/rest/api/fabric/core/gateways/delete-gateway>`_.
87
91
 
92
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
93
+
88
94
  Parameters
89
95
  ----------
90
96
  gateway : str | uuid.UUID
@@ -92,7 +98,7 @@ def delete_gateway(gateway: str | UUID):
92
98
  """
93
99
 
94
100
  gateway_id = _resolve_gateway_id(gateway)
95
- _base_api(request=f"/v1/gateways/{gateway_id}", method="delete")
101
+ _base_api(request=f"/v1/gateways/{gateway_id}", client="fabric_sp", method="delete")
96
102
  print(f"{icons.green_dot} The '{gateway}' gateway has been deleted.")
97
103
 
98
104
 
@@ -102,6 +108,8 @@ def list_gateway_role_assigments(gateway: str | UUID) -> pd.DataFrame:
102
108
 
103
109
  This is a wrapper function for the following API: `Gateways - List Gateway Role Assignments <https://learn.microsoft.com/rest/api/fabric/core/gateways/list-gateway-role-assignments>`_.
104
110
 
111
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
112
+
105
113
  Parameters
106
114
  ----------
107
115
  gateway : str | uuid.UUID
@@ -122,7 +130,9 @@ def list_gateway_role_assigments(gateway: str | UUID) -> pd.DataFrame:
122
130
  df = _create_dataframe(columns=columns)
123
131
  gateway_id = _resolve_gateway_id(gateway)
124
132
  responses = _base_api(
125
- request=f"/v1/gateways/{gateway_id}/roleAssignments", uses_pagination=True
133
+ request=f"/v1/gateways/{gateway_id}/roleAssignments",
134
+ client="fabric_sp",
135
+ uses_pagination=True,
126
136
  )
127
137
 
128
138
  for r in responses:
@@ -145,6 +155,8 @@ def delete_gateway_role_assignment(gateway: str | UUID, role_assignment_id: UUID
145
155
 
146
156
  This is a wrapper function for the following API: `Gateways - Delete Gateway Role Assignment <https://learn.microsoft.com/rest/api/fabric/core/gateways/delete-gateway-role-assignment>`_.
147
157
 
158
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
159
+
148
160
  Parameters
149
161
  ----------
150
162
  gateway : str | uuid.UUID
@@ -156,6 +168,7 @@ def delete_gateway_role_assignment(gateway: str | UUID, role_assignment_id: UUID
156
168
  gateway_id = _resolve_gateway_id(gateway)
157
169
  _base_api(
158
170
  request=f"/v1/gateways/{gateway_id}/roleAssignments/{role_assignment_id}",
171
+ client="fabric_sp",
159
172
  method="delete",
160
173
  )
161
174
 
@@ -187,6 +200,8 @@ def delete_gateway_member(gateway: str | UUID, gateway_member: str | UUID):
187
200
 
188
201
  This is a wrapper function for the following API: `Gateways - Delete Gateway Member <https://learn.microsoft.com/rest/api/fabric/core/gateways/delete-gateway-member>`_.
189
202
 
203
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
204
+
190
205
  Parameters
191
206
  ----------
192
207
  gateway : str | uuid.UUID
@@ -200,7 +215,11 @@ def delete_gateway_member(gateway: str | UUID, gateway_member: str | UUID):
200
215
  gateway=gateway_id, gateway_member=gateway_member
201
216
  )
202
217
 
203
- _base_api(request=f"/v1/gateways/{gateway_id}/members/{member_id}", method="delete")
218
+ _base_api(
219
+ request=f"/v1/gateways/{gateway_id}/members/{member_id}",
220
+ client="fabric_sp",
221
+ method="delete",
222
+ )
204
223
  print(
205
224
  f"{icons.green_dot} The '{member_id}' member for the '{gateway}' gateway has been deleted."
206
225
  )
@@ -212,6 +231,8 @@ def list_gateway_members(gateway: str | UUID) -> pd.DataFrame:
212
231
 
213
232
  This is a wrapper function for the following API: `Gateways - List Gateway Members <https://learn.microsoft.com/rest/api/fabric/core/gateways/list-gateway-members>`_.
214
233
 
234
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
235
+
215
236
  Parameters
216
237
  ----------
217
238
  gateway : str | uuid.UUID
@@ -235,7 +256,9 @@ def list_gateway_members(gateway: str | UUID) -> pd.DataFrame:
235
256
  }
236
257
  df = _create_dataframe(columns=columns)
237
258
 
238
- response = _base_api(request=f"/v1/gateways/{gateway_id}/members")
259
+ response = _base_api(
260
+ request=f"/v1/gateways/{gateway_id}/members", client="fabric_sp"
261
+ )
239
262
 
240
263
  for v in response.json().get("value", []):
241
264
  new_data = {
@@ -269,6 +292,8 @@ def create_vnet_gateway(
269
292
 
270
293
  This is a wrapper function for the following API: `Gateways - Create Gateway <https://learn.microsoft.com/rest/api/fabric/core/gateways/create-gateway>`_.
271
294
 
295
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
296
+
272
297
  Parameters
273
298
  ----------
274
299
  name : str
@@ -304,7 +329,13 @@ def create_vnet_gateway(
304
329
  "numberOfMemberGateways": number_of_member_gateways,
305
330
  }
306
331
 
307
- _base_api(request="/v1/gateways", method="post", payload=payload, status_codes=201)
332
+ _base_api(
333
+ request="/v1/gateways",
334
+ client="fabric_sp",
335
+ method="post",
336
+ payload=payload,
337
+ status_codes=201,
338
+ )
308
339
 
309
340
  print(
310
341
  f"{icons.green_dot} The '{name}' gateway was created within the '{capacity}' capacity."
@@ -322,6 +353,8 @@ def update_on_premises_gateway(
322
353
 
323
354
  This is a wrapper function for the following API: `Gateways - Update Gateway <https://learn.microsoft.com/rest/api/fabric/core/gateways/update-gateway>`_.
324
355
 
356
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
357
+
325
358
  Parameters
326
359
  ----------
327
360
  gateway : str | uuid.UUID
@@ -352,7 +385,12 @@ def update_on_premises_gateway(
352
385
 
353
386
  payload["type"] = "OnPremises"
354
387
 
355
- _base_api(request=f"/v1/gateways/{gateway_id}", method="patch", payload=payload)
388
+ _base_api(
389
+ request=f"/v1/gateways/{gateway_id}",
390
+ client="fabric_sp",
391
+ method="patch",
392
+ payload=payload,
393
+ )
356
394
 
357
395
  print(f"{icons.green_dot} The '{gateway}' has been updated accordingly.")
358
396
 
@@ -368,6 +406,8 @@ def update_vnet_gateway(
368
406
 
369
407
  This is a wrapper function for the following API: `Gateways - Update Gateway <https://learn.microsoft.com/rest/api/fabric/core/gateways/update-gateway>`_.
370
408
 
409
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
410
+
371
411
  Parameters
372
412
  ----------
373
413
  gateway : str | uuid.UUID
@@ -399,7 +439,12 @@ def update_vnet_gateway(
399
439
 
400
440
  payload["type"] = "VirtualNetwork"
401
441
 
402
- _base_api(request=f"/v1/gateways/{gateway_id}", method="patch", payload=payload)
442
+ _base_api(
443
+ request=f"/v1/gateways/{gateway_id}",
444
+ client="fabric_sp",
445
+ method="patch",
446
+ payload=payload,
447
+ )
403
448
  print(f"{icons.green_dot} The '{gateway}' has been updated accordingly.")
404
449
 
405
450
 
@@ -411,6 +456,8 @@ def bind_semantic_model_to_gateway(
411
456
 
412
457
  This is a wrapper function for the following API: `Datasets - Bind To Gateway In Group <https://learn.microsoft.com/rest/api/power-bi/datasets/bind-to-gateway-in-group>`_.
413
458
 
459
+ Service Principal Authentication is supported (see `here <https://github.com/microsoft/semantic-link-labs/blob/main/notebooks/Service%20Principal.ipynb>`_ for examples).
460
+
414
461
  Parameters
415
462
  ----------
416
463
  dataset : str | uuid.UUID
@@ -435,6 +482,7 @@ def bind_semantic_model_to_gateway(
435
482
 
436
483
  _base_api(
437
484
  request=f"/v1.0/myorg/groups/{workspace_id}/datasets/{dataset_id}/Default.BindToGateway",
485
+ client="fabric_sp",
438
486
  method="post",
439
487
  payload=payload,
440
488
  )
@@ -11,6 +11,7 @@ from sempy_labs._helper_functions import (
11
11
  _conv_b64,
12
12
  _decode_b64,
13
13
  _base_api,
14
+ _mount,
14
15
  )
15
16
  from sempy_labs.lakehouse._lakehouse import lakehouse_attached
16
17
  import sempy_labs._icons as icons
@@ -252,6 +253,7 @@ def deploy_semantic_model(
252
253
  target_workspace: Optional[str | UUID] = None,
253
254
  refresh_target_dataset: bool = True,
254
255
  overwrite: bool = False,
256
+ perspective: Optional[str] = None,
255
257
  ):
256
258
  """
257
259
  Deploys a semantic model based on an existing semantic model.
@@ -274,6 +276,8 @@ def deploy_semantic_model(
274
276
  If set to True, this will initiate a full refresh of the target semantic model in the target workspace.
275
277
  overwrite : bool, default=False
276
278
  If set to True, overwrites the existing semantic model in the workspace if it exists.
279
+ perspective : str, default=None
280
+ Set this to the name of a perspective in the model and it will reduce the deployed model down to the tables/columns/measures/hierarchies within that perspective.
277
281
  """
278
282
 
279
283
  (source_workspace_name, source_workspace_id) = resolve_workspace_name_and_id(
@@ -307,7 +311,21 @@ def deploy_semantic_model(
307
311
  f"{icons.warning} The '{target_dataset}' semantic model already exists within the '{target_workspace_name}' workspace. The 'overwrite' parameter is set to False so the source semantic model was not deployed to the target destination."
308
312
  )
309
313
 
310
- bim = get_semantic_model_bim(dataset=source_dataset, workspace=source_workspace_id)
314
+ if perspective is not None:
315
+
316
+ from sempy_labs.tom import connect_semantic_model
317
+
318
+ with connect_semantic_model(
319
+ dataset=source_dataset, workspace=source_workspace, readonly=True
320
+ ) as tom:
321
+
322
+ df_added = tom._reduce_model(perspective_name=perspective)
323
+ bim = tom.get_bim()
324
+
325
+ else:
326
+ bim = get_semantic_model_bim(
327
+ dataset=source_dataset, workspace=source_workspace_id
328
+ )
311
329
 
312
330
  # Create the semantic model if the model does not exist
313
331
  if dfD_filt.empty:
@@ -325,6 +343,9 @@ def deploy_semantic_model(
325
343
  if refresh_target_dataset:
326
344
  refresh_semantic_model(dataset=target_dataset, workspace=target_workspace_id)
327
345
 
346
+ if perspective is not None:
347
+ return df_added
348
+
328
349
 
329
350
  @log
330
351
  def get_semantic_model_bim(
@@ -368,16 +389,16 @@ def get_semantic_model_bim(
368
389
  f"{icons.red_dot} In order to save the model.bim file, a lakehouse must be attached to the notebook. Please attach a lakehouse to this notebook."
369
390
  )
370
391
 
371
- lakehouse = resolve_lakehouse_name()
372
- folderPath = "/lakehouse/default/Files"
373
- fileExt = ".bim"
374
- if not save_to_file_name.endswith(fileExt):
375
- save_to_file_name = f"{save_to_file_name}{fileExt}"
376
- filePath = os.path.join(folderPath, save_to_file_name)
377
- with open(filePath, "w") as json_file:
392
+ local_path = _mount()
393
+ save_folder = f"{local_path}/Files"
394
+ file_ext = ".bim"
395
+ if not save_to_file_name.endswith(file_ext):
396
+ save_to_file_name = f"{save_to_file_name}{file_ext}"
397
+ file_path = os.path.join(save_folder, save_to_file_name)
398
+ with open(file_path, "w") as json_file:
378
399
  json.dump(bimJson, json_file, indent=4)
379
400
  print(
380
- f"{icons.green_dot} The {fileExt} file for the '{dataset_name}' semantic model has been saved to the '{lakehouse}' in this location: '{filePath}'.\n\n"
401
+ f"{icons.green_dot} The {file_ext} file for the '{dataset_name}' semantic model has been saved to the lakehouse attached to the notebook within: 'Files/{save_to_file_name}'.\n\n"
381
402
  )
382
403
 
383
404
  return bimJson
@@ -31,7 +31,9 @@ def _build_url(url: str, params: dict) -> str:
31
31
 
32
32
 
33
33
  def create_abfss_path(
34
- lakehouse_id: UUID, lakehouse_workspace_id: UUID, delta_table_name: str
34
+ lakehouse_id: UUID,
35
+ lakehouse_workspace_id: UUID,
36
+ delta_table_name: Optional[str] = None,
35
37
  ) -> str:
36
38
  """
37
39
  Creates an abfss path for a delta table in a Fabric lakehouse.
@@ -42,18 +44,22 @@ def create_abfss_path(
42
44
  ID of the Fabric lakehouse.
43
45
  lakehouse_workspace_id : uuid.UUID
44
46
  ID of the Fabric workspace.
45
- delta_table_name : str
47
+ delta_table_name : str, default=None
46
48
  Name of the delta table name.
47
49
 
48
50
  Returns
49
51
  -------
50
52
  str
51
- An abfss path which can be used to save/reference a delta table in a Fabric lakehouse.
53
+ An abfss path which can be used to save/reference a delta table in a Fabric lakehouse or lakehouse.
52
54
  """
53
55
 
54
56
  fp = _get_default_file_path()
57
+ path = f"abfss://{lakehouse_workspace_id}@{fp}/{lakehouse_id}"
58
+
59
+ if delta_table_name is not None:
60
+ path += f"/Tables/{delta_table_name}"
55
61
 
56
- return f"abfss://{lakehouse_workspace_id}@{fp}/{lakehouse_id}/Tables/{delta_table_name}"
62
+ return path
57
63
 
58
64
 
59
65
  def _get_default_file_path() -> str:
@@ -506,7 +512,6 @@ def save_as_delta_table(
506
512
  or if no lakehouse attached, resolves to the workspace of the notebook.
507
513
  """
508
514
 
509
- from pyspark.sql import SparkSession
510
515
  from pyspark.sql.types import (
511
516
  StringType,
512
517
  IntegerType,
@@ -538,8 +543,9 @@ def save_as_delta_table(
538
543
  f"{icons.red_dot} Invalid 'delta_table_name'. Delta tables in the lakehouse cannot have spaces in their names."
539
544
  )
540
545
 
541
- dataframe.columns = dataframe.columns.str.replace(" ", "_")
542
- spark = SparkSession.builder.getOrCreate()
546
+ dataframe.columns = [col.replace(" ", "_") for col in dataframe.columns]
547
+
548
+ spark = _create_spark_session()
543
549
 
544
550
  type_mapping = {
545
551
  "string": StringType(),
@@ -1248,7 +1254,6 @@ def _get_column_aggregate(
1248
1254
  default_value: int = 0,
1249
1255
  ) -> int:
1250
1256
 
1251
- from pyspark.sql import SparkSession
1252
1257
  from pyspark.sql.functions import approx_count_distinct
1253
1258
  from pyspark.sql import functions as F
1254
1259
 
@@ -1257,7 +1262,7 @@ def _get_column_aggregate(
1257
1262
  lakehouse_id = resolve_lakehouse_id(lakehouse, workspace)
1258
1263
  path = create_abfss_path(lakehouse_id, workspace_id, table_name)
1259
1264
 
1260
- spark = SparkSession.builder.getOrCreate()
1265
+ spark = _create_spark_session()
1261
1266
  df = spark.read.format("delta").load(path)
1262
1267
 
1263
1268
  if function in {"COUNTDISTINCT", "DISTINCTCOUNT"}:
@@ -1591,3 +1596,73 @@ def _print_success(item_name, item_type, workspace_name, action="created"):
1591
1596
  )
1592
1597
  else:
1593
1598
  raise NotImplementedError
1599
+
1600
+
1601
+ def _pure_python_notebook() -> bool:
1602
+
1603
+ from sempy.fabric._environment import _on_jupyter
1604
+
1605
+ return _on_jupyter()
1606
+
1607
+
1608
+ def _create_spark_session():
1609
+
1610
+ if _pure_python_notebook():
1611
+ raise ValueError(
1612
+ f"{icons.red_dot} This function is only available in a PySpark notebook."
1613
+ )
1614
+
1615
+ from pyspark.sql import SparkSession
1616
+
1617
+ return SparkSession.builder.getOrCreate()
1618
+
1619
+
1620
+ def _read_delta_table(path: str):
1621
+
1622
+ spark = _create_spark_session()
1623
+
1624
+ return spark.read.format("delta").load(path)
1625
+
1626
+
1627
+ def _delta_table_row_count(table_name: str) -> int:
1628
+
1629
+ spark = _create_spark_session()
1630
+
1631
+ return spark.table(table_name).count()
1632
+
1633
+
1634
+ def _run_spark_sql_query(query):
1635
+
1636
+ spark = _create_spark_session()
1637
+
1638
+ return spark.sql(query)
1639
+
1640
+
1641
+ def _mount(lakehouse, workspace) -> str:
1642
+ """
1643
+ Mounts a lakehouse to a notebook if it is not already mounted. Returns the local path to the lakehouse.
1644
+ """
1645
+
1646
+ import notebookutils
1647
+
1648
+ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace=workspace)
1649
+ (lakehouse_name, lakehouse_id) = resolve_lakehouse_name_and_id(
1650
+ lakehouse=lakehouse, workspace=workspace
1651
+ )
1652
+
1653
+ lake_path = create_abfss_path(lakehouse_id, workspace_id)
1654
+ mounts = notebookutils.fs.mounts()
1655
+ mount_point = f"/{workspace_name.replace(' ', '')}{lakehouse_name.replace(' ', '')}"
1656
+ if not any(i.get("source") == lake_path for i in mounts):
1657
+ # Mount lakehouse if not mounted
1658
+ notebookutils.fs.mount(lake_path, mount_point)
1659
+ print(
1660
+ f"{icons.green_dot} Mounted the '{lakehouse_name}' lakehouse within the '{workspace_name}' to the notebook."
1661
+ )
1662
+
1663
+ mounts = notebookutils.fs.mounts()
1664
+ local_path = next(
1665
+ i.get("localPath") for i in mounts if i.get("source") == lake_path
1666
+ )
1667
+
1668
+ return local_path