Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sphinx/autodoc reports metaclass conflict #11

Closed
antonagestam opened this issue Jan 23, 2022 · 9 comments
Closed

Sphinx/autodoc reports metaclass conflict #11

antonagestam opened this issue Jan 23, 2022 · 9 comments

Comments

@antonagestam
Copy link

Hi again 👋

When adding numerary to phantom-types I forgot to update the dependencies of the docs build, which left two sections empty 😬 I'll need to work on the CI so that it blows up earlier for that, and obviously fix the missing imports.

However, as I'm trying to reproduce this failure locally (was a silent warning in CI), I get the traceback below. It seems to indicate that numerary has a metaclass conflict, which confuses me profoundly since both your and my test suites are passing. It's also not reproduced when I in the same environment fire up a REPL and do from phantom.sized import *.

WARNING: autodoc: failed to import module 'sized' from module 'phantom'; the following exception was raised:
Traceback (most recent call last):
  File "/Users/annevoab/.pyenv/versions/docs/lib/python3.10/site-packages/sphinx/ext/autodoc/importer.py", line 32, in import_module
    return importlib.import_module(modname)
  File "/Users/annevoab/.pyenv/versions/3.10.0/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/annevoab/projects/phantom-types/src/phantom/sized.py", line 27, in <module>
    from numerary.types import RealLike
  File "/Users/annevoab/.pyenv/versions/docs/lib/python3.10/site-packages/numerary/__init__.py", line 13, in <module>
    from .types import *  # noqa: F401,F403
  File "/Users/annevoab/.pyenv/versions/docs/lib/python3.10/site-packages/numerary/types.py", line 353, in <module>
    class SupportsAbs(
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

It seems very likely that isn't really an issue with numerary but rather with my sphinx/autodoc setup, but I it seemed worth reaching out here in case someone has ran into similar issues. Feel free to instantly close this though as I doubt it's actionable within numerary.

@posita
Copy link
Collaborator

posita commented Jan 24, 2022

Thanks for raising this! Obviously, numerary shouldn't break doc generation. If it does, that should be fixed.

I saw something (superficially) similar that came up during my attempt to port numerary's caching functionality to beartype (beartype/beartype#86). I'm wondering if it's related. I think we can get to the bottom of this, though. Let me take a look….

@posita
Copy link
Collaborator

posita commented Jan 24, 2022

What's the workflow you use that generates that error? Is it something like pip install --requirement docs/requirements.txt && make -C docs html? UPDATE: Yup. That looks like it lands on the same issue. Choking on phantom-types/src/phantom/sized.py#L27 among several other places.

@antonagestam
Copy link
Author

antonagestam commented Jan 30, 2022

@posita I think I've found something interesting, if I fire up a normal Python REPL in my project, I can do this as expected:

>>> from numerary import RealLike
>>> isinstance(RealLike, type)
True

But if I put in a breakpoint() within my code and run the sphinx build, I get this:

(Pdb) from numerary import RealLike
(Pdb) isinstance(RealLike, type)
False

I have no idea why yet, but it seems highly relevant ... 🤔

Edit: also this:

Normal REPL

>>> type(RealLike)
<class 'numerary.types.CachingProtocolMeta'>

At breakpoint

(Pdb) type(RealLike)
<class 'numerary.RealLike'>

@posita
Copy link
Collaborator

posita commented Jan 30, 2022

Thanks! I did take a look at this and was equally stupefied. Rest assured I haven't forgotten about you! I'm working with the beartype project on some mods to the underlying protocol that may help with this. (Please! I hope! 🤞😖) I'm also hoping to make resolving #10 more straightforward. Apologies for the apparent lack of progress on this. Still digging in!

@antonagestam
Copy link
Author

@posita No worries, take your time 👍

@posita
Copy link
Collaborator

posita commented Jan 30, 2022

I think I found the issue! The (good?) news is that it wasn't what I thought it was, so thanks for the nudge!

For some reason, at some point, your Sphinx build process invokes something that ends up with typing.TYPE_CHECKING set to True and then (re?)loads numerary.numerary.types, which ends up here:

if TYPE_CHECKING:
    # Warning: Deep typing voodoo ahead. See
    # <https://github.com/python/mypy/issues/11614>.
    from abc import ABCMeta as _ProtocolMeta  # <-- this is a lie that is only there for type-checking; it should never be executed at runtime
else:
    _ProtocolMeta = type(Protocol)

I discovered this by adding the following lines to numerary/numerary/types.py immediately prior to where the exception occurs:

if not issubclass(CachingProtocolMeta, (type(_SupportsAbs), type(Protocol))):
    breakpoint()

Once that triggered, it allowed me to inspect CachingProtocolMeta:

(Pdb) CachingProtocolMeta.__bases__
(<class 'abc.ABCMeta'>,)
(Pdb) TYPE_CHECKING
True

Yikes. 😬

The documentation for typing.TYPE_CHECKING reads, unequivocally (emphasis mine):

A special constant that is assumed to be True by 3rd party static type checkers. It is False at runtime.

So TYPE_CHECKING should never be True during execution. It doesn't appear to be set to True anywhere in the standard library (despite suspiciously showing up in the docs for importlib).

I'm pretty sure this is on sphinx-autodoc-typehints. How do I know? This is about as close to a smoking gun as one can find:

% find ~/.virtualenvs/phantom-types/lib/python3.9/site-packages -type f -print0 | xargs -0 grep -E 'TYPE_CHECKING.*=.*True'
…/.virtualenvs/phantom-types/lib/python3.9/site-packages/sphinx_autodoc_typehints.py:        typing.TYPE_CHECKING = True

Yowza. 🤯 (As an aside, some of these are cringe worthy.)

I updated my clone of your repo via pip install --upgrade 'sphinx-autodoc-typehints>=0.15.3' 'furo==2022.01.02' and the error went away. The update of furo is to resolve this warning that appears when upgrading sphinx-autodoc-typehints alone, namely:

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
furo 2021.4.11b34 requires sphinx~=3.0, but you have sphinx 4.4.0 which is incompatible.

Are you able to upgrade? If so, can you verify on your end?

@antonagestam
Copy link
Author

Oh jeez, that's some exceptional digging! 🤯 I'll look into upgrading the docs dependencies. Beers are on me if you're ever in Gothenburg, Sweden!

@antonagestam
Copy link
Author

It looks like this solved the whole thing, docs built fine on main and I'm about to push out a patch release. Thanks again! 🥳

@posita
Copy link
Collaborator

posita commented Jan 30, 2022

Woohoo!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants