16

I want to debug a small Flask server inside Jupyter Notebook for demo.

I created a virtualenv on the latest Ubuntu and Python 2 (on Mac with Python 3 this error occurs as well), pip install flask jupyter.

However, when I create a cell with “Hello World” script, it does not run inside notebook.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(debug=True,port=1234)
File "/home/***/test/local/lib/python2.7/site-packages/ipykernel/kernelapp.py",
line 177, in _bind_socket 
    s.bind("tcp://%s:%i" % (self.ip, port))   
File "zmq/backend/cython/socket.pyx", line 495, in
    zmq.backend.cython.socket.Socket.bind
   (zmq/backend/cython/socket.c:5653)   
File "zmq/backend/cython/checkrc.pxd", line 25, in
    zmq.backend.cython.checkrc._check_rc
   (zmq/backend/cython/socket.c:10014)
       raise ZMQError(errno) ZMQError: Address already in use

NB – I change the port number after each time it fails.

Sure, it runs as a standalone script.

Update:

Without debug=True it works OK.

0

4 Answers 4

30

I installed Jupyter and Flask and your original code works.


The flask.Flask object is a WSGI application, not a server. Flask uses Werkzeug's development server as a WSGI server when you call python -m flask run in your shell. It creates a new WSGI server and then passes your app as paremeter to werkzeug.serving.run_simple. Maybe you can try doing that manually:

from werkzeug.wrappers import Request, Response
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 9000, app)

Flask.run() calls run_simple() internally, so there should be no difference here.

Sign up to request clarification or add additional context in comments.

5 Comments

How do you run Flask from inside a Jupyter notebook? Please link to actual jupyter notebook and any related code. Thank you.
if that's being run on the Jupyter server, which is being run on port 8888 locally, how do you reach it? localhost:9000 gives me nothing. Using the jupyter terminal and # curl localhost:9000 gives me the response. I guess I need to set up an ssh tunnel to the Jupyter server
using 0.0.0.0 instead of localhost gives more options but none of those seem to work either
The first import in your code - from werkzeug.wrappers import Request, Response - is not used and may be omitted.
As OP in his question, in the Update: part of it wrote, without debug=True his code work. So it seems that the code in your answer is not a solution for OP's question.
4

The trick is to run the Flask server in a separate thread. This code allows registering data providers. The key features are

  • Find a free port for the server. If you run multiple instances of the server in different notebooks they would compete for the same port.

  • The register_data function returns the URL of the server so you can use it for whatever you need.

  • The server is started on-demand (when the first data provider is registered)

  • Note: I added the @cross_origin() decorator from the flask-cors package. Else you cannot call the API form within the notebook.

  • Note: there is no way to stop the server in this code...

  • Note: The code uses typing and python 3.

  • Note: There is no good error handling at the moment

import socket
import threading
import uuid
from typing import Any, Callable, cast, Optional

from flask import Flask, abort, jsonify
from flask_cors import cross_origin
from werkzeug.serving import run_simple

app = Flask('DataServer')


@app.route('/data/<id>')
@cross_origin()
def data(id: str) -> Any:
    func = _data.get(id)
    if not func:
        abort(400)
    return jsonify(func())


_data = {}

_port: int = 0


def register_data(f: Callable[[], Any], id: Optional[str] = None) -> str:
    """Sets a callback for data and returns a URL"""
    _start_sever()
    id = id or str(uuid.uuid4())
    _data[id] = f
    return f'http://localhost:{_port}/data/{id}'


def _init_port() -> int:
    """Creates a random free port."""
    # see https://stackoverflow.com/a/5089963/2297345
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 0))

    port = sock.getsockname()[1]
    sock.close()
    return cast(int, port)


def _start_sever() -> None:
    """Starts a flask server in the background."""
    global _port
    if _port:
        return
    _port = _init_port()
    thread = threading.Thread(target=lambda: run_simple('localhost', _port, app))
    thread.start()

3 Comments

I'm a bit confused if this would be correct approach to use Jupiter Notebook with a Flask App. I started a Flask server and a notebook, but I keep getting the RuntimeError: No application found when trying to query a database using Flask-SQLAlchemy. Would your answer apply? Thanks.
This is a way to start a flask server from within a notebook.
is this just for running curl's to from within the notebook ? I'm not able to get to the localhost url in a browser
0

Although this question was asked long ago, I come up with another suggestion:

The following code is adapted from how PyCharm starts a Flask console.

import sys
from flask.cli import ScriptInfo
app = None
locals().update(ScriptInfo(create_app=None).load_app().make_shell_context())
print("Python %s on %s\nApp: %s [%s]\nInstance: %s" % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))

Now you can access app and use everything described in the Flask docs on working with the shell

Comments

0

Adding on to Peter_B's answer, if you add the following lines you can test most of your app functionality:

ctx = app.app_context().push()
ctx = app.test_request_context().push()

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.