Skip to content

Commit 9c14b2f

Browse files
author
Arzaroth Lekva
committed
Merge branch 'dev'
2 parents 2ec6cd9 + 0433be5 commit 9c14b2f

11 files changed

Lines changed: 247 additions & 43 deletions

File tree

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ python:
44
- "2.7"
55
- "3.3"
66
- "3.4"
7+
- "3.5"
8+
- "3.6"
79
install:
810
- pip install -r test-requirements.txt
911
- python setup.py install

README.md

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,39 @@ A library providing python bindings for rapidxml
44

55
### Example
66

7-
import rapidxml
7+
```python
8+
import rapidxml
89

9-
r = rapidxml.RapidXml(b"<test/><test2>foo</test2><test></test>") # parsing from bytes
10-
test = r.first_node("test") # get first node named test
11-
test.name = "foo" # changing node's name to foo
12-
r.first_node("test2").value = "bar" # changing node's value to bar
10+
r = rapidxml.RapidXml(b"<test/><test2>foo</test2><test></test>") # parsing from bytes
11+
test = r.first_node("test") # get first node named test
12+
test.name = "foo" # changing node's name to foo
13+
r.first_node("test2").value = "bar" # changing node's value to bar
1314

14-
print(str(r)) # will output a prettified string of the xml document
15-
print(r.unparse(pretty=False, raw=True)) # will output the xml document as flat bytes
16-
print(test) # also works for nodes
15+
print(str(r)) # will output a prettified string of the xml document
16+
print(r.unparse(pretty=False, raw=True)) # will output the xml document as flat bytes
17+
print(test) # also works for nodes
1718

18-
with open('dump.xml', 'w') as f:
19-
f.write(str(r))
20-
r = rapidxml.RapidXml("dump.xml", True) # loading from file
21-
22-
assert(str(r) == r.unparse(True, False)) # is always True
23-
assert(repr(r) == r.unparse(pretty=False, raw=False)) # also always True
19+
with open('dump.xml', 'w') as f:
20+
f.write(str(r))
21+
r = rapidxml.RapidXml("dump.xml", from_file=True) # loading from file
2422

23+
assert(str(r) == r.unparse(True, False)) # is always True
24+
assert(repr(r) == r.unparse(pretty=False, raw=False)) # also always True
25+
```
2526

2627
### Install
2728

28-
[![Latest Version](https://pypip.in/version/RapidXml/badge.svg)](https://pypi.python.org/pypi/RapidXml/)
29-
[![Supported Python Versions](https://pypip.in/py_versions/RapidXml/badge.svg)](https://pypi.python.org/pypi/RapidXml/)
29+
[![Latest Version](https://img.shields.io/pypi/v/RapidXml.svg)](https://pypi.python.org/pypi/RapidXml/)
30+
[![Supported Python Versions](https://img.shields.io/pypi/pyversions/RapidXml.svg)](https://pypi.python.org/pypi/RapidXml/)
3031

3132
If you have downloaded the source code:
32-
33-
python setup.py install
34-
33+
```sh
34+
python setup.py install
35+
```
3536
or if you want to obtain a copy from the Pypi repository:
36-
37-
pip install rapidxml
38-
37+
```sh
38+
pip install rapidxml
39+
```
3940
Both commands will install the required package dependencies.
4041

4142
A distribution package can be obtained for manual installation at:
@@ -46,9 +47,9 @@ A distribution package can be obtained for manual installation at:
4647
### Source
4748

4849
python_rapidxml's git repo is available on GitHub, which can be browsed at [github](https://github.com/Arzaroth/python_rapidxml) and cloned like that:
49-
50-
git clone https://github.com/Arzaroth/python_rapidxml.git
51-
50+
```sh
51+
git clone https://github.com/Arzaroth/python_rapidxml.git
52+
```
5253

5354
### License
5455

@@ -57,6 +58,6 @@ MIT license. See the LICENSE file.
5758

5859
### Development status
5960

60-
[![Build Status](https://travis-ci.org/Arzaroth/python_rapidxml.svg?branch=2.0)](https://travis-ci.org/Arzaroth/python_rapidxml)
61+
[![Build Status](https://travis-ci.org/Arzaroth/python_rapidxml.svg?branch=master)](https://travis-ci.org/Arzaroth/python_rapidxml)
6162

6263
This project is currently under development.

rapidxml/c_ext/src/document_object.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ static void rapidxml_DocumentObject_dealloc(rapidxml_DocumentObject* self) {
2525

2626
static int _parse(rapidxml_DocumentObject* self,
2727
Py_buffer* text_buff,
28-
bool from_file) {
28+
bool from_file,
29+
bool parse_cdata) {
2930
const char* text;
3031
std::vector<char> text_vector;
3132

@@ -43,9 +44,14 @@ static int _parse(rapidxml_DocumentObject* self,
4344
}
4445
try {
4546
self->base.base.document->clear();
46-
(self->base.base.document
47-
->parse<rapidxml::parse_no_utf8 | rapidxml::parse_no_data_nodes>)
48-
(self->base.base.document->allocate_string(text));
47+
char* data = self->base.base.document->allocate_string(text);
48+
if (!parse_cdata) {
49+
(self->base.base.document
50+
->parse<rapidxml::parse_no_utf8 | rapidxml::parse_no_data_nodes>)(data);
51+
} else {
52+
(self->base.base.document
53+
->parse<rapidxml::parse_declaration_node>)(data);
54+
}
4955
} catch (rapidxml::parse_error &e) {
5056
PyErr_SetString(rapidxml_RapidXmlError, e.what());
5157
return 0;
@@ -58,17 +64,20 @@ static PyObject* rapidxml_DocumentObject_parse(rapidxml_DocumentObject* self,
5864
PyObject* kwds) {
5965
Py_buffer text_buff;
6066
PyObject* from_file_obj = NULL;
67+
PyObject* read_cdata = NULL;
6168
char kw_text[] = "text";
6269
char kw_from_file[] = "from_file";
70+
char kw_parse_cdata[] = "parse_cdata";
6371

64-
static char* kwlist[] = {kw_text, kw_from_file, NULL};
65-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s*|O", kwlist,
66-
&text_buff, &from_file_obj)) {
72+
static char* kwlist[] = {kw_text, kw_from_file, kw_parse_cdata, NULL};
73+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s*|OO", kwlist,
74+
&text_buff, &from_file_obj, &read_cdata)) {
6775
return NULL;
6876
}
6977

7078
if (!_parse(self, &text_buff,
71-
(from_file_obj != NULL) && PyObject_IsTrue(from_file_obj))) {
79+
(from_file_obj != NULL) && PyObject_IsTrue(from_file_obj),
80+
(read_cdata != NULL) && PyObject_IsTrue(read_cdata))) {
7281
return NULL;
7382
}
7483
Py_INCREF(Py_None);
@@ -80,21 +89,26 @@ static int rapidxml_DocumentObject_init(rapidxml_DocumentObject* self,
8089
PyObject* kwds) {
8190
Py_buffer text_buff = {0};
8291
PyObject* from_file_obj = NULL;
92+
PyObject* read_cdata = NULL;
8393
char kw_text[] = "text";
8494
char kw_from_file[] = "from_file";
95+
char kw_parse_cdata[] = "parse_cdata";
8596

8697
if (rapidxml_NodeType.tp_init(reinterpret_cast<PyObject*>(self), args, kwds) < 0) {
8798
return -1;
8899
}
89-
static char* kwlist[] = {kw_text, kw_from_file, NULL};
90-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s*O", kwlist,
91-
&text_buff, &from_file_obj)) {
100+
static char* kwlist[] = {kw_text, kw_from_file, kw_parse_cdata, NULL};
101+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s*OO", kwlist,
102+
&text_buff, &from_file_obj, &read_cdata)) {
92103
return -1;
93104
}
94105
self->base.base.underlying_obj = new rapidxml::xml_document<>();
95106
self->base.base.document = static_cast<rapidxml::xml_document<>*>(self->base.base.underlying_obj);
96107
if (text_buff.buf) {
97-
return _parse(self, &text_buff, (from_file_obj != NULL) && PyObject_IsTrue(from_file_obj)) - 1;
108+
return (_parse(self, &text_buff,
109+
(from_file_obj != NULL) && PyObject_IsTrue(from_file_obj),
110+
(read_cdata != NULL) && PyObject_IsTrue(read_cdata))
111+
- 1);
98112
}
99113
return 0;
100114
}
@@ -128,7 +142,7 @@ static PyObject* rapidxml_DocumentObject_allocate_node(rapidxml_DocumentObject*
128142
return _bind_result(reinterpret_cast<rapidxml_BaseObject*>(self),
129143
node, &rapidxml_NodeType);
130144
}
131-
#include <iostream>
145+
132146
static PyObject* rapidxml_DocumentObject_allocate_attribute(rapidxml_DocumentObject* self,
133147
PyObject* args,
134148
PyObject* kwds) {

rapidxml/rapidxml.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,16 @@ def __iter__(self):
9494
self.cdata_key,
9595
self.always_aslist)
9696

97-
9897
class RapidXml(DictNode, rapidxml.c_ext.Document):
9998
def __init__(self,
10099
text="",
101100
from_file=False,
102101
attribute_prefix='@',
103102
cdata_key='#text',
104-
always_aslist=False):
103+
always_aslist=False,
104+
parse_cdata=False):
105105
DictNode.__init__(self, attribute_prefix, cdata_key, always_aslist)
106-
rapidxml.c_ext.Document.__init__(self, text, from_file)
106+
rapidxml.c_ext.Document.__init__(self, text, from_file, parse_cdata)
107107

108108
def allocate_node(self, *args):
109109
return DictNode(self.attribute_prefix,

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from setuptools import setup, Extension, find_packages
1010

11-
VERSION = ("2", "0", "0")
11+
VERSION = ("2", "1", "0")
1212

1313
rapidxml = Extension("rapidxml.c_ext",
1414
define_macros=[('MAJOR_VERSION', VERSION[0]),

tests/conftest.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,24 @@ def init_rapidxml():
2222
<test>some text</test>
2323
</root>""")
2424
return r
25+
26+
@pytest.fixture(scope="function")
27+
def init_rapidxml_with_CDADA():
28+
data = b"<root><test attr1=\"one\" attr2=\"two\" attr3=\"three\"/><test2><node id=\"1\"/><node id=\"2\"/><node id=\"3\"/></test2><test>some text</test><ns2:AdditionalData><ns2:Data TID=\"AD_1\"><![CDATA[{\"Cart\":{\"expirationTime\":\"2017-04-22T09:40\",\"id\":\"b469df3b-f626-4fe3-898c-825373e546a2\",\"products\":[\"1223\"],\"creationTime\":\"2017-04-21T09:40\",\"totalPrice\":{\"currencyCode\":\"EUR\",\"amount\":\"138.000\"}}}]]></ns2:Data></ns2:AdditionalData></root>"
29+
r = rapidxml.RapidXml(data, parse_cdata=True)
30+
return r
31+
32+
@pytest.fixture(scope="module")
33+
def cdata_obj():
34+
return {
35+
"Cart": {
36+
"expirationTime": "2017-04-22T09:40",
37+
"id": "b469df3b-f626-4fe3-898c-825373e546a2",
38+
"products": ["1223"],
39+
"creationTime":"2017-04-21T09:40",
40+
"totalPrice": {
41+
"currencyCode": "EUR",
42+
"amount": "138.000",
43+
},
44+
},
45+
}

tests/test_attributes.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,51 @@ def test_insert_attribute(init_rapidxml):
5353
test = attr2.previous_attribute()
5454
assert test.name == "test"
5555
assert test.value == "test"
56+
57+
def test_first_attribute_cdata(init_rapidxml_with_CDADA):
58+
test = init_rapidxml_with_CDADA.first_node().first_node("test")
59+
assert test.first_attribute().name == "attr1"
60+
assert test.first_attribute().value == "one"
61+
attr2 = test.first_attribute("attr2")
62+
assert attr2.name == "attr2"
63+
assert attr2.value == "two"
64+
65+
def test_last_attribute_cdata(init_rapidxml_with_CDADA):
66+
test = init_rapidxml_with_CDADA.first_node().first_node("test")
67+
assert test.last_attribute().name == "attr3"
68+
assert test.last_attribute().value == "three"
69+
attr2 = test.last_attribute("attr2")
70+
assert attr2.name == "attr2"
71+
assert attr2.value == "two"
72+
73+
def test_append_attribute_cdata(init_rapidxml_with_CDADA):
74+
root = init_rapidxml_with_CDADA.first_node()
75+
root.append_attribute(init_rapidxml_with_CDADA.allocate_attribute())
76+
assert root.last_attribute().name == ""
77+
assert root.last_attribute().value == ""
78+
root.append_attribute(init_rapidxml_with_CDADA.allocate_attribute("test"))
79+
assert root.last_attribute().name == "test"
80+
assert root.last_attribute().value == ""
81+
root.append_attribute(init_rapidxml_with_CDADA.allocate_attribute(value="test"))
82+
assert root.last_attribute().name == ""
83+
assert root.last_attribute().value == "test"
84+
root.append_attribute(init_rapidxml_with_CDADA.allocate_attribute("test", "test"))
85+
assert root.last_attribute().name == "test"
86+
assert root.last_attribute().value == "test"
87+
88+
def test_prepend_attribute_cdata(init_rapidxml_with_CDADA):
89+
root = init_rapidxml_with_CDADA.first_node()
90+
root.prepend_attribute(init_rapidxml_with_CDADA.allocate_attribute("test", "test"))
91+
assert root.first_attribute().name == "test"
92+
assert root.first_attribute().value == "test"
93+
root.prepend_attribute(init_rapidxml_with_CDADA.allocate_attribute("test2", "test2"))
94+
assert root.first_attribute().name == "test2"
95+
assert root.first_attribute().value == "test2"
96+
97+
def test_insert_attribute_cdata(init_rapidxml_with_CDADA):
98+
test = init_rapidxml_with_CDADA.first_node().first_node()
99+
attr2 = test.first_attribute("attr2")
100+
test.insert_attribute(attr2, init_rapidxml_with_CDADA.allocate_attribute("test", "test"))
101+
test = attr2.previous_attribute()
102+
assert test.name == "test"
103+
assert test.value == "test"

tests/test_basic.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,62 @@ def test_assign(init_rapidxml):
5757
assert init_rapidxml.unparse() == ('<new_root><new_test new_attr1="one" attr2="new_two" attr3="three"/>'
5858
'<test2><node id="1"/><node id="2"/><node id="3"/></test2>'
5959
'<test>some new text</test></new_root>')
60+
61+
def test_init_cdata(init_rapidxml_with_CDADA):
62+
datra_str =('<root><test attr1="one" attr2="two" attr3="three"/>'
63+
'<test2><node id="1"/><node id="2"/><node id="3"/></test2>'
64+
'<test>some text</test>'
65+
"<ns2:AdditionalData><ns2:Data TID=\"AD_1\">"
66+
"<![CDATA[{\"Cart\":{\"expirationTime\":\"2017-04-22T09:40\","
67+
"\"id\":\"b469df3b-f626-4fe3-898c-825373e546a2\",\"products\":[\"1223\"],"
68+
"\"creationTime\":\"2017-04-21T09:40\",\"totalPrice\":"
69+
"{\"currencyCode\":\"EUR\",\"amount\":\"138.000\"}}}]]>"
70+
"</ns2:Data></ns2:AdditionalData></root>")
71+
assert init_rapidxml_with_CDADA.unparse() == rapidxml.RapidXml(datra_str,
72+
from_file=False,
73+
attribute_prefix='@',
74+
cdata_key='#text',
75+
always_aslist=False,
76+
parse_cdata=True).unparse()
77+
assert init_rapidxml_with_CDADA.unparse() == repr(init_rapidxml_with_CDADA)
78+
assert init_rapidxml_with_CDADA.unparse(True) == str(init_rapidxml_with_CDADA)
79+
80+
def test_parse_cdata(init_rapidxml_with_CDADA):
81+
r = rapidxml.RapidXml()
82+
try:
83+
data = init_rapidxml_with_CDADA.unparse().encode('utf-8')
84+
except UnicodeDecodeError:
85+
data = init_rapidxml_with_CDADA.unparse()
86+
r.parse(data, from_file=False, parse_cdata=True)
87+
assert str(r) == str(init_rapidxml_with_CDADA)
88+
89+
def test_parse_from_file_cdata(init_rapidxml_with_CDADA, tmpdir):
90+
f = tmpdir.join("dump.xml")
91+
f.write(init_rapidxml_with_CDADA.unparse())
92+
r = rapidxml.RapidXml(str(f), from_file=True, parse_cdata=True)
93+
assert str(r) == str(init_rapidxml_with_CDADA)
94+
95+
def test_equals_cdata(init_rapidxml_with_CDADA):
96+
assert init_rapidxml_with_CDADA == init_rapidxml_with_CDADA
97+
root = init_rapidxml_with_CDADA.first_node()
98+
assert root == root
99+
assert root == init_rapidxml_with_CDADA.first_node()
100+
assert root.first_node() != root.first_node("test2")
101+
assert (root != root) == (not (root == root))
102+
103+
def test_parent_cdata(init_rapidxml_with_CDADA):
104+
assert init_rapidxml_with_CDADA.parent is None
105+
assert init_rapidxml_with_CDADA.first_node().parent == init_rapidxml_with_CDADA
106+
107+
def test_assign_cdata(init_rapidxml_with_CDADA):
108+
root = init_rapidxml_with_CDADA.first_node()
109+
root.name = "new_root"
110+
assert root.name == "new_root"
111+
test = root.first_node()
112+
test.name = "new_test"
113+
test.first_attribute().name = "new_attr1"
114+
test.first_attribute().next_attribute().value = "new_two"
115+
test = root.first_node("test")
116+
test.value = "some new text"
117+
assert test.value == "some new text"
118+

tests/test_iterations.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,27 @@ def test_attributes(init_rapidxml):
2929
i_attr = i_attr.next_attribute()
3030
if i_attr:
3131
assert i_attr.previous_attribute() == attr
32+
33+
def test_children_cdata(init_rapidxml_with_CDADA):
34+
test2 = init_rapidxml_with_CDADA.first_node().first_node("test2")
35+
i_node = test2.first_node()
36+
for node in test2.children:
37+
assert i_node == node
38+
assert node.name == "node"
39+
assert node.value == ""
40+
i_node = i_node.next_sibling()
41+
if i_node:
42+
assert i_node.previous_sibling() == node
43+
44+
def test_attributes_cdata(init_rapidxml_with_CDADA):
45+
test = init_rapidxml_with_CDADA.first_node().first_node()
46+
i_attr = test.first_attribute()
47+
for attr, expected_attr in zip(test.attributes, [('attr1', 'one'),
48+
('attr2', 'two'),
49+
('attr3', 'three')]):
50+
assert i_attr == attr
51+
assert (attr.name, attr.value) == expected_attr
52+
assert (i_attr.name, i_attr.value) == expected_attr
53+
i_attr = i_attr.next_attribute()
54+
if i_attr:
55+
assert i_attr.previous_attribute() == attr

0 commit comments

Comments
 (0)