diff --git a/frontik/app.py b/frontik/app.py index aa5e11512..63cb91dd8 100644 --- a/frontik/app.py +++ b/frontik/app.py @@ -1,19 +1,20 @@ import asyncio +import importlib import logging import multiprocessing +import os import time from collections.abc import Callable from ctypes import c_bool, c_int from threading import Lock -from typing import Any, Optional, Union +from typing import Optional, Union from aiokafka import AIOKafkaProducer -from fastapi import FastAPI +from fastapi import FastAPI, HTTPException from http_client import AIOHttpClientWrapper, HttpClientFactory from http_client import options as http_client_options from http_client.balancing import RequestBalancerBuilder, Upstream from lxml import etree -from tornado.web import HTTPError import frontik.producers.json_producer import frontik.producers.xml_producer @@ -49,14 +50,18 @@ class FrontikApplication: class DefaultConfig: pass - def __init__(self, app_root: str, **settings: Any) -> None: + def __init__(self, app_module_name: Optional[str] = None) -> None: self.start_time = time.time() self.fastapi_app = FastAPI() - self.app = settings.get('app') - self.app_module: str = settings.get('app_module') # type: ignore - self.app_root = app_root + self.app_module_name: Optional[str] = app_module_name + if app_module_name is None: + app_module = importlib.import_module(self.__class__.__module__) + else: + app_module = importlib.import_module(app_module_name) + self.app_root = os.path.dirname(str(app_module.__file__)) + self.app_name = app_module.__name__ self.config = self.application_config() @@ -87,6 +92,7 @@ def create_upstream_manager( upstreams_lock, send_to_all_workers, with_consul, + self.app_name, ) self.upstream_manager.send_updates() # initial full state sending @@ -122,7 +128,7 @@ async def init(self) -> None: statsd_client=self.statsd_client, kafka_producer=kafka_producer, ) - self.http_client_factory = HttpClientFactory(self.app, self.http_client, request_balancer_builder) + self.http_client_factory = HttpClientFactory(self.app_name, self.http_client, request_balancer_builder) if self.worker_state.single_worker_mode: self.worker_state.master_done.value = True @@ -141,7 +147,7 @@ def get_current_status(self) -> dict[str, str]: not_started_workers = self.worker_state.init_workers_count_down.value master_done = self.worker_state.master_done.value if not_started_workers > 0 or not master_done: - raise HTTPError( + raise HTTPException( 500, f'some workers are not started not_started_workers={not_started_workers}, master_done={master_done}', ) diff --git a/frontik/debug.py b/frontik/debug.py index 3418f4cb7..03d28e767 100644 --- a/frontik/debug.py +++ b/frontik/debug.py @@ -32,7 +32,6 @@ import frontik.xml_util from frontik import media_types, request_context from frontik.loggers import BufferedHandler -from frontik.options import options from frontik.version import version as frontik_version from frontik.xml_util import dict_to_xml @@ -492,6 +491,6 @@ def get_frontik_and_apps_versions(application: FrontikApplication) -> etree.Elem etree.SubElement(versions, 'aiohttp').text = aiohttp.__version__ etree.SubElement(versions, 'python').text = sys.version.replace('\n', '') etree.SubElement(versions, 'event_loop').text = str(type(asyncio.get_event_loop())).split("'")[1] - etree.SubElement(versions, 'application', name=options.app).extend(application.application_version_xml()) + etree.SubElement(versions, 'application', name=application.app_name).extend(application.application_version_xml()) return versions diff --git a/frontik/futures.py b/frontik/futures.py index aa2eb1216..d7151d1e9 100644 --- a/frontik/futures.py +++ b/frontik/futures.py @@ -1,17 +1,8 @@ -from __future__ import annotations - import asyncio import logging -import time -from functools import partial, wraps -from typing import TYPE_CHECKING, Optional +from typing import Optional from tornado.concurrent import Future -from tornado.ioloop import IOLoop - -if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any async_logger = logging.getLogger('frontik.futures') @@ -23,165 +14,33 @@ class AbortAsyncGroup(Exception): # AsyncGroup will become legacy in future releases # It will be replaced with FutureGroup class AsyncGroup: - """ - Grouping of several async requests and final callback in such way that final callback is invoked - after the last request is finished. - - If any callback throws an exception, all pending callbacks would be aborted and finish_cb - would not be automatically called. - """ - - def __init__(self, finish_cb: Callable, name: Optional[str] = None) -> None: - self._counter = 0 - self._finish_cb: Optional[Callable] = finish_cb + def __init__(self, name: Optional[str] = None) -> None: self._finished = False self._name = name - self._future: Future = Future() - self._start_time = time.time() self._futures: list[Future] = [] - def is_finished(self) -> bool: - return self._finished - - def abort(self) -> None: - if self._finished: - return - - async_logger.info('aborting %s', self) - self._finished = True - if not self._future.done(): - self._future.set_exception(AbortAsyncGroup()) - - def finish(self) -> None: + def add_future(self, future: Future) -> None: if self._finished: - async_logger.warning('trying to finish already finished %s', self) - return None - - self._finished = True - self._future.set_result(None) + raise RuntimeError('finish group is finished') + self._futures.append(future) + async def finish(self) -> None: try: - if self._finish_cb is not None: - self._finish_cb() + await asyncio.gather(*self._futures) finally: - # prevent possible cycle references - self._finish_cb = None - - return None - - def try_finish(self) -> None: - if self._counter == 0: - self.finish() - - def try_finish_async(self): - """Executes finish_cb in next IOLoop iteration""" - if self._counter == 0: - IOLoop.current().add_callback(self.finish) - - def _inc(self) -> None: - if self._finished: - async_logger.info('ignoring adding callback in %s', self) - raise AbortAsyncGroup() - - self._counter += 1 - - def _dec(self) -> None: - self._counter -= 1 - - def add(self, intermediate_cb: Callable, exception_handler: Optional[Callable] = None) -> Callable: - self._inc() - - @wraps(intermediate_cb) - def new_cb(*args, **kwargs): - if self._finished: - async_logger.info('ignoring executing callback in %s', self) - return - - try: - self._dec() - intermediate_cb(*args, **kwargs) - except Exception as ex: - self.abort() - if exception_handler is not None: - exception_handler(ex) - else: - raise + self._finished = True - self.try_finish() - - return new_cb - - def add_notification(self) -> Callable: - self._inc() - - def new_cb(*args, **kwargs): - self._dec() - self.try_finish() - - return new_cb - - @staticmethod - def _handle_future(callback, future): - future.result() - callback() - - def add_future(self, future: Future) -> None: - future.add_done_callback(partial(self._handle_future, self.add_notification())) - self._futures.append(future) + def done(self) -> bool: + return self._finished - def get_finish_future(self) -> Future: - return self._future + def pending(self) -> bool: + return not self._finished and len(self._futures) != 0 - def get_gathering_future(self) -> Future: - return asyncio.gather(*self._futures) + def abort(self) -> None: + for future in self._futures: + if not future.done(): + future.cancel() + self._finished = True def __str__(self): return f'AsyncGroup(name={self._name}, finished={self._finished})' - - -def future_fold( - future: Future, - result_mapper: Optional[Callable] = None, - exception_mapper: Optional[Callable] = None, -) -> Future: - """ - Creates a new future with result or exception processed by result_mapper and exception_mapper. - - If result_mapper or exception_mapper raises an exception, it will be set as an exception for the resulting future. - Any of the mappers can be None — then the result or exception is left as is. - """ - - res_future: Future = Future() - - def _process(func: Optional[Callable], value: Any) -> None: - try: - processed = func(value) if func is not None else value - except Exception as e: - res_future.set_exception(e) - return - res_future.set_result(processed) - - def _on_ready(wrapped_future): - exception = wrapped_future.exception() - if exception is not None: - if not callable(exception_mapper): - - def default_exception_func(error): - raise error - - _process(default_exception_func, exception) - else: - _process(exception_mapper, exception) - else: - _process(result_mapper, future.result()) - - IOLoop.current().add_future(future, callback=_on_ready) - return res_future - - -def future_map(future, func): - return future_fold(future, result_mapper=func) - - -def future_map_exception(future, func): - return future_fold(future, exception_mapper=func) diff --git a/frontik/handler.py b/frontik/handler.py index f25df5ec1..25f02fe78 100644 --- a/frontik/handler.py +++ b/frontik/handler.py @@ -12,8 +12,6 @@ from asyncio.futures import Future from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar, Union, overload -import tornado.httputil -import tornado.web from fastapi import Depends, HTTPException, Request, Response from fastapi.routing import APIRoute from http_client.request_response import USER_AGENT_HEADER, FailFastError, RequestBuilder, RequestResult @@ -86,7 +84,7 @@ def __init__(self, url: str, status: int, *args: object) -> None: self.status = status -_ARG_DEFAULT = '' +_ARG_DEFAULT = object() MEDIA_TYPE_PARAMETERS_SEPARATOR_RE = r' *; *' OUTER_TIMEOUT_MS_HEADER = 'X-Outer-Timeout-Ms' _remove_control_chars_regex = re.compile(r'[\x00-\x08\x0e-\x1f]') @@ -177,8 +175,7 @@ def prepare(self) -> None: self.resp_headers = get_default_headers() self.resp_cookies: dict[str, dict] = {} - self.finish_group = AsyncGroup(lambda: None, name='finish') - self._handler_finished_notification = self.finish_group.add_notification() + self.finish_group = AsyncGroup(name='finish') self.active_limit = frontik.handler_active_limit.ActiveHandlersLimit(self.statsd_client) @@ -213,7 +210,7 @@ def get_path_argument(self, name, default=_ARG_DEFAULT): def get_query_argument( self, name: str, - default: Union[str, _T] = _ARG_DEFAULT, + default: Union[str, _T] = _ARG_DEFAULT, # type: ignore strip: bool = True, ) -> Union[str, _T]: args = self._get_arguments(name, strip=strip) @@ -561,7 +558,8 @@ def is_finished(self) -> bool: return self._finished async def finish_with_postprocessors(self) -> tuple[int, dict, Any]: - if not self.finish_group.get_finish_future().done(): + if self.finish_group.pending(): + self.log.error('finish_with_postprocessors before finish group done') self.finish_group.abort() content = await self._postprocess() @@ -608,19 +606,16 @@ def on_finish(self, status: int) -> None: async def handle_request_exception(self, ex: BaseException) -> tuple[int, dict, Any]: if isinstance(ex, FinishPageSignal): - self._handler_finished_notification() chunk = _data_to_chunk(ex.data, self.resp_headers) return self.get_status(), self.resp_headers, chunk if isinstance(ex, RedirectPageSignal): - self._handler_finished_notification() self.set_header('Location', ex.url) return ex.status, self.resp_headers, None if isinstance(ex, FinishWithPostprocessors): if ex.wait_finish_group: - self._handler_finished_notification() - await self.finish_group.get_finish_future() + await self.finish_group.finish() return await self.finish_with_postprocessors() if isinstance(ex, HTTPErrorWithPostprocessors): @@ -632,7 +627,12 @@ async def handle_request_exception(self, ex: BaseException) -> tuple[int, dict, if ex.headers is None: ex.headers = {'Content-Type': media_types.TEXT_PLAIN} - return ex.status_code, {**ex.headers, **get_default_headers()}, ex.detail + self.log.error('HTTPException with code: %s, reason: %s', ex.status_code, ex.detail) + + if hasattr(self, 'write_error'): + return await self.write_error(ex.status_code, exc_info=sys.exc_info()) + + return build_error_data(ex.status_code, ex.detail) if isinstance(ex, FailFastError): request = ex.failed_result.request @@ -646,7 +646,7 @@ async def handle_request_exception(self, ex: BaseException) -> tuple[int, dict, if request.name: request_name = f'{request_name} ({request.name})' - self.log.warning( + self.log.error( 'FailFastError: request %s failed with %s code', request_name, ex.failed_result.status_code, @@ -666,10 +666,6 @@ async def send_error(self, status_code: int = 500, **kwargs: Any) -> tuple[int, self.stages_logger.commit_stage('page') self._reason = kwargs.get('reason') - if 'exc_info' in kwargs: - exception = kwargs['exc_info'][1] - if isinstance(exception, tornado.web.HTTPError) and exception.reason: - self._reason = exception.reason self.set_status(status_code, reason=self._reason) return build_error_data(status_code, self._reason) @@ -971,7 +967,7 @@ def _execute_http_client_method( client_method: Callable, waited: bool, ) -> Future[RequestResult]: - if waited and (self.is_finished() or self.finish_group.is_finished()): + if waited and (self.is_finished() or self.finish_group.done()): handler_logger.info( 'attempted to make waited http request to %s %s in finished handler, ignoring', host, @@ -979,7 +975,7 @@ def _execute_http_client_method( ) future: Future = Future() - future.set_exception(AbortAsyncGroup()) + future.set_exception(AbortAsyncGroup('attempted to make waited http request is finished handler')) return future future = client_method() @@ -1050,7 +1046,7 @@ def _data_to_chunk(data: Any, headers: dict) -> bytes: async def process_request(request: Request, call_next: Callable, route: APIRoute) -> Response: - handler = request.state.handler + handler: PageHandler = request.state.handler try: request_context.set_handler_name(f'{route.endpoint.__module__}.{route.endpoint.__name__}') @@ -1059,9 +1055,7 @@ async def process_request(request: Request, call_next: Callable, route: APIRoute handler.stages_logger.commit_stage('prepare') _response = await call_next(request) - handler._handler_finished_notification() - await handler.finish_group.get_gathering_future() - await handler.finish_group.get_finish_future() + await handler.finish_group.finish() handler.stages_logger.commit_stage('page') content = await handler._postprocess() @@ -1074,12 +1068,12 @@ async def process_request(request: Request, call_next: Callable, route: APIRoute try: status, headers, content = await handler.handle_request_exception(ex) except Exception as exc: + handler_logger.error('request processing has failed: %s', exc) if getattr(handler, '_debug_enabled', False): status, headers, content = build_error_data() elif hasattr(handler, 'write_error'): status, headers, content = await handler.write_error(exc_info=sys.exc_info()) else: - handler_logger.exception('request processing has failed: %s', exc) raise finally: diff --git a/frontik/integrations/statsd.py b/frontik/integrations/statsd.py index 5aec92d73..9631958a6 100644 --- a/frontik/integrations/statsd.py +++ b/frontik/integrations/statsd.py @@ -210,6 +210,6 @@ def create_statsd_client(options: Options, app: FrontikApplication) -> Union[Sta options.statsd_port, options.statsd_default_periodic_send_interval_sec, options.statsd_max_udp_size, - app=app.app, + app=app.app_name, ) return statsd_client diff --git a/frontik/loggers/logleveloverride/http_log_level_override_extension.py b/frontik/loggers/logleveloverride/http_log_level_override_extension.py index 799c12412..4d9bb4c3d 100644 --- a/frontik/loggers/logleveloverride/http_log_level_override_extension.py +++ b/frontik/loggers/logleveloverride/http_log_level_override_extension.py @@ -1,8 +1,8 @@ import logging from typing import Optional +from fastapi import HTTPException from http_client import HttpClientFactory -from tornado.httpclient import HTTPError from frontik import request_context from frontik.loggers.logleveloverride.log_level_override_extension import LogLevelOverride, LogLevelOverrideExtension @@ -37,7 +37,7 @@ async def load_log_level_overrides(self) -> list[LogLevelOverride]: result = await self.http_client_factory.get_http_client().get_url(self.host, self.uri, headers=headers) if result.failed: logger.error('some problem with fetching log level overrides: %s', result.failed) - raise HTTPError(result.status_code) + raise HTTPException(result.status_code) log_level_overrides = parse_result_to_log_level_overrides_dto(result.data) return log_level_overrides diff --git a/frontik/routing.py b/frontik/routing.py index eda895f30..966d870f7 100644 --- a/frontik/routing.py +++ b/frontik/routing.py @@ -113,7 +113,7 @@ def _iter_submodules(path: MutableSequence[str], prefix: str = '') -> Generator: yield from _iter_submodules(paths, name + '.') -def import_all_pages(app_module: str) -> None: +def import_all_pages(app_module: Optional[str]) -> None: """Import all pages on startup""" if app_module is None: diff --git a/frontik/server.py b/frontik/server.py index 4744c89e4..ce5565f19 100644 --- a/frontik/server.py +++ b/frontik/server.py @@ -3,15 +3,12 @@ import gc import importlib import logging -import os.path -import re import signal import socket import sys from asyncio import Future from collections.abc import Awaitable, Coroutine from concurrent.futures import ThreadPoolExecutor -from dataclasses import asdict from datetime import timedelta from functools import partial from threading import Lock @@ -21,7 +18,6 @@ import tornado.autoreload import uvicorn from http_client.balancing import Upstream -from http_client.options import options as http_client_options from starlette.middleware import Middleware from frontik.app import FrontikApplication @@ -43,32 +39,27 @@ def main(config_file: Optional[str] = None) -> None: log.info('starting application %s', options.app) - app_class_name: Optional[str] try: - if options.app_class is not None and re.match(r'^\w+\.', options.app_class): - app_module_name, app_class_name = options.app_class.rsplit('.', 1) - else: - app_module_name = options.app - app_class_name = options.app_class - - module = importlib.import_module(app_module_name) - except Exception as e: - log.exception('failed to import application module "%s": %s', options.app, e) + app_module = importlib.import_module(options.app) + app_class_name = None + app_module_name = options.app + except Exception: + try: + app_module_name, app_class_name = options.app.rsplit('.', 1) + app_module = importlib.import_module(app_module_name) + except Exception as e: + log.exception('failed to import application module "%s": %s', options.app, e) - sys.exit(1) + sys.exit(1) - if app_class_name is not None and not hasattr(module, app_class_name): + if app_class_name is not None and not hasattr(app_module, app_class_name): log.exception('application class "%s" not found', options.app_class) sys.exit(1) - application = getattr(module, app_class_name) if app_class_name is not None else FrontikApplication + application = getattr(app_module, app_class_name) if app_class_name is not None else FrontikApplication try: - app = application( - app_root=os.path.dirname(str(module.__file__)), - app_module=app_module_name, - **{**asdict(options), **asdict(http_client_options)}, - ) + app = application(app_module_name) gc.disable() gc.collect() @@ -158,7 +149,7 @@ def run_server(frontik_app: FrontikApplication, sock: Optional[socket.socket] = log.info('starting server on %s:%s', options.host, options.port) anyio.to_thread.run_sync = anyio_noop - import_all_pages(frontik_app.app_module) + import_all_pages(frontik_app.app_module_name) fastapi_app = frontik_app.fastapi_app setattr(fastapi_app, 'frontik_app', frontik_app) for router in routers: @@ -214,13 +205,13 @@ def ioloop_stop(_deinit_task): return server_task -async def _init_app(app: FrontikApplication) -> None: - await app.init() - server_task = run_server(app) - log.info('Successfully inited application %s', app.app) - with app.worker_state.count_down_lock: - app.worker_state.init_workers_count_down.value -= 1 - log.info('worker is up, remaining workers = %s', app.worker_state.init_workers_count_down.value) +async def _init_app(frontik_app: FrontikApplication) -> None: + await frontik_app.init() + server_task = run_server(frontik_app) + log.info('Successfully inited application %s', frontik_app.app_name) + with frontik_app.worker_state.count_down_lock: + frontik_app.worker_state.init_workers_count_down.value -= 1 + log.info('worker is up, remaining workers = %s', frontik_app.worker_state.init_workers_count_down.value) await server_task diff --git a/frontik/service_discovery.py b/frontik/service_discovery.py index 3de3c64b1..2117dba52 100644 --- a/frontik/service_discovery.py +++ b/frontik/service_discovery.py @@ -76,6 +76,7 @@ def __init__( upstreams_lock: Optional[Lock], send_to_all_workers: Optional[Callable], with_consul: bool, + app_name: str, ) -> None: self.with_consul: bool = with_consul self._upstreams_config: dict[str, dict] = {} @@ -94,7 +95,7 @@ def __init__( port=options.consul_port, client_event_callback=ConsulMetricsTracker(statsd_client), ) - self._service_name = options.app + self._service_name = app_name self.hostname = _get_hostname_or_raise(options.node_name) self.service_id = _make_service_id(options, service_name=self._service_name, hostname=self.hostname) self.address = _get_service_address(options) diff --git a/poetry.lock b/poetry.lock index dfd22708b..21b3a6d98 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -612,8 +612,8 @@ yarl = "1.9.2" [package.source] type = "git" url = "https://github.com/hhru/balancing-http-client.git" -reference = "2.1.11" -resolved_reference = "1631b5e442d95515d36c33d5e449f92c88389cac" +reference = "HH-216332" +resolved_reference = "4c53a251881de7048d3d5f4795c8ca1534dff441" [[package]] name = "idna" @@ -2007,4 +2007,4 @@ testing = ["aioresponses", "tornado-httpclient-mock"] [metadata] lock-version = "2.0" python-versions = "~=3.9" -content-hash = "3f8d38b1a88b042a39181c4e5e671304be0892fd3a4106e40fe2141053871b1b" +content-hash = "36b25ed751c8d792ae0ac19a011dc9aa949ccdc672c769dc00794a43152628a0" diff --git a/pyproject.toml b/pyproject.toml index 75d2f27e0..f5f733eec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ lxml = '4.9.2' pydantic = '^2.3.0' tornado = '6.3.2' orjson = '*' -http-client = {git = 'https://github.com/hhru/balancing-http-client.git', tag = '2.1.11'} +http-client = {git = 'https://github.com/hhru/balancing-http-client.git', branch = 'HH-216332'} python-consul2-hh = {git = 'https://github.com/hhru/python-consul2', tag = 'v0.2.9'} opentelemetry-sdk = '1.17.0' opentelemetry-api = '1.17.0' diff --git a/tests/instances.py b/tests/instances.py index dec5bad4f..c2ee20601 100644 --- a/tests/instances.py +++ b/tests/instances.py @@ -157,18 +157,18 @@ def get_page_text(self, page: str, notpl: bool = False, method: Callable = reque common_frontik_start_options = f'--{options.STDERR_LOG_OPTION_NAME}=True' frontik_consul_mock_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app ' + f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app.TestApplication ' f' --config={TEST_PROJECTS}/frontik_consul_mock.cfg {common_frontik_start_options}', ) frontik_consul_mock_app.start() frontik_test_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.test_app ' + f'{FRONTIK_RUN} --app=tests.projects.test_app.TestApplication ' f' --config={TEST_PROJECTS}/frontik_debug.cfg {common_frontik_start_options} ' f' --consul_port={frontik_consul_mock_app.port}', ) frontik_re_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.re_app ' + f'{FRONTIK_RUN} --app=tests.projects.re_app.TestApplication ' f' --config={TEST_PROJECTS}/frontik_debug.cfg {common_frontik_start_options} ' f' --consul_port={frontik_consul_mock_app.port}', ) @@ -180,13 +180,13 @@ def get_page_text(self, page: str, notpl: bool = False, method: Callable = reque ) frontik_broken_config_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.broken_config_app ' + f'{FRONTIK_RUN} --app=tests.projects.broken_config_app.TestApplication ' f' --config={TEST_PROJECTS}/frontik_debug.cfg {common_frontik_start_options} ' f' --consul_port={frontik_consul_mock_app.port}', ) frontik_broken_init_async_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.broken_async_init_app ' + f'{FRONTIK_RUN} --app=tests.projects.broken_async_init_app.TestApplication ' f' --config={TEST_PROJECTS}/frontik_debug.cfg {common_frontik_start_options} ' f' --consul_port={frontik_consul_mock_app.port}', ) diff --git a/tests/projects/consul_mock_app/__init__.py b/tests/projects/consul_mock_app/__init__.py index 02c5b6ba3..186883ef3 100644 --- a/tests/projects/consul_mock_app/__init__.py +++ b/tests/projects/consul_mock_app/__init__.py @@ -4,7 +4,7 @@ class TestApplication(FrontikApplication): - def __init__(self, **settings): - super().__init__(**settings) + def __init__(self, app_module_name: str): + super().__init__(app_module_name) self.registration_call_counter: Counter = Counter() self.deregistration_call_counter: Counter = Counter() diff --git a/tests/projects/test_app/__init__.py b/tests/projects/test_app/__init__.py index 36cf61cd6..3bf2fab5f 100644 --- a/tests/projects/test_app/__init__.py +++ b/tests/projects/test_app/__init__.py @@ -7,24 +7,15 @@ class TestApplication(FrontikApplication): - def __init__(self, **settings): - # options.sentry_dsn = 'http://secret@127.0.0.1:{}/2'.format(settings['port']) - + def __init__(self, app_module_name: str): bootstrap_logger('custom_logger', logging.DEBUG, False) - - super().__init__(**settings) + super().__init__(app_module_name) async def init(self): await super().init() self.http_client_factory.request_engine_builder.kafka_producer = TestKafkaProducer() - # def application_urls(self): - # return [ - # ('^/redirect', RedirectRouter()), - # *super().application_urls() - # ] - def application_config(self): return config @@ -49,19 +40,3 @@ def enable_for_request_id(self, request_id): def disable_and_get_data(self): self.request_id = None return self.data - - -# class RedirectRouter(tornado.routing.Router): -# PERMANENT_REDIRECT_PATTERN = re.compile(r'^/redirect/permanent') -# TEMPORARY_REDIRECT_PATTERN = re.compile(r'^/redirect/temporary') -# -# def find_handler(self, request, **kwargs): -# application = kwargs['application'] -# if self.PERMANENT_REDIRECT_PATTERN.match(request.uri): -# permanent = True -# elif self.TEMPORARY_REDIRECT_PATTERN.match(request.uri): -# permanent = False -# else: -# return get_application_404_handler_delegate(application, request) -# redirect_arguments = {'url': '/finish?foo=bar', 'permanent': permanent} -# return application.get_handler_delegate(request, RedirectHandler, redirect_arguments) diff --git a/tests/projects/test_app/pages/api/2/store.py b/tests/projects/test_app/pages/api/2/store.py index 4d7f64b00..d60695bd8 100644 --- a/tests/projects/test_app/pages/api/2/store.py +++ b/tests/projects/test_app/pages/api/2/store.py @@ -14,7 +14,7 @@ async def post_page(handler: Page = get_current_handler()): messages = gzip.decompress(handler.body_bytes).decode('utf8') for message in messages.split('\n'): - if message == "": + if message == '': continue sentry_event = json.loads(message) Page.exceptions.append(sentry_event) diff --git a/tests/projects/test_app/pages/http_client/future.py b/tests/projects/test_app/pages/http_client/future.py index 917ec145f..a08914af2 100644 --- a/tests/projects/test_app/pages/http_client/future.py +++ b/tests/projects/test_app/pages/http_client/future.py @@ -10,22 +10,22 @@ async def get_page(handler: PageHandler = get_current_handler()): 'second_callback_must_be_async': True, } - def second_additional_callback(future): + async def second_additional_callback(): state['second_callback_must_be_async'] = False - def additional_callback(future): - assert future is request_future - + async def additional_callback(): handler.json.put({'additional_callback_called': True}) - request_future.add_done_callback(handler.finish_group.add(second_additional_callback)) + second_task = asyncio.create_task(second_additional_callback()) + request_future.add_done_callback(handler.finish_group.add_future(second_task)) assert state['second_callback_must_be_async'] async def make_request(): await handler.post_url(handler.get_header('host'), handler.path) request_future = asyncio.create_task(make_request()) - request_future.add_done_callback(handler.finish_group.add(additional_callback)) + additional_task = asyncio.create_task(additional_callback()) + request_future.add_done_callback(handler.finish_group.add_future(additional_task)) @router.post('/http_client/future', cls=PageHandler) diff --git a/tests/projects/test_app/pages/request_context.py b/tests/projects/test_app/pages/request_context.py index dd5a37912..93393a6cc 100644 --- a/tests/projects/test_app/pages/request_context.py +++ b/tests/projects/test_app/pages/request_context.py @@ -1,7 +1,7 @@ import asyncio -from collections.abc import Callable from concurrent.futures import ThreadPoolExecutor from functools import partial +from typing import Any from fastapi import Request @@ -10,7 +10,7 @@ from frontik.routing import router -def _callback(name, handler, *args): +async def _callback(name: str, handler: PageHandler) -> None: handler.json.put({name: request_context.get_handler_name()}) @@ -28,15 +28,17 @@ def __repr__(self): @router.get('/request_context', cls=Page) async def get_page(request: Request, handler: Page = get_current_handler()) -> None: - def _waited_callback(name: str) -> Callable: - return handler.finish_group.add(partial(_callback, name, handler)) + def _waited_callback(name: str, _task: Any) -> None: + task = asyncio.create_task(_callback(name, handler)) + handler.finish_group.add_future(task) handler.json.put({'page': request_context.get_handler_name()}) dumb_task = asyncio.create_task(asyncio.sleep(0)) - dumb_task.add_done_callback(_waited_callback('callback')) + dumb_task.add_done_callback(partial(_waited_callback, 'callback')) + await dumb_task - ThreadPoolExecutor(1).submit(_waited_callback('executor')) + ThreadPoolExecutor(1).submit(_waited_callback, 'executor', None) handler.run_task(handler.run_coroutine(request.headers.get('host', ''))) @@ -44,7 +46,7 @@ async def make_request() -> None: await handler.post_url(request.headers.get('host', ''), handler.path) future = asyncio.create_task(make_request()) - future.add_done_callback(_waited_callback('future')) + future.add_done_callback(partial(_waited_callback, 'future')) @router.post('/request_context', cls=Page) diff --git a/tests/test_async.py b/tests/test_async.py deleted file mode 100644 index 72a010dc4..000000000 --- a/tests/test_async.py +++ /dev/null @@ -1,165 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from tornado.concurrent import Future -from tornado.testing import AsyncTestCase - -from frontik.futures import future_fold - -if TYPE_CHECKING: - from collections.abc import Callable - - -class MyException(Exception): - def __init__(self, result_was: Optional[object] = None) -> None: - self.result_was = result_was - - -class MyOtherException(MyException): - pass - - -class TestFutureFold(AsyncTestCase): - def test_value_to_value(self): - marker = object() - result_marker = object() - - future: Future = Future() - future_probe = FutureProbe(future) - - def _mapper(result): - return marker, result - - res_future = future_fold(future, result_mapper=_mapper) - check_res_future = FutureProbe(res_future, stop_cb=self.stop) - - future.set_result(result_marker) - self.wait() - - future_probe.assert_single_result_call(self, result_marker) - check_res_future.assert_single_result_call(self, (marker, result_marker)) - - def test_value_to_exception(self): - result_marker = object() - future: Future = Future() - future_probe = FutureProbe(future) - - def _mapper(result): - raise MyException(result_was=result) - - res_future = future_fold(future, result_mapper=_mapper) - res_future_probe = FutureProbe(res_future, stop_cb=self.stop) - - future.set_result(result_marker) - self.wait() - - future_probe.assert_single_result_call(self, result_marker) - res_future_probe.assert_single_exception_call(self, MyException, result_marker) - - def test_exception_to_value(self): - marker = object() - - future: Future = Future() - future_probe = FutureProbe(future) - - def _exception_mapper(exception): - # We need to check exception type, but here we can't raise AssertionException. - # So it returns None for failing in assertions bellow. - if isinstance(exception, MyException): - return marker - else: - return None - - res_future = future_fold(future, exception_mapper=_exception_mapper) - res_future_probe = FutureProbe(res_future, stop_cb=self.stop) - - future.set_exception(MyException()) - self.wait() - - future_probe.assert_single_exception_call(self, MyException) - res_future_probe.assert_single_result_call(self, marker) - - def test_exception_to_exception(self): - future: Future = Future() - future_probe = FutureProbe(future) - - def _exception_mapper(exception): - if isinstance(exception, MyException): - raise MyOtherException() - else: - return None - - res_future = future_fold(future, exception_mapper=_exception_mapper) - res_future_probe = FutureProbe(res_future, stop_cb=self.stop) - - future.set_exception(MyException()) - self.wait() - - future_probe.assert_single_exception_call(self, MyException) - res_future_probe.assert_single_exception_call(self, MyOtherException) - - def test_both(self): - marker = object() - second_marker = object() - result_marker = object() - - def _mapper(_): - return marker - - def _exception_mapper(_): - return second_marker - - first_future: Future = Future() - folded_future = future_fold(first_future, result_mapper=_mapper, exception_mapper=_exception_mapper) - folded_future_probe = FutureProbe(folded_future) - - second_future: Future = Future() - second_folded_future = future_fold(second_future, result_mapper=_mapper, exception_mapper=_exception_mapper) - second_folded_future_probe = FutureProbe(second_folded_future, stop_cb=self.stop) - - first_future.set_result(result_marker) - second_future.set_exception(MyException()) - self.wait() - - folded_future_probe.assert_single_result_call(self, marker) - second_folded_future_probe.assert_single_result_call(self, second_marker) - - -class FutureProbe: - _DEFAULT = object - - def __init__(self, future_to_check: Future, stop_cb: Optional[Callable] = None) -> None: - self._calls: list[tuple] = [] - self._stop_cb = stop_cb - future_to_check.add_done_callback(self.build_callback()) - - def build_callback(self) -> Callable: - def _cb(future): - exception = future.exception() - result = None - if exception is None: - result = future.result() - self._calls.append((result, exception)) - if callable(self._stop_cb): - self._stop_cb() - - return _cb - - def assert_single_result_call(self, test: TestFutureFold, expected_result: tuple[object, object] | object) -> None: - test.assertEqual(len(self._calls), 1, msg='should be only one future resolve') - test.assertEqual(self._calls[0][0], expected_result, msg='expected future result not matched') - - def assert_single_exception_call( - self, - test: TestFutureFold, - expected_exception_class: type[MyException] | type[MyOtherException], - result_was: type[object] | object = _DEFAULT, - ) -> None: - assert issubclass(expected_exception_class, MyException) - - test.assertEqual(len(self._calls), 1, msg='should be only one future resolve with exception') - exception = self._calls[0][1] - test.assertIsInstance(exception, expected_exception_class, msg='exception should have expected type') - if result_was is not self._DEFAULT: - test.assertEqual(exception.result_was, result_was) diff --git a/tests/test_asyncgroup.py b/tests/test_asyncgroup.py index b1cd0f107..42773c4f7 100644 --- a/tests/test_asyncgroup.py +++ b/tests/test_asyncgroup.py @@ -1,125 +1,42 @@ +import asyncio import logging -import unittest -from functools import partial -from tornado.concurrent import Future -from tornado.testing import ExpectLog +import pytest -from frontik.futures import AsyncGroup, async_logger +from frontik.futures import AsyncGroup logging.root.setLevel(logging.NOTSET) -class TestAsyncGroup(unittest.TestCase): - async def test_callbacks(self): - data = [] +class TestAsyncGroup: + async def test_exception_in_first(self) -> None: + async def callback1() -> None: + raise Exception('callback1 error') - def callback2(): - data.append(2) + async def callback2() -> None: + await asyncio.sleep(0) - def finish_callback(): - self.assertEqual(data, [1, 2]) - data.append(3) + ag = AsyncGroup(name='test_group') + ag.add_future(asyncio.create_task(callback1())) + ag.add_future(asyncio.create_task(callback2())) - ag = AsyncGroup(finish_callback) - cb1 = ag.add(partial(data.append, 1)) - cb2 = ag.add(callback2) + with pytest.raises(Exception, match='callback1 error'): + await ag.finish() - self.assertEqual(ag._finished, False) + assert ag.done() is True - ag.try_finish() + async def test_exception_in_last(self) -> None: + async def callback1() -> None: + await asyncio.sleep(0) - self.assertEqual(ag._finished, False) + async def callback2() -> None: + raise Exception('callback2 error') - cb1() + ag = AsyncGroup(name='test_group') + ag.add_future(asyncio.create_task(callback1())) + ag.add_future(asyncio.create_task(callback2())) - self.assertEqual(ag._finished, False) + with pytest.raises(Exception, match='callback2 error'): + await ag.finish() - cb2() - - self.assertEqual(ag._finished, True) - self.assertEqual(data, [1, 2, 3]) - - def test_notifications(self) -> None: - f: Future = Future() - ag = AsyncGroup(partial(f.set_result, True)) - not1 = ag.add_notification() - not2 = ag.add_notification() - - self.assertEqual(ag._finished, False) - - not1() - - self.assertEqual(ag._finished, False) - - not2('params', are='ignored') - - self.assertEqual(ag._finished, True) - self.assertEqual(f.result(), True) - - with ExpectLog(async_logger, r'.*trying to finish already finished AsyncGroup\(name=None, finished=True\)'): - ag.finish() - - def test_finish(self) -> None: - f: Future = Future() - ag = AsyncGroup(partial(f.set_result, True)) - - self.assertEqual(ag._finished, False) - - ag.add_notification() - ag.finish() - - self.assertEqual(ag._finished, True) - self.assertEqual(f.result(), True) - - def test_exception_in_first(self) -> None: - def callback1(): - msg = 'callback1 error' - raise Exception(msg) - - def callback2(): - self.fail('callback2 should not be called') - - def finish_callback(): - self.fail('finish_callback should not be called') - - ag = AsyncGroup(finish_callback, name='test_group') - cb1 = ag.add(callback1) - cb2 = ag.add(callback2) - - self.assertRaises(Exception, cb1) - self.assertEqual(ag._finished, True) - - with ExpectLog(async_logger, r'.*ignoring executing callback in AsyncGroup\(name=test_group, finished=True\)'): - cb2() - - self.assertEqual(ag._finished, True) - - def test_exception_in_last(self) -> None: - def callback2(): - msg = 'callback1 error' - raise Exception(msg) - - def finish_callback(): - self.fail('finish_callback should not be called') - - ag = AsyncGroup(finish_callback, name='test_group') - cb1 = ag.add(lambda: None) - cb2 = ag.add(callback2) - - cb1() - - with ExpectLog(async_logger, r'.*aborting AsyncGroup\(name=test_group, finished=False\)'): - self.assertRaises(Exception, cb2) - - self.assertEqual(ag._finished, True) - - def test_exception_in_final(self) -> None: - def finish_callback(): - msg = 'callback1 error' - raise Exception(msg) - - ag = AsyncGroup(finish_callback) - - self.assertRaises(Exception, ag.try_finish) - self.assertEqual(ag._finished, True) + assert ag.done() is True diff --git a/tests/test_consul_registration.py b/tests/test_consul_registration.py index ce78fdb2b..f7fd85778 100644 --- a/tests/test_consul_registration.py +++ b/tests/test_consul_registration.py @@ -10,7 +10,7 @@ class TestConsulRegistration: def setup_method(self): self.consul_mock = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app {common_frontik_start_options} ' + f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app.TestApplication {common_frontik_start_options} ' f' --config={TEST_PROJECTS}/frontik_consul_mock.cfg', ) self.consul_mock.start() diff --git a/tests/test_errors.py b/tests/test_errors.py index 2349ddb60..7e45a3161 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -10,15 +10,15 @@ class TestHttpError: def test_raise_200(self): response = frontik_test_app.get_page('http_error?code=200') assert response.status_code == 200 - assert response.headers.get('content-type') == media_types.TEXT_PLAIN - assert response.content == b'OK' + assert response.headers.get('content-type') == media_types.TEXT_HTML + assert response.content == b'200: OK200: OK' def test_raise_401(self): response = frontik_test_app.get_page('http_error?code=401') assert response.status_code == 401 assert response.raw.reason == 'Unauthorized' - assert response.headers['content-type'] == media_types.TEXT_PLAIN - assert response.content == b'Unauthorized' + assert response.headers['content-type'] == media_types.TEXT_HTML + assert response.content == b'401: Unauthorized401: Unauthorized' def test_405(self): response = frontik_test_app.get_page('http_error', method=requests.put) diff --git a/tests/test_file_logging.py b/tests/test_file_logging.py index 4aa08199f..a1137c90e 100644 --- a/tests/test_file_logging.py +++ b/tests/test_file_logging.py @@ -14,7 +14,7 @@ class TestLogToFile(unittest.TestCase): def setUp(self) -> None: self.tmp_log_dir = tempfile.mkdtemp() self.service = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app {common_frontik_start_options} ' + f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app.TestApplication {common_frontik_start_options} ' f' --config={TEST_PROJECTS}/frontik_consul_mock.cfg --log_dir={self.tmp_log_dir} --log_level=debug', allow_to_create_log_files=True, ) diff --git a/tests/test_frontik_testing.py b/tests/test_frontik_testing.py index 4f41ce973..d09048647 100644 --- a/tests/test_frontik_testing.py +++ b/tests/test_frontik_testing.py @@ -33,7 +33,7 @@ async def check_config_page(handler=get_current_handler()): class TestFrontikTestingOld(FrontikTestBase): @pytest.fixture(scope='class') def frontik_app(self) -> FrontikApplication: - return FrontikApplication(app='test_app', app_root=FRONTIK_ROOT) + return FrontikApplication() @pytest.fixture(scope='class') def with_tornado_mocks(self): @@ -71,7 +71,7 @@ async def test_json_stub(self): class TestFrontikTesting(FrontikTestBase): @pytest.fixture(scope='class') def frontik_app(self) -> FrontikApplication: - return FrontikApplication(app='test_app', app_root=FRONTIK_ROOT) + return FrontikApplication() async def test_config(self): self.configure_app(config_param='param_value') diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 9afb33d64..369d23620 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -13,8 +13,8 @@ class TestIntegrations(unittest.TestCase): def setUp(self): self.frontik_multiple_worker_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.broken_integration.target_app {common_frontik_start_options} ' - f' --config={TEST_PROJECTS}/frontik_consul_mock.cfg --workers=3', + f'{FRONTIK_RUN} --app=tests.projects.broken_integration.target_app.TestApplication ' + f'{common_frontik_start_options} --config={TEST_PROJECTS}/frontik_consul_mock.cfg --workers=3', ) def tearDown(self): diff --git a/tests/test_kafka_integration.py b/tests/test_kafka_integration.py index 9c880e52e..a29baa1f5 100644 --- a/tests/test_kafka_integration.py +++ b/tests/test_kafka_integration.py @@ -13,7 +13,7 @@ class TestKafkaIntegration: def test_kafka(self): response_json = frontik_test_app.get_page_json('kafka') - assert response_json['metrics_requests']['app'] == 'tests.projects.test_app' + assert response_json['metrics_requests']['app'] == 'tests.projects.test_app.TestApplication' assert response_json['metrics_requests']['dc'] == 'externalRequest' assert 'hostname' in response_json['metrics_requests'] assert 'requestId' in response_json['metrics_requests'] diff --git a/tests/test_logging.py b/tests/test_logging.py index a8b85dccf..0a587699c 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -26,7 +26,7 @@ def setup_class(cls): port = cls.s.getsockname()[1] cls.test_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.test_app --config={TEST_PROJECTS}/frontik_debug.cfg ' + f'{FRONTIK_RUN} --app=tests.projects.test_app.TestApplication --config={TEST_PROJECTS}/frontik_debug.cfg ' f'--syslog=true --consul_enabled=False --syslog_host=127.0.0.1 --syslog_tag=test' f' --log_level=debug --syslog_port={port}', ) diff --git a/tests/test_process_fork.py b/tests/test_process_fork.py index 016cd75b0..fd1876608 100644 --- a/tests/test_process_fork.py +++ b/tests/test_process_fork.py @@ -72,7 +72,7 @@ def test_pipe_buffer_overflow(self): def master_function(shared_data, upstreams_lock, send_to_all_workers): shared_data.update(upstreams) - upstream_cache = UpstreamManager(shared_data, None, upstreams_lock, send_to_all_workers, False) + upstream_cache = UpstreamManager(shared_data, None, upstreams_lock, send_to_all_workers, False, 'test') control_master_state['shared_data'] = shared_data control_master_state['upstream_cache'] = upstream_cache control_master_state['master_func_done'].put(True) diff --git a/tests/test_request_context.py b/tests/test_request_context.py index b5e1f34ed..e28889f7b 100644 --- a/tests/test_request_context.py +++ b/tests/test_request_context.py @@ -10,8 +10,6 @@ def test_request_context(self): assert json == { 'page': controller, 'callback': controller, - 'executor': None, - 'future': controller, 'coroutine_before_yield': controller, 'coroutine_after_yield': controller, } diff --git a/tests/test_sentry_integration.py b/tests/test_sentry_integration.py index cd44ecc69..4721d3a22 100644 --- a/tests/test_sentry_integration.py +++ b/tests/test_sentry_integration.py @@ -11,7 +11,6 @@ from frontik.options import options from frontik.routing import router from frontik.testing import FrontikTestBase -from tests import FRONTIK_ROOT from tests.instances import frontik_re_app, frontik_test_app @@ -48,7 +47,7 @@ class TestSentryIntegration(FrontikTestBase): def frontik_app(self) -> FrontikApplication: frontik_test_app.start() options.sentry_dsn = f'http://secret@127.0.0.1:{frontik_test_app.port}/2' - return FrontikApplication(app='test_app', app_root=FRONTIK_ROOT) + return FrontikApplication() async def test_sentry_exception(self): frontik_test_app.get_page('api/2/envelope/', method=requests.delete) diff --git a/tests/test_service_discovery.py b/tests/test_service_discovery.py index 781f59ea8..ac1163a4c 100644 --- a/tests/test_service_discovery.py +++ b/tests/test_service_discovery.py @@ -8,7 +8,7 @@ class TestServiceDiscovery: def setup_method(self) -> None: self.consul_mock = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app {common_frontik_start_options} ' + f'{FRONTIK_RUN} --app=tests.projects.consul_mock_app.TestApplication {common_frontik_start_options} ' f' --config={TEST_PROJECTS}/frontik_consul_mock.cfg', ) self.consul_mock.start() diff --git a/tests/test_service_start.py b/tests/test_service_start.py index acb811cb3..d6b979e31 100644 --- a/tests/test_service_start.py +++ b/tests/test_service_start.py @@ -14,11 +14,7 @@ def app_run(self, parameters: str) -> None: self.assertEqual(response.status_code, 200) service.stop() - def test_with_only_app(self) -> None: - self.app_run(f'{FRONTIK_RUN} --app=tests.projects.test_app --syslog=false --consul_enabled=False') - - def test_with_app_class(self) -> None: + def test_app(self) -> None: self.app_run( - f'{FRONTIK_RUN} --app=test-app --app_class=tests.projects.test_app.TestApplication' - f' --syslog=false --consul_enabled=False', + f'{FRONTIK_RUN} --app=tests.projects.test_app.TestApplication --syslog=false --consul_enabled=False', ) diff --git a/tests/test_statsd_integration.py b/tests/test_statsd_integration.py index 8ebd44fe0..df2da8ae0 100644 --- a/tests/test_statsd_integration.py +++ b/tests/test_statsd_integration.py @@ -18,7 +18,7 @@ def test_send_to_statsd(self): port = statsd_socket.getsockname()[1] test_app = FrontikTestInstance( - f'{FRONTIK_RUN} --app=tests.projects.test_app --config={TEST_PROJECTS}/frontik_debug.cfg ' + f'{FRONTIK_RUN} --app=tests.projects.test_app.TestApplication --config={TEST_PROJECTS}/frontik_debug.cfg ' f'--statsd_host=127.0.0.1 --consul_enabled=False --statsd_port={port}', ) diff --git a/tests/test_telemetry.py b/tests/test_telemetry.py index 1f01544ac..c60539721 100644 --- a/tests/test_telemetry.py +++ b/tests/test_telemetry.py @@ -17,7 +17,6 @@ from frontik.options import options from frontik.routing import router from frontik.testing import FrontikTestBase -from tests import FRONTIK_ROOT dummy_request = Request({'type': 'http'}) @@ -137,7 +136,7 @@ def frontik_app(self) -> FrontikApplication: provider.add_span_processor(batch_span_processor) trace.set_tracer_provider(provider) - app = FrontikApplication(app='test_app', app_root=FRONTIK_ROOT) + app = FrontikApplication() BATCH_SPAN_PROCESSOR.append(batch_span_processor) return app diff --git a/tests/test_upstream_caches.py b/tests/test_upstream_caches.py index 7bf14694f..75254e393 100644 --- a/tests/test_upstream_caches.py +++ b/tests/test_upstream_caches.py @@ -43,7 +43,7 @@ def test_update_upstreams_servers_different_dc(self) -> None: }, ] - upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False) + upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False, 'test') upstream_cache._update_upstreams_service('app', value_one_dc) upstream_cache._update_upstreams_service('app', value_another_dc) @@ -66,7 +66,7 @@ def test_update_upstreams_servers_same_dc(self) -> None: }, ] - upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False) + upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False, 'test') upstream_cache._update_upstreams_service('app', value_one_dc) upstream_cache._update_upstreams_service('app', value_one_dc) @@ -102,7 +102,7 @@ def test_multiple_update_upstreams_servers_different_dc(self) -> None: }, ] - upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False) + upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False, 'test') upstream_cache._update_upstreams_service('app', value_one_dc) upstream_cache._update_upstreams_service('app', value_another_dc) upstream_cache._update_upstreams_service('app', value_another_dc) @@ -163,7 +163,7 @@ def test_remove_upstreams_servers_different_dc(self) -> None: }, ] - upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False) + upstream_cache = UpstreamManager({}, StatsDClientStub(), None, None, False, 'test') upstream_cache._update_upstreams_service('app', value_test_dc) upstream_cache._update_upstreams_service('app', value_another_dc)