From c017bc519e82beee0b815f9981f13853c648f4fe Mon Sep 17 00:00:00 2001 From: Hussain Sultan Date: Mon, 4 May 2026 07:18:54 -0400 Subject: [PATCH] refactor: audit broad except sites in ops.py Walked all 26 except Exception blocks. Result: - 4 narrowed to specific types: 3 ImportError sites (xorq imports inside try blocks, only ImportError makes sense) and 1 (AttributeError, TypeError) for _is_mean_expr's duck-type probe. - 8 silent fallbacks now log at logger.debug with exc_info so backend- compat / probing failures are observable in development without flooding production logs. These cover from_ibis fallback, walk_nodes on plain ibis trees, join-defer eligibility checks, full-join chasm fallback, and column-requirement traversal fallbacks. - The remaining 22 broad excepts are intentional: predicate-probing loops that test whether a filter applies to a given table, repr fallbacks that must never raise, Result-pattern wrappers that re-encapsulate any exception, and the dim-resolution loop that recovers via _extract_missing_column_name. No behavior change. 930 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/boring_semantic_layer/ops.py | 37 ++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/boring_semantic_layer/ops.py b/src/boring_semantic_layer/ops.py index a73189e..695a2a4 100644 --- a/src/boring_semantic_layer/ops.py +++ b/src/boring_semantic_layer/ops.py @@ -243,6 +243,13 @@ def _ensure_xorq_table(table): return from_ibis(table) except Exception: + # Backend isn't supported by xorq's map_ibis registry (e.g. + # Databricks). Fall back so plain-ibis paths can still execute. + logger.debug( + "from_ibis failed for %s; using plain ibis table", + type(table).__module__, + exc_info=True, + ) return table return table @@ -256,7 +263,7 @@ def _rebind_to_backend(expr, target_backend): """ try: from ._xorq import relations as xorq_rel - except Exception: + except ImportError: return expr def _recreate(op, _kwargs, **overrides): @@ -289,12 +296,14 @@ def _rebind_to_canonical_backend(expr): """ try: from ._xorq import relations as xorq_rel, walk_nodes - except Exception: + except ImportError: return expr try: db_tables = list(walk_nodes((xorq_rel.DatabaseTable,), expr)) except Exception: + # walk_nodes can't traverse plain ibis trees; treat as no-op. + logger.debug("walk_nodes failed on plain ibis expr", exc_info=True) return expr canonical = db_tables[0].source if db_tables else None @@ -1874,6 +1883,7 @@ def walk(node): return left_cols = tuple(sorted(join_keys_result.left_columns)) except Exception: + logger.debug("join-defer eligibility check failed", exc_info=True) return # Identify which group-by dimensions from the right table will be deferred @@ -1986,7 +1996,7 @@ def _is_mean_expr(expr): """Check if an ibis expression is a Mean/Average reduction.""" try: return isinstance(expr.op(), _reductions_for_expr(expr).Mean) - except Exception: + except (AttributeError, TypeError): return False @@ -2438,7 +2448,9 @@ def _to_untagged_with_preagg( pred_expr = _resolve_expr(pred_fn, resolver) tbl = tbl.filter(pred_expr) except Exception: - tbl = None # chasm / column collision – work without full join + # chasm / column collision – work without full join + logger.debug("full-join construction failed; using per-table path", exc_info=True) + tbl = None # --- 2. Build aggregation plan --- if tbl is not None: @@ -4065,13 +4077,15 @@ def _rebind_join_backends(left_tbl, right_tbl): """ try: from ._xorq import relations as xorq_rel, walk_nodes - except Exception: + except ImportError: return left_tbl, right_tbl # Find a canonical backend from the left tree. try: db_tables = list(walk_nodes((xorq_rel.DatabaseTable,), left_tbl)) except Exception: + # Plain ibis tree — no DatabaseTable nodes for xorq to walk. + logger.debug("walk_nodes failed on plain ibis left side", exc_info=True) return left_tbl, right_tbl canonical = db_tables[0].source if db_tables else None @@ -5257,7 +5271,12 @@ def _extract_requirements_from_keys( if root.name: requirements = requirements.with_column(root.name, key) except Exception: - # Fallback: assume key name is column name + logger.debug( + "dimension graph traversal failed for %r; " + "treating key name as column name", + key, + exc_info=True, + ) for root in all_roots: if root.name: requirements = requirements.with_column(root.name, key) @@ -5296,6 +5315,12 @@ def _extract_requirements_from_measures( if root.name: requirements = requirements.with_columns(root.name, actual_cols) except Exception: + logger.debug( + "measure graph traversal failed for %r; " + "falling back to name-based column inference", + measure_name, + exc_info=True, + ) # Conservative fallback: if measure name looks like a column, include it if measure_name.isidentifier(): for root in all_roots: