Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Forward reference to enclosing class in optional return type, without typing.Optional?

Python 3.10 introduced the Foo | None alternative type annotation syntax for Optional[Foo]. This doesn’t seem to work when using forward reference to the enclosing type, like 'Foo' | None, which instead results in a runtime error:

class Foo:
    @staticmethod
    def maybe_create(arg: str) -> 'Foo' | None:
        if len(arg) < 5:
            return Foo()
        else:
            return None

if __name__ == '__main__':
    import sys
    print(Foo.maybe_create(sys.argv[1]))
Traceback (most recent call last):
  File "staticmethod-test.py", line 1, in <module>
    class Foo:
  File "staticmethod-test.py", line 3, in Foo
    def maybe_create(arg: str) -> 'Foo' | None:
                                  ~~~~~~^~~~~~
TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'

Using typing.Optional works as expected, like:

from typing import Optional

class Foo:
    @staticmethod
    def maybe_create(arg: str) -> Optional['Foo']:
        if len(arg) < 5:
            return Foo()
        else:
            return None

if __name__ == '__main__':
    import sys
    print(Foo.maybe_create(sys.argv[1]))

Is there a way to express this optionality without extra imports?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Yes. If the union type contains forward references you have to wrap the entire return type in quotes.
Quoting from the documentation,

The | operand cannot be used at runtime to define unions where one or
more members is a forward reference. For example, int | "Foo", where
"Foo" is a reference to a class not yet defined, will fail at runtime.
For unions which include forward references, present the whole
expression as a string, e.g. "int | Foo"
.

class Foo:
    @staticmethod
    def maybe_create(arg: str) -> "Foo | None":
        if len(arg) < 5:
            return Foo()
        else:
            return None
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading