How to properly unplug a socket when application closes

I am running locally a bokeh app on ipython. When the application terminates, the server socket is not released, thus on the next run, the socket cannot be re-established.

Kindly educate what is the correct way to release a socket? Bokeh version is 3.4.1.

Below is a minimum reproducible example (saved as mre.py) and an example ipython session.

import sys
from bokeh.server.server import Server

def app_close(session_context):
    print("Test done")
    sys.exit()

def app_init(doc):
    doc.title = "Close tab to terminate"
    doc.on_session_destroyed(app_close)
    return doc

server = Server({"/": app_init},
                 check_unused_sessions_milliseconds=500,
                 unused_session_lifetime_milliseconds=500,                
                 )

if server._started == False:
    server.start()
server.io_loop.add_callback(server.show, "/")  

try:
    server.io_loop.start()
except:
    pass

An ipython session. Closing the browser tab and running the app second time yields an error.

C:\WPy64-31241\scripts>ipython
Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun  6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.25.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: run "c:/Users/Heke/fartdetector/mre.py"
Test done

In [2]: run "c:/Users/Heke/fartdetector/mre.py"
OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted

@heke I would suggest calling server.stop() before exiting, so that the Tornado app and HTTP server are shut down in an orderly manner. Otherwise, the address and port will be unusable for however long it takes the OS to notice the resource can be reclaimed.

Hi @Bryan ,

Many thanks for your great support and amazing Bokeh.

Yeah, I was assuming that the.stop() would be required, but I am wondering how to do that. I could not find the .stop() method through the parameter that is passed to the on_session_destroyed callback. Do I need to store the server instance to a global variable (does not sound pythonic or bokehic)?

Is there a reason why the server is not stopped automatically when the session is terminated?

Cheers, heke

Well, use of globals is ubiquitous in Python. Every time a builtin like open or a module imported at the top of the module is used, that’s a global. It’s also fairly common in my experience to set things like this at the top-level (e.g. Flask apps are “global”). It’s the simplest option, and probably what I would do given the information so far. But if you don’t want to do that, some ideas would be to bake the server object into the callback using functools.partial, or else to wrap everything up in a class and use an instance method for the callback so that it can access the server via self._server or whatever.

Is there a reason why the server is not stopped automatically when the session is terminated?

The same reason any web server does not shut down after a single view. The actual intended purpose of Server is to be a long-running process that asynchronously handles new sessions forever. So that’s another option: don’t shut things down at all. If your use case involves multiple sessions, let a single Server instance handle all of them.

Hi @Bryan ,

Many thanks for your advice.

I am happy with that. Just being afraid of functional programming purists…
I just replaced sys.exit() with server.stop() in the above example and things started to roll. No need to restart kernel anymore. Super.

It seems like servers are not my thing. Thanks for the clarification.

Well, could do that, but then just wondering what is the correct procedure to close the socket. Anyway, now things work, thus I row off to enjoy Bokeh programming. Thanks again.
Cheers, heke

1 Like