Add Annotate Return Type On Enum Function as a Python TIL

This commit is contained in:
jbranchaud
2026-08-07 22:27:43 -05:00
parent a83030bb6d
commit 47b83535b8
2 changed files with 63 additions and 1 deletions
+2 -1
View File
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1854 TILs and counting..._
_1855 TILs and counting..._
See some of the other learning resources I work on:
@@ -1076,6 +1076,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
- [Access SQLite Result Values By Name With Row Factory](python/access-sqlite-result-values-by-name-with-row-factory.md)
- [Access Variables Outside Loop Scope](python/access-variables-outside-loop-scope.md)
- [Annotate Return Type On Enum Function](python/annotate-return-type-on-enum-function.md)
- [Another Way To Mark Keyword-Only Dataclass Fields](python/another-way-to-mark-keyword-only-dataclass-fields.md)
- [Argument Defaults Are Evaluated When Function Is Defined](python/argument-defaults-are-evaluated-when-function-is-defined.md)
- [Assert Is Only A Development Check](python/assert-is-only-a-development-check.md)
@@ -0,0 +1,61 @@
# Annotate Return Type On Enum Function
I defined the following `StrEnum` class to capture the canonical set of valid
values for a _storage format_ in
[`py-vmt`](https://github.com/jbranchaud/py-vmt). It includes a `default`
function that can be called to get what the system considers the default storage
format.
```python
class StorageFormat(StrEnum):
SQLITE = "sqlite"
JSON = "json"
@staticmethod
def default():
return StorageFormat.SQLITE
```
This works, but what is missing is a type annotation for the return value of
`default`.
I'd like to use `StorageFormat` as the return type annotation. However, the
annotations are eagerly evaluated at the time the function definition is being
processed. The `StorageFormat` class is not fully evaluated at that point and so
isn't available.
Instead, what I can do is quote `StorageFormat` as a string to make it a
_forward reference_.
```python
class StorageFormat(StrEnum):
SQLITE = "sqlite"
JSON = "json"
@staticmethod
def default() -> "StorageFormat":
return StorageFormat.SQLITE
```
The string type annotation will be lazily evaluated at a later time when
`StorageFormat` fully exists. This is behavior that is slated to be deprecated.
And it is no longer needed as of [Python 3.14 which lazily evaluates all
annotations by default
now](https://docs.python.org/3/reference/compound_stmts.html#annotations).
If I want to be more specific with the above annotation, I can import [`Literal`
from `typing`](https://docs.python.org/3/library/typing.html#typing.Literal) and
then wrap a specific enum value.
```python
class StorageFormat(StrEnum):
SQLITE = "sqlite"
JSON = "json"
@staticmethod
def default() -> "Literal[StorageFormat.SQLITE]":
return StorageFormat.SQLITE
```
Now it will specifically enforce `SQLITE` rather than accepting `SQLITE` or
`JSON`.