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