Skip to content
This repository was archived by the owner on Jun 13, 2023. It is now read-only.

Commit 602f77e

Browse files
authored
feat(handler.py): support general wrapper (#21)
1 parent 7365c6a commit 602f77e

19 files changed

+304
-167
lines changed

.pylintrc

+112-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,122 @@
1-
# Based on:
2-
# https://github.com/kpreid/shinysdr/blob/master/pylintrc
1+
# Based on https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/ci_build/pylintrc
32

43
[MASTER]
5-
# XXX should be changed to your project folder
6-
# see http://stackoverflow.com/a/37238692/3828891 for explanation
7-
init-hook='base_dir="epsagon"; import sys,os,re; _re=re.search(r".+\/" + base_dir, os.getcwd()); project_dir = _re.group() if _re else os.path.join(os.getcwd(), base_dir); sys.path.append(project_dir)'
4+
5+
# Profiled execution.
6+
profile=no
7+
8+
# Don't pickle collected data for later comparisons.
9+
persistent=no
10+
11+
# List of plugins (as comma separated values of python modules names) to load,
12+
# usually to register additional checkers.
13+
load-plugins=pylint_quotes
14+
815

916
[MESSAGES CONTROL]
10-
# Find available symbolic names in:
11-
# https://docs.pylint.org/features.html
12-
disable=duplicate-code,too-few-public-methods,too-many-arguments,fixme,too-many-instance-attributes,bad-continuation,useless-object-inheritance
17+
18+
disable=duplicate-code,too-few-public-methods,too-many-arguments,fixme,too-many-instance-attributes,bad-continuation,too-many-locals,logging-format-interpolation,too-many-branches,useless-object-inheritance,assignment-from-no-return,useless-import-alias
19+
20+
# Ignore no member when source is unavailable
21+
extension-pkg-whitelist=ujson
22+
23+
[REPORT]
24+
25+
msg-template='{abspath}:{line}: [{msg_id}({symbol}) {obj}] {msg}'
26+
27+
output-format=parseable
28+
29+
# Tells whether to display a full report or only the messages
30+
reports=no
31+
32+
# Python expression which should return a note less than 10 (10 is the highest
33+
# note). You have access to the variables errors warning, statement which
34+
# respectively contain the number of errors / warnings messages and the total
35+
# number of statements analyzed. This is used by the global evaluation report
36+
# (RP0004).
37+
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
38+
39+
# Add a comment according to your evaluation note. This is used by the global
40+
# evaluation report (RP0004).
41+
comment=no
42+
43+
44+
[BASIC]
45+
46+
# Required attributes for module, separated by a comma
47+
required-attributes=
48+
49+
# List of builtins function names that should not be used, separated by a comma
50+
bad-functions=apply,input,reduce
51+
52+
# Regular expression which should only match correct module names
53+
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
54+
55+
# Regular expression which should only match correct module level names
56+
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
57+
58+
# Regular expression which should only match correct class names
59+
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
60+
61+
# Regular expression which should only match correct function names
62+
function-rgx=^(?:(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
63+
64+
# Regular expression which should only match correct method names
65+
method-rgx=^(?:(?P<exempt>__[a-z0-9_]+__|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
66+
67+
# Regular expression which should only match correct instance attribute names
68+
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
69+
70+
# Regular expression which should only match correct argument names
71+
argument-rgx=^[_a-z][a-z0-9_]*$
72+
73+
# Regular expression which should only match correct variable names
74+
variable-rgx=^[a-z][a-z0-9_]*$
75+
76+
# Regular expression which should only match correct attribute names in class
77+
# bodies
78+
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
79+
80+
# Regular expression which should only match correct list comprehension /
81+
# generator expression variable names
82+
inlinevar-rgx=^[a-z][a-z0-9_]*$
83+
84+
# Good variable names which should always be accepted, separated by a comma
85+
good-names=main,_
86+
87+
# Regular expression which should only match function or class names that do
88+
# not require a docstring.
89+
no-docstring-rgx=(__.*__|main)
90+
91+
# Minimum line length for functions/classes that require docstrings, shorter
92+
# ones are exempt.
93+
docstring-min-length=10
94+
1395

1496
[FORMAT]
1597
# Maximum number of characters on a single line.
1698
max-line-length=80
1799

100+
18101
# Maximum number of lines in a module
19-
max-module-lines=500
102+
max-module-lines=500
103+
104+
# Allow the body of an if to be on the same line as the test if there is no
105+
# else.
106+
single-line-if-stmt=y
107+
108+
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
109+
# tab).
110+
indent-string=' '
111+
112+
# Set the linting for string quotes
113+
string-quote=single
114+
triple-quote=double
115+
docstring-quote=double
116+
117+
118+
[TOKENS]
119+
120+
# Number of spaces of indent required when the last token on the preceding line
121+
# is an open (, [, or {.
122+
indent-after-paren=4

epsagon/__init__.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
import os
77
from .utils import init
88
from .patcher import patch_all
9-
from .constants import __version__
9+
from .constants import __version__, EPSAGON_HANDLER
1010
from .trace import tracer
1111

12+
if os.getenv(EPSAGON_HANDLER):
13+
from .handler import wrapper
14+
1215

1316
def dummy_wrapper(func):
1417
"""
@@ -19,7 +22,7 @@ def dummy_wrapper(func):
1922
return func
2023

2124

22-
if os.environ.get('DISABLE_EPSAGON') == 'TRUE':
25+
if os.getenv('DISABLE_EPSAGON') == 'TRUE':
2326
os.environ['DISABLE_EPSAGON_PATCH'] = 'TRUE'
2427
lambda_wrapper = dummy_wrapper # pylint: disable=C0103
2528
step_lambda_wrapper = dummy_wrapper # pylint: disable=C0103
@@ -46,9 +49,9 @@ def dummy_wrapper(func):
4649
label = tracer.add_label
4750

4851
__all__ = ['lambda_wrapper', 'azure_wrapper', 'python_wrapper', 'init',
49-
'step_lambda_wrapper', 'flask_wrapper']
52+
'step_lambda_wrapper', 'flask_wrapper', 'wrapper']
5053

5154

5255
# The modules are patched only if DISABLE_EPSAGON_PATCH variable is NOT 'TRUE'
53-
if os.environ.get('DISABLE_EPSAGON_PATCH') != 'TRUE':
56+
if os.getenv('DISABLE_EPSAGON_PATCH') != 'TRUE':
5457
patch_all()

epsagon/constants.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
__version__ = '1.0.27'
66

77
DEFAULT_REGION = 'us-east-1'
8-
REGION = os.environ.get('AWS_REGION', DEFAULT_REGION)
8+
REGION = os.getenv('AWS_REGION', DEFAULT_REGION)
99

10-
TRACE_COLLECTOR_URL = "{protocol}{region}.tc.epsagon.com"
10+
TRACE_COLLECTOR_URL = '{protocol}{region}.tc.epsagon.com'
1111
COLD_START = True
1212

13+
# Customer original handler.
14+
EPSAGON_HANDLER = 'EPSAGON_HANDLER'
15+
1316
# How long we try to send traces in seconds.
1417
SEND_TIMEOUT = 0.5
1518

epsagon/events/dbapi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def parse_dsn(dsn):
1717
:return:
1818
"""
1919
return dict(
20-
attribute.split("=") for attribute in dsn.split()
21-
if "=" in attribute
20+
attribute.split('=') for attribute in dsn.split()
21+
if '=' in attribute
2222
)
2323

2424
from ..trace import tracer

epsagon/events/requests.py

+1-97
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from urlparse import urlparse
1010
import traceback
1111
from uuid import uuid4
12-
from six.moves import urllib
1312

1413
from epsagon.utils import add_data_if_needed
1514
from ..trace import tracer
@@ -104,119 +103,24 @@ def update_response(self, response):
104103
self.set_error()
105104

106105

107-
class RequestsAuth0Event(RequestsEvent):
108-
"""
109-
Represents auth0 requests event.
110-
"""
111-
112-
RESOURCE_TYPE = 'auth0'
113-
API_TAG = '/api/v2/'
114-
115-
def __init__(self, wrapped, instance, args, kwargs, start_time, response,
116-
exception):
117-
"""
118-
Initialize.
119-
:param wrapped: wrapt's wrapped
120-
:param instance: wrapt's instance
121-
:param args: wrapt's args
122-
:param kwargs: wrapt's kwargs
123-
:param start_time: Start timestamp (epoch)
124-
:param response: response data
125-
:param exception: Exception (if happened)
126-
"""
127-
128-
super(RequestsAuth0Event, self).__init__(
129-
wrapped,
130-
instance,
131-
args,
132-
kwargs,
133-
start_time,
134-
response,
135-
exception
136-
)
137-
138-
prepared_request = args[0]
139-
url = prepared_request.path_url
140-
self.resource['metadata']['endpoint'] = \
141-
url[url.find(self.API_TAG) + len(self.API_TAG):]
142-
143-
144-
class RequestsTwilioEvent(RequestsEvent):
145-
"""
146-
Represents Twilio requests event.
147-
"""
148-
149-
RESOURCE_TYPE = 'twilio'
150-
151-
def __init__(self, wrapped, instance, args, kwargs, start_time, response,
152-
exception):
153-
"""
154-
Initialize.
155-
:param wrapped: wrapt's wrapped
156-
:param instance: wrapt's instance
157-
:param args: wrapt's args
158-
:param kwargs: wrapt's kwargs
159-
:param start_time: Start timestamp (epoch)
160-
:param response: response data
161-
:param exception: Exception (if happened)
162-
"""
163-
164-
super(RequestsTwilioEvent, self).__init__(
165-
wrapped,
166-
instance,
167-
args,
168-
kwargs,
169-
start_time,
170-
response,
171-
exception
172-
)
173-
174-
prepared_request = args[0]
175-
self.resource['metadata']['endpoint'] = \
176-
prepared_request.path_url.split('/')[-1]
177-
178-
179106
class RequestsEventFactory(object):
180107
"""
181108
Factory class, generates requests event.
182109
"""
183110

184-
FACTORY = {
185-
class_obj.RESOURCE_TYPE: class_obj
186-
for class_obj in RequestsEvent.__subclasses__()
187-
}
188-
189111
@staticmethod
190112
def create_event(wrapped, instance, args, kwargs, start_time, response,
191113
exception):
192114
"""
193115
Create an event according to the given api_name.
194-
:param wrapped:
195-
:param instance:
196-
:param args:
197-
:param kwargs:
198-
:param start_time:
199-
:param response:
200-
:param exception:
201-
:return:
202116
"""
203117
prepared_request = args[0]
204118

205119
# Detect if URL is blacklisted, and ignore.
206120
if is_blacklisted_url(prepared_request.url):
207121
return
208122

209-
base_url = urllib.parse.urlparse(prepared_request.url).netloc
210-
211-
# Start with base event
212-
instance_type = RequestsEvent
213-
214-
# Look for API matching in url
215-
for api_name in RequestsEventFactory.FACTORY:
216-
if api_name in base_url.lower():
217-
instance_type = RequestsEventFactory.FACTORY[api_name]
218-
219-
event = instance_type(
123+
event = RequestsEvent(
220124
wrapped,
221125
instance,
222126
args,

epsagon/handler.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Epsagon generic wrapper used only in Lambda environments.
3+
"""
4+
5+
from .utils import init, import_original_module
6+
from .wrappers import lambda_wrapper
7+
8+
9+
def init_module():
10+
"""
11+
Initialize user's module handler.
12+
:return: wrapper handler.
13+
"""
14+
original_module, module_path, handler_name = import_original_module()
15+
try:
16+
return getattr(original_module, handler_name)
17+
except AttributeError:
18+
raise AttributeError(
19+
'No handler {} in module {}'.format(handler_name, module_path)
20+
)
21+
22+
23+
init()
24+
wrapper = lambda_wrapper(init_module())

epsagon/modules/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
if filename in IGNORE_MODULES or ext not in PYTHON_EXTENSIONS:
1616
continue
1717
try:
18-
imported = import_module(".{}".format(filename), __name__)
18+
imported = import_module('.{}'.format(filename), __name__)
1919
MODULES[filename] = imported
2020
except ImportError:
2121
pass

epsagon/runners/aws_lambda.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, start_time, context):
3535
'memory': context.memory_limit_in_mb,
3636
'aws_account': context.invoked_function_arn.split(':')[4],
3737
'cold_start': constants.COLD_START,
38-
'region': os.environ.get('AWS_REGION', ''),
38+
'region': os.getenv('AWS_REGION', ''),
3939
}
4040

4141

epsagon/runners/azure_function.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ def __init__(self, start_time):
2424

2525
super(AzureFunctionRunner, self).__init__(start_time)
2626

27-
self.event_id = os.environ.get('EXECUTION_CONTEXT_INVOCATIONID', '')
28-
self.resource['name'] = os.environ.get(
27+
self.event_id = os.getenv('EXECUTION_CONTEXT_INVOCATIONID', '')
28+
self.resource['name'] = os.getenv(
2929
'EXECUTION_CONTEXT_FUNCTIONNAME',
3030
''
3131
)
3232
self.resource['operation'] = self.OPERATION
3333

3434
self.resource['metadata'] = {
35-
'region': os.environ.get('REGION_NAME', ''),
36-
'memory': os.environ.get('WEBSITE_MEMORY_LIMIT_MB', ''),
35+
'region': os.getenv('REGION_NAME', ''),
36+
'memory': os.getenv('WEBSITE_MEMORY_LIMIT_MB', ''),
3737
}

0 commit comments

Comments
 (0)