Skip to content

Commit c988d80

Browse files
committed
Update README with instructions for a Python project and using flask run
1 parent 8ba9121 commit c988d80

File tree

1 file changed

+138
-19
lines changed

1 file changed

+138
-19
lines changed

README.md

Lines changed: 138 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ uWSGI with Nginx is one of the best ways to deploy a Python web application, so
3535

3636
* **`python3.6`** tag: general Flask web application:
3737

38-
[**example-flask-python3.6.zip**](<https://github.com/tiangolo/uwsgi-nginx-flask-docker/releases/download/v0.3.0/example-flask-python3.6.zip>)
38+
[**example-flask-python3.6.zip**](<https://github.com/tiangolo/uwsgi-nginx-flask-docker/releases/download/v0.3.1/example-flask-python3.6.zip>)
39+
40+
* **`python3.6`** tag: general Flask web application, structured as a package, for bigger Flask projects, with different submodules. Use it only as an example of how to import your modules and how to structure your own project:
41+
42+
[**example-flask-package-python3.6.zip**](<https://github.com/tiangolo/uwsgi-nginx-flask-docker/releases/download/v0.3.1/example-flask-package-python3.6.zip>)
3943

4044
* **`python3.6-index`** tag: `static/index.html` served directly in `/`, e.g. for Angular, React, or any other Single-Page Application that uses a static `index.html`, not modified by Python:
4145

42-
[**example-flask-python3.6-index.zip**](<https://github.com/tiangolo/uwsgi-nginx-flask-docker/releases/download/v0.3.0/example-flask-python3.6-index.zip>)
46+
[**example-flask-python3.6-index.zip**](<https://github.com/tiangolo/uwsgi-nginx-flask-docker/releases/download/v0.3.1/example-flask-python3.6-index.zip>)
4347

4448
## General Instructions
4549

@@ -55,7 +59,7 @@ There are several image tags available for Python 3.6, Python 3.5 and Python 2.7
5559

5660
As of now, [everyone](https://www.python.org/dev/peps/pep-0373/) [should be](http://flask.pocoo.org/docs/0.12/python3/#python3-support) [using **Python 3**](https://docs.djangoproject.com/en/1.11/faq/install/#what-python-version-should-i-use-with-django).
5761

58-
There are two template projects that you can download (as a `.zip` file) to bootstrap your project in the section "**Examples (project templates)**" above.
62+
There are several template projects that you can download (as a `.zip` file) to bootstrap your project in the section "**Examples (project templates)**" above.
5963

6064
This Docker image is based on [**tiangolo/uwsgi-nginx**](https://hub.docker.com/r/tiangolo/uwsgi-nginx/). That Docker image has uWSGI and Nginx installed in the same container and was made to be the base of this image.
6165

@@ -89,6 +93,7 @@ def hello():
8993
return "Hello World from Flask"
9094

9195
if __name__ == "__main__":
96+
# Only for debugging while developing
9297
app.run(host='0.0.0.0', debug=True, port=80)
9398
```
9499

@@ -174,6 +179,7 @@ def route_frontend(path):
174179
return send_file('./static/index.html')
175180

176181
if __name__ == "__main__":
182+
# Only for debugging while developing
177183
app.run(host='0.0.0.0', debug=True, port=80)
178184
```
179185

@@ -228,6 +234,85 @@ docker run -d --name mycontainer -p 80:80 myimage
228234

229235
**Note**: As your `index.html` file will be served from `/` and from `/static/index.html`, it would be better to have absolute paths in the links to other files in the `static` directory from your `index.html` file. As in `/static/css/styles.css` instead of relative paths as in `./css/styles.css`. But still, above you added code in your `main.py` to handle that too, just in case.
230236

237+
## QuickStart for bigger projects structured as a Python package
238+
239+
**Note**: You can download the **example-flask-package-python3.6.zip** project example and use it as an example or template for your project from the section **Examples** above.
240+
241+
---
242+
243+
You should be able to follow the same instructions as in the "**QuickStart**" section above, with some minor modifications:
244+
245+
* Instead of putting your code in the `app/` directory, put it in a directory `app/main/`.
246+
* Add a file `__init__.py` inside of that `app/main/` directory.
247+
248+
Your file structure would look like:
249+
250+
```
251+
.
252+
├── app
253+
│   ├── main
254+
│   │   ├── __init__.py
255+
│   │   ├── main.py
256+
└── Dockerfile
257+
```
258+
259+
...instead of:
260+
261+
```
262+
.
263+
├── app
264+
│ ├── main.py
265+
└── Dockerfile
266+
```
267+
268+
* In the file `app/main/__init__.py` put:
269+
270+
```python
271+
from .main import app
272+
```
273+
274+
...after that, everything should work as expected. All the other instructions would apply normally.
275+
276+
### Working with submodules
277+
278+
* After adding all your modules you could end up with a file structure similar to (taken from the example project):
279+
280+
```
281+
.
282+
├── app
283+
│   ├── main
284+
│   │   ├── api
285+
│   │   │   ├── api.py
286+
│   │   │   ├── endpoints
287+
│   │   │   │   ├── __init__.py
288+
│   │   │   │   └── user.py
289+
│   │   │   ├── __init__.py
290+
│   │   │   └── utils.py
291+
│   │   ├── core
292+
│   │   │   ├── app_setup.py
293+
│   │   │   ├── database.py
294+
│   │   │   └── __init__.py
295+
│   │   ├── __init__.py
296+
│   │   ├── main.py
297+
│   │   └── models
298+
│   │   ├── __init__.py
299+
│   │   └── user.py
300+
└── Dockerfile
301+
```
302+
303+
* Make sure you follow [the offical docs while importing your modules](https://docs.python.org/3/tutorial/modules.html#intra-package-references):
304+
305+
* For example, if you are in `app/main/main.py` and want to import the module in `app/main/core/app_setup.py` you would wirte it like:
306+
307+
```python
308+
from .core import app_setup
309+
```
310+
311+
* And if you are in `app/main/api/endpoints/user.py` and you want to import the `users` object from `app/main/core/database.py` you would write it like:
312+
313+
```python
314+
from ...core.database import users
315+
```
231316

232317
## Advanced instructions
233318

@@ -416,34 +501,39 @@ If you go to your Docker container URL you should see your app, and you should b
416501

417502
...but, as uWSGI loads your whole Python Flask web application once it starts, you won't be able to edit your Python Flask code and see the changes reflected.
418503

419-
To be able to (temporarily) debug your Python Flask code live, you can run your container overriding the default command (that starts Supervisord which in turn starts uWSGI and Nginx) and run your application directly with `python`, in debug mode.
504+
To be able to (temporarily) debug your Python Flask code live, you can run your container overriding the default command (that starts Supervisord which in turn starts uWSGI and Nginx) and run your application directly with `python`, in debug mode, using the `flask` command with its environment variables.
420505

421-
So, with all the modifications above and making your app run directly with `python`, the final Docker command would be:
506+
So, with all the modifications above and making your app run directly with `flask`, the final Docker command would be:
422507

423508
```bash
424-
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app myimage python /app/main.py
509+
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app -e FLASK_APP=main.py -e FLASK_DEBUG=1 myimage flask run --host=0.0.0.0 --port=80
510+
```
511+
512+
Or in the case of a package project, you would set `FLASK_APP=main/main.py`:
513+
514+
```bash
515+
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app -e FLASK_APP=main/main.py -e FLASK_DEBUG=1 myimage flask run --host=0.0.0.0 --port=80
425516
```
426517

427518
Now you can edit your Flask code in your local machine and once you refresh your browser, you will see the changes live.
428519

429520
Remember that you should use this only for debugging and development, for deployment in production you shouldn't mount volumes and you should let Supervisord start and let it start uWSGI and Nginx (which is what happens by default).
430521

431-
For these last steps to work (live debugging and development), your Python Flask code should have that section with:
522+
An alternative for these last steps to work when you don't have a package but just a flat structure with single files (modules), your Python Flask code could have that section with:
432523

433-
```python
434-
if __name__ == "__main__":
435-
app.run(host='0.0.0.0', debug=True, port=80)
436-
```
524+
```python
525+
if __name__ == "__main__":
526+
# Only for debugging while developing
527+
app.run(host='0.0.0.0', debug=True, port=80)
528+
```
437529

438-
otherwise your app will only listen to `localhost` (inside the container), in another port (5000) and not in debug mode.
530+
...and you could run it with `python main.py`. But that will only work when you are not using a package structure and don't plan to do it later. In that specific case, if you didn't add the code block above, your app would only listen to `localhost` (inside the container), in another port (5000) and not in debug mode.
439531

440532
**Note**: The example project **example-flask-python3.6** includes a `docker-compose.yml` and `docker-compose.override.yml` with all these configurations, if you are using Docker Compose.
441533

442534
---
443535

444-
Also, if you want to do the same live debugging using the tags with `-index` (to serve `/app/static/index.html` directly when requested for `/`) your Nginx won't serve it directly as it won't be running (only your Python Flask app in debug mode will be running).
445-
446-
For that, your Python Flask code should have that section with:
536+
Also, if you want to do the same live debugging using the environment variable `STATIC_INDEX=1` (to serve `/app/static/index.html` directly when requested for `/`) your Nginx won't serve it directly as it won't be running (only your Python Flask app in debug mode will be running).
447537

448538
```python
449539
from flask import Flask, send_file
@@ -457,7 +547,25 @@ def main():
457547
return send_file('./static/index.html')
458548
```
459549

460-
That makes sure your app also serves the `/app/static/index.html` file when requested for `/`.
550+
...that makes sure your app also serves the `/app/static/index.html` file when requested for `/`.
551+
552+
And if you are using a SPA framework, to allow it to handle the URLs in the browser, your Python Flask code should have the section with:
553+
554+
```python
555+
# Everything not declared before (not a Flask route / API endpoint)...
556+
@app.route('/<path:path>')
557+
def route_frontend(path):
558+
# ...could be a static file needed by the front end that
559+
# doesn't use the `static` path (like in `<script src="bundle.js">`)
560+
file_path = './static/' + path
561+
if os.path.isfile(file_path):
562+
return send_file(file_path)
563+
# ...or should be handled by the SPA's "router" in front end
564+
else:
565+
return send_file('./static/index.html')
566+
```
567+
568+
...that makes Flask send all the CSS, JavaScript and image files when requested in the root (`/`) URL but also makes sure that your front end SPA handles all the other URLs that are not defined in your Flask app.
461569

462570
That's how it is written in the tutorial above and is included in the downloadable examples.
463571

@@ -471,12 +579,18 @@ And since the only process running was your debugging server, that now is stoppe
471579

472580
Then you will have to start your container again after fixing your code and you won't see very easily what is the error that is crashing your server.
473581

474-
So, while developing, you could do the following (that's what I normally do):
582+
So, while developing, you could do the following (that's what I normally do, although I do it with Docker Compose, as in the example projects):
475583

476584
* Make your container run and keep it alive in an infinite loop (without running any server):
477585

478586
```bash
479-
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app myimage bash -c "while true ; do sleep 10 ; done"
587+
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app -e FLASK_APP=main.py -e FLASK_DEBUG=1 myimage bash -c "while true ; do sleep 10 ; done"
588+
```
589+
590+
* Or, if your project is a package, set `FLASK_APP=main/main.py`:
591+
592+
```bash
593+
docker run -d --name mycontainer -p 80:80 -v $(pwd)/app:/app -e FLASK_APP=main/main.py -e FLASK_DEBUG=1 myimage bash -c "while true ; do sleep 10 ; done"
480594
```
481595

482596
* Connect to your container with a new interactive session:
@@ -490,13 +604,18 @@ You will now be inside your container in the `/app` directory.
490604
* Now, from inside the container, run your Flask debugging server:
491605

492606
```bash
493-
python main.py
607+
flask run --host=0.0.0.0 --port=80
494608
```
495609

496610
You will see your Flask debugging server start, you will see how it sends responses to every request, you will see the errors thrown when you break your code and how they stop your server and you will be able to re-start your server very fast, by just running the command above again.
497611

498612
## What's new
499613

614+
2017-09-02:
615+
616+
* Example project with a [Python package](https://docs.python.org/3/tutorial/modules.html#packages) structure and a section explaining how to use it and structure a Flask project like that.
617+
* Also, the examples and documentation now use the [`flask run`](http://flask.pocoo.org/docs/0.12/quickstart/#a-minimal-application) commands, that allows running a package application while developing more easily.
618+
500619
2017-08-10: Many changes:
501620

502621
* New official image tags: `python3.6`, `python3.6-index`, `python.3.5`, `python3.5-index`, `python2.7` and `python2.7-index`. All the other images are deprecated in favor is this ones.

0 commit comments

Comments
 (0)