85 conf: typing.Mapping[str, typing.Any] = {},
86 uid: typing.Optional[str] =
None,
88 """Define devices and where to serve them.
90 A device definition for use in deviceserver config files.
93 cls: :class:`Device` class of device to serve or function that
94 returns a map of `Device` instances to wanted Pyro ID.
95 The device class will be constructed, or the function will
96 be called, with the arguments in ``conf``.
97 host: hostname or ip address serving the devices.
98 port: port number used to serve the devices.
99 conf: keyword arguments for ``cls``. The device or function
100 are effectively constructed or called with `cls(**conf)`.
101 uid: used to identify "floating" devices (see documentation
102 for :class:`FloatingDeviceMixin`). This must be specified
103 if ``cls`` is a floating device.
107 .. code-block:: python
109 def construct_devices() -> typing.Dict[str, Device]:
110 camera = Camera(some, arguments)
111 # ... any other configuration that might be wanted
112 return {'RedCamera': camera}
115 # passing a function that returns devices
116 device(construct_devices, '127.0.0.1', 8000),
117 # passing a Device class
118 device(Camera, '127.0.0.1', 8001,
119 conf={'kwarg1': some, 'kwarg2': arguments})
123 if not callable(cls):
124 raise TypeError(
"cls must be a callable")
125 elif isinstance(cls, type):
126 if issubclass(cls, FloatingDeviceMixin)
and uid
is None:
127 raise TypeError(
"uid must be specified for floating devices")
128 elif not issubclass(cls, FloatingDeviceMixin)
and uid
is not None:
129 raise TypeError(
"uid must not be given for non floating devices")
130 return dict(cls=cls, host=host, port=int(port), uid=uid, conf=conf)
398 devices = copy.deepcopy(devices)
400 root_logger = logging.getLogger()
402 log_handler = RotatingFileHandler(
"__MAIN__.log")
404 root_logger.addHandler(log_handler)
409 if exit_event
is None:
410 exit_event = multiprocessing.Event()
421 parent = multiprocessing.current_process()
423 def term_func(sig, frame):
424 """Terminate subprocesses cleanly."""
425 if parent == multiprocessing.current_process():
426 _logger.debug(
"Shutting down all servers.")
430 keep_alive_thread.join()
431 for this_server
in servers:
435 if sys.platform !=
"win32":
436 signal.signal(signal.SIGTERM, term_func)
437 signal.signal(signal.SIGINT, term_func)
442 by_class[dev[
"cls"]] = by_class.get(dev[
"cls"], []) + [dev]
445 _logger.warning(
"No valid devices specified. Maybe an empty list?")
447 for cls, devs
in by_class.items():
458 if isinstance(cls, type)
and issubclass(cls, FloatingDeviceMixin):
464 uid_to_host[uid] = dev[
"host"]
465 uid_to_port[uid] = dev[
"port"]
467 dev[
"conf"][
"index"] = count
477 exit_event=exit_event,
486 """Keep DeviceServers alive."""
487 while not exit_event.is_set():
493 "DeviceServer Failure. Process %s is dead with"
494 " exitcode %s. Restarting...",
499 servers.append(s.clone())
504 _logger.error(
"... could not join PID %s.", s.pid)
510 "... DeviceServer with PID %s restarted"
518 _logger.info(
"No servers running. Exiting.")
523 except (KeyboardInterrupt, IOError):
526 keep_alive_thread = Thread(target=keep_alive)
527 keep_alive_thread.start()
529 _logger.info(
"Device Server started. Press Ctrl+C to exit.")
530 while not exit_event.is_set():
533 except (KeyboardInterrupt, IOError):
534 _logger.debug(
"KeyboardInterrupt or IOError")
537 _logger.debug(
"Shutting down servers ...")
544 _logger.info(
" ... No more servers running.")
545 _logger.debug(
"Joining threads ...")
546 keep_alive_thread.join()
547 _logger.debug(
"... Threads joined. Exiting.")
552 parser = argparse.ArgumentParser(prog=
"device-server")
558 choices=[
"debug",
"info",
"warning",
"error",
"critical"],
559 help=
"Set logging level",
565 metavar=
"CONFIG-FILEPATH",
566 help=
"Path to the configuration file",
568 parsed = parser.parse_args(args)
570 config_fpath=parsed.config_fpath,
571 logging_level=getattr(logging, parsed.logging_level.upper()),
594def main(argv: typing.Sequence[str]) -> int:
597 root_logger = logging.getLogger()
598 root_logger.setLevel(options.logging_level)
600 stderr_handler = StreamHandler(sys.stderr)
602 root_logger.addHandler(stderr_handler)
604 root_logger.addFilter(
Filter())
__init__(self, device_def, DeviceServerOptions options, typing.Mapping[str, str] id_to_host, typing.Mapping[str, int] id_to_port, typing.Optional[multiprocessing.Event] exit_event=None)