How to add type hints for

I have two questions about using type hints:

  • how to properly use type hints when the type has not been declared yet? For example within a class that declares that type or in a situation where the type cannot get imported because that would cause cyclic imports?
  • how to properly specify type hints when the type is within a private module of some library? For example, I want a parameter that accepts either a string, or a compiled pattern from the “regex” package or a compiled pattern from the “re” package. What would a type hint for these have to look like?

PEP 563 -- Postponed Evaluation of Annotations | Python.org addressed the problem of cycles. Nowadays, annotations are not evaluated directly, so there is no problem as long as you have

from __future__ import annotations

(I have seen discussions on python-dev to change the exact mechanism, storing code objects instead of strings, but that won’t change the fact that forward references are allowed.)

I don’t have an answer to the second question (not being knowledgeable about annotations).

1 Like

I’m not an expert on type hinting, but the standard way to do forward
references is to use a string.

class Spam:
    def method(self, other:'Spam') -> 'Spam':
        pass

In the future, that behaviour will be changed so that annotations will
no longer be evaluated at definition-time, so you can drop the quotes.
Starting in Python 3.7 you can say:

from __future__ import annotations

to postpone evaluation of annotations. Eventually that will become the
default. (3.10 or 3.11 I expect.) See the PEP for details:

https://www.python.org/dev/peps/pep-0563/

Your other question is an interesting one. You shouldn’t rely on
private types, and that includes any type defined in a private module.
Its private, and you can’t rely on it existing and you shouldn’t be
touching it at all, not even as a type hint.

However Python’s re module is not private, and the type of compiled
patterns is not private either. So its just a type:

from re import Pattern

If you don’t want to do the actual import, then treat it as a forward
reference. I think this would work, although to be honest I’m not sure
if type checkers understand dots:

def function(arg:'re.Pattern') -> str:
    pass

I would hope that the third-party regex module defines a similar public
class, so you can do this:

from typing import Union
Pattern = Union['re.Pattern', 'regex.Pattern']

As I said, I’m not a type hinting expert, so it would be good if you
could reply here and let us know if this actually works.

1 Like

Thank you – I was unsure about using strings as I think my IDE and several linters kept complaining about that. Maybe I should just drop support for Python 3.6 and use the solution available in 3.7.

That other problem is a more general though: even though Python does provide a special type for its own re module, there is apparently no such thing for regex. Neither re nor regex provide a public type for the Pattern or Match classes.

Also, this problem generalizes to all parameters which should get restricted to instances of types from third party packages which often are private. I think two approaches to development clash here, since many packages deliberately have implemented things in a way where the point is that the actual type of something does not matter, only how it behaves matters (duck typing) while the type hinting is based on nominal types, not behaviour.

I would really like to use type hinting and benefit from what it could do in the IDE and before running the code, but these things are confusing me as to how far it can actually get me in situations where a lot of the types come from other packages where I do not have any control.

It’s available in the typing module.

from typing import Pattern

Try taking a peek inside typing, lots of interesting things there :wink:

Thanks! But this is what I meant, the Pattern type is what Python provides for its own re module, but there is no equivalent for the pattern and match types from the regex module (which is more powerful than re in some details), and there are other libraries which return instances of “private” classes which are not known to the type system. Of course I can use string type names in all those places but it feels wrong.

Well, since it’s a third-party module, you really should raise an issue on the module’s repo.