Comments (17)
One more note which is interesting. This odd behaviour appears specifically with pydantic:
from pydantic import BaseModel
from inspect import isclass
from numpy.typing import NDArray
isinstance(NDArray, type) # True
isclass(NDArray) # True
issubclass(NDArray, int) # False - works
issubclass(int, BaseModel) # False - works
issubclass(NDArray, BaseModel) # Raises TypeError - doesn't work
Hence, this is some weird interaction between NDArray
and pydantic's BaseModel
because they work as expected with other types.
from autodoc_pydantic.
I created a repo with a minimum test case https://github.com/j-carson/bug_report
from autodoc_pydantic.
@j-carson Thanks for reporting this!
It is interesting how numpy's NDArray
behaves in conjunction with issubclass
.
As the resulting error suggests (TypeError: issubclass() arg 1 must be a class
), NDArray does not seem to be a class. However, exactly this is checked before via isinstance(obj, type)
. According to ìnspect.issclass(NDArray)
it is a class, too.
Do you want to provide a PR? I think it is a good issue to start with. If not, I can do it.
from autodoc_pydantic.
Are you OK with the above solution of wrapping this bit of code in a try/except block? If so, I can do a PR.
from autodoc_pydantic.
It is a perfect starting point - give it a go!
The more tricky part is about testing that the issue is fixed without adding numpy as a dev/test dependency. I don't want to add numpy as a dependency just because of this odd ndarray behavior. Instead, it's probably best to mimic numpy's ndarray. However, this may be difficult to achieve. An object being a class and not working with issubclass
at the same time is very strange.
Interestingly, numpy itself has custom function to test for subclasses (see here) which adds a try/except block around pythons builtin issubclass
.
from autodoc_pydantic.
Here's another oddball - it works if I test against BaseModel's parent class, which is 'pydantic.main.ModelMetaclass'
issubclass(NDArray, BaseModel.__class__) # False, no exception raised
from autodoc_pydantic.
This also looks relevant:
https://bugs.python.org/issue45326
I can use
issubclass(dict[str,str], BaseModel)
as a test case that does not require importing numpy.
Edit to add: Hmmm, I have a case where issubclass of BaseModel throws the desired exception and does not require numpy, but I'm not sure how to get autodoc to call autodoc_pydantic with this test case...
from autodoc_pydantic.
OK, so the more I look at it, it's an interaction between a generic type class (Numpy implements it's own backported version so NDArray is a generic across all versions of python https://github.com/numpy/numpy/blob/main/numpy/typing/_generic_alias.py) and an abc-flavored class (BaseModel is an abstract base class).
Rather than writing a bunch of code that checks for generics based on different versions of python, the only practical way to solve this is with the try/except code above - autodoc_pydantic shouldn't be in the business of digging into the internals of types and annotations. There are a lot of libraries forced to do this (the pydantic code dealing with types is scary-complicated) and it would be a real pain to maintain it.
So, I'm happy with my solution. I'm still working through the testing.
from autodoc_pydantic.
This also looks relevant:
https://bugs.python.org/issue45326
I can useissubclass(dict[str,str], BaseModel)
as a test case that does not require importing numpy.
That's exactly what I was thinking of. I found several other related python issues regarding issubclass
bugs but I did not come across this one.
Edit to add: Hmmm, I have a case where issubclass of BaseModel throws the desired exception and does not require numpy, but I'm not sure how to get autodoc to call autodoc_pydantic with this test case...
I'm not sure if I understand you correctly but I assume you want to call autodoc_pydantic
's auto-documenters to confirm that they don't fail anymore when encountering NDArray-like objects. Calling auto-documenters in the test suite has some complexity (test sphinx app required etc...). Perhaps this helps a little. But anyway I can assist you here once you've added your PR.
There is a second test option which does not require calling auto-documenters and which is even more straightforward. You can explicitly write tests for ModelInspector.static.is_pydantic_model
passing all possible objects (e.g. subclasses of BaseModel or subclasses that mimic NDArray). I think you can start with this first and I can help with the auto-documenter integration test.
Rather than writing a bunch of code that checks for generics based on different versions of python, the only practical way to solve this is with the try/except code above - autodoc_pydantic shouldn't be in the business of digging into the internals of types and annotations. There are a lot of libraries forced to do this (the pydantic code dealing with types is scary-complicated) and it would be a real pain to maintain it.
I couldn't agree more 😃.
from autodoc_pydantic.
I am currently just trying to get sphinx to crash again with a test case in my development environment, then I'll worry about hooking it up to pytest. I'm not having any luck reproducing it without numpy...
My little dict[str,str] test case above throws a similar exception to my problem on the oneliner subclass check, but when I try putting a dict[str,str] in my test case, somehow autodoc doesn't do the same thing anymore... Not sure why...
from autodoc_pydantic.
What python version do you use? Given the bug report you've mentioned earlier, it only occurs since python 3.9 upwards.
from autodoc_pydantic.
Also, can you share the example code you've used so far to recreate the issue?
from autodoc_pydantic.
The example code that crashes is in this repo - https://github.com/j-carson/bug_report
I caught it on Python 3.7, but also see it on 3.9. (Numpy has it's own backport of generic typing.)
from autodoc_pydantic.
The code that I was trying to make dict[str,str] crash was to replace the NDArray with the "Trouble" class below, but without success:
try:
BaseClass = dict[str,str]
except TypeError:
from typing import Dict
BaseClass = Dict[str,str]
class Trouble(BaseClass):
pass
While trying to figure out why autodoc was able to handle the above, I was digging into autodoc a little more deeply. I figured out I could configure
autodoc_mock_imports = ['numpy.typing']
and my problems disappear, so there is a workaround for this issue at least.
from autodoc_pydantic.
For pragmatic reasons and due to the minor benefit that a custom test case mimicing numpy's NDArray really adds, I'm considering to just ignore this special case in the test suite. In fact, this test case may introduce more complexity and overhead than value it provides.
Also, it might be useful to simply use a try/except block around the issubclass
because that's really the only relevant information required. If issubclass
fails for whatever reason then it is not a pydantic model. Hence, there is also no need to handle any specific exceptions:
@staticmethod
def is_pydantic_model(obj: Any) -> bool:
"""Determine if object is a valid pydantic model.
"""
try:
return issubclass(obj, BaseModel)
except:
return False
The isinstance(obj, type)
is actually not required anymore. It was used before to prevent exceptions to occur in issubclass
but this is now handled via the try/except block. This latter approach that you've suggested is more robust because it handles all kinds of objects (like NDArray) which might throw some weird exceptions in issubclass
.
To sum up, you're good to go with your PR without mimicing numpy's NDArray. It's enought to add test cases that succeed and fail on is_pydantic_model
. What's your take on this?
from autodoc_pydantic.
This is both easier to do and fine... doing this seems to be unlikely to introduce a new bug. I'll probably have time to start it on Friday and hopefully do a pull request over the weekend or early next week.
from autodoc_pydantic.
Sounds great, thanks! No need to hurry - take your time. I'm happy to review your PR at any point in time.
from autodoc_pydantic.
Related Issues (20)
- Pydantic 2.0+ import issue HOT 1
- Build failure with Enum under Pydantic 2.1, autodoc-pydantic 2.0.0 HOT 6
- @computed_field not showing in json schema HOT 2
- Deprecation warning for `ObjectMember` for Sphinx 7.2.2 HOT 3
- Package not honoring sphinxcontrib namespace package structure HOT 10
- Deprecation warning `sphinx.util.typing.stringify` for Sphinx v7.2.6 HOT 7
- Add `pydantic` classifier to help finding package HOT 2
- Cannot generate documentation when having class with arbitrary types HOT 1
- Cannot use `inherited-members` directive with autosummary HOT 5
- Generate output with nonvalidation methods HOT 4
- ForwardRef.__init__() got an unexpected keyword argument 'is_class' HOT 3
- Render field examples HOT 2
- Type aliases don't propagate for nested pydantic models with inherited members.
- Allow `autodoc_pydantic_field_signature_prefix` to be empty HOT 5
- Does `autodoc-pydantic` support the `pydantic.v1` compatibility layer? HOT 2
- `model_post_init` from `BaseModel` appears in the rendered documentation HOT 5
- [Feature Request] Print json schema as (collapsible) table HOT 2
- model_computed_fields is shown in rendered output HOT 3
- Consider dropping support for sphinx version 4.X HOT 3
- Model inherited from two parents does not seem to show inherited members HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from autodoc_pydantic.