Is there some good way to manage static files with fastapi ? I currently am trying to make a bigger application in fastapi and have to create multiple such folders for each router :/.
I also had the same issue some time ago...
My file structure is like
main.py # The main file which contains the fastapi instance )
src
-- folders in here with `__init__.py` in src folder and each folder src contains
# It would be like
main.py
src
- __init__.py
- folder1
- __init__.py
- folder1.py
Then in the main.py file
from src.folder1 import folder1 as folder1_main
...
# In the end of code i add
app.include_router(folder1_main.router)
So, for static files just create a folder named like staticserve in src folder and in there put something like this ( not to add as router though )
def templateit(appobj):
dir_path = os.path.dirname(os.path.realpath(__file__))
appobj.mount("/static", StaticFiles(directory=f"{dir_path}/static"), name="static")
templates = Jinja2Templates(directory=f"{dir_path}/templates")
return templates
Now when using templates in main.py or in some other folder in src, just import this func from there and
return cur.TemplateResponse(
"index.html",
{
"request": request,
}, status_code=200
)
Note : make a folder named static and templates in staticserve dir...
Also, sometimes it gives issue with endpoints like /a/b , works fine with /a though..
Related
I am trying to read a file in a custom module I have created in Python. But it is showing error when I try to do so.
Directory structure
base/
|
|_ app.py
|_ cmod/
|
|_ __init__.py
|_ util.py
|_ db.csv
util.py snippet
from csvhandler import CSVFile
def get_db():
with open("db.csv", "r+") as db:
data = CSVFile(db)
return data
app.py snippet
from cmod import util
data = util.get_db()
It throws the following error
FileNotFoundError: [Errno 2] No such file or directory: 'db.csv'
Is there any issue with imports of placement?
Note: The db.csv has to be put there only.
The current working directory when you run this is likely the one app.py sits in. open() looks for files in the current working directory, not the directory of the currently executing file.
The most portable way to open a file stored relatively would be to get the full path to the python file, isolate the directory, and then join it.
util.py
import os
def get_db():
directory = os.path.dirname(__file__)
with open(os.path.join(directory, "db.csv"), "r+") as db:
data = CSVFile(db)
return data
I am trying out static web pages service from Azure. But I need to access a list of files (for example images stored on the server) from the client side. I am trying to implement a Function that will iterate over the files in a particular folder and return a list of names.
The problem is that I simply cannot access those files. It can't find them from the context in which the Function runs as if they are stored on another machine.
I now use this function to print the folders and files accessible to the python.
This is the function I used called GetResources:
import logging
import os
import sys
import azure.functions as func
import json
import os
def showFolderTree(path):
show_files=True
indentation=1
file_output=False
tree = []
result=""
if not show_files:
for root, dirs, files in os.walk(path):
level = root.replace(path, '').count(os.sep)
indent = ' '*indentation*(level)
tree.append('{}{}/'.format(indent,os.path.basename(root)))
if show_files:
for root, dirs, files in os.walk(path):
level = root.replace(path, '').count(os.sep)
indent = ' '*indentation*(level)
tree.append('{}{}/'.format(indent,os.path.basename(root)))
for f in files:
subindent=' ' * indentation * (level+1)
tree.append('{}{}'.format(subindent,f))
for line in tree:
result+=line+"\n"
return result
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
# logging.info('Python HTTP trigger function processed a request.') --> where is this logged?
try:
errors=context.function_directory+"\n"
except Exception as e:
error="context error\n"
try:
errors+=os.path.dirname(os.path.realpath(__file__))+"\n"
errors+=os.getcwd()+"\n"
errors+=showFolderTree("/")
except Exception as e:
errors+=e
return func.HttpResponse(errors,status_code=200)
This function returns:
/home/site/wwwroot/GetResources
/home/site/wwwroot/GetResources
/home/site/wwwroot
/
app/
.bash_logout
.bashrc
.profile
site/
wwwroot/
.funcignore
requirements.txt
proxies.json
.gitignore
host.json
GetResources/
function.json
sample.dat
__init__.py
__pycache__/
__init__.cpython-38.pyc
.python_packages/
lib/
site-packages/
azure/
functions/
...
but I cannot find my files in the list.
Observations:
The Python code runs on a linux environment
I tried a similar code with C# and a Windows environment running on Azure
I tried placing a folder of assets with pictures in the api folder in which the function resides. Still could not find the files.
What am I doing wrong?
Note: I have a student subscription
Side issue: I cannot find anywhere the logs generated by the Function (generated by the logging function)
While Azure Static Web Apps act as a proxy to Azure Functions for APIs, the Functions themself are hosted separate to the Static Web App and don't share the filesystem.
So, instead of having these files pushed alongside the Web App, they should be stored in Blob Storage and from Functions, you could leverage Blob Storage Bindings to read/write them. To list files in a container, you need to bind to BlobContainerClient and use a method like BlobContainerClient.GetBlobs.
Duplicate of my answer on Microsoft Q&A
Below folder structure of my application:
rootfolder
/subfolder1/
/subfolder2
/subfolder3/test.py
my code inside of the subfolder3. But I want to write output of the code to subfolder1.
script_dir = os.path.dirname(__file__)
full_path = os.path.join(script_dir,'/subfolder1/')
I would like to know how can I do this wihout importing full path to directory.
It sounds like you want something along the lines of
project_root = os.path.dirname(os.path.dirname(__file__))
output_path = os.path.join(project_root, 'subfolder1')
The project_root is set to the folder above your script's parent folder, which matches your description. The output folder then goes to subfolder1 under that.
I would also rephrase my import as
from os.path import dirname, join
That shortens your code to
project_root = dirname(dirname(__file__))
output_path = join(project_root, 'subfolder1')
I find this version to be easier to read.
The best way to get this done is to turn your project into a module. Python uses an __init__.py file to recognize this setup. So we can simply create an empty __init__.py file at the root directory. The structure would look like:
rootfolder
/subfolder1/
/subfolder2
/subfolder3/test.py
__init__.py
Once that is done, you can reference any subfolders like the following:
subfolder1/output.txt
Therefore, your script would look something like this:
f = open("subfolder1/output.txt", "w+")
f.write("works!")
f.close()
I have this problem and do not know how to solve it efficiently.
I've this file structure
THE NUMBER OF FOLDERS NOR THE NAMES ARE GIVEN, IT'S ALL UNKNOWN
app/
__init__.py
modules/
__init__.py
ModuleA/
__init__.py
file.py
otherfile.py
config.ini
ModuleB/
__init__.py
file.py
otherfile.py
config.ini
ModuleC/
__init__.py
file.py
otherfile.py
config.ini
**arbitrary number of modules with same structure*
As you can notiche, app is the main package of my app, but I need an efficient way to import the mods folder and its' content
* My actual solution *
from app import modules ad mods
def load_modules_from_packages(self, pkg = mods):
pkgname = pkg.__name__
pkgpath = dirname(pkg.__file__)
for loader,name,ispkg in pkgutil.walk_packages(pkg.__path__, pkgname+'.'):
if ispkg is True:
__import__(name,globals(),locals(),[],0)
elif ispkg is False:
__import__(name,globals(),locals(),[],0)
This works since pkgutil iterate the structure with the dot notation for names, so import works well.
But now I want load infos in the config file if I am in one of the somemodule folder(the one with own init.py and config.ini
I want to do this to recreate the structure of module package and output it in a JSON rapresentation for another thing
* my other solution does not works*
def load_modules_from_packages(directory)
dir_path = dirname(directory.__file__)
dir_name = directory.__name__
for filename in glob.glob(dir_path + '/**/*.ini', recursive=True):
plugin = {}
plugin['name'] = filename.split('/')[-2]
plugin['path'] = dirname(filename)
plugin['config_file'] = filename
for pyname in glob.glob(dirname(filename)+ '/**/*.py', recursive=True):
importlib.import_module(pyname)
I cant use the solution posted in this thread
How to import a module given the full path?
since I do not know the module name, ad pointed without solutions in the comment.
spec = importlib.util.spec_from_file_location('what.ever', 'foo.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
I know 'foo.py' but I cant figure out 'what.ever' like pkgutil.walk_package does.
In fact the modules imported in this way have the package and name entry wrong. With this approach I cant figure out where I am in the file structure to create modules dictionary and the relative modules (for the JSON output)
Any help?
I followed the instructions from How to serve static files in Flask, but still couldn't get it working.
Here's my project structure:
Project_path
|
+--app
| |
| +--main.py
+--static
|
+--js
|
+--jquery-1.11.2.min.js
Here's main.py:
#app.route('/js/<path:path>')
def serve_static(path):
root_dir = os.path.dirname(os.getcwd())
print(os.path.join(root_dir, 'static', 'js', path))
return app.send_static_file(os.path.join(root_dir, 'static', 'js', path))
Here's index.html:
...
<script type="text/javascript" src="/js/jquery-1.11.2.min.js"></script>
...
And when I visit /, I could see the correct path of javascript file printed on the screen
which is Project_path/static/js/jquery-1.11.2.min.js.
But still, I got
127.0.0.1 - - [22/Dec/2014 11:26:30] "GET /js/jquery-1.11.2.min.js HTTP/1.1" 404 -
Any help is appreciated.
EDIT
After stepping through the send_static_file method, I find out what's going on. Basically, I shouldn't use abspath as argument, flask has a judgement in send_static_file:
if os.path.isabs(filename) or \
filename == '..' or \
filename.startswith('../'):
raise NotFound()
And since the filename I passed into is a abspath, flask raise NotFound().
It seems that what it supposed to be passed in is a relative path to self.static_folder(self is <Flask 'main'>), which, in my project, is Project_name/app/static. However, I didn't set static_folder myself which means flask thinks the static folder should be there.
I'm still trying to figure out what to do.
Finally got it working. use flask.send_from_directory
from flask import send_from_directory
#app.route('/js/<path:filename>')
def serve_static(filename):
root_dir = os.path.dirname(os.getcwd())
return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename)
It is now clear to me that flask really hate people putting app.py or in my case main.py into a subdirectory. Use send_static_file only if your static folder is what flask thinks to be, i.e. a folder with name static in the same directory with app.py.
All you need to do is, pass the static_folder parameter to the initiator:
static_url_path – can be used to specify a different path for the
static files on the web. Defaults to the name of the static_folder
folder.
static_folder – the folder with static files that should be served at
static_url_path. Defaults to the 'static' folder in the root path of
the application.
app = Flask(__name__, static_folder=os.path.abspath('/foo/bar/zoo/'))
Now, flask will look for a directory named static in /foo/bar/zoo from where to serve static files. You only use send_from_directory if you are serving media files which may not be in the same location as static files.
You forgot to add 'static' in the last os.path.join in the return clause.
for me this one worked :
#app.route('/static/<path:filename>')
def serve_static(filename):
root_dir = os.path.dirname(os.getcwd())
return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename)
beside adding this script into init
app._static_folder = os.path.abspath("static/")
app = Flask(__name__)
into __init__.py
One possible cause of 404 error for pages you just added (even if programmed correctly), is if you have previous versions of the script (.py file) still running: make sure to close out and end (terminate) the process.