Passed
Push — master ( 914a61...5d391a )
by srz
07:01
created

xml2file   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 236
Duplicated Lines 19.92 %

Importance

Changes 0
Metric Value
eloc 175
dl 47
loc 236
rs 8.96
c 0
b 0
f 0
wmc 43

16 Functions

Rating   Name   Duplication   Size   Complexity  
A log() 0 2 1
A fopen() 0 5 1
A clean_dir() 0 3 2
A get_user_properties() 0 16 3
A _get_user_properties() 0 6 3
A write_result() 0 18 5
A logd() 0 3 2
B parse_command_line() 47 47 1
A make_rootpath() 0 4 1
A get_properties_node() 0 6 4
A loge() 0 2 1
A make_path() 0 5 1
A logv() 0 3 2
A mkdir_p() 0 9 4
B xml2file() 0 31 6
B main() 0 18 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like xml2file often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
#
3
# xml2file.py
4
#
5
# Copyright (C) 2019, Takazumi Shirayanagi
6
# This software is released under the new BSD License,
7
# see LICENSE
8
#
9
10
import os
11
import sys
12
import errno
13
import json
14
import codecs
15
import shutil
16
import tempfile
17
import xml.etree.ElementTree as ET
18
19
from argparse import ArgumentParser
20
21
# command line option
22 View Code Duplication
def parse_command_line():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
23
    parser = ArgumentParser()
24
    parser.add_argument(
25
        '-v',
26
        '--version',
27
        action='version',
28
        version=u'%(prog)s version 0.1'
29
    )
30
    parser.add_argument(
31
        '-o',
32
        '--output',
33
        default=None,
34
        help='output file path.'
35
    )
36
    parser.add_argument(
37
        '--no-time',
38
        action='store_true',
39
        help='no output time attribute'
40
    )
41
    parser.add_argument(
42
        '--verbose',
43
        action='store_true',
44
        help='log verbose'
45
    )
46
    parser.add_argument(
47
        '--encoding',
48
        default='utf-8',
49
        help='output file encoding.'
50
    )
51
    parser.add_argument(
52
        '--clean',
53
        action='store_true',
54
        help='clean output directory (before)'
55
    )
56
    parser.add_argument(
57
        '--debug',
58
        action='store_true',
59
        help='log debug'
60
    )
61
    parser.add_argument(
62
        'file',
63
        metavar='FILE',
64
        nargs='+',
65
        help='test result xml files'
66
    )
67
    options = parser.parse_args()
68
    return options
69
70
cmdline_options = None
71
72
73
def log(msg):
74
    print(msg)
75
76
77
def logv(msg):
78
    if cmdline_options.verbose:
79
        print(msg)
80
81
82
def logd(msg):
83
    if cmdline_options.debug:
84
        print(msg)
85
86
87
def loge(msg):
88
    sys.stderr.write(msg + "\n")
89
90
91
def mkdir_p(path):
92
    try:
93
        os.makedirs(path)
94
    except OSError as exc:  # Python >2.5
95
        if exc.errno == errno.EEXIST and os.path.isdir(path):
96
            pass
97
        else:
98
            loge('failed mkdirs: ' + path)
99
            raise
100
101
102
def clean_dir(path):
103
    if os.path.exists(path):
104
        shutil.rmtree(path)
105
106
107
def fopen(path):
108
    dir = os.path.dirname(path)
109
    mkdir_p(dir)
110
    f = codecs.open(path, 'w', cmdline_options.encoding)
111
    return f
112
113
114
def make_rootpath(xml_filename):
115
    root_name = xml_filename
116
    path = os.path.join(cmdline_options.output, root_name)
117
    return path
118
119
120
def make_path(root_path, testsuite, testcase):
121
    suite_name = testsuite.attrib['name'].lstrip('/')
122
    case_name = testcase.attrib['name'].lstrip('/')
123
    ext = '.json'
124
    return os.path.join(os.path.join(root_path, suite_name), case_name + ext)
125
126
127
def get_properties_node(node):
128
    users = {}
129
    for prop in node:
130
        if ('name' in prop.attrib) and ('value' in prop.attrib):
131
            users[prop.attrib['name']] = prop.attrib['value']
132
    return users
133
134
135
def _get_user_properties(node, system_attributes):
136
    users = {}
137
    for a in node.attrib:
138
        if a not in system_attributes:
139
            users[a] = node.attrib[a]
140
    return users
141
142
143
def get_user_properties(node):
144
    system_attributes = {
145
        "testsuites": [
146
            "name", "tests", "failures", "disabled", "skip", "errors", "time", "timestamp", "random_seed"
147
        ],
148
        "testsuite": [
149
            "name", "tests", "failures", "disabled", "skip", "errors", "time", "timestamp", "random_seed"
150
        ],
151
        "testcase": [
152
            "name", "status", "time", "classname", "type_param", "value_param"
153
        ]
154
    }
155
    for k,v in system_attributes.items():
156
        if node.tag == k:
157
            return _get_user_properties(node, v)
158
    return node.attrib
159
160
161
def write_result(f, testsuites_user_attrib, testsuite_user_attrib, testcase):
162
    d = testcase.attrib
163
    if cmdline_options.no_time:
164
        if 'time' in d:
165
            del d['time']
166
    d['testsuites_attrib'] = testsuites_user_attrib
167
    d['testsuite_attrib'] = testsuite_user_attrib
168
    # failure and skipped ...
169
    for child in testcase:
170
        tag = child.tag
171
        if tag not in d:
172
            d[tag] = []
173
        fd = child.attrib
174
        fd['text'] = child.text
175
        d[tag].append(fd)
176
    jt = json.dumps(d, indent=4, ensure_ascii=False)
177
    logd(jt)
178
    f.write(jt)
179
180
181
def xml2file(path):
182
    basename = os.path.basename(path)
183
    filename = os.path.splitext(basename)[0]
184
    logv(basename)
185
186
    root_path = make_rootpath(filename)
187
    clean_dir(root_path)
188
189
    try:
190
        xmlp = ET.XMLParser(encoding=cmdline_options.encoding)
191
        tree = ET.parse(path, xmlp)
192
        root = tree.getroot()
193
        testsuites = root
194
195
        testsuites_user_attrib = get_user_properties(testsuites)
196
        for testsuite in testsuites:
197
            logv("  " + testsuite.attrib['name'])
198
            testsuite_user_attrib = get_user_properties(testsuite)
199
            for testcase in testsuite:
200
                if testcase.tag == 'testcase':
201
                    logv("    " + testcase.attrib['name'])
202
                    f = fopen(make_path(root_path, testsuite, testcase))
203
                    write_result(f, testsuites_user_attrib, testsuite_user_attrib, testcase)
204
                    f.close()
205
                elif testcase.tag == 'properties':
206
                    testsuite_user_attrib.update(get_properties_node(testcase))
207
    except Exception as e:
208
        f = fopen(os.path.join(root_path, "parser_error.txt"))
209
        f.write(str(e))
210
        f.close()
211
        raise
212
213
214
def main():
215
    result=True
216
    global cmdline_options
217
    cmdline_options = parse_command_line()
218
    if cmdline_options.output is None:
219
        cmdline_options.output = tempfile.mkdtemp(prefix='xml2file')
220
    logd(sys.getfilesystemencoding())
221
    log("output: " + cmdline_options.output)
222
    if cmdline_options.clean:
223
        clean_dir(cmdline_options.output)
224
    for path in cmdline_options.file:
225
        try:
226
            xml2file(path)
227
        except Exception as e:
228
            loge("error: " + path + ": " + str(e))
229
            result = False
230
    if not result:
231
        exit(1)
232
233
234
if __name__ == '__main__':
235
    main()
236