mypy typing leeds to unexpected traceback - python

I'm trying to use a work-around for the problems described in this GitHub issue ("Class with function fields incorrectly thinks the first argument is self").
from dataclasses import dataclass
from typing import TypeVar, Generic, Any, Iterable, List
T = TypeVar("T")
# See https://github.com/python/mypy/issues/5485
#dataclass
class Box(Generic[T]):
inner: T
#property
def unboxed(self) -> T:
return self.inner
I run into a traceback like this, though:
However, upon importing (only(!) ) the code above from another module I run into a traceback like this:
(py) sugarline:~/src/oss/ormsnack write-compile-eval
Traceback (most recent call last):
[.....]
File "/Users/jacob/src/oss/ormsnack/ormsnack/ng_desc.py", line 8, in <module>
from kingston.kind import Box # type: ignore
File "kingston/kind.py", line 12, in <module>
AttributeError: attribute '__dict__' of 'type' objects is not writable
Googling only lands me in old bugs that seem to have gone stale, though (https://bugs.python.org/issue38099, https://github.com/man-group/arctic/issues/17), etc.
Is anyone able to figure out a work-around?

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.

pyQiwiP2P pay_sources: list[str] = None

I need to make payment via qiwi
from pyqiwip2p import QiwiP2P
from pyqiwip2p.Qiwip2p import PaymentMethods
def create_trans(amount=400, lifetime=30, comment="def"):
p2p = QiwiP2P(auth_key="I specifically removed")
bill = p2p.bill(amount=amount, lifetime=lifetime, comment=comment, bill_id=random_id, pay_sources=[PaymentMethods.qiwi, PaymentMethods.card, PaymentMethods.mobile])
return bill
The program ends with an error:
File "G:\bot\venv\lib\site-packages\pyqiwip2p\Qiwip2p.py", line 118, in QiwiP2P
pay_sources: list[str] = None,
TypeError: 'type' object is not subscriptable
This looks to be a bug in pyQiwiP2P.
As per your traceback, line 118 of pyqiwip2p.Qiwip2p.py is as follows:
pay_sources: list[str] = None,
This contains a broken type hint, list[str]. It seems the author wanted to add a type hint saying that the method parameter pay_sources should contain a list of strings. In this case, they should write
pay_sources: typing.List[str] = None,
or perhaps
pay_sources: typing.Union[typing.List[str], None] = None,
instead, given that pay_sources can also be None.
To confirm this is the case, we can easily reproduce your exception in a Python interactive session:
>>> def test(a: list[str]):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'type' object is not subscriptable
I would suggest you get in contact with the package author, perhaps by raising an issue on the project's GitHub repository.
Perhaps you are using a version of python later than 3.9. I solved this problem by downloading an older version of the pyQiwiP2P library before deleting the one that was already on my PC:
pip uninstall pyQiwiP2P
pip install pyQiwiP2P==2.0.1

Python jsonpickle error: 'OrderedDict' object has no attribute '_OrderedDict__root'

I'm hitting this exception with jsonpickle, when trying to pickle a rather complex object that unfortunately I'm not sure how to describe here. I know that makes it tough to say much, but for what it's worth:
>>> frozen = jsonpickle.encode(my_complex_object_instance)
>>> thawed = jsonpickle.decode(frozen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/jsonpickle/__init__.py",
line 152, in decode
return unpickler.decode(string, backend=backend, keys=keys)
:
:
File "/Library/Python/2.7/site-packages/jsonpickle/unpickler.py",
line 336, in _restore_from_dict
instance[k] = value
File "/Library/Python/2.7/site-packages/botocore/vendored/requests/packages/urllib3/packages/ordered_dict.py",
line 49, in __setitem__
root = self.__root
AttributeError: 'OrderedDict' object has no attribute '_OrderedDict__root'
I don't find much of assistance when googling the error. I do see what looks like the same issue was resolved at some time past for simpler objects:
https://github.com/jsonpickle/jsonpickle/issues/33
The cited example in that report works for me:
>>> jsonpickle.decode(jsonpickle.encode(collections.OrderedDict()))
OrderedDict()
>>> jsonpickle.decode(jsonpickle.encode(collections.OrderedDict(a=1)))
OrderedDict([(u'a', 1)])
Has anyone ever run into this themselves and found a solution? I ask with the understanding that my case may be "differently idiosynchratic" than another known example.
The requests module for me seems to be running into problems when I .decode(). After looking at the jsonpickle code a bit, I decided to fork it and change the following lines to see what was going on (and I ended up keeping a private copy of jsonpickle with the changes so I can move forward).
In jsonpickle/unpickler.py (in my version it's line 368), search for the if statement section in the method _restore_from_dict():
if (util.is_noncomplex(instance) or
util.is_dictionary_subclass(instance)):
instance[k] = value
else:
setattr(instance, k, value)
and change it to this (it will logERROR the ones that are failing and then you can either keep the code in place or change your OrderedDict's version that have __root)
if (util.is_noncomplex(instance) or
util.is_dictionary_subclass(instance)):
# Currently requests.adapters.HTTPAdapter is using a non-standard
# version of OrderedDict which doesn't have a _OrderedDict__root
# attribute
try:
instance[k] = value
except AttributeError as e:
import logging
import pprint
warnmsg = 'Unable to unpickle {}[{}]={}'.format(pprint.pformat(instance), pprint.pformat(k), pprint.pformat(value))
logging.error(warnmsg)
else:
setattr(instance, k, value)

Django queryset.first bug? May involve django-polymorphic and mixins

I have a model, and I have instantiated several instances and persisted them to the database. Through the python shell I can confirm that the objects exist, but I run into trouble as soon as I try to work with the queryset via the queryset.first() call, as well as indexing into the queryset.all() result. I'm using the django-polymorphic plugin.
>>> Thing.objects.all()
[<Thing: thing1>, <Thing: thing2>, <Thing: thing3>]
>>> Thing.objects.first()
>>> str(Thing.objects.first())
'None'
>>> Thing.objects.all()[0]
Traceback (most recent call last):
File "<console>", line 1, in <module>
File ".../python3.4/site-packages/django/db/models/query.py", line 201, in __getitem__
return list(qs)[0]
IndexError: list index out of range
>>> Thing.objects.all().__class__
<class 'polymorphic.query.PolymorphicQuerySet'>
>>>
I strongly suspect this involves the fact that I'm using a mixin for Thing on top of the polymorphic base class. Here's what my hierarchy looks like (boiled down to make it easier for y'all):
from django.db import models
from polymorphic.polymorphic_model import PolymorphicModel
class AwesomeMixin(object):
def reallyUsefulMethod(self):
print('show me your MOVES')
class BaseThing(PolymorphicModel, AwesomeMixin):
pass
class Thing(BaseThing):
pass
class Doohickey(BaseThing):
pass
class SomethingElse(models.Model, AwesomeMixin):
pass
Anyone know what's going on here? Am I using mixins correctly? Is the issue related to django-polymorphic? Thanks!

how to simulate const variable in python

Hello i am trying to create a const in python using this example found from Creating constant in Python (in the first answer from the link) and use instance as module.
The first file const.py has
# Put in const.py...:
class _const:
class ConstError(TypeError): pass
def __setattr__(self,name,value):
if self.__dict__ in (name):
raise self.ConstError("Can't rebind const(%s)"%name)
self.__dict__[name]=value
import sys
sys.modules[__name__]=_const()
And the rest goes to test.py for example.
# that's all -- now any client-code can
import const
# and bind an attribute ONCE:
const.magic = 23
# but NOT re-bind it:
const.magic = 88 # raises const.ConstError
# you may also want to add the obvious __delattr__
Although i have made 2 changes cause i am using python 3 i still get errors
Traceback (most recent call last):
File "E:\Const_in_python\test.py", line 4, in <module>
const.magic = 23
File "E:\Const_in_python\const.py", line 5, in __setattr__
if self.__dict__ in (name):
TypeError: 'in <string>' requires string as left operand, not dict
I dont understand what the line 5 error is. Can anyone explain? Correcting the example would also be nice. Thanks in advance.
This looks weird (where did it come from?)
if self.__dict__ in (name):
shouldn't it be
if name in self.__dict__:
That fixes your example
Python 3.2.3 (default, May 3 2012, 15:51:42)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import const
>>> const.magic = 23
>>> const.magic = 88
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "const.py", line 6, in __setattr__
raise self.ConstError("Can't rebind const(%s)"%name)
const.ConstError: Can't rebind const(magic)
Do you really need this const hack? Lots of Python code seems to somehow work without it
This line:
if self.__dict__ in (name):
should be
if name in self.__dict__:
... you want to know if the attribute is in the dict, not if the dict is in the attribute name (which doesn't work, because strings contain strings, not dictionaries).
Maybe kkconst - pypi is what you search.
support str, int, float, datetime
the const field instance will keep its base type behavior.
Like orm model definition, BaseConst is Constant Helper which manage const field.
For example:
from __future__ import print_function
from kkconst import (
BaseConst,
ConstFloatField,
)
class MathConst(BaseConst):
PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant") # Euler's number"
GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")
magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)
print(magic_num) # 0.6180339887
print(magic_num.verbose_name) # Golden Ratio
# MathConst.GOLDEN_RATIO = 1024 # raise Error, because assignment allow only once
more details usage you can read the pypi url:
pypi or github
same answer: Creating constant in Python

Categories