123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- import datetime
- import re
- import sys
- from decimal import Decimal
- from toml.decoder import InlineTableDict
- if sys.version_info >= (3,):
- unicode = str
- def dump(o, f, encoder=None):
- """Writes out dict as toml to a file
- Args:
- o: Object to dump into toml
- f: File descriptor where the toml should be stored
- encoder: The ``TomlEncoder`` to use for constructing the output string
- Returns:
- String containing the toml corresponding to dictionary
- Raises:
- TypeError: When anything other than file descriptor is passed
- """
- if not f.write:
- raise TypeError("You can only dump an object to a file descriptor")
- d = dumps(o, encoder=encoder)
- f.write(d)
- return d
- def dumps(o, encoder=None):
- """Stringifies input dict as toml
- Args:
- o: Object to dump into toml
- encoder: The ``TomlEncoder`` to use for constructing the output string
- Returns:
- String containing the toml corresponding to dict
- Examples:
- ```python
- >>> import toml
- >>> output = {
- ... 'a': "I'm a string",
- ... 'b': ["I'm", "a", "list"],
- ... 'c': 2400
- ... }
- >>> toml.dumps(output)
- 'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n'
- ```
- """
- retval = ""
- if encoder is None:
- encoder = TomlEncoder(o.__class__)
- addtoretval, sections = encoder.dump_sections(o, "")
- retval += addtoretval
- outer_objs = [id(o)]
- while sections:
- section_ids = [id(section) for section in sections.values()]
- for outer_obj in outer_objs:
- if outer_obj in section_ids:
- raise ValueError("Circular reference detected")
- outer_objs += section_ids
- newsections = encoder.get_empty_table()
- for section in sections:
- addtoretval, addtosections = encoder.dump_sections(
- sections[section], section)
- if addtoretval or (not addtoretval and not addtosections):
- if retval and retval[-2:] != "\n\n":
- retval += "\n"
- retval += "[" + section + "]\n"
- if addtoretval:
- retval += addtoretval
- for s in addtosections:
- newsections[section + "." + s] = addtosections[s]
- sections = newsections
- return retval
- def _dump_str(v):
- if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str):
- v = v.decode('utf-8')
- v = "%r" % v
- if v[0] == 'u':
- v = v[1:]
- singlequote = v.startswith("'")
- if singlequote or v.startswith('"'):
- v = v[1:-1]
- if singlequote:
- v = v.replace("\\'", "'")
- v = v.replace('"', '\\"')
- v = v.split("\\x")
- while len(v) > 1:
- i = -1
- if not v[0]:
- v = v[1:]
- v[0] = v[0].replace("\\\\", "\\")
- # No, I don't know why != works and == breaks
- joinx = v[0][i] != "\\"
- while v[0][:i] and v[0][i] == "\\":
- joinx = not joinx
- i -= 1
- if joinx:
- joiner = "x"
- else:
- joiner = "u00"
- v = [v[0] + joiner + v[1]] + v[2:]
- return unicode('"' + v[0] + '"')
- def _dump_float(v):
- return "{}".format(v).replace("e+0", "e+").replace("e-0", "e-")
- def _dump_time(v):
- utcoffset = v.utcoffset()
- if utcoffset is None:
- return v.isoformat()
- # The TOML norm specifies that it's local time thus we drop the offset
- return v.isoformat()[:-6]
- class TomlEncoder(object):
- def __init__(self, _dict=dict, preserve=False):
- self._dict = _dict
- self.preserve = preserve
- self.dump_funcs = {
- str: _dump_str,
- unicode: _dump_str,
- list: self.dump_list,
- bool: lambda v: unicode(v).lower(),
- int: lambda v: v,
- float: _dump_float,
- Decimal: _dump_float,
- datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'),
- datetime.time: _dump_time,
- datetime.date: lambda v: v.isoformat()
- }
- def get_empty_table(self):
- return self._dict()
- def dump_list(self, v):
- retval = "["
- for u in v:
- retval += " " + unicode(self.dump_value(u)) + ","
- retval += "]"
- return retval
- def dump_inline_table(self, section):
- """Preserve inline table in its compact syntax instead of expanding
- into subsection.
- https://github.com/toml-lang/toml#user-content-inline-table
- """
- retval = ""
- if isinstance(section, dict):
- val_list = []
- for k, v in section.items():
- val = self.dump_inline_table(v)
- val_list.append(k + " = " + val)
- retval += "{ " + ", ".join(val_list) + " }\n"
- return retval
- else:
- return unicode(self.dump_value(section))
- def dump_value(self, v):
- # Lookup function corresponding to v's type
- dump_fn = self.dump_funcs.get(type(v))
- if dump_fn is None and hasattr(v, '__iter__'):
- dump_fn = self.dump_funcs[list]
- # Evaluate function (if it exists) else return v
- return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v)
- def dump_sections(self, o, sup):
- retstr = ""
- if sup != "" and sup[-1] != ".":
- sup += '.'
- retdict = self._dict()
- arraystr = ""
- for section in o:
- section = unicode(section)
- qsection = section
- if not re.match(r'^[A-Za-z0-9_-]+$', section):
- qsection = _dump_str(section)
- if not isinstance(o[section], dict):
- arrayoftables = False
- if isinstance(o[section], list):
- for a in o[section]:
- if isinstance(a, dict):
- arrayoftables = True
- if arrayoftables:
- for a in o[section]:
- arraytabstr = "\n"
- arraystr += "[[" + sup + qsection + "]]\n"
- s, d = self.dump_sections(a, sup + qsection)
- if s:
- if s[0] == "[":
- arraytabstr += s
- else:
- arraystr += s
- while d:
- newd = self._dict()
- for dsec in d:
- s1, d1 = self.dump_sections(d[dsec], sup +
- qsection + "." +
- dsec)
- if s1:
- arraytabstr += ("[" + sup + qsection +
- "." + dsec + "]\n")
- arraytabstr += s1
- for s1 in d1:
- newd[dsec + "." + s1] = d1[s1]
- d = newd
- arraystr += arraytabstr
- else:
- if o[section] is not None:
- retstr += (qsection + " = " +
- unicode(self.dump_value(o[section])) + '\n')
- elif self.preserve and isinstance(o[section], InlineTableDict):
- retstr += (qsection + " = " +
- self.dump_inline_table(o[section]))
- else:
- retdict[qsection] = o[section]
- retstr += arraystr
- return (retstr, retdict)
- class TomlPreserveInlineDictEncoder(TomlEncoder):
- def __init__(self, _dict=dict):
- super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True)
- class TomlArraySeparatorEncoder(TomlEncoder):
- def __init__(self, _dict=dict, preserve=False, separator=","):
- super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve)
- if separator.strip() == "":
- separator = "," + separator
- elif separator.strip(' \t\n\r,'):
- raise ValueError("Invalid separator for arrays")
- self.separator = separator
- def dump_list(self, v):
- t = []
- retval = "["
- for u in v:
- t.append(self.dump_value(u))
- while t != []:
- s = []
- for u in t:
- if isinstance(u, list):
- for r in u:
- s.append(r)
- else:
- retval += " " + unicode(u) + self.separator
- t = s
- retval += "]"
- return retval
- class TomlNumpyEncoder(TomlEncoder):
- def __init__(self, _dict=dict, preserve=False):
- import numpy as np
- super(TomlNumpyEncoder, self).__init__(_dict, preserve)
- self.dump_funcs[np.float16] = _dump_float
- self.dump_funcs[np.float32] = _dump_float
- self.dump_funcs[np.float64] = _dump_float
- self.dump_funcs[np.int16] = self._dump_int
- self.dump_funcs[np.int32] = self._dump_int
- self.dump_funcs[np.int64] = self._dump_int
- def _dump_int(self, v):
- return "{}".format(int(v))
- class TomlPreserveCommentEncoder(TomlEncoder):
- def __init__(self, _dict=dict, preserve=False):
- from toml.decoder import CommentValue
- super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve)
- self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value)
- class TomlPathlibEncoder(TomlEncoder):
- def _dump_pathlib_path(self, v):
- return _dump_str(str(v))
- def dump_value(self, v):
- if (3, 4) <= sys.version_info:
- import pathlib
- if isinstance(v, pathlib.PurePath):
- v = str(v)
- return super(TomlPathlibEncoder, self).dump_value(v)
|