Beispiel #1
0
    async def start_scheduler(cls: Any, obj: Any,
                              context: Dict) -> Optional[Callable]:
        if context.get("_schedule_loop_started"):
            return None
        context["_schedule_loop_started"] = True

        set_execution_context({
            "scheduled_functions_enabled": True,
            "scheduled_functions_current_tasks": 0,
            "scheduled_functions_total_tasks": 0,
        })

        async def _schedule() -> None:
            cls.close_waiter = asyncio.Future()

            for interval, timestamp, timezone, immediately, func, handler in context.get(
                    "_schedule_scheduled_functions", []):
                cls.next_call_at(
                    time.time(), interval, timestamp, cls.get_timezone(
                        timezone))  # test provided interval/timestamp on init

            for interval, timestamp, timezone, immediately, func, handler in context.get(
                    "_schedule_scheduled_functions", []):
                await cls.start_schedule_loop(cls, obj, context, handler, func,
                                              interval, timestamp, timezone,
                                              immediately)

        return _schedule
Beispiel #2
0
    def run_until_complete(
        cls,
        service_files: Union[List, set],
        configuration: Optional[Dict] = None,
        watcher: Any = None,
    ) -> None:
        def sigintHandler(*args: Any) -> None:
            sys.stdout.write("\b\b\r")
            sys.stdout.flush()
            logging.getLogger("system").warning(
                "Received <ctrl+c> interrupt [SIGINT]")
            cls.restart_services = False

        def sigtermHandler(*args: Any) -> None:
            logging.getLogger("system").warning(
                "Received termination signal [SIGTERM]")
            cls.restart_services = False

        logging.basicConfig(level=logging.DEBUG)

        loop = asyncio.get_event_loop()
        if loop and loop.is_closed():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)

        for signame in ("SIGINT", "SIGTERM"):
            loop.add_signal_handler(getattr(signal, signame),
                                    cls.stop_services)

        signal.siginterrupt(signal.SIGTERM, False)
        signal.siginterrupt(signal.SIGUSR1, False)
        signal.signal(signal.SIGINT, sigintHandler)
        signal.signal(signal.SIGTERM, sigtermHandler)

        watcher_future = None
        if watcher:

            async def _watcher_restart(
                    updated_files: Union[List, set]) -> None:
                cls.restart_services = True

                for file in service_files:
                    try:
                        ServiceImporter.import_service_file(file)
                    except (SyntaxError, IndentationError) as e:
                        logging.getLogger("exception").exception(
                            "Uncaught exception: {}".format(str(e)))
                        logging.getLogger("watcher.restart").warning(
                            "Service cannot restart due to errors")
                        cls.restart_services = False
                        return

                pre_import_current_modules = [m for m in sys.modules.keys()]
                cwd = os.getcwd()
                for file in updated_files:
                    if file.lower().endswith(".py"):
                        module_name = file[:-3].replace("/", ".")
                        module_name_full_path = "{}/{}".format(
                            os.path.realpath(cwd),
                            file)[:-3].replace("/", ".")
                        try:
                            for m in pre_import_current_modules:
                                if m == module_name or (
                                        len(m) > len(file)
                                        and module_name_full_path.endswith(m)):
                                    ServiceImporter.import_module(file)
                        except (SyntaxError, IndentationError) as e:
                            logging.getLogger("exception").exception(
                                "Uncaught exception: {}".format(str(e)))
                            logging.getLogger("watcher.restart").warning(
                                "Service cannot restart due to errors")
                            cls.restart_services = False
                            return

                logging.getLogger("watcher.restart").warning(
                    "Restarting services")
                cls.stop_services()

            watcher_future = loop.run_until_complete(
                watcher.watch(loop=loop, callback_func=_watcher_restart))

        cls.restart_services = True
        init_modules = [m for m in sys.modules.keys()]

        restarting = False
        while cls.restart_services:
            init_timestamp = time.time()
            init_timestamp_str = datetime.datetime.utcfromtimestamp(
                init_timestamp).isoformat() + "Z"

            process_id = os.getpid()

            event_loop_alias = ""
            event_loop_version = ""
            try:
                if "uvloop." in str(loop.__class__):
                    event_loop_alias = "uvloop"
                    import uvloop  # noqa  # isort:skip

                    event_loop_version = str(uvloop.__version__)
                elif "asyncio." in str(loop.__class__):
                    event_loop_alias = "asyncio"
                else:
                    event_loop_alias = "{}.{}".format(
                        loop.__class__.__module__, loop.__class__.__name__)
            except Exception:
                event_loop_alias = str(loop)

            clear_services()
            clear_execution_context()
            set_execution_context({
                "tomodachi_version": tomodachi.__version__,
                "python_version": platform.python_version(),
                "system_platform": platform.system(),
                "process_id": process_id,
                "init_timestamp": init_timestamp_str,
                "event_loop": event_loop_alias,
            })

            if event_loop_alias == "uvloop" and event_loop_version:
                set_execution_context({
                    "uvloop_version": event_loop_version,
                })

            if watcher:
                tz: Any = None
                utc_tz: Any = None

                try:
                    import pytz  # noqa  # isort:skip
                    import tzlocal  # noqa  # isort:skip

                    utc_tz = pytz.UTC
                    try:
                        tz = tzlocal.get_localzone()
                        if not tz:
                            tz = pytz.UTC
                    except Exception:
                        tz = pytz.UTC
                except Exception:
                    pass

                init_local_datetime = (
                    datetime.datetime.fromtimestamp(init_timestamp)
                    if tz and tz is not utc_tz and str(tz) != "UTC" else
                    datetime.datetime.utcfromtimestamp(init_timestamp))

                print("---")
                print("Starting tomodachi services (pid: {}) ...".format(
                    process_id))
                for file in service_files:
                    print("* {}".format(file))

                print()
                print("Current version: tomodachi {} on Python {}".format(
                    tomodachi.__version__, platform.python_version()))
                print("Event loop implementation: {}{}".format(
                    event_loop_alias, " {}".format(event_loop_version)
                    if event_loop_version else ""))
                if tz:
                    print("Local time: {} {}".format(
                        init_local_datetime.strftime(
                            "%B %d, %Y - %H:%M:%S,%f"), str(tz)))
                print("Timestamp in UTC: {}".format(init_timestamp_str))
                print()
                print(
                    "File watcher is active - code changes will automatically restart services"
                )
                print("Quit running services with <ctrl+c>")
                print()

            tomodachi.SERVICE_EXIT_CODE = tomodachi.DEFAULT_SERVICE_EXIT_CODE

            cls._close_waiter = asyncio.Future()
            cls._stopped_waiter = asyncio.Future()
            cls.restart_services = False

            try:
                cls.services = set([
                    ServiceContainer(ServiceImporter.import_service_file(file),
                                     configuration) for file in service_files
                ])
                result = loop.run_until_complete(
                    asyncio.wait([
                        asyncio.ensure_future(service.run_until_complete())
                        for service in cls.services
                    ]))
                exception = [
                    v.exception()
                    for v in [value for value in result if value][0]
                    if v.exception()
                ]
                if exception:
                    raise cast(Exception, exception[0])
            except tomodachi.importer.ServicePackageError:
                pass
            except Exception as e:
                logging.getLogger("exception").exception(
                    "Uncaught exception: {}".format(str(e)))

                if isinstance(e, ModuleNotFoundError):  # pragma: no cover
                    missing_module_name = str(getattr(e, "name", None) or "")
                    if missing_module_name:
                        color = ""
                        color_reset = ""
                        try:
                            import colorama  # noqa  # isort:skip

                            color = colorama.Fore.WHITE + colorama.Back.RED
                            color_reset = colorama.Style.RESET_ALL
                        except Exception:
                            pass

                        print("")
                        print(
                            "{}[fatal error] The '{}' package is missing or cannot be imported.{}"
                            .format(color, missing_module_name, color_reset))
                        print("")

                if restarting:
                    logging.getLogger("watcher.restart").warning(
                        "Service cannot restart due to errors")
                    logging.getLogger("watcher.restart").warning(
                        "Trying again in 1.5 seconds")
                    loop.run_until_complete(asyncio.wait([asyncio.sleep(1.5)]))
                    if cls._close_waiter and not cls._close_waiter.done():
                        cls.restart_services = True
                    else:
                        for signame in ("SIGINT", "SIGTERM"):
                            loop.remove_signal_handler(getattr(
                                signal, signame))
                else:
                    for signame in ("SIGINT", "SIGTERM"):
                        loop.remove_signal_handler(getattr(signal, signame))

            current_modules = [m for m in sys.modules.keys()]
            for m in current_modules:
                if m not in init_modules and m not in SAFE_MODULES:
                    del sys.modules[m]

            importlib.reload(tomodachi.container)
            importlib.reload(tomodachi.invoker)
            importlib.reload(tomodachi.invoker.base)
            importlib.reload(tomodachi.importer)

            restarting = True

        if watcher:
            if watcher_future and not watcher_future.done():
                try:
                    watcher_future.set_result(None)
                except RuntimeError:  # pragma: no cover
                    watcher_future.cancel()
                if not watcher_future.done():  # pragma: no cover
                    try:
                        loop.run_until_complete(watcher_future)
                    except (Exception, CancelledError, _CancelledError):
                        pass
Beispiel #3
0
    async def subscribe(cls, obj: Any, context: Dict) -> Optional[Callable]:
        if context.get("_amqp_subscribed"):
            return None
        context["_amqp_subscribed"] = True

        set_execution_context({
            "amqp_enabled": True,
            "amqp_current_tasks": 0,
            "amqp_total_tasks": 0,
            "aioamqp_version": aioamqp.__version__,
        })

        cls.channel = None
        channel = await cls.connect(obj, context)
        queue_prefetch_count = get_item_by_path(
            context, "options.amqp.qos.queue_prefetch_count", 100)
        global_prefetch_count = get_item_by_path(
            context, "options.amqp.qos.global_prefetch_count", 400)
        await channel.basic_qos(prefetch_count=queue_prefetch_count,
                                prefetch_size=0,
                                connection_global=False)
        await channel.basic_qos(prefetch_count=global_prefetch_count,
                                prefetch_size=0,
                                connection_global=True)

        async def _subscribe() -> None:
            async def declare_queue(
                routing_key: str,
                func: Callable,
                exchange_name: str = "",
                exchange_type: str = "topic",
                queue_name: Optional[str] = None,
                passive: bool = False,
                durable: bool = True,
                exclusive: bool = False,
                auto_delete: bool = False,
                competing_consumer: Optional[bool] = None,
            ) -> Optional[str]:
                try:
                    if exchange_name and exchange_name != "amq.topic":
                        await channel.exchange_declare(
                            exchange_name=exchange_name,
                            type_name=exchange_type,
                            passive=False,
                            durable=True,
                            auto_delete=False,
                        )
                except aioamqp.exceptions.ChannelClosed as e:
                    error_message = e.args[1]
                    if e.args[0] == 403 and exchange_name.startswith("amq."):
                        logging.getLogger("transport.amqp").warning(
                            'Unable to declare exchange [amqp] "{}", starts with reserved "amq." ({})'
                            .format(exchange_name, error_message))
                        raise
                    elif e.args[0] == 507 or e.args[0] == 406:
                        logging.getLogger("transport.amqp").warning(
                            'Unable to change type of existing exchange [amqp] "{}" ({})'
                            .format(exchange_name, error_message))
                        raise
                    else:
                        logging.getLogger("transport.amqp").warning(
                            'Unable to declare exchange [amqp] "{}" ({})'.
                            format(exchange_name, error_message))
                        raise

                if queue_name and competing_consumer is None:
                    competing_consumer = True

                _uuid = obj.uuid
                max_consumers = 1 if not competing_consumer else None

                if queue_name is None:
                    queue_name = cls.get_queue_name(
                        cls.encode_routing_key(routing_key), func.__name__,
                        _uuid, competing_consumer, context)
                else:
                    queue_name = cls.prefix_queue_name(queue_name, context)

                amqp_arguments = {}
                ttl = context.get("options",
                                  {}).get("amqp", {}).get("queue_ttl", 86400)
                if ttl:
                    amqp_arguments["x-expires"] = int(ttl * 1000)

                try:
                    data = await channel.queue_declare(
                        queue_name,
                        passive=passive,
                        durable=durable,
                        exclusive=exclusive,
                        auto_delete=auto_delete,
                        arguments=amqp_arguments,
                    )
                    if max_consumers is not None and data.get(
                            "consumer_count", 0) >= max_consumers:
                        logging.getLogger("transport.amqp").warning(
                            'Max consumers ({}) for queue [amqp] "{}" has been reached'
                            .format(max_consumers, queue_name))
                        raise AmqpTooManyConsumersException(
                            "Max consumers for this queue has been reached")
                except aioamqp.exceptions.ChannelClosed as e:
                    if e.args[0] == 405:
                        raise AmqpExclusiveQueueLockedException(str(e)) from e
                    raise AmqpException(str(e)) from e

                await channel.queue_bind(
                    queue_name,
                    exchange_name or "amq.topic",
                    cls.encode_routing_key(
                        cls.get_routing_key(routing_key, context)),
                )

                return queue_name

            def callback(routing_key: str, handler: Callable) -> Callable:
                async def _callback(self: Any, body: bytes, envelope: Any,
                                    properties: Any) -> None:
                    # await channel.basic_reject(delivery_tag, requeue=True)
                    await asyncio.shield(
                        handler(body.decode(), envelope.delivery_tag,
                                routing_key))

                return _callback

            for routing_key, exchange_name, competing, queue_name, func, handler in context.get(
                    "_amqp_subscribers", []):
                queue_name = await declare_queue(routing_key,
                                                 func,
                                                 exchange_name=exchange_name,
                                                 competing_consumer=competing,
                                                 queue_name=queue_name)
                await channel.basic_consume(callback(routing_key, handler),
                                            queue_name=queue_name)

        return _subscribe
Beispiel #4
0
    def run_until_complete(
        cls,
        service_files: Union[List, set],
        configuration: Optional[Dict] = None,
        watcher: Any = None,
    ) -> None:
        def stop_services() -> None:
            asyncio.ensure_future(_stop_services())

        async def _stop_services() -> None:
            if cls._close_waiter and not cls._close_waiter.done():
                cls._close_waiter.set_result(None)
                for service in cls.services:
                    try:
                        service.stop_service()
                    except Exception:
                        pass
                if cls._stopped_waiter:
                    cls._stopped_waiter.set_result(None)
            if cls._stopped_waiter:
                await cls._stopped_waiter

        def sigintHandler(*args: Any) -> None:
            sys.stdout.write("\b\b\r")
            sys.stdout.flush()
            logging.getLogger("system").warning(
                "Received <ctrl+c> interrupt [SIGINT]")
            cls.restart_services = False

        def sigtermHandler(*args: Any) -> None:
            logging.getLogger("system").warning(
                "Received termination signal [SIGTERM]")
            cls.restart_services = False

        logging.basicConfig(level=logging.DEBUG)

        loop = asyncio.get_event_loop()
        if loop and loop.is_closed():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)

        for signame in ("SIGINT", "SIGTERM"):
            loop.add_signal_handler(getattr(signal, signame), stop_services)

        signal.siginterrupt(signal.SIGTERM, False)
        signal.siginterrupt(signal.SIGUSR1, False)
        signal.signal(signal.SIGINT, sigintHandler)
        signal.signal(signal.SIGTERM, sigtermHandler)

        if watcher:

            async def _watcher_restart(
                    updated_files: Union[List, set]) -> None:
                cls.restart_services = True

                for file in service_files:
                    try:
                        ServiceImporter.import_service_file(file)
                    except (SyntaxError, IndentationError) as e:
                        logging.getLogger("exception").exception(
                            "Uncaught exception: {}".format(str(e)))
                        logging.getLogger("watcher.restart").warning(
                            "Service cannot restart due to errors")
                        cls.restart_services = False
                        return

                pre_import_current_modules = [m for m in sys.modules.keys()]
                cwd = os.getcwd()
                for file in updated_files:
                    if file.lower().endswith(".py"):
                        module_name = file[:-3].replace("/", ".")
                        module_name_full_path = "{}/{}".format(
                            os.path.realpath(cwd),
                            file)[:-3].replace("/", ".")
                        try:
                            for m in pre_import_current_modules:
                                if m == module_name or (
                                        len(m) > len(file)
                                        and module_name_full_path.endswith(m)):
                                    ServiceImporter.import_module(file)
                        except (SyntaxError, IndentationError) as e:
                            logging.getLogger("exception").exception(
                                "Uncaught exception: {}".format(str(e)))
                            logging.getLogger("watcher.restart").warning(
                                "Service cannot restart due to errors")
                            cls.restart_services = False
                            return

                logging.getLogger("watcher.restart").warning(
                    "Restarting services")
                stop_services()

            watcher_future = loop.run_until_complete(
                watcher.watch(loop=loop, callback_func=_watcher_restart))

        cls.restart_services = True
        init_modules = [m for m in sys.modules.keys()]
        safe_modules = [
            "__future__",
            "__main__",
            "_abc",
            "_asyncio",
            "_bisect",
            "_blake2",
            "_bootlocale",
            "_bz2",
            "_cares",
            "_cares.lib",
            "_cffi_backend",
            "_codecs",
            "_collections",
            "_collections_abc",
            "_compat_pickle",
            "_compression",
            "_contextvars",
            "_ctypes",
            "_cython_0_29_21",
            "_datetime",
            "_decimal",
            "_elementtree",
            "_frozen_importlib",
            "_frozen_importlib_external",
            "_functools",
            "_hashlib",
            "_heapq",
            "_imp",
            "_io",
            "_json",
            "_locale",
            "_lzma",
            "_markupbase",
            "_opcode",
            "_operator",
            "_pickle",
            "_posixsubprocess",
            "_queue",
            "_random",
            "_sha3",
            "_sha512",
            "_signal",
            "_sitebuiltins",
            "_socket",
            "_sre",
            "_ssl",
            "_stat",
            "_string",
            "_struct",
            "_thread",
            "_uuid",
            "_warnings",
            "_weakref",
            "_weakrefset",
            "abc",
            "aioamqp",
            "aioamqp.channel",
            "aioamqp.constants",
            "aioamqp.envelope",
            "aioamqp.exceptions",
            "aioamqp.frame",
            "aioamqp.properties",
            "aioamqp.protocol",
            "aioamqp.version",
            "aiobotocore",
            "aiobotocore._endpoint_helpers",
            "aiobotocore.args",
            "aiobotocore.client",
            "aiobotocore.config",
            "aiobotocore.credentials",
            "aiobotocore.endpoint",
            "aiobotocore.eventstream",
            "aiobotocore.hooks",
            "aiobotocore.paginate",
            "aiobotocore.parsers",
            "aiobotocore.response",
            "aiobotocore.session",
            "aiobotocore.signers",
            "aiobotocore.utils",
            "aiobotocore.waiter",
            "aiodns",
            "aiodns.error",
            "aiohttp",
            "aiohttp._frozenlist",
            "aiohttp._helpers",
            "aiohttp._http_parser",
            "aiohttp._http_writer",
            "aiohttp._websocket",
            "aiohttp.abc",
            "aiohttp.base_protocol",
            "aiohttp.client",
            "aiohttp.client_exceptions",
            "aiohttp.client_proto",
            "aiohttp.client_reqrep",
            "aiohttp.client_ws",
            "aiohttp.connector",
            "aiohttp.cookiejar",
            "aiohttp.formdata",
            "aiohttp.frozenlist",
            "aiohttp.hdrs",
            "aiohttp.helpers",
            "aiohttp.http",
            "aiohttp.http_exceptions",
            "aiohttp.http_parser",
            "aiohttp.http_websocket",
            "aiohttp.http_writer",
            "aiohttp.locks",
            "aiohttp.log",
            "aiohttp.multipart",
            "aiohttp.payload",
            "aiohttp.payload_streamer",
            "aiohttp.resolver",
            "aiohttp.signals",
            "aiohttp.streams",
            "aiohttp.tcp_helpers",
            "aiohttp.tracing",
            "aiohttp.typedefs",
            "aiohttp.web",
            "aiohttp.web_app",
            "aiohttp.web_exceptions",
            "aiohttp.web_fileresponse",
            "aiohttp.web_log",
            "aiohttp.web_middlewares",
            "aiohttp.web_protocol",
            "aiohttp.web_request",
            "aiohttp.web_response",
            "aiohttp.web_routedef",
            "aiohttp.web_runner",
            "aiohttp.web_server",
            "aiohttp.web_urldispatcher",
            "aiohttp.web_ws",
            "aioitertools",
            "aioitertools.__version__",
            "aioitertools.asyncio",
            "aioitertools.builtins",
            "aioitertools.helpers",
            "aioitertools.itertools",
            "aioitertools.types",
            "argparse",
            "async_timeout",
            "asyncio",
            "asyncio.base_events",
            "asyncio.base_futures",
            "asyncio.base_subprocess",
            "asyncio.base_tasks",
            "asyncio.constants",
            "asyncio.coroutines",
            "asyncio.events",
            "asyncio.exceptions",
            "asyncio.format_helpers",
            "asyncio.futures",
            "asyncio.locks",
            "asyncio.log",
            "asyncio.protocols",
            "asyncio.queues",
            "asyncio.runners",
            "asyncio.selector_events",
            "asyncio.sslproto",
            "asyncio.staggered",
            "asyncio.streams",
            "asyncio.subprocess",
            "asyncio.tasks",
            "asyncio.transports",
            "asyncio.trsock",
            "asyncio.unix_events",
            "atexit",
            "attr",
            "attr._compat",
            "attr._config",
            "attr._funcs",
            "attr._make",
            "attr._next_gen",
            "attr._version_info",
            "attr.converters",
            "attr.exceptions",
            "attr.filters",
            "attr.setters",
            "attr.validators",
            "base64",
            "binascii",
            "bisect",
            "botocore",
            "botocore.args",
            "botocore.auth",
            "botocore.awsrequest",
            "botocore.client",
            "botocore.compat",
            "botocore.config",
            "botocore.configloader",
            "botocore.configprovider",
            "botocore.credentials",
            "botocore.discovery",
            "botocore.docs",
            "botocore.docs.bcdoc",
            "botocore.docs.bcdoc.docstringparser",
            "botocore.docs.bcdoc.restdoc",
            "botocore.docs.bcdoc.style",
            "botocore.docs.client",
            "botocore.docs.docstring",
            "botocore.docs.example",
            "botocore.docs.method",
            "botocore.docs.paginator",
            "botocore.docs.params",
            "botocore.docs.service",
            "botocore.docs.shape",
            "botocore.docs.sharedexample",
            "botocore.docs.utils",
            "botocore.docs.waiter",
            "botocore.endpoint",
            "botocore.errorfactory",
            "botocore.eventstream",
            "botocore.exceptions",
            "botocore.handlers",
            "botocore.history",
            "botocore.hooks",
            "botocore.httpsession",
            "botocore.loaders",
            "botocore.model",
            "botocore.monitoring",
            "botocore.paginate",
            "botocore.parsers",
            "botocore.regions",
            "botocore.response",
            "botocore.retries",
            "botocore.retries.adaptive",
            "botocore.retries.base",
            "botocore.retries.bucket",
            "botocore.retries.quota",
            "botocore.retries.special",
            "botocore.retries.standard",
            "botocore.retries.throttling",
            "botocore.retryhandler",
            "botocore.serialize",
            "botocore.session",
            "botocore.signers",
            "botocore.translate",
            "botocore.utils",
            "botocore.validate",
            "botocore.vendored",
            "botocore.vendored.requests",
            "botocore.vendored.requests.exceptions",
            "botocore.vendored.requests.packages",
            "botocore.vendored.requests.packages.urllib3",
            "botocore.vendored.requests.packages.urllib3.exceptions",
            "botocore.vendored.six",
            "botocore.vendored.six.moves",
            "botocore.vendored.six.moves.urllib",
            "botocore.vendored.six.moves.urllib.request",
            "botocore.vendored.six.moves.urllib_parse",
            "botocore.waiter",
            "builtins",
            "bz2",
            "calendar",
            "cchardet",
            "cchardet._cchardet",
            "cchardet.version",
            "certifi",
            "certifi.core",
            "cgi",
            "codecs",
            "collections",
            "collections.abc",
            "colorama",
            "colorama.ansi",
            "colorama.ansitowin32",
            "colorama.initialise",
            "colorama.win32",
            "colorama.winterm",
            "concurrent",
            "concurrent.futures",
            "concurrent.futures._base",
            "contextlib",
            "contextvars",
            "copy",
            "copyreg",
            "ctypes",
            "ctypes._endian",
            "cython_runtime",
            "datetime",
            "dateutil",
            "dateutil._common",
            "dateutil._version",
            "dateutil.parser",
            "dateutil.parser._parser",
            "dateutil.parser.isoparser",
            "dateutil.relativedelta",
            "dateutil.tz",
            "dateutil.tz._common",
            "dateutil.tz._factories",
            "dateutil.tz.tz",
            "decimal",
            "dis",
            "email",
            "email._encoded_words",
            "email._parseaddr",
            "email._policybase",
            "email.base64mime",
            "email.charset",
            "email.encoders",
            "email.errors",
            "email.feedparser",
            "email.header",
            "email.iterators",
            "email.message",
            "email.parser",
            "email.quoprimime",
            "email.utils",
            "encodings",
            "encodings.aliases",
            "encodings.latin_1",
            "encodings.utf_8",
            "enum",
            "errno",
            "fnmatch",
            "functools",
            "genericpath",
            "getopt",
            "getpass",
            "gettext",
            "google",
            "google.protobuf",
            "grp",
            "hashlib",
            "heapq",
            "hmac",
            "html",
            "html.entities",
            "html.parser",
            "http",
            "http.client",
            "http.cookies",
            "http.server",
            "idna",
            "idna.core",
            "idna.idnadata",
            "idna.intranges",
            "idna.package_data",
            "importlib",
            "importlib._bootstrap",
            "importlib._bootstrap_external",
            "importlib.abc",
            "importlib.machinery",
            "importlib.resources",
            "importlib.util",
            "inspect",
            "io",
            "ipaddress",
            "itertools",
            "jmespath",
            "jmespath.ast",
            "jmespath.compat",
            "jmespath.exceptions",
            "jmespath.functions",
            "jmespath.lexer",
            "jmespath.parser",
            "jmespath.visitor",
            "json",
            "json.decoder",
            "json.encoder",
            "json.scanner",
            "keyword",
            "linecache",
            "locale",
            "logging",
            "logging.handlers",
            "lzma",
            "marshal",
            "math",
            "mimetypes",
            "multidict",
            "multidict._abc",
            "multidict._compat",
            "multidict._multidict",
            "multidict._multidict_base",
            "netrc",
            "ntpath",
            "numbers",
            "opcode",
            "operator",
            "os",
            "os.path",
            "pamqp",
            "pamqp.body",
            "pamqp.constants",
            "pamqp.decode",
            "pamqp.encode",
            "pamqp.exceptions",
            "pamqp.frame",
            "pamqp.header",
            "pamqp.heartbeat",
            "pamqp.specification",
            "pathlib",
            "pickle",
            "platform",
            "posix",
            "posixpath",
            "pwd",
            "pycares",
            "pycares._cares",
            "pycares._version",
            "pycares.errno",
            "pycares.utils",
            "pyexpat",
            "pyexpat.errors",
            "pyexpat.model",
            "pytz",
            "pytz.exceptions",
            "pytz.lazy",
            "pytz.tzfile",
            "pytz.tzinfo",
            "queue",
            "quopri",
            "random",
            "re",
            "reprlib",
            "select",
            "selectors",
            "shlex",
            "shutil",
            "signal",
            "site",
            "six",
            "six.moves",
            "socket",
            "socketserver",
            "sre_compile",
            "sre_constants",
            "sre_parse",
            "ssl",
            "stat",
            "string",
            "struct",
            "subprocess",
            "sys",
            "tempfile",
            "termios",
            "threading",
            "time",
            "token",
            "tokenize",
            "tomodachi",
            "tomodachi.__version__",
            "tomodachi.cli",
            "tomodachi.config",
            "tomodachi.container",
            "tomodachi.envelope",
            "tomodachi.envelope.json_base",
            "tomodachi.protocol.json_base",
            "tomodachi.envelope.proto_build",
            "tomodachi.envelope.proto_build.protobuf",
            "tomodachi.envelope.proto_build.protobuf.sns_sqs_message_pb2",
            "tomodachi.envelope.protobuf_base",
            "tomodachi.helpers",
            "tomodachi.helpers.crontab",
            "tomodachi.helpers.dict",
            "tomodachi.helpers.execution_context",
            "tomodachi.helpers.logging",
            "tomodachi.helpers.middleware",
            "tomodachi.importer",
            "tomodachi.invoker",
            "tomodachi.invoker.base",
            "tomodachi.invoker.decorator",
            "tomodachi.launcher",
            "tomodachi.protocol",
            "tomodachi.protocol.json_base",
            "tomodachi.protocol.protobuf_base",
            "tomodachi.transport",
            "tomodachi.transport.amqp",
            "tomodachi.transport.aws_sns_sqs",
            "tomodachi.transport.http",
            "tomodachi.transport.schedule",
            "tomodachi.watcher",
            "traceback",
            "types",
            "typing",
            "typing.io",
            "typing.re",
            "typing_extensions",
            "tzlocal",
            "tzlocal.unix",
            "tzlocal.utils",
            "unicodedata",
            "urllib",
            "urllib.error",
            "urllib.parse",
            "urllib.request",
            "urllib.response",
            "urllib3",
            "urllib3._collections",
            "urllib3._version",
            "urllib3.connection",
            "urllib3.connectionpool",
            "urllib3.contrib",
            "urllib3.contrib._appengine_environ",
            "urllib3.exceptions",
            "urllib3.fields",
            "urllib3.filepost",
            "urllib3.packages",
            "urllib3.packages.six",
            "urllib3.packages.six.moves",
            "urllib3.packages.six.moves.http_client",
            "urllib3.packages.six.moves.urllib",
            "urllib3.packages.six.moves.urllib.parse",
            "urllib3.packages.ssl_match_hostname",
            "urllib3.poolmanager",
            "urllib3.request",
            "urllib3.response",
            "urllib3.util",
            "urllib3.util.connection",
            "urllib3.util.queue",
            "urllib3.util.request",
            "urllib3.util.response",
            "urllib3.util.retry",
            "urllib3.util.ssl_",
            "urllib3.util.timeout",
            "urllib3.util.url",
            "urllib3.util.wait",
            "uu",
            "uuid",
            "warnings",
            "weakref",
            "wrapt",
            "wrapt.decorators",
            "wrapt.importer",
            "wrapt.wrappers",
            "xml",
            "xml.etree",
            "xml.etree.ElementPath",
            "xml.etree.ElementTree",
            "xml.etree.cElementTree",
            "yarl",
            "yarl._quoting",
            "yarl._quoting_c",
            "yarl._url",
            "zipimport",
            "zlib",
        ]

        restarting = False
        while cls.restart_services:
            init_timestamp = time.time()
            init_timestamp_str = datetime.datetime.utcfromtimestamp(
                init_timestamp).isoformat() + "Z"

            process_id = os.getpid()

            event_loop_alias = ""
            event_loop_version = ""
            try:
                if "uvloop." in str(loop.__class__):
                    event_loop_alias = "uvloop"
                    import uvloop  # noqa  # isort:skip

                    event_loop_version = str(uvloop.__version__)
                elif "asyncio." in str(loop.__class__):
                    event_loop_alias = "asyncio"
                else:
                    event_loop_alias = "{}.{}".format(
                        loop.__class__.__module__, loop.__class__.__name__)
            except Exception:
                event_loop_alias = str(loop)

            clear_services()
            clear_execution_context()
            set_execution_context({
                "tomodachi_version": tomodachi.__version__,
                "python_version": platform.python_version(),
                "system_platform": platform.system(),
                "process_id": process_id,
                "init_timestamp": init_timestamp_str,
                "event_loop": event_loop_alias,
            })

            if event_loop_alias == "uvloop" and event_loop_version:
                set_execution_context({
                    "uvloop_version": event_loop_version,
                })

            if watcher:
                tz: Any = None
                utc_tz: Any = None

                try:
                    import pytz  # noqa  # isort:skip
                    import tzlocal  # noqa  # isort:skip

                    utc_tz = pytz.UTC
                    try:
                        tz = tzlocal.get_localzone()
                        if not tz:
                            tz = pytz.UTC
                    except Exception:
                        tz = pytz.UTC
                except Exception:
                    pass

                init_local_datetime = (
                    datetime.datetime.fromtimestamp(init_timestamp)
                    if tz and tz is not utc_tz and str(tz) != "UTC" else
                    datetime.datetime.utcfromtimestamp(init_timestamp))

                print("---")
                print("Starting tomodachi services (pid: {}) ...".format(
                    process_id))
                for file in service_files:
                    print("* {}".format(file))

                print()
                print("Current version: tomodachi {} on Python {}".format(
                    tomodachi.__version__, platform.python_version()))
                print("Event loop implementation: {}{}".format(
                    event_loop_alias, " {}".format(event_loop_version)
                    if event_loop_version else ""))
                if tz:
                    print("Local time: {} {}".format(
                        init_local_datetime.strftime(
                            "%B %d, %Y - %H:%M:%S,%f"), str(tz)))
                print("Timestamp in UTC: {}".format(init_timestamp_str))
                print()
                print(
                    "File watcher is active - code changes will automatically restart services"
                )
                print("Quit running services with <ctrl+c>")
                print()

            cls._close_waiter = asyncio.Future()
            cls._stopped_waiter = asyncio.Future()
            cls.restart_services = False

            try:
                cls.services = set([
                    ServiceContainer(ServiceImporter.import_service_file(file),
                                     configuration) for file in service_files
                ])
                result = loop.run_until_complete(
                    asyncio.wait([
                        asyncio.ensure_future(service.run_until_complete())
                        for service in cls.services
                    ]))
                exception = [
                    v.exception()
                    for v in [value for value in result if value][0]
                    if v.exception()
                ]
                if exception:
                    raise cast(Exception, exception[0])
            except tomodachi.importer.ServicePackageError:
                pass
            except Exception as e:
                logging.getLogger("exception").exception(
                    "Uncaught exception: {}".format(str(e)))

                if isinstance(e, ModuleNotFoundError):  # pragma: no cover
                    missing_module_name = str(getattr(e, "name", None) or "")
                    if missing_module_name:
                        color = ""
                        color_reset = ""
                        try:
                            import colorama  # noqa  # isort:skip

                            color = colorama.Fore.WHITE + colorama.Back.RED
                            color_reset = colorama.Style.RESET_ALL
                        except Exception:
                            pass

                        print("")
                        print(
                            "{}[fatal error] The '{}' package is missing or cannot be imported.{}"
                            .format(color, missing_module_name, color_reset))
                        print("")

                if restarting:
                    logging.getLogger("watcher.restart").warning(
                        "Service cannot restart due to errors")
                    logging.getLogger("watcher.restart").warning(
                        "Trying again in 1.5 seconds")
                    loop.run_until_complete(asyncio.wait([asyncio.sleep(1.5)]))
                    if cls._close_waiter and not cls._close_waiter.done():
                        cls.restart_services = True
                    else:
                        for signame in ("SIGINT", "SIGTERM"):
                            loop.remove_signal_handler(getattr(
                                signal, signame))
                else:
                    for signame in ("SIGINT", "SIGTERM"):
                        loop.remove_signal_handler(getattr(signal, signame))

            current_modules = [m for m in sys.modules.keys()]
            for m in current_modules:
                if m not in init_modules and m not in safe_modules:
                    del sys.modules[m]

            importlib.reload(tomodachi.container)
            importlib.reload(tomodachi.invoker)
            importlib.reload(tomodachi.invoker.base)
            importlib.reload(tomodachi.importer)

            restarting = True

        if watcher:
            if not watcher_future.done():
                try:
                    watcher_future.set_result(None)
                except RuntimeError:  # pragma: no cover
                    watcher_future.cancel()
                if not watcher_future.done():  # pragma: no cover
                    try:
                        loop.run_until_complete(watcher_future)
                    except (Exception, CancelledError):
                        pass