WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@lillianpenner
Copy link
Contributor

@lillianpenner lillianpenner commented Nov 30, 2025

Closes issue

Description

This pull request adds type hints to CaselessDict and to the helper functions canonsort_keys and canonsort_items. These annotations improve static analysis and readability while preserving existing behavior. No runtime logic was modified.

Checklist

  • I've added a change log entry to CHANGES.rst.
  • I've added or updated tests if applicable.
  • I've run and ensured all tests pass locally by following Run tests.
  • I've added or edited documentation, both as docstrings to be rendered in the API documentation and narrative documentation, as necessary. (none necessary for this PR)
  • I've added myself to docs/credits.rst as a contributor in this pull request or have done so previously.

Additional information

Tests were run in a fresh virtual environment using:
tox

Results:
= 9698 passed, 21 skipped in 24.68s

I added a small test file (test_type_hints.py) to exercise CaselessDict, canonsort_keys, and canonsort_items, and confirm existing behavior with type hints.


📚 Documentation preview 📚: https://icalendar--1015.org.readthedocs.build/

@coveralls
Copy link

coveralls commented Nov 30, 2025

Pull Request Test Coverage Report for Build 19876975935

Details

  • 50 of 50 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.006%) to 98.012%

Totals Coverage Status
Change from base Build 19795611258: 0.006%
Covered Lines: 9856
Relevant Lines: 10049

💛 - Coveralls

Copy link
Member

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

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

Suggestions fix minor docs issues and improve a few others.

This needs a technical review, but it looks good.

Copy link
Member

@SashankBhamidi SashankBhamidi left a comment

Choose a reason for hiding this comment

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

Can be merged after Steve's suggestions + Optional[Any] fix applied

Copy link
Member

@niccokunzmann niccokunzmann left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution and the tests.... ! It looks good generally. I left some comments. What are your thoughts?

@lillianpenner
Copy link
Contributor Author

Thanks, these suggestions all make sense! Once this PR is ready to be merged, I’d love to help with adding type hints to other modules as well. Are there any files you’d recommend I look at next, or should I start exploring and find untyped areas myself?

@niccokunzmann
Copy link
Member

niccokunzmann commented Dec 1, 2025

@lillianpenner It looks like tests are passing. I was wondering just now: Are keys always str in a CaselessDict? I think so... That could be changed. What do you think? When you looked through it does anything else than str as a key make sense?

When I think about what would be nice next... Probably looking at the public API helps to find the most impactful places. E.g. icalendar.Component could use a few more type hints: https://icalendar.readthedocs.io/en/latest/reference/api/icalendar.cal.component.html

This is a valid contribution and can be merged from my perspective.

@lillianpenner
Copy link
Contributor Author

@niccokunzmann This brings up a good point. Here’s how I’m thinking about it:
Although CaselessDict always stores keys as strings internally, it still accepts incoming keys of any type. Every method normalizes the key via to_unicode(key), so even inputs like integers or bytes are converted to str before calling .upper().

Because of that, I think parameter types for key should stay as Any, since callers may pass non-string keys, while the stored and returned keys are always str, since normalization happens before insertion and lookup.

Please let me know if I’m misunderstanding anything here!

@stevepiercy
Copy link
Member

I was wondering just now: Are keys always str in a CaselessDict?

No. CaselessDict has a base class of OrderedDict and its base class of dict allows keys of any immutable data type, including integers, floats, strings, bytes, tuples (as long as the tuple contains only immutable elements), and booleans.

@lillianpenner @niccokunzmann for icalendar, though, I can't imagine using anything beside strings or bytes for a key in CaselessDict, especially since the expectation is to use the string or bytes Python method of upper() on the key. The docstring of CaselessDict is inaccurate, stating "A dictionary that isn't case sensitive, and only uses strings as keys" (emphasis added), even though it still allows any immutable element as a key. Additionally icalendar's to_unicode method returns either a string or bytes type.

So I'd make sure that each key is either a string or bytes and nothing else in its __init__ method.

Finally, I don't know if it's possible to enforce this through typing, but TypedDict looks promising.

Copy link
Member

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

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

Docs LGTM.

See my last comment and let's discuss whether anything further should be changed.

@lillianpenner
Copy link
Contributor Author

lillianpenner commented Dec 2, 2025

@stevepiercy Thanks for the explanation and the context around CaselessDict keys. That makes sense.

For this PR, I avoided changing any runtime behavior, which is why I left the key parameter typed as Any. I wasn’t sure whether adding runtime restrictions in init would introduce compatibility issues, but I agree that tightening this area could be valuable.

Would you prefer that I explore these ideas in this PR or follow up with these separately?

Side note: I didn't mean to close the PR, but I just reopened it, oops

@stevepiercy
Copy link
Member

@lillianpenner this PR is an improvement because it adds typing. On that point alone, I'd say that this is OK to merge on the condition that a new issue be used specifically for refining the typing, and possibly the code, to enforce that the keys in a CaselessDict be strings or bytes to avoid a crash. There's another issue already open at #1016 where I think it would be good to continue the discussion of how to proceed.

@niccokunzmann @SashankBhamidi thoughts?

@SashankBhamidi
Copy link
Member

Agree, this PR improves typing without breaking changes. The broader key type enforcement discussion should continue in #1016 as Steve suggested.

Copy link
Member

@SashankBhamidi SashankBhamidi left a comment

Choose a reason for hiding this comment

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

Approving to merge.

@niccokunzmann
Copy link
Member

Yes, thanks, way better than before and we can sort our ideas out in another issue.

@niccokunzmann niccokunzmann merged commit 118d84c into collective:main Dec 8, 2025
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants