Skip to content

stubtest: Spurious warning related to symbols in '__all__' #9935

Closed

Description

@dalcinl

Bug Report

I'm getting an error from stubtest checking a package that lists a submodule in __all__.

To Reproduce

  1. Create a package with a submodule
$ mkdir pkg
$ touch pkg/__init__.py
$ touch pkg/mod.py
  1. So far, stubtest seems to be happy with it
$ PYTHONPATH=$PWD stubtest pkg
  1. Now add mod to __all__ in __init.py and run stubtest again
$ echo "__all__=['mod']" > pkg/__init__.py
$ PYTHONPATH=$PWD stubtest pkg
error: pkg.mod is not present in stub
Stub:
MISSING
Runtime:
MISSING

Note, adding respective *.pyi files do not fix the problem.

  • Mypy version used: latest master
  • Python version used: 3.9
  • Operating system and version: Linux (Fedora 33)

Activity

  1. hauntsaninja commented on Jan 22, 2021

    @hauntsaninja
    Collaborator

    This example is confusing, since the "stub" stubtest is looking at is actually just the source file. This is a weird way of using stubtest — what are you trying to accomplish?

    Moreover, the complaint is kind of true.

    λ PYTHONPATH=$(pwd) python3 -c 'import pkg; pkg.mod'
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    AttributeError: module 'pkg' has no attribute 'mod'
    

    Note if you were to add from . import mod to your __init__.py, things would work.

    Finally, the flag --ignore-missing-stub will let you ignore errors caused by things missing from your stubs.

  2. dalcinl commented on Jan 22, 2021

    @dalcinl
    Author

    As I mentioned before, adding *.pyi files do not fix the problem.

    Let's try again, using *.pyi stub files alongside the *.py files:

    $ mkdir pkg
    $ touch pkg/__init__.py
    $ touch pkg/__init__.pyi
    $ touch pkg/mod.py
    $ touch pkg/mod.pyi
    $ PYTHONPATH=$PWD stubtest pkg
    $ echo "__all__=['mod']" > pkg/__init__.py
    $ PYTHONPATH=$PWD stubtest pkg
    error: pkg.mod is not present in stub
    Stub:
    MISSING
    Runtime:
    MISSING

    IMHO, the complaint is not true, this is a perfectly valid Python package. The fact that mod is listed in __all__ does not imply that the package's __init__.py should import the submodule. To the best of my knowledge, the purpose of __all__ is to control how from pkg import * behaves, look yourself:

    $ PYTHONPATH=$PWD python -c "from pkg import *; print(mod)"
    <module 'pkg.mod' from '/tmp/pkg/mod.py'>

    so there is no real requirement for from . import mod in pkg/__init__.py.

    I'm not sure of this behavior of stubtest on handling __all__ is a bug or it is intentional, but in any case it is incorrect. Again, listing 'something' in __all__ does not mean that something has to be present in the namespace. That's my understanding from Python's documentation, and it has worked that way at least since the beginnings of this century, when we where using Python 2.2.

    I'm trying to use stubtest to setup a sort of "lint" for my manually maintained stubs. The flag --ignore-missing-stub is not an option, it does not operate at the stub file level, it will ignore actual missing stub entries.

  3. added 2 commits that reference this issue on Jan 22, 2021
    404d27a
    67d1b26
  4. hauntsaninja commented on Jan 22, 2021

    @hauntsaninja
    Collaborator

    Okay, gotcha. I think mypy in general doesn't handle this use of __all__ correctly. Leaving stubtest aside for now, something like:

    cat > pkg/__init__.py <<HERE
    __all__ = ["mod", "x"]
    x: int
    y: str
    HERE
    
    touch pkg/mod.py
    
    cat > script.py <<HERE
    from pkg import *
    reveal_type(mod)  # incorrectly does not resolve
    reveal_type(x)  # correctly resolves
    reveal_type(y)  # correctly does not resolve
    HERE
    

    Anyway, for now I opened #9943 which should fix the runtime half of stubtest. That is, after that PR you would be able to get things working by adding from . import mod to your stub.

    cc @srittau since you were resurrecting the stub PEP and you might have opinions about how __all__ in stubs should work

  5. srittau commented on Jan 23, 2021

    @srittau
    Contributor

    We decided that it's probably best if __all__ in stubs works exactly like it does in implementations. It must be a list literal of string and customizes export behavior.

  6. added a commit that references this issue on Jul 26, 2021
    6fd2dc2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions