diff --git a/gSchoolWinter16_PythonFlaskApp.md b/gSchoolWinter16_PythonFlaskApp.md new file mode 100644 index 0000000..113a930 --- /dev/null +++ b/gSchoolWinter16_PythonFlaskApp.md @@ -0,0 +1,187 @@ + +# Your first Python API server + +In this tutorial you will learn how to make a simple HTTP/REST API in Python 2.7 (or 3.5). When you're done you will know about: + +* how to install/verify your Python installation +* how to do a basic package installation with `pip`, [Python's package manager](https://en.wikipedia.org/wiki/Pip_(Python)) +* a few common Python data structures (notably Python objects, dictionaries, tuples, lists) +* how to use the [`json` module](https://docs.python.org/2/library/json.html) in Python +* how to install the [Flask microframework](http://flask.pocoo.org/) for Python +* how to build and run a basic API in Flask + +#### ADDITIONAL RESOURCES + + +## STEP 1: Environment check + +You will minimally need Python 2.7.1x for this tutorial. Most if not all of the code will work in 3.5.x as well, so feel free to use that moving forward. + +### CHECK YOUR VERSION OF PYTHON +1. open terminal and type `python -V` +``` + $ python -V + Python 2.7.12 :: Anaconda 4.2.0 (64-bit) +``` + +2. execute `pip install flask` + * if you need to know more about `pip`, please [read more on it](http://www.pythonforbeginners.com/basics/python-pip-usage/) + +3. check to see if you have json installed + +``` + $ python + Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32 + Type "help", "copyright", "credits" or "license" for more information. + Anaconda is brought to you by Continuum Analytics. + Please check out: http://continuum.io/thanks and https://anaconda.org + >>> import json + >>> exit() +``` +* **If this returns an error please execute `pip install json`.** + +## Step 2: Build Your First Example App + +We're now going to build our first functional app. It isn't going to do anything yet -- we just want to make sure that we can see that everything is looking good in out environment. + +1. Open your favorite editor and **create the file `app.py`**. + +2. Write the following code in your file and **save it**: +```python + # -*- coding: utf-8 -*- + from flask import Flask + + app = Flask(__name__) + + if __name__ == "__main__": + app.run() +``` + +3. Go to your command line and type: +```bash + $ python app.py +``` +You should see the following: +``` + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) +``` +4. Hooray! You have your first Flask app, test it by opening your browser and pointing it to [localhost:5000](http://localhost:5000) (**except if** you don't see that last line `* Running ...`, then something is really wrong!) + + 1. **NOTE:** You haven't implemented any endpoints/routes, so you should get HTTP/404 when you hit [localhost:5000](http://localhost:5000). This is the correct behavior. + 2. The default port is `5000`, which can be changed later + + +## Step 3: Let's dissect what is going on ... + +line by line now ... + +```python +from flask import Flask +``` +This is a required import to get any basic Flask app running ... don't forget it, and don't linger on it. + +```python +app = Flask(__name__) +``` +This variable is necessary to initialize the Flask app -- it is the [Application Object](http://flask.pocoo.org/docs/0.11/api/#application-object). We're giving the name `__name__` because we are running this application as a standalone in a single file (`app.py`). You can read more about how Python handles application and module name spaces, but if a file you create is run from the command line, you it's application `__name__` attribute will be set to `__main__` (unless overridden by the developer, but that is not a typical move in Python, so don't go playing footsy with it until you know why you might want to do so!) + +```python +if __name__ == "__main__": + app.run() +``` + +This last bit of code tells Python that if this file is run from the command line (as opposed to being imported as a module), then execute the Flask application method [`run()`](http://flask.pocoo.org/docs/0.11/api/#flask.Flask.run), which starts the HTTP server (read [the docs carefully if you're deploying](http://flask.pocoo.org/docs/0.11/deploying/#deployment) in a production environment). + + +## Step 3a: Let's make a few quick changes + + +### Change the port + +Let's change the port to something like `5200` using the `port` parameter if the `Flask.run()` method like this: +```python +if __name__ == "__main__": + app.run(port=5200) +``` +### Change the host ipaddress +The default parameter for the `run()` method is indeed the IP of your running server (which defaults to `127.0.0.1`). You can set it to your fixed IP or use `0.0.0.0` to make it externally available. + +Let's put your fixed/assigned/DHCP address in first (remember this _may_ change depending on what network you are on): + +* Get the IP of your machine with `ifconfig` (on \*X) or `ipconfig` on (Win\*): +```bash + $ ifconfig +``` + +and make the change accordingly: + ```python + if __name__ == "__main__": + app.run(_your_ip_address_, port=5200) + ``` + + +### Set DEBUG mode ON + +Finally, let's make sure we run in debug mode so if anything crazy happens, we can find out what ... using the `debug` parameter makes that a snap ... + +And now update your `app.run()` accordingly: +```python + app.run(_your_ip_address_, port=5200, debug=True) +``` +With this, others will be able to see your server (on the same network) and you will be able to make changes to your code and **dynamically update your code without stopping and starting the server**. + +## Step 4: Let's make a route + +In our final step, we're going to implement something interesting (well sort of anyway). Let's say we have an application which will respond to `/status` by returning the following json: + + +### INGREDIENT A: Some JSON response spec + +```json +{ + "appname": "My awesome app", + "version": "0.1", + "creator": "BigBird007" +} +``` + +Because Python dictionaries are *very* similar to JSON objects, we can specify the above JSON like this in Python: +```python +data = { + "appname": "My awesome app", + "version": "0.1", + "creator": "BigBird007" +} +``` + +To convert this Python dictionary to JSON so we can return it over the wire, we use the `json` library. The two methods you should just remember are `json.dumps(a_python_object)` and `json.loads(a_json_string)` ... these two methods allow you to go back and forth from Python a JSON string (`dumps`) and from a JSON object to a Python object (`loads`) so + +### INGREDIENT B: A `/status` route and handler + +The syntax for specifying a route is +```python +@app.route("your/route/here") +def the_handler_for_the_route(): + # your awesome implementation here + + # return some cool JSON usually ... + return +``` + +This pattern will be used over and over in your code, study it and remember it. To learn how to use this pattern to specify things like your `GET` or `POST` handlers, read the docs further here: http://flask.pocoo.org/docs/0.11/api/#flask.Flask.route and here: http://flask.pocoo.org/docs/0.11/api/#url-route-registrations. Everything you'd want to know will be there (including handling variable parts of your route, defaults, method handling, etc.). + +Let's finish off our `/status` route ... make sure you place the route somewhere before the `if __name__ ...` line of code: + + +```python +@app.route("/status") +def status_endpoint(): + data = { + "appname": "My awesome app", + "version": "0.1", + "creator": "BigBird007" + } + return json.dumps(data) +``` + +Your final code should look like something like the sample file provided in [app.py](./app.py).