Feature: Enforce PEP8 standard specified in .flake8 for toml.py

With the exception that the `loads()` function triggers:
```toml.py:107:1: C901 'loads' is too complex (87)
def loads(s, _dict=dict):
```

This commit is based off PR #99.
This commit is contained in:
x10an14
2017-05-17 13:16:39 +02:00
committed by Uiri
parent b3abd4c356
commit 16e4b1d102
2 changed files with 115 additions and 42 deletions

42
.flake8 Normal file
View File

@@ -0,0 +1,42 @@
[flake8]
exclude =
# Do not track .git dir
.git,
# Do not track virtualenv dir
venv,
# Do not track pycache dirs
__pycache__,
# A nested complexity of more than 5 is a sign that we should perhaps
# flatten out the code's cyclomatic complexity...
max-complexity = 5
# All modern monitors should at least handle 120 chars in a row without wrapping
max-line-length = 120
# Default errors ++: http://pep8.readthedocs.io/en/release-1.7.x/intro.html
# (Contains E/W error codes, at least some)
# DXYZ error codes listed here: http://pep257.readthedocs.io/en/latest/error_codes.html
ignore =
D204, # Not Default
E121,
E126,
E133,
E226,
W503,
# Which errors to enable (display)
#select-errors =
E,
F,
W,
C
# Ignore '#noqa' pragmas
disable-noqa = True
# Output config:
show-source = True
statistics = True
tee = True
output-file = .flake8.log

115
toml.py
View File

@@ -1,5 +1,7 @@
# This software is released under the MIT license """Python module which parses and emits TOML.
Released under the MIT license.
"""
import re import re
import io import io
import datetime import datetime
@@ -9,11 +11,13 @@ from os import linesep
__version__ = "0.9.2" __version__ = "0.9.2"
__spec__ = "0.4.0" __spec__ = "0.4.0"
class TomlDecodeError(Exception): class TomlDecodeError(Exception):
"""Base toml Exception / Error."""
pass pass
class TomlTz(datetime.tzinfo):
class TomlTz(datetime.tzinfo):
def __init__(self, toml_offset): def __init__(self, toml_offset):
if toml_offset == "Z": if toml_offset == "Z":
self._raw_offset = "+00:00" self._raw_offset = "+00:00"
@@ -24,7 +28,7 @@ class TomlTz(datetime.tzinfo):
self._minutes = int(self._raw_offset[4:6]) self._minutes = int(self._raw_offset[4:6])
def tzname(self, dt): def tzname(self, dt):
return "UTC"+self._raw_offset return "UTC" + self._raw_offset
def utcoffset(self, dt): def utcoffset(self, dt):
return self._sign * datetime.timedelta(hours=self._hours, minutes=self._minutes) return self._sign * datetime.timedelta(hours=self._hours, minutes=self._minutes)
@@ -32,9 +36,11 @@ class TomlTz(datetime.tzinfo):
def dst(self, dt): def dst(self, dt):
return datetime.timedelta(0) return datetime.timedelta(0)
class InlineTableDict(object): class InlineTableDict(object):
"""Sentinel subclass of dict for inline tables.""" """Sentinel subclass of dict for inline tables."""
def _get_empty_inline_table(_dict): def _get_empty_inline_table(_dict):
class DynamicInlineTableDict(_dict, InlineTableDict): class DynamicInlineTableDict(_dict, InlineTableDict):
"""Concrete sentinel subclass for inline tables. """Concrete sentinel subclass for inline tables.
@@ -44,6 +50,7 @@ def _get_empty_inline_table(_dict):
return DynamicInlineTableDict() return DynamicInlineTableDict()
try: try:
_range = xrange _range = xrange
except NameError: except NameError:
@@ -52,6 +59,7 @@ except NameError:
basestring = str basestring = str
unichr = chr unichr = chr
def load(f, _dict=dict): def load(f, _dict=dict):
"""Parses named file or files as toml and returns a dictionary """Parses named file or files as toml and returns a dictionary
@@ -94,8 +102,10 @@ def load(f, _dict=dict):
else: else:
raise TypeError("You can only load a file descriptor, filename or list") raise TypeError("You can only load a file descriptor, filename or list")
_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') _groupname_re = re.compile(r'^[A-Za-z0-9_-]+$')
def loads(s, _dict=dict): def loads(s, _dict=dict):
"""Parses string as toml """Parses string as toml
@@ -130,7 +140,7 @@ def loads(s, _dict=dict):
keygroup = False keygroup = False
keyname = 0 keyname = 0
for i, item in enumerate(sl): for i, item in enumerate(sl):
if item == '\r' and sl[i+1] == '\n': if item == '\r' and sl[i + 1] == '\n':
sl[i] = ' ' sl[i] = ' '
continue continue
if keyname: if keyname:
@@ -153,11 +163,11 @@ def loads(s, _dict=dict):
if item == '=': if item == '=':
keyname = 0 keyname = 0
else: else:
raise TomlDecodeError("Found invalid character in key name: '"+item+"'. Try quoting the key name.") raise TomlDecodeError("Found invalid character in key name: '" + item + "'. Try quoting the key name.")
if item == "'" and openstrchar != '"': if item == "'" and openstrchar != '"':
k = 1 k = 1
try: try:
while sl[i-k] == "'": while sl[i - k] == "'":
k += 1 k += 1
if k == 3: if k == 3:
break break
@@ -177,12 +187,12 @@ def loads(s, _dict=dict):
k = 1 k = 1
tripquote = False tripquote = False
try: try:
while sl[i-k] == '"': while sl[i - k] == '"':
k += 1 k += 1
if k == 3: if k == 3:
tripquote = True tripquote = True
break break
while sl[i-k] == '\\': while sl[i - k] == '\\':
oddbackslash = not oddbackslash oddbackslash = not oddbackslash
k += 1 k += 1
except IndexError: except IndexError:
@@ -209,7 +219,7 @@ def loads(s, _dict=dict):
if item == '[' and not openstring and not keygroup and \ if item == '[' and not openstring and not keygroup and \
not arrayoftables: not arrayoftables:
if beginline: if beginline:
if sl[i+1] == '[': if sl[i + 1] == '[':
arrayoftables = True arrayoftables = True
else: else:
keygroup = True keygroup = True
@@ -219,7 +229,7 @@ def loads(s, _dict=dict):
if keygroup: if keygroup:
keygroup = False keygroup = False
elif arrayoftables: elif arrayoftables:
if sl[i-1] == ']': if sl[i - 1] == ']':
arrayoftables = False arrayoftables = False
else: else:
openarr -= 1 openarr -= 1
@@ -227,10 +237,10 @@ def loads(s, _dict=dict):
if openstring or multilinestr: if openstring or multilinestr:
if not multilinestr: if not multilinestr:
raise TomlDecodeError("Unbalanced quotes") raise TomlDecodeError("Unbalanced quotes")
if (sl[i-1] == "'" or sl[i-1] == '"') and sl[i-2] == sl[i-1]: if (sl[i - 1] == "'" or sl[i - 1] == '"') and sl[i - 2] == sl[i - 1]:
sl[i] = sl[i-1] sl[i] = sl[i - 1]
if sl[i-3] == sl[i-1]: if sl[i - 3] == sl[i - 1]:
sl[i-3] = ' ' sl[i - 3] = ' '
elif openarr: elif openarr:
sl[i] = ' ' sl[i] = ' '
else: else:
@@ -264,7 +274,7 @@ def loads(s, _dict=dict):
multikey = None multikey = None
multilinestr = "" multilinestr = ""
else: else:
k = len(multilinestr) -1 k = len(multilinestr) - 1
while k > -1 and multilinestr[k] == '\\': while k > -1 and multilinestr[k] == '\\':
multibackslash = not multibackslash multibackslash = not multibackslash
k -= 1 k -= 1
@@ -288,15 +298,15 @@ def loads(s, _dict=dict):
groups[i] = groups[i].strip() groups[i] = groups[i].strip()
if groups[i][0] == '"' or groups[i][0] == "'": if groups[i][0] == '"' or groups[i][0] == "'":
groupstr = groups[i] groupstr = groups[i]
j = i+1 j = i + 1
while not groupstr[0] == groupstr[-1]: while not groupstr[0] == groupstr[-1]:
j += 1 j += 1
groupstr = '.'.join(groups[i:j]) groupstr = '.'.join(groups[i:j])
groups[i] = groupstr[1:-1] groups[i] = groupstr[1:-1]
groups[i+1:j] = [] groups[i + 1:j] = []
else: else:
if not _groupname_re.match(groups[i]): if not _groupname_re.match(groups[i]):
raise TomlDecodeError("Invalid group name '"+groups[i]+"'. Try quoting it.") raise TomlDecodeError("Invalid group name '" + groups[i] + "'. Try quoting it.")
i += 1 i += 1
currentlevel = retval currentlevel = retval
for i in _range(len(groups)): for i in _range(len(groups)):
@@ -313,7 +323,7 @@ def loads(s, _dict=dict):
elif arrayoftables: elif arrayoftables:
currentlevel[group].append(_dict()) currentlevel[group].append(_dict())
else: else:
raise TomlDecodeError("What? "+group+" already exists?"+str(currentlevel)) raise TomlDecodeError("What? " + group + " already exists?" + str(currentlevel))
except TypeError: except TypeError:
if i != len(groups) - 1: if i != len(groups) - 1:
implicitgroups.append(group) implicitgroups.append(group)
@@ -346,6 +356,7 @@ def loads(s, _dict=dict):
multikey, multilinestr, multibackslash = ret multikey, multilinestr, multibackslash = ret
return retval return retval
def _load_inline_object(line, currentlevel, _dict, multikey=False, multibackslash=False): def _load_inline_object(line, currentlevel, _dict, multikey=False, multibackslash=False):
candidate_groups = line[1:-1].split(",") candidate_groups = line[1:-1].split(",")
groups = [] groups = []
@@ -370,9 +381,11 @@ def _load_inline_object(line, currentlevel, _dict, multikey=False, multibackslas
if status is not None: if status is not None:
break break
# Matches a TOML number, which allows underscores for readability # Matches a TOML number, which allows underscores for readability
_number_with_underscores = re.compile('([0-9])(_([0-9]))*') _number_with_underscores = re.compile('([0-9])(_([0-9]))*')
def _strictly_valid_num(n): def _strictly_valid_num(n):
n = n.strip() n = n.strip()
if not n: if not n:
@@ -395,6 +408,7 @@ def _strictly_valid_num(n):
return False return False
return True return True
def _load_line(line, currentlevel, _dict, multikey, multibackslash): def _load_line(line, currentlevel, _dict, multikey, multibackslash):
i = 1 i = 1
pair = line.split('=', i) pair = line.split('=', i)
@@ -425,10 +439,10 @@ def _load_line(line, currentlevel, _dict, multikey, multibackslash):
pair[0] = pair[0][1:-1] pair[0] = pair[0][1:-1]
if len(pair[1]) > 2 and (pair[1][0] == '"' or pair[1][0] == "'") \ if len(pair[1]) > 2 and (pair[1][0] == '"' or pair[1][0] == "'") \
and pair[1][1] == pair[1][0] and pair[1][2] == pair[1][0] \ and pair[1][1] == pair[1][0] and pair[1][2] == pair[1][0] \
and not (len(pair[1]) > 5 and pair[1][-1] == pair[1][0] and \ and not (len(pair[1]) > 5 and pair[1][-1] == pair[1][0] and
pair[1][-2] == pair[1][0] and \ pair[1][-2] == pair[1][0] and
pair[1][-3] == pair[1][0]): pair[1][-3] == pair[1][0]):
k = len(pair[1]) -1 k = len(pair[1]) - 1
while k > -1 and pair[1][k] == '\\': while k > -1 and pair[1][k] == '\\':
multibackslash = not multibackslash multibackslash = not multibackslash
k -= 1 k -= 1
@@ -450,6 +464,7 @@ def _load_line(line, currentlevel, _dict, multikey, multibackslash):
except: except:
raise TomlDecodeError("Duplicate keys!") raise TomlDecodeError("Duplicate keys!")
def _load_date(val): def _load_date(val):
microsecond = 0 microsecond = 0
tz = None tz = None
@@ -464,11 +479,15 @@ def _load_date(val):
except ValueError: except ValueError:
tz = None tz = None
try: try:
d = datetime.datetime(int(val[:4]), int(val[5:7]), int(val[8:10]), int(val[11:13]), int(val[14:16]), int(val[17:19]), microsecond, tz) d = datetime.datetime(
int(val[:4]), int(val[5:7]),
int(val[8:10]), int(val[11:13]),
int(val[14:16]), int(val[17:19]), microsecond, tz)
except ValueError: except ValueError:
return None return None
return d return d
def _load_unicode_escapes(v, hexbytes, prefix): def _load_unicode_escapes(v, hexbytes, prefix):
hexchars = ['0', '1', '2', '3', '4', '5', '6', '7', hexchars = ['0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
@@ -504,10 +523,12 @@ def _load_unicode_escapes(v, hexbytes, prefix):
v += unicode(hx[len(hxb):]) v += unicode(hx[len(hxb):])
return v return v
# Unescape TOML string values. # Unescape TOML string values.
_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] # content after the \ _escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] # content after the \
_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] # What it should be replaced by _escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] # What it should be replaced by
_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) # Used for substitution _escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) # Used for substitution
def _unescape(v): def _unescape(v):
"""Unescape characters in a TOML string.""" """Unescape characters in a TOML string."""
@@ -517,9 +538,9 @@ def _unescape(v):
if backslash: if backslash:
backslash = False backslash = False
if v[i] in _escapes: if v[i] in _escapes:
v = v[:i-1] + _escape_to_escapedchars[v[i]] + v[i+1:] v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:]
elif v[i] == '\\': elif v[i] == '\\':
v = v[:i-1] + v[i:] v = v[:i - 1] + v[i:]
elif v[i] == 'u' or v[i] == 'U': elif v[i] == 'u' or v[i] == 'U':
i += 1 i += 1
else: else:
@@ -530,6 +551,7 @@ def _unescape(v):
i += 1 i += 1
return v return v
def _load_value(v, _dict, strictly_valid=True): def _load_value(v, _dict, strictly_valid=True):
if not v: if not v:
raise TomlDecodeError("Empty value is invalid") raise TomlDecodeError("Empty value is invalid")
@@ -618,6 +640,7 @@ def _load_value(v, _dict, strictly_valid=True):
return (0 - v, itype) return (0 - v, itype)
return (v, itype) return (v, itype)
def _load_array(a, _dict): def _load_array(a, _dict):
atype = None atype = None
retval = [] retval = []
@@ -657,12 +680,12 @@ def _load_array(a, _dict):
ab = a[b].strip() ab = a[b].strip()
while ab[-1] != ab[0] or (ab[0] == ab[1] == ab[2] and while ab[-1] != ab[0] or (ab[0] == ab[1] == ab[2] and
ab[-2] != ab[0] and ab[-3] != ab[0]): ab[-2] != ab[0] and ab[-3] != ab[0]):
a[b] = a[b] + ',' + a[b+1] a[b] = a[b] + ',' + a[b + 1]
ab = a[b].strip() ab = a[b].strip()
if b < len(a) - 2: if b < len(a) - 2:
a = a[:b+1] + a[b+2:] a = a[:b + 1] + a[b + 2:]
else: else:
a = a[:b+1] a = a[:b + 1]
b += 1 b += 1
else: else:
al = list(a[1:-1]) al = list(a[1:-1])
@@ -676,7 +699,7 @@ def _load_array(a, _dict):
openarr -= 1 openarr -= 1
elif al[i] == ',' and not openarr: elif al[i] == ',' and not openarr:
a.append(''.join(al[j:i])) a.append(''.join(al[j:i]))
j = i+1 j = i + 1
a.append(''.join(al[j:])) a.append(''.join(al[j:]))
for i in _range(len(a)): for i in _range(len(a)):
a[i] = a[i].strip() a[i] = a[i].strip()
@@ -690,6 +713,7 @@ def _load_array(a, _dict):
retval.append(nval) retval.append(nval)
return retval return retval
def dump(o, f): def dump(o, f):
"""Writes out dict as toml to a file """Writes out dict as toml to a file
@@ -710,6 +734,7 @@ def dump(o, f):
f.write(d) f.write(d)
return d return d
def dumps(o, preserve=False): def dumps(o, preserve=False):
"""Stringifies input dict as toml """Stringifies input dict as toml
@@ -733,14 +758,15 @@ def dumps(o, preserve=False):
if addtoretval or (not addtoretval and not addtosections): if addtoretval or (not addtoretval and not addtosections):
if retval and retval[-2:] != "\n\n": if retval and retval[-2:] != "\n\n":
retval += "\n" retval += "\n"
retval += "["+section+"]\n" retval += "[" + section + "]\n"
if addtoretval: if addtoretval:
retval += addtoretval retval += addtoretval
for s in addtosections: for s in addtosections:
newsections[section+"."+s] = addtosections[s] newsections[section + "." + s] = addtosections[s]
sections = newsections sections = newsections
return retval return retval
def _dump_sections(o, sup, preserve=False): def _dump_sections(o, sup, preserve=False):
retstr = "" retstr = ""
if sup != "" and sup[-1] != ".": if sup != "" and sup[-1] != ".":
@@ -763,8 +789,8 @@ def _dump_sections(o, sup, preserve=False):
if arrayoftables: if arrayoftables:
for a in o[section]: for a in o[section]:
arraytabstr = "\n" arraytabstr = "\n"
arraystr += "[["+sup+qsection+"]]\n" arraystr += "[[" + sup + qsection + "]]\n"
s, d = _dump_sections(a, sup+qsection) s, d = _dump_sections(a, sup + qsection)
if s: if s:
if s[0] == "[": if s[0] == "[":
arraytabstr += s arraytabstr += s
@@ -773,12 +799,12 @@ def _dump_sections(o, sup, preserve=False):
while d != {}: while d != {}:
newd = {} newd = {}
for dsec in d: for dsec in d:
s1, d1 = _dump_sections(d[dsec], sup+qsection+"."+dsec) s1, d1 = _dump_sections(d[dsec], sup + qsection + "." + dsec)
if s1: if s1:
arraytabstr += "["+sup+qsection+"."+dsec+"]\n" arraytabstr += "[" + sup + qsection + "." + dsec + "]\n"
arraytabstr += s1 arraytabstr += s1
for s1 in d1: for s1 in d1:
newd[dsec+"."+s1] = d1[s1] newd[dsec + "." + s1] = d1[s1]
d = newd d = newd
arraystr += arraytabstr arraystr += arraytabstr
else: else:
@@ -792,6 +818,7 @@ def _dump_sections(o, sup, preserve=False):
retstr += arraystr retstr += arraystr
return (retstr, retdict) return (retstr, retdict)
def _dump_inline_table(section): def _dump_inline_table(section):
"""Preserve inline table in its compact syntax instead of expanding """Preserve inline table in its compact syntax instead of expanding
into subsection. into subsection.
@@ -809,6 +836,7 @@ def _dump_inline_table(section):
else: else:
return str(_dump_value(section)) return str(_dump_value(section))
def _dump_value(v): def _dump_value(v):
dump_funcs = { dump_funcs = {
str: lambda: _dump_str(v), str: lambda: _dump_str(v),
@@ -816,13 +844,14 @@ def _dump_value(v):
list: lambda: _dump_list(v), list: lambda: _dump_list(v),
bool: lambda: str(v).lower(), bool: lambda: str(v).lower(),
float: lambda: _dump_float(v), float: lambda: _dump_float(v),
datetime.datetime: lambda: v.isoformat()[:19]+'Z', datetime.datetime: lambda: v.isoformat()[:19] + 'Z',
} }
# Lookup function corresponding to v's type # Lookup function corresponding to v's type
dump_fn = dump_funcs.get(type(v)) dump_fn = dump_funcs.get(type(v))
# Evaluate function (if it exists) else return v # Evaluate function (if it exists) else return v
return dump_fn() if dump_fn is not None else v return dump_fn() if dump_fn is not None else v
def _dump_str(v): def _dump_str(v):
v = "%r" % v v = "%r" % v
if v[0] == 'u': if v[0] == 'u':
@@ -833,7 +862,8 @@ def _dump_str(v):
v = v.replace("\\'", "'") v = v.replace("\\'", "'")
v = v.replace('"', '\\"') v = v.replace('"', '\\"')
v = v.replace("\\x", "\\u00") v = v.replace("\\x", "\\u00")
return str('"'+v+'"') return str('"' + v + '"')
def _dump_list(v): def _dump_list(v):
t = [] t = []
@@ -852,5 +882,6 @@ def _dump_list(v):
retval += "]" retval += "]"
return retval return retval
def _dump_float(v): def _dump_float(v):
return "{0:.16g}".format(v).replace("e+0", "e+").replace("e-0", "e-") return "{0:.16g}".format(v).replace("e+0", "e+").replace("e-0", "e-")