././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1586988343.1184306 dict2xml-1.7.0/0000755000076500000240000000000000000000000015101 5ustar00stephenmoorestaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1582686701.0 dict2xml-1.7.0/LICENSE0000644000076500000240000000207100000000000016106 0ustar00stephenmoorestaff00000000000000The MIT License (MIT) Copyright (c) 2018 Stephen Moore Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1582686701.0 dict2xml-1.7.0/MANIFEST.in0000644000076500000240000000004300000000000016634 0ustar00stephenmoorestaff00000000000000include README.rst include LICENSE ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1586988343.118079 dict2xml-1.7.0/PKG-INFO0000644000076500000240000000736300000000000016207 0ustar00stephenmoorestaff00000000000000Metadata-Version: 2.1 Name: dict2xml Version: 1.7.0 Summary: Small utility to convert a python dictionary into an XML string Home-page: http://github.com/delfick/python-dict2xml Author: Stephen Moore Author-email: stephen@delfick.com License: MIT Description: dict2xml ======== Super Simple utility to convert a python dictionary into an xml string Installation ------------ Make youself a virtualenv and do the following:: $ pip install . Or if you want to develop on dict2xml, recommended commands are:: $ pip install -e . $ pip install -e ".[tests]" Or if you don't want to install from source:: $ pip install dict2xml example ------- .. code-block:: python from dict2xml import dict2xml data = { 'a': 1, 'b': [2, 3], 'c': { 'd': [ {'p': 9}, {'o': 10} ], 'e': 7 } } print dict2xml(data, wrap="all", indent=" ") Output ------ .. code-block:: xml 1 2 3

9

10 7
methods ------- ``dict2xml.dict2xml(data, *args, **kwargs)`` Equivalent to: .. code-block:: python dict2xml.Converter(*args, **kwargs).build(data) ``dict2xml.Converter(wrap="", indent=" ", newlines=True)`` Knows how to convert a dictionary into an xml string * wrap: Wraps the entire tree in this tag * indent: Amount to prefix each line for each level of nesting * newlines: Whether or not to use newlines ``dict2xml.Converter.build(data)`` Instance method on Converter that takes in the data and creates the xml string Limitations ----------- * No attributes on elements * Currently no explicit way to hook into how to cope with your custom data * Currently no way to insert an xml declaration line Changelog --------- 1.7.0 - 16 April, 2020 * Use collections.abc to avoid deprecation warning. Thanks @mangin. * This library no longer supports Python2 and is only supported for Python3.6+. Note that the library should still work in Python3.5 as I have not used f-strings, but the framework I use for the tests is only 3.6+. 1.6.1 - August 27, 2019 * Include readme and LICENSE in the package 1.6 - April 27, 2018 * No code changes * changed the licence to MIT * Added more metadata to pypi * Enabled travis ci * Updated the tests slightly 1.5 * No changelog was kept before this point. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: XML Provides-Extra: tests ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988232.0 dict2xml-1.7.0/README.rst0000644000076500000240000000426000000000000016572 0ustar00stephenmoorestaff00000000000000dict2xml ======== Super Simple utility to convert a python dictionary into an xml string Installation ------------ Make youself a virtualenv and do the following:: $ pip install . Or if you want to develop on dict2xml, recommended commands are:: $ pip install -e . $ pip install -e ".[tests]" Or if you don't want to install from source:: $ pip install dict2xml example ------- .. code-block:: python from dict2xml import dict2xml data = { 'a': 1, 'b': [2, 3], 'c': { 'd': [ {'p': 9}, {'o': 10} ], 'e': 7 } } print dict2xml(data, wrap="all", indent=" ") Output ------ .. code-block:: xml 1 2 3

9

10 7
methods ------- ``dict2xml.dict2xml(data, *args, **kwargs)`` Equivalent to: .. code-block:: python dict2xml.Converter(*args, **kwargs).build(data) ``dict2xml.Converter(wrap="", indent=" ", newlines=True)`` Knows how to convert a dictionary into an xml string * wrap: Wraps the entire tree in this tag * indent: Amount to prefix each line for each level of nesting * newlines: Whether or not to use newlines ``dict2xml.Converter.build(data)`` Instance method on Converter that takes in the data and creates the xml string Limitations ----------- * No attributes on elements * Currently no explicit way to hook into how to cope with your custom data * Currently no way to insert an xml declaration line Changelog --------- 1.7.0 - 16 April, 2020 * Use collections.abc to avoid deprecation warning. Thanks @mangin. * This library no longer supports Python2 and is only supported for Python3.6+. Note that the library should still work in Python3.5 as I have not used f-strings, but the framework I use for the tests is only 3.6+. 1.6.1 - August 27, 2019 * Include readme and LICENSE in the package 1.6 - April 27, 2018 * No code changes * changed the licence to MIT * Added more metadata to pypi * Enabled travis ci * Updated the tests slightly 1.5 * No changelog was kept before this point. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1586988343.1158354 dict2xml-1.7.0/dict2xml/0000755000076500000240000000000000000000000016627 5ustar00stephenmoorestaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586987523.0 dict2xml-1.7.0/dict2xml/__init__.py0000644000076500000240000000035200000000000020740 0ustar00stephenmoorestaff00000000000000from dict2xml.logic import Converter, Node def dict2xml(data, *args, **kwargs): """Return an XML string of a Python dict object.""" return Converter(*args, **kwargs).build(data) __all__ = ["dict2xml", "Converter", "Node"] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586987696.0 dict2xml-1.7.0/dict2xml/logic.py0000644000076500000240000002130100000000000020273 0ustar00stephenmoorestaff00000000000000import collections.abc import collections import re start_ranges = "|".join( "[{0}]".format(r) for r in [ "\xC0-\xD6", "\xD8-\xF6", "\xF8-\u02FF", "\u0370-\u037D", "\u037F-\u1FFF", "\u200C-\u200D", "\u2070-\u218F", "\u2C00-\u2FEF", "\u3001-\uD7FF", "\uF900-\uFDCF", "\uFDF0-\uFFFD", ] ) NameStartChar = re.compile(r"(:|[A-Z]|_|[a-z]|{0})".format(start_ranges)) NameChar = re.compile(r"(\-|\.|[0-9]|\xB7|[\u0300-\u036F]|[\u203F-\u2040])") ######################## ### NODE ######################## class Node(object): """ Represents each tag in the tree Each node has _either_ a single value or one or more children If it has a value: The serialized result is <%(tag)s>%(value)s If it has children: The serialized result is <%(wrap)s> %(children)s Which one it is depends on the implementation of self.convert """ # A mapping of characters to treat as escapable entities and their replacements entities = [("&", "&"), ("<", "<"), (">", ">")] def __init__(self, wrap="", tag="", data=None, iterables_repeat_wrap=True): self.tag = self.sanitize_element(tag) self.wrap = self.sanitize_element(wrap) self.data = data self.type = self.determine_type() self.iterables_repeat_wrap = iterables_repeat_wrap if self.type == "flat" and isinstance(self.data, str): # Make sure we deal with entities for entity, replacement in self.entities: self.data = self.data.replace(entity, replacement) def serialize(self, indenter): """Returns the Node serialized as an xml string""" # Determine the start and end of this node wrap = self.wrap end, start = "", "" if wrap: end = "".format(wrap) start = "<{0}>".format(wrap) # Convert the data attached in this node into a value and children value, children = self.convert() # Determine the content of the node (essentially the children as a string value) content = "" if children: if self.type != "iterable": # Non-iterable wraps all it's children in the same tag content = indenter((c.serialize(indenter) for c in children), wrap) else: if self.iterables_repeat_wrap: # Iterables repeat the wrap for each child result = [] for c in children: content = c.serialize(indenter) if c.type == "flat": # Child with value, it already is surrounded by the tag result.append(content) else: # Child with children of it's own, they need to be wrapped by start and end content = indenter([content], True) result.append("".join((start, content, end))) # We already have what we want, return the indented result return indenter(result, False) else: result = [] for c in children: result.append(c.serialize(indenter)) return "".join([start, indenter(result, True), end]) # If here, either: # * Have a value # * Or this node is not an iterable return "".join((start, value, content, end)) def determine_type(self): """ Return the type of the data on this node as an identifying string * Iterable : Supports "for item in data" * Mapping : Supports "for key in data: value = data[key]" * flat : A string or something that isn't iterable or a mapping """ data = self.data if isinstance(data, str): return "flat" elif isinstance(data, collections.abc.Mapping): return "mapping" elif isinstance(data, collections.abc.Iterable): return "iterable" else: return "flat" def convert(self): """ Convert data on this node into a (value, children) tuple depending on the type of the data If the type is : * flat : Use self.tag to surround the value. value * mapping : Return a list of tags where the key for each child is the wrap for that node * iterable : Return a list of Nodes where self.wrap is the tag for that node """ val = "" typ = self.type data = self.data children = [] if typ == "mapping": sorted_data = data if not isinstance(data, collections.OrderedDict): sorted_data = sorted(data) for key in sorted_data: item = data[key] children.append( Node(key, "", item, iterables_repeat_wrap=self.iterables_repeat_wrap) ) elif typ == "iterable": for item in data: children.append( Node("", self.wrap, item, iterables_repeat_wrap=self.iterables_repeat_wrap,) ) else: val = str(data) if self.tag: val = "<{0}>{1}".format(self.tag, val, self.tag) return val, children @staticmethod def sanitize_element(wrap): """ Convert `wrap` into a valid tag name applying the XML Naming Rules. * Names can contain letters, numbers, and other characters * Names cannot start with a number or punctuation character * Names cannot start with the letters xml (or XML, or Xml, etc) * Names cannot contain spaces * Any name can be used, no words are reserved. :ref: http://www.w3.org/TR/REC-xml/#NT-NameChar """ if wrap and isinstance(wrap, str): if wrap.lower().startswith("xml"): wrap = "_" + wrap return "".join( ["_" if not NameStartChar.match(wrap) else ""] + ["_" if not (NameStartChar.match(c) or NameChar.match(c)) else c for c in wrap] ) else: return wrap ######################## ### CONVERTER ######################## class Converter(object): """Logic for creating a Node tree and serialising that tree into a string""" def __init__(self, wrap=None, indent=" ", newlines=True): """ wrap: The tag that the everything else will be contained within indent: The string that is multiplied at the start of each new line, to represent each level of nesting newlines: A boolean specifying whether we want each tag on a new line. Note that indent only works if newlines is True """ self.wrap = wrap self.indent = indent self.newlines = newlines def _make_indenter(self): """Returns a function that given a list of strings, will return that list as a single, indented, string""" indent = self.indent newlines = self.newlines if not newlines: # No newlines, don't care about indentation ret = lambda nodes, wrapped: "".join(nodes) else: if not indent: indent = "" def eachline(nodes): """Yield each line in each node""" for node in nodes: for line in node.split("\n"): yield line def ret(nodes, wrapped): """ Indent nodes depending on value of wrapped and indent If not wrapped, then don't indent Otherwise, Seperate each child by a newline and indent each line in the child by one indent unit """ if wrapped: seperator = "\n{0}".format(indent) surrounding = "\n{0}{{0}}\n".format(indent) else: seperator = "\n" surrounding = "{0}" return surrounding.format(seperator.join(eachline(nodes))) return ret def build(self, data, iterables_repeat_wrap=True): """Create a Node tree from the data and return it as a serialized xml string""" indenter = self._make_indenter() return Node( wrap=self.wrap, data=data, iterables_repeat_wrap=iterables_repeat_wrap ).serialize(indenter) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1586988343.1176314 dict2xml-1.7.0/dict2xml.egg-info/0000755000076500000240000000000000000000000020321 5ustar00stephenmoorestaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988342.0 dict2xml-1.7.0/dict2xml.egg-info/PKG-INFO0000644000076500000240000000736300000000000021427 0ustar00stephenmoorestaff00000000000000Metadata-Version: 2.1 Name: dict2xml Version: 1.7.0 Summary: Small utility to convert a python dictionary into an XML string Home-page: http://github.com/delfick/python-dict2xml Author: Stephen Moore Author-email: stephen@delfick.com License: MIT Description: dict2xml ======== Super Simple utility to convert a python dictionary into an xml string Installation ------------ Make youself a virtualenv and do the following:: $ pip install . Or if you want to develop on dict2xml, recommended commands are:: $ pip install -e . $ pip install -e ".[tests]" Or if you don't want to install from source:: $ pip install dict2xml example ------- .. code-block:: python from dict2xml import dict2xml data = { 'a': 1, 'b': [2, 3], 'c': { 'd': [ {'p': 9}, {'o': 10} ], 'e': 7 } } print dict2xml(data, wrap="all", indent=" ") Output ------ .. code-block:: xml 1 2 3

9

10 7
methods ------- ``dict2xml.dict2xml(data, *args, **kwargs)`` Equivalent to: .. code-block:: python dict2xml.Converter(*args, **kwargs).build(data) ``dict2xml.Converter(wrap="", indent=" ", newlines=True)`` Knows how to convert a dictionary into an xml string * wrap: Wraps the entire tree in this tag * indent: Amount to prefix each line for each level of nesting * newlines: Whether or not to use newlines ``dict2xml.Converter.build(data)`` Instance method on Converter that takes in the data and creates the xml string Limitations ----------- * No attributes on elements * Currently no explicit way to hook into how to cope with your custom data * Currently no way to insert an xml declaration line Changelog --------- 1.7.0 - 16 April, 2020 * Use collections.abc to avoid deprecation warning. Thanks @mangin. * This library no longer supports Python2 and is only supported for Python3.6+. Note that the library should still work in Python3.5 as I have not used f-strings, but the framework I use for the tests is only 3.6+. 1.6.1 - August 27, 2019 * Include readme and LICENSE in the package 1.6 - April 27, 2018 * No code changes * changed the licence to MIT * Added more metadata to pypi * Enabled travis ci * Updated the tests slightly 1.5 * No changelog was kept before this point. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: XML Provides-Extra: tests ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988342.0 dict2xml-1.7.0/dict2xml.egg-info/SOURCES.txt0000644000076500000240000000040700000000000022206 0ustar00stephenmoorestaff00000000000000LICENSE MANIFEST.in README.rst pyproject.toml readme.rst setup.py dict2xml/__init__.py dict2xml/logic.py dict2xml.egg-info/PKG-INFO dict2xml.egg-info/SOURCES.txt dict2xml.egg-info/dependency_links.txt dict2xml.egg-info/requires.txt dict2xml.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988342.0 dict2xml-1.7.0/dict2xml.egg-info/dependency_links.txt0000644000076500000240000000000100000000000024367 0ustar00stephenmoorestaff00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988342.0 dict2xml-1.7.0/dict2xml.egg-info/requires.txt0000644000076500000240000000005100000000000022715 0ustar00stephenmoorestaff00000000000000 [tests] noseOfYeti==2.0.1 pytest==5.3.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988342.0 dict2xml-1.7.0/dict2xml.egg-info/top_level.txt0000644000076500000240000000001100000000000023043 0ustar00stephenmoorestaff00000000000000dict2xml ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586987523.0 dict2xml-1.7.0/pyproject.toml0000644000076500000240000000016400000000000020016 0ustar00stephenmoorestaff00000000000000[tool.black] line-length = 100 include = '\.py$' exclude = ''' /( \.git | \.tox | dist | tools )/ ''' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1586988343.1185412 dict2xml-1.7.0/setup.cfg0000644000076500000240000000004600000000000016722 0ustar00stephenmoorestaff00000000000000[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586988214.0 dict2xml-1.7.0/setup.py0000644000076500000240000000177400000000000016624 0ustar00stephenmoorestaff00000000000000from setuptools import setup # fmt: off # Setup the project setup( name = "dict2xml" , version = '1.7.0' , packages = ['dict2xml'] , extras_require = { 'tests' : [ "noseOfYeti==2.0.1" , "pytest==5.3.1" ] } , classifiers = [ "Development Status :: 5 - Production/Stable" , "License :: OSI Approved :: MIT License" , "Operating System :: OS Independent" , "Programming Language :: Python" , "Programming Language :: Python :: 2.7" , "Programming Language :: Python :: 3" , "Topic :: Software Development :: Libraries :: Python Modules" , "Topic :: Text Processing :: Markup :: XML" ] # metadata , url = "http://github.com/delfick/python-dict2xml" , author = "Stephen Moore" , author_email = "stephen@delfick.com" , description = "Small utility to convert a python dictionary into an XML string" , long_description = open("README.rst").read() , license = "MIT" ) # fmt: on