xml2file   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 247
Duplicated Lines 19.84 %

Importance

Changes 0
Metric Value
eloc 184
dl 49
loc 247
rs 8.72
c 0
b 0
f 0
wmc 46

17 Functions

Rating   Name   Duplication   Size   Complexity  
A log() 0 2 1
A fopen() 0 5 1
B xml2file() 0 30 6
A clean_dir() 0 3 2
A get_user_properties() 0 16 3
A _get_user_properties() 0 6 3
A opentree() 0 8 3
A write_result() 0 18 5
A logd() 0 3 2
B main() 0 18 6
B parse_command_line() 49 49 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

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 srz-zumix
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
        type=int,
44
        default=0,
45
        metavar='LEVEL',
46
        help='log verbose'
47
    )
48
    parser.add_argument(
49
        '--encoding',
50
        default='utf-8',
51
        help='output file encoding.'
52
    )
53
    parser.add_argument(
54
        '--clean',
55
        action='store_true',
56
        help='clean output directory (before)'
57
    )
58
    parser.add_argument(
59
        '--debug',
60
        action='store_true',
61
        help='log debug'
62
    )
63
    parser.add_argument(
64
        'file',
65
        metavar='FILE',
66
        nargs='+',
67
        help='test result xml files'
68
    )
69
    options = parser.parse_args()
70
    return options
71
72
cmdline_options = None
73
74
75
def log(msg):
76
    print(msg)
77
78
79
def logv(lv, msg):
80
    if cmdline_options.verbose >= lv:
81
        print(msg)
82
83
84
def logd(msg):
85
    if cmdline_options.debug:
86
        print(msg)
87
88
89
def loge(msg):
90
    sys.stderr.write(msg + "\n")
91
92
93
def mkdir_p(path):
94
    try:
95
        os.makedirs(path)
96
    except OSError as exc:  # Python >2.5
97
        if exc.errno == errno.EEXIST and os.path.isdir(path):
98
            pass
99
        else:
100
            loge('failed mkdirs: ' + path)
101
            raise
102
103
104
def clean_dir(path):
105
    if os.path.exists(path):
106
        shutil.rmtree(path)
107
108
109
def fopen(path):
110
    dir = os.path.dirname(path)
111
    mkdir_p(dir)
112
    f = codecs.open(path, 'w', cmdline_options.encoding)
113
    return f
114
115
116
def make_rootpath(xml_filename):
117
    root_name = xml_filename
118
    path = os.path.join(cmdline_options.output, root_name)
119
    return path
120
121
122
def make_path(root_path, testsuite, testcase):
123
    suite_name = testsuite.attrib['name'].lstrip('/')
124
    case_name = testcase.attrib['name'].lstrip('/')
125
    ext = '.json'
126
    return os.path.join(os.path.join(root_path, suite_name), case_name + ext)
127
128
129
def get_properties_node(node):
130
    users = {}
131
    for prop in node:
132
        if ('name' in prop.attrib) and ('value' in prop.attrib):
133
            users[prop.attrib['name']] = prop.attrib['value']
134
    return users
135
136
137
def _get_user_properties(node, system_attributes):
138
    users = {}
139
    for a in node.attrib:
140
        if a not in system_attributes:
141
            users[a] = node.attrib[a]
142
    return users
143
144
145
def get_user_properties(node):
146
    system_attributes = {
147
        "testsuites": [
148
            "name", "tests", "failures", "disabled", "skip", "errors", "time", "timestamp", "random_seed"
149
        ],
150
        "testsuite": [
151
            "name", "tests", "failures", "disabled", "skip", "errors", "time", "timestamp", "random_seed"
152
        ],
153
        "testcase": [
154
            "name", "status", "time", "classname", "type_param", "value_param"
155
        ]
156
    }
157
    for k,v in system_attributes.items():
158
        if node.tag == k:
159
            return _get_user_properties(node, v)
160
    return node.attrib
161
162
163
def write_result(f, testsuites_user_attrib, testsuite_user_attrib, testcase):
164
    d = testcase.attrib
165
    if cmdline_options.no_time:
166
        if 'time' in d:
167
            del d['time']
168
    d['testsuites_attrib'] = testsuites_user_attrib
169
    d['testsuite_attrib'] = testsuite_user_attrib
170
    # failure and skipped ...
171
    for child in testcase:
172
        tag = child.tag
173
        if tag not in d:
174
            d[tag] = []
175
        fd = child.attrib
176
        fd['text'] = child.text
177
        d[tag].append(fd)
178
    jt = json.dumps(d, indent=4, ensure_ascii=False)
179
    logd(jt)
180
    f.write(jt)
181
182
183
def opentree(path):
184
    try:
185
        with codecs.open(path, 'r', encoding=cmdline_options.encoding) as f:
186
            return ET.parse(f)
187
    except Exception as e:
188
        loge("error: " + path + ": " + str(e))
189
        xmlp = ET.XMLParser(encoding=cmdline_options.encoding)
190
        return ET.parse(path, xmlp)
191
192
193
def xml2file(path):
194
    basename = os.path.basename(path)
195
    filename = os.path.splitext(basename)[0]
196
    logv(1, basename)
197
198
    root_path = make_rootpath(filename)
199
    clean_dir(root_path)
200
201
    try:
202
        tree = opentree(path)
203
        root = tree.getroot()
204
        testsuites = root
205
206
        testsuites_user_attrib = get_user_properties(testsuites)
207
        for testsuite in testsuites:
208
            logv(2, "  " + testsuite.attrib['name'])
209
            testsuite_user_attrib = get_user_properties(testsuite)
210
            for testcase in testsuite:
211
                if testcase.tag == 'testcase':
212
                    logv(3, "    " + testcase.attrib['name'])
213
                    f = fopen(make_path(root_path, testsuite, testcase))
214
                    write_result(f, testsuites_user_attrib, testsuite_user_attrib, testcase)
215
                    f.close()
216
                elif testcase.tag == 'properties':
217
                    testsuite_user_attrib.update(get_properties_node(testcase))
218
    except Exception as e:
219
        f = fopen(os.path.join(root_path, "parser_error.txt"))
220
        f.write(str(e))
221
        f.close()
222
        raise
223
224
225
def main():
226
    result=True
227
    global cmdline_options
228
    cmdline_options = parse_command_line()
229
    if cmdline_options.output is None:
230
        cmdline_options.output = tempfile.mkdtemp(prefix='xml2file')
231
    logd(sys.getfilesystemencoding())
232
    log("output: " + cmdline_options.output)
233
    if cmdline_options.clean:
234
        clean_dir(cmdline_options.output)
235
    for path in cmdline_options.file:
236
        try:
237
            xml2file(path)
238
        except Exception as e:
239
            loge("error: " + path + ": " + str(e))
240
            result = False
241
    if not result:
242
        exit(1)
243
244
245
if __name__ == '__main__':
246
    main()
247