Skip to content

[feat] Extract leading icon from alert body#14173

Merged
lukasmasuch merged 7 commits intodevelopfrom
lukasmasuch/alert-icon-body-extract
Mar 4, 2026
Merged

[feat] Extract leading icon from alert body#14173
lukasmasuch merged 7 commits intodevelopfrom
lukasmasuch/alert-icon-body-extract

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Mar 2, 2026

Describe your changes

Automatically extract a leading emoji or Material icon from alert body text when no explicit icon parameter is provided.

  • When calling st.error(), st.warning(), st.info(), or st.success() without an icon parameter, the function now checks if the body starts with an emoji or Material icon shortcode (e.g., :material/warning:)
  • If found, the icon is extracted and displayed as the alert's icon, while being removed from the body text
  • Explicit icon parameter takes precedence over body extraction (backwards compatible)

Example:

# Before: emoji displayed inline in body
st.warning("🔔 Important notification")

# After: emoji automatically becomes the icon
st.warning("🔔 Important notification")  # 🔔 shown as icon, body shows "Important notification"

Github

Testing Plan

  • Unit tests for extract_leading_icon in string_util_test.py
  • Unit tests for icon extraction behavior in alert_test.py
  • E2E tests for emoji and material icon extraction from body

…ovided

When the `icon` parameter is not explicitly set, alert functions (st.error,
st.warning, st.info, st.success) now automatically extract a leading emoji
or Material icon from the body text and use it as the alert icon.

- Add `extract_leading_icon` function to string_util.py
- Add `_process_alert_body_and_icon` helper to alert.py
- Comprehensive unit tests for icon extraction logic
- E2E tests for emoji and material icon extraction from body
Copilot AI review requested due to automatic review settings March 2, 2026 16:04
@lukasmasuch lukasmasuch added change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users labels Mar 2, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Mar 2, 2026

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

Status Scanner 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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 2, 2026

✅ PR preview is ready!

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

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

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

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

This PR adds automatic extraction of a leading emoji or material icon shortcode from the body text of st.error(), st.warning(), st.info(), and st.success() alert functions, when no explicit icon parameter is provided. Previously, an emoji at the start of the body was rendered inline; now it is promoted to the alert's icon slot and removed from the body text. Explicit icon parameters always take precedence over auto-extraction.

Changes:

  • A new extract_leading_icon() function in string_util.py that checks for a leading material icon shortcode (validated) or emoji at the start of a string and returns a (icon, remaining_text) tuple.
  • A new _process_alert_body_and_icon() helper in alert.py that replaces the direct validate_icon_or_emoji/clean_text calls in all four alert methods, adding the auto-extraction logic.
  • Tests and E2E test cases covering the new extraction behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
lib/streamlit/string_util.py Adds extract_leading_icon() function for material icon/emoji detection at start of text
lib/streamlit/elements/alert.py Refactors alert body/icon processing into _process_alert_body_and_icon() with auto-extraction
lib/tests/streamlit/string_util_test.py Unit tests for the new extract_leading_icon() function
lib/tests/streamlit/elements/alert_test.py Unit tests verifying extraction behavior in all four alert types
e2e_playwright/st_alert.py Adds two new test alert cases with leading emoji and material icon
e2e_playwright/st_alert_test.py Updates alert count assertions and adds snapshot tests for the new extraction cases

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

github-actions bot commented Mar 2, 2026

Consolidated Code Review

Summary

This PR adds automatic leading-icon extraction for alert elements (st.error, st.warning, st.info, st.success). When the icon parameter is not explicitly passed, the alert body is inspected for a leading emoji or Material icon shortcode (e.g., 🔔 or :material/warning:). If found, the icon is extracted from the body and rendered in the dedicated icon slot. When an explicit icon parameter is provided, it takes precedence and the body remains unchanged.

Key implementation changes:

  • lib/streamlit/string_util.py: New extract_leading_icon function that detects leading emoji or Material icon shortcodes, delegating to existing validate_material_icon and extract_leading_emoji utilities.
  • lib/streamlit/elements/alert.py: New _process_alert_body_and_icon helper that centralizes body cleaning, icon extraction, and validation for all four alert methods, eliminating duplicated logic.
  • Unit tests: Added in lib/tests/streamlit/string_util_test.py (parameterized extract_leading_icon tests) and lib/tests/streamlit/elements/alert_test.py (new AlertIconExtractionTest class).
  • E2E tests: Two new snapshot assertions in e2e_playwright/st_alert_test.py with corresponding app entries in e2e_playwright/st_alert.py.

Reviewer Agreement

Both reviewers (gpt-5.3-codex-high and opus-4.6-thinking) agree on all major points:

  1. Implementation quality is solid — the shared _process_alert_body_and_icon helper is well-structured and avoids duplication.
  2. Docstrings are inaccurate — all four alert methods still state "If icon is None (default), no icon is displayed", which is now incorrect.
  3. Missing E2E snapshot baselines — the new snapshot names (st_alert-warning_emoji_from_body, st_alert-info_material_icon_from_body) have no corresponding baseline images, confirmed by inspecting e2e_playwright/__snapshots__/linux/st_alert_test/.
  4. Backward compatibility concern — this silently changes rendering for existing apps whose alert body starts with an emoji/Material icon and omits the icon parameter.
  5. Verdict: CHANGES REQUESTED.

Verified Findings

After inspecting the codebase, I verified:

  • Docstrings: Confirmed inaccurate. Lines 81-84, 159-162, 236-238, 314-316 of lib/streamlit/elements/alert.py all state "no icon is displayed" when icon is None, which is now false.
  • Missing snapshots: Confirmed. The e2e_playwright/__snapshots__/linux/st_alert_test/ directory contains no files matching *emoji_from_body* or *material_icon_from_body*. The E2E tests will fail when run.
  • No opt-out mechanism: Confirmed. Passing icon="" goes through validate_icon_or_emoji("") which calls validate_emoji("")is_emoji("") → raises StreamlitAPIException. There is currently no way to suppress extraction while keeping icon=None behavior, short of modifying the body text.

Issues

Blocking

  1. Inaccurate docstrings (lib/streamlit/elements/alert.py, lines 81-84, 159-162, 236-238, 314-316): All four alert methods' icon parameter documentation states "If icon is None (default), no icon is displayed." This must be updated to describe the new auto-extraction behavior, including how explicit icon takes precedence. Since these docstrings are the public API documentation (rendered on docs.streamlit.io), shipping inaccurate docs would cause user confusion.

  2. Missing E2E snapshot baselines: The two new snapshot assertions (st_alert-warning_emoji_from_body, st_alert-info_material_icon_from_body) reference baselines that do not exist in e2e_playwright/__snapshots__/linux/st_alert_test/. This will cause E2E test failures in CI for all browser/theme variants. Either generate the baselines or replace these with non-snapshot assertions.

Non-Blocking (Recommendations)

  1. No opt-out mechanism for auto-extraction: Users who intentionally start body text with an emoji (e.g., st.warning("⚠️ Please review...")) and want it inline in the body currently have no way to suppress extraction without modifying their text. Consider one of:

    • Accept icon="" as a "no icon, skip extraction" sentinel (distinct from icon=None).
    • Document a workaround (e.g., prefixing with a zero-width space).
    • This is a behavioral change that should be prominently documented in release notes/changelog.
  2. E2E text content assertions: The new E2E tests only use assert_snapshot but do not verify via expect(...).to_have_text(...) that the body text was correctly separated from the icon. Adding a non-visual assertion (e.g., checking the extracted emoji does not appear in the body text element) would strengthen confidence beyond pixel comparison.

  3. Multiline body extraction test: Consider adding an integration test for extraction from a body with newlines after the icon (e.g., :material/warning:\nLine 1\nLine 2) to verify the re.DOTALL flag works as expected end-to-end.

Code Quality

The implementation is clean and follows existing patterns well:

  • _process_alert_body_and_icon properly centralizes the shared logic.
  • extract_leading_icon correctly delegates to existing validation functions (validate_material_icon, extract_leading_emoji), with proper exception handling for invalid Material icons.
  • The re.DOTALL flag on the regex is appropriate for multiline body text.
  • Unit test coverage is strong with good parameterized cases covering edge cases (empty string, no icon, emoji, multi-character emoji, Material icons, invalid Material icons, non-material colon patterns, mid-text emoji).

Test Coverage

  • Unit tests: Good coverage via parameterized tests in both string_util_test.py and alert_test.py. The AlertIconExtractionTest class covers emoji extraction, Material icon extraction, explicit icon precedence, no-icon fallback, and icon-only body across all four alert functions.
  • E2E tests: Two new themed snapshot tests added, but missing baselines (blocking). No text-content assertions beyond snapshots (non-blocking).

Security & Risk

No security concerns. The implementation reuses existing, battle-tested validation functions. Regression risk is moderate due to the behavioral nature of the change — it silently alters how existing alert calls render when they have leading emoji/icons in the body text.

Accessibility

No accessibility concerns. Moving the icon from inline body text to the dedicated icon slot is neutral or positive since the icon slot likely has better semantic handling in the existing Alert frontend component.

Verdict

CHANGES REQUESTED: The core implementation is solid and well-tested, but the PR is not merge-ready due to (1) inaccurate public API docstrings and (2) missing E2E snapshot baselines that will cause CI failures. The opt-out mechanism concern should also be addressed or explicitly documented before merge.


Consolidated review by opus-4.6-thinking.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR adds automatic leading-icon extraction for alerts: when icon is not explicitly passed, st.error, st.warning, st.info, and st.success now try to extract a leading emoji or Material icon shortcode from the alert body. The implementation is centralized via _process_alert_body_and_icon and a new extract_leading_icon utility, with added unit tests and e2e coverage.

Code Quality

The implementation is clean and avoids duplication by routing all four alert commands through a shared helper in lib/streamlit/elements/alert.py.

Issue:

  • lib/streamlit/elements/alert.py (error/warning/info/success docstrings around L82-L84, L160-L162, L237-L239, L315-L317) still say: "If icon is None (default), no icon is displayed." This is now inaccurate because None triggers body extraction.

Test Coverage

Coverage is generally strong:

  • Unit tests added for extract_leading_icon in lib/tests/streamlit/string_util_test.py.
  • Alert behavior tests added in lib/tests/streamlit/elements/alert_test.py for emoji extraction, Material extraction, explicit icon precedence, and no-icon body.
  • E2E scenarios added in e2e_playwright/st_alert.py and asserted in e2e_playwright/st_alert_test.py.

Blocking issue:

  • e2e_playwright/st_alert_test.py:L56-L59 introduces new snapshot names (st_alert-warning_emoji_from_body, st_alert-info_material_icon_from_body), but no corresponding baseline images are included under e2e_playwright/__snapshots__/linux/st_alert_test/. This will produce missing-snapshot test failures.

Backwards Compatibility

The explicit icon= parameter still takes precedence, which preserves existing explicit-icon behavior.
However, this is still a user-visible behavioral change for calls that previously relied on leading emoji/material shortcode text remaining in the body when icon was omitted.

Security & Risk

No direct security concerns identified.
Primary merge risk is test instability/failure from missing e2e snapshot baselines and documentation mismatch for the icon=None behavior.

Accessibility

No frontend structural changes were made, so accessibility risk is low. Existing alert rendering/accessibility semantics should remain unchanged.

Recommendations

  1. Add the missing e2e snapshot baselines for st_alert-warning_emoji_from_body and st_alert-info_material_icon_from_body (all required browser/theme variants), or replace these assertions with non-snapshot checks if snapshot maintenance is not intended.
  2. Update the icon parameter docstrings in all four alert APIs to accurately describe auto-extraction behavior when icon is None.
  3. (Optional) Add a targeted unit test for separator edge cases after extraction (e.g., newline vs space) to lock in expected normalization semantics.

Verdict

CHANGES REQUESTED: The core implementation is solid, but the PR is not merge-ready until missing e2e snapshot baselines are added (and docs are aligned with the new behavior).


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR adds automatic extraction of a leading emoji or Material icon from alert body text (st.error, st.warning, st.info, st.success) when no explicit icon parameter is provided. If a leading emoji (e.g., 🔔) or Material icon shortcode (e.g., :material/warning:) is found, it is removed from the body and displayed as the alert's icon. When an explicit icon parameter is passed, it takes precedence and the body remains unchanged.

Key changes:

  • lib/streamlit/string_util.py: New extract_leading_icon function that detects leading emoji or Material icons.
  • lib/streamlit/elements/alert.py: New _process_alert_body_and_icon helper that orchestrates body cleaning, icon extraction, and validation for all four alert methods.
  • Unit and E2E tests added to cover the new behavior.

Code Quality

The implementation is clean and well-structured. The _process_alert_body_and_icon helper eliminates duplicated logic across the four alert methods, which is a good pattern. The extract_leading_icon function properly delegates to validate_material_icon and extract_leading_emoji, reusing existing validation logic.

A few observations:

  1. Docstrings not updated (lib/streamlit/elements/alert.py, lines 81-83, 159-161, 236-238, 314-316): All four alert methods still state "If icon is None (default), no icon is displayed." This is no longer accurate — an icon may now be auto-extracted from the body. Both the icon and body parameter docstrings should be updated to describe the new extraction behavior and how explicit icon takes precedence.

  2. No opt-out mechanism: If a user intentionally has an emoji at the start of their body text and does not want it extracted (e.g., st.warning("⚠️ Please review...")), there is no way to suppress the extraction while keeping icon=None. Passing icon="" raises StreamlitAPIException. A potential solution would be to accept an empty string as a valid "no icon" sentinel that skips extraction, or document a workaround (e.g., prepending a zero-width space to the body). This should at minimum be documented.

  3. Regex with re.DOTALL (lib/streamlit/string_util.py, line 182): The re.DOTALL flag in r"^(:[^:]+:)\s*(.*)" is correct for multiline body text, ensuring the remaining text after the icon captures everything including newlines. This is a good choice.

Test Coverage

Unit tests (lib/tests/streamlit/string_util_test.py and lib/tests/streamlit/elements/alert_test.py): Good coverage of the new functionality.

  • test_extract_leading_icon: Covers empty string, no icon, emoji extraction (single and multi-character), Material icon extraction, invalid Material icons, non-material colon patterns, and middle-of-text emoji — a solid set of parameterized cases.
  • AlertIconExtractionTest: Tests all four alert functions with @parameterized.expand for emoji extraction, Material icon extraction, explicit icon precedence, no-icon fallback, and icon-only body.

E2E tests (e2e_playwright/st_alert_test.py): Two snapshot tests are added for the themed app, covering emoji-from-body and material-icon-from-body. However:

  1. Missing text content assertions: The E2E tests only use assert_snapshot but don't include expect(...).to_have_text(...) assertions to verify the body text was correctly separated from the icon. Adding non-visual verification would strengthen confidence (e.g., verifying the body text doesn't contain the extracted icon).

  2. No negative E2E assertion: Per E2E best practices, adding at least one "must NOT happen" check would be valuable — for example, verifying that the extracted emoji does not appear in the body text area of the alert.

Backwards Compatibility

This is a breaking behavioral change for existing users whose alert body text starts with an emoji or Material icon shortcode and who do not pass an explicit icon parameter. For example:

# Before: 🔔 displayed inline in body text
st.warning("🔔 Important notification")

# After: 🔔 extracted as icon, body shows only "Important notification"

While arguably an improvement in UX, this will silently change the rendering of existing apps. Users who intentionally placed emoji at the start of body text for inline display currently have no way to opt out without modifying their text content.

The explicit icon parameter correctly takes precedence, so st.warning("🔔 text", icon="⚠️") preserves the emoji in the body — this backward compatibility path works correctly.

Recommendation: Consider whether this should be gated behind a deprecation cycle, a config option, or at minimum prominently documented in a changelog/migration guide. At the very least, provide a documented workaround for users who want to suppress extraction.

Security & Risk

No security concerns identified. The implementation reuses existing validation functions (validate_material_icon, extract_leading_emoji) which are already battle-tested. Invalid material icons are properly caught via exception handling and fall through gracefully.

Regression risk is moderate due to the behavioral nature of the change — it silently alters how existing alert calls render when they have leading emoji/icons in the body text.

Accessibility

No accessibility concerns. The icon extraction simply changes where the icon is rendered (from inline body to the dedicated icon slot), which is already handled by the existing frontend Alert component. This should be a neutral or positive accessibility change since the icon slot likely has better semantic handling.

Recommendations

  1. Update docstrings: Update the icon and body parameter docstrings for all four alert methods to describe the auto-extraction behavior. The current statement "no icon is displayed" when icon is None is now inaccurate.

  2. Provide opt-out mechanism: Allow icon="" (empty string) to mean "no icon, skip extraction" as distinguished from icon=None (default, which triggers extraction). Alternatively, document a workaround such as prefixing the body with a zero-width space.

  3. Add E2E text content assertions: Alongside the snapshot tests, add expect assertions verifying the alert body text does not contain the extracted icon, providing non-visual verification of correctness.

  4. Consider deprecation path: Since this changes observable behavior for existing apps, consider adding a note in the changelog or release notes. The magnitude of impact is hard to gauge, but users of st.warning("⚠️ ...") patterns will notice.

  5. Add a multiline body extraction test: Consider testing extraction from a body that includes newlines after the icon (e.g., :material/warning:\nLine 1\nLine 2) to verify re.DOTALL works as expected in an integration test.

Verdict

CHANGES REQUESTED: The implementation is solid and well-tested, but the docstrings are inaccurate after this change (stating "no icon is displayed" when icon is None), and the lack of an opt-out mechanism for the new extraction behavior is a backward compatibility concern that should be addressed or explicitly documented before merge.


This is an automated AI review by opus-4.6-thinking.

@github-actions github-actions bot added the do-not-merge PR is blocked from merging label Mar 2, 2026
@lukasmasuch lukasmasuch added the update-snapshots Trigger snapshot autofix workflow label Mar 2, 2026
@github-actions github-actions bot removed the update-snapshots Trigger snapshot autofix workflow label Mar 2, 2026
lukasmasuch and others added 2 commits March 2, 2026 17:50
Refactor _transformed_format_func in button_group.py to use the shared
extract_leading_icon utility instead of manual icon detection logic.
This keeps icon extraction consistent across st.alert and st.pills.
## Describe your changes

Automated snapshot updates for #14173 created via the snapshot autofix
CI workflow.

This workflow was triggered by adding the `update-snapshots` label to a
PR after Playwright E2E tests failed with snapshot mismatches.

**Updated snapshots:** 12 file(s)

⚠️ **Please review the snapshot changes carefully** - they could mask
visual bugs if accepted blindly.

This PR targets a feature branch and can be merged without review
approval.

Co-authored-by: Streamlit Bot <[email protected]>
@lukasmasuch lukasmasuch removed the do-not-merge PR is blocked from merging label Mar 2, 2026
Add re.DOTALL flag to EMOJI_EXTRACTION_REGEX to ensure multiline body
text is preserved when extracting leading emoji icons, consistent with
the material icon extraction path.
…for alerts

- Add E2E text assertions to verify body text is correctly separated from
  extracted icon (not just visual snapshot comparison)
- Add integration test for multiline body extraction with material icons
  to validate re.DOTALL flag works correctly end-to-end
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 3, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 3, 2026

Consolidated Code Review

Summary

This PR adds automatic extraction of a leading emoji or Material icon shortcode from alert body text (st.error, st.warning, st.info, st.success) when no explicit icon parameter is provided. The extracted icon is displayed in the alert's icon slot and removed from the body text. Explicit icon parameters take full precedence. The PR also refactors button_group.py icon-extraction logic to reuse the new shared extract_leading_icon utility in string_util.py, and adds re.DOTALL to EMOJI_EXTRACTION_REGEX for multiline support.

Code Quality

Both reviewers agree the implementation is clean, well-structured, and follows existing codebase patterns. Specific highlights:

  • extract_leading_icon in string_util.py: Clean implementation that first checks for material icons then falls back to emoji detection. Good use of re.DOTALL for multiline support. Clear docstring with examples.
  • _process_alert_body_and_icon in alert.py: Nicely encapsulates the extraction logic and is reused across all four alert methods, reducing duplication.
  • button_group.py refactoring: Simplifies the code by delegating to extract_leading_icon. The split(" ", 1) approach is slightly more efficient than the original.

Disagreement on button_group.py regression risk:

  • gpt-5.3-codex-high flagged a potential regression: because EMOJI_EXTRACTION_REGEX includes [_ -]* separators, calling extract_leading_icon on tokens like "😃-" or "😃_" would return ("😃", ""), causing the not remaining check to incorrectly treat them as pure icons. The previous code used is_emoji() on the full token, which would reject these.
  • opus-4.6-thinking assessed the refactoring as behavior-preserving, noting the not remaining guard maintains original semantics.

Consolidator verdict on this disagreement: GPT-5.3's concern is technically correct. The [_ -]* in the regex consumes trailing separators, making remaining empty for inputs like "😃-". However, this is an extremely narrow edge case -- a button-group option would need to be formatted as e.g. "😃- label" for this to trigger, which is highly unlikely in practice. This is a low-severity issue, not a blocking concern, but worth noting for future-proofing.

Test Coverage

Both reviewers agree test coverage is thorough and well-organized:

  • string_util_test.py: 15 parametrized cases for extract_leading_icon covering empty strings, plain text, emoji/material icon extraction, invalid icons, multiline text, and mid-text emojis.
  • alert_test.py: New AlertIconExtractionTest class with 6 test methods, each parametrized across all 4 alert functions (24 test cases total). Covers emoji extraction, material icon extraction, explicit icon precedence, no-icon body, icon-only body, and multiline body.
  • E2E tests: Two new test cases with snapshot assertions and text content verification in st_alert_test.py. All 12 snapshot images (6 themes x 2 cases) are included.

Shared gap identified by both reviewers: No test covers the button_group.py refactoring specifically. While existing button-group tests likely provide implicit coverage, a targeted test would be reassuring. Both reviewers recommend adding one as a follow-up.

Backwards Compatibility

Both reviewers acknowledge this is an intentional behavioral change: previously, st.warning("🔔 Important notification") displayed the emoji inline in the body text with no icon. After this PR, the emoji is automatically extracted and displayed as the alert's icon.

opus-4.6-thinking additionally noted:

  • There is no explicit opt-out mechanism (icon=None triggers extraction, icon="" raises an exception).
  • st.error("✅") now produces an alert with a checkmark icon and an empty body, which may surprise some users.
  • The PR is labeled impact:users and change:feature, indicating the team has accepted this behavioral change.

Both reviewers confirm that explicit icon parameters take precedence, providing a smooth upgrade path for most users.

Security & Risk

No security concerns identified by either reviewer. The changes only parse text against known emoji/icon sets and validate material icon names. No user input is executed or used in unsafe contexts.

Risk assessment (agreed):

  • The re.DOTALL change to EMOJI_EXTRACTION_REGEX is global but low-risk, as it only affects the (.*) capture group behavior with newlines, which is harmless for single-line inputs like page names.
  • The button-group refactoring should be monitored for edge cases, though the practical risk is very low.

Accessibility

Both reviewers agree there are no accessibility concerns. Moving icons from inline body text to the dedicated icon slot is semantically better for screen readers.

Recommendations

  1. Add a test for button_group.py refactoring -- both reviewers agree on this. A simple test verifying that st.pills with emoji-prefixed options still correctly extracts icons would confirm the refactoring is correct.
  2. Consider adding an opt-out mechanism for auto-extraction (e.g., icon="" to mean "no icon, don't extract") -- this could be a follow-up if user feedback warrants it.
  3. Document the breaking change in release notes, especially for users who have emojis at the start of alert body text intentionally.
  4. (Low priority) For extra safety, consider using stricter token validation in button_group.py (e.g., checking is_emoji(first_part) directly) rather than relying on extract_leading_icon's regex-based separator handling. This would close the narrow edge case with trailing separators.

Verdict

APPROVED -- Well-implemented feature with thorough test coverage and clean code. Both reviewers agree on code quality and test completeness. The key disagreement on the button-group regression is resolved: while GPT-5.3's concern is technically valid, the edge case is extremely narrow and unlikely to affect real users. The behavioral change to alerts is intentional and properly labeled. The recommendations above are non-blocking suggestions for follow-up.


Consolidated review by opus-4.6-thinking. Based on 2 of 2 expected reviews.

Expected models: gpt-5.3-codex-high, opus-4.6-thinking -- all present.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR adds automatic extraction of a leading emoji or Material icon from alert body text when icon is not explicitly provided, and updates unit/e2e coverage accordingly. The main behavior is implemented in lib/streamlit/string_util.py and consumed by alert APIs in lib/streamlit/elements/alert.py, with additional refactoring in button-group icon parsing.

Code Quality

Overall, the implementation is clean and mostly consistent with existing patterns, but there is one functional regression in the button-group refactor:

  • Regression risk (behavior change) in icon parsing: lib/streamlit/elements/widgets/button_group.py (around lines 805-811) now calls extract_leading_icon(first_part) and treats the token as an icon when remaining == "". Because extract_leading_emoji accepts separators [_ -]*, tokens like "😃-" or "😃_" are now treated as valid icon-only prefixes, which was not true before (previous code required is_emoji(maybe_icon) on the full token). This silently changes existing option parsing behavior and can strip content unexpectedly.

Test Coverage

Coverage is strong for the new alert feature:

  • Unit tests in lib/tests/streamlit/string_util_test.py cover emoji/material extraction, invalid material icon fallback, and multiline behavior.
  • Unit tests in lib/tests/streamlit/elements/alert_test.py cover all 4 alert APIs, explicit icon precedence, icon-only body, and multiline extraction.
  • E2E updates in e2e_playwright/st_alert_test.py validate rendered snapshots and body text for both emoji and material extraction.

Gap related to the regression above:

  • There is no button-group test covering tokens like "😃- label" / "😃_ label" to verify legacy behavior is preserved.

Backwards Compatibility

Alert behavior changes are intentional and documented (auto-extraction when icon=None), and explicit icon precedence is preserved.

However, the button-group refactor introduces an unintended compatibility change for certain formatted option labels (e.g. trailing - or _ after emoji in the first token), which should be addressed before merge.

Security & Risk

No direct security issues found.
Primary risk is a user-visible parsing regression in button group option rendering due to the shared helper semantics.

Accessibility

No frontend accessibility regressions were identified in this PR. The change is backend-driven and e2e snapshots/text assertions confirm expected rendering for the new alert icon extraction cases.

Recommendations

  1. In lib/streamlit/elements/widgets/button_group.py, keep strict token validation for button-group icon extraction (equivalent to prior behavior), e.g. require is_emoji(first_part) or validate_material_icon(first_part) directly for the first token, instead of using extract_leading_icon on that token.
  2. Add a regression unit test in lib/tests/streamlit/elements/button_group_test.py for first tokens like "😃-" and "😃_" to ensure they are not treated as icon-only prefixes.

Verdict

CHANGES REQUESTED: Alert extraction changes are good, but the button-group refactor introduces a backward-incompatible parsing regression that should be fixed before merge.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR adds automatic extraction of leading emojis or Material icon shortcodes from alert body text (st.error, st.warning, st.info, st.success) when no explicit icon parameter is provided. The extracted icon is displayed in the alert's icon slot and removed from the body text. Explicit icon parameters take full precedence. Additionally, the button_group.py icon extraction logic is refactored to reuse the new shared extract_leading_icon utility, and EMOJI_EXTRACTION_REGEX gains the re.DOTALL flag for multiline support.

Code Quality

The code is well-structured and follows existing patterns in the codebase.

  • extract_leading_icon in string_util.py (lines 155-193): Clean implementation that first checks for material icons then falls back to emoji detection. Good use of re.DOTALL for multiline support. The docstring is clear with proper examples.

  • _process_alert_body_and_icon in alert.py (lines 35-55): Nicely encapsulates the logic and is reused across all four alert methods, reducing duplication. The function signature using tuple[str, str] is clear.

  • button_group.py refactoring (lines 791-816): The refactoring simplifies the code by delegating to extract_leading_icon instead of inline icon parsing. The split(" ", 1) approach is slightly more efficient than the original split(" ") + " ".join(). The if icon and not remaining check correctly preserves the original semantics where only a pure icon token (not emoji+text) gets extracted.

  • Minor: The content_icon=icon or None on line 816 of button_group.py is functionally equivalent to the old content_icon=icon (since icon is now "" instead of None when no icon is found), but the intent could be slightly more explicit. This is a nitpick.

  • emojis.py (line 36): Adding re.DOTALL to EMOJI_EXTRACTION_REGEX is a global change affecting all callers of extract_leading_emoji, including page name parsing. However, since page names are single-line, this flag only affects the (.*) capture group and is harmless for existing use cases.

Test Coverage

Test coverage is thorough and well-organized:

  • string_util_test.py: 15 parametrized cases for extract_leading_icon covering empty strings, plain text, emoji extraction, material icon extraction, invalid icons, multiline text, and mid-text emojis. Good edge case coverage.

  • alert_test.py: New AlertIconExtractionTest class with 6 test methods, each parametrized across all 4 alert functions (24 test cases total). Covers: emoji extraction, material icon extraction, explicit icon precedence, no-icon body, icon-only body, and multiline body extraction.

  • E2E tests: Two new test cases in st_alert.py (emoji and material icon extraction from body), with corresponding snapshot assertions and text content verification in st_alert_test.py. All 12 snapshot images (6 themes x 2 cases) are included.

  • Missing test: There is no unit test or E2E test for the button_group.py refactoring. While the refactoring is behavior-preserving, it would be reassuring to have at least one test verifying that st.pills with an emoji-prefixed option still extracts the icon correctly after the refactoring. However, existing button_group tests should already cover this implicitly.

Backwards Compatibility

This is a behavioral breaking change for existing users. Previously, st.warning("🔔 Important notification") would display the emoji inline in the body text with no icon. After this PR, the emoji is automatically extracted and displayed as the alert's icon, and the body becomes "Important notification".

Key considerations:

  1. No opt-out mechanism: There is no way for a user to disable auto-extraction while keeping the default no-icon behavior. icon=None (default) triggers extraction. Passing an empty string icon="" raises a StreamlitAPIException. There is no sentinel value like icon=False to explicitly request "no icon, no extraction."

  2. Icon-only body text: st.error("✅") now produces an alert with a checkmark icon and an empty body. Previously, it showed "✅" as the body text. This may be surprising for some users.

  3. The PR is labeled impact:users and change:feature, indicating the team is aware of and has accepted this behavioral change.

  4. The button_group.py refactoring is behavior-preserving. The not remaining check ensures only pure icon tokens are extracted, matching the original logic.

Security & Risk

No security concerns identified. The changes only parse text against known emoji/icon sets and validate material icon names. No user input is executed or used in unsafe contexts.

Regression risks:

  • The re.DOTALL change to EMOJI_EXTRACTION_REGEX is global but low-risk since it only affects the (.*) capture group behavior with newlines, which is harmless for single-line inputs like page names.
  • The button_group refactoring should be closely monitored in case any edge case in icon extraction was inadvertently changed, though analysis shows it's behavior-preserving.

Accessibility

No accessibility concerns. The feature moves icons from inline body text to the dedicated icon slot, which is semantically better for screen readers that can distinguish between icon and text content in the alert structure.

Recommendations

  1. Consider adding an opt-out mechanism for auto-extraction. For example, supporting icon="" (empty string) to mean "no icon, don't extract" would give users a way to preserve the old behavior when they intentionally start body text with an emoji. This could be a follow-up if user feedback warrants it.

  2. Document the breaking change. Ensure the release notes clearly describe this behavioral change, especially for users who have emojis at the start of alert body text intentionally (not as icons).

  3. Add a test for button_group.py refactoring. A simple test verifying that st.pills with [":material/thumb_up: Like", "😃 Smile", "Plain"] still correctly extracts icons would confirm the refactoring is correct. This could be a quick follow-up.

  4. Consider whether st.exception should also get this treatment for consistency across all alert-like elements, if applicable.

Verdict

APPROVED: Well-implemented feature with thorough tests and clean code. The behavioral breaking change is acknowledged via labels and the explicit-icon-takes-precedence design ensures a smooth upgrade path for most users. The recommendations above are non-blocking suggestions for follow-up.


This is an automated AI review by opus-4.6-thinking.

it parsed to the frontend.

Note: The icon is only extracted if it's followed by a space or is the
entire content (icon-only). This matches the original behavior.
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: I would remove the This matches the original behavior. comment as it seems like an LLM point-in-time comment.

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.

Removed 👍

Address PR review feedback to remove point-in-time comments that
reference "original behavior" rather than describing current state.
@lukasmasuch lukasmasuch enabled auto-merge (squash) March 4, 2026 20:29
@lukasmasuch lukasmasuch merged commit 283e372 into develop Mar 4, 2026
44 checks passed
@lukasmasuch lukasmasuch deleted the lukasmasuch/alert-icon-body-extract branch March 4, 2026 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make st.warning etc pick up icons at the beginning of the text

3 participants