Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow Feature **id** to be either a String or a Number #91

Merged
merged 3 commits into from
Mar 13, 2023

Conversation

vincentsarago
Copy link
Member

@vincentsarago vincentsarago commented Dec 16, 2022

closes #90

I'm a bit hesitant on this because pydantic smart-union is slower https://docs.pydantic.dev/usage/model_config/#smart-union

Maybe we can wait for pydantic v2 馃し

@eseglem
Copy link
Collaborator

eseglem commented Mar 13, 2023

I just realized this could use "Strict" types instead of dealing with smart union. Could even say that is more correct if the goal is to strictly enforce the GeoJSON spec. With the current Optional[str] bad inputs (bool, float) can be coerced to a string without errors. Whereas we remove the weird behavior and we don't coerce int to str if we do this instead:

from pydantic import StrictInt, StrictStr
...
    id: Optional[Union[StrictInt, StrictStr]] = None

And yes, v2 of pydantic is supposedly just around the corner, but I think this could be the right route to go even once v2 is out.

Probably worth a look to see if there is other places where use of "Strict" could be useful as well.


With smart union you can still do weird stuff. Especially since bool is a subclass of int in python.

>>> class Foo(BaseModel):
...     bar: int | str
...     class Config:
...         smart_union = True
... 
>>> Foo(bar=3)
Foo(bar=3)
>>> Foo(bar="a")
Foo(bar='a')
>>> Foo(bar=False)
Foo(bar=False)
>>> Foo(bar=3.14159)
Foo(bar=3)
# Flipping order
>>> class Foo(BaseModel):
...     bar: str | int
...     class Config:
...         smart_union = True
... 
# The rest are the same
>>> Foo(bar=3.14159)
Foo(bar='3.14159')

Whereas with "Strict" types, they have to be the exact types.

>>> class Foo(BaseModel):
...     bar: StrictInt | StrictStr # Order doesn't matter here
... 
>>> Foo(bar=3)
Foo(bar=3)
>>> Foo(bar="a")
Foo(bar='a')
# Both of these produce the same error
>>> Foo(bar=3.14159)
>>> Foo(bar=False)
# Which I think is probably the most accurate enforcement of the spec
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 2 validation errors for Foo
bar
  value is not a valid integer (type=type_error.integer)
bar
  str type expected (type=type_error.str)

@vincentsarago
Copy link
Member Author

馃檹 thanks @eseglem, will switch to strict

geojson_pydantic/features.py Outdated Show resolved Hide resolved
tests/test_features.py Show resolved Hide resolved
tests/test_features.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@eseglem eseglem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM whenever it stops complaining about stuff.

@vincentsarago
Copy link
Member Author

馃う I messed up my branch sorry

@vincentsarago vincentsarago merged commit 9864544 into main Mar 13, 2023
@vincentsarago vincentsarago deleted the IdAsStringOrNumber branch March 13, 2023 16:49
@bluenote10
Copy link

According to the specs the id is a number, which in JSON terminology should include float as well, no? It is probably not the best idea to use floats as an id, but if the specs allow it, the model should probably not reject it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

id should be either a number or a string
3 participants