Forum Discussion
Checking if semantic model is composite
- 9 months ago
kinsin5 As far as I know, there isn’t a first-class “IsComposite” flag in SemPy right now; you infer it from things like partition storage modes and data source types.
Below is a pattern that might work in Fabric notebooks.
1. Basic semantic model metadata with SemPy
From a Fabric notebook:
%pip install semantic-link -q %load_ext sempy import sempy.fabric as fabricTypical metadata calls for a single semantic model:
dataset = "My Semantic Model" workspace = "My Workspace" # or workspace ID # Tables / columns / measures tables = fabric.list_tables(dataset=dataset, workspace=workspace) columns = fabric.list_columns(dataset=dataset, workspace=workspace) measures = fabric.list_measures(dataset=dataset, workspace=workspace) # Relationships relationships = fabric.list_relationships(dataset=dataset, workspace=workspace) # Partitions (this is where storage mode lives) partitions = fabric.list_partitions(dataset=dataset, workspace=workspace, extended=True) # Data sources (for checking Analysis Services / other PBI models, etc.) datasources = fabric.list_datasources(dataset=dataset, workspace=workspace)These all come back as pandas DataFrames, so you can query them however you like.
2. Listing all composite models across all Fabric workspaces
A semantic model is “composite” if, for example:
It has different storage modes across tables/partitions (Import + DirectQuery, DirectLake + Import, etc.), or
It uses another semantic model / AAS as a data source (via AnalysisServices / PowerBI source types).
SemPy gives you enough to derive that:
fabric.list_workspaces() -> all workspaces you can see
fabric.list_datasets(workspace=...) -> semantic models in a workspace
fabric.list_partitions(...) + fabric.list_datasources(...) -> storage modes & source types per model
So maybe something along these lines:
%load_ext sempy import sempy.fabric as fabric import pandas as pd def classify_composite_for_dataset(workspace_name: str, dataset_name: str) -> dict: # Partitions: storage mode per table parts = fabric.list_partitions( workspace=workspace_name, dataset=dataset_name, extended=True ) if parts is None or parts.empty: modes = set() else: modes = set(parts["Mode"].dropna().unique()) # Data sources: where the data is coming from try: ds_df = fabric.list_datasources( workspace=workspace_name, dataset=dataset_name ) except Exception: ds_df = pd.DataFrame() if ds_df is None or ds_df.empty: source_types = set() else: # Column name is typically "Type" in list_datasources output source_types = set(ds_df["Type"].dropna().unique()) # Heuristics for "composite" has_import = "Import" in modes has_directquery = "DirectQuery" in modes has_directlake = "DirectLake" in modes has_multiple_modes = len(modes) > 1 # Remote semantic models / AAS as datasources uses_remote_semantic = any( t in source_types for t in ["AnalysisServices", "PowerBI"] ) is_composite = ( has_multiple_modes or (has_directquery and (modes - {"DirectQuery"})) # DQ + something else or (has_directlake and (has_import or "Dual" in modes)) or uses_remote_semantic ) return { "Workspace": workspace_name, "Dataset": dataset_name, "Modes": ", ".join(sorted(modes)) if modes else "", "SourceTypes": ", ".join(sorted(source_types)) if source_types else "", "IsComposite": is_composite, } def list_composite_models(include_personal_workspaces: bool = False) -> pd.DataFrame: workspaces = fabric.list_workspaces() # all workspaces you can access # Optional: filter out personal workspaces if the column exists if not include_personal_workspaces and "Type" in workspaces.columns: workspaces = workspaces[workspaces["Type"] != "Personal"] rows = [] for _, ws in workspaces.iterrows(): ws_name = ws["Name"] # could also use ws["Id"]; sempy accepts name or ID in most functions datasets = fabric.list_datasets(workspace=ws_name) if datasets is None or datasets.empty: continue for _, ds in datasets.iterrows(): ds_name = ds["Name"] info = classify_composite_for_dataset(ws_name, ds_name) rows.append(info) return pd.DataFrame(rows) # Run the scan all_models = list_composite_models(include_personal_workspaces=False) # Only composite semantic models composite_models = all_models[all_models["IsComposite"]].copy() display(composite_models)That composite_models DataFrame will give you, per semantic model:
Workspace name
Dataset / semantic model name
All storage modes it uses (from list_partitions)
All data source types (from list_datasources)
A boolean IsComposite based on the rules above
You can then:
Write it to a Lakehouse table and build a governance semantic model on top,
Or slice and dice it directly in the notebook.
Notes:
Permissions: You’ll only see workspaces and semantic models you have rights to. For full-tenant scanning you typically combine this with admin or scanner APIs (often via Semantic Link Labs, not plain SemPy).
Definition of “composite”: If you want to be super strict (e.g., “DQ over PBI dataset + local tables only”), tweak the heuristic in classify_composite_for_dataset.
Performance: On big tenants, you may want to:
Filter list_workspaces() first (e.g., only dedicated capacities, only certain domains), and/or
Parallelize per-workspace scans with multiprocessing in Python.
Hi kinsin5
Step 1: Install sempy (if not already installed)
pip install sempy
---
Step 2: Connect to Power BI Service
from sempy import Workspace
# Connect to all workspaces
workspaces = Workspace.list_workspaces()
---
Step 3: Loop through workspaces and datasets
for ws in workspaces:
print(f"Workspace: {ws.name}")
datasets = ws.datasets # List all datasets in the workspace
for ds in datasets:
# Check if the dataset is a composite model
if ds.is_composite:
print(f" Composite Model: {ds.name}")
Explanation:
Workspace.list_workspaces() → gets all workspaces you have access to.
ws.datasets → lists all datasets inside the workspace.
ds.is_composite → identifies composite models (direct query + import, etc.).
---
Step 4: Optional – Export results to CSV
import csv
results = []
for ws in workspaces:
for ds in ws.datasets:
if ds.is_composite:
results.append([ws.name, ds.name])
# Save to CSV
with open("composite_models.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["Workspace", "Dataset"])
writer.writerows(results)
If this solution helped you, please mark it as the accepted answer so it can help others as well.