aboutsummaryrefslogtreecommitdiffstats
path: root/python/toml/toml.py
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2014-08-28 09:34:23 -0600
committerJack Moffitt <jack@metajack.im>2014-09-08 20:21:42 -0600
commitc6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch)
treed1d74076cf7fa20e4f77ec7cb82cae98b67362cb /python/toml/toml.py
parentdb2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff)
downloadservo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz
servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip
Cargoify servo
Diffstat (limited to 'python/toml/toml.py')
-rw-r--r--python/toml/toml.py443
1 files changed, 443 insertions, 0 deletions
diff --git a/python/toml/toml.py b/python/toml/toml.py
new file mode 100644
index 00000000000..8b3ecb67a2a
--- /dev/null
+++ b/python/toml/toml.py
@@ -0,0 +1,443 @@
+import datetime, decimal
+
+try:
+ _range = xrange
+except NameError:
+ unicode = str
+ _range = range
+ basestring = str
+ unichr = chr
+
+def load(f):
+ """Returns a dictionary containing the named file parsed as toml."""
+ if isinstance(f, basestring):
+ with open(f) as ffile:
+ return loads(ffile.read())
+ elif isinstance(f, list):
+ for l in f:
+ if not isinstance(l, basestring):
+ raise Exception("Load expects a list to contain filenames only")
+ d = []
+ for l in f:
+ d.append(load(l))
+ r = {}
+ for l in d:
+ toml_merge_dict(r, l)
+ return r
+ elif f.read:
+ return loads(f.read())
+ else:
+ raise Exception("You can only load a file descriptor, filename or list")
+
+def loads(s):
+ """Returns a dictionary containing s, a string, parsed as toml."""
+ implicitgroups = []
+ retval = {}
+ currentlevel = retval
+ if isinstance(s, basestring):
+ try:
+ s.decode('utf8')
+ except AttributeError:
+ pass
+ sl = list(s)
+ openarr = 0
+ openstring = False
+ arrayoftables = True
+ beginline = True
+ keygroup = False
+ delnum = 1
+ for i in range(len(sl)):
+ if sl[i] == '"':
+ oddbackslash = False
+ try:
+ k = 1
+ j = sl[i-k]
+ oddbackslash = False
+ while j == '\\':
+ oddbackslash = not oddbackslash
+ k += 1
+ j = sl[i-k]
+ except IndexError:
+ pass
+ if not oddbackslash:
+ openstring = not openstring
+ if keygroup and (sl[i] == ' ' or sl[i] == '\t'):
+ keygroup = False
+ if arrayoftables and (sl[i] == ' ' or sl[i] == '\t'):
+ arrayoftables = False
+ if sl[i] == '#' and not openstring and not keygroup and not arrayoftables:
+ j = i
+ while sl[j] != '\n':
+ sl.insert(j, ' ')
+ sl.pop(j+1)
+ j += 1
+ if sl[i] == '[' and not openstring and not keygroup and not arrayoftables:
+ if beginline:
+ if sl[i+1] == '[':
+ arrayoftables = True
+ else:
+ keygroup = True
+ else:
+ openarr += 1
+ if sl[i] == ']' and not openstring and not keygroup and not arrayoftables:
+ if keygroup:
+ keygroup = False
+ elif arrayoftables:
+ if sl[i-1] == ']':
+ arrayoftables = False
+ else:
+ openarr -= 1
+ if sl[i] == '\n':
+ if openstring:
+ raise Exception("Unbalanced quotes")
+ if openarr:
+ sl.insert(i, ' ')
+ sl.pop(i+1)
+ else:
+ beginline = True
+ elif beginline and sl[i] != ' ' and sl[i] != '\t':
+ beginline = False
+ keygroup = True
+ s = ''.join(sl)
+ s = s.split('\n')
+ else:
+ raise Exception("What exactly are you trying to pull?")
+ for line in s:
+ line = line.strip()
+ if line == "":
+ continue
+ if line[0] == '[':
+ arrayoftables = False
+ if line[1] == '[':
+ arrayoftables = True
+ line = line[2:].split(']]', 1)
+ else:
+ line = line[1:].split(']', 1)
+ if line[1].strip() != "":
+ raise Exception("Key group not on a line by itself.")
+ line = line[0]
+ if '[' in line:
+ raise Exception("Key group name cannot contain '['")
+ if ']' in line:
+ raise Exception("Key group name cannot contain']'")
+ groups = line.split('.')
+ currentlevel = retval
+ for i in range(len(groups)):
+ group = groups[i]
+ if group == "":
+ raise Exception("Can't have a keygroup with an empty name")
+ try:
+ currentlevel[group]
+ if i == len(groups) - 1:
+ if group in implicitgroups:
+ implicitgroups.remove(group)
+ if arrayoftables:
+ raise Exception("An implicitly defined table can't be an array")
+ elif arrayoftables:
+ currentlevel[group].append({})
+ else:
+ raise Exception("What? "+group+" already exists?"+str(currentlevel))
+ except TypeError:
+ if i != len(groups) - 1:
+ implicitgroups.append(group)
+ currentlevel = currentlevel[0]
+ if arrayoftables:
+ currentlevel[group] = [{}]
+ else:
+ currentlevel[group] = {}
+ except KeyError:
+ if i != len(groups) - 1:
+ implicitgroups.append(group)
+ currentlevel[group] = {}
+ if i == len(groups) - 1 and arrayoftables:
+ currentlevel[group] = [{}]
+ currentlevel = currentlevel[group]
+ if arrayoftables:
+ try:
+ currentlevel = currentlevel[-1]
+ except KeyError:
+ pass
+ elif "=" in line:
+ i = 1
+ pair = line.split('=', i)
+ l = len(line)
+ while pair[-1][0] != ' ' and pair[-1][0] != '\t' and pair[-1][0] != '"' and pair[-1][0] != '[' and pair[-1] != 'true' and pair[-1] != 'false':
+ try:
+ float(pair[-1])
+ break
+ except ValueError:
+ try:
+ datetime.datetime.strptime(pair[-1], "%Y-%m-%dT%H:%M:%SZ")
+ break
+ except ValueError:
+ i += 1
+ pair = line.split('=', i)
+ newpair = []
+ newpair.append('='.join(pair[:-1]))
+ newpair.append(pair[-1])
+ pair = newpair
+ pair[0] = pair[0].strip()
+ pair[1] = pair[1].strip()
+ value, vtype = load_value(pair[1])
+ try:
+ currentlevel[pair[0]]
+ raise Exception("Duplicate keys!")
+ except KeyError:
+ currentlevel[pair[0]] = value
+ return retval
+
+def load_value(v):
+ if v == 'true':
+ return (True, "bool")
+ elif v == 'false':
+ return (False, "bool")
+ elif v[0] == '"':
+ testv = v[1:].split('"')
+ closed = False
+ for tv in testv:
+ if tv == '':
+ closed = True
+ else:
+ oddbackslash = False
+ try:
+ i = -1
+ j = tv[i]
+ while j == '\\':
+ oddbackslash = not oddbackslash
+ i -= 1
+ j = tv[i]
+ except IndexError:
+ pass
+ if not oddbackslash:
+ if closed:
+ raise Exception("Stuff after closed string. WTF?")
+ else:
+ closed = True
+ escapes = ['0', 'b', 'f', '/', 'n', 'r', 't', '"', '\\']
+ escapedchars = ['\0', '\b', '\f', '/', '\n', '\r', '\t', '\"', '\\']
+ escapeseqs = v.split('\\')[1:]
+ backslash = False
+ for i in escapeseqs:
+ if i == '':
+ backslash = not backslash
+ else:
+ if i[0] not in escapes and i[0] != 'u' and not backslash:
+ raise Exception("Reserved escape sequence used")
+ if backslash:
+ backslash = False
+ if "\\u" in v:
+ hexchars = ['0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
+ hexbytes = v.split('\\u')
+ newv = hexbytes[0]
+ hexbytes = hexbytes[1:]
+ for hx in hexbytes:
+ hxb = ""
+ try:
+ if hx[0].lower() in hexchars:
+ hxb += hx[0].lower()
+ if hx[1].lower() in hexchars:
+ hxb += hx[1].lower()
+ if hx[2].lower() in hexchars:
+ hxb += hx[2].lower()
+ if hx[3].lower() in hexchars:
+ hxb += hx[3].lower()
+ except IndexError:
+ if len(hxb) != 2:
+ raise Exception("Invalid escape sequence")
+ if len(hxb) != 4 and len(hxb) != 2:
+ raise Exception("Invalid escape sequence")
+ newv += unichr(int(hxb, 16))
+ newv += unicode(hx[len(hxb):])
+ v = newv
+ for i in range(len(escapes)):
+ v = v.replace("\\"+escapes[i], escapedchars[i])
+ # (where (n) signifies a member of escapes:
+ # undo (\\)(\\)(n) -> (\\)(\n)
+ v = v.replace("\\"+escapedchars[i], "\\\\"+escapes[i])
+ return (v[1:-1], "str")
+ elif v[0] == '[':
+ return (load_array(v), "array")
+ elif len(v) == 20 and v[-1] == 'Z':
+ if v[10] == 'T':
+ return (datetime.datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ"), "date")
+ else:
+ raise Exception("Wait, what?")
+ else:
+ itype = "int"
+ digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+ neg = False
+ if v[0] == '-':
+ neg = True
+ v = v[1:]
+ if '.' in v:
+ if v.split('.', 1)[1] == '':
+ raise Exception("This float is missing digits after the point")
+ if v[0] not in digits:
+ raise Exception("This float doesn't have a leading digit")
+ v = float(v)
+ itype = "float"
+ else:
+ v = int(v)
+ if neg:
+ return (0 - v, itype)
+ return (v, itype)
+
+
+def load_array(a):
+ atype = None
+ retval = []
+ a = a.strip()
+ if '[' not in a[1:-1]:
+ strarray = False
+ tmpa = a[1:-1].strip()
+ if tmpa != '' and tmpa[0] == '"':
+ strarray = True
+ a = a[1:-1].split(',')
+ b = 0
+ if strarray:
+ while b < len(a) - 1:
+ while a[b].strip()[-1] != '"' and a[b+1].strip()[0] != '"':
+ a[b] = a[b] + ',' + a[b+1]
+ if b < len(a) - 2:
+ a = a[:b+1] + a[b+2:]
+ else:
+ a = a[:b+1]
+ b += 1
+ else:
+ al = list(a[1:-1])
+ a = []
+ openarr = 0
+ j = 0
+ for i in range(len(al)):
+ if al[i] == '[':
+ openarr += 1
+ elif al[i] == ']':
+ openarr -= 1
+ elif al[i] == ',' and not openarr:
+ a.append(''.join(al[j:i]))
+ j = i+1
+ a.append(''.join(al[j:]))
+ for i in range(len(a)):
+ a[i] = a[i].strip()
+ if a[i] != '':
+ nval, ntype = load_value(a[i])
+ if atype:
+ if ntype != atype:
+ raise Exception("Not a homogeneous array")
+ else:
+ atype = ntype
+ retval.append(nval)
+ return retval
+
+def dump(o, f):
+ """Writes out to f the toml corresponding to o. Returns said toml."""
+ if f.write:
+ d = dumps(o)
+ f.write(d)
+ return d
+ else:
+ raise Exception("You can only dump an object to a file descriptor")
+
+def dumps(o):
+ """Returns a string containing the toml corresponding to o, a dictionary"""
+ retval = ""
+ addtoretval, sections = dump_sections(o, "")
+ retval += addtoretval
+ while sections != {}:
+ newsections = {}
+ for section in sections:
+ addtoretval, addtosections = dump_sections(sections[section], section)
+ if addtoretval:
+ retval += "["+section+"]\n"
+ retval += addtoretval
+ for s in addtosections:
+ newsections[section+"."+s] = addtosections[s]
+ sections = newsections
+ return retval
+
+def dump_sections(o, sup):
+ retstr = ""
+ if sup != "" and sup[-1] != ".":
+ sup += '.'
+ retdict = {}
+ arraystr = ""
+ for section in o:
+ 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 = ""
+ arraystr += "[["+sup+section+"]]\n"
+ s, d = dump_sections(a, sup+section)
+ if s:
+ if s[0] == "[":
+ arraytabstr += s
+ else:
+ arraystr += s
+ while d != {}:
+ newd = {}
+ for dsec in d:
+ s1, d1 = dump_sections(d[dsec], sup+section+dsec)
+ if s1:
+ arraytabstr += "["+sup+section+"."+dsec+"]\n"
+ arraytabstr += s1
+ for s1 in d1:
+ newd[dsec+"."+s1] = d1[s1]
+ d = newd
+ arraystr += arraytabstr
+ else:
+ retstr += section + " = " + str(dump_value(o[section])) + '\n'
+ else:
+ retdict[section] = o[section]
+ retstr += arraystr
+ return (retstr, retdict)
+
+def dump_value(v):
+ if isinstance(v, list):
+ t = []
+ retval = "["
+ for u in v:
+ t.append(dump_value(u))
+ while t != []:
+ s = []
+ for u in t:
+ if isinstance(u, list):
+ for r in u:
+ s.append(r)
+ else:
+ retval += " " + str(u) + ","
+ t = s
+ retval += "]"
+ return retval
+ if isinstance(v, (str, unicode)):
+ escapes = ['\\', '0', 'b', 'f', '/', 'n', 'r', 't', '"']
+ escapedchars = ['\\', '\0', '\b', '\f', '/', '\n', '\r', '\t', '\"']
+ for i in range(len(escapes)):
+ v = v.replace(escapedchars[i], "\\"+escapes[i])
+ return str('"'+v+'"')
+ if isinstance(v, bool):
+ return str(v).lower()
+ if isinstance(v, datetime.datetime):
+ return v.isoformat()[:19]+'Z'
+ if isinstance(v, float):
+ return '{0:f}'.format(decimal.Decimal(str(v)))
+ return v
+
+def toml_merge_dict(a, b):
+ for k in a:
+ if isinstance(a[k], dict):
+ try:
+ b[k]
+ except KeyError:
+ continue
+ if isinstance(b[k], dict):
+ b[k] = toml_merge_dict(a[k], b[k])
+ else:
+ raise Exception("Can't merge dict and nondict in toml object")
+ a.update(b)
+ return a