-
Notifications
You must be signed in to change notification settings - Fork 506
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
Fix ambiguity error with Polymorphism candidates (isinstance() problem) #691
Conversation
Right now Polymorphism fails when using something like this (models in sqlalchemy): models: ```python class Product(BaseModel): discr = Column(String) __mapper_args__ = { 'polymorphic_on': discr, 'polymorphic_identity': "product" } class VegetableProduct(Product): name = Column(String) __mapper_args__ = { 'polymorphic_identity': "vegetable" } class DairyProduct(VegetableProduct): __mapper_args__ = { 'polymorphic_identity': "dairy" } ``` mapper: ```python resource_product = api.model("Product", { 'discr': fields.String }) product_mapper = { VegetableProduct: api.inherit("Vegetable", resource_product, { 'name': fields.String }), DairyProduct: api.inherit("Dairy", resource_product, { 'name': fields.String }), } resource_result = api.model("ResourceResult", { 'products': fields.List(fields.Polymorphism(product_mapper)) }) ``` This sparkles error here: https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/fields.py#L682 Because candidates will be: VegetableProduct and DairyProduct, since isinstance() will return True for both classes (I personally surprised it works like this) Checking by __class__.__name__ removes this error and it works as intended. I hope my quick fix did not break anything, I'm eager to update it if I don't know, more elegant solution is needed.
Thank you for the contribution. We'll need to take a look at this one. In the meantime, could you adjust the tests based on the change in functionality? This change breaks the test below. Please refer to the contribution doc for what we expect: https://github.com/noirbizarre/flask-restplus/blob/master/CONTRIBUTING.rst
|
@j5awry sorry I did not answer right away. Did not expect your response to be this quick! 😄 I adjusted tests. With new functionality requirement is exactly opposite - no errors with ambiguous (for isinstance()) feature I suspect this functionality better suits current way of writing mappings: mapping = {
Parent: parent_resource,
Child: child_resource,
SecondChild: second_child_resource
} If we write explicitly source classes which instances should be serialized, we expect Polymorph to use exact classes, which is not possible right now with isinstance check, but possible with |
Thanks for using Flask-RESTPlus and for your contribution! Just to explain the reason for the
Using the built in Python API for inspecting classes would be preferable to manually inspecting candidates = [fields for cls, fields in iteritems(self.mapping) if type(value) == type(cls)] Manually checking the class Parent:
pass
class Child:
pass
p = Parent()
c = Child() One could break the proposed changes: >>> c.__class__.__name__ == p.__class.__name__
True
>>> c.__class__.__name__ = 'foo'
>>> c.__class__.__name__ == p.__class.__name__
False When using the >>> type(c) == type(p)
True
>>> c.__class__.__name__ = 'foo'
>>> type(c) == type(p)
True Would you be willing to make these changes? |
@SteadBytes I tried to do as you said, but I suspect I will need advice and thoughts on problem which occured with with python 2.7 and pypy python do not have similar behaviour as later versions. python == 2.7 type(A()) == A
False python > 2.7 type(A()) == A
True May I suggest to make to My last commit users simply |
For Python 2.7, the base class needs to inherit from class Parent(object):
pass
class Child(Parent):
pass
p = Parent()
c = Child() The >>> type(p) == type(c)
False Apologies for making my previous example Python 3 only, does the above make sense? |
@SteadBytes ok, got it! Thanks! What do you think about my current code? With |
If using |
@SteadBytes using type() requires to change tests functionality, adding (object) to test with Polymorphism. Inheritance within those tests did not have additional (object) in inheritance. Using type() will require test files to be additionally modified. Using Should I modify tests for type(), so classes inherit (object) for compatibility with python 2.7 and pypy, or should I leave current Test which would be modified for type(): https://github.com/noirbizarre/flask-restplus/blob/master/tests/test_swagger.py#L2240 |
Yes those tests should be modified IMO. It is well documented that, in Python 2, classes should inherit from Since that is the best practice we should follow suit for our Python 2 compatibility 👍 |
@SteadBytes I believe this is it! Adjusted tests and using type() in the code. |
@SteadBytes sorry to ping you, but maybe there is possibility this changes can be merged, since tests are ok and it's relatively small changes and they are consistent with current documentation? |
@idchlife No need to apologise - thank you for pinging me! It can be difficult to keep track of issues so please don't hesitate to ping me if I've not replied for a while 👍 I'm just looking through this now, the |
Previously that error would be thrown if multiple candidates fields were available for a given value, this would be the case in your original issue - a mapping where multiple classes would return @j5awry I would appreciate if you could give your thoughts on this - have I misunderstood and do you agree with this new behaviour? @idchlife Once we have this clarified (I just want to be sure that I've understood that properly) we will finish this up 👍 |
@SteadBytes my understanding is the same -- the ambiguity is being removed, thus the test isn't valid. That's fine. As for the approach, in concept I'm fine with it. I think we should do some good PR on this. It's a slight, subtle change that shouldn't break anything that currently works, but offers something to more experienced users. |
@SteadBytes hello again! Maybe I should just rename test file so it will be suitable now? Like testing the exact opposite functionality: lack of error because there cannot be ambiguity. |
@idchlife The correct behaviour is already tested in
|
@SteadBytes thank you! I made last commit with your suggestion. I suspect all is ready now :) Also, thank you very much for thorough replies! |
@idchlife You are more than welcome! Thank you for contributing to the project and for taking on board our feedback 😄 I will approve and merge the PR now 👍 |
Right now Polymorphism fails when using something like this (models in sqlalchemy):
models:
mapper:
This sparkles error here:
https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/fields.py#L682
Because candidates will be: VegetableProduct and DairyProduct, since isinstance() will return True for both classes (I personally surprised it works like this)
But actually it should be only DairyProduct
Checking by
__class__.__name__
removes this error and it works as intended.I hope my quick fix did not break anything, I'm eager to update it if I don't know, more elegant solution is needed.