-
Notifications
You must be signed in to change notification settings - Fork 0
Expose _repr_html_ Building Blocks
#3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/anndata/_repr/__init__.py (1)
154-171: Remove unusednoqadirectives.Static analysis indicates
E402is not enabled, so these# noqa: E402comments are unnecessary and add noise.Apply this diff:
# Building blocks for packages that want to create their own _repr_html_ # These allow reusing anndata's styling while building custom representations -from anndata._repr.css import get_css # noqa: E402 -from anndata._repr.javascript import get_javascript # noqa: E402 -from anndata._repr.utils import ( # noqa: E402 +from anndata._repr.css import get_css +from anndata._repr.javascript import get_javascript +from anndata._repr.utils import ( escape_html, format_memory_size, format_number, ) # HTML rendering helpers for building custom sections -from anndata._repr.html import ( # noqa: E402 +from anndata._repr.html import ( render_formatted_entry, render_section, ) # Inline styles for graceful degradation (from single source of truth) -from anndata._repr.constants import STYLE_HIDDEN # noqa: E402 +from anndata._repr.constants import STYLE_HIDDENtests/visual_inspect_repr_html.py (2)
434-458: Unusedcontainer_idparameter is acceptable for demonstration code.The static analysis flags
container_idas unused. However, this is demonstration code showing the API pattern - a real implementation might use it for scoping. Consider adding a brief comment if clarity is desired.
1066-1066: Remove unusednoqadirective.Static analysis indicates
PLR0915andPLR0912are not enabled.Apply this diff:
-def main(): # noqa: PLR0915, PLR0912 +def main():src/anndata/_repr/html.py (1)
1547-1638: Well-designed public API with comprehensive documentation.The
render_sectionfunction provides a clean, flexible interface for building sections. The parameters cover common use cases (custom count strings, collapse behavior, section IDs) while maintaining sensible defaults.Note: Static analysis flags the
noqa: PLR0913as unused. Consider removing it:-def render_section( # noqa: PLR0913 +def render_section(
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/anndata/_repr/__init__.py(2 hunks)src/anndata/_repr/constants.py(1 hunks)src/anndata/_repr/html.py(9 hunks)src/anndata/_repr/registry.py(1 hunks)tests/visual_inspect_repr_html.py(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/anndata/_repr/__init__.py (4)
src/anndata/_repr/css.py (1)
get_css(14-18)src/anndata/_repr/javascript.py (1)
get_javascript(17-41)src/anndata/_repr/utils.py (3)
escape_html(270-272)format_memory_size(293-305)format_number(308-315)src/anndata/_repr/html.py (2)
render_formatted_entry(406-518)render_section(1547-1638)
tests/visual_inspect_repr_html.py (5)
src/anndata/_repr/registry.py (12)
FormatterContext(105-144)doc_url(264-266)FormattedEntry(91-101)FormattedOutput(56-87)FormatterRegistry(342-420)SectionFormatter(205-280)TypeFormatter(147-202)tooltip(269-271)section_name(244-246)get_section_formatter(414-416)register_type_formatter(358-366)register_section_formatter(368-370)src/anndata/_repr/css.py (1)
get_css(14-18)src/anndata/_repr/javascript.py (1)
get_javascript(17-41)src/anndata/_repr/html.py (3)
render_formatted_entry(406-518)render_section(1547-1638)generate_repr_html(124-242)src/anndata/_repr/formatters.py (12)
can_format(51-52)can_format(88-89)can_format(127-149)can_format(228-229)can_format(295-298)can_format(321-324)format(54-80)format(91-107)format(151-204)format(231-287)format(300-313)format(326-342)
src/anndata/_repr/html.py (2)
src/anndata/_repr/registry.py (1)
FormattedEntry(91-101)src/anndata/_repr/utils.py (1)
escape_html(270-272)
🪛 Ruff (0.14.7)
src/anndata/_repr/__init__.py
156-156: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
157-157: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
158-158: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
165-165: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
171-171: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
tests/visual_inspect_repr_html.py
434-434: Unused method argument: container_id
(ARG002)
525-525: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
584-584: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
647-647: Unused method argument: context
(ARG002)
664-664: Unused method argument: context
(ARG002)
671-671: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
1066-1066: Unused noqa directive (non-enabled: PLR0915, PLR0912)
Remove unused noqa directive
(RUF100)
src/anndata/_repr/html.py
406-406: Unused function argument: section
(ARG001)
452-452: Docstring contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF002)
1547-1547: Unused noqa directive (non-enabled: PLR0913)
Remove unused noqa directive
(RUF100)
🔇 Additional comments (13)
src/anndata/_repr/registry.py (1)
77-81: LGTM - Clean addition ofmeta_contentfield.The new optional field with
Nonedefault maintains backward compatibility. The docstring clearly describes its purpose for the meta column.src/anndata/_repr/constants.py (1)
23-27: LGTM - Well-organized style constants.Centralizing these inline styles provides a single source of truth and enables consistent graceful degradation across rendering components.
src/anndata/_repr/__init__.py (1)
200-208: LGTM - Public API surface is well-organized.The new exports are logically grouped under "Building blocks for custom repr_html implementations" and provide the necessary components for downstream packages.
tests/visual_inspect_repr_html.py (4)
272-274: LGTM - Good addition ofdoc_urlproperty.Adding documentation URL to the MuData section formatter improves the help icon functionality.
362-432: Well-structured demonstration of the building blocks API.The
MockSpatialDataclass effectively demonstrates how downstream packages can build custom_repr_html_implementations using anndata's exported utilities. The pattern of:
get_css()for styling- Container with unique ID
- Custom header
render_section()+render_formatted_entry()for sectionsget_javascript()for interactivityprovides a clear template for other packages.
562-597: Good demonstration of nested AnnData embedding.The tables section effectively shows how to use
generate_repr_html()withis_expandable=Trueto embed fully interactive nested AnnData objects.
1616-1637: LGTM - Test case properly exercises the new public API.The SpatialData test case validates the full integration of building blocks: CSS/JS reuse, section rendering, entry formatting, nested AnnData, and custom sections via FormatterRegistry.
src/anndata/_repr/html.py (6)
32-36: LGTM - Centralized style constant imports.Importing from
constants.pyestablishes a single source of truth for inline styles used in graceful degradation.
386-403: Good refactor to userender_sectionfor consistency.The custom section rendering now delegates to the public
render_sectionfunction, ensuring consistent structure and behavior across all sections.
406-459: Well-documented public API with clear examples.The
render_formatted_entryfunction provides a clean interface for external packages. Thesectionparameter is appropriately documented as "for future use" - this is a reasonable forward-compatibility pattern.Note: Static analysis flags
sectionas unused, but this is intentional per the docstring.
483-488: Minor: Prefer usinglist()constructor for clarity.The existing code works correctly, but using
list()makes the intent clearer when copying a list.- warnings_list = list(output.warnings) + warnings_list = output.warnings.copy() if isinstance(output.warnings, list) else list(output.warnings)Actually, the current code is fine since
output.warningsis typed aslist[str]-list()creates a shallow copy. No change needed.
502-506: LGTM - Clean meta column rendering.The meta column now renders
output.meta_contentwhen available, supporting the newFormattedOutput.meta_contentfield.
740-757: Good consolidation usingrender_section.The dataframe section renderer now collects rows and delegates to
render_section, eliminating duplicate section structure code.
|
Feeling like this PR might be the version we want to adopt, I started implementing other features like case sensitive search, regular expression, and better search of nested anndatas only here. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis PR expands AnnData's HTML representation system by introducing a comprehensive public API for rendering components and utilities. Changes include improved exception handling with logging, new CSS style constants, enhanced search functionality with regex/case sensitivity, support for multi-section formatters, and defensive attribute checks for raw objects. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Key areas requiring careful attention:
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
src/anndata/_repr/utils.py (1)
259-275: Missing defensive checks for.obsand.varin the iteration loop.While line 259-261 adds a guard for missing
.uns, the iteration on line 269 still accessesadata.obsandadata.vardirectly. For consistency withget_matching_column_colors, consider addinghasattrchecks here as well.Apply this diff to add defensive checks:
color_key = f"{column_name}_colors" if color_key not in adata.uns: return None colors = adata.uns[color_key] - for df in (adata.obs, adata.var): + dfs = [] + if hasattr(adata, "obs"): + dfs.append(adata.obs) + if hasattr(adata, "var"): + dfs.append(adata.var) + + for df in dfs: if column_name in df.columns and hasattr(df[column_name], "cat"): n_cats = len(df[column_name].cat.categories) if len(colors) != n_cats: return f"Color mismatch: {len(colors)} colors for {n_cats} categories"src/anndata/_repr/css.py (1)
439-446: Consider using CSS variables for error colors in dark mode.The hardcoded
#dc3545for regex error states doesn't adapt to dark mode. For consistency with the rest of the theme system, consider using the existing--anndata-error-colorvariable./* Regex error indicator */ .anndata-repr .adata-search-box.regex-error { - border-color: #dc3545; + border-color: var(--anndata-error-color); } .anndata-repr .adata-search-box.regex-error .adata-search-input { - background: rgba(220, 53, 69, 0.05); + background: var(--anndata-error-bg); }src/anndata/_repr/__init__.py (1)
139-172: Remove unusednoqadirectives flagged by static analysis.The
# noqa: E402comments on lines 139, 143, 147, 157, and 172 are flagged as unnecessary by Ruff. These were likely added when E402 was enabled, but the rule appears to be disabled now.-from anndata._repr.constants import STYLE_HIDDEN # noqa: E402 +from anndata._repr.constants import STYLE_HIDDEN # Building blocks for packages that want to create their own _repr_html_ # These allow reusing anndata's styling while building custom representations -from anndata._repr.css import get_css # noqa: E402 +from anndata._repr.css import get_css # HTML rendering helpers for building custom sections # UI component helpers (search box, fold icon, badges, etc.) -from anndata._repr.html import ( # noqa: E402 # noqa: E402 +from anndata._repr.html import ( generate_repr_html, render_badge, render_copy_button, render_fold_icon, render_formatted_entry, render_header_badges, render_search_box, render_section, ) -from anndata._repr.javascript import get_javascript # noqa: E402 +from anndata._repr.javascript import get_javascript from anndata._repr.registry import ( # noqa: E402 ... ) -from anndata._repr.utils import ( # noqa: E402 +from anndata._repr.utils import ( escape_html, format_memory_size, format_number, )tests/visual_inspect_repr_html.py (1)
1163-1163: Remove unused noqa directive.The
# noqa: PLR0915, PLR0912directive is unnecessary as these rules are not enabled in the current Ruff configuration.-def main(): # noqa: PLR0915, PLR0912 +def main():src/anndata/_repr/html.py (4)
106-112: Consider logging suppressed exceptions for debugging.The try-except-pass pattern silently swallows exceptions. While this is intentional to avoid breaking the repr on corrupted data, logging at DEBUG level would aid troubleshooting without impacting users.
try: mapping = getattr(adata, attr, None) if mapping is not None: all_names.extend(mapping.keys()) except Exception: # noqa: BLE001 - # Skip sections that fail to access (will show error during rendering) - pass + # Skip sections that fail to access (will show error during rendering) + import logging + logging.getLogger(__name__).debug( + "Failed to access %s for field width calculation", attr, exc_info=True + )Alternatively, if logging is not desired, keeping the pass is acceptable since errors will surface during actual rendering.
425-478: Unusedsectionparameter in public API.The
sectionparameter is documented as "for future use" but is currently unused. For a public API, consider either:
- Removing it until needed (simpler API, can add later with default)
- Prefixing with
_to indicate it's reserved:_section- Adding a TODO comment explaining the planned use case
Since this is a new public API, removing unused parameters keeps the surface area minimal.
-def render_formatted_entry(entry: FormattedEntry, section: str = "") -> str: +def render_formatted_entry(entry: FormattedEntry) -> str: """ Render a FormattedEntry as a table row. ... Parameters ---------- entry A FormattedEntry containing the key and FormattedOutput - section - Optional section name (for future use) ... """If keeping for future compatibility, add
_prefix:-def render_formatted_entry(entry: FormattedEntry, section: str = "") -> str: +def render_formatted_entry(entry: FormattedEntry, _section: str = "") -> str:
1636-1640: Callable check may not work as intended.The condition
not callable(val)checks if the value itself is callable, butvalis already the result ofgetattr(adata, attr). Ifattris a method,valwould be a bound method (callable). However, methods are typically excluded by thecallable(getattr(adata, attr))check conceptually, but the current code first gets the value then checks if it's mapping-like.The logic works because methods don't typically have
keys()and__getitem__, but thenot callable(val)seems redundant here since you're checking for mapping-like properties.Consider simplifying:
if isinstance(val, Mapping) or ( hasattr(val, "keys") and hasattr(val, "__getitem__") - and not callable(val) ):Or clarify the intent with a comment if the check is needed for edge cases.
2023-2033: Remove unused noqa directive from render_section.The
# noqa: PLR0913directive is flagged as unused since this rule is not enabled. The function has appropriate parameters for its public API purpose.-def render_section( # noqa: PLR0913 +def render_section( name: str,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/anndata/_core/anndata.py(1 hunks)src/anndata/_repr/__init__.py(3 hunks)src/anndata/_repr/constants.py(1 hunks)src/anndata/_repr/css.py(2 hunks)src/anndata/_repr/html.py(22 hunks)src/anndata/_repr/javascript.py(6 hunks)src/anndata/_repr/registry.py(4 hunks)src/anndata/_repr/utils.py(3 hunks)tests/visual_inspect_repr_html.py(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/anndata/_core/anndata.py (1)
src/anndata/_warnings.py (1)
warn(52-65)
src/anndata/_repr/utils.py (3)
tests/test_repr_html.py (1)
adata(69-80)src/anndata/_core/anndata.py (6)
obs(889-891)obs(894-895)obs(898-899)var(912-914)var(917-918)var(921-922)src/anndata/_core/xarray.py (1)
columns(254-266)
src/anndata/_repr/__init__.py (4)
src/anndata/_repr/css.py (1)
get_css(14-18)src/anndata/_repr/html.py (8)
generate_repr_html(128-246)render_badge(642-677)render_copy_button(611-639)render_fold_icon(590-608)render_formatted_entry(425-537)render_header_badges(680-722)render_search_box(548-587)render_section(2023-2114)src/anndata/_repr/javascript.py (1)
get_javascript(17-41)src/anndata/_repr/utils.py (3)
escape_html(278-280)format_memory_size(301-313)format_number(316-323)
src/anndata/_repr/html.py (2)
src/anndata/_repr/registry.py (2)
FormattedEntry(91-101)FormatterContext(105-144)src/anndata/_repr/utils.py (2)
escape_html(278-280)format_number(316-323)
🪛 Ruff (0.14.8)
tests/visual_inspect_repr_html.py
275-275: Unused method argument: obj
(ARG002)
278-278: Unused method argument: obj
(ARG002)
278-278: Unused method argument: context
(ARG002)
598-598: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
657-657: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
724-724: Unused method argument: context
(ARG002)
743-743: Unused method argument: context
(ARG002)
748-748: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
1163-1163: Unused noqa directive (non-enabled: PLR0915, PLR0912)
Remove unused noqa directive
(RUF100)
src/anndata/_repr/__init__.py
139-139: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
143-143: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
147-147: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
157-157: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
172-172: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
src/anndata/_repr/html.py
110-112: try-except-pass detected, consider logging the exception
(S110)
425-425: Unused function argument: section
(ARG001)
471-471: Docstring contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF002)
1713-1713: Consider moving this statement to an else block
(TRY300)
1724-1725: try-except-pass detected, consider logging the exception
(S110)
1729-1730: try-except-pass detected, consider logging the exception
(S110)
1778-1778: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
1868-1868: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?
(RUF001)
2023-2023: Unused noqa directive (non-enabled: PLR0913)
Remove unused noqa directive
(RUF100)
🔇 Additional comments (25)
src/anndata/_repr/utils.py (2)
210-212: LGTM: Defensive check for objects without.uns.Good addition to handle
Rawobjects and other AnnData-like objects that may not have the.unsattribute.
222-225: LGTM: Defensive attribute checks for.obsand.var.These guards correctly prevent
AttributeErrorwhen processing objects that don't implement standard AnnData attributes.src/anndata/_core/anndata.py (1)
602-609: LGTM: Improved error handling with informative warning.The change from silent fallback to emitting a
UserWarningwith exception details is a good improvement for debugging HTML rendering issues while still maintaining graceful degradation.src/anndata/_repr/constants.py (1)
23-27: LGTM: Centralized inline style constants.Good addition of style constants for graceful no-JS degradation. Centralizing these in the constants module is the right approach for maintainability.
src/anndata/_repr/css.py (1)
360-388: LGTM: Well-structured search box container refactor.The move from a single input to a flex container with input and toggles is cleanly implemented. The
focus-withinpseudo-class correctly highlights the container when any child has focus.src/anndata/_repr/javascript.py (4)
55-63: LGTM: Updated visibility initialization for new search UI elements.Properly shows the search box container and toggle buttons when JavaScript is enabled.
116-165: LGTM: Well-implemented search state management with accessibility.The toggle buttons correctly update ARIA pressed states and the debounced search trigger prevents excessive filtering. The
stopPropagation()calls appropriately prevent button clicks from bubbling to parent handlers.
168-190: LGTM: Robust regex matching with graceful error handling.The
matchesQueryhelper properly handles:
- Empty queries (returns true)
- Regex mode with invalid patterns (catches exception, shows error state, returns false instead of crashing)
- Case sensitivity for both regex and plain text modes
The error class management on
searchBoxprovides good visual feedback.
236-285: LGTM: Ancestor visibility and X-entry management.The nested entry ancestor reveal logic has appropriate safety bounds (maxIterations = 20) to prevent runaway loops. The X-entry visibility logic correctly hides orphaned entries when filtering is active and resets them when the query is cleared.
src/anndata/_repr/registry.py (3)
77-81: LGTM: Enhanced FormattedOutput with meta_content field.The addition of
meta_contentfor the meta column and the clarifiedhtml_contentdocstring appropriately extend the formatting capabilities.
268-276: LGTM: Well-designed multi-section support.The
section_namesproperty with a sensible default maintains backward compatibility while enabling a single formatter to handle multiple sections. This is a clean extension point.
398-401: LGTM: Registration iterates over all section names.Correctly registers the formatter for each name in
section_names, enabling the multi-section pattern.src/anndata/_repr/__init__.py (2)
138-176: LGTM: Well-organized public API surface for custom HTML representations.The imports are properly grouped by purpose with helpful comments explaining their use case. This enables external packages like SpatialData to build custom
_repr_html_implementations while reusing anndata's styling and interactivity.
205-219: LGTM: Comprehensive all exports for the building blocks API.The new exports are well-organized with clear category comments and provide everything needed for external packages to build custom representations.
tests/visual_inspect_repr_html.py (4)
391-465: Well-structured demonstration of the public API pattern.The
MockSpatialData._repr_html_method clearly documents the 7-step pattern for building custom HTML representations. This serves as excellent reference documentation for external packages.The
×characters flagged by static analysis (RUF001) are intentional multiplication signs for dimension display (e.g., "100 × 50") and are correct for visual representation.
743-751: Consider edge case in transform entry generation.The loop
range(len(cs) - 1)works correctly whenlen(cs) >= 2(due toshould_showcheck), but ifcoordinate_systemscontains only 1 item,should_showreturnsFalseso this is safe. The unusedcontextparameter is required by theSectionFormatter.get_entriesinterface.
1885-1898: FailingMapping test helper is well-designed for error simulation.The
FailingMappingclass correctly simulates I/O errors by raising onkeys()and__iter__while returning1from__len__to trigger rendering attempts. This effectively tests the error handling paths in section rendering.
263-279: MuDataInternalSectionsFormatter has correct suppression logic.The
section_namestuple approach for multi-section suppression is clean. The unusedobjandcontextparameters are required by theSectionFormatterinterface, so the ARG002 static analysis hints are false positives. The registry explicitly iterates over allsection_namesduring registration (seeregister_section_formatter()insrc/anndata/_repr/registry.py), so all three names—obsmap,varmap,axis—are properly registered to the formatter.src/anndata/_repr/html.py (7)
548-587: Well-designed public API for search box rendering.The
render_search_boxfunction properly handles:
- Unique ID generation for label association
- Hidden by default with JS-enabled show
- Accessibility attributes (aria-label, aria-pressed)
- Filter indicator span for visual feedback
Good use of the
STYLE_HIDDENconstant for graceful degradation.
642-678:render_badgevariant documentation is helpful.The docstring clearly documents the built-in variants with their semantic meanings. The implementation correctly handles the base class concatenation and optional tooltip.
1709-1731: Safe attribute access patterns are appropriate for error resilience.The
_safe_get_attrhelper and_get_raw_meta_partsuse try-except patterns to handle corrupted or inaccessible data gracefully. This is important for the repr to remain functional even with problematic data.The static analysis suggestion (TRY300) to move line 1713 to an
elseblock is a minor style improvement:def _safe_get_attr(obj, attr: str, default="?"): """Safely get an attribute with fallback.""" try: val = getattr(obj, attr, None) - return val if val is not None else default except Exception: # noqa: BLE001 return default + else: + return val if val is not None else defaultHowever, the current form is readable and functionally correct.
1734-1810: Raw section rendering is well-structured with defensive checks.The implementation properly:
- Safely accesses
rawwithgetattrfallback- Uses
_safe_get_attrfor dimensions to handle corrupted data- Checks
can_expandbefore generating nested content- Builds meta info with
_get_raw_meta_partsthat handles failures gracefullyThe
×character (RUF001) is intentional for visual dimension display.
2023-2113: Publicrender_sectionAPI is well-designed.The function provides a clean interface for external packages with:
- Sensible parameter defaults
- Clear documentation with examples
- Consistent use of internal helpers (_render_section_header, _render_empty_section)
- Proper HTML escaping throughout
The parameter count is appropriate for the flexibility this API provides.
323-341: Good error handling wrapper for section rendering.The try-except wrapper around section rendering ensures the HTML repr remains functional even when individual sections fail (e.g., due to corrupted data or I/O errors). The
_render_error_entryprovides user-visible feedback about what failed.
1879-1897: No compatibility issue detected. The utility functions already handle Raw objects gracefully.Both
get_matching_column_colors()andcheck_color_category_mismatch()explicitly checkif not hasattr(adata, "uns")and returnNonefor objects without.uns(with code comments noting "Handle objects without .uns (e.g., Raw)"). The code will function correctly with Raw objects passed to_render_dataframe_section().Likely an incorrect or invalid review comment.
Export HTML Repr Building Blocks for External Packages
Modifies #2236 (Rich HTML representation for AnnData)
Summary
This PR exports building blocks from
anndata._reprthat allow external packages like SpatialData to build their own_repr_html_while reusing anndata's CSS, JavaScript, and interactive features.This is an alternative to the
ObjectFormatterapproach explored in settylab/anndata#2. Instead of adding new extension points to anndata, this approach lets SpatialData implement its own_repr_html_while reusing anndata's styling infrastructure.Exported Building Blocks
Live Demo
Live interactive demo (see test cases 20, 23 for SpatialData and serialization warnings) | Gist source
SpatialData Example
The visual test includes a complete
MockSpatialDataexample (~280 lines) demonstrating:coordinate_systems:instead ofobs_names/var_names)Comparison: Building Blocks vs ObjectFormatter
_repr_html_FormatterRegistryclassWhat SpatialData Can Do
With the building blocks approach, SpatialData has two main implementation options:
Option A: Full Custom (Maximum Flexibility)
Build entire
_repr_html_from scratch using onlyget_css()andget_javascript():Option B: Use Helpers (Recommended)
Use
render_section()andrender_formatted_entry()for consistent section structure:UI Component Helpers
Use helpers instead of hardcoding CSS classes and inline styles:
This makes the code more readable and future-proof - if CSS classes change, only anndata needs updating.
Embedding Nested AnnData
Both options can use
generate_repr_html()for nested AnnData with full interactivity:FormattedOutput Columns
FormattedOutputhas two optional HTML fields for different columns:html_content- Expandable content (nested AnnData, SVG previews, etc.)meta_content- Meta column (rightmost) for data previews, dimensions, etc.Extensibility via Registry
SpatialData can reuse anndata's
FormatterRegistryclass to enable the same extensibility pattern for their own types. This supports both:SpatialData then renders these in
_repr_html_:This allows the same extension pattern that anndata uses (third-party packages registering formatters) to work for SpatialData's element types AND custom sections.
Maintenance Burden on anndata
Minimal. The exported API is:
get_css(),get_javascript()- stable, internal changes don't affect APIescape_html(),format_number(),format_memory_size()- simple, stablerender_section(),render_formatted_entry()- used internally, must remain stable anywayFormattedEntry,FormattedOutput,FormatterContext- already public for TypeFormatter/SectionFormatterFormatterRegistry,TypeFormatter,SectionFormatter- already public for anndata's own extensibilitycheck_column_name(),render_warning_icon()- for consistent serialization warningsThe key difference from ObjectFormatter: we're not adding new extension points or configuration classes (
HeaderConfig,IndexPreviewConfig). We're exposing existing classes that anndata already uses and must maintain.Note:
generate_repr_html()serves dual purpose - it's both production code for AnnData and a reference implementation that external packages can study or adapt.Style Consistency Across scverse
Both approaches achieve visual consistency through shared CSS/JS:
get_css()andget_javascript()→ same stylingWith the building blocks approach:
Test Coverage
Trade-offs
Building blocks wins:
ObjectFormatter wins:
Recommendation
The building blocks approach shifts implementation effort to SpatialData but reduces anndata's maintenance burden and gives SpatialData maximum flexibility. This may be preferable if SpatialData's needs diverge significantly from AnnData's structure.