Skip to content

calendar module fails to import on Wine due to incomplete %OB error handling #155245

Description

@MaurizioGian

Title: calendar module fails to import on Wine due to incomplete %OB error handling

Component: Library (Lib)
Type: bug
Python Version: 3.15

Summary:

The calendar module added in Python 3.15 (gh-131146) fails to import when running
under Wine because the error handling for unsupported %OB/%Ob format specifiers
is incomplete.

The try/except block catches ValueError during _localized_month object construction,
but the actual strftime call happens lazily in getitem when set() iterates
over the sequence in the else block.

Steps to Reproduce:

  1. Run Python 3.15 under Wine (any version 9.0-11.14)
  2. Execute: import calendar

Expected Result:

calendar module imports successfully, falling back to month_name/month_abbr
when %OB/%Ob are not supported

Actual Result:

Traceback (most recent call last):
File "", line 1, in
File "calendar.py", line 156, in
File "calendar.py", line 110, in getitem
ValueError: Invalid format string

Analysis:

In Lib/calendar.py lines 143-156:

try:
standalone_month_name = _localized_month('%OB')
standalone_month_abbr = _localized_month('%Ob')
except ValueError:
standalone_month_name = month_name
standalone_month_abbr = month_abbr
else:
# This calls getitem which triggers the actual strftime call
if len(set(standalone_month_name)) != len(set(month_name)): # <-- ValueError here
standalone_month_name = month_name

The _localized_month constructor just stores the format string; the actual
datetime.strftime() call happens in getitem when set() iterates. Wine's
strftime doesn't support %O and raises ValueError at this point, which is
not caught.

Suggested Fix:

Wrap the set() comparison in the else block with try/except:

try:
standalone_month_name = _localized_month('%OB')
standalone_month_abbr = _localized_month('%Ob')
except ValueError:
standalone_month_name = month_name
standalone_month_abbr = month_abbr
else:
try:
if len(set(standalone_month_name)) != len(set(month_name)):
standalone_month_name = month_name
if len(set(standalone_month_abbr)) != len(set(month_abbr)):
standalone_month_abbr = month_abbr
except ValueError:
standalone_month_name = month_name
standalone_month_abbr = month_abbr

Impact:

This breaks Python 3.15 compatibility with Wine, affecting users who build
Windows executables using PyInstaller under Wine (a common CI/CD pattern).

Related:

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    stdlibStandard Library Python modules in the Lib/ directory
    on Aug 5, 2026
  2. Sudeep-A-Kulkarni commented on Aug 6, 2026

    @Sudeep-A-Kulkarni

    Hi, I'd like to work on this issue. I'll submit a PR with the suggested fix (wrapping the set() comparison in a try/except ValueError) along with a regression test and NEWS entry.

  3. Sudeep-A-Kulkarni commented on Aug 6, 2026

    @Sudeep-A-Kulkarni

    Thanks for the quick fix, @tekinertekin! Since a PR is already up and looks solid, I'll hold off on submitting a separate one and look for another issue to work on instead.

  4. tekinertekin commented on Aug 6, 2026

    @tekinertekin

    Thanks for the quick fix, @tekinertekin! Since a PR is already up and looks solid, I'll hold off on submitting a separate one and look for another issue to work on instead.

    Thanks for being so gracious about this, @Sudeep-A-Kulkarni and sorry for the collision. You claimed this an hour before I opened the PR and I genuinely missed your comment; I only checked for a linked PR, not for someone having called it. Your description of the fix was the same one I ended up with.

    If you'd rather take this one yourself, say so and I'll close mine — you were here first. Otherwise I'm happy for you to review it.

  5. picnixz commented on Aug 6, 2026

    @picnixz
    Member

    I closed @tekinertekin's PR because (1) it's fully generated by an agent and we don't want to encourage such work just to be able to claim a "contributor" badge for CPython as it's not entirely fair to those who made the work by themselves instead of just letting an agent do the job (2) @Sudeep-A-Kulkarni wanted to work on that first.

    @Sudeep-A-Kulkarni Please go ahead, but avoid using an LLM to just write the PR. Try to first find a locale for which we can exercise the issue and otherwise. In addition, make sure that you don't just trust your agent's output. We don't want a fully-LLM generated PR anyway.

  6. Sudeep-A-Kulkarni commented on Aug 7, 2026

    @Sudeep-A-Kulkarni

    I've reproduced the root cause locally (without needing an actual Wine environment) by subclassing datetime.date so strftime raises ValueError whenever %O appears in the format string, then modeling the same lazy-evaluation shape used by _localized_month. This confirms the issue: the try/except ValueError around the _localized_month('%OB') construction only guards the constructor call, but the actual strftime invocation happens lazily later, when set() iterates over the object in the else block — which has no exception handling of its own. That's why Wine's lack of %O support isn't caught.

    I also verified a fix locally: wrapping the set() comparisons inside the else block in their own nested try/except ValueError (falling back to month_name/month_abbr on failure) resolves it cleanly. I'll follow up with a PR that applies this fix along with a regression test that mocks strftime to simulate the %O-unsupported case, so it doesn't require Wine to run in CI.

  7. added 6 commits that reference this issue on Aug 7, 2026
  8. added a commit that references this issue on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-unsupportedstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions