Skip to content

Commit 1777c9d

Browse files
authored
Add python XML formatter (#947)
* Adding a simple xml formatter to pygeos * Adding gitignore * Updating the xml formatter * Adding missing line at end of pygeos gitignore * Updating xml formatter * Handling optional namespace information in the xml formatter * Pluggin the xml formatter into the cmake structure * Changing comment spacing in xml formatter
1 parent 8c35438 commit 1777c9d

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

pygeos_package/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build
2+
pygeos.egg-info
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import argparse
2+
import os
3+
from xml.etree import ElementTree
4+
5+
6+
def format_xml_level(output,
7+
node,
8+
level,
9+
indent=' '*2,
10+
block_separation_max_depth=2,
11+
modify_attribute_indent=False,
12+
sort_attributes=False,
13+
close_tag_newline=False,
14+
include_namespace=False):
15+
# Handle comments
16+
if node.tag is ElementTree.Comment:
17+
output.write('\n%s<!--%s-->' % (indent*level, node.text))
18+
19+
else:
20+
# Write opening line
21+
opening_line = '\n%s<%s' % (indent*level, node.tag)
22+
output.write(opening_line)
23+
24+
# Write attributes
25+
if (len(node.attrib) > 0):
26+
# Choose indentation
27+
attribute_indent = '%s' % (indent*(level+1))
28+
if modify_attribute_indent:
29+
attribute_indent = ' ' * (len(opening_line))
30+
31+
# Get a copy of the attributes
32+
attribute_dict = {}
33+
if ((level == 0) & include_namespace):
34+
# Handle the optional namespace information at the root level
35+
# Note: preferably, this would point to a schema we host online
36+
attribute_dict['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
37+
attribute_dict['xsi:noNamespaceSchemaLocation'] = '/usr/gapps/GEOS/schema/schema.xsd'
38+
elif (level > 0):
39+
attribute_dict = node.attrib
40+
41+
# Sort attribute names
42+
akeys = list(attribute_dict.keys())
43+
if sort_attributes:
44+
akeys = sorted(akeys)
45+
46+
for ii in range(0, len(akeys)):
47+
k = akeys[ii]
48+
if ((ii == 0) & modify_attribute_indent):
49+
output.write(' %s=\"%s\"' % (k, attribute_dict[k]))
50+
else:
51+
output.write('\n%s%s=\"%s\"' % (attribute_indent, k, attribute_dict[k]))
52+
53+
# Write children
54+
if len(node):
55+
output.write('>')
56+
Nc = len(node)
57+
for ii, child in zip(range(Nc), node):
58+
format_xml_level(output, child, level+1, indent,
59+
block_separation_max_depth,
60+
modify_attribute_indent,
61+
sort_attributes,
62+
close_tag_newline,
63+
include_namespace)
64+
65+
# Add space between blocks
66+
if ((level < block_separation_max_depth) & (ii < Nc-1) & (child.tag is not ElementTree.Comment)):
67+
output.write('\n')
68+
69+
# Write the end tag
70+
output.write('\n%s</%s>' % (indent*level, node.tag))
71+
else:
72+
if close_tag_newline:
73+
output.write('\n%s/>' % (indent*level))
74+
else:
75+
output.write('/>')
76+
77+
78+
# Class to handle commented xml structure
79+
class CommentedTreeBuilder(ElementTree.TreeBuilder):
80+
def comment(self, data):
81+
self.start(ElementTree.Comment, {})
82+
self.data(data)
83+
self.end(ElementTree.Comment)
84+
85+
86+
def main():
87+
"""Script to format xml files
88+
89+
@arg input Input file name
90+
"""
91+
92+
# Parse the user arguments
93+
parser = argparse.ArgumentParser()
94+
parser.add_argument('input', type=str, help='Input file name')
95+
parser.add_argument('-i', '--indent', type=int, help='Indent size', default=2)
96+
parser.add_argument('-s', '--style', type=int, help='Indent style', default=0)
97+
parser.add_argument('-d', '--depth', type=int, help='Block separation depth', default=2)
98+
parser.add_argument('-a', '--alphebitize', type=int, help='Alphebetize attributes', default=0)
99+
parser.add_argument('-c', '--close', type=int, help='Close tag style', default=0)
100+
parser.add_argument('-n', '--namespace', type=int, help='Include namespace', default=0)
101+
args = parser.parse_args()
102+
103+
# Process the xml file
104+
fname = os.path.expanduser(args.input)
105+
try:
106+
xml_parser = ElementTree.XMLParser(target=CommentedTreeBuilder())
107+
tree = ElementTree.parse(fname, xml_parser)
108+
root = tree.getroot()
109+
110+
with open(fname, 'w') as f:
111+
f.write('<?xml version=\"1.0\" ?>\n')
112+
format_xml_level(f,
113+
root,
114+
0,
115+
indent=' '*args.indent,
116+
block_separation_max_depth=args.depth,
117+
modify_attribute_indent=args.style,
118+
sort_attributes=args.alphebitize,
119+
close_tag_newline=args.close,
120+
include_namespace=args.namespace)
121+
f.write('\n')
122+
123+
except ElementTree.ParseError as err:
124+
print('\nCould not load file: %s' % (fname))
125+
print(err.msg)
126+
raise Exception('\nCheck input file!')
127+
128+
129+
if __name__ == "__main__":
130+
main()
131+
132+

pygeos_package/pygeos/xml_processor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def merge_xml_nodes(existingNode, targetNode, level):
3232
insertCurrentLevel = True
3333

3434
# Check to see if a node with the appropriate type
35-
# exists at this level
35+
# exists at this level
3636
if (currentTag != target.tag):
3737
currentTag = target.tag
3838
matchingSubNodes = existingNode.findall(target.tag)
@@ -260,3 +260,4 @@ def validate_xml(fname, schema, verbose):
260260
if verbose:
261261
print('Done!')
262262

263+

pygeos_package/setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from distutils.core import setup
22

33
setup(name='pygeos',
4-
version='0.4.1',
4+
version='0.5.0',
55
description='GEOS front-end preprocessing package',
66
author='Chris Sherman',
77
author_email='[email protected]',
88
packages=['pygeos', 'pygeos.tests'],
99
entry_points={'console_scripts': ['pygeos = pygeos.__main__:main',
10+
'format_xml = pygeos.xml_formatter:main',
1011
'test_pygeos = pygeos.tests.test_manager:run_unit_tests']},
1112
install_requires=['lxml', 'numpy'])
1213

0 commit comments

Comments
 (0)