Skip to content

fix(models): type CardBlock icon/hero_image as image element (not str) - #1937

Open
zimeg wants to merge 2 commits into
mainfrom
feat/card-block-image-element-typing
Open

fix(models): type CardBlock icon/hero_image as image element (not str)#1937
zimeg wants to merge 2 commits into
mainfrom
feat/card-block-image-element-typing

Conversation

@zimeg

@zimeg zimeg commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

The card block reference specifies that icon and hero_image are image element objects ({"type": "image", "image_url": ..., "alt_text": ...}), not plain URL strings. But CardBlock typed both as Optional[str], so passing an ImageElement — the correct, accessible form (it carries alt_text) — raised a mypy [arg-type] error and forced callers to use # type: ignore.

This widens both parameters to Optional[Union[str, dict, ImageElement]], matching how title/subtitle/body already accept Union[str, dict, TextObject].

Details

  • slack_sdk/models/blocks/blocks.pyCardBlock.__init__: hero_image/icon typed Optional[Union[str, dict, ImageElement]]; docstrings updated to the reference wording (incl. the max-length notes).
  • Runtime behavior is unchanged — serialization already handled all three forms: str → bare URL (back-compat, still covered by test_document), dict/ImageElement → the object shape. This is a type-widening only.
  • Adds test_image_element_icon_and_hero_image asserting the ImageElement form round-trips to the documented object.

Testing

  • pytest tests/slack_sdk/models/test_blocks.py85 passed (incl. the new test; no regressions).
  • mypy slack_sdk/models/blocks/blocks.py — clean.

Motivation

Downstream: the Bolt bolt-python-examples card/carousel examples build icon/hero_image with ImageElement (docs-correct) and currently need # type: ignore[arg-type] because of this mis-typing. This fix lets those examples drop the suppression.

🤖 Generated with Claude Code

Per the card block reference, `icon` and `hero_image` are image element
objects (`type`/`image_url`/`alt_text`), not plain URL strings — but
CardBlock typed them as Optional[str], so passing an ImageElement (the
correct, accessible form with alt_text) tripped mypy [arg-type].

Widen both to Optional[Union[str, dict, ImageElement]], matching how
title/subtitle/body already accept Union[str, dict, TextObject]. Runtime
serialization is unchanged (str -> bare URL, dict/ImageElement -> object);
this only broadens the accepted/typed input. Docstrings updated to the
reference wording. Adds a test asserting the ImageElement form round-trips.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg
zimeg requested a review from a team as a code owner August 4, 2026 23:05
@zimeg zimeg added bug M-T: A confirmed bug report. Issues are confirmed when the reproduction steps are documented semver:minor python Pull requests that update Python code Version: 3x labels Aug 4, 2026
@zimeg zimeg self-assigned this Aug 4, 2026
@zimeg zimeg added this to the 3.x milestone Aug 4, 2026
zimeg added a commit to slack-samples/bolt-python-examples that referenced this pull request Aug 4, 2026
Examples should not carry type-checker suppressions. Remove the
# type: ignore[arg-type] added for the ImageElement icon/hero_image args.
Until slackapi/python-slack-sdk#1937 lands (widening CardBlock.icon /
hero_image to accept an image element), mypy reports [arg-type] here by
design — the honest signal that slack_sdk mis-types these fields as str.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
zimeg added a commit to slack-samples/bolt-python-examples that referenced this pull request Aug 4, 2026
Examples should not carry type-checker suppressions. Remove the
# type: ignore[arg-type] on the ImageElement icon/hero_image args. Until
slackapi/python-slack-sdk#1937 lands (widening CardBlock.icon / hero_image
to accept an image element), mypy reports [arg-type] here by design — the
honest signal that slack_sdk mis-types these fields as str.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg

zimeg commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Note on keeping str in the type

The card block reference documents icon/hero_image as image-element objects only — it never shows a bare URL string. So one could argue for typing these as Union[dict, ImageElement] (object-only).

This PR deliberately keeps str (Union[str, dict, ImageElement]) because:

  • The string form is already shipped and tested (CardBlockTests.test_document passes icon/hero_image as URL strings and round-trips them). Dropping str would be a breaking change (semver:major) and would fail that test.
  • This change is purely additive — it widens the accepted/typed input to include the docs-correct object form, without removing the existing behavior. That keeps it semver:minor.

If maintainers prefer to make these object-only, that'd be a separate major-version change (update test_document, drop str). Happy to follow up if that's the preference.

Slack's card block schema (and the docs reference) define icon/hero_image
as image element objects — a bare URL string is not a valid payload and is
rejected by the API. CardBlock previously typed them as Optional[str],
modeling a form that does not actually work end to end.

Type both as Optional[Union[dict, ImageElement]] (object-only), matching
title/subtitle/body's object-based typing. Update test_document to the
documented object form (it previously passed bare-string icons). Runtime
serialization of an ImageElement/dict was already correct; this drops the
never-valid str form.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg zimeg changed the title fix(models): CardBlock icon/hero_image accept an image element fix(models): type CardBlock icon/hero_image as image element (not str) Aug 4, 2026
@zimeg zimeg modified the milestones: 3.x, 3.44.0 Aug 4, 2026
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.17%. Comparing base (c2eacc2) to head (bb2a6a1).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1937   +/-   ##
=======================================
  Coverage   84.17%   84.17%           
=======================================
  Files         118      118           
  Lines       13425    13425           
=======================================
  Hits        11301    11301           
  Misses       2124     2124           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving a callout on the breaking change that I'm marking as a patch update 📣

Comment on lines -952 to -953
hero_image: Optional[str] = None,
icon: Optional[str] = None,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug M-T: A confirmed bug report. Issues are confirmed when the reproduction steps are documented python Pull requests that update Python code semver:patch Version: 3x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant