Skip to content

[chore] Add unit tests to improve Python coverage#14479

Merged
lukasmasuch merged 5 commits intodevelopfrom
lukasmasuch/python-coverage-boost
Mar 27, 2026
Merged

[chore] Add unit tests to improve Python coverage#14479
lukasmasuch merged 5 commits intodevelopfrom
lukasmasuch/python-coverage-boost

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Mar 24, 2026

Describe your changes

Adds unit tests to improve Python code coverage by ~0.17%, targeting 90%+ coverage goal:

  • Adds errors_test.py with 14 tests covering error exception classes
  • Adds image_utils_test.py with 19 tests covering image utility functions
  • Extends write_test.py with StringIO handling tests
  • Extends string_util_test.py with edge case tests for validation functions
  • Extends url_util_test.py with raw GitHub URL handling tests

Files improved:

  • lib/streamlit/errors.py: 87% → 100%
  • lib/streamlit/elements/lib/image_utils.py: 88.1% → 93.2%
  • lib/streamlit/string_util.py: 91.5% → 99.1%
  • lib/streamlit/url_util.py: 90.5% → 92.9%

GitHub Issue Link (if applicable)

N/A

Testing Plan

  • Unit Tests (Python)
    • lib/tests/streamlit/errors_test.py — Tests error exception classes
    • lib/tests/streamlit/elements/lib/image_utils_test.py — Tests image utility functions
    • lib/tests/streamlit/write_test.py — Tests StringIO handling in st.write
    • lib/tests/streamlit/string_util_test.py — Tests validation functions
    • lib/tests/streamlit/url_util_test.py — Tests GitHub URL processing
Agent metrics
Type Name Count
skill checking-changes 1
skill improving-python-coverage 2
subagent fixing-pr 1
subagent general-purpose 7

- Add errors_test.py covering error exception classes
- Add image_utils_test.py covering image utility functions
- Extend write_test.py with StringIO handling tests
- Extend string_util_test.py with edge case tests
- Extend url_util_test.py with raw GitHub URL tests

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch added impact:internal PR changes only affect internal code change:chore PR contains maintenance or housekeeping change labels Mar 24, 2026
Copilot AI review requested due to automatic review settings March 24, 2026 02:16
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io Bot commented Mar 24, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 24, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14479/streamlit-1.55.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14479.streamlit.app (☁️ Deploy here if not accessible)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds/extends Python unit tests to raise coverage across several Streamlit utility modules, supporting the repo’s 90%+ coverage goal without changing production behavior.

Changes:

  • Add new unit test suites for streamlit.errors and streamlit.elements.lib.image_utils.
  • Extend existing unit tests for st.write, string_util, and url_util with additional edge cases.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lib/tests/streamlit/write_test.py Adds coverage for st.write handling io.StringIO.
lib/tests/streamlit/url_util_test.py Adds a regression test ensuring GitHub .../raw/... URLs remain unchanged.
lib/tests/streamlit/string_util_test.py Adds edge-case tests for emoji/material icon validation, binary detection, and from_number error paths.
lib/tests/streamlit/errors_test.py New tests covering exception/warning classes and key message formatting behaviors in streamlit.errors.
lib/tests/streamlit/elements/lib/image_utils_test.py New tests covering internal image utilities: format selection, numpy shape validation, and clipping behavior.

Comment thread lib/tests/streamlit/write_test.py Outdated
Comment thread lib/tests/streamlit/write_test.py Outdated
@github-actions github-actions Bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 24, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds new unit tests and extends existing ones to improve Python code coverage for errors.py, image_utils.py, string_util.py, url_util.py, and st.write (StringIO handling). Additionally, the PR removes the Windows-specific file-watcher stability check from event_based_path_watcher.py along with its corresponding tests. While the test additions are well-structured, both individual reviewers characterized this as a "test-only" PR, overlooking the production code removal in the watcher module.

Reviewer consensus: 2 of 3 expected models completed reviews (gemini-3.1-pro and gpt-5.3-codex-high). claude-4.6-opus-high-thinking failed to complete its review. gemini-3.1-pro requested changes (local import nit); gpt-5.3-codex-high approved. Both reviewers agreed on code quality, no security risk, no external-test need, and no accessibility impact — but both missed the production code change.

Code Quality

The test additions are well-structured, following project conventions with clear docstrings, appropriate assertions, and good edge-case coverage. Minor style issue: from io import StringIO is imported locally inside two test functions in write_test.py (lines 779, 791) rather than at the top of the file, which contradicts lib/tests/AGENTS.md guidelines. This is a low-severity nit covered by the inline comment.

Production code change (missed by both reviewers): The diff removes ~68 lines of production code from lib/streamlit/watcher/event_based_path_watcher.py, specifically the Windows-specific stability check that mitigated spurious file-change events from Windows Defender, Search Indexer, and OneDrive (linked to issue #13954). The corresponding _WINDOWS_STABILITY_DELAY_SECS constant and the module-level docstring describing the Windows considerations are also removed, along with ~145 lines of related tests. This is a functional behavior change on Windows, not a test-only change.

Test Coverage

The new tests provide meaningful coverage improvements for the targeted modules:

  • errors_test.py (new): Covers exception classes and message formatting.
  • image_utils_test.py (new): Covers format normalization, ndarray shape validation, and clipping range behavior.
  • string_util_test.py (extended): Adds edge-case checks for None icon input, binary detection, and from_number failure paths.
  • url_util_test.py (extended): Adds regression test for already-raw GitHub URLs.
  • write_test.py (extended): Adds coverage for StringIO handling in st.write.

The removal of 3 Windows-specific watcher tests is appropriate given the corresponding production code was removed. However, the net effect reduces test coverage for Windows file-watching behavior.

Backwards Compatibility

The test additions carry no backwards compatibility risk. The removal of the Windows stability check in event_based_path_watcher.py could cause regressions on Windows where background processes (Windows Defender, Search Indexer, OneDrive) trigger spurious file-change events, potentially leading to unnecessary app reruns. This should be confirmed as intentional.

Security & Risk

No security concerns identified. The changes do not touch auth, session management, networking, or other security-sensitive areas. Regression risk from test additions is negligible. The watcher code removal carries minor regression risk limited to Windows platforms.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • Test files only exercise internal utility functions and error classes.
    • The watcher production code removal is internal file-watching behavior, not related to routing, auth, embedding, or cross-origin behavior.
  • Suggested external_test focus areas: N/A
  • Confidence: High
  • Assumptions and gaps: Assessment assumes the Windows stability check removal was intentionally decided outside this PR's scope.

Accessibility

N/A — No frontend or UI changes are included in this PR.

Recommendations

  1. Move from io import StringIO to the top-level imports in lib/tests/streamlit/write_test.py per lib/tests/AGENTS.md guidelines (both occurrences at lines 779 and 791).
  2. Confirm the removal of the Windows-specific stability check in event_based_path_watcher.py (and its tests) is intentional. If this was a revert of a prior change, a brief note in the PR description would help reviewers understand the context and link back to issue #13954.

Verdict

CHANGES REQUESTED: The test additions are solid, but the StringIO import should be moved to the top of the file per project guidelines, and the production code removal (Windows watcher stability check) should be explicitly acknowledged in the PR description to confirm it is intentional — both reviewers missed this functional change.


This is a consolidated AI review by claude-4.6-opus-high-thinking, synthesizing reviews from gemini-3.1-pro and gpt-5.3-codex-high. claude-4.6-opus-high-thinking failed to complete its individual review. Please verify the feedback and use your judgment.

This review also includes 1 inline comment(s) on specific code lines.

Comment thread lib/tests/streamlit/write_test.py Outdated
@github-actions github-actions Bot added the do-not-merge PR is blocked from merging label Mar 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 24, 2026

📈 Python coverage change detected

The Python unit test coverage has increased by 0.1176%

  • Current PR: 93.8336% (24293 statements, 1498 missed)
  • Latest develop: 93.7161% (24125 statements, 1516 missed)

🎉 Great job on improving test coverage!

Coverage by files
Name Stmts Miss Cover
streamlit/__init__.py 140 0 100%
streamlit/__main__.py 3 3 0%
streamlit/auth_util.py 216 11 95%
streamlit/cli_util.py 33 0 100%
streamlit/column_config.py 3 0 100%
streamlit/commands/__init__.py 0 0 100%
streamlit/commands/echo.py 54 2 96%
streamlit/commands/execution_control.py 70 10 86%
streamlit/commands/logo.py 53 1 98%
streamlit/commands/navigation.py 128 2 98%
streamlit/commands/page_config.py 106 4 96%
streamlit/components/__init__.py 0 0 100%
streamlit/components/lib/__init__.py 0 0 100%
streamlit/components/lib/local_component_registry.py 35 2 94%
streamlit/components/types/__init__.py 0 0 100%
streamlit/components/types/base_component_registry.py 14 0 100%
streamlit/components/types/base_custom_component.py 48 6 88%
streamlit/components/v1/__init__.py 5 0 100%
streamlit/components/v1/component_arrow.py 33 2 94%
streamlit/components/v1/component_registry.py 35 3 91%
streamlit/components/v1/components.py 4 4 0%
streamlit/components/v1/custom_component.py 82 5 94%
streamlit/components/v2/__init__.py 27 0 100%
streamlit/components/v2/bidi_component/__init__.py 4 0 100%
streamlit/components/v2/bidi_component/constants.py 5 0 100%
streamlit/components/v2/bidi_component/main.py 148 17 89%
streamlit/components/v2/bidi_component/serialization.py 81 2 98%
streamlit/components/v2/bidi_component/state.py 14 0 100%
streamlit/components/v2/component_definition_resolver.py 30 0 100%
streamlit/components/v2/component_file_watcher.py 117 9 92%
streamlit/components/v2/component_manager.py 97 13 87%
streamlit/components/v2/component_manifest_handler.py 24 0 100%
streamlit/components/v2/component_path_utils.py 65 5 92%
streamlit/components/v2/component_registry.py 121 8 93%
streamlit/components/v2/get_bidi_component_manager.py 8 1 88%
streamlit/components/v2/manifest_scanner.py 227 25 89%
streamlit/components/v2/presentation.py 84 19 77%
streamlit/components/v2/types.py 8 8 0%
streamlit/config.py 420 11 97%
streamlit/config_option.py 77 3 96%
streamlit/config_util.py 308 7 98%
streamlit/connections/__init__.py 6 0 100%
streamlit/connections/base_connection.py 51 0 100%
streamlit/connections/snowflake_connection.py 98 16 84%
streamlit/connections/snowpark_connection.py 46 3 93%
streamlit/connections/sql_connection.py 56 6 89%
streamlit/connections/util.py 33 0 100%
streamlit/cursor.py 130 1 99%
streamlit/dataframe_util.py 485 40 92%
streamlit/delta_generator.py 253 7 97%
streamlit/delta_generator_singletons.py 86 7 92%
streamlit/deprecation_util.py 66 4 94%
streamlit/development.py 1 0 100%
streamlit/elements/__init__.py 0 0 100%
streamlit/elements/alert.py 72 0 100%
streamlit/elements/arrow.py 231 23 90%
streamlit/elements/balloons.py 10 0 100%
streamlit/elements/bokeh_chart.py 9 0 100%
streamlit/elements/code.py 20 1 95%
streamlit/elements/deck_gl_json_chart.py 125 7 94%
streamlit/elements/dialog_decorator.py 38 0 100%
streamlit/elements/empty.py 16 4 75%
streamlit/elements/exception.py 101 10 90%
streamlit/elements/form.py 56 2 96%
streamlit/elements/graphviz_chart.py 36 1 97%
streamlit/elements/heading.py 56 0 100%
streamlit/elements/help.py 227 9 96%
streamlit/elements/html.py 49 0 100%
streamlit/elements/iframe.py 29 0 100%
streamlit/elements/image.py 36 0 100%
streamlit/elements/json.py 48 6 88%
streamlit/elements/layouts.py 252 5 98%
streamlit/elements/lib/__init__.py 0 0 100%
streamlit/elements/lib/built_in_chart_utils.py 401 29 93%
streamlit/elements/lib/color_util.py 103 4 96%
streamlit/elements/lib/column_config_utils.py 169 1 99%
streamlit/elements/lib/column_types.py 201 4 98%
streamlit/elements/lib/dialog.py 67 0 100%
streamlit/elements/lib/dicttools.py 39 2 95%
streamlit/elements/lib/file_uploader_utils.py 54 0 100%
streamlit/elements/lib/form_utils.py 26 0 100%
streamlit/elements/lib/image_utils.py 176 12 93%
streamlit/elements/lib/js_number.py 28 3 89%
streamlit/elements/lib/layout_utils.py 121 1 99%
streamlit/elements/lib/mutable_expander_container.py 19 0 100%
streamlit/elements/lib/mutable_popover_container.py 19 0 100%
streamlit/elements/lib/mutable_status_container.py 71 3 96%
streamlit/elements/lib/mutable_tab_container.py 19 0 100%
streamlit/elements/lib/options_selector_utils.py 142 2 99%
streamlit/elements/lib/pandas_styler_utils.py 80 2 98%
streamlit/elements/lib/policies.py 58 1 98%
streamlit/elements/lib/shortcut_utils.py 42 2 95%
streamlit/elements/lib/streamlit_plotly_theme.py 48 0 100%
streamlit/elements/lib/subtitle_utils.py 76 5 93%
streamlit/elements/lib/utils.py 76 0 100%
streamlit/elements/map.py 111 1 99%
streamlit/elements/markdown.py 69 2 97%
streamlit/elements/media.py 182 8 96%
streamlit/elements/metric.py 102 0 100%
streamlit/elements/pdf.py 47 0 100%
streamlit/elements/plotly_chart.py 129 6 95%
streamlit/elements/progress.py 36 0 100%
streamlit/elements/pyplot.py 37 0 100%
streamlit/elements/snow.py 10 0 100%
streamlit/elements/space.py 12 0 100%
streamlit/elements/spinner.py 44 3 93%
streamlit/elements/table.py 56 3 95%
streamlit/elements/text.py 16 0 100%
streamlit/elements/toast.py 26 0 100%
streamlit/elements/vega_charts.py 244 5 98%
streamlit/elements/widgets/__init__.py 0 0 100%
streamlit/elements/widgets/audio_input.py 68 1 99%
streamlit/elements/widgets/button.py 265 6 98%
streamlit/elements/widgets/button_group.py 205 15 93%
streamlit/elements/widgets/camera_input.py 62 1 98%
streamlit/elements/widgets/chat.py 230 36 84%
streamlit/elements/widgets/checkbox.py 54 0 100%
streamlit/elements/widgets/color_picker.py 70 2 97%
streamlit/elements/widgets/data_editor.py 257 14 95%
streamlit/elements/widgets/feedback.py 69 0 100%
streamlit/elements/widgets/file_uploader.py 108 1 99%
streamlit/elements/widgets/menu_button.py 81 0 100%
streamlit/elements/widgets/multiselect.py 118 5 96%
streamlit/elements/widgets/number_input.py 153 4 97%
streamlit/elements/widgets/radio.py 105 5 95%
streamlit/elements/widgets/select_slider.py 127 4 97%
streamlit/elements/widgets/selectbox.py 99 3 97%
streamlit/elements/widgets/slider.py 256 12 95%
streamlit/elements/widgets/text_widgets.py 144 6 96%
streamlit/elements/widgets/time_widgets.py 465 25 95%
streamlit/elements/write.py 166 18 89%
streamlit/emojis.py 4 0 100%
streamlit/env_util.py 21 3 86%
streamlit/error_util.py 33 2 94%
streamlit/errors.py 168 2 99%
streamlit/external/__init__.py 0 0 100%
streamlit/external/langchain/__init__.py 2 0 100%
streamlit/external/langchain/streamlit_callback_handler.py 137 78 43%
streamlit/file_util.py 84 8 90%
streamlit/git_util.py 100 0 100%
streamlit/logger.py 54 0 100%
streamlit/material_icon_names.py 1 0 100%
streamlit/navigation/__init__.py 0 0 100%
streamlit/navigation/page.py 125 2 98%
streamlit/net_util.py 55 3 95%
streamlit/path_security.py 17 1 94%
streamlit/platform.py 10 1 90%
streamlit/runtime/__init__.py 8 0 100%
streamlit/runtime/app_session.py 466 89 81%
streamlit/runtime/caching/__init__.py 21 0 100%
streamlit/runtime/caching/cache_data_api.py 191 3 98%
streamlit/runtime/caching/cache_errors.py 44 4 91%
streamlit/runtime/caching/cache_resource_api.py 165 1 99%
streamlit/runtime/caching/cache_type.py 11 1 91%
streamlit/runtime/caching/cache_utils.py 173 4 98%
streamlit/runtime/caching/cached_message_replay.py 108 1 99%
streamlit/runtime/caching/hashing.py 310 25 92%
streamlit/runtime/caching/legacy_cache_api.py 14 0 100%
streamlit/runtime/caching/storage/__init__.py 2 0 100%
streamlit/runtime/caching/storage/cache_storage_protocol.py 29 0 100%
streamlit/runtime/caching/storage/dummy_cache_storage.py 21 0 100%
streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py 67 1 99%
streamlit/runtime/caching/storage/local_disk_cache_storage.py 86 4 95%
streamlit/runtime/caching/ttl_cleanup_cache.py 35 0 100%
streamlit/runtime/connection_factory.py 96 11 89%
streamlit/runtime/context.py 137 0 100%
streamlit/runtime/context_util.py 18 0 100%
streamlit/runtime/credentials.py 139 4 97%
streamlit/runtime/download_data_util.py 27 0 100%
streamlit/runtime/forward_msg_cache.py 23 2 91%
streamlit/runtime/forward_msg_queue.py 63 4 94%
streamlit/runtime/fragment.py 110 1 99%
streamlit/runtime/media_file_manager.py 110 7 94%
streamlit/runtime/media_file_storage.py 15 0 100%
streamlit/runtime/memory_media_file_storage.py 73 0 100%
streamlit/runtime/memory_session_storage.py 15 0 100%
streamlit/runtime/memory_uploaded_file_manager.py 46 1 98%
streamlit/runtime/metrics_util.py 201 13 94%
streamlit/runtime/pages_manager.py 59 2 97%
streamlit/runtime/runtime.py 250 16 94%
streamlit/runtime/runtime_util.py 30 1 97%
streamlit/runtime/script_data.py 16 0 100%
streamlit/runtime/scriptrunner/__init__.py 5 0 100%
streamlit/runtime/scriptrunner/exec_code.py 49 5 90%
streamlit/runtime/scriptrunner/magic.py 83 1 99%
streamlit/runtime/scriptrunner/magic_funcs.py 10 1 90%
streamlit/runtime/scriptrunner/script_cache.py 27 0 100%
streamlit/runtime/scriptrunner/script_runner.py 231 25 89%
streamlit/runtime/scriptrunner_utils/__init__.py 0 0 100%
streamlit/runtime/scriptrunner_utils/exceptions.py 9 1 89%
streamlit/runtime/scriptrunner_utils/script_requests.py 104 5 95%
streamlit/runtime/scriptrunner_utils/script_run_context.py 118 0 100%
streamlit/runtime/secrets.py 241 18 93%
streamlit/runtime/session_manager.py 71 2 97%
streamlit/runtime/state/__init__.py 7 0 100%
streamlit/runtime/state/common.py 60 1 98%
streamlit/runtime/state/presentation.py 19 4 79%
streamlit/runtime/state/query_params.py 343 7 98%
streamlit/runtime/state/query_params_proxy.py 71 0 100%
streamlit/runtime/state/safe_session_state.py 77 9 88%
streamlit/runtime/state/session_state.py 543 36 93%
streamlit/runtime/state/session_state_proxy.py 62 8 87%
streamlit/runtime/state/widgets.py 23 0 100%
streamlit/runtime/stats.py 134 4 97%
streamlit/runtime/theme_util.py 46 1 98%
streamlit/runtime/uploaded_file_manager.py 39 2 95%
streamlit/runtime/websocket_session_manager.py 112 0 100%
streamlit/source_util.py 34 1 97%
streamlit/starlette.py 2 0 100%
streamlit/string_util.py 106 1 99%
streamlit/temporary_directory.py 18 1 94%
streamlit/testing/__init__.py 0 0 100%
streamlit/testing/v1/__init__.py 2 0 100%
streamlit/testing/v1/app_test.py 261 4 98%
streamlit/testing/v1/element_tree.py 1569 63 96%
streamlit/testing/v1/local_script_runner.py 75 2 97%
streamlit/testing/v1/util.py 17 0 100%
streamlit/time_util.py 27 0 100%
streamlit/type_util.py 152 13 91%
streamlit/url_util.py 42 3 93%
streamlit/user_info.py 105 8 92%
streamlit/util.py 38 1 97%
streamlit/version.py 3 0 100%
streamlit/watcher/__init__.py 3 0 100%
streamlit/watcher/event_based_path_watcher.py 197 27 86%
streamlit/watcher/folder_black_list.py 14 1 93%
streamlit/watcher/local_sources_watcher.py 127 9 93%
streamlit/watcher/path_watcher.py 40 1 98%
streamlit/watcher/polling_path_watcher.py 55 2 96%
streamlit/watcher/util.py 59 1 98%
streamlit/web/__init__.py 0 0 100%
streamlit/web/bootstrap.py 176 21 88%
streamlit/web/cache_storage_manager_config.py 5 0 100%
streamlit/web/cli.py 184 12 93%
streamlit/web/server/__init__.py 5 0 100%
streamlit/web/server/app_discovery.py 104 5 95%
streamlit/web/server/app_static_file_handler.py 34 3 91%
streamlit/web/server/authlib_tornado_integration.py 42 5 88%
streamlit/web/server/bidi_component_request_handler.py 65 8 88%
streamlit/web/server/browser_websocket_handler.py 147 20 86%
streamlit/web/server/component_file_utils.py 27 0 100%
streamlit/web/server/component_request_handler.py 55 4 93%
streamlit/web/server/media_file_handler.py 65 9 86%
streamlit/web/server/oauth_authlib_routes.py 162 35 78%
streamlit/web/server/oidc_mixin.py 46 0 100%
streamlit/web/server/routes.py 94 10 89%
streamlit/web/server/server.py 200 13 94%
streamlit/web/server/server_util.py 87 5 94%
streamlit/web/server/starlette/__init__.py 3 0 100%
streamlit/web/server/starlette/starlette_app.py 148 4 97%
streamlit/web/server/starlette/starlette_app_utils.py 101 7 93%
streamlit/web/server/starlette/starlette_auth_routes.py 233 51 78%
streamlit/web/server/starlette/starlette_gzip_middleware.py 30 0 100%
streamlit/web/server/starlette/starlette_path_security_middleware.py 22 0 100%
streamlit/web/server/starlette/starlette_routes.py 341 82 76%
streamlit/web/server/starlette/starlette_server.py 167 7 96%
streamlit/web/server/starlette/starlette_server_config.py 13 0 100%
streamlit/web/server/starlette/starlette_static_routes.py 70 6 91%
streamlit/web/server/starlette/starlette_websocket.py 203 23 89%
streamlit/web/server/stats_request_handler.py 59 5 92%
streamlit/web/server/upload_file_request_handler.py 59 7 88%
streamlit/web/server/websocket_headers.py 19 1 95%
TOTAL 24293 1498 94%

📊 View detailed coverage comparison

…parameterized test

- Move `from io import StringIO` to top-level imports as per test guidelines
- Combine two identical StringIO tests into a single parameterized test

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 24, 2026
@github-actions github-actions Bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 24, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds and extends Python unit tests to improve code coverage (~0.17% increase) toward the 90%+ coverage goal. No production code is modified — only test files are added or extended. The changes cover:

  • New file errors_test.py: 14 tests for error/exception classes in streamlit/errors.py
  • New file image_utils_test.py: 19 tests for image utility functions (_validate_image_format_string, _verify_np_shape, _clip_image)
  • Extended write_test.py: StringIO handling in st.write
  • Extended string_util_test.py: Edge cases for validate_emoji, validate_material_icon, is_binary_string, and from_number
  • Extended url_util_test.py: GitHub raw URL handling in process_gitblob_url

Reviewer consensus: All three reviewers (claude-4.6-opus-high-thinking, gemini-3.1-pro, gpt-5.3-codex-high) approved this PR unanimously. No critical or blocking issues were raised.

Code Quality

The tests are well-structured, readable, and follow existing patterns in the codebase. Each test has a clear docstring and targeted assertions. All reviewers agreed on the high quality of the test code.

Minor style observations (all non-blocking):

  • The two new test files use pytest-compatible classes for grouping, whereas lib/tests/AGENTS.md prefers standalone pytest functions with @pytest.mark.parametrize. Two reviewers flagged this; one did not. The class-based approach works correctly with pytest and is internally consistent, so this is a style preference only.
  • New test methods added to legacy unittest.TestCase files lack return type annotations (-> None). One reviewer noted this deviates from current guidance, while another noted it is consistent with the surrounding existing methods. Either approach is reasonable for legacy files.

Test Coverage

All reviewers agreed the tests are well-targeted and cover meaningful code paths:

  • errors_test.py: __repr__, stack capture, message formatting, conditional error messages (uses_pages_directory branches), and mixed numeric type error parameters.
  • image_utils_test.py: Format validation (explicit formats + auto-detection), numpy shape validation (valid shapes, invalid dimensions, channel conversion), and image clipping (float/int ranges, clamping, error cases).
  • string_util_test.py: None-input edge cases for validators, binary string detection, and from_number with non-numeric item() return values.
  • url_util_test.py: Raw GitHub URLs pass through unchanged.
  • write_test.py: StringIO → markdown rendering path.

One area where assertion strength could be improved: the _clip_image tests only check min()/max() bounds rather than exact array values. Using np.testing.assert_array_equal would provide stronger regression protection (see inline comment).

Backwards Compatibility

No concerns. This PR only adds tests — no source code, APIs, protobufs, or frontend code are modified. All reviewers unanimously agreed there is zero risk of breaking existing functionality.

Security & Risk

No security concerns. All reviewers agreed the changes are entirely within the test suite and do not affect runtime code, dependencies, or configuration.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • All changed files are in lib/tests/streamlit/ — pure unit test additions with no runtime impact
    • No changes to routing, auth, WebSocket, embedding, assets, CORS, security headers, or any other external-facing behavior
  • Suggested external_test focus areas: None
  • Confidence: High (all three reviewers agreed)
  • Assumptions and gaps: None

Accessibility

Not applicable — no frontend changes. All reviewers agreed.

Recommendations

  1. Strengthen _clip_image assertions: Use np.testing.assert_array_equal to verify exact expected array values instead of just min()/max() bounds in test_float_image_with_clamp, test_float_image_without_clamp_valid_range, and test_int_image_with_clamp. (Raised by gemini-3.1-pro, confirmed on review.)

  2. Document min_value=0 falsy behavior: In errors_test.py line 127, min_value=0 is falsy, so the source code's if min_value: check silently omits it from the error message. Adding an explicit negative assertion (e.g., assert "min_value" not in str(error)) would codify this behavior. (Raised by claude-4.6-opus-high-thinking.)

  3. Consider standalone pytest functions for new files: For errors_test.py and image_utils_test.py, lib/tests/AGENTS.md prefers standalone pytest functions with @pytest.mark.parametrize over class-based grouping. Low priority and non-blocking.

Verdict

APPROVED: Clean, well-structured test-only PR that meaningfully improves Python code coverage across five modules with no risk to existing functionality. All three reviewers approved unanimously with no blocking issues.


Consolidated AI review by claude-4.6-opus-high-thinking. Individual reviews: claude-4.6-opus-high-thinking (APPROVED), gemini-3.1-pro (APPROVED), gpt-5.3-codex-high (APPROVED). All expected models completed their reviews successfully.

This review also includes 3 inline comment(s) on specific code lines.

Comment thread lib/tests/streamlit/elements/lib/image_utils_test.py Outdated
Comment thread lib/tests/streamlit/errors_test.py Outdated
Comment thread lib/tests/streamlit/elements/lib/image_utils_test.py Outdated
@github-actions github-actions Bot removed the do-not-merge PR is blocked from merging label Mar 24, 2026
lukasmasuch and others added 2 commits March 24, 2026 11:40
…parameterized test

- Remove errors_test.py (errors.py excluded from coverage)
- Refactor image_utils_test.py to use standalone pytest functions with @pytest.mark.parametrize

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ude trivial ones

- Remove errors.py from coverage omit (was hiding real coverage gaps)
- Add pragma: no cover to trivial exception classes (no logic to test)
- Add focused tests for classes with non-trivial logic:
  - LocalizableStreamlitException (exec_kwargs property)
  - StreamlitAPIWarning (captures stack trace)
  - StreamlitMixedNumericTypesError (conditional message building)
  - StreamlitPageNotFoundError (conditional message)
  - StreamlitSelectionCountExceedsMaxError (pluralization)
  - StreamlitInvalidMaxError (optional corrective action)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch requested a review from a team as a code owner March 24, 2026 10:47
Comment thread lib/tests/streamlit/errors_test.py Outdated
assert str(exc) == "Value 42 is invalid for test_param"


def test_localizable_exception_exec_kwargs_property():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Added tests in this file don't follow the pattern in image_utils_test.py above which have the -> None return on test functions

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these here as well 👍

),
],
)
def test_clip_image_valid(array: np.ndarray, clamp: bool, check_fn) -> None:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The lambda-based check_fn assertions could be improved. For deterministic operations like clamping + scaling, checking only min() >= 0 and max() <= 255 would still pass if clipping accidentally zeroed all values. Consider using np.testing.assert_array_almost_equal with the exact expected output (e.g., [[0, 127.5], [255, 204]] for the float-clamp case). The int-without-clamp case already does this well with np.array_equal.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use np.testing.assert_array_almost_equal 👍

Comment thread lib/tests/streamlit/errors_test.py Outdated
"""Test message when all numeric args have different types."""
exc = errors.StreamlitMixedNumericTypesError(
value=1.0,
min_value=1, # Use non-zero to ensure it's included
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: outside the scope of this PR, but perhaps worth a follow up - the comment here acknowledges that min_value=0 would be silently excluded because the source uses if min_value: (falsy check) rather than if min_value is not None: - should these just be fixed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to if min_value is not None:

…zero-value bug

- Add `-> None` return type annotations to all test functions in errors_test.py
- Replace weak min/max bound checks with exact value assertions in image_utils_test.py
- Fix StreamlitMixedNumericTypesError to use `is not None` checks instead of truthiness,
  so zero values are correctly included in error messages
- Add test for zero-value handling in mixed numeric types error

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch enabled auto-merge (squash) March 27, 2026 08:35
@lukasmasuch lukasmasuch merged commit 377b12d into develop Mar 27, 2026
42 checks passed
@lukasmasuch lukasmasuch deleted the lukasmasuch/python-coverage-boost branch March 27, 2026 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:chore PR contains maintenance or housekeeping change impact:internal PR changes only affect internal code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants