Error with Python import - python

I have problem:
I am using this package: https://github.com/ulule/django-badgify
It works perfectly, but now I need to create Custom model Badge. I have made everything, as in docs:
main.models.py:
from badgify.models.base.badge import Badge as BaseBadge
class GuidaBadge(BaseBadge):
class Meta(BaseBadge.Meta):
abstract = False
settings.py:
BADGIFY_BADGE_MODEL = "main.models.GuidaBadge"
But it cause error:
File "D:\virtenvs\codeguida\codeguida\main\models.py", line 11, in <module>
from badgify.models.base.badge import Badge as BaseBadge
File "D:\virtenvs\codeguida\lib\site-packages\badgify\models\__init__.py", line 8, in <module>
Badge = load_class(settings.BADGE_MODEL)
File "D:\virtenvs\codeguida\lib\site-packages\badgify\utils.py", line 88, in load_class
raise exceptions.ImproperlyConfigured(txt)
django.core.exceptions.ImproperlyConfigured: Backend module "main.models" does not define a "GuidaBadge" class.
That is, https://github.com/ulule/django-badgify/blob/master/badgify/utils.py#L79
It seems that Python can`t find "GuidaBadge" class. So I have tried to use function load_class() in shell - it return right class...
I think that the error cause, in this way:
As we can see in Traceback, firstly:
File "D:\virtenvs\codeguida\codeguida\main\models.py", line 11, in <module>
from badgify.models.base.badge import Badge as BaseBadge
Program asks python to import Badge class from badgify package
Then Python tried to import it, and encounter with
File "D:\virtenvs\codeguida\lib\site-packages\badgify\models\__init__.py", line 8, in <module>
Badge = load_class(settings.BADGE_MODEL)
Here program asks python to load_class from string (that is stored in settings, e.g. 'main.models.GuidaBadge')
But Python has not run this part of models, yet. And it cause error that there is not class "GuidaBadge" in "main.models".
Am I right?
How to fix it?

dont do
from badgify.models.base.badge import Badge as BaseBadge
instead do
import badgify.models.base.badge
class GuidaBadge(badgify.models.base.badge.Badge):
class Meta(BaseBadge.Meta):
abstract = False
"from" and "as" imports are having problems with circularity because of namespace changes (the from imported module is not recognized as the original model because it is imported as a different namespace).

main.models.py:
Is your file named that way or is it models.py that lives in main directory? If first one, change it to second one.
Check also that you have __init__.py file in main directory and that your main directory lives in python path.

Related

FastAPI - "TypeError: issubclass() arg 1 must be a class" with modular imports

When working with modular imports with FastAPI and SQLModel, I am getting the following error if I open /docs:
TypeError: issubclass() arg 1 must be a class
Python 3.10.6
pydantic 1.10.2
fastapi 0.85.2
sqlmodel 0.0.8
macOS 12.6
Here is a reproducible example.
user.py
from typing import List, TYPE_CHECKING, Optional
from sqlmodel import SQLModel, Field
if TYPE_CHECKING:
from item import Item
class User(SQLModel):
id: int = Field(default=None, primary_key=True)
age: Optional[int]
bought_items: List["Item"] = []
item.py
from sqlmodel import SQLModel, Field
class Item(SQLModel):
id: int = Field(default=None, primary_key=True)
price: float
name: str
main.py
from fastapi import FastAPI
from user import User
app = FastAPI()
#app.get("/", response_model=User)
def main():
return {"message": "working just fine"}
I followed along the tutorial from sqlmodel https://sqlmodel.tiangolo.com/tutorial/code-structure/#make-circular-imports-work.
If I would put the models in the same file, it all works fine. As my actual models are quite complex, I need to rely on the modular imports though.
Traceback:
Traceback (most recent call last):
File "/Users/felix/opt/anaconda3/envs/fastapi_test/lib/python3.10/site-packages/fastapi/utils.py", line 45, in get_model_definitions
m_schema, m_definitions, m_nested_models = model_process_schema(
File "pydantic/schema.py", line 580, in pydantic.schema.model_process_schema
File "pydantic/schema.py", line 621, in pydantic.schema.model_type_schema
File "pydantic/schema.py", line 254, in pydantic.schema.field_schema
File "pydantic/schema.py", line 461, in pydantic.schema.field_type_schema
File "pydantic/schema.py", line 847, in pydantic.schema.field_singleton_schema
File "pydantic/schema.py", line 698, in pydantic.schema.field_singleton_sub_fields_schema
File "pydantic/schema.py", line 526, in pydantic.schema.field_type_schema
File "pydantic/schema.py", line 921, in pydantic.schema.field_singleton_schema
File "/Users/felix/opt/anaconda3/envs/fastapi_test/lib/python3.10/abc.py", line 123, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class
TL;DR
You need to call User.update_forward_refs(Item=Item) before the OpenAPI setup.
Explanation
So, this is actually quite a bit trickier and I am not quite sure yet, why this is not mentioned in the docs. Maybe I am missing something. Anyway...
If you follow the traceback, you'll see that the error occurs because in line 921 of pydantic.schema in the field_singleton_schema function a check is performed to see if issubclass(field_type, BaseModel) and at that point field_type is not in fact a type instance.
A bit of debugging reveals that this occurs, when the schema for the User model is being generated and the bought_items field is being processed. At that point the annotation is processed and the type argument for List is still a forward reference to Item. Meaning it is not the actual Item class itself. And that is what is passed to issubclass and causes the error.
This is a fairly common problem, when dealing with recursive or circular relationships between Pydantic models, which is why they were so kind to provide a special method just for that. It is explained in the Postponed annotations section of the documentation. The method is update_forward_refs and as the name suggests, it is there to resolve forward references.
What is tricky in this case, is that you need to provide it with an updated namespace to resolve the Item reference. To do that you need to actually have the real Item class in scope because that is what needs to be in that namespace. Where you do it does not really matter. You could for example import User model into your item module and call it there (obviously below the definition of Item):
from sqlmodel import SQLModel, Field
from .user import User
class Item(SQLModel):
id: int = Field(default=None, primary_key=True)
price: float
name: str
User.update_forward_refs(Item=Item)
But that call needs to happen before an attempt is made to set up that schema. Thus you'll at least need to import the item module in your main module:
from fastapi import FastAPI
from .user import User
from . import item
api = FastAPI()
#api.get("/", response_model=User)
def main():
return {"message": "working just fine"}
At that point it is probably simpler to have a sub-package with just the model modules and import all of them in the __init__.py of that sub-package.
The reason I gave the example of putting the User.update_forward_refs call in below your Item definition is that these situations typically occur, when you actually have a circular relationship, i.e. if your Item class had a users field for example, which was typed as list[User]. Then you'd have to import User there anyway and might as well just update the references there.
In your specific example, you don't actually have any circular dependencies, so there is strictly speaking no need for the TYPE_CHECKING escape. You can simply do from .item import Item inside user.py and put the actual class in your annotation as bought_items: list[Item]. But I assume you simplified the actual use case and simply forgot to include the circular dependency.
Maybe I am missing something and someone else here can find a way to call update_forward_refs without the need to provide Item explicitly, but this way should definitely work.
For anyone ending up here who (just like me) got the same error but couldn't resolve it using the solution above, my script looked like this. It seems that SQLModel relies on the pydantic.BaseModel so this solution also applies here.
from pydantic import BaseModel
class Model(BaseModel):
values: list[int, ...]
class SubModel(Model):
values = list[int, int, int]
It took me a long time to realize what my mistake was, but in SubModel I used = (assignment) whereas I should have used : (type hint).
The strangest thing was that it did work in a docker container (Linux) but not locally (Windows). Also, mypy did not pick up on this.

Python get line number of classes from imported module

I have been using importlib to get the module from an imported python file and would like to get the line number where each class is defined in the python file.
For example I have something like this:
testSpec = importlib.util.spec_from_file_location("", old_file)
testModule = importlib.util.module_from_spec(testSpec)
testSpec.loader.exec_module(testModule)
Where old_file is a python file (lets call it old.py). Then using the inspect library I can find all of the classes from old.py in this way:
for name, obj in inspect.getmembers(testModule):
if inspect.isclass(obj):
print(name)
This gives me all of the created class names from old.py which is correct. However, what I would like to do is also get the line number where this class appears in old.py. I have tried to add this line in the if statement right after the print statement: print(inspect.getsourcelines(obj))
However, this errors out in this way:
File "old.py", line 665, in getfile
raise TypeError('{!r} is a built-in class'.format(object))
TypeError: <class '.exampleClassName'> is a built-in class
I am not sure why it considers this user-created class a built-in class, but is there another way that I can just get the line number where this class is defined in old.py? For example if old.py looks like this:
#test comment line 1
#test comment line 2
class exampleClassName:
test = 0
Then when I have the class object exampleClassName I would expect to print out 4 from inspect.getsourcelines(obj) since it is defined on line 4.
An option would be to loop through the file with open and .readline(), and see where the line matches class, then save the line (count) number and class name into a dict.

Python import cycle when importing modules only

I know this question has been asked countless times here, but I've been stuck with this problem for a long time and have not been able to find a solution online.
I have an import cycle, and here is the stack trace:
Traceback (most recent call last):
File "openldap_lookup.py", line 2, in <module>
import pure.directory_service.ds_lookup as dsl
File "/home/myname/purity/tools/pure/directory_service/ds_lookup.py", line 8, in <module>
import pure.authorization.auth as auth
File "/home/myname/purity/tools/pure/authorization/auth.py", line 16, in <module>
import auth_unix as auth_impl
File "/home/myname/purity/tools/pure/authorization/auth_unix.py", line 17, in <module>
import pure.directory_service.ad_lookup as ad_lookup
File "/home/myname/purity/tools/pure/directory_service/ad_lookup.py", line 1, in <module>
import pure.authorization.auth as auth
AttributeError: 'module' object has no attribute 'auth'
I import modules only; I avoid the from <module> import <class> and from <module> import <method>
I tried to reproduce the error locally, but python has no complaints. These are the local test files:
openldap_lookup.py
import ds_lookup
def openldap_foo():
print ds_lookup.ds_foo
print 'openldap_lookup importing ds_lookup'
ds_lookup.py
import auth as au
def ds_foo():
print au.auth_foo
print 'ds_lookup importing auth'
auth.py
import auth_unix
def auth_foo():
print auth_unix.auth_unix_foo
print 'auth importing auth_unix'
auth_unix.py
import ad_lookup
def auth_unix_foo():
print ad_lookup.ad_foo
print 'auth_unix importing ad_lookup'
ad_lookup.py
import auth as au
def ad_foo():
print au.auth_foo
print 'ad_lookup importing auth'
But python doesn't complain:
myname#myname-mbp:~/cycletest$ python openldap_lookup.py
ad_lookup importing auth
auth_unix importing ad_lookup
auth importing auth_unix
ds_lookup importing auth
openldap_lookup importing ds_lookup
myname#myname-mbp:~/cycletest$
I am not a python expert, but I understand that an import cycle is causing the error. But why doesn't the same error occur with the small test files? When is an import cycle legal in python and when is it not? What can I do to resolve this?
I would greatly appreciate any help from the python experts out there.
Since many are bound to ask, why do I have this cycle in the first place?
Well, openldap_lookup and ad_lookup both contain subclasses of a base class in ds_lookup. ds_lookup requires constants from auth. auth requires auth_unix as an implementation, and auth_unix in turn calls the implementations openldap_lookup and ad_lookup.
I would love to move the constants out from auth and remove the cycle, but this code is part of a large git repo where hundreds of files depend on the constants and methods in auth, and I would like to avoid having to refactor all of them if possible.
Actually, you're not just importing modules -- you're importing modules from packages, and your test case doesn't actually reflect this.
I think the problem is that in your first import of pure.authorization.auth, the interpreter is still building the pure.authorization module (it hasn't yet bound auth into pure.authorization, because it hasn't finished importing auth), so the second time it encounters this, it finds the pure.authorization module, but there is no global variable auth in it yet.
As far as breaking the cycle goes, does auth really need to import auth_unix right away, or could that be deferred until you really need an auth implementation?

Django 1.6.8 with ADMIN_URL

I would like to add the https://github.com/RobCombs/django-locking project to my Django 1.6.8 project's admin interface. I understand that this locking code is meant for an older version of Django, but I'd like to see if it's possible to install anyway.
While following step 7, I'm encountering this exception:
File "/usr/local/lib/python2.7/dist-packages/django_locking-0.3.2-py2.7.egg/locking/admin.py", line 15, in <module>
class LockableAdmin(admin.ModelAdmin):
File "/usr/local/lib/python2.7/dist-packages/django_locking-0.3.2-py2.7.egg/locking/admin.py", line 17, in LockableAdmin
class Media:
File "/usr/local/lib/python2.7/dist-packages/django_locking-0.3.2-py2.7.egg/locking/admin.py", line 21, in Media
_s.ADMIN_URL + "ajax/variables.js",
File "/usr/local/lib/python2.7/dist-packages/django/conf/__init__.py", line 55, in __getattr__
return getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'ADMIN_URL'
Indeed my settings.py has nothing about ADMIN_URL. I can't find any documentation on how to set this, or otherwise proceed with adding this locking functionality.
Is it possible to continue from here on Django 1.6.8?
Apparently the construction with getattr() in settings.py of django-locking doesn't work anymore (because Django's Settings object raises AttributeError?) which is surprising to me. I can't test right now, unfortunately.
The good news is that simply setting ADMIN_URL = '/admin/' in your settings will most likely fix this issue.
The bad news is that you'll probably run into a few more that may not be easy to fix, but who knows...

AttributeError: 'module' object has no attribute

I've been scouring the internet for a solution and everything i've come across hasn't helped. So now i turn to you.
Traceback (most recent call last):
File "cardreader.py", line 9, in <module>
import ATRdb as ATR
File "/home/pi/Desktop/CardReader/ATRdb.py", line 4, in <module>
import cardreader
File "/home/pi/Desktop/CardReader/cardreader.py", line 113, in <module>
main()
File "/home/pi/Desktop/CardReader/cardreader.py", line 40, in main
getData(db)
File "/home/pi/Desktop/CardReader/cardreader.py", line 98, in getData
if ATR.checkPerms(db,track1):
AttributeError: 'module' object has no attribute 'checkPerms'
I have two files cardreader.py & ATRdb.py
---ATRdb.py has this setup
import sys
import MYSQLdb
import datetime
import cardreader
def checkPerms(db, securitycode):
try:
cursor = db.cursor()
cursor.execute("""SELECT permissions FROM atrsecurity.employee WHERE securitycode = %s""", (securitycode))
r = cursor.fetchone()
Permissions = r
if '3' in Permissions[0]:
return True
else:
return False
except Exception:
cardreader.main()
return False
---cardreader.py has this setup
import sys
import usb.core
import usb.util
import MYSQLdb
import ATRdb as ATR
def main():
db = MYSQLdb.connect(HOST,USER, PASS, DB)
print("Please swipe your card...")
getData(db)
main()
db.close()
def getData(db):
#
#lots of code to get card data
#
if ATR.checkPerms(db, track1):
print ("User has permission")
unlockDoor()
i get the error at the "If ATR.checkPerms():" part. Any help would be appreciated
(first python project)
Your problem is circular imports.
In cardreader, you do this:
import ATRdb as ATR
That starts importing ATRdb, but a few lines into the code, it hits this:
import cardreader
The exact sequence from here depends on whether cardreader.py is your main script or not, and on whether your top-level code that calls main is protected by an if __name__ == '__main__' guard (and assuming that top-level code is in cardreader rather than elsewhere). Rather than try to explain all the possibilities in detail (or wait for you to tell us which one matches your actual code), let's look at what we know is true based on the behavior:
In some way, you're calling main before finishing the import of ATRdb.
This means that, at this point, ATRdb has nothing in it but sys, MYSQLdb, and datetime (and a handful of special attributes that every module gets automatically). In particular, it hasn't gotten to the definition of checkPerms yet, so no such attribute exists in the module yet.
Of course eventually it's going to finish importing the rest of ATRdb, but at that point it's too late; you've already called main and it tried to call ATR.checkPerms and that failed.
While there are various complicated ways to make circular imports work (see the official FAQ for some), the easiest and cleanest solution is to just not do it. If ATRdb needs some functions that are in cardreader, you should probably factor those out into a third module, like cardutils, that both ATRdb and cardreader can import.

Categories