diff --git a/branch b/branch new file mode 100644 index 0000000..e9e7ccd --- /dev/null +++ b/branch @@ -0,0 +1 @@ +F-8 diff --git a/python-xmltramp.spec b/python-xmltramp.spec new file mode 100644 index 0000000..a6b44c9 --- /dev/null +++ b/python-xmltramp.spec @@ -0,0 +1,55 @@ +%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} + +Name: python-xmltramp +Version: 2.17 +Release: 3%{?dist} +Summary: Pythonic API for XML + +Group: Development/Languages +License: GPLv2 +# License text is not present in the upstream file, though clearly marked as GPLv2 +# See https://www.redhat.com/archives/fedora-legal-list/2008-January/msg00010.html + +URL: http://www.aaronsw.com/2002/xmltramp/ +Source0: http://www.aaronsw.com/2002/xmltramp/xmltramp-%{version}.py +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +BuildArch: noarch +BuildRequires: python-devel + +%description +xmltramp is a simple Pythonic API for working with XML + +%prep +%setup -c -T + +%build +# noarch + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/%{python_sitelib}/ +cp -p %{SOURCE0} $RPM_BUILD_ROOT/%{python_sitelib}/xmltramp.py + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%files +%defattr(-,root,root,-) +%{python_sitelib}/xmltramp.py* + + +%changelog +* Fri Jan 18 2008 David Malcolm - 2.17-3 +- add comment in specfile about the License text + +* Tue Nov 13 2007 David Malcolm - 2.17-2 +- fix License tag +- fix capitalization of Summary tag +- preserve timestamp when installing + +* Fri Aug 3 2007 David Malcolm - 2.17-1 +- initial packaging + diff --git a/xmltramp-2.17.py b/xmltramp-2.17.py new file mode 100644 index 0000000..25dd0d0 --- /dev/null +++ b/xmltramp-2.17.py @@ -0,0 +1,361 @@ +"""xmltramp: Make XML documents easily accessible.""" + +__version__ = "2.17" +__author__ = "Aaron Swartz" +__credits__ = "Many thanks to pjz, bitsko, and DanC." +__copyright__ = "(C) 2003-2006 Aaron Swartz. GNU GPL 2." + +if not hasattr(__builtins__, 'True'): True, False = 1, 0 +def isstr(f): return isinstance(f, type('')) or isinstance(f, type(u'')) +def islst(f): return isinstance(f, type(())) or isinstance(f, type([])) + +empty = {'http://www.w3.org/1999/xhtml': ['img', 'br', 'hr', 'meta', 'link', 'base', 'param', 'input', 'col', 'area']} + +def quote(x, elt=True): + if elt and '<' in x and len(x) > 24 and x.find(']]>') == -1: return "" + else: x = x.replace('&', '&').replace('<', '<').replace(']]>', ']]>') + if not elt: x = x.replace('"', '"') + return x + +class Element: + def __init__(self, name, attrs=None, children=None, prefixes=None): + if islst(name) and name[0] == None: name = name[1] + if attrs: + na = {} + for k in attrs.keys(): + if islst(k) and k[0] == None: na[k[1]] = attrs[k] + else: na[k] = attrs[k] + attrs = na + + self._name = name + self._attrs = attrs or {} + self._dir = children or [] + + prefixes = prefixes or {} + self._prefixes = dict(zip(prefixes.values(), prefixes.keys())) + + if prefixes: self._dNS = prefixes.get(None, None) + else: self._dNS = None + + def __repr__(self, recursive=0, multiline=0, inprefixes=None): + def qname(name, inprefixes): + if islst(name): + if inprefixes[name[0]] is not None: + return inprefixes[name[0]]+':'+name[1] + else: + return name[1] + else: + return name + + def arep(a, inprefixes, addns=1): + out = '' + + for p in self._prefixes.keys(): + if not p in inprefixes.keys(): + if addns: out += ' xmlns' + if addns and self._prefixes[p]: out += ':'+self._prefixes[p] + if addns: out += '="'+quote(p, False)+'"' + inprefixes[p] = self._prefixes[p] + + for k in a.keys(): + out += ' ' + qname(k, inprefixes)+ '="' + quote(a[k], False) + '"' + + return out + + inprefixes = inprefixes or {u'http://www.w3.org/XML/1998/namespace':'xml'} + + # need to call first to set inprefixes: + attributes = arep(self._attrs, inprefixes, recursive) + out = '<' + qname(self._name, inprefixes) + attributes + + if not self._dir and (self._name[0] in empty.keys() + and self._name[1] in empty[self._name[0]]): + out += ' />' + return out + + out += '>' + + if recursive: + content = 0 + for x in self._dir: + if isinstance(x, Element): content = 1 + + pad = '\n' + ('\t' * recursive) + for x in self._dir: + if multiline and content: out += pad + if isstr(x): out += quote(x) + elif isinstance(x, Element): + out += x.__repr__(recursive+1, multiline, inprefixes.copy()) + else: + raise TypeError, "I wasn't expecting "+`x`+"." + if multiline and content: out += '\n' + ('\t' * (recursive-1)) + else: + if self._dir: out += '...' + + out += '' + + return out + + def __unicode__(self): + text = '' + for x in self._dir: + text += unicode(x) + return ' '.join(text.split()) + + def __str__(self): + return self.__unicode__().encode('utf-8') + + def __getattr__(self, n): + if n[0] == '_': raise AttributeError, "Use foo['"+n+"'] to access the child element." + if self._dNS: n = (self._dNS, n) + for x in self._dir: + if isinstance(x, Element) and x._name == n: return x + raise AttributeError, 'No child element named %s' % repr(n) + + def __hasattr__(self, n): + for x in self._dir: + if isinstance(x, Element) and x._name == n: return True + return False + + def __setattr__(self, n, v): + if n[0] == '_': self.__dict__[n] = v + else: self[n] = v + + + def __getitem__(self, n): + if isinstance(n, type(0)): # d[1] == d._dir[1] + return self._dir[n] + elif isinstance(n, slice(0).__class__): + # numerical slices + if isinstance(n.start, type(0)): return self._dir[n.start:n.stop] + + # d['foo':] == all s + n = n.start + if self._dNS and not islst(n): n = (self._dNS, n) + out = [] + for x in self._dir: + if isinstance(x, Element) and x._name == n: out.append(x) + return out + else: # d['foo'] == first + if self._dNS and not islst(n): n = (self._dNS, n) + for x in self._dir: + if isinstance(x, Element) and x._name == n: return x + raise KeyError + + def __setitem__(self, n, v): + if isinstance(n, type(0)): # d[1] + self._dir[n] = v + elif isinstance(n, slice(0).__class__): + # d['foo':] adds a new foo + n = n.start + if self._dNS and not islst(n): n = (self._dNS, n) + + nv = Element(n) + self._dir.append(nv) + + else: # d["foo"] replaces first and dels rest + if self._dNS and not islst(n): n = (self._dNS, n) + + nv = Element(n); nv._dir.append(v) + replaced = False + + todel = [] + for i in range(len(self)): + if self[i]._name == n: + if replaced: + todel.append(i) + else: + self[i] = nv + replaced = True + if not replaced: self._dir.append(nv) + for i in todel: del self[i] + + def __delitem__(self, n): + if isinstance(n, type(0)): del self._dir[n] + elif isinstance(n, slice(0).__class__): + # delete all s + n = n.start + if self._dNS and not islst(n): n = (self._dNS, n) + + for i in range(len(self)): + if self[i]._name == n: del self[i] + else: + # delete first foo + for i in range(len(self)): + if self[i]._name == n: del self[i] + break + + def __call__(self, *_pos, **_set): + if _set: + for k in _set.keys(): self._attrs[k] = _set[k] + if len(_pos) > 1: + for i in range(0, len(_pos), 2): + self._attrs[_pos[i]] = _pos[i+1] + if len(_pos) == 1 is not None: + return self._attrs[_pos[0]] + if len(_pos) == 0: + return self._attrs + + def __len__(self): return len(self._dir) + +class Namespace: + def __init__(self, uri): self.__uri = uri + def __getattr__(self, n): return (self.__uri, n) + def __getitem__(self, n): return (self.__uri, n) + +from xml.sax.handler import EntityResolver, DTDHandler, ContentHandler, ErrorHandler + +class Seeder(EntityResolver, DTDHandler, ContentHandler, ErrorHandler): + def __init__(self): + self.stack = [] + self.ch = '' + self.prefixes = {} + ContentHandler.__init__(self) + + def startPrefixMapping(self, prefix, uri): + if not self.prefixes.has_key(prefix): self.prefixes[prefix] = [] + self.prefixes[prefix].append(uri) + def endPrefixMapping(self, prefix): + self.prefixes[prefix].pop() + + def startElementNS(self, name, qname, attrs): + ch = self.ch; self.ch = '' + if ch and not ch.isspace(): self.stack[-1]._dir.append(ch) + + attrs = dict(attrs) + newprefixes = {} + for k in self.prefixes.keys(): newprefixes[k] = self.prefixes[k][-1] + + self.stack.append(Element(name, attrs, prefixes=newprefixes.copy())) + + def characters(self, ch): + self.ch += ch + + def endElementNS(self, name, qname): + ch = self.ch; self.ch = '' + if ch and not ch.isspace(): self.stack[-1]._dir.append(ch) + + element = self.stack.pop() + if self.stack: + self.stack[-1]._dir.append(element) + else: + self.result = element + +from xml.sax import make_parser +from xml.sax.handler import feature_namespaces + +def seed(fileobj): + seeder = Seeder() + parser = make_parser() + parser.setFeature(feature_namespaces, 1) + parser.setContentHandler(seeder) + parser.parse(fileobj) + return seeder.result + +def parse(text): + from StringIO import StringIO + return seed(StringIO(text)) + +def load(url): + import urllib + return seed(urllib.urlopen(url)) + +def unittest(): + parse('afoobara').__repr__(1,1) == \ + '\n\ta\n\t\tfoobar\n\ta\n' + + assert str(parse("")) == "" + assert str(parse("I love you.")) == "I love you." + assert parse("\nmom\nwow\n")[0].strip() == "mom\nwow" + assert str(parse(' center ')) == "center" + assert str(parse('\xcf\x80')) == '\xcf\x80' + + d = Element('foo', attrs={'foo':'bar'}, children=['hit with a', Element('bar'), Element('bar')]) + + try: + d._doesnotexist + raise "ExpectedError", "but found success. Damn." + except AttributeError: pass + assert d.bar._name == 'bar' + try: + d.doesnotexist + raise "ExpectedError", "but found success. Damn." + except AttributeError: pass + + assert hasattr(d, 'bar') == True + + assert d('foo') == 'bar' + d(silly='yes') + assert d('silly') == 'yes' + assert d() == d._attrs + + assert d[0] == 'hit with a' + d[0] = 'ice cream' + assert d[0] == 'ice cream' + del d[0] + assert d[0]._name == "bar" + assert len(d[:]) == len(d._dir) + assert len(d[1:]) == len(d._dir) - 1 + assert len(d['bar':]) == 2 + d['bar':] = 'baz' + assert len(d['bar':]) == 3 + assert d['bar']._name == 'bar' + + d = Element('foo') + + doc = Namespace("http://example.org/bar") + bbc = Namespace("http://example.org/bbc") + dc = Namespace("http://purl.org/dc/elements/1.1/") + d = parse(""" + John Polk and John Palfrey + John Polk + John Palfrey + Buffy + """) + + assert repr(d) == '...' + assert d.__repr__(1) == 'John Polk and John PalfreyJohn PolkJohn PalfreyBuffy' + assert d.__repr__(1,1) == '\n\tJohn Polk and John Palfrey\n\tJohn Polk\n\tJohn Palfrey\n\tBuffy\n' + + assert repr(parse("")) == '' + + assert str(d.author) == str(d['author']) == "John Polk and John Palfrey" + assert d.author._name == doc.author + assert str(d[dc.creator]) == "John Polk" + assert d[dc.creator]._name == dc.creator + assert str(d[dc.creator:][1]) == "John Palfrey" + d[dc.creator] = "Me!!!" + assert str(d[dc.creator]) == "Me!!!" + assert len(d[dc.creator:]) == 1 + d[dc.creator:] = "You!!!" + assert len(d[dc.creator:]) == 2 + + assert d[bbc.show](bbc.station) == "4" + d[bbc.show](bbc.station, "5") + assert d[bbc.show](bbc.station) == "5" + + e = Element('e') + e.c = '' + assert e.__repr__(1) == '<img src="foo">' + e.c = '2 > 4' + assert e.__repr__(1) == '2 > 4' + e.c = 'CDATA sections are closed with ]]>.' + assert e.__repr__(1) == 'CDATA sections are <em>closed</em> with ]]>.' + e.c = parse('
i
love
you
') + assert e.__repr__(1) == '
i
love
you
' + + e = Element('e') + e('c', 'that "sucks"') + assert e.__repr__(1) == '' + + + assert quote("]]>") == "]]>" + assert quote('< dkdkdsd dkd sksdksdfsd fsdfdsf]]> kfdfkg >') == '< dkdkdsd dkd sksdksdfsd fsdfdsf]]> kfdfkg >' + + assert parse('').__repr__(1) == '' + assert parse('').__repr__(1) == '' + +if __name__ == '__main__': unittest()