Skip to content

feat(python): Make UNSET a PEP 661 sentinel - #1086

Merged
dangotbanned merged 14 commits into
uwdata:mainfrom
dangotbanned:vgplot-python/unset-sentinel
Jul 31, 2026
Merged

feat(python): Make UNSET a PEP 661 sentinel#1086
dangotbanned merged 14 commits into
uwdata:mainfrom
dangotbanned:vgplot-python/unset-sentinel

Conversation

@dangotbanned

@dangotbanned dangotbanned commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Description

As defined on main, UNSET has a few issues - most of which are not that visible.

This PR introduces a conditional-backport for sentinel (python 3.15).
Mosaic gets both the runtime and typing benefits - while requiring no new dependencies .
When available 1, typing_extensions.sentinel is prefered - before defining a minimal 2 vendored version 3

image1

The new tests are a combination of:

  • tests adapted from the typing_extensions test suite (1, 2)
  • examples from the sentinel (python 3.15) docs
  • and a handful of runtime & static cases I didn't steal 😅

What's fixed?

(4dcdb43)

UNSET can be used directly in annotations and is understood by type checkers:

from vgplot._types import UNSET

def function(arg: int | str | UNSET = UNSET) -> None:
    #                       ^^^^^^^
    #      would cause a runtime `TypeError` on main
    from typing_extensions import assert_never, assert_type

    if arg is not UNSET:  # would not perform type narrowing on `main`, 
                          # requires a solution like https://github.com/vega/altair/blob/48b388f140c79d29056d6ea56e519b27e2ed8838/altair/utils/schemapi.py#L1012-L1023
        assert_type(arg, int | str)
    elif isinstance(arg, (int, str)):
        assert_never(arg)
        #            ~~~ branch is unreachable because there is only 1 `UNSET`
    else:
        assert_type(arg, UNSET)

UNSET maintains identity when copied or pickled:

import copy
import pickle

from vgplot._types import UNSET

print(
    copy.copy(UNSET)
    is copy.deepcopy(UNSET)
    is pickle.loads(pickle.dumps(UNSET))
    is UNSET
)
# True

UNSET now has a visible docstring:

image2

Related

Footnotes

  1. typing_extensions>=4.16.0 is installed

  2. I've documented the original source and the differences are the exclusion of deprecation paths,
    which serve no benefit to mosaic.

  3. We've used this technique a few times in narwhals, but not (yet) for sentinel

`UNSET` currently fails on most tests against [PEP 661](https://peps.python.org/pep-0661/) behavior
- Good news: all the tests I wrote are fixed
- Bad news: `ty` is now reporting "Found 3460 diagnostics"
    - Need to tweak the codegen, which relied on `Any`
Fixes all 3460 `ty` diagnostics
Makes sense in `typing_extensions` as a helper,
but here it just adds complexity
@dangotbanned

Copy link
Copy Markdown
Collaborator Author

I've had this sitting ready since shortly after opening #1075

But didn't want to create too much noise - so here's a present now if anyone wants it 😄

@dangotbanned
dangotbanned marked this pull request as ready for review July 18, 2026 22:39
Comment thread packages/vgplot/vgplot-python/vgplot/_compat.py
# Was renamed in https://github.com/python/typing_extensions/releases/tag/4.16.0
from typing_extensions import Sentinel as sentinel # noqa: N813
else: # noqa: PLR5501
if sys.version_info >= (3, 15):

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.

@domoritz

Can we remove this in the future? Would be good to add a note saying when we can remove this?

So this part would be detected by Ruff via outdated-version-block (UP036)

Anything that uses sys.version_info works the same and will be flagged in-step with

requires-python = ">=3.10"

But on this part:

when we can remove this?

Without dependencies?
October 2031

With "typing_extensions>=4.16"?
Any time 😄

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.

I could add some of that here I suppose 😅

image

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm generally a fan of updating version requirements when it helps us clean up stuff. We don't need to support the oldest Python versions so feel free to update version requirements.

@dangotbanned dangotbanned Jul 31, 2026

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.

If we were to update requirements, adding this would be my preference as it is a single file dependency and very common:

dependencies = [
  "typing-extensions>=4.16 ; python_full_version < '3.15'",
]

Depending on the latest python version can simplify maintainence, but it will hurt adoption of Mosaic.
It would mean that no downstream packages can offer backwards compatibility, if they have vgplot as a required dependency

@dangotbanned dangotbanned added the python Pull requests that update Python code label Jul 30, 2026
@dangotbanned
dangotbanned enabled auto-merge (squash) July 31, 2026 09:51
@dangotbanned
dangotbanned merged commit e3cb141 into uwdata:main Jul 31, 2026
5 checks passed
@dangotbanned
dangotbanned deleted the vgplot-python/unset-sentinel branch August 1, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants