uriyyo / fastapi-pagination

FastAPI pagination šŸ“–
https://uriyyo-fastapi-pagination.netlify.app/
MIT License
1.18k stars 135 forks source link

Pydantic v2 migration problem #790

Closed pjcopado closed 1 year ago

pjcopado commented 1 year ago

Hi! I'm using FastApi 0.101.1 and Pydantic 1.10.7 and it works great, but I'm trying to migrate to Pydantic 2.1.1 and I'm getting this error.

INFO:     Started server process [7591]
INFO:     Waiting for application startup.
ERROR:    Traceback (most recent call last):
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
    async with self.lifespan_context(app) as maybe_state:
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/starlette/routing.py", line 566, in __aenter__
    await self._router.startup()
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/starlette/routing.py", line 656, in startup
    handler()
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 339, in on_startup
    _add_pagination(parent)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 331, in _add_pagination
    _update_route(route)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 317, in _update_route
    get_parameterless_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 124, in get_parameterless_sub_dependant
    return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 147, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 268, in get_dependant
    sub_dependant = get_param_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 111, in get_param_sub_dependant
    return get_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 147, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 289, in get_dependant
    add_param_to_fields(field=param_field, dependant=dependant)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 462, in add_param_to_fields
    if field_info.in_ == params.ParamTypes.path:
AttributeError: 'FieldInfo' object has no attribute 'in_'

ERROR:    Application startup failed. Exiting.

If I comment the line add_pagination(app) in main.py, the application works, but of course the paginated endpoints won't. Is there any change I have to make? Thanks!

uriyyo commented 1 year ago

Hi @pjcopado,

Could you please show code example?

pjcopado commented 1 year ago

Mmmm, I'm not sure how to provide an example of the whole app šŸ˜…

uriyyo commented 1 year ago

I need some code, I can't reproduce this issue(

I am also using fastapi 0.101.1 and pydantic 2.1.1.

pjcopado commented 1 year ago

I just found out the issue appears when I create a custom Page

from pydantic import Field
from fastapi_pagination import Page

Page = Page.with_custom_options(size=Field(10, ge=1, le=50))
uriyyo commented 1 year ago

Thanks, I will take a look!

uriyyo commented 1 year ago

@pjcopado What version of fastapi-pagination are you using?

Code bellow is working with latest version:

from fastapi import FastAPI
from pydantic import Field

from fastapi_pagination import Page, add_pagination, paginate

Page = Page.with_custom_options(size=Field(10, ge=1, le=50))

app = FastAPI()

@app.get("/items")
async def get_items() -> Page[str]:
    return paginate(["foo", "bar", "baz"])

add_pagination(app)
pjcopado commented 1 year ago

Uhmm thats weird, I'm using 0.12.8.. I'll continue debugging

pjcopado commented 1 year ago

Ok so I literally deleted my whole main.py file and it's only the same code as yours, and still getting the problem.

# main.py

from fastapi import FastAPI
from pydantic import Field

from fastapi_pagination import Page, add_pagination, paginate

Page = Page.with_custom_options(size=Field(10, ge=1, le=50))

app = FastAPI()

@app.get("/items")
async def get_items() -> Page[str]:
    return paginate(["foo", "bar", "baz"])

add_pagination(app)
Process SpawnProcess-4:
Traceback (most recent call last):
  File "/Users/pjcopado/.pyenv/versions/3.10.6/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/Users/pjcopado/.pyenv/versions/3.10.6/lib/python3.10/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/uvicorn/_subprocess.py", line 76, in subprocess_started
    target(sockets=sockets)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/uvicorn/server.py", line 61, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "/Users/pjcopado/.pyenv/versions/3.10.6/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/Users/pjcopado/.pyenv/versions/3.10.6/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/uvicorn/server.py", line 68, in serve
    config.load()
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/uvicorn/config.py", line 467, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
  File "/Users/pjcopado/.pyenv/versions/3.10.6/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/pjcopado/code/project/backend-v2/main.py", line 136, in <module>
    add_pagination(app)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 335, in add_pagination
    _add_pagination(parent)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 331, in _add_pagination
    _update_route(route)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi_pagination/api.py", line 317, in _update_route
    get_parameterless_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 124, in get_parameterless_sub_dependant
    return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 147, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 268, in get_dependant
    sub_dependant = get_param_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 111, in get_param_sub_dependant
    return get_sub_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 147, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 289, in get_dependant
    add_param_to_fields(field=param_field, dependant=dependant)
  File "/Users/pjcopado/.pyenv/versions/project/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 462, in add_param_to_fields
    if field_info.in_ == params.ParamTypes.path:
AttributeError: 'FieldInfo' object has no attribute 'in_'

I think there might be another library messing up..

aiosmtplib==2.0.2
alembic==1.10.4
annotated-types==0.5.0
anyio==3.7.1
appnope==0.1.3
argon2-cffi==21.3.0
argon2-cffi-bindings==21.2.0
arrow==1.2.3
asttokens==2.2.1
async-lru==2.0.3
async-timeout==4.0.2
attrs==23.1.0
Babel==2.12.1
backcall==0.2.0
beautifulsoup4==4.12.2
black==23.1.0
bleach==6.0.0
blinker==1.6.2
bump-pydantic==0.6.1
CacheControl==0.13.1
cachetools==5.3.0
certifi==2023.7.22
cffi==1.15.1
charset-normalizer==3.2.0
click==8.1.6
comm==0.1.3
cryptography==41.0.2
debugpy==1.6.7
decorator==5.1.1
defusedxml==0.7.1
dnspython==2.4.0
email-validator==2.0.0.post2
et-xmlfile==1.1.0
exceptiongroup==1.1.2
executing==1.2.0
fastapi==0.101.1
fastapi-mail==1.4.1
fastapi-pagination==0.12.8
fastjsonschema==2.18.0
firebase-admin==6.1.0
fqdn==1.5.1
gcloud==0.18.3
GeoAlchemy2==0.14.0
google-api-core==2.11.0
google-api-python-client==2.78.0
google-auth==2.16.1
google-auth-httplib2==0.1.0
google-cloud-core==2.3.2
google-cloud-firestore==2.9.1
google-cloud-storage==2.7.0
google-crc32c==1.5.0
google-resumable-media==2.4.1
googleapis-common-protos==1.58.0
grpcio==1.56.2
grpcio-status==1.56.2
h11==0.14.0
httpcore==0.17.3
httplib2==0.22.0
idna==3.4
iniconfig==2.0.0
ipykernel==6.25.0
ipython==8.14.0
ipython-genutils==0.2.0
ipywidgets==8.0.7
isoduration==20.11.0
jedi==0.18.2
Jinja2==3.1.2
joblib==1.3.1
json5==0.9.14
jsonpointer==2.4
jsonschema==4.18.4
jsonschema-specifications==2023.7.1
jupyter==1.0.0
jupyter-console==6.6.3
jupyter-events==0.6.3
jupyter-lsp==2.2.0
jupyter_client==8.3.0
jupyter_core==5.3.1
jupyter_server==2.7.0
jupyter_server_terminals==0.4.4
jupyterlab==4.0.3
jupyterlab-pygments==0.2.2
jupyterlab-widgets==3.0.8
jupyterlab_server==2.24.0
jws==0.1.3
libcst==1.0.1
Mako==1.2.4
markdown-it-py==3.0.0
MarkupSafe==2.1.3
matplotlib-inline==0.1.6
mdurl==0.1.2
mistune==3.0.1
msgpack==1.0.5
mypy-extensions==1.0.0
nbclient==0.8.0
nbconvert==7.7.3
nbformat==5.9.1
nest-asyncio==1.5.6
notebook==7.0.0
notebook_shim==0.2.3
numpy==1.24.2
oauth2client==4.1.3
openpyxl==3.1.2
overrides==7.3.1
packaging==23.1
pandas==2.0.1
pandocfilters==1.5.0
parso==0.8.3
pathspec==0.11.1
pexpect==4.8.0
pickleshare==0.7.5
platformdirs==3.9.1
pluggy==1.2.0
prometheus-client==0.17.1
prompt-toolkit==3.0.39
proto-plus==1.22.3
protobuf==4.24.0rc2
psutil==5.9.5
psycopg2-binary==2.9.5
ptyprocess==0.7.0
pure-eval==0.2.2
py==1.11.0
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycparser==2.21
pycryptodome==3.17
pydantic==2.1.1
pydantic-settings==2.0.3
pydantic_core==2.4.0
Pygments==2.15.1
PyJWT==2.8.0
pyparsing==3.1.0
pytest==7.1.3
python-dateutil==2.8.2
python-dotenv==1.0.0
python-json-logger==2.0.7
python-jwt==2.0.1
python-multipart==0.0.5
pytz==2023.2
PyYAML==6.0.1
pyzmq==25.1.0
qtconsole==5.4.3
QtPy==2.3.1
redis==4.5.5
referencing==0.30.0
requests==2.28.2
requests-toolbelt==0.10.1
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.5.2
rpds-py==0.9.2
rsa==4.9
scikit-learn==1.2.2
scipy==1.11.1
Send2Trash==1.8.2
six==1.16.0
sniffio==1.3.0
soupsieve==2.4.1
SQLAlchemy==1.4.47
stack-data==0.6.2
starlette==0.27.0
terminado==0.17.1
threadpoolctl==3.2.0
tinycss2==1.2.1
tomli==2.0.1
tornado==6.3.2
traitlets==5.9.0
typer==0.9.0
typing-inspect==0.9.0
typing_extensions==4.7.1
tzdata==2023.3
uri-template==1.3.0
uritemplate==4.1.1
urllib3==1.26.15
uvicorn==0.23.2
wcwidth==0.2.6
webcolors==1.13
webencodings==0.5.1
websocket-client==1.6.1
widgetsnbextension==4.0.8

Btw, I'm using Python 3.10.6

uriyyo commented 1 year ago

Hi @pjcopado,

Sorry for a long response. I was able to reproduce this issue on my env.

Could you please replace Field with Query in your code:

from fastapi import Query
from fastapi_pagination import Page

Page = Page.with_custom_options(size=Query(10, ge=1, le=50))
pjcopado commented 1 year ago

Hi @uriyyo

It works with Query! šŸ˜€

uriyyo commented 1 year ago

Great, happy to hear that!

Can I close this issue?

pjcopado commented 1 year ago

Yes! I'm just confused and wondering if in the future it'll work with Field, but you can close the issue. Thanks!

uriyyo commented 1 year ago

I guess this is actually fastapi issue, not fastapi-pagination itself.

I will close this issue but will keep in mind that this can happen. I will add documentation for this case.

dhait commented 1 year ago

Thank you, I had this problem too and Query fixed it. Also I note that the add_pagination() call has to come after the route definition where Page is defined -- this is not consistent in the docs.

uriyyo commented 1 year ago

Yup, this library definitely lacks good documentation.

I always want to improve it, but never have enough time to do it (I wish to have better documentation skills).