0

In a templates/index.html file I can write

<link rel="stylesheet" type="text/css"
      href={{ url_for('static', filename='css/main.css') }}>

and url_for retrieves the CSS file from a static folder just fine (FWIW, notwithstanding that css/main.css has a forward slash on Windows).

I would like to open a static file static/number.txt, read one number from it, and display that number.

app.py

from flask import Flask, render_template, url_for


app = Flask(__name__)


@app.route('/')
def number():
    filename = url_for('static', filename='txt/number.txt')
    with open(filename) as f:
        number = next(f)
    return render_template('index.html', number=number)


if __name__ == '__main__':
    app.run()

static/txt/number.txt

2345

templates/index.html

<body>
    {{ number }}
</body>

For this simple example, I am not attempting to optimize by running Nginx and IIUC this simple task requires neither send_from_directory() nor os.getcwd() . There is also no need to tinker with app.static_folder nor to be worried about the folder separator by using os.join.path() (I happen to be on Windows, but ideally this should be invisible in the code). There is also no need to modify the default static folder.

The code above gives me

c:\read-static>python app.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
ERROR in app: Exception on / [GET]
Traceback (most recent call last):
  File "C:\anaconda3\flask\app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  ...
  File "app.py", line 10, in number
    with open(filename) as f:
FileNotFoundError: [Errno 2] No such file or directory: '/static/txt/number.txt'
127.0.0.1 - "GET / HTTP/1.1" 500 -

What am I missing? Should I be using os.getcwd() to read a static file in Flask? Something in relying on os.getcwd() seems unpalatable/hackish.

Vrokipal
  • 784
  • 5
  • 18

3 Answers3

0

Using

import os

and replacing the url_for(...) line with:

filename = os.path.join(os.getcwd(), 'static', 'txt', 'number.txt')

is a solution.

But the question stands. Isn't it perfectly all right to use url_for(...)?

Vrokipal
  • 784
  • 5
  • 18
  • 1
    No. URLs and file paths are conceptually separate, even though they largely overlap. – Dave W. Smith Sep 28 '18 at 03:21
  • @DaveW.Smith Ah!! Now we're onto something. But this is a trap even with the more rigorous, say, C++ type declarations. There too `int`s and `std::string`s are unguarded. And so we are either left to be more cautious, or regret that a particular library does not define type wrappers for, in this case, isolating URLs from OS paths. – Vrokipal Sep 28 '18 at 13:02
0

To setup full path you do not have to use url_for in python.

So, my recomendation is to change you code:

from os import path # os needed for folder and file address
# receiving address of a current dirrectory 
script_dir = path.dirname(path.abspath(__file__))
# adding address of your file
filename = script_dir.join('/static/txt/number.txt')
Viktor Ilienko
  • 817
  • 8
  • 15
0

To answer your second question:

"But the question stands. Isn't it perfectly all right to use url_for(...)?"

url_for(static, filename='bla.txt') returns /static/bla.txt, and not static/bla.txt. Since the starting slash indicates the root of your computer, you try to open a static folder directly at the root of your PC (which is not there).

url_for(static, filename='bla.txt')[1:] should work I think, but I think it's a very hacky solution.

So no, it is definity not perfectly alright, and I would definitely not recommend using it. Site routes and folder structure are not the same thing, and should not be intermingled without a good reason (and this is not one).

Joost
  • 3,609
  • 2
  • 12
  • 29