Skip to content

Commit

Permalink
Added multi option to Server class.
Browse files Browse the repository at this point in the history
Activating this option tells a server instance to accept multiple
client connections. Not activating it, which is the default, maintains
the established behavior, where the server would shut down as soon as
the first connected client disconnects itself.
  • Loading branch information
john-hen committed Oct 9, 2021
1 parent ece13c7 commit 99cdf27
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 8 deletions.
2 changes: 1 addition & 1 deletion deploy/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pathlib import Path

tests = ['meta', 'config', 'discovery', 'server', 'session', 'standalone',
'client', 'node', 'model', 'exit']
'client', 'multi', 'node', 'model', 'exit']

here = Path(__file__).resolve().parent
root = here.parent
Expand Down
2 changes: 1 addition & 1 deletion deploy/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import sys

tests = ['meta', 'config', 'discovery', 'server', 'session', 'standalone',
'client', 'node', 'model', 'exit']
'client', 'multi', 'node', 'model', 'exit']

folder = Path(__file__).parent.parent / 'tests'
python = sys.executable
Expand Down
4 changes: 2 additions & 2 deletions mph/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ def disconnect(self):
"""
Disconnects the client from the server.
Note that the server, unless started with the command-line option
`-multi on`, will shut down when the client disconnects.
Note that the server, unless started with the option `multi`
set to `'on'`, will shut down as soon as the client disconnects.
"""
if self.port:
log.debug('Disconnecting from server.')
Expand Down
21 changes: 17 additions & 4 deletions mph/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from subprocess import TimeoutExpired # communication time-out
from re import match as regex # regular expression
from time import perf_counter as now # wall-clock time
from sys import version_info # Python version
from logging import getLogger # event logging

########################################
Expand Down Expand Up @@ -61,12 +60,18 @@ class Server:
server will then select a random free port, which will almost always
avoid collisions.
If `multi` is `False` or `'off'` or `None` (the default), then
the server will shut down as soon as the first connected clients
disconnects itself. If it is `True` or `'on'`, the server process
will stay alive and accept multiple client connections.
A `timeout` can be set for the server start-up. The default is 60
seconds. `TimeoutError` is raised if the server failed to start
within that period.
"""

def __init__(self, cores=None, version=None, port=None, timeout=60):
def __init__(self, cores=None, version=None, port=None,
multi=None, timeout=60):

# Start Comsol server as an external process.
backend = discovery.backend(version)
Expand All @@ -81,9 +86,17 @@ def __init__(self, cores=None, version=None, port=None, timeout=60):
log.info(f'Server restricted to {cores} processor {noun}.')
if port is not None:
arguments += ['-port', str(port)]
if multi:
if multi in (True, 'on'):
arguments += ['-multi', 'on']
elif multi in (False, 'off'):
arguments += ['-multi', 'off']
else:
error = f'Invalid value "{multi}" for option "multi".'
log.error(error)
raise ValueError(error)
command = server + arguments
if version_info < (3, 8):
command[0] = str(command[0])
command[0] = str(command[0]) # Required for Python 3.6 and 3.7.
process = start(command, stdin=PIPE, stdout=PIPE, errors='ignore')

# Remember the requested port (if any).
Expand Down
74 changes: 74 additions & 0 deletions tests/test_multi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Tests multiple client connections to the same server."""

########################################
# Dependencies #
########################################
import parent # noqa F401
import mph
from sys import argv
from time import sleep
from pytest import raises
import logging


########################################
# Fixtures #
########################################
server = None


def teardown_module():
if server and server.running():
try:
server.stop()
except Exception:
pass


########################################
# Tests #
########################################

def test_multi():
global server
with raises(ValueError):
server = mph.Server(multi='invalid')
server = mph.Server(cores=1, multi=False)
assert server.running()
assert server.port
client = mph.Client(port=server.port)
assert not client.standalone
assert client.version == server.version
assert client.cores == server.cores
assert client.port == server.port
client.disconnect()
sleep(3)
assert not server.running()
server = mph.Server(cores=1, multi=True)
client.connect(port=server.port)
assert client.version == server.version
assert client.cores == server.cores
assert client.port == server.port
client.disconnect()
sleep(3)
assert server.running()
server.stop()


########################################
# Main #
########################################

if __name__ == '__main__':

arguments = argv[1:]
if 'log' in arguments:
logging.basicConfig(
level = logging.DEBUG if 'debug' in arguments else logging.INFO,
format = '[%(asctime)s.%(msecs)03d] %(message)s',
datefmt = '%H:%M:%S')

try:
test_multi()
finally:
teardown_module()

0 comments on commit 99cdf27

Please sign in to comment.