Closed gitgithan closed 1 year ago
Well, that may be something I overlooked because of the cat picture which is located at the root folder, not in chapter3. If you are in the root folder, you can run the example like this without any issue:
uvicorn chapter3.chapter3_custom_response_04:app
The path chapter3.chapter3_custom_response_04
is what we usually call the dot notation in Python. It's a way to tell Python the path of a module. In Python, a module is any valid Python file from which we can import functions or variables: chapter3_custom_response_04.py
is a module.
Modules are contained in packages. Packages are special directories for Python. To make Python treat a directory as a package, you need to create an __init__.py
file (which can be totally empty) at the root of this directory. chapter3
has an __init__.py
file, so it's a package.
This is why we can tell Python to look for chapter3.chapter3_custom_response_04
: chapter3_custom_response_04
is a valid module inside chapter3
package.
__file__
variableWe use the __file__
variable to know the path of the current module. This variable adds a .
at some point to tell us where we run the script from. Here how we are getting the root directory from the example:
root_directory = path.dirname(path.dirname(__file__))
Basically, we take the parent of the parent of the current file. If we run it from the root directory, __file__
has the following value:
/Users/fvoron/Development/Building-Data-Science-Applications-with-FastAPI/./chapter3/chapter3_custom_response_04.py
So taking the parent twice returns us the right path:
/Users/fvoron/Development/Building-Data-Science-Applications-with-FastAPI/.
However, if we run it from chapter3
, __file__
has the following value:
/Users/fvoron/Development/Building-Data-Science-Applications-with-FastAPI/chapter3/./chapter3_custom_response_04.py
If we take the parent twice here, we get chapter3
(because the parent of .
is chapter3
...), which is not the path we want:
/Users/fvoron/Development/Building-Data-Science-Applications-with-FastAPI/chapter3
A more robust solution would probably have been to make Python resolve the path so we don't have this problem. Doing this solves the problem whether we run the script from the root or chapter3
:
from os import path
from pathlib import Path
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/cat")
async def get_cat():
root_directory = Path(__file__).parent.parent
picture_path = path.join(root_directory, "assets", "cat.jpg")
return FileResponse(picture_path)
Basically, Path
is a more high-level library to help us manage path. When we pass it a string path, it'll resolve it completely so it'll discard the .
. Again, we get the parent twice.
chapter6
and name clashingThis is a more or less related problem of where we run the script from. If we run it from the root directory like this, no problem:
uvicorn chapter6.sqlalchemy.app:app
We get the module app
inside the package sqlalchemy
, which is itself inside the chapter6
package.
Problems arise if we want to run it from chapter6
. My clumsiness here was to call the package sqlalchemy
which is in conflict with the library sqlalchemy
. In this context, Python will always choose the "closest" package, i.e. our package, and ignore the library. This is why we get cannot import name 'text' from 'sqlalchemy'
.
Why does it work when running from the root folder? Simply because there is no sqlalchemy
package in the root folder. If we had one, we would get the same problem. Once again, the "bad" thing I did was to name my package exactly like the library.
Hope it clarifies things!
@gitgithan let me know if we can close this issue now. @frankie567 do tell me if there is something regarding this exchange that needs to be added to the README if it can benefit our other reader!
@rajat-packt Yep we can close this, thanks so much to frankie567 for being so willing to share knowledge
@gitgithan let me know if we can close this issue now. @frankie567 do tell me if there is something regarding this exchange that needs to be added to the README if it can benefit our other reader!
I got tripped up with the sqlalchemy directory name issue myself, and Google searches did not find this closed issue (I just happened to find this when looking to file a new issue on the matter) - would suggest an update to the README or any other online resource to suggest using a different name for that directory (and a note that it is never a good idea to name anything in your code the same as a reserved word, package/subpackage/module name, etc)
Since
chapter3_first_endpoint_01
, we have been instructed to run uvicorn asuvicorn 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 asuvicorn chapter3_custom_response_04:app
, which starts uvicorn proper, but upon httpie query gives Internal Server Error.To investigate:
Output:
At that moment I just hacked my way through by wrapping another
path.dirname
after observing the folder structure to seeassets
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, withuvicorn 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:
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
withprint(__file__)
inside chapter3 folder andpython -m test
doesn't show this extra dot)Only at
chapter6/sqlalchemy
did i realize what i did wrong in chapter3. Again I ranuvicorn sqlalchemy.app:app
(inside chapter6 folder) and gotThen i saw
from chapter6.sqlalchemy.models import
in app.py and realized I must run it outside chapter6 withuvicorn 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 runninguvicorn 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 theBuilding-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