Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add UnionType support to query method #5578

Merged
merged 5 commits into from
Mar 26, 2025

Conversation

zarch
Copy link
Contributor

@zarch zarch commented Feb 23, 2025

Add Union Type Support to Query Method

This PR adds support for using Python's Union type syntax (|) in the query method, allowing developers to search for multiple widget types in a single query. This makes the API more intuitive and concise when searching for different kinds of widgets.

Features

  • Query for multiple widget types using the | operator
  • Support for nested unions (e.g., (Input | Select) | Static)
  • Type validation ensuring all union members are Widget subclasses
  • Deduplication of results when using nested unions

Usage Examples

# Find all Input or Select widgets
app.query(Input | Select)

# Combining multiple widget types
app.query(Input | Static | Select)

# Using nested unions
TextOrInput = TextArea | Input
app.query(TextOrInput | Button)

Technical Details

The implementation converts Union types into CSS selectors internally, maintaining compatibility with the existing query system. It handles nested unions by recursively extracting all unique widget types and validates that all types in the union are Widget subclasses.

Testing

Added comprehensive test coverage including:

  • Basic union type queries
  • Nested unions
  • Type validation
  • Widget uniqueness
  • Edge cases with additional widgets

Backwards Compatibility

This change is fully backwards compatible. Existing query usage with strings, single types, or None continues to work as before.

Please review the following checklist.

  • Docstrings on all new or modified functions / classes
  • Updated documentation
  • Updated CHANGELOG.md (where appropriate)

Allow querying for multiple widget types using | operator, such as
app.query(Input | Select). Supports nested unions and validates widget types.
@willmcgugan
Copy link
Collaborator

I do like this syntax!

@willmcgugan
Copy link
Collaborator

Failing to import UnionType. I wonder if that is in typing_extensions?

@zarch
Copy link
Contributor Author

zarch commented Mar 12, 2025

Failing to import UnionType. I wonder if that is in typing_extensions?

@willmcgugan I've investigated this and found that UnionType is only available natively in Python 3.10+. I checked typing_extensions as well, but it doesn't provide a backport for UnionType.

This means we would need to update our Python requirement from >=3.8 to >=3.10 to support this feature.

Regarding Python version support:

  • Python 3.8 reached end of security support in October 2024
  • Python 3.9 will reach end of security support in October 2025

Here's the current Python release timeline for reference:

Release Released Active Support Security Support Latest
3.13 07 Oct 2024 01 Oct 2026 31 Oct 2029 3.13.2 (04 Feb 2025)
3.12 02 Oct 2023 02 Apr 2025 31 Oct 2028 3.12.9 (04 Feb 2025)
3.11 24 Oct 2022 01 Apr 2024 (Ended) 31 Oct 2027 3.11.11 (03 Dec 2024)
3.10 04 Oct 2021 05 Apr 2023 (Ended) 31 Oct 2026 3.10.16 (03 Dec 2024)
3.9 05 Oct 2020 17 May 2022 (Ended) 31 Oct 2025 3.9.21 (03 Dec 2024)
3.8 14 Oct 2019 03 May 2021 (Ended) 07 Oct 2024 (Ended) 3.8.20 (06 Sep 2024)

Since this would be a breaking change in Python compatibility, how would you like to proceed?
Would you prefer to:

  1. Raise the minimum Python version to 3.10+
  2. Drop this feature and in case resume it later

What are your thoughts on this?

@willmcgugan
Copy link
Collaborator

That is disappointing! I'm afraid the jump from 3.8 to 3.10 is too large, and we like to support Python's for a while after their EOL.

It's a nice feature, and I'd hate to have to wait. How about we make it conditional on Python 3.10 or above?

- Use try/except to conditionally import UnionType and get_args
- Make UnionType overload signature conditional
- Add runtime checks before using UnionType functionality
- Maintain backward compatibility with Python <3.10

This change allows users on Python 3.10+ to query with Union types
while preserving compatibility with earlier Python versions.
@zarch
Copy link
Contributor Author

zarch commented Mar 12, 2025

That is disappointing! I'm afraid the jump from 3.8 to 3.10 is too large, and we like to support Python's for a while after their EOL.

Yes, definitely it make sense.

It's a nice feature, and I'd hate to have to wait. How about we make it conditional on Python 3.10 or above?

@willmcgugan Thanks for the suggestion, I didn't think about this option.
I've implemented conditional UnionType support using a try/except approach.

The changes:

  1. Conditionally import UnionType and get_args using try/except
  2. Make the UnionType overload signature conditional with an if-check
  3. Add runtime checks before using any UnionType-specific functionality
  4. Preserve the parameter typing to maintain IDE hints for Python 3.10+ users

This approach allows Python 3.10+ users to benefit from Union type queries while maintaining full backward compatibility. Users on older Python versions simply won't have access to this specific feature, but all other functionality continues to work normally.

Let me know if you'd like any adjustments to this implementation!

@zarch
Copy link
Contributor Author

zarch commented Mar 12, 2025

I've thoroughly tested this change across multiple Python versions: 3.8 (representing pre-UnionType versions), 3.10 (first version with UnionType support), and 3.13 (latest version).

Tests were executed using:
uvx --python=3.8 poetry run pytest tests/test_dom.py
uvx --python=3.10 poetry run pytest tests/test_dom.py
uvx --python=3.13 poetry run pytest tests/test_dom.py

All tests pass successfully on each Python version, confirming that the conditional implementation works as expected - with UnionType support on 3.10+ and graceful degradation on earlier versions.

@zarch
Copy link
Contributor Author

zarch commented Mar 21, 2025

@willmcgugan Should I add in the PR also a change in the Changelog file and on the documentation?
Or it is something that is generally handled by someone from textual?

@willmcgugan
Copy link
Collaborator

Could use an entry in the Changelog. I'll add that, unless you beat me too it. I'll also add something to the docs.

@willmcgugan
Copy link
Collaborator

Fixes the tests. That pattern with fixtures doesn't work for some reason.

@willmcgugan willmcgugan merged commit 7dcd1d3 into Textualize:main Mar 26, 2025
1 check 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.

2 participants