packtpublishing / building-data-science-applications-with-fastapi Goto Github PK
View Code? Open in Web Editor NEWBuilding Data Science Applications with FastAPI, Published by Packt
License: MIT License
Building Data Science Applications with FastAPI, Published by Packt
License: MIT License
Page 320 says "If you need to deep copy the values"
According to https://numpy.org/doc/stable/reference/generated/numpy.copy.html:
To ensure all elements within an object array are copied, use copy.deepcopy:
chapter9_app_external_api_test.py in this code repo has the fixtures, but is missing the actual test function
From the book:
@pytest.mark.asyncio
async def test_get_employees(test_client: httpx.AsyncClient):
response = await test_client.get("/employees")
assert response.status_code == status.HTTP_200_OK
json = response.json()
assert json == MockExternalAPI.mock_data
return response.json()
If you try to test the code as is you get a "no tests ran" message. Have to add the test function (above) to the test.py
Delete .vscode
and newsgroups_model.joblib
and add .vscode
in .gitignore
.
Book says run alembic revision --autogenerate -m "initial migration"
, implicitly saying run from the folder Building-Data-Science-Applications-with-FastAPI
since alembic.ini
is referencing script_location = chapter6/sqlalchemy_relationship/alembic
instead of the default alembic
.
However this will result in errors saying FAILED: No config file 'alembic.ini' found, or file has no '[alembic]' section
.
What i needed to make this work was to specify the path of alembic.ini using -c option.
alembic -c chapter6/sqlalchemy_relationship/alembic.ini revision --autogenerate -m "initial migration"
,
same for alembic -c chapter6/sqlalchemy_relationship/alembic.ini upgrade head
.
Another issue
When I first follow along the book, i get a empty migration script
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
I found this message (https://stackoverflow.com/a/67509934/8621823) explaining that if the table is already there, the migration script will be empty at those portions, so I deleted the sqlite database, and deleted all the migration scripts, tried again and it worked.
I also had to delete the database because somewhere along the way, running alembic revision
gave me ERROR [alembic.util.messaging] Can't locate revision identified by 'ca9e1b2549f3'
. I'm guessing this revision was stored in the database, and it was looking for this revision under alembic/versions folder, which i had cleared.
Question
Can't locate revision error
is right, and whether deleting sqlite database and all the existing migration scripts is necessary)alembic revision
command to add some new tables and columns under def upgrade
in the migration script, to be applied with alembic upgrade head
to change the database?Although the with
statements mentioned below are written in tests, they can be merged. I am not very much sure about this but we can have a discussion on it.
29 @pytest.fixture
30 async def test_client():
31 app.dependency_overrides[get_database] = get_test_database
32 async with LifespanManager(app):
33 async with httpx.AsyncClient(app=app, base_url="http://app.io") as test_client:
34 yield test_client
35
36
37 @pytest.fixture(autouse=True, scope="module")
this can reframed as:
29 @pytest.fixture
30 async def test_client():
31 app.dependency_overrides[get_database] = get_test_database
32 async with LifespanManager(app), httpx.AsyncClient(app=app, base_url="http://app.io") as test_client:
33 yield test_client
34
35
36 @pytest.fixture(autouse=True, scope="module")
Similarly in chapter9/chapter9_app_post_test.py
, chapter9/chapter9_app_test.py
, chapter9/chapter9_db_test.py
. There could be other collapsable with
statements that could be merged that I haven't looked.
I'm getting the following error message when testing out chapter3_response_path_parameters_02.py.
h11._util.LocalProtocolError: Too much data for declared Content-Length
This looks like a known issue. I'm guessing its because content-length: 4
in the response when returning None
. Returning Response(status_code=status.HTTP_204_NO_CONTENT)
seems to fix the issue (content-length zero in this case).
The book demonstrates calling using httpie, so i wanted to test calling from the docs page since it provides convenience in
chapter5_function_dependency_03
However I am unable to leave it empty. If i type gibberish, then it gives in response
{
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...
}
Question
Where is the validation on the docs defined? It seems like a separate system from fastapi?
When things like Query(...,ge=0) are defined and you enter -1, the docs do not even allow Execute to be clicked, showing a red background in the textbox. What is causing this?
I also noticed in chapter5_function_dependency_02
if skip: int = Query(0, ge=0)
was changed to skip: int = Query(0, gt=0)
, minimum: 0
will disappear from the docs, how does this happen?
What is the difference between calling from httpie, docs or other tools?
I found this happening in 2 places by searching detail=f"Post
in vscode.
On the front-end, the interpolated string looks like it's printing some information about a function instead of the post id. I guess the python built-in id
was used when it should be comment.post_id
?
Edit
Below is no longer an issue. My confusion arose because I set exclude_unset=False
to investigate its effects, then gave only {"title": "string"}
when calling the endpoint. This combination leads to Internal Server Error which prompted this issue.
If we do it properly by setting exclude_unset=True
, there are no issues with the question below
To allow user to call update endpoint with only title, with only content, and with none of both?
Nevertheless the investigations on effects of exclude_unset=False
could be helpful to other readers.
There are 2 independent dimensions of experimentation
This post is half error reporting and half what I wish was explained better.
I was testing chapter4_working_pydantic_objects_05.py
.
First I edited the file to set up dummy data because it was empty and cannot be tested (will always get 404)
class DummyDatabase:
posts: Dict[int, PostDB] = {1:PostDB(id=1,title='title1',content='content1',nb_views=10)}
Then from automated docs, I called the endpoint with {"title": "string"}
, intentionally leaving out content key that was suggested by the docs (or else cannot see effect of exclude_unset=True
).
Then terminal errors with
/site-packages/fastapi/routing.py", line 138, in serialize_response
raise ValidationError(errors, field.type_)
pydantic.error_wrappers.ValidationError: 1 validation error for PostPublic
response -> content
I guess this is because PostPublic
which was inherited from PostBase
which had content:str
type which did not allow None.
Question:
So how do we make this work? (To allow user to call update endpoint with only title, with only content, and with none of both?).
Making fields in PostBase
optional doesn't seem like the correct way to handle this.
On a separate but related issue, this lesson could have been clearer on how a user could understand the effects of exclude_unset=True
. To look at what this parameter is doing, and to solve the above issue, I
response_model=PostPublic
from the path decorator so it doesn't constrain response types (which solves this issue, probably wrongly though)print(updated_fields)
to the code{"title": "string"}
(you won't see the effect of exclude_unset
if you used default {"title": "string", "content": "string"}
suggested by automated docs)Response Body:
{
"title": "string",
"content": "content1",
"id": 1,
"nb_views": 10
}
print(updated_fields)
gives {'title': 'string'}
{
"title": "string",
"content": null,
"id": 1,
"nb_views": 10
}
print(updated_fields)
gives {'title': 'string', 'content': None}
Explanation
When exclude_unset=False
, the update endpoint was not given content
key when called. PostPartialUpdate
did not exclude what was unset/not given (content), so it filled in content with None from content: Optional[str] = None
. This content:None
was then put into the response through post_db.copy(update=updated_fields)
.
When exclude_unset=True
, the update endpoint was also not given the content
key when called. PostPartialUpdate
excluded what was not unset/not given (content), so updated_fields
contains only the title, leaving content
as the original hardcoded value
Hi François, first off, thanks for your amazing book! Very straight to the point in presenting the core concepts of FastAPI and its ecosystem :)
I'd just like to point out a possible typo (though quite irrelevant) in the create_comment
definition in Tortoise
section. If I'm not wrong, in the spirit of the previous examples the comment
argument should rather be type-hinted as a CommentCreate
instance.
Since chapter3_first_endpoint_01
, we have been instructed to run uvicorn as uvicorn chapter3_first_endpoint_01:app
, implicitly meaning we should go inside the chapter3 folder first.
Later I got stuck at chapter3_custom_response_04
because I ran it as uvicorn chapter3_custom_response_04:app
, which starts uvicorn proper, but upon httpie query gives Internal Server Error.
RuntimeError: File at path /Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/chapter3/assets/cat.jpg does not exist.
To investigate:
print(__file__)
print(path.dirname(__file__))
print(path.dirname(path.dirname(__file__)))
Output:
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/chapter3/./chapter3_custom_response_04.py
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/chapter3/.
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/chapter3
At that moment I just hacked my way through by wrapping another path.dirname
after observing the folder structure to see assets
folder is on parallel level as chapter folders.
It is pretty impossible for the average reader to know that it has to be ran from outside chapter3, with uvicorn chapter3.chapter3_custom_response_04:app
for the httpie GET to work, after being accustomed to all previous exercises being run from inside chapter3.
Same 3 prints when run from outside chapter3:
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/./chapter3/chapter3_custom_response_04.py
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/./chapter3
/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/.
Question 1/2
Why is there a dot added somewhere along the path? (It caused me to have to wrap a 3rd path.dirname to move up 1 more level past the dot when I ran it wrongly).
Is that vscode/fastapi/uvicorn's behaviour? What's the logic to where in the path, or when it is added? (making a simple test.py
with print(__file__)
inside chapter3 folder and python -m test
doesn't show this extra dot)
Only at chapter6/sqlalchemy
did i realize what i did wrong in chapter3.
Again I ran uvicorn sqlalchemy.app:app
(inside chapter6 folder) and got
File "/Users/hanqi/.pyenv/versions/3.8.12/envs/lewagon/lib/python3.8/site-packages/databases/core.py", line 10, in <module>
from sqlalchemy import text
ImportError: cannot import name 'text' from 'sqlalchemy' (/Users/hanqi/code/Building-Data-Science-Applications-with-FastAPI/chapter6/./sqlalchemy/__init__.py)
Then i saw from chapter6.sqlalchemy.models import
in app.py and realized I must run it outside chapter6 with
uvicorn chapter6.sqlalchemy.app:app
.
Question 2/2
From the error I see databases/core.py
is trying to import something from sqlalchemy.
Is this a case of clashing names, and the sqlalchemy folder overwrote the sqlalchemy library because the folder came first in import path?
If this is true, why does running uvicorn chapter6.sqlalchemy.app:app
outside chapter 6 correctly look for the sqlachemy library since the folder should still come first in import path? Is it because python does not recursively search into the Building-Data-Science-Applications-with-FastAPI
folder, so doesn't know there is a sqlachemy folder there too and thus no clash this time?
P.S I tested uvicorn sqlalchemy_relationship.app:app
inside chapter6 hoping there's no clash but get same error
In chapter 7, the author has written from fastapi.params import Depends
https://github.com/PacktPublishing/Building-Data-Science-Applications-with-FastAPI/blob/main/chapter7/chapter7_api_key_header.py#L2
I don't find any use in it. Can @frankie567 clarify this? Maybe, I am wrong.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.