Why isn't the code for this button to go to web page working?

I'm working Adobe Animate and I've been looking for a solution to this issue, but haven't been able to find anything. I'm new to coding in general, but have some experience.

I'm working Adobe Animate and I've been looking for a solution to this issue, but haven't been able to find anything. I'm new to coding in general, but have some experience.

What I'm trying to do is have a button link to a website and all the code I've been able to find hasn't been able to make it work. All I need it to do is function by clicking the button and it go to the page in a new tab. I tried the code snippet and got an error, but I've resolved that now and the button won't work.

See the code below.

function btn1Click(evt:MouseEvent):void {

var url:String = "https://www.discovergreece.com/en/wellness-and-spas";

var req:URLRequest = new URLRequest(url);

navigateToURL(req);

}

Any assistance is appricated.

Working with APIs using Flask, Flask-RESTPlus and Swagger UI

Working with APIs using Flask, Flask-RESTPlus and Swagger UI

The article discusses about how to use Flask and Flask-RESTPlus to create APIs and then use them to send and retrieve information.

The article discusses about how to use Flask and Flask-RESTPlus to create APIs and then use them to send and retrieve information.

While working on Machine Learning projects, I decided that I would like to develop complete applications. This would require developing APIs, so that we can post values and get responses of predictions. This is where Flask and Flask-RESTPlus come into the picture.

Flask enables exposure of Python functions as APIs. Flask-RESTPlus is an extension to Flask which improves upon its capabilities. It allows us to not only define REST APIs but also brings in Swagger UI for all the APIs.

In this article, I’ll explain how I developed a Flask application with several APIs and dummy data. The project is available as a GitHub Repository.

Installation

I began the process by creating a virtual environment using pipenv. You can read more about it in my article on comparison of virtual environments. I installed Flask and Flask-RESTPlus.

pipenv install flask
pipenv install flask-restplus

However, if you do not wish to work inside a pipenv environment, you can simply use the following commands.

pip install flask
pip install flask-restplus

Basics

Import

I began by importing Flask from flask. From flask_restplus, I imported Api to define the app and Resource which is received as a parameter in the various classes defined in the project.

from flask import Flask
from flask_restplus import Api, Resource

Define App

I defined the application as a flask application using the method Flask() which sets the name using __name__. Next, I’ll used Api to initialise the application.

flask_app = Flask(__name__)
app = Api(app = flask_app)

name_space = app.namespace('main', description='Main APIs')

I defined a namespace here. The concept is very simple. Whenever APIs are defined under a given namespace, they appear under a given heading in Swagger (we’ll explore Swagger later in this article). In namespace, the first variable defines the path and second defines the description for that space.

In the example above, the url for the namespace is [http://127.0.0.1:5000/main](http://127.0.0.1:5000/main "http://127.0.0.1:5000/main") and has the description as Main APIs in Swagger.

Define APIs

Lastly, I defined the endpoints. As our namespace is name_space, I’ll define the url through route as @name_space.route("/"). Next, Flask-RESTPlus requires us to define all endpoints under a given route inside a class. The methods can beget(), put(), and many others.

@name_space.route("/")
class MainClass(Resource):
	def get(self):
		return {
			"status": "Got new data"
		}
	def post(self):
		return {
			"status": "Posted new data"
}

In the example above, the APIs can be accessed at the path [http://127.0.0.1:5000/main](http://127.0.0.1:5000/main "http://127.0.0.1:5000/main"). The classname is set as MainClass with two methods, get() and post(). Whenever, I make a GET call, I get the reply with status field as Got new data and with POST call, I get the reply as Posted new data.

Run App

Now, everything is set up. The file is saved as basic.py. I run the app by the following command using pipenv.

pipenv shell
FLASK_APP=basic.py flask run

Using pip, you can use the following command.

FLASK_APP=basic.py flask run

Swagger UI

The best part of Flask-RESTPlus is that it automatically documents the APIs that we have created and they are visible in the Swagger UI. Go to [http://127.0.0.1:5000/](http://127.0.0.1:5000/ "http://127.0.0.1:5000/") and you can see all the APIs.

Both APIs are visible under the main namespace with the description Main APIs. We can try either API and check their functioning by clicking the Try it out button.

Try API

I used curl to make GET and POST requests from the terminal.

While using the curl command, first use the word curl followed by the method after the character -X. Finally, the endpoint is specified. Taking a look at our curl responses, we see that I received the correct data for both GET and POST APIs.

Taking it up a notch

There is so much more to Flask and Flask REST-Plus. Let’s explore them in even more depth and understand them better. The following code is available as [app.py]([https://github.com/kb22/Understanding-Flask-and-Flask-RESTPlus/blob/master/app.py)](https://github.com/kb22/Understanding-Flask-and-Flask-RESTPlus/blob/master/app.py) "https://github.com/kb22/Understanding-Flask-and-Flask-RESTPlus/blob/master/app.py)") in the GitHub Repository.

We can use a POST request to send data and save it. We can then use the GET request to get that data. Let’s say we have a project that manages names of individuals and stores them. We create a GET endpoint to fetch the name using id and POST endpoint to save a name against an id.

Here, I have created the path as [http://127.0.0.1:5000/names/<int:id>](http://127.0.0.1:5000/names/ "http://127.0.0.1:5000/names/<int:id>") where we will pass the id each time. To store names, I have created an object list_of_names which will be used to get and receive data.

Import more libraries

We have already imported Flask, Api, and Resource. We also import request from the flask package. This helps us get the request object and then retrieve information such as JSON data from it. We also import fields from flask_restplus package to define the type of elements such as String.

from flask import Flask, request
from flask_restplus import Api, Resource, fields

Add Application Information

We can also add extra information to our Flask app. This information is useful and is displayed in the Swagger UI.

flask_app = Flask(__name__)
app = Api(app = flask_app, 
		  version = "1.0", 
		  title = "Name Recorder", 
		  description = "Manage names of various users of the application")

name_space = app.namespace('names', description='Manage names')

We can define the version, title and the description of our application. We’ve set only one namespace namely names. The Swagger UI header would now look like the image below.

Define Models

Whenever we want to receive or send information in a particular format (JSON) we accomplish this with the help of model. We specify the name of the model. Next, we describe the information it expects and the properties of each expected value.

model = app.model('Name Model', 
		  {'name': fields.String(required = True, 
					 description="Name of the person", 
help="Name cannot be blank.")})

We define the model name as Name Model. It includes one parameter, namely name which is a required field, and define its description and help text. The API which will use this model will expect a JSON with a key as name.

list_of_names = {}

To keep track of all names, I’ll store them in list_of_names.

Define APIs

@name_space.route("/<int:id>")
class MainClass(Resource):

	@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, 
			 params={ 'id': 'Specify the Id associated with the person' })
	def get(self, id):
		try:
			name = list_of_names[id]
			return {
				"status": "Person retrieved",
				"name" : list_of_names[id]
			}
		except KeyError as e:
			name_space.abort(500, e.__doc__, status = "Could not retrieve information", statusCode = "500")
		except Exception as e:
			name_space.abort(400, e.__doc__, status = "Could not retrieve information", statusCode = "400")

	@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, 
			 params={ 'id': 'Specify the Id associated with the person' })
	@app.expect(model)		
	def post(self, id):
		try:
			list_of_names[id] = request.json['name']
			return {
				"status": "New person added",
				"name": list_of_names[id]
			}
		except KeyError as e:
			name_space.abort(500, e.__doc__, status = "Could not save information", statusCode = "500")
		except Exception as e:
name_space.abort(400, e.__doc__, status = "Could not save information", statusCode = "400")

Let’s break down the code snippet above into smaller parts to understand it better. We’ll explore the POST endpoint. The functionality of GET would be very similar.

Define route and class

@name_space.route("/<int:id>")
class MainClass(Resource):

We use the name_space namespace to define the route i.e. [http://127.0.0.1:5000/main/<int:id>](http://127.0.0.1:5000/main/ "http://127.0.0.1:5000/main/<int:id>"). It expects an Id to be sent as an integer. The name of out class is MainClass which has one parameter, Resource.

Define docs for the API

@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, 
params={ 'id': 'Specify the Id associated with the person' })

Using doc we can define the documentation for the API in Swagger. The responses key defines the various possible HTTP Status Codes. For each status code, we also define a text that describes it to the user. The params key defines the expected parameter. The API expects id in the URL and we specify a help text for the user. The Swagger UI looks like the image below.

The parameters are defined in the top part. All the expected responses with their description appear in the lower part.

Define the method

@app.expect(model)		
def post(self, id):
  try:
    list_of_names[id] = request.json['name']
    return {
      "status": "New person added",
      "name": list_of_names[id]
    }
  except KeyError as e:
    name_space.abort(500, e.__doc__, status = "Could not save information", statusCode = "500")
  except Exception as e:
name_space.abort(400, e.__doc__, status = "Could not save information", statusCode = "400")

We can now define our method. Before our method, we add the line expect(model) which defines that the API expects model. We wrap our code inside a try block and catch all errors that might occur. The request.json['name] gets us the received name and we can save it as well as send it back in the response. If the name key is missing, we get KeyError and we send Status Code 500. In other cases, we send Status Code 400.

Try the App

Let’s start the app.

FLASK_APP=app.py flask run

POST

We parse the response from the request, read the name and store it against the id in list_of_names. We also return the status and the name of the newly added person.

Error in POST Request

Say, we forgot to supply the name parameter in the data object. In this case, we’ll get an error.

On not supplying the key name, we got an error with Status Code 500 and the message Mapping key not found.

GET

We just pass the id that we want to get the name for and we get the status and the name of the person back if available.

Error in GET Request

Say, we don’t have any person against Id 2. If we try to retrieve that information, it’ll throw an error.

As that specific Id is not found, we get Status Code 500 and the message Mapping key not found.

Conclusion

In this article, we explored creation of APIs using Flask and Flask-RESTPlus. Both of these are great libraries to develop as well as document APIs in Python and interact with APIs using Swagger.

Please feel free to share your thoughts and ideas.

An Introduction to Flask

An Introduction to Flask

Flask is a micro web framework written in Python, easy-to-use microframework for Python that can help build scalable and secure web applications.

Flask is a micro web framework written in Python, easy-to-use microframework for Python that can help build scalable and secure web applications.

Here are a few reasons why Flask is great for beginners:

  1. It's easy to set up
  2. It's supported by an active community
  3. It's well documented
  4. It's very simple and minimalistic, and doesn't include anything you won't use
  5. At the same time, it's flexible enough that you can add extensions if you need more functionality

In this tutorial, we'll cover the following:

Table of Contents

  • Installation
  • Say "Hello World!" with Flask
  • Directory Structure
  • File Structure
  • Configuration
  • Initialization
  • Run, Flask, Run!
  • Views
  • Templates
  • Conclusion
  1. Installation of Flask and its prerequisites
  2. Recommended file and directory structure for a Flask project
  3. Configuration and initialization of a Flask app
  4. Creating views and templates

By the end of this tutorial, we will have built a simple static website using Flask. The code used in this tutorial is available for your reference on GitHub.

Ready? Let's dive in!

Installation

We'll need the following installed for this tutorial:

  1. Python (this tutorial uses Python 2)
  2. virtualenv and virtualenvwrapper
  3. Flask

You may already have Python installed on your system. You can check by running the python command in your terminal. If it's installed, you should see the following output:

$ python
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

If you don't have it installed, you can download it here.

We'll start by installing virtualenv, a tool to create isolated Python environments. We need to use virtual environments to keep the dependencies used by different Python projects separate, and to keep our global site-packages directory clean. We'll go one step further and install virtualenvwrapper, a set of extensions that make using virtualenv a whole lot easier by providing simpler commands.

$ pip install virtualenv
$ pip install virtualenvwrapper
$ export WORKON_HOME=~/Envs
$ source /usr/local/bin/virtualenvwrapper.sh

To create and activate a virtualenv, run the following commands:

$ mkvirtualenv my-venv
$ workon my-venv

Told you the commands were simple! We now have a virtualenv called my-venv, which we have activated and are currently working on. Now, any dependencies we install will be installed here and not globally. Remember to activate the virtualenv whenever you want to use or work on this project!

Next, let's create a directory for our app. This is where all our files will go:

$ mkdir my-project
$ cd my-project

Finally, let's install Flask:

$ pip install Flask

Installing Flask also installs a few other dependencies, which you will see when you run the following command:

$ pip freeze
click==6.6
Flask==0.11.1
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.11

What do all these packages do? Flask uses Click (Command Line Interface Creation Kit) for its command-line interface, which allows you to add custom shell commands for your app. ItsDangerous provides security when sending data using cryptographical signing. Jinja2 is a powerful template engine for Python, while MarkupSafe is a HTML string handling library. Werkzeug is a utility library for WSGI, a protocol that ensures web apps and web servers can communicate effectively.

You can save the output above in a file. This is good practice because anyone who wants to work on or run your project will need to know the dependencies to install. The following command will save the dependencies in a requirements.txt file:

pip freeze > requirements.txt

Say "Hello World!" with Flask

I think any beginner programming tutorial would be remiss if it didn't start with the classic "Hello World!" So here's how to do this in Flask:

Create the following file, hello_world.py, in your favourite text editor (I'm an Atom girl, myself):

# hello_world.py

from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'

We begin by importing the Flask class, and creating an instance of it. We use the __name__ argument to indicate the app's module or package, so that Flask knows where to find other files such as templates. Then we have a simple function that will display the string Hello World!. The preceeding decorator simply tells Flask which path to display the result of the function. In this case, we have specified the route /, which is the home URL.

Let's see this in action, shall we? In your terminal, run the following:

$ export FLASK_APP=hello_world.py
$ flask run
 * Serving Flask app "hello_world"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

The first command tells the system which app to run. The next one starts the server. Enter the specified URL ([http://127.0.0.1:5000/](http://127.0.0.1:5000/)) in your browser. Voila! It works!

Directory Structure

So far, we only have one functional file in our project: hello_world.py. A real-world web project usually has more files than that. It's important to maintain a good directory structure, so as to organize the different components of the application separately. These are a few of the common directories in a Flask project:

  1. /app: This is a directory within my-project . We'll put all our code in here, and leave other files, such as the requirements.txt file, outside.
  2. /app/templates: This is where our HTML files will go.
  3. /app/static: This is where static files such as CSS and JavaScript files as well as images usually go. However, we won't be needing this folder for this tutorial since we won't be using any static files.
$ mkdir app app/templates

Your project directory should now look like this:

├── my-project
       ├── app
       │   ├── templates
       ├── hello_world.py
       └── requirements.txt

The hello_world.py seems a little out of place now, doesn't it? Don't worry, we'll fix that in the next section.

File Structure

For the "Hello World!" example, we only had one file. To build our website, we'll need more files that serve various functions. Most Flask apps have the following basic file structure:

  1. run.py: This is the application's entry point. We'll run this file to start the Flask server and launch our application.
  2. config.py: This file contains the configuration variables for your app, such as database details.
  3. app/__init__.py: This file intializes a Python module. Without it, Python will not recognize the app directory as a module.
  4. app/views.py: This file contains all the routes for our application. This will tell Flask what to display on which path.
  5. app/models.py: This is where the models are defined. A model is a representation of a database table in code. However, because we will not be using a database in this tutorial, we won't be needing this file.

Some projects have more modules (for example, an app/views directory with many views files in it), but this'll do for now. Go ahead and create these files, and delete hello_world.py since we won't be needing it anymore:

$ touch run.py config.py
$ cd app
$ touch __init__.py views.py
$ rm hello_world.py

Here's our latest directory structure:

├── my-project
       ├── app
       │   ├── __init__.py
       │   ├── templates
       │   └── views.py
       ├── config.py
       ├── requirements.txt
       └── run.py

Now let's fill these empty files with some code!

Configuration

The config.py file should contain one variable per line, like so:

# config.py

# Enable Flask's debugging features. Should be False in production
DEBUG = True

Note that this config file is very simplified and would not be appropriate for a more complex application. For bigger applications, you may choose to have different config.py files for testing, development, and production, and put them in a config directory, making use of classes and inheritance. You may have some variables that should not be publicly shared, such as passwords and secret keys. These can be put in an instance/config.py file, which should not be pushed to version control.

Initialization

Next, we have to initialize our app with all our configurations. This is done in the app/__init__.py file. Note that if we set instance_relative_config to True, we can use app.config.from_object('config') to load the config.py file.

# app/__init__.py

from flask import Flask

# Initialize the app
app = Flask(__name__, instance_relative_config=True)

# Load the views
from app import views

# Load the config file
app.config.from_object('config')
Run, Flask, Run!

All we have to do now is configure our run.py file so we can start the Flask server.

# run.py

from app import app

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

To use the command flask run like we did before, we would need to set the FLASK_APP environment variable to run.py, like so:

$ export FLASK_APP=run.py
$ flask run

We'll get a 404 page because we haven't written any views for our app. We'll be fixing that shortly.

Views

From the "Hello World!" example, you already have an understanding of how views work. We use the @app.route decorator to specify the path we'd like the view to be dispayed on. We've already seen how to write a view that returns a string. Let's see what else we can do with views.

# views.py

from flask import render_template

from app import app

@app.route('/')
def index():
    return render_template("index.html")


@app.route('/about')
def about():
    return render_template("about.html")

Flask provides a method, render_template, which we can use to specifiy which HTML file should be loaded in a particular view. Of course, the index.html and about.html files don't exist yet, so Flask will give us a Template Not Found error when we navigate to these paths. Go ahead; run the app and see:

Templates

Flask allows us to use a variety of template languages, but Jinja2 is by far the most popular one. Remember it from our installed dependencies? Jinja provides syntax that allows us to add some functionality to our HTML files, like if-else blocks and for loops, and also use variables inside our templates. Jinja also lets us implement template inheritance, which means we can have a base template that other templates inherit from. Cool, right?

Let's begin by creating the following three HTML files:

$ cd app/templates
$ touch base.html index.html about.html

We'll start with the base.html file, using a slightly modified version of this example Bootstrap template:





  
    {% block title %}{% endblock %}
    
    
    
    
  
  
    
      
        
          
            [Home](/ "Home")
            [About](/about "About")
            [More About Flask](http://flask.pocoo.org "More About Flask")
          
        
      
      {% block body %}
      {% endblock %}
      
        
© 2016 Your Name Here

      
     
  

Did you notice the {% block %} and {% endblock %} tags? We'll also use them in the templates that inherit from the base template:



{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block body %}

  # Flask Is Awesome

  
And I'm glad to be learning so much about it!


{% endblock %}


{% extends "base.html" %}
{% block title %}About{% endblock %}
{% block body %}

  # The About Page

  
You can learn more about my website here.


{% endblock %}

We use the {% extends %} tag to inherit from the base template. We insert the dynamic content inside the {% block %} tags. Everything else is loaded right from the base template, so we don't have to re-write things that are common to all pages, such as the navigation bar and the footer.

Let's refresh our browser and see what we have now:

Conclusion

Congratulations for making it this far and getting your first Flask website up and running! I hope this introduction to Flask has whetted your appetite for exploring more. You now have a great foundation to start building more complex apps. Do have a look at the official documentation for more information.

Have you used other Python frameworks before? Where do you think Flask stands? Let's have a conversation in the comments below.

Dockerize your Flask Application

Dockerize your Flask Application

A simple article to explain how dockerized a Flask App.

In this article, I will show you how was my approach to solve this challenge!

The Flask Architecture and Dependencies

Even though a simple "Hello World" I worked with the best practices of architecture to it. I had the following directory structure:

.
├── backend
│   ├── app.py
│   ├── blueprints
│   │   ├── core
│   │   │   ├── bp.py
│   │   │   ├── __init__.py
│   │   │   ├── routes.py
│   │   │   └── tests
│   │   │       └── test_bp_core.py
│   │   └── __init__.py
│   └── __init__.py
├── conftest.py
├── Dockerfile
├── .dockerignore
├── .gitignore
├── Pipfile
├── Pipfile.lock
├── pytest.ini
└── README.md

The . (dot) is the root of project. In the backend I have Flask application properly. I used the factory pattern for the architecture. So, in the app.py I have this:

from flask import Flask

def create_app():
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'My_Top_Secert_Key'

    # Blueprint

    from backend.blueprints.core import bp as bp_core
    bp_core.config(app)

    return app

This code is responsible to create my Flask app. The focus of this article isn't to explain about Flask and so on. But, how to construct an app and run it into a Docker container. In the backend/bluprints/core I have my blueprint (see de documentation to know more about blueprints), the code is in bp.py and route.py, both configure my core blueprint. The first have this code:

from flask import Blueprint

bp = Blueprint('core', __name__)

def config(app):
    from backend.blueprints.core import routes # noqa
    app.register_blueprint(bp)

And the second has this code:

from backend.blueprints.core.bp import bp

@bp.route('/')
def home():
    return "Hello World"

In the backend/blueprint/core/tests I have my tests of this blueprint. It's a simple test, and its code is:

import pytest

from flask import url_for

@pytest.fixture
def resp(client):
    return client.get(url_for('core.home'))

def test_bp_home_status_code_ok(resp):
    assert resp.status_code == 200

Again, the focus of this article isn't to explain detailed of the Flask application, so if you have questions about how to test Flask apps

Back to the root of the application, I have some files that are interesting to see!

One is the conftest.py. This file is responsible for set up pytest to run all tests in the application. Its code is:

import pytest

from backend.app import create_app

@pytest.fixture
def app():
    app = create_app()
    return app

@pytest.fixture
def client(app):
    with app.test_client() as c:
        yield c

Other is the pytest.ini a simple file that inform to pytest the pattern of the files tests names, in our case the pytest.ini code is:

[pytest]
python_files=test*.py *tests.py

Two other files are Pipfile and Pipfile.lock. These files are created or updated by the command pipenv install [package]. As we are going to see later, the most important in our case is the Pipfile.lock because it locks the versions of all dependencies and allows a deterministic installation of these dependencies in our environment.

And we have Dockerfile, too. This file is responsible for the Docker Image creation. This Image will be used to up the container with the Flask Application. Its code is:

FROM python:3.7.1

LABEL Author="Vicente Marçal"
LABEL E-mail="[email protected]"
LABEL version="0.0.1b"

ENV PYTHONDONTWRITEBYTECODE 1
ENV FLASK_APP "backend/app.py"
ENV FLASK_ENV "development"
ENV FLASK_DEBUG True

RUN mkdir /app
WORKDIR /app

COPY Pip* /app/

RUN pip install --upgrade pip && \
    pip install pipenv && \
    pipenv install --dev --system --deploy --ignore-pipfile

ADD . /app

EXPOSE 5000

CMD flask run --host=0.0.0.0

A brief explanaton about it:

FROM python:3.7.1
The `FROM` define the base image that will be used to create my image. In this case, I use the base image of `pyton:3.7.1`.
LABEL Author="Vicente Marçal"
The `LABEL`'s are the way to put some information like the author's name, e-mail to contact and the version of the Dockerfile
RUN mkdir /app
The `RUN` is responsible to execute commands, in this case the `mkdir /app` that create a direcotyr called app into the root.
WORKDIR /app
The `WORKDIR` defines the work directory, in this case my `/app` created previosly in the `RUN` clause.
COPY Pip* /app/
The `COPY` is resposible to copy files from the host to the container, in this case with the `*` inform to Docker to copy all files start whit `Pip` to `/app/`. Pay attention: it is very important to finalize the path of the director with `/`
RUN pip install --upgrade pip && \
    pip install pipenv && \
    pipenv install --dev --system --deploy --ignore-pipfile
This `RUN` clause does the upgrade of `pip`, install `pipenv` and install the Flask App dependencies. To intall Flask dependencies was used `pipenv install` with some flags: `--dev` that indicates to install development dependencies, too; `--system` this is very important because in a Docker container we have an isolated environment yet and it is not necessary create a virtualenv, this flag informs `pipenv` exactly this; `--deploy` with `--ignore-pipfile` inform `pipenv` to use the `Pipfile.lock` to install all dependencies. In the `Pipfile.lock` we have all dependencies with your versions locked
ADD . /app
After installation of all dependencies with clause `ADD` added all files of the same directory where is Dockerfile on host to `/app` on the container. Obs: In the same directory there is a file called `.dockerignore` that like the `.gitignore` inform Docker to ignore the files and directories in its content.
EXPOSE 5000
This clause exposes the port 5000 of the container to the external world.
CMD flask run --host=0.0.0.0
Finally, the clause `CMD` execute the commands after build and up the container. In this case, the command is a `flask run --host=0.0.0.0` that execute the Flask server in the host 0.0.0.0 and servers our Flask application. Obs: Obviously, this is a small test. If you need to use this in production it is recommended to run a WSGI server like a uWSGI or gunicorn instead the Flask Server that is used only in development environment.

Some words about the use of COPY and ADD:

Although ADD and COPY are functionally similar, generally speaking, COPY is preferred. That’s because it’s more transparent than ADD. COPY only supports the basic copying of local files into the container, while ADD has some features (like local-only tar extraction and remote URL support) that are not immediately obvious. Consequently, the best use for ADD is local tar file auto-extraction into the image, as in ADD rootfs.tar.xz /.

That's all we need to Dockerize our Flask App. Now we need to build and run our Docker image.

To build we use this Docker CLI command:

docker build -t vm_docker_flask .

The command docker build will build our image, with the flag -t it put the tag vm_docker_flask in our image and, finally, the last part of the CLI is . (dot) that inform to Docker that the Dockerfile is in the current directory.

This command shows us this result in our terminal:

Sending build context to Docker daemon  29.18kB
Step 1/15 : FROM python:3.7.1
 ---> 1e80caffd59e
Step 2/15 : LABEL Author="Vicente Marçal"
 ---> Running in a7975e93672a
Removing intermediate container a7975e93672a
 ---> 448662ef63d8
Step 3/15 : LABEL E-mail="[email protected]"
 ---> Running in 75f6319066de
Removing intermediate container 75f6319066de
 ---> 0898192a03f7
Step 4/15 : LABEL version="0.0.1b"
 ---> Running in defb5aee2083
Removing intermediate container defb5aee2083
 ---> 02042247ded0
Step 5/15 : ENV PYTHONDONTWRITEBYTECODE 1
 ---> Running in 5e9124dfa05c
Removing intermediate container 5e9124dfa05c
 ---> c35cd3ecc42f
Step 6/15 : ENV FLASK_APP "backend/app.py"
 ---> Running in ea58e08644ad
Removing intermediate container ea58e08644ad
 ---> f2fb780d29fc
Step 7/15 : ENV FLASK_ENV "development"
 ---> Running in f72976410ba2
Removing intermediate container f72976410ba2
 ---> bb444664e3b0
Step 8/15 : ENV FLASK_DEBUG True
 ---> Running in e18257443538
Removing intermediate container e18257443538
 ---> a2bbca32f540
Step 9/15 : RUN mkdir /app
 ---> Running in ebbcc284fe40
Removing intermediate container ebbcc284fe40
 ---> 7fb8c7fac9f8
Step 10/15 : WORKDIR /app
 ---> Running in c76604d10578
Removing intermediate container c76604d10578
 ---> 074aa15fee4c
Step 11/15 : COPY Pip* /app/
 ---> 554f403d7b11
Step 12/15 : RUN pip install --upgrade pip && pip install pipenv && pipenv install --dev --system --deploy --ignore-pipfile
 ---> Running in 08faec42b7d5
Collecting pip
  Downloading https://files.pythonhosted.org/packages/d8/f3/413bab4ff08e1fc4828dfc59996d721917df8e8583ea85385d51125dceff/pip-19.0.3-py2.py3-none-any.whl (1.4MB)
Installing collected packages: pip
  Found existing installation: pip 18.1
    Uninstalling pip-18.1:
      Successfully uninstalled pip-18.1
Successfully installed pip-19.0.3
Collecting pipenv
  Downloading https://files.pythonhosted.org/packages/13/b4/3ffa55f77161cff9a5220f162670f7c5eb00df52e00939e203f601b0f579/pipenv-2018.11.26-py3-none-any.whl (5.2MB)
Requirement already satisfied: pip>=9.0.1 in /usr/local/lib/python3.7/site-packages (from pipenv) (19.0.3)
Collecting virtualenv-clone>=0.2.5 (from pipenv)
  Downloading https://files.pythonhosted.org/packages/e3/d9/d9c56deb483c4d3289a00b12046e41428be64e8236fa210111a1f57cc42d/virtualenv_clone-0.5.1-py2.py3-none-any.whl
Collecting virtualenv (from pipenv)
  Downloading https://files.pythonhosted.org/packages/33/5d/314c760d4204f64e4a968275182b7751bd5c3249094757b39ba987dcfb5a/virtualenv-16.4.3-py2.py3-none-any.whl (2.0MB)
Collecting certifi (from pipenv)
  Downloading https://files.pythonhosted.org/packages/9f/e0/accfc1b56b57e9750eba272e24c4dddeac86852c2bebd1236674d7887e8a/certifi-2018.11.29-py2.py3-none-any.whl (154kB)
Requirement already satisfied: setuptools>=36.2.1 in /usr/local/lib/python3.7/site-packages (from pipenv) (40.6.2)
Installing collected packages: virtualenv-clone, virtualenv, certifi, pipenv
Successfully installed certifi-2018.11.29 pipenv-2018.11.26 virtualenv-16.4.3 virtualenv-clone-0.5.1
Installing dependencies from Pipfile.lock (9a5a3a)…
Removing intermediate container 08faec42b7d5
 ---> 740ed1329305
Step 13/15 : ADD . /app
 ---> 3551608282e2
Step 14/15 : EXPOSE 5000
 ---> Running in 07b57fe6a5e7
Removing intermediate container 07b57fe6a5e7
 ---> 2e07658bbae8
Step 15/15 : CMD flask run --host=0.0.0.0
 ---> Running in bdf7404770f6
Removing intermediate container bdf7404770f6
 ---> cf7d3ee68072
Successfully built cf7d3ee68072
Successfully tagged vm_docker_flask:latest

After that, we need to run our container. The follow Docker CLI command makes the magic:

 docker run -d --name my_container_flask -p 5000:5000 vm_docker_flask

The command docker run runs our container, the flag -d inform to Docker that is running in background, the flag --name put the my_container_flask to our container, the flag -p bind the port 5000 of container to port 5000 of host and, at the end, is the name of our image built in the docker build command.

This command shows us this result in our terminal (or similar, because this command returns the container id):

33169f573b7bdf078ef8d55741ec037f2914aa78343ad96c8277854c3bcdf6b2

Now we can do our first test, see if our container is running, for that we execute Docker CLI command like that:

pythonprojects/docker_flask_app [ docker container ps        ] 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
880a5b59f2aa        vm_docker_flask     "/bin/sh -c 'flask r…"   15 minutes ago      Up 15 minutes       0.0.0.0:5000->5000/tcp   my_container_flask

This Docker CLI command shows us if our which containers are running and their info like Container ID, Image, Command and so on. In our case only my_container_flask how you can see above.

Another test that we can do is a simple HTTP request. I use httpie application in this test. See the terminal result:

pythonprojects/docker_flask_app [ http 0.0.0.0:5000         ]
HTTP/1.0 200 OK
Content-Length: 11
Content-Type: text/html; charset=utf-8
Date: Tue, 05 Mar 2019 18:17:24 GMT
Server: Werkzeug/0.14.1 Python/3.7.1

Hello World

Now the most important test. We execute the pytest into our container to run tests against our Flask application. The test is just simple only assert if the status code returned by the Flask App is 200 Ok!

To do it we execute the follow Docker CLI command:

 docker exec my_container_flask pytest 

This docker CLI command runs pytest against my_container_flask and if all it is ok we have this terminal result:

=========================== test session starts ============================
platform linux -- Python 3.7.1, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /app, inifile: pytest.ini
plugins: sugar-0.9.2, flask-0.14.0
collected 1 item

backend/blueprints/core/tests/test_bp_core.py .                      [100%]

========================= 1 passed in 0.06 seconds =========================

That's all folks!!

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank for reading !