1
def get_source(import_name: str) -> Optional[str]:
    spec = get_spec(import_name)
    # Now, here. The spec.loader might be one of several values.
    # But I *know* that it is gonna be importlib.machinery.SourceFileloader
    # I need to typecast the attribute of the spec.loader into the above
    if spec and spec.loader.path.endswith('.py'):
        return spec.loader.get_data(spec.loader.path).decode("utf-8")
    return None


def get_spec(import_name: str) -> importlib.machinery.ModuleSpec:
    try:
        return importlib.util.find_spec(import_name)
    except (ImportError, AttributeError, TypeError, ValueError):
        return None

Apparently PEP526 allows what I'm trying to do with the most basic syntax imaginable

obj.attr: annotation

However apparently type checkers do not have to support this syntax as far as I can tell from Rossum's comment. And mypy gives the error Type cannot be declared in assignment to non-self attribute.

Now, according to another issue I found on github on typeshed, you can do asserts to let mypy know the type that the object. Instead of typehitting.

assert isinstance(obj.attr, annotation)

However this, to me, feels wrong. I'm trying to use typing features if possible, and the project I'm trying to contribute uses mypy as their type checker.

The assert version that works but makes me hate myself is:

def get_source(import_name: str) -> Optional[str]:
    spec = get_spec(import_name)
    assert isinstance(spec.loader, importlib.machinery.SourceFileLoader)
    assert isinstance(spec.loader.path, str)
    if spec and spec.loader.path.endswith('.py'):
        return spec.loader.get_data(spec.loader.path).decode("utf-8")
    return None

Countless typeshed and mypy issues I've read didn't help, so I'm here.

Işık Kaplan
  • 2,815
  • 2
  • 13
  • 28
  • 1
    Asserts look like the semantically appropriate option to me. A type annotation would be inappropriate - statically speaking, you don't actually know `spec` is a `SourceFileLoader`. You're making a dynamic assertion. – user2357112 Sep 26 '20 at 09:12
  • Technically there's `typing.cast`, but that's less safe. `typing.cast` makes more sense for static types that *can't* be checked dynamically, or are expensive to check dynamically. – user2357112 Sep 26 '20 at 09:15
  • I tried `typing.cast` but couldn't quite figure out how it works, and say for my example, the `spec.loader` is definitely as `SourceFileLoader`, that's why we don't check if is a `FileLoader` or `SourceLoader` already. How should I let mypy know that? – Işık Kaplan Sep 26 '20 at 09:18
  • With an `assert`. – user2357112 Sep 26 '20 at 09:19
  • Oh welp, still feels weird but if that's what I have to do, it's what I'll do. Danke Schön. – Işık Kaplan Sep 26 '20 at 09:22
  • 1
    I think the problem is that you're thinking of annotations as making a *claim*, when they're really about *definitions*. You want to use an annotation to *claim* to the type checker that this particular value of `spec.loader` in this particular code location is a SourceFileLoader, but mypy reads it as a definition that doesn't make sense. The type of `spec.loader` was already defined elsewhere, and it doesn't make sense to redefine it here. – user2357112 Sep 26 '20 at 09:29

0 Answers0