Skip to content

Commit d65834f

Browse files
Aigars MahinovsAigars Mahinovs
authored andcommitted
Update from upstream
* Better Python 3 support * Reformatting with Black
1 parent b97a7ee commit d65834f

7 files changed

Lines changed: 109 additions & 93 deletions

File tree

dltlyse/core/analyser.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
import os.path
1010
import signal
1111
import sys
12-
1312
from contextlib import contextmanager
13+
import six
14+
15+
from dlt import dlt
1416

1517
from dltlyse.core.report import XUnitReport, Result
1618
from dltlyse.core.plugin_base import Plugin
1719

18-
from dlt import dlt
1920

21+
# pylint: disable= too-many-nested-blocks, no-member
2022

2123
logger = logging.getLogger(__name__)
2224
stdoutlogger = logging.getLogger("summary")
@@ -25,19 +27,20 @@
2527
DEFAULT_PLUGINS_DIRS = [
2628
os.path.join(os.path.dirname(__file__), "../plugins"), # installation folder
2729
# e.g. /usr/bin/pythonX.X/site-packages/dltlyse/plugins
28-
os.path.join(os.getcwd(), "plugins") # plugins folder in current working directory
30+
os.path.join(os.getcwd(), "plugins"), # plugins folder in current working directory
2931
]
3032

3133
# Traces to buffer since they might be stored before lifecycle start message
32-
buffer_matches = [{"apid": b"DA1", "ctid": b"DC1",
33-
"payload_decoded": b"[connection_info ok] connected \x00\x00\x00\x00"},
34-
{"ecuid": "XORA"}]
34+
buffer_matches = [
35+
{"apid": "DA1", "ctid": "DC1", "payload_decoded": "[connection_info ok] connected \00\00\00\00"},
36+
{"ecuid": "XORA"},
37+
]
3538
MAX_BUFFER_SIZE = 50
3639

3740
DLT_LIFECYCLE_START = {
38-
"apid": b"DLTD",
39-
"ctid": b"INTM",
40-
"payload_decoded": b"Daemon launched. Starting to output traces...",
41+
"apid": "DLTD",
42+
"ctid": "INTM",
43+
"payload_decoded": "Daemon launched. Starting to output traces...",
4144
}
4245

4346

@@ -130,7 +133,7 @@ def _scan_folder(root, plugin_classes):
130133
return
131134

132135
filenames = os.listdir(root)
133-
if '__NO_PLUGINS__' in filenames: # If the folder hasn't plugins, we skip it.
136+
if "__NO_PLUGINS__" in filenames: # If the folder hasn't plugins, we skip it.
134137
return
135138

136139
sys.path.insert(0, root)
@@ -148,8 +151,14 @@ def _scan_folder(root, plugin_classes):
148151
module = sys.modules[module_name]
149152
for class_name in dir(module):
150153
cls = getattr(module, class_name)
151-
if hasattr(cls, "__mro__") and issubclass(cls, Plugin) and not cls.__abstractmethods__:
152-
plugin_classes.append(cls)
154+
if six.PY3:
155+
if (hasattr(cls, "__mro__") and issubclass(cls, Plugin) and
156+
(not any(hasattr(getattr(cls, item), "__isabstractmethod__") and
157+
not isinstance(getattr(cls, item), property) for item in dir(cls)))):
158+
plugin_classes.append(cls)
159+
else:
160+
if hasattr(cls, "__mro__") and issubclass(cls, Plugin) and not cls.__abstractmethods__:
161+
plugin_classes.append(cls)
153162
except (ImportError, ValueError):
154163
logger.error("Could not load plugin %s\n%s", module_name, traceback.format_exc())
155164

@@ -168,6 +177,7 @@ def get_plugin_classes(plugin_dirs): # pylint: disable=too-many-locals
168177

169178
class DLTAnalyser(object):
170179
"""DLT Analyser"""
180+
171181
def __init__(self):
172182
self.plugins = []
173183
self.file_exceptions = {}
@@ -192,7 +202,7 @@ def load_plugins(self, plugin_dirs, plugins=None, exclude=None, no_default_dir=F
192202
for cls in plugin_classes:
193203
if plugins is None:
194204
if cls.manually_executed and \
195-
os.environ.get("DLTLYSE_ALL_INCLUDES_MANUAL", "false").lower() not in ('1', 'true', 'yes'):
205+
os.environ.get("DLTLYSE_ALL_INCLUDES_MANUAL", "false").lower() not in ('1', 'true', 'yes',):
196206
continue
197207
else:
198208
if not cls.get_plugin_name() in plugins:
@@ -245,9 +255,9 @@ def process_message(self, message):
245255
"""Pass on the message to plugins that need it"""
246256
for plugin in self.plugins:
247257
if plugin.message_filters == "all" or \
248-
(message.apid, message.ctid) in plugin.message_filters or \
249-
("", message.ctid) in plugin.message_filters or \
250-
(message.apid, "") in plugin.message_filters:
258+
(message.apid, message.ctid) in plugin.message_filters or \
259+
("", message.ctid) in plugin.message_filters or \
260+
(message.apid, "") in plugin.message_filters:
251261
with handle_plugin_exceptions(plugin, "calling"):
252262
plugin(message)
253263

@@ -267,7 +277,7 @@ def run_analyse(self, traces, xunit, no_sort, is_live, testsuite_name="dltlyse")
267277
filters = self.get_filters()
268278
# add filter for lifecycle start message in case it is missing
269279
# filters == None means no filtering is done at all
270-
flt = (DLT_LIFECYCLE_START["apid"], DLT_LIFECYCLE_START["ctid"])
280+
flt = (DLT_LIFECYCLE_START["apid"].encode("utf-8"), DLT_LIFECYCLE_START["ctid"].encode("utf-8"))
271281
if filters and flt not in filters:
272282
filters.append(flt)
273283

@@ -362,23 +372,23 @@ def generate_reports(self, xunit, testsuite_name):
362372
output = "Report for file"
363373
if filename in self.file_exceptions:
364374
stdoutlogger.debug(self.file_exceptions[filename])
365-
stdoutlogger.info(output + " %s ... = failed", filename)
375+
stdoutlogger.info("%s %s ... = failed", output, filename)
366376
file_results.append(Result(
367377
classname="DLTAnalyser",
368378
testname="File Sanity Checks During Execution",
369379
state="error",
370380
stdout=self.file_exceptions[filename],
371381
message=self.file_exceptions[filename]
372-
))
382+
))
373383
else:
374-
stdoutlogger.info(output + " %s ... = passed", filename)
384+
stdoutlogger.info("%s %s ... = passed", output, filename)
375385
file_results.append(Result(
376386
classname="DLTAnalyser",
377387
testname="File Sanity Checks During Execution",
378388
state="success",
379389
stdout="File Parsed Successfully",
380390
message="File Parsed Successfully"
381-
))
391+
))
382392

383393
xreport.add_results(file_results)
384394

dltlyse/core/plugin_base.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99

1010
from abc import ABCMeta, abstractmethod
1111
from collections import defaultdict
12+
from six import string_types
13+
1214
from dltlyse.core.report import Result
1315
from dltlyse.core.utils import round_float
1416

17+
# pylint: disable= unsupported-membership-test
18+
1519
EXTRACT_DIR = "extracted_files"
1620
logger = logging.getLogger(__name__)
1721

@@ -66,9 +70,7 @@ def add_result(self, **kwargs):
6670
except AttributeError:
6771
testname = ""
6872
kwargs.setdefault("testname", testname)
69-
self.__results.append(Result(
70-
**kwargs
71-
))
73+
self.__results.append(Result(**kwargs))
7274

7375
def add_attachments(self, attachments):
7476
"""Adds attachments to the last result, creating a result if none exist"""
@@ -118,7 +120,7 @@ class CSVPlugin(Plugin): # pylint: disable=abstract-method
118120

119121
# If you have only one file you can use these two lines
120122
csv_filename = None # If you only have one file, you can use this. Set to "subdir/example.csv" in subclass
121-
csv_fields = None # If using only one file, set this to the list of column headers
123+
csv_fields = None # If using only one file, set this to the list of column headers
122124

123125
# If you want to use multiple CSV files, please use csv_filenames and provide columns per file
124126
csv_filenames = None
@@ -137,30 +139,28 @@ def __init__(self):
137139

138140
def _create_csvfile(self, filename=None):
139141
"""Create csv file and add first row with column names"""
140-
filename = filename or self.csv_filenames.keys()[0]
142+
filename = filename or list(self.csv_filenames)[0]
141143
pathname = os.path.join("extracted_files", filename)
142144
if not os.path.exists(os.path.dirname(pathname)):
143145
os.makedirs(os.path.dirname(pathname))
144146

145147
self._csv_fileobj[filename] = open(pathname, "w")
146-
self._csv[filename] = csv.writer(
147-
self._csv_fileobj[filename],
148-
)
148+
self._csv[filename] = csv.writer(self._csv_fileobj[filename])
149149
if self.csv_filenames[filename]: # Only write header line if columns are defined.
150150
self._csv[filename].writerow(self.csv_filenames[filename])
151151
else:
152152
logger.debug("No header line written to file %s", filename)
153153

154154
def writerow(self, data_row, filename=None):
155155
"""Write a row to CSV file"""
156-
filename = filename or self.csv_filenames.keys()[0]
156+
filename = filename or list(self.csv_filenames)[0]
157157
if filename not in self._csv:
158158
self._create_csvfile(filename)
159159
self._csv[filename].writerow(data_row)
160160

161161
def writerows(self, data_rows, filename=None):
162162
"""Write several rows to csv file"""
163-
filename = filename or self.csv_filenames.keys()[0]
163+
filename = filename or list(self.csv_filenames)[0]
164164
if filename not in self._csv:
165165
self._create_csvfile(filename)
166166
self._csv[filename].writerows(data_rows)
@@ -172,7 +172,7 @@ def report(self):
172172

173173
def _close_csv_file(self, filename=None):
174174
"""Close CSV file"""
175-
filename = filename or self.csv_filenames.keys()[0]
175+
filename = filename or list(self.csv_filenames)[0]
176176
if self._csv[filename]:
177177
self._csv_fileobj[filename].close()
178178

@@ -222,8 +222,9 @@ def dlt_callback(app_id=None, ctx_id=None):
222222
app_id(str): if defined, is the app_id that we want to catch.
223223
ctx_id(str): if defined, is the ctx_id that we want to catch.
224224
"""
225+
225226
def wrapper(func): # pylint: disable=missing-docstring
226-
func.filter_condition = app_id or '', ctx_id or ''
227+
func.filter_condition = app_id or "", ctx_id or ""
227228

228229
return func
229230

@@ -255,6 +256,7 @@ def gather_version_info(self, frame):
255256
Finally, it automatically sets the log level to DEBUG, and creates a logger using the class
256257
name. The logger is available as the logger member.
257258
"""
259+
258260
def __init__(self):
259261
"""Automatically sets a default for report (None -> no report) and logger."""
260262
self.collect_and_register_callbacks()
@@ -274,14 +276,14 @@ def collect_and_register_callbacks(self):
274276
self.dlt_greedy_callbacks = []
275277
for member_name in dir(self): # Scans the class members.
276278
member = getattr(self, member_name)
277-
filter_condition = getattr(member, 'filter_condition', None)
279+
filter_condition = getattr(member, "filter_condition", None)
278280
if filter_condition:
279281
if filter_condition[0] or filter_condition[1]:
280-
if self.message_filters != 'all':
282+
if self.message_filters != "all":
281283
self.message_filters.append(filter_condition) # pylint: disable=no-member
282284
self.dlt_callbacks[filter_condition].append(member)
283285
else:
284-
self.message_filters = 'all'
286+
self.message_filters = "all"
285287
self.dlt_greedy_callbacks.append(member)
286288

287289
# pylint: disable=invalid-name
@@ -298,17 +300,17 @@ def add_callback_from_template_function(self, template_function, app_id, ctx_id,
298300
"""
299301
# Data should be converted to strings, since dltlyse fails to register a filter if it's using unicode strings.
300302
app_id, ctx_id, userdata = (str(app_id), str(ctx_id),
301-
str(userdata) if isinstance(userdata, basestring) else userdata)
303+
str(userdata) if isinstance(userdata, string_types) else userdata)
302304

303305
callback = functools.partial(template_function, app_id=app_id, ctx_id=ctx_id, userdata=userdata)
304306
callback = dlt_callback(app_id, ctx_id)(callback)
305307
filter_condition = app_id, ctx_id
306308
if filter_condition[0] or filter_condition[1]:
307-
if self.message_filters != 'all':
309+
if self.message_filters != "all":
308310
self.message_filters.append(filter_condition) # pylint: disable=no-member
309311
self.dlt_callbacks[filter_condition].append(callback)
310312
else:
311-
self.message_filters = 'all'
313+
self.message_filters = "all"
312314
self.dlt_greedy_callbacks.append(callback)
313315

314316
def get_result_dir(self):
@@ -321,9 +323,9 @@ def get_result_dir(self):
321323
def report_filename(self):
322324
"""Builds & returns a standard/base filename for the report."""
323325
# Converts all uppercase letters in lowercase, pre-pending them with a '_'.
324-
report_filename = re.sub(r'([A-Z])', r'_\1', self.get_plugin_name())
326+
report_filename = re.sub(r"([A-Z])", r"_\1", self.get_plugin_name())
325327

326-
return report_filename.lower().strip('_') + '.txt'
328+
return report_filename.lower().strip("_") + ".txt"
327329

328330
def prepare_report(self):
329331
"""It's invoked just before writing the report to file, in case that some operation needs
@@ -341,9 +343,8 @@ def get_report(self):
341343
"""
342344
self.prepare_report()
343345
if self.report_output is None:
344-
return 'No report is generated!'
345-
else:
346-
return self.write_to_domain_file(self.report_filename(), str(self.report_output))
346+
return "No report is generated!"
347+
return self.write_to_domain_file(self.report_filename(), str(self.report_output))
347348

348349
def write_to_domain_file(self, filename, report):
349350
"""Write the given report to a file.
@@ -353,7 +354,7 @@ def write_to_domain_file(self, filename, report):
353354
report(str): the string with the report to be saved.
354355
"""
355356
fullpath = os.path.join(self.get_result_dir(), filename)
356-
with open(fullpath, 'wb') as report_file:
357+
with open(fullpath, "w") as report_file:
357358
report_file.write(report)
358359
self.logger.info("See %s", fullpath)
359360

dltlyse/core/report.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,41 @@
77
'<?xml version="1.0" encoding="UTF-8"?>'
88
'<testsuite name="{testsuite_name}" tests="{number_of_tests}" errors="{number_of_errors}" '
99
'failures="{number_of_failures}" skip="{number_of_skipped}">'
10-
'{testcases}'
11-
'</testsuite>'
10+
"{testcases}"
11+
"</testsuite>"
1212
)
1313

1414
xunit_tc_template = dict(
1515
error=(
1616
'<testcase classname="{classname}" name="{testname}" time="0">'
1717
'<error type="error" message="{message}"></error>'
18-
'<system-out><![CDATA[{stdout}]]></system-out>'
19-
'{attach}'
20-
'</testcase>'
18+
"<system-out><![CDATA[{stdout}]]></system-out>"
19+
"{attach}"
20+
"</testcase>"
2121
),
2222
failure=(
2323
'<testcase classname="{classname}" name="{testname}" time="0">'
2424
'<failure type="failure" message="{message}"></failure>'
25-
'<system-out><![CDATA[{stdout}]]></system-out>'
26-
'{attach}'
27-
'</testcase>'
25+
"<system-out><![CDATA[{stdout}]]></system-out>"
26+
"{attach}"
27+
"</testcase>"
2828
),
2929
skipped=(
3030
'<testcase classname="{classname}" name="{testname}" time="0">'
3131
'<skipped type="skip" message="{message}"></skipped>'
32-
'<system-out><![CDATA[{stdout}]]></system-out>'
33-
'{attach}'
34-
'</testcase>'
32+
"<system-out><![CDATA[{stdout}]]></system-out>"
33+
"{attach}"
34+
"</testcase>"
3535
),
3636
success=(
3737
'<testcase classname="{classname}" name="{testname}" time="0">'
38-
'<system-out><![CDATA[{stdout}]]></system-out>'
39-
'{attach}'
40-
'</testcase>'
38+
"<system-out><![CDATA[{stdout}]]></system-out>"
39+
"{attach}"
40+
"</testcase>"
4141
),
4242
)
4343

44-
attachment_template = ('[[ATTACHMENT|{filename}]]')
44+
attachment_template = "[[ATTACHMENT|{filename}]]"
4545

4646

4747
def xunit_render(result):
@@ -53,6 +53,7 @@ def xunit_render(result):
5353

5454
class Result(object):
5555
"""Class representing a single testcase result"""
56+
5657
def __init__(self, classname="Unknown", testname="Unknown", state="success", stdout="", stderr="", message="",
5758
attach=None):
5859
self.classname = classname
@@ -97,4 +98,4 @@ def render(self):
9798
report = xunit_template.format(**kwargs)
9899
if self.outfile:
99100
with open(self.outfile, "w") as reportfile:
100-
reportfile.write(report.encode("utf8"))
101+
reportfile.write(report)

0 commit comments

Comments
 (0)