Closed sunildkumar closed 9 months ago
Hey, this usually happens when the module gets imported by the Python runtime, before beartype was initialized.
Can you share some more details, eg. the exact import package/subpackage present in the error message?
Right - that makes sense. For more context, I'm trying to use your tool to add beartype
during testing of a django
project (unfortunately this repo is private 😞 ). A django
project is broken into "django apps" (see docs) which are like submodules within the django project. I tried to add testing to a single "django app" inside my project. The project has a single pyproject toml, where I specified the submodule to add beartype
to, and each application within the django
project has an __init__.py
. I tried to solve this problem by writing my own pytest
plugin that would import beartype
before django
had a chance to init, but that didn't work unfortunately. I understand this is very niche, but any insights or suggestions on how to better integrate pytest-beartype
with django
would be very appreciated!
Sorry for the radio silence, I'll see if I can reproduce this case this week.
Likewise, apologies for the delay. I forgot to properly watch this repo. Oopsie. :woozy_face:
Probably nobody wants to hear this, but... the unfortunate reality is that pytest-beartype
won't be able to automate this issue away for you, @sunildkumar. Django or somebody else has (for whatever reason) already imported your package before pytest-beartype
gets its chance. There's basically nothing pytest-beartype
can do about that. Since Python doesn't support module reloading, ...officially, anyway pytest-beartype
can't just "unimport" your package and then quietly re-import your package with @beartype
support.
That said, there is still something you can do about this – but you'll have to get your hands a little dirty. I like dirt under my fingernails, but not everybody does. Your own tolerance for special dark magic may vary. If you're still interested, you can:
{your_package}.__init__
whether you are currently running tests (i.e., whether your test
package has been imported or not).beartype_this_package()
.The code is actually super trivial. So, I'd at least consider trying this on your end:
# In your top-level "{your_package}.__init__" submodule:
import sys
# If this package's test suite is currently being run, runtime type-check this package.
if 'test' in sys.modules: # <-- replace 'test' with your top-level test directory name
from beartype.claw import beartype_this_package
beartype_this_package()
That approach also has the added benefit of configuration. That is, you can explicitly configure @beartype with app-specific logic by passing a custom conf=BeartypeConf(...)
parameter to the above beartype_this_package()
call. On the other hand, pytest-beartype
supports no configuration.
And... I'm spent. Let's sleep, everybody. :sleepy:
Lovely! I'll try it out soon and update this issue appropriately. Thanks!
Unfortunately the dark magic failed, as django
practices a darker insidious magic 😞. I was able to verify this, as manually annotating functions with @beartype
worked. So something with beartype_this_package()
doesn't play nice with how django
initializes stuff.
Thank you for trying! You're welcome to close this issue.
Wait! Hold the Django presses, @sunildkumar. An even darker magic avails us. Beartype actually supports multiple types of import hooks. beartype_this_package()
is just the most syntactically convenient one, which is why our documentation pushes it everywhere. As you've noted, however, it tends to not play nicely with big dynamic frameworks like Django.
If you'll bear ...heh with me for a moment, would you mind trying one final act of desperation? Please replace beartype_this_package()
with beartype_package('{your_package}')
: e.g.,
# In your top-level "{your_package}.__init__" submodule:
import sys
# If this package's test suite is currently being run, runtime type-check this package.
if 'test' in sys.modules: # <-- replace 'test' with your top-level test directory name
print('Let's make sure something is actually happening, people.') # <-- just to verify that "if" statement is doing something
from beartype.claw import beartype_package
beartype_package('{your_package}') # <-- replace '{your_package}' with your top-level main directory name
Did the print()
statement happen? Did beartype_package()
still do absolutely nothing? Tune in next time to find out the stunning answer to these questions and more – including, "Why did @leycec eat that five day-old baloney sandwich in the fridge, anyway?"
Still no luck 😞. I actually added a print to your original suggestion to verify the code was running, and it was. Even unconditionally using either of the import hooks fails, e.g.
# in __init__.py
from beartype.claw import beartype_package
beartype_package('my_package')
and
# in __init__.py
from beartype.claw import beartype_this_package
beartype_this_package()
fail to catch a poorly annotated function. However, beartype
easily saves the day when I decorate it directly.
I also tried some other dark magic, like automatically adding the decorator to every function in the module via some code in init, but that didn't play nice with django
either.
...ugh. Thanks so much for going the extra mile – and then going beyond that mile to a new plateau of exhaustion. Indeed, Django appears to be doing something perfidious with respect to imports. Clearly, this is a low-level @beartype + Django issue of some sort. My kidneys are hurting.
Let's open a new feature request over at the @beartype issue tracker helpfully tracking this Unidentified Import Phenomena (UIP). Even if it takes us several decades, we will resolve this issue shortly before collapsing in a tragic cross-country skiing accident in British Columbia.
Thank you for building this!!
I'm trying to use this tool to add
beartype
to one specific module in my project. However, I get this warning:.venv/lib/python3.10/site-packages/pytest_beartype/__init__.py:69: BeartypePytestWarning: Previously imported packages <module name> not checkable by beartype.
Is there a standard way of fixing this?