...and there are no errors. This works fine in Python 2 with the Google Cloud SDK. π
Actual Behavior
Using Python 3.9.16 and appengine-python-standard, however, we see:
BadValueError: Value b'red' for property b'cars.color' is not an allowed choice`
...which is confusing because 'red' was provided as the value, not b'red'. π€π€π€
Digging around a bit, it appears to fail in StructuredProperty._comparison() (triggered by the Dealership.cars == Car(color=RED) expression), specifically right here:
...because the provided color value is converted to a _BaseValue() of bytes via Property._get_base_value_unwrapped_as_list() β Property._get_base_value() β Property._opt_call_to_base_type() β Property._apply_to_values() β TextProperty._to_base_type():
This does not appear to have anything to do with persisting data -- the persisted value of Dealership.cars[].color remains a str: type(d1.cars[0].color) == str.
It only happens during "comparison" (a key part of querying) when the model with a StringProperty(choices=...) is a StructuredProperty of another model. . We can see this by skipping entity creation and just calling Dealership.query(Dealership.cars == Car(color=BLUE)), or even Dealership.cars == Car(color=BLUE) as the most minimal case.
When using a standalone model, all works fine:
ferrari = Car(name="Ferrari", color=RED).put().get()
print(ferrari)
red_car = Car.query(Car.color == RED).get()
print(red_car)
print(str(ferrari == red_car))
assert ferrari == red_car
Expected Behavior
Given the following example ndb models and code:
...we should see:
...and there are no errors. This works fine in Python 2 with the Google Cloud SDK. π
Actual Behavior
Using Python 3.9.16 and appengine-python-standard, however, we see:
...which is confusing because
'red'
was provided as the value, notb'red'
. π€π€π€Digging around a bit, it appears to fail in
StructuredProperty._comparison()
(triggered by theDealership.cars == Car(color=RED)
expression), specifically right here:https://github.com/GoogleCloudPlatform/appengine-python-standard/blob/882b8fa098220636a1c259a1dfaa1e307bcfcf72/src/google/appengine/ext/ndb/model.py#L2381-L2382
...because the provided
color
value is converted to a_BaseValue()
ofbytes
viaProperty._get_base_value_unwrapped_as_list()
βProperty._get_base_value()
βProperty._opt_call_to_base_type()
βProperty._apply_to_values()
βTextProperty._to_base_type()
:https://github.com/GoogleCloudPlatform/appengine-python-standard/blob/882b8fa098220636a1c259a1dfaa1e307bcfcf72/src/google/appengine/ext/ndb/model.py#L1831-L1833
...before being compared to the allowed
choices
(which arestr
). πA workaround is to adjust the values in
choices
to be of typebytes
:...and we then see the same result:
(This also works fine in Python 2 with the Google Cloud SDK. π )
Steps to Reproduce the Problem
(see code above)
Specifications
Darwin Kernel Version 23.0.0: Fri Sep 15 14:42:42 PDT 2023; root:xnu-10002.1.13~1/RELEASE_X86_64 x86_64
)Additional Info
This does not appear to have anything to do with persisting data -- the persisted value of
Dealership.cars[].color
remains astr
:type(d1.cars[0].color) == str
.It only happens during "comparison" (a key part of querying) when the model with a
StringProperty(choices=...)
is aStructuredProperty
of another model. . We can see this by skipping entity creation and just callingDealership.query(Dealership.cars == Car(color=BLUE))
, or evenDealership.cars == Car(color=BLUE)
as the most minimal case.When using a standalone model, all works fine:
...yields: