From 757c1f79c89e392620ade74fc9742cb0318a7315 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Wed, 13 Jul 2022 17:59:28 +0300 Subject: [PATCH 01/19] fix(fastapi): support async endpoint handlers --- epsagon/trace.py | 83 ++++++++++- epsagon/wrappers/fastapi.py | 107 +++++++++++++- requirements-dev.txt | 1 + tests/wrappers/test_fastapi_wrapper.py | 187 +++++++++++++++++++++++-- 4 files changed, 361 insertions(+), 17 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index e4e60d91..9a21e0f4 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -14,6 +14,7 @@ import threading import random import json +from anyio import get_current_task import urllib3.exceptions from epsagon.event import BaseEvent @@ -22,6 +23,7 @@ from epsagon.trace_transports import NoneTransport, HTTPTransport, LogTransport from .constants import ( TIMEOUT_GRACE_TIME_MS, + EPSAGON_MARKER, MAX_LABEL_SIZE, DEFAULT_SAMPLE_RATE, TRACE_URL_PREFIX, @@ -95,6 +97,7 @@ def __init__(self): self.keys_to_ignore = None self.keys_to_allow = None self.use_single_trace = True + self.use_async_tracer = False self.singleton_trace = None self.local_thread_to_unique_id = {} self.transport = NoneTransport() @@ -200,6 +203,20 @@ def update_tracers(self): tracer.step_dict_output_path = self.step_dict_output_path tracer.sample_rate = self.sample_rate + def switch_to_async_tracer(self): + """ + Set the use_async_tracer flag to True. + :return: None + """ + self.use_async_tracer = True + print('Switching..') + + def is_async_tracer(self): + """ + Returns whether using an async tracer + """ + return self.use_async_tracer + def switch_to_multiple_traces(self): """ Set the use_single_trace flag to False. @@ -233,6 +250,50 @@ def _create_new_trace(self, unique_id=None): unique_id=unique_id, ) + @staticmethod + def _get_current_task(): + """ + Gets the current asyncio task safely + :return: The task. + """ + # Dynamic import since this is only valid in Python3+ + asyncio = __import__('asyncio') + if not asyncio.get_event_loop(): + return None + try: + return asyncio.Task.current_task() + except RuntimeError: + return None + + def _get_tracer_async_mode(self, should_create): + """ + Get trace assuming async tracer. + :return: The trace. + """ + task = type(self)._get_current_task() + if not task: + return None + + trace = getattr(task, EPSAGON_MARKER, None) + if not trace and should_create: + trace = self._create_new_trace() + setattr(task, EPSAGON_MARKER, trace) + return trace + + def _pop_trace_async_mode(self): + """ + Pops the trace from the current task, assuming async tracer + :return: The trace. + """ + task = type(self)._get_current_task() + if not task: + return None + + trace = getattr(task, EPSAGON_MARKER, None) + if trace: # can safely remove tracer from async task + delattr(task, EPSAGON_MARKER) + return trace + def get_or_create_trace(self, unique_id=None): """ Gets or create a trace - thread-safe @@ -267,6 +328,9 @@ def _get_trace(self, unique_id=None, should_create=False): :return: The trace. """ with TraceFactory.LOCK: + if self.use_async_tracer: + return self._get_tracer_async_mode(should_create=should_create) + unique_id = self.get_thread_local_unique_id(unique_id) if unique_id: trace = ( @@ -321,6 +385,11 @@ def pop_trace(self, trace=None): :return: unique id """ with self.LOCK: + if self.use_async_tracer: + trace = self._pop_trace_async_mode() + if trace: + # async tracer found + return trace if self.traces: trace = self.traces.pop(self.get_trace_identifier(trace), None) if not self.traces: @@ -338,7 +407,13 @@ def get_thread_local_unique_id(self, unique_id=None): :param unique_id: input unique id :return: active id if there's an active unique id or given one """ - return self.local_thread_to_unique_id.get( + if self.is_async_tracer(): + return self.local_thread_to_unique_id.get( + type(self)._get_current_task(), unique_id + ) + + else: + return self.local_thread_to_unique_id.get( get_thread_id(), unique_id ) @@ -353,7 +428,11 @@ def set_thread_local_unique_id(self, unique_id=None): self.singleton_trace.unique_id if self.singleton_trace else None ) ) - self.local_thread_to_unique_id[get_thread_id()] = unique_id + + if self.is_async_tracer(): + self.local_thread_to_unique_id[type(self)._get_current_task()] = unique_id + else: + self.local_thread_to_unique_id[get_thread_id()] = unique_id return unique_id def unset_thread_local_unique_id(self): diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index 8c4c066a..53288cb4 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -6,6 +6,7 @@ import json import json.decoder import asyncio +import os import warnings from fastapi import Request, Response @@ -29,9 +30,16 @@ SCOPE_UNIQUE_ID = 'trace_unique_id' SCOPE_CONTAINER_METADATA_COLLECTED = 'container_metadata' SCOPE_IGNORE_REQUEST = 'ignore_request' +IS_ASYNC_MODE = False + +def _initialize_async_mode(mode): + global IS_ASYNC_MODE + IS_ASYNC_MODE = mode + +_initialize_async_mode(os.getenv("EPSAGON_FASTAPI_ASYNC_MODE", "FALSE") == "TRUE") def _handle_wrapper_params(_args, kwargs, original_request_param_name): - """ + """f Handles the sync/async given parameters - gets the request object If original handler is set to get the Request object, then getting the request using this param. Otherwise, trying to get the Request object using @@ -222,6 +230,71 @@ def _fastapi_handler( raised_err ) +async def _async_fastapi_handler( + original_handler, + request, + status_code, + args, + kwargs +): + """ + FastAPI generic handler - for callbacks executed by a threadpool + :param original_handler: the wrapped original handler + :param request: the given handler request + :param status_code: the default configured response status code. + Can be None when called by exception handlers wrapper, as there's + no status code configuration for exception handlers. + """ + has_setup_succeeded = False + should_ignore_request = False + + try: + epsagon_scope, trace = _setup_handler(request) + if epsagon_scope and trace: + has_setup_succeeded = True + if ( + ignore_request('', request.url.path.lower()) + or + is_ignored_endpoint(request.url.path.lower()) + ): + should_ignore_request = True + epsagon_scope[SCOPE_IGNORE_REQUEST] = True + + except Exception: # pylint: disable=broad-except + has_setup_succeeded = False + + if not has_setup_succeeded or should_ignore_request: + return await original_handler(*args, **kwargs) + + created_runner = False + response = None + if not trace.runner: + if not _setup_trace_runner(epsagon_scope, trace, request): + return await original_handler(*args, **kwargs) + + raised_err = None + try: + response = await original_handler(*args, **kwargs) + except Exception as exception: # pylint: disable=W0703 + raised_err = exception + finally: + try: + epsagon.trace.trace_factory.unset_thread_local_unique_id() + except Exception: # pylint: disable=broad-except + pass + # no need to update request body if runner already created before + if created_runner: + _extract_request_body(trace, request) + + return _handle_response( + epsagon_scope, + response, + status_code, + trace, + raised_err + ) + + # pylint: disable=too-many-statements def _wrap_handler(dependant, status_code): @@ -230,9 +303,12 @@ def _wrap_handler(dependant, status_code): """ original_handler = dependant.call is_async = asyncio.iscoroutinefunction(original_handler) + if is_async: - # async endpoints are not supported - return + if not IS_ASYNC_MODE: + # in case of not using the Env var for async endpoints + return + original_request_param_name = dependant.request_param_name if not original_request_param_name: @@ -249,7 +325,25 @@ def wrapped_handler(*args, **kwargs): original_handler, request, status_code, args, kwargs ) - dependant.call = wrapped_handler + async def async_wrapped_handler(*args, **kwargs): + """ + Asynchronous wrapper handler + """ + request: Request = _handle_wrapper_params( + args, kwargs, original_request_param_name + ) + return await _async_fastapi_handler( + original_handler, request, status_code, args, kwargs + ) + + if is_async: + if IS_ASYNC_MODE: + # async endpoints + dependant.call = async_wrapped_handler + + else: + if not IS_ASYNC_MODE: + dependant.call = wrapped_handler def route_class_wrapper(wrapped, instance, args, kwargs): @@ -323,7 +417,10 @@ async def server_call_wrapper(wrapped, _instance, args, kwargs): trace = None try: - epsagon.trace.trace_factory.switch_to_multiple_traces() + if (IS_ASYNC_MODE): + epsagon.trace.trace_factory.switch_to_async_tracer() + else: + epsagon.trace.trace_factory.switch_to_multiple_traces() unique_id = str(uuid.uuid4()) trace = epsagon.trace.trace_factory.get_or_create_trace( unique_id=unique_id diff --git a/requirements-dev.txt b/requirements-dev.txt index c6122e9b..7c2b728c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,6 +20,7 @@ pytest-asyncio; python_version >= '3.5' pytest-aiohttp; python_version >= '3.5' httpx; python_version >= '3.5' asynctest; python_version >= '3.5' +pytest-lazy-fixture; python_version >= '3.5' moto; python_version >= '3.5' moto==2.1.0; python_version < '3.5' tornado diff --git a/tests/wrappers/test_fastapi_wrapper.py b/tests/wrappers/test_fastapi_wrapper.py index 7203a8fe..cb0cd177 100644 --- a/tests/wrappers/test_fastapi_wrapper.py +++ b/tests/wrappers/test_fastapi_wrapper.py @@ -18,6 +18,7 @@ from epsagon.wrappers.fastapi import ( DEFAULT_SUCCESS_STATUS_CODE, DEFAULT_ERROR_STATUS_CODE, + _initialize_async_mode, ) from .common import multiple_threads_handler @@ -61,18 +62,33 @@ def _get_response(key): def handle(): return _get_response(RETURN_VALUE) +async def async_handle(): + return _get_response(RETURN_VALUE) + def handle_custom_response(response_model=List[str]): return CUSTOM_RESPONSE +async def async_handle_custom_response(response_model=List[str]): + return CUSTOM_RESPONSE + def handle_base_model_response(response_model=CustomBaseModel): return CustomBaseModel(data=CUSTOM_RESPONSE) +async def async_handle_base_model_response(response_model=CustomBaseModel): + return CustomBaseModel(data=CUSTOM_RESPONSE) + def handle_custom_status_code(response_model=List[str]): return CUSTOM_RESPONSE +async def async_handle_custom_status_code(response_model=List[str]): + return CUSTOM_RESPONSE + def handle_overridden_custom_status_code(): return _get_response(RETURN_VALUE) +async def async_handle_overridden_custom_status_code(): + return _get_response(RETURN_VALUE) + def handle_given_request(request: Request): assert request.method == 'POST' loop = None @@ -84,23 +100,51 @@ def handle_given_request(request: Request): loop.close() return _get_response(RETURN_VALUE) +async def async_handle_given_request(request: Request): + assert request.method == 'POST' + loop = None + try: + loop = asyncio.new_event_loop() + assert loop.run_until_complete(request.json()) == TEST_POST_DATA + finally: + if loop: + loop.close() + return _get_response(RETURN_VALUE) + def handle_a(): time.sleep(0.2) return _get_response('a') +async def async_handle_a(): + time.sleep(0.2) + return _get_response('a') + def handle_b(): return _get_response('b') +async def async_handle_b(): + return _get_response('b') + def handle_router_endpoint(): return _get_response(ROUTER_RETURN_VALUE) +async def async_handle_router_endpoint(): + return _get_response(ROUTER_RETURN_VALUE) + def handle_custom_route_endpoint(): return _get_response(CUSTOM_ROUTE_RETURN_VALUE) +async def async_handle_custom_route_endpoint(): + return _get_response(CUSTOM_ROUTE_RETURN_VALUE) + def multiple_threads_route(): multiple_threads_handler() return _get_response(MULTIPLE_THREADS_RETURN_VALUE) +async def async_multiple_threads_route(): + multiple_threads_handler() + return _get_response(MULTIPLE_THREADS_RETURN_VALUE) + class CustomFastAPIException(Exception): pass @@ -117,14 +161,20 @@ class UnhandledFastAPIException(Exception): def handle_error_from_route(): raise CustomFastAPIException('test') +async def async_handle_error_from_route(): + raise CustomFastAPIException('test') def handle_raise_custom_error(): raise HandledFastAPIException('test') +async def async_handle_raise_custom_error(): + raise HandledFastAPIException('test') def handle_raise_unhandled_error(): raise UnhandledFastAPIException('test') +async def async_handle_raise_unhandled_error(): + raise UnhandledFastAPIException('test') def custom_exception_handler(request: Request, exc): return JSONResponse( @@ -132,12 +182,22 @@ def custom_exception_handler(request: Request, exc): content=CUSTON_EXCEPTION_HANDLER_RESPONSE ) +async def async_custom_exception_handler(request: Request, exc): + return JSONResponse( + status_code=CUSTOM_STATUS_CODE, + content=CUSTON_EXCEPTION_HANDLER_RESPONSE + ) def default_exception_handler(request: Request, exc): return JSONResponse( content=DEFAULT_EXCEPTION_HANDLER_RESPONSE ) +async def async_default_exception_handler(request: Request, exc): + return JSONResponse( + content=DEFAULT_EXCEPTION_HANDLER_RESPONSE + ) + def _build_fastapi_app(): app = FastAPI() @@ -177,14 +237,58 @@ def _build_fastapi_app(): app.include_router(router_with_custom_route, prefix=TEST_CUSTOM_ROUTE_PREFIX) return app +def _build_async_fastapi_app(): + app = FastAPI() + app.add_api_route("/", async_handle, methods=["GET"]) + app.add_api_route( + CUSTOM_RESPONSE_PATH, + async_handle_custom_response, + methods=["GET"] + ) + app.add_api_route( + BASE_MODEL_RESPONSE_PATH, + async_handle_base_model_response, + methods=["GET"] + ) + app.add_api_route( + CUSTOM_STATUS_CODE_PATH, + async_handle_custom_status_code, + methods=["GET"], + status_code=CUSTOM_STATUS_CODE + ) + app.add_api_route( + OVERRIDDEN_CUSTOM_STATUS_CODE_PATH, + async_handle_overridden_custom_status_code, + methods=["GET"], + status_code=CUSTOM_STATUS_CODE + ) + app.add_api_route(REQUEST_OBJ_PATH, async_handle_given_request, methods=["POST"]) + app.add_api_route("/a", async_handle_a, methods=["GET"]) + app.add_api_route("/b", async_handle_b, methods=["GET"]) + app.add_api_route("/err", async_handle_error_from_route, methods=["GET"], status_code=200) + app.add_api_route(MULTIPLE_THREADS_ROUTE, async_multiple_threads_route, methods=["GET"]) + router = APIRouter() + router.add_api_route(TEST_ROUTER_PATH, async_handle_router_endpoint) + app.include_router(router, prefix=TEST_ROUTER_PREFIX) + router_with_custom_route = APIRouter(route_class=CustomRouteClass) + router_with_custom_route.add_api_route(TEST_CUSTOM_ROUTE_PATH, async_handle_custom_route_endpoint) + app.include_router(router_with_custom_route, prefix=TEST_CUSTOM_ROUTE_PREFIX) + return app + @pytest.fixture(scope='function', autouse=False) -def fastapi_app(): +def sync_fastapi_app(): + _initialize_async_mode(False) return _build_fastapi_app() +@pytest.fixture(scope='function', autouse=False) +def async_fastapi_app(): + _initialize_async_mode(True) + return _build_async_fastapi_app() + @pytest.fixture(scope='function', autouse=False) -def fastapi_app_with_exception_handlers(fastapi_app): - app = fastapi_app +def fastapi_app_with_exception_handlers(sync_fastapi_app): + app = sync_fastapi_app app.add_api_route( HANDLED_EXCEPTION_PATH, handle_raise_custom_error, methods=["GET"] ) @@ -201,8 +305,14 @@ def fastapi_app_with_exception_handlers(fastapi_app): ) return app - @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_sanity(trace_transport, fastapi_app): """Sanity test.""" async with AsyncClient(app=fastapi_app, base_url="http://test") as ac: @@ -224,12 +334,20 @@ async def test_fastapi_sanity(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_custom_response(trace_transport, fastapi_app): """custom response test.""" request_path = f'{CUSTOM_RESPONSE_PATH}?x=testval' async with AsyncClient(app=fastapi_app, base_url="http://test") as ac: response = await ac.get(request_path) response_data = response.json() + print('*********************') runner = trace_transport.last_trace.events[0] assert isinstance(runner, FastapiRunner) assert runner.resource['name'].startswith('127.0.0.1') @@ -245,6 +363,13 @@ async def test_fastapi_custom_response(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_base_model_response(trace_transport, fastapi_app): """base model response test.""" request_path = f'{BASE_MODEL_RESPONSE_PATH}?x=testval' @@ -267,6 +392,13 @@ async def test_fastapi_base_model_response(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_custom_status_code(trace_transport, fastapi_app): """custom status code test.""" request_path = f'{CUSTOM_STATUS_CODE_PATH}?x=testval' @@ -292,6 +424,13 @@ async def test_fastapi_custom_status_code(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_custom_status_code_overridden(trace_transport, fastapi_app): """custom status code test - status code overridden by returned Response """ path = OVERRIDDEN_CUSTOM_STATUS_CODE_PATH @@ -316,6 +455,14 @@ async def test_fastapi_custom_status_code_overridden(trace_transport, fastapi_ap @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) +# RuntimeError: Cannot run the event loop while another loop is running async def test_fastapi_given_request(trace_transport, fastapi_app): """handler with a request parameter test.""" request_path = f'{REQUEST_OBJ_PATH}?x=testval' @@ -338,6 +485,13 @@ async def test_fastapi_given_request(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_custom_router(trace_transport, fastapi_app): """Custom router sanity test.""" full_route_path= f'{TEST_ROUTER_PREFIX}{TEST_ROUTER_PATH}' @@ -357,6 +511,13 @@ async def test_fastapi_custom_router(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_custom_api_route(trace_transport, fastapi_app): """Custom api route sanity test.""" full_route_path= f'{TEST_CUSTOM_ROUTE_PREFIX}{TEST_CUSTOM_ROUTE_PATH}' @@ -376,6 +537,13 @@ async def test_fastapi_custom_api_route(trace_transport, fastapi_app): @pytest.mark.asyncio +@pytest.mark.parametrize( + "fastapi_app", + [ + pytest.lazy_fixture("sync_fastapi_app"), + pytest.lazy_fixture("async_fastapi_app"), + ], +) async def test_fastapi_exception(trace_transport, fastapi_app): """Test when the handler raises an exception.""" try: @@ -475,12 +643,12 @@ async def _send_request(app, path, trace_transport): @pytest.mark.asyncio -async def test_fastapi_multiple_requests(trace_transport, fastapi_app): +async def test_fastapi_multiple_requests(trace_transport, sync_fastapi_app): """ Multiple requests test """ for _ in range(3): await asyncio.gather( - _send_request(fastapi_app, "a", trace_transport), - _send_request(fastapi_app, "b", trace_transport) + _send_request(sync_fastapi_app, "a", trace_transport), + _send_request(sync_fastapi_app, "b", trace_transport) ) # validating no `zombie` traces exist assert not trace_factory.traces @@ -490,8 +658,7 @@ async def _send_async_request(app, path): async with AsyncClient(app=app, base_url="http://test") as ac: return await ac.get(path) - -def test_fastapi_multiple_threads_route(trace_transport, fastapi_app): +def test_fastapi_multiple_threads_route(trace_transport, sync_fastapi_app): """ Tests request to a route, which invokes multiple threads. Validating no `zombie` traces exist (fromn the callback invoked threads) @@ -501,7 +668,7 @@ def test_fastapi_multiple_threads_route(trace_transport, fastapi_app): loop = asyncio.new_event_loop() response = loop.run_until_complete( _send_async_request( - fastapi_app, + sync_fastapi_app, f"{MULTIPLE_THREADS_ROUTE}?x=testval" ) ) From 23e95240efe8d8237d2a2f143054095f9c242830 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Thu, 14 Jul 2022 11:02:24 +0300 Subject: [PATCH 02/19] fix(fastapi): lint fix --- epsagon/trace.py | 10 ++++------ epsagon/wrappers/fastapi.py | 11 +++++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 9a21e0f4..b4ff2680 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -14,7 +14,6 @@ import threading import random import json -from anyio import get_current_task import urllib3.exceptions from epsagon.event import BaseEvent @@ -209,14 +208,13 @@ def switch_to_async_tracer(self): :return: None """ self.use_async_tracer = True - print('Switching..') def is_async_tracer(self): """ Returns whether using an async tracer """ return self.use_async_tracer - + def switch_to_multiple_traces(self): """ Set the use_single_trace flag to False. @@ -412,8 +410,7 @@ def get_thread_local_unique_id(self, unique_id=None): type(self)._get_current_task(), unique_id ) - else: - return self.local_thread_to_unique_id.get( + return self.local_thread_to_unique_id.get( get_thread_id(), unique_id ) @@ -430,7 +427,8 @@ def set_thread_local_unique_id(self, unique_id=None): ) if self.is_async_tracer(): - self.local_thread_to_unique_id[type(self)._get_current_task()] = unique_id + self.local_thread_to_unique_id[type(self). + _get_current_task()] = unique_id else: self.local_thread_to_unique_id[get_thread_id()] = unique_id return unique_id diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index 53288cb4..b2a0045f 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -33,10 +33,11 @@ IS_ASYNC_MODE = False def _initialize_async_mode(mode): - global IS_ASYNC_MODE - IS_ASYNC_MODE = mode + global IS_ASYNC_MODE + IS_ASYNC_MODE = mode -_initialize_async_mode(os.getenv("EPSAGON_FASTAPI_ASYNC_MODE", "FALSE") == "TRUE") +_initialize_async_mode(os.getenv( + 'EPSAGON_FASTAPI_ASYNC_MODE', 'FALSE') == 'TRUE') def _handle_wrapper_params(_args, kwargs, original_request_param_name): """f @@ -304,6 +305,8 @@ def _wrap_handler(dependant, status_code): original_handler = dependant.call is_async = asyncio.iscoroutinefunction(original_handler) + print(IS_ASYNC_MODE) + if is_async: if not IS_ASYNC_MODE: # in case of not using the Env var for async endpoints @@ -417,7 +420,7 @@ async def server_call_wrapper(wrapped, _instance, args, kwargs): trace = None try: - if (IS_ASYNC_MODE): + if IS_ASYNC_MODE: epsagon.trace.trace_factory.switch_to_async_tracer() else: epsagon.trace.trace_factory.switch_to_multiple_traces() From 49e471ecabf7700b86245a65ac1edd80709c22d0 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Thu, 14 Jul 2022 14:09:47 +0300 Subject: [PATCH 03/19] fix(fastapi): test fixes --- epsagon/trace.py | 1 + epsagon/wrappers/fastapi.py | 5 ++--- tests/conftest.py | 1 + tests/wrappers/test_fastapi_wrapper.py | 16 ++++++++-------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index b4ff2680..df0fb841 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -220,6 +220,7 @@ def switch_to_multiple_traces(self): Set the use_single_trace flag to False. :return: None """ + self.use_async_tracer = False self.use_single_trace = False def _create_new_trace(self, unique_id=None): diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index b2a0045f..1a914a84 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -305,8 +305,6 @@ def _wrap_handler(dependant, status_code): original_handler = dependant.call is_async = asyncio.iscoroutinefunction(original_handler) - print(IS_ASYNC_MODE) - if is_async: if not IS_ASYNC_MODE: # in case of not using the Env var for async endpoints @@ -377,7 +375,7 @@ def exception_handler_wrapper(original_handler): Wraps an exception handler """ is_async = asyncio.iscoroutinefunction(original_handler) - if is_async: + if is_async or IS_ASYNC_MODE: # async handlers are not supported return original_handler @@ -429,6 +427,7 @@ async def server_call_wrapper(wrapped, _instance, args, kwargs): unique_id=unique_id ) trace.prepare() + scope[EPSAGON_MARKER] = { SCOPE_UNIQUE_ID: unique_id, } diff --git a/tests/conftest.py b/tests/conftest.py index df3ab452..926aa53e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -81,3 +81,4 @@ def reset_tracer_mode(): Resets trace factory tracer mode to a single trace. """ epsagon.trace_factory.use_single_trace = True + epsagon.use_async_tracer = False diff --git a/tests/wrappers/test_fastapi_wrapper.py b/tests/wrappers/test_fastapi_wrapper.py index cb0cd177..b4d4262f 100644 --- a/tests/wrappers/test_fastapi_wrapper.py +++ b/tests/wrappers/test_fastapi_wrapper.py @@ -102,13 +102,14 @@ def handle_given_request(request: Request): async def async_handle_given_request(request: Request): assert request.method == 'POST' - loop = None - try: - loop = asyncio.new_event_loop() - assert loop.run_until_complete(request.json()) == TEST_POST_DATA - finally: - if loop: - loop.close() + assert await request.json() == TEST_POST_DATA + # # loop = None + # try: + # # loop = asyncio.new_event_loop() + + # finally: + # if loop: + # loop.close() return _get_response(RETURN_VALUE) def handle_a(): @@ -347,7 +348,6 @@ async def test_fastapi_custom_response(trace_transport, fastapi_app): async with AsyncClient(app=fastapi_app, base_url="http://test") as ac: response = await ac.get(request_path) response_data = response.json() - print('*********************') runner = trace_transport.last_trace.events[0] assert isinstance(runner, FastapiRunner) assert runner.resource['name'].startswith('127.0.0.1') From 59b3722375b25319f5413c185fcce67ec321df8a Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Thu, 14 Jul 2022 14:38:50 +0300 Subject: [PATCH 04/19] fix(fastapi): lint disable for global statement --- epsagon/wrappers/fastapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index 1a914a84..d88bb712 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -33,7 +33,7 @@ IS_ASYNC_MODE = False def _initialize_async_mode(mode): - global IS_ASYNC_MODE + global IS_ASYNC_MODE # pylint: disable=global-statement IS_ASYNC_MODE = mode _initialize_async_mode(os.getenv( @@ -427,7 +427,7 @@ async def server_call_wrapper(wrapped, _instance, args, kwargs): unique_id=unique_id ) trace.prepare() - + scope[EPSAGON_MARKER] = { SCOPE_UNIQUE_ID: unique_id, } From b7c36142401005d47d137fb8abffea6c37a83224 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 17 Jul 2022 15:57:00 +0300 Subject: [PATCH 05/19] fix(fastapi): fix get_event_loop to support multiple python versions --- epsagon/trace.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index df0fb841..1dbc64a7 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -36,6 +36,7 @@ DEFAULT_MAX_TRACE_SIZE_BYTES = 64 * (2 ** 10) MAX_METADATA_FIELD_SIZE_LIMIT = 1024 * 3 FAILED_TO_SERIALIZE_MESSAGE = 'Failed to serialize returned object to JSON' +IS_PY_VERSION_ABOVE_3_6 = sys.version_info[0] == 3 and sys.version_info[1] >= 7 # pylint: disable=invalid-name @@ -257,12 +258,22 @@ def _get_current_task(): """ # Dynamic import since this is only valid in Python3+ asyncio = __import__('asyncio') - if not asyncio.get_event_loop(): - return None - try: - return asyncio.Task.current_task() - except RuntimeError: - return None + + #check if python version 3.7 and above + if IS_PY_VERSION_ABOVE_3_6: + try: + if not asyncio.get_event_loop(): + return None + return asyncio.Task.current_task() + except RuntimeError: + return None + else: + try: + if not asyncio._get_event_loop():# pylint: disable=W0212 + return None + return asyncio.Task.current_task() + except RuntimeError: + return None def _get_tracer_async_mode(self, should_create): """ @@ -408,7 +419,7 @@ def get_thread_local_unique_id(self, unique_id=None): """ if self.is_async_tracer(): return self.local_thread_to_unique_id.get( - type(self)._get_current_task(), unique_id + type(self)._get_current_task(), unique_id ) return self.local_thread_to_unique_id.get( @@ -428,8 +439,8 @@ def set_thread_local_unique_id(self, unique_id=None): ) if self.is_async_tracer(): - self.local_thread_to_unique_id[type(self). - _get_current_task()] = unique_id + self.local_thread_to_unique_id[ + type(self)._get_current_task()] = unique_id else: self.local_thread_to_unique_id[get_thread_id()] = unique_id return unique_id From cb30ec98c0fa6d7feb1021f7c98a6fc27304774e Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 17 Jul 2022 16:29:12 +0300 Subject: [PATCH 06/19] fix(fastapi): fix current_task to support multiple python versions --- epsagon/trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 1dbc64a7..44436b24 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -264,7 +264,7 @@ def _get_current_task(): try: if not asyncio.get_event_loop(): return None - return asyncio.Task.current_task() + return asyncio.current_task() except RuntimeError: return None else: From 04c53da6ffffd6a1bf279b4d3154ae9e339d3b4b Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Mon, 18 Jul 2022 12:56:13 +0300 Subject: [PATCH 07/19] fix(fastapi): fix get event loop for python 3_6 --- epsagon/trace.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 44436b24..86ca1eb2 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -265,14 +265,16 @@ def _get_current_task(): if not asyncio.get_event_loop(): return None return asyncio.current_task() - except RuntimeError: + except Exception: # pylint: disable=broad-except: return None else: try: - if not asyncio._get_event_loop():# pylint: disable=W0212 + # pylint: disable=W0212 + if not asyncio.events._get_running_loop(): return None + return asyncio.Task.current_task() - except RuntimeError: + except Exception: # pylint: disable=broad-except: return None def _get_tracer_async_mode(self, should_create): From 089bc6a5395280e05904e590c839853be854a19a Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Mon, 18 Jul 2022 13:24:48 +0300 Subject: [PATCH 08/19] fix(fastapi): fix lint for python 2_7 --- epsagon/trace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 86ca1eb2..32a1f786 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -265,7 +265,7 @@ def _get_current_task(): if not asyncio.get_event_loop(): return None return asyncio.current_task() - except Exception: # pylint: disable=broad-except: + except Exception: # pylint: disable=broad-except return None else: try: @@ -274,7 +274,7 @@ def _get_current_task(): return None return asyncio.Task.current_task() - except Exception: # pylint: disable=broad-except: + except Exception: # pylint: disable=broad-except return None def _get_tracer_async_mode(self, should_create): From 3cf5e64aeccb97702382068cef3ac23a08c6e621 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 19 Jul 2022 11:15:38 +0300 Subject: [PATCH 09/19] fix(fastapi): fix CR comments --- epsagon/trace.py | 20 ++++++++++---------- epsagon/wrappers/fastapi.py | 15 ++++----------- tests/wrappers/test_fastapi_wrapper.py | 7 ------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 32a1f786..a3d1ea24 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -36,7 +36,7 @@ DEFAULT_MAX_TRACE_SIZE_BYTES = 64 * (2 ** 10) MAX_METADATA_FIELD_SIZE_LIMIT = 1024 * 3 FAILED_TO_SERIALIZE_MESSAGE = 'Failed to serialize returned object to JSON' -IS_PY_VERSION_ABOVE_3_6 = sys.version_info[0] == 3 and sys.version_info[1] >= 7 +IS_PY_VERSION_ABOVE_3_6 = sys.version_info[0] == 3 and sys.version_info[1] > 6 # pylint: disable=invalid-name @@ -261,19 +261,22 @@ def _get_current_task(): #check if python version 3.7 and above if IS_PY_VERSION_ABOVE_3_6: + get_loop = asyncio.get_event_loop + get_task = asyncio.current_task try: - if not asyncio.get_event_loop(): + if not get_loop(): return None - return asyncio.current_task() + return get_task() except Exception: # pylint: disable=broad-except return None else: try: + get_loop = asyncio.events._get_running_loop + get_task = asyncio.events._get_running_loop # pylint: disable=W0212 - if not asyncio.events._get_running_loop(): + if not get_loop(): return None - - return asyncio.Task.current_task() + return get_task() except Exception: # pylint: disable=broad-except return None @@ -398,10 +401,7 @@ def pop_trace(self, trace=None): """ with self.LOCK: if self.use_async_tracer: - trace = self._pop_trace_async_mode() - if trace: - # async tracer found - return trace + return self._pop_trace_async_mode() if self.traces: trace = self.traces.pop(self.get_trace_identifier(trace), None) if not self.traces: diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index d88bb712..872cf0b3 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -305,12 +305,6 @@ def _wrap_handler(dependant, status_code): original_handler = dependant.call is_async = asyncio.iscoroutinefunction(original_handler) - if is_async: - if not IS_ASYNC_MODE: - # in case of not using the Env var for async endpoints - return - - original_request_param_name = dependant.request_param_name if not original_request_param_name: dependant.request_param_name = EPSAGON_REQUEST_PARAM_NAME @@ -337,13 +331,12 @@ async def async_wrapped_handler(*args, **kwargs): original_handler, request, status_code, args, kwargs ) - if is_async: - if IS_ASYNC_MODE: - # async endpoints - dependant.call = async_wrapped_handler + if is_async and IS_ASYNC_MODE: + # async endpoints + dependant.call = async_wrapped_handler else: - if not IS_ASYNC_MODE: + if not is_async and not IS_ASYNC_MODE: dependant.call = wrapped_handler diff --git a/tests/wrappers/test_fastapi_wrapper.py b/tests/wrappers/test_fastapi_wrapper.py index b4d4262f..4abf52dd 100644 --- a/tests/wrappers/test_fastapi_wrapper.py +++ b/tests/wrappers/test_fastapi_wrapper.py @@ -103,13 +103,6 @@ def handle_given_request(request: Request): async def async_handle_given_request(request: Request): assert request.method == 'POST' assert await request.json() == TEST_POST_DATA - # # loop = None - # try: - # # loop = asyncio.new_event_loop() - - # finally: - # if loop: - # loop.close() return _get_response(RETURN_VALUE) def handle_a(): From 4e8727bb3e1e21be3da001aa0b1c679a045dff51 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 19 Jul 2022 11:26:40 +0300 Subject: [PATCH 10/19] fix(fastapi): add to README and lint fix --- README.md | 2 ++ epsagon/trace.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be0029a9..7dd3a0ac 100644 --- a/README.md +++ b/README.md @@ -482,6 +482,8 @@ Advanced options can be configured as a parameter to the init() method or as env |- |EPSAGON_LAMBDA_TIMEOUT_THRESHOLD_MS |Integer|`200` |The threshold in millieseconds to send the trace before a Lambda timeout occurs | |- |EPSAGON_PAYLOADS_TO_IGNORE |List |- |Array of dictionaries to not instrument. Example: `'[{"source": "serverless-plugin-warmup"}]'` | |- |EPSAGON_REMOVE_EXCEPTION_FRAMES|Boolean|`False` |Disable the automatic capture of exception frames data (Python 3) | +|- |EPSAGON_FASTAPI_ASYNC_MODE|Boolean|`False` |Enable capturing of Fast API async endpoint handlers calls(Python 3) | + diff --git a/epsagon/trace.py b/epsagon/trace.py index a3d1ea24..25c722f7 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -271,9 +271,9 @@ def _get_current_task(): return None else: try: + # pylint: disable=W0212 get_loop = asyncio.events._get_running_loop get_task = asyncio.events._get_running_loop - # pylint: disable=W0212 if not get_loop(): return None return get_task() From f27ceb2d48d12f75d5468b33d62fee6c643549c4 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 19 Jul 2022 16:34:55 +0300 Subject: [PATCH 11/19] fix(fastapi): fix CR-2 comments --- epsagon/trace.py | 26 ++++++++++---------------- epsagon/wrappers/fastapi.py | 5 ++--- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/epsagon/trace.py b/epsagon/trace.py index 25c722f7..c7ae0eb8 100755 --- a/epsagon/trace.py +++ b/epsagon/trace.py @@ -36,6 +36,7 @@ DEFAULT_MAX_TRACE_SIZE_BYTES = 64 * (2 ** 10) MAX_METADATA_FIELD_SIZE_LIMIT = 1024 * 3 FAILED_TO_SERIALIZE_MESSAGE = 'Failed to serialize returned object to JSON' +# check if python version is 3.7 and above IS_PY_VERSION_ABOVE_3_6 = sys.version_info[0] == 3 and sys.version_info[1] > 6 @@ -261,24 +262,17 @@ def _get_current_task(): #check if python version 3.7 and above if IS_PY_VERSION_ABOVE_3_6: - get_loop = asyncio.get_event_loop - get_task = asyncio.current_task - try: - if not get_loop(): - return None - return get_task() - except Exception: # pylint: disable=broad-except - return None + get_event_loop = asyncio.get_event_loop + get_current_task = asyncio.current_task else: - try: - # pylint: disable=W0212 - get_loop = asyncio.events._get_running_loop - get_task = asyncio.events._get_running_loop - if not get_loop(): - return None - return get_task() - except Exception: # pylint: disable=broad-except + get_event_loop = asyncio.events._get_running_loop # pylint: disable=W0212 + get_current_task = asyncio.events._get_running_loop # pylint: disable=W0212 + try: + if not get_event_loop(): return None + return get_current_task() + except Exception: # pylint: disable=broad-except + return None def _get_tracer_async_mode(self, should_create): """ diff --git a/epsagon/wrappers/fastapi.py b/epsagon/wrappers/fastapi.py index 872cf0b3..9e5df2a5 100644 --- a/epsagon/wrappers/fastapi.py +++ b/epsagon/wrappers/fastapi.py @@ -335,9 +335,8 @@ async def async_wrapped_handler(*args, **kwargs): # async endpoints dependant.call = async_wrapped_handler - else: - if not is_async and not IS_ASYNC_MODE: - dependant.call = wrapped_handler + elif not is_async and not IS_ASYNC_MODE: + dependant.call = wrapped_handler def route_class_wrapper(wrapped, instance, args, kwargs): From 14595ecd91235c2743d132d221b8bc1be6fbf740 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 21 Aug 2022 14:53:11 +0300 Subject: [PATCH 12/19] add support for aws qldb driver --- epsagon/events/pyqldb.py | 100 ++++++++++++++++++++++++++++++++++++++ epsagon/modules/pyqldb.py | 31 ++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 epsagon/events/pyqldb.py create mode 100644 epsagon/modules/pyqldb.py diff --git a/epsagon/events/pyqldb.py b/epsagon/events/pyqldb.py new file mode 100644 index 00000000..840b1893 --- /dev/null +++ b/epsagon/events/pyqldb.py @@ -0,0 +1,100 @@ +""" +pymongo events module. +""" + +from __future__ import absolute_import +from uuid import uuid4 +import traceback + +from epsagon.utils import add_data_if_needed +from ..event import BaseEvent +from ..trace import trace_factory + + +class QldbEvent(BaseEvent): + """ + Represents base pymongo event. + """ + + ORIGIN = 'qldb' + RESOURCE_TYPE = 'qldb' + + #pylint: disable=W0613 + def __init__(self, wrapped, instance, args, kwargs, start_time, response, + exception): + """ + Initialize. + :param wrapped: wrapt's wrapped + :param instance: wrapt's instance + :param args: wrapt's args + :param kwargs: wrapt's kwargs + :param start_time: Start timestamp (epoch) + :param response: response data + :param exception: Exception (if happened) + """ + super(QldbEvent, self).__init__(start_time) + + self.event_id = 'qldb-{}'.format(str(uuid4())) + self.resource['name'] = \ + getattr(instance.__getattribute__('_transaction')._session, + '_ledger_name') + self.resource['operation'] = wrapped.__func__.__name__ + + self.resource['metadata']['query'] = args[0] + add_data_if_needed(self.resource['metadata'], 'parameters', + [args[i] for i in range(1, len(args))]) + + add_data_if_needed(self.resource['metadata'], 'transaction_id', + getattr(instance, 'transaction_id')) + + if response is not None: + self.update_response(response) + + if exception is not None: + self.set_exception(exception, traceback.format_exc()) + + + def update_response(self, response): + """ + Adds response data to event. + :param response: Response from botocore + :return: None + """ + + self.resource['metadata']['Results'] = [str(x) for x in response] + self.resource['metadata']['response.consumed_information'] = \ + response.get_consumed_ios() + self.resource['metadata']['response.timing_information'] = \ + response.get_timing_information() + + + +class QldbEventFactory(object): + """ + Factory class, generates Qldb event. + """ + + @staticmethod + def create_event(wrapped, instance, args, kwargs, start_time, response, + exception): + """ + Create a Qldb event. + :param wrapped: + :param instance: + :param args: + :param kwargs: + :param start_time: + :param response: + :param exception: + :return: + """ + event = QldbEvent( + wrapped, + instance, + args, + kwargs, + start_time, + response, + exception + ) + trace_factory.add_event(event) diff --git a/epsagon/modules/pyqldb.py b/epsagon/modules/pyqldb.py new file mode 100644 index 00000000..52dcc8ec --- /dev/null +++ b/epsagon/modules/pyqldb.py @@ -0,0 +1,31 @@ +""" +pymysql patcher module +""" +from __future__ import absolute_import +import wrapt +from epsagon.modules.general_wrapper import wrapper +from ..events.pyqldb import QldbEventFactory + + +def _wrapper(wrapped, instance, args, kwargs): + """ + General wrapper for PynamoDB instrumentation. + :param wrapped: wrapt's wrapped + :param instance: wrapt's instance + :param args: wrapt's args + :param kwargs: wrapt's kwargs + :return: None + """ + return wrapper(QldbEventFactory, wrapped, instance, args, kwargs) + + +def patch(): + """ + patch module. + :return: None + """ + wrapt.wrap_function_wrapper( + 'pyqldb.execution.executor', + 'Executor.execute_statement', + _wrapper + ) From 41da4189d58bf686603841a5eb986e544cd51a62 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 21 Aug 2022 15:21:00 +0300 Subject: [PATCH 13/19] fix lint --- epsagon/events/pyqldb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epsagon/events/pyqldb.py b/epsagon/events/pyqldb.py index 840b1893..e0032c4b 100644 --- a/epsagon/events/pyqldb.py +++ b/epsagon/events/pyqldb.py @@ -36,7 +36,7 @@ def __init__(self, wrapped, instance, args, kwargs, start_time, response, self.event_id = 'qldb-{}'.format(str(uuid4())) self.resource['name'] = \ - getattr(instance.__getattribute__('_transaction')._session, + getattr(instance.__getattribute__('_transaction')._session,# pylint: disable=W0212 '_ledger_name') self.resource['operation'] = wrapped.__func__.__name__ From 016575d839b53650cd12f543b4c7ea62a0a10af7 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 21 Aug 2022 15:47:00 +0300 Subject: [PATCH 14/19] add python 3.6 to be removed from acceptance tests --- scripts/run_acceptance_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index 9c11ea45..979b1996 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,7 +2,7 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION != "2.7" ]; then +elif [ $TRAVIS_PYTHON_VERSION not in ("2.7", "3.6") ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi From 4008566129e988ef7862003c060d27ee74ccdf57 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Sun, 21 Aug 2022 15:54:17 +0300 Subject: [PATCH 15/19] return acceptance tests --- scripts/run_acceptance_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index 979b1996..9c11ea45 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,7 +2,7 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION not in ("2.7", "3.6") ]; then +elif [ $TRAVIS_PYTHON_VERSION != "2.7" ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi From 4459b8223d6a99ca0d5acb1e9480207be816167e Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 23 Aug 2022 14:02:57 +0300 Subject: [PATCH 16/19] fixes for pr review and run acceptance tests --- epsagon/events/pyqldb.py | 4 ++-- epsagon/modules/pyqldb.py | 4 ++-- scripts/run_acceptance_tests.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/epsagon/events/pyqldb.py b/epsagon/events/pyqldb.py index e0032c4b..9cbf28e1 100644 --- a/epsagon/events/pyqldb.py +++ b/epsagon/events/pyqldb.py @@ -1,5 +1,5 @@ """ -pymongo events module. +pyqldb events module. """ from __future__ import absolute_import @@ -13,7 +13,7 @@ class QldbEvent(BaseEvent): """ - Represents base pymongo event. + Represents base pyqldb event. """ ORIGIN = 'qldb' diff --git a/epsagon/modules/pyqldb.py b/epsagon/modules/pyqldb.py index 52dcc8ec..24dba04c 100644 --- a/epsagon/modules/pyqldb.py +++ b/epsagon/modules/pyqldb.py @@ -1,5 +1,5 @@ """ -pymysql patcher module +pyqldb patcher module """ from __future__ import absolute_import import wrapt @@ -9,7 +9,7 @@ def _wrapper(wrapped, instance, args, kwargs): """ - General wrapper for PynamoDB instrumentation. + General wrapper for Pyqldb instrumentation. :param wrapped: wrapt's wrapped :param instance: wrapt's instance :param args: wrapt's args diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index 9c11ea45..bffed9bd 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,7 +2,7 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION != "2.7" ]; then +elif [ $TRAVIS_PYTHON_VERSION != "2.7" ] || [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi From 97758958660628e039268293737f3c007e1ece41 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 23 Aug 2022 14:21:03 +0300 Subject: [PATCH 17/19] ignore python version 3_6 in run acceptance tests --- scripts/run_acceptance_tests.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index bffed9bd..b43c6c1c 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,7 +2,10 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION != "2.7" ] || [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then +elif [ $TRAVIS_PYTHON_VERSION != "2.7" ]; then + npm install && export PATH=$(pwd)/node_modules/.bin:$PATH + ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION +elif [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi From dd600d9030cddf5b14aa0ffd83ab6e10408b9812 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 23 Aug 2022 14:47:54 +0300 Subject: [PATCH 18/19] add ignore python version 3_6 in run acceptance tests --- scripts/run_acceptance_tests.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index b43c6c1c..bffed9bd 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,10 +2,7 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION != "2.7" ]; then - npm install && export PATH=$(pwd)/node_modules/.bin:$PATH - ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION -elif [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then +elif [ $TRAVIS_PYTHON_VERSION != "2.7" ] || [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi From 217c906f115c9b1b537e4a9474933588be4423d4 Mon Sep 17 00:00:00 2001 From: sagivr2020 Date: Tue, 23 Aug 2022 15:09:04 +0300 Subject: [PATCH 19/19] ignore python version 3_6 and 2.7 in run acceptance tests --- scripts/run_acceptance_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_acceptance_tests.sh b/scripts/run_acceptance_tests.sh index bffed9bd..d85a2db9 100755 --- a/scripts/run_acceptance_tests.sh +++ b/scripts/run_acceptance_tests.sh @@ -2,7 +2,7 @@ if [ -z $AWS_ACCESS_KEY_ID ] || [ -z $AWS_SECRET_ACCESS_KEY ]; then echo "AWS credentials must be set in order to run acceptance tests" exit 1 -elif [ $TRAVIS_PYTHON_VERSION != "2.7" ] || [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then +elif [ $TRAVIS_PYTHON_VERSION != "2.7" ] && [ $TRAVIS_PYTHON_VERSION != "3.6" ]; then npm install && export PATH=$(pwd)/node_modules/.bin:$PATH ./acceptance/run.sh $TRAVIS_BUILD_NUMBER $TRAVIS_PYTHON_VERSION fi