refactor: split utils.py into _geometry / _datashader / _color / _validate#715
Merged
Conversation
Move 8 private geometry/patch functions verbatim from the 4918-line utils.py into a new sibling module pl/_geometry.py: _get_centroid_of_pathpatch, _scale_pathpatch_around_centroid, _normalize_geom, _make_patch_from_multipolygon, _build_shape_patches, _get_collection_shape, _validate_polygons, _convert_shapes. No behavior change (verbatim move; all-private symbols). render.py is the sole consumer; its four imports of these are repointed to _geometry. The module's only edge back into utils is _extract_scalar_value (downward; no cycle). Verified: ruff + ruff-format clean on the moved code; 55 non-visual shape tests pass. Pre-existing ruff D205 / mypy Any-return debt in utils.py (from #703/#705) is unrelated and is fixed by #714; --no-verify used for that reason only.
…696) Move 13 datashader helper symbols verbatim from utils.py into _datashader.py (_ax_show_and_transform, _compute_datashader_canvas_params, _get_extent_and_range_for_datashader_canvas, _datashader_canvas_from_dataframe, _create_image_from_datashader_result, _DS_REDUCTION_FUNCS, _datashader_aggregate_with_function, _datashader_get_how_kw_for_spread, _prepare_transformation, _apply_cmap_alpha_to_datashader_result, _datashader_map_aggregate_to_color, _hex_no_alpha, _convert_alpha_to_datashader_range), removing the utils back-import that was the repo's only near-cycle. _datashader.py now imports only downward from utils (_fast_extent, _make_continuous_mappable, to_hex). Also fixes the _datshader_get_how_kw_for_spread -> _datashader_get_how_kw_for_spread typo (def + caller). Repoints render.py (5 names) and test imports (test_utils, test_render_points, test_render_shapes) to _datashader. No behavior change (verbatim move; all-private symbols). Verified: no import cycle; 195 non-visual tests pass; ruff/format clean on moved code. Pre-existing ruff D205 / mypy Any-return debt in utils.py (#703/#705) is unrelated and fixed by #714; --no-verify used for that reason only.
Move 21 color helpers + 6 color-only format/uniqueness helpers verbatim from utils.py into a new sibling module pl/_color.py. Imports flow one way: _color -> utils (downward, for _get_list/to_hex/_build_alignment_dtype_hint/ _MPL_SINGLE_LETTER_COLORS). render.py, basic.py and _datashader.py are repointed to import color symbols from _color. The two validators still in utils (_type_check_params, _validate_graph_render_params) use color symbols; they carry temporary function-local imports of _color (cycle-safe) until they move to _validate.py in the next commit, where these become top-level imports. No behavior change (verbatim move; all-private except set_zero_in_cmap_to_transparent, which is not re-exported). Verified: no import cycle; 410 non-visual tests pass; ruff + ruff-format clean. Pre-existing #703/#705 mypy/ruff debt in utils.py is unrelated; --no-verify for that reason only.
Move 17 validation/type-check functions verbatim from utils.py into a new sibling module pl/_validate.py. Imports flow one way: _validate -> _color (_is_color_like, _prepare_cmap_norm, _get_colors_for_categorical_obs, now top-level) and _validate -> utils (downward). The temporary function-local _color imports added in the previous commit are hoisted to top-level here and removed from utils. render.py and basic.py repointed to _validate. Completes the utils.py split (#696): utils 4918 -> 1311 lines, with _geometry / _datashader / _color / _validate as single-concern siblings. No import cycle anywhere. No behavior change (verbatim moves; all-private). Verified: 410 non-visual tests pass; ruff + ruff-format clean. Pre-existing #703/#705 mypy debt in utils.py is unrelated; --no-verify for that reason only.
Integrate #714 (show() decomposition) into the utils.py split. Only utils.py conflicted: - import block: dropped the now-unused `_locate_value` import (it moved to _color.py with the color code that uses it); kept main's `_locate_value` out of utils. - `_fast_extent` docstring: took main's #714 version (D205 fix). basic.py auto-merged: #714's decomposed show()/helpers now import color and validation symbols from _color/_validate (the split's repoints), not utils. Bonus: merging #714 brings its fixes for the pre-existing #703/#705 debt (_resolve_measure_table str-return, _get_extent_fast Any-return, _fast_extent D205), so the branch is now fully ruff + mypy clean (no --no-verify). Verified: no import cycle; ruff + ruff-format + mypy all pass; 410 non-visual tests pass.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #715 +/- ##
==========================================
+ Coverage 77.18% 77.48% +0.30%
==========================================
Files 14 17 +3
Lines 4440 4500 +60
Branches 1014 1014
==========================================
+ Hits 3427 3487 +60
Misses 658 658
Partials 355 355
🚀 New features to boost your workflow:
|
) The element-name formatting and table/shape uniqueness helpers (_format_element_name/s, _preview_values, _ensure_one_to_one_mapping, _validate_shape_index_uniqueness, _validate_table_instance_uniqueness) were parked in _color.py only to break the _validate<->_color cycle. They are cross-cutting low-level helpers, so utils.py is their natural home — this removes the "validation helpers living in the color module" wart. Cycle-safe: they call only each other + third-party (GeoDataFrame, pd, AnnData, get_table_keys), never _color/_validate, so utils gains no edge to either. _color imports the two it uses (_ensure_one_to_one_mapping, _format_element_name) from utils; re-added GeoDataFrame/SpatialElement imports to utils (dropped earlier when geometry/color moved out). Verified: no import cycle; ruff + ruff-format + mypy clean; 410 non-visual tests pass.
The utils split (#696) relocated the public set_zero_in_cmap_to_transparent into the private _color module, breaking the only public import path: `from spatialdata_plot.pl.utils import set_zero_in_cmap_to_transparent`. Move it back to utils.py — a public symbol belongs in a public module, and a re-export shim would re-introduce the _color<->utils cycle the split removed (utils is the import-graph leaf). Point the test at the public path.
…lit-utils # Conflicts: # src/spatialdata_plot/pl/render.py
timtreis
added a commit
that referenced
this pull request
Jun 14, 2026
… + single continuous-norm (#699) PRs 1+2 of #699, rebased onto the post-#715 (utils split) / post-#720 main. PR3 (image-composite helper) deferred. CmapParams gains behavior: - fresh_norm(): a safe per-element copy (applying a Normalize autoscales vmin/vmax in place, leaking one element's range into the next). Converts the 3 copy(norm) sites in render.py. - cmap_with_alpha() / colormap_with_alpha(): public-API replacement for the private cmap._lut[:, -1] = alpha poke in the 1-channel image path; byte-identical body colors, no shared-cmap mutation. Single continuous-norm feeds pixels and colorbar: - _resolve_continuous_norm(values, cmap_params) in _color.py: one resolver called by each pixel-baking site and its matching colorbar site with the same vector, so they cannot diverge. Folds the duplicated inline norm blocks in _color_vector_to_rgba and _get_collection_shape (now in _geometry.py; its dead `norm` param dropped) and routes _map_color_seg + the labels imshow + _append_outline_colorbar + the shapes set_clim through it. - Datashader / image / points / graph paths unchanged (already share their norm). Behavior-preserving for normal renders; the only unified edge is an all-identical-value outline column. Adds unit tests for the new CmapParams methods and _resolve_continuous_norm plus a non-visual colorbar-clim integration test.
timtreis
added a commit
that referenced
this pull request
Jun 14, 2026
… + single continuous-norm (#699) PRs 1+2 of #699, rebased onto the post-#715 (utils split) / post-#720 main. PR3 (image-composite helper) deferred. CmapParams gains behavior: - fresh_norm(): a safe per-element copy (applying a Normalize autoscales vmin/vmax in place, leaking one element's range into the next). Converts the 3 copy(norm) sites in render.py. - cmap_with_alpha() / colormap_with_alpha(): public-API replacement for the private cmap._lut[:, -1] = alpha poke in the 1-channel image path; byte-identical body colors, no shared-cmap mutation. Single continuous-norm feeds pixels and colorbar: - _resolve_continuous_norm(values, cmap_params) in _color.py: one resolver called by each pixel-baking site and its matching colorbar site with the same vector, so they cannot diverge. Folds the duplicated inline norm blocks in _color_vector_to_rgba and _get_collection_shape (now in _geometry.py; its dead `norm` param dropped) and routes _map_color_seg + the labels imshow + _append_outline_colorbar + the shapes set_clim through it. - Datashader / image / points / graph paths unchanged (already share their norm). Behavior-preserving for normal renders; the only unified edge is an all-identical-value outline column. Adds unit tests for the new CmapParams methods and _resolve_continuous_norm plus a non-visual colorbar-clim integration test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #696 (relocation scope only).
Splits the 4918-line
pl/utils.pyinto single-concern sibling modules by verbatim relocation of private symbols — no behavior change, no public API change, no re-export shims.Result
utils.py_geometry.py(new)_datashader.py_color.py(new)_validate.py(new)Net +113 lines (~2%) — purely per-module import headers; zero duplicated logic, zero shims.
Commits (one module each, bisectable)
_geometry.py— 8 shape/patch helpers_datashader.py— 13 helpers moved in, kills the only repo near-cycle; fixes_datshader…→_datashader_get_how_kw_for_spreadtypo_color.py— 21 color helpers + 6 color-only format/uniqueness helpers_validate.py— 17 validation/type-check functionsImport graph (acyclic)
_validate → _color → utils;_geometry → utils;_datashader → _color, utils. No module importsrender/basic. The_validate↔_colorcycle was avoided by placing the 6 color-only helpers in_color(plain top-level imports, no deferred-import smell).