Forum Discussion
Checking if semantic model is composite
- 8 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,
I would also take a moment to thank GeraldGEmerick , for actively participating in the community forum and for the solutions you’ve been sharing in the community forum. Your contributions make a real difference.
I wanted to check if you had the opportunity to review the information provided. Please feel free to contact us if you have any further questions.
Regards,
Community Support Team.