Skip to content

Typechecking appears broken due to off-by-one relative package imports #1765

@CAM-Gerlach

Description

@CAM-Gerlach

Describe the bug

PRAW includes respectable coverage type annotations where practical (particularly given its dynamic nature), which have the potential to be very helpful to downstream developers seeking to catch many bugs statically without having to hit the live Reddit API, as well as PRAW development itself.

PRAW modules that use relative imports include the following block, presumably to help the type checker find the parent package (example from praw/reddit.py):

if TYPE_CHECKING:  # pragma: no cover
    from .. import praw

...with the number of .. increasing with each level deep the given module is in the package. However, this results in the relative import going outside the package, which is neither valid Python (without PEP 420 namespace packages) nor works with MyPy, as it means it recurses into a directory that is not itself a package. In addition, one file (praw/objector.py) has an extra .. (the only file that did not consistently follow this pattern), either due to a typo or being previously located in a subpackage.

The net result is that running mypy (and presumably other type checkers) on the PRAW package, or on user code importing praw after adding a py.typed file to the PRAW package root per PEP 561, will fail with a critical error and prevent type checking entirely, as the . Furthermore, this not only prevents type-checking what is often the most critical code in a Reddit-bot or client (along with PRAW's own correctness), but also causes stubgen to fail with a similar error, so users can't even provide their own stubs.

To Reproduce
Steps to reproduce the behavior:

  1. Install PRAW and MyPy
  2. Run stubgen -p praw -> Error occurs
  3. Run mypy $SITE_PACKAGES/praw -> Error occurs
  4. Run touch $SITE_PACKAGES/praw/py.typed to enable type-checking for PRAW and then mypy $YOUR_PACKAGE on a local package that users PRAW -> Error occurs

Expected behavior

Type checking to work without errors. To fix this issue, the relative imports for TYPE_CHECKING only can be simply changed to absolute imports (import praw), which are shorter, simpler and other than avoiding the errors/invalid behavior, have the exact same intended effect. Because all of these blocks are protected by if TYPE_CHECKING, there is zero runtime impact, it just fixes the type checker.

In addition, to allow PRAW users to take advantage of PRAW's existing type hints to type check their own code, I advise per PEP 561 adding an empty file named py.typed to the root of the package.

I've manually hacked it in there, and it provided helpful type hinting with no major issues and minimal false positives . However, to make this work for other devs locally and on our CIs, we have to run a script that implements these same hacks, which is fragile, ugly and has the potential to break the install. Fixing the import statements will alleviate 90% of that (we'll just have to touch one file), and adding the py.typed will do the rest.

I'm happy to submit a PR to fix this; let me know and I'll go ahead. I'd also be interested in cleaning up the remaining flagged typing inconsistencies in PRAW and getting it set up to continually check for bad types (e.g. with pre-commit, either locally or CI-only) so there isn't a regression on this front. I'd also hope to help add typing to prawcore, if that's desirable, as it currently seems to be missing at least some of it. Thanks!

Code/Logs

Error with mypy $SITE_PACKAGES/praw:

$SITE_PACKAGES/praw/models/base.py:6: error: No parent module -- cannot perform relative import
Found 1 error in 1 file (errors prevented further checking)

Same error with stubgen -p praw:

Critical error during semantic analysis: $SITE_PACKAGES/praw/models/base.py:6: error: No parent module -- cannot perform relative import

System Info

  • OS: Windows 8.1 x64, Ubuntu 20.04 LTS, Raspbian 10
  • Python: 3.7, 3.8, 3.9
  • PRAW Version: 7.2.0, 7.3.0, latest master
  • MyPy Version: 0.902, 0.910 (latest as of when I tested)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions