diff --git a/.travis.yml b/.travis.yml index a6fe14d..8ff53a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,11 +13,12 @@ matrix: env: TOX_ENV=wheezy install: - pip install -U pip - - pip install pyflakes tox + - pip install flake8 tox - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm script: - tox -e $TOX_ENV - - python -m pyflakes.__main__ $(git ls-files | grep -E "^isso/.+.py$" | grep -v "^isso/compat.py") + - IGNORE=E226,E241,E265,E402,E501,E704 + - flake8 . --count --ignore=${IGNORE} --max-line-length=127 --show-source --statistics notifications: irc: channels: diff --git a/docs/_isso/remove_heading.py b/docs/_isso/remove_heading.py index 48d7708..f15f5d5 100644 --- a/docs/_isso/remove_heading.py +++ b/docs/_isso/remove_heading.py @@ -9,4 +9,3 @@ class IssoTranslator(HTMLTranslator): if self.section_level == 1: raise nodes.SkipNode HTMLTranslator.visit_title(self, node) - diff --git a/docs/conf.py b/docs/conf.py index 0b85a07..0d0c005 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -200,22 +200,22 @@ htmlhelp_basename = 'Issodoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Isso.tex', u'Isso Documentation', - u'Martin Zimmermann', 'manual'), + ('index', 'Isso.tex', u'Isso Documentation', + u'Martin Zimmermann', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -260,9 +260,9 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Isso', u'Isso Documentation', - u'Martin Zimmermann', 'Isso', 'a commenting server similar to Disqus', - 'Miscellaneous'), + ('index', 'Isso', u'Isso Documentation', + u'Martin Zimmermann', 'Isso', 'a commenting server similar to Disqus', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. diff --git a/isso/__init__.py b/isso/__init__.py index 035cafd..0af5b43 100644 --- a/isso/__init__.py +++ b/isso/__init__.py @@ -35,7 +35,8 @@ import sys if sys.argv[0].startswith("isso"): try: - import gevent.monkey; gevent.monkey.patch_all() + import gevent.monkey + gevent.monkey.patch_all() except ImportError: pass @@ -87,7 +88,8 @@ class Isso(object): self.conf = conf self.db = db.SQLite3(conf.get('general', 'dbpath'), conf) - self.signer = URLSafeTimedSerializer(self.db.preferences.get("session-key")) + self.signer = URLSafeTimedSerializer( + self.db.preferences.get("session-key")) self.markup = html.Markup(conf.section('markup')) self.hasher = hash.new(conf.section("hash")) @@ -122,7 +124,8 @@ class Isso(object): local.request = request local.host = wsgi.host(request.environ) - local.origin = origin(self.conf.getiter("general", "host"))(request.environ) + local.origin = origin(self.conf.getiter( + "general", "host"))(request.environ) adapter = self.urls.bind_to_environ(request.environ) @@ -136,7 +139,8 @@ class Isso(object): except HTTPException as e: return e except Exception: - logger.exception("%s %s", request.method, request.environ["PATH_INFO"]) + logger.exception("%s %s", request.method, + request.environ["PATH_INFO"]) return InternalServerError() else: return response @@ -181,19 +185,19 @@ def make_app(conf=None, threading=True, multiprocessing=False, uwsgi=False): if isso.conf.getboolean("server", "profile"): wrapper.append(partial(ProfilerMiddleware, - sort_by=("cumulative", ), restrictions=("isso/(?!lib)", 10))) + sort_by=("cumulative", ), restrictions=("isso/(?!lib)", 10))) wrapper.append(partial(SharedDataMiddleware, exports={ '/js': join(dirname(__file__), 'js/'), '/css': join(dirname(__file__), 'css/'), '/img': join(dirname(__file__), 'img/'), '/demo': join(dirname(__file__), 'demo/') - })) + })) wrapper.append(partial(wsgi.CORSMiddleware, - origin=origin(isso.conf.getiter("general", "host")), - allowed=("Origin", "Referer", "Content-Type"), - exposed=("X-Set-Cookie", "Date"))) + origin=origin(isso.conf.getiter("general", "host")), + allowed=("Origin", "Referer", "Content-Type"), + exposed=("X-Set-Cookie", "Date"))) wrapper.extend([wsgi.SubURI, ProxyFix]) @@ -208,9 +212,10 @@ def main(): parser = ArgumentParser(description="a blog comment hosting service") subparser = parser.add_subparsers(help="commands", dest="command") - parser.add_argument('--version', action='version', version='%(prog)s ' + dist.version) + parser.add_argument('--version', action='version', + version='%(prog)s ' + dist.version) parser.add_argument("-c", dest="conf", default="/etc/isso.conf", - metavar="/etc/isso.conf", help="set configuration file") + metavar="/etc/isso.conf", help="set configuration file") imprt = subparser.add_parser('import', help="import Disqus XML export") imprt.add_argument("dump", metavar="FILE") @@ -225,7 +230,8 @@ def main(): subparser.add_parser("run", help="run server") args = parser.parse_args() - conf = config.load(join(dist.location, dist.project_name, "defaults.ini"), args.conf) + conf = config.load( + join(dist.location, dist.project_name, "defaults.ini"), args.conf) if args.command == "import": conf.set("guard", "enabled", "off") diff --git a/isso/compat.py b/isso/compat.py index e38ecc5..c93270d 100644 --- a/isso/compat.py +++ b/isso/compat.py @@ -12,11 +12,13 @@ except NameError: # Python 3 if not PY2K: buffer = memoryview filter, map, zip = filter, map, zip - iteritems = lambda dikt: iter(dikt.items()) # noqa: E731 + + def iteritems(dikt): return iter(dikt.items()) # noqa: E731 from functools import reduce else: buffer = buffer from itertools import ifilter, imap, izip filter, map, zip = ifilter, imap, izip - iteritems = lambda dikt: dikt.iteritems() # noqa: E731 + + def iteritems(dikt): return dikt.iteritems() # noqa: E731 reduce = reduce diff --git a/isso/config.py b/isso/config.py index c2a05ce..488224b 100644 --- a/isso/config.py +++ b/isso/config.py @@ -123,8 +123,8 @@ def new(options=None): def load(default, user=None): # return set of (section, option) - setify = lambda cp: set((section, option) for section in cp.sections() - for option in cp.options(section)) + def setify(cp): return set((section, option) for section in cp.sections() + for option in cp.options(section)) parser = new() parser.read(default) diff --git a/isso/core.py b/isso/core.py index 368c8e3..9992df9 100644 --- a/isso/core.py +++ b/isso/core.py @@ -122,7 +122,8 @@ class uWSGIMixin(Mixin): self.cache = uWSGICache timedelta = conf.getint("moderation", "purge-after") - purge = lambda signum: self.db.comments.purge(timedelta) + + def purge(signum): return self.db.comments.purge(timedelta) uwsgi.register_signal(1, "", purge) uwsgi.add_timer(1, timedelta) diff --git a/isso/db/__init__.py b/isso/db/__init__.py index 919137c..ecc7a44 100644 --- a/isso/db/__init__.py +++ b/isso/db/__init__.py @@ -98,10 +98,11 @@ class SQLite3: # limit max. nesting level to 1 if self.version == 2: - first = lambda rv: list(map(operator.itemgetter(0), rv)) + def first(rv): return list(map(operator.itemgetter(0), rv)) with sqlite3.connect(self.path) as con: - top = first(con.execute("SELECT id FROM comments WHERE parent IS NULL").fetchall()) + top = first(con.execute( + "SELECT id FROM comments WHERE parent IS NULL").fetchall()) flattened = defaultdict(set) for id in top: @@ -109,13 +110,15 @@ class SQLite3: ids = [id, ] while ids: - rv = first(con.execute("SELECT id FROM comments WHERE parent=?", (ids.pop(), ))) + rv = first(con.execute( + "SELECT id FROM comments WHERE parent=?", (ids.pop(), ))) ids.extend(rv) flattened[id].update(set(rv)) for id in flattened.keys(): for n in flattened[id]: - con.execute("UPDATE comments SET parent=? WHERE id=?", (id, n)) + con.execute( + "UPDATE comments SET parent=? WHERE id=?", (id, n)) con.execute('PRAGMA user_version = 3') logger.info("%i rows changed", con.total_changes) diff --git a/isso/db/comments.py b/isso/db/comments.py index a66c248..0f359a3 100644 --- a/isso/db/comments.py +++ b/isso/db/comments.py @@ -83,7 +83,7 @@ class Comments: """ self.db.execute([ 'UPDATE comments SET', - ','.join(key + '=' + '?' for key in data), + ','.join(key + '=' + '?' for key in data), 'WHERE id=?;'], list(data.values()) + [id]) @@ -94,7 +94,8 @@ class Comments: Search for comment :param:`id` and return a mapping of :attr:`fields` and values. """ - rv = self.db.execute('SELECT * FROM comments WHERE id=?', (id, )).fetchone() + rv = self.db.execute( + 'SELECT * FROM comments WHERE id=?', (id, )).fetchone() if rv: return dict(zip(Comments.fields, rv)) @@ -122,7 +123,7 @@ class Comments: for f in fields_comments]) sql_threads_fields = ', '.join(['threads.' + f for f in fields_threads]) - sql = ['SELECT ' + sql_comments_fields + ', ' + \ + sql = ['SELECT ' + sql_comments_fields + ', ' + sql_threads_fields + ' ' 'FROM comments INNER JOIN threads ' 'ON comments.tid=threads.id ' @@ -162,9 +163,9 @@ class Comments: """ Return comments for :param:`uri` with :param:`mode`. """ - sql = [ 'SELECT comments.* FROM comments INNER JOIN threads ON', - ' threads.uri=? AND comments.tid=threads.id AND (? | comments.mode) = ?', - ' AND comments.created>?'] + sql = ['SELECT comments.* FROM comments INNER JOIN threads ON', + ' threads.uri=? AND comments.tid=threads.id AND (? | comments.mode) = ?', + ' AND comments.created>?'] sql_args = [uri, mode, mode, after] @@ -216,7 +217,8 @@ class Comments: In the second case this comment can be safely removed without any side effects.""" - refs = self.db.execute('SELECT * FROM comments WHERE parent=?', (id, )).fetchone() + refs = self.db.execute( + 'SELECT * FROM comments WHERE parent=?', (id, )).fetchone() if refs is None: self.db.execute('DELETE FROM comments WHERE id=?', (id, )) @@ -226,7 +228,8 @@ class Comments: self.db.execute('UPDATE comments SET text=? WHERE id=?', ('', id)) self.db.execute('UPDATE comments SET mode=? WHERE id=?', (4, id)) for field in ('author', 'website'): - self.db.execute('UPDATE comments SET %s=? WHERE id=?' % field, (None, id)) + self.db.execute('UPDATE comments SET %s=? WHERE id=?' % + field, (None, id)) self._remove_stale() return self.get(id) diff --git a/isso/db/preferences.py b/isso/db/preferences.py index 28936e4..3eb12a3 100644 --- a/isso/db/preferences.py +++ b/isso/db/preferences.py @@ -24,7 +24,7 @@ class Preferences: def get(self, key, default=None): rv = self.db.execute( - 'SELECT value FROM preferences WHERE key=?', (key, )).fetchone() + 'SELECT value FROM preferences WHERE key=?', (key, )).fetchone() if rv is None: return default diff --git a/isso/db/spam.py b/isso/db/spam.py index d198f6a..5069229 100644 --- a/isso/db/spam.py +++ b/isso/db/spam.py @@ -50,7 +50,7 @@ class Guard: return False, "%i direct responses to %s" % (len(rv), uri) # block replies to self unless :param:`reply-to-self` is enabled - elif self.conf.getboolean("reply-to-self") == False: + elif self.conf.getboolean("reply-to-self") is False: rv = self.db.execute([ 'SELECT id FROM comments WHERE' ' remote_addr = ?', diff --git a/isso/db/threads.py b/isso/db/threads.py index bb7067b..4f0b476 100644 --- a/isso/db/threads.py +++ b/isso/db/threads.py @@ -26,5 +26,6 @@ class Threads(object): return Thread(*self.db.execute("SELECT * FROM threads WHERE uri=?", (uri, )).fetchone()) def new(self, uri, title): - self.db.execute("INSERT INTO threads (uri, title) VALUES (?, ?)", (uri, title)) + self.db.execute( + "INSERT INTO threads (uri, title) VALUES (?, ?)", (uri, title)) return self[uri] diff --git a/isso/dispatch.py b/isso/dispatch.py index bf3a163..7c8bb14 100644 --- a/isso/dispatch.py +++ b/isso/dispatch.py @@ -25,7 +25,8 @@ class Dispatcher(DispatcherMiddleware): def __init__(self, *confs): self.isso = {} - default = os.path.join(dist.location, dist.project_name, "defaults.ini") + default = os.path.join( + dist.location, dist.project_name, "defaults.ini") for i, path in enumerate(confs): conf = config.load(default, path) @@ -45,7 +46,8 @@ class Dispatcher(DispatcherMiddleware): return super(Dispatcher, self).__call__(environ, start_response) def default(self, environ, start_response): - resp = Response("\n".join(self.isso.keys()), 404, content_type="text/plain") + resp = Response("\n".join(self.isso.keys()), + 404, content_type="text/plain") return resp(environ, start_response) diff --git a/isso/ext/notifications.py b/isso/ext/notifications.py index 3ba708b..80e1504 100644 --- a/isso/ext/notifications.py +++ b/isso/ext/notifications.py @@ -58,7 +58,8 @@ class SMTP(object): uwsgi.spooler = spooler def __enter__(self): - klass = (smtplib.SMTP_SSL if self.conf.get('security') == 'ssl' else smtplib.SMTP) + klass = (smtplib.SMTP_SSL if self.conf.get( + 'security') == 'ssl' else smtplib.SMTP) self.client = klass(host=self.conf.get('host'), port=self.conf.getint('port'), timeout=self.conf.getint('timeout')) @@ -104,7 +105,8 @@ class SMTP(object): rv.write("User's URL: %s\n" % comment["website"]) rv.write("IP address: %s\n" % comment["remote_addr"]) - rv.write("Link to comment: %s\n" % (local("origin") + thread["uri"] + "#isso-%i" % comment["id"])) + rv.write("Link to comment: %s\n" % + (local("origin") + thread["uri"] + "#isso-%i" % comment["id"])) rv.write("\n") uri = local("host") + "/id/%i" % comment["id"] @@ -173,7 +175,8 @@ class Stdout(object): logger.info("comment created: %s", json.dumps(comment)) def _edit_comment(self, comment): - logger.info('comment %i edited: %s', comment["id"], json.dumps(comment)) + logger.info('comment %i edited: %s', + comment["id"], json.dumps(comment)) def _delete_comment(self, id): logger.info('comment %i deleted', id) diff --git a/isso/migrate.py b/isso/migrate.py index f53341a..f6297b7 100644 --- a/isso/migrate.py +++ b/isso/migrate.py @@ -30,6 +30,7 @@ from xml.etree import ElementTree logger = logging.getLogger("isso") + def strip(val): if isinstance(val, string_types): return val.strip() @@ -82,7 +83,8 @@ class Disqus(object): remap = dict() if path not in self.db.threads: - self.db.threads.new(path, thread.find(Disqus.ns + 'title').text.strip()) + self.db.threads.new(path, thread.find( + Disqus.ns + 'title').text.strip()) for item in sorted(posts, key=lambda k: k['created']): @@ -112,9 +114,11 @@ class Disqus(object): } if post.find(Disqus.ns + 'parent') is not None: - item['dsq:parent'] = post.find(Disqus.ns + 'parent').attrib.get(Disqus.internals + 'id') + item['dsq:parent'] = post.find( + Disqus.ns + 'parent').attrib.get(Disqus.internals + 'id') - res[post.find('%sthread' % Disqus.ns).attrib.get(Disqus.internals + 'id')].append(item) + res[post.find('%sthread' % Disqus.ns).attrib.get( + Disqus.internals + 'id')].append(item) progress = Progress(len(tree.findall(Disqus.ns + 'thread'))) for i, thread in enumerate(tree.findall(Disqus.ns + 'thread')): @@ -135,7 +139,8 @@ class Disqus(object): progress.finish("{0} threads, {1} comments".format( len(self.threads), len(self.comments))) - orphans = set(map(lambda e: e.attrib.get(Disqus.internals + "id"), tree.findall(Disqus.ns + "post"))) - self.comments + orphans = set(map(lambda e: e.attrib.get(Disqus.internals + "id"), + tree.findall(Disqus.ns + "post"))) - self.comments if orphans and not self.threads: print("Isso couldn't import any thread, try again with --empty-id") elif orphans: @@ -258,22 +263,22 @@ def autodetect(peek): def dispatch(type, db, dump, empty_id=False): - if db.execute("SELECT * FROM comments").fetchone(): - if input("Isso DB is not empty! Continue? [y/N]: ") not in ("y", "Y"): - raise SystemExit("Abort.") - - if type == "disqus": - cls = Disqus - elif type == "wordpress": - cls = WordPress - else: - with io.open(dump, encoding="utf-8") as fp: - cls = autodetect(fp.read(io.DEFAULT_BUFFER_SIZE)) + if db.execute("SELECT * FROM comments").fetchone(): + if input("Isso DB is not empty! Continue? [y/N]: ") not in ("y", "Y"): + raise SystemExit("Abort.") + + if type == "disqus": + cls = Disqus + elif type == "wordpress": + cls = WordPress + else: + with io.open(dump, encoding="utf-8") as fp: + cls = autodetect(fp.read(io.DEFAULT_BUFFER_SIZE)) - if cls is None: - raise SystemExit("Unknown format, abort.") + if cls is None: + raise SystemExit("Unknown format, abort.") - if cls is Disqus: - cls = functools.partial(cls, empty_id=empty_id) + if cls is Disqus: + cls = functools.partial(cls, empty_id=empty_id) - cls(db, dump).migrate() + cls(db, dump).migrate() diff --git a/isso/tests/fixtures.py b/isso/tests/fixtures.py index 710d0d2..303e8bb 100644 --- a/isso/tests/fixtures.py +++ b/isso/tests/fixtures.py @@ -37,5 +37,7 @@ class Dummy: pass -curl = lambda method, host, path: Dummy() -loads = lambda data: json.loads(data.decode('utf-8')) +def curl(method, host, path): return Dummy() + + +def loads(data): return json.loads(data.decode('utf-8')) diff --git a/isso/tests/test_comments.py b/isso/tests/test_comments.py index e7bc011..cc27ae8 100644 --- a/isso/tests/test_comments.py +++ b/isso/tests/test_comments.py @@ -50,7 +50,8 @@ class TestComments(unittest.TestCase): def testGet(self): - self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'})) + self.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Lorem ipsum ...'})) r = self.get('/id/1') self.assertEqual(r.status_code, 200) @@ -61,7 +62,8 @@ class TestComments(unittest.TestCase): def testCreate(self): - rv = self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'})) + rv = self.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Lorem ipsum ...'})) self.assertEqual(rv.status_code, 201) self.assertIn("Set-Cookie", rv.headers) @@ -73,7 +75,8 @@ class TestComments(unittest.TestCase): def textCreateWithNonAsciiText(self): - rv = self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Здравствуй, мир!'})) + rv = self.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Здравствуй, мир!'})) self.assertEqual(rv.status_code, 201) rv = loads(rv.data) @@ -105,14 +108,16 @@ class TestComments(unittest.TestCase): def testCreateInvalidParent(self): self.post('/new?uri=test', data=json.dumps({'text': '...'})) - self.post('/new?uri=test', data=json.dumps({'text': '...', 'parent': 1})) - invalid = self.post('/new?uri=test', data=json.dumps({'text': '...', 'parent': 2})) + self.post('/new?uri=test', + data=json.dumps({'text': '...', 'parent': 1})) + invalid = self.post( + '/new?uri=test', data=json.dumps({'text': '...', 'parent': 2})) self.assertEqual(loads(invalid.data)["parent"], 1) def testVerifyFields(self): - verify = lambda comment: comments.API.verify(comment)[0] + def verify(comment): return comments.API.verify(comment)[0] # text is missing self.assertFalse(verify({})) @@ -128,10 +133,12 @@ class TestComments(unittest.TestCase): # email/website length self.assertTrue(verify({"text": "...", "email": "*"*254})) - self.assertTrue(verify({"text": "...", "website": "google.de/" + "a"*128})) + self.assertTrue( + verify({"text": "...", "website": "google.de/" + "a"*128})) self.assertFalse(verify({"text": "...", "email": "*"*1024})) - self.assertFalse(verify({"text": "...", "website": "google.de/" + "*"*1024})) + self.assertFalse( + verify({"text": "...", "website": "google.de/" + "*"*1024})) # valid website url self.assertTrue(comments.isurl("example.tld")) @@ -139,7 +146,8 @@ class TestComments(unittest.TestCase): self.assertTrue(comments.isurl("https://example.tld")) self.assertTrue(comments.isurl("https://example.tld:1337/")) self.assertTrue(comments.isurl("https://example.tld:1337/foobar")) - self.assertTrue(comments.isurl("https://example.tld:1337/foobar?p=1#isso-thread")) + self.assertTrue(comments.isurl( + "https://example.tld:1337/foobar?p=1#isso-thread")) self.assertFalse(comments.isurl("ftp://example.tld/")) self.assertFalse(comments.isurl("tel:+1234567890")) @@ -149,7 +157,8 @@ class TestComments(unittest.TestCase): def testGetInvalid(self): self.assertEqual(self.get('/?uri=%2Fpath%2F&id=123').status_code, 404) - self.assertEqual(self.get('/?uri=%2Fpath%2Fspam%2F&id=123').status_code, 404) + self.assertEqual( + self.get('/?uri=%2Fpath%2Fspam%2F&id=123').status_code, 404) self.assertEqual(self.get('/?uri=?uri=%foo%2F').status_code, 404) def testGetLimited(self): @@ -166,7 +175,8 @@ class TestComments(unittest.TestCase): def testGetNested(self): self.post('/new?uri=test', data=json.dumps({'text': '...'})) - self.post('/new?uri=test', data=json.dumps({'text': '...', 'parent': 1})) + self.post('/new?uri=test', + data=json.dumps({'text': '...', 'parent': 1})) r = self.get('/?uri=test&parent=1') self.assertEqual(r.status_code, 200) @@ -178,7 +188,8 @@ class TestComments(unittest.TestCase): self.post('/new?uri=test', data=json.dumps({'text': '...'})) for i in range(20): - self.post('/new?uri=test', data=json.dumps({'text': '...', 'parent': 1})) + self.post('/new?uri=test', + data=json.dumps({'text': '...', 'parent': 1})) r = self.get('/?uri=test&parent=1&limit=10') self.assertEqual(r.status_code, 200) @@ -188,7 +199,8 @@ class TestComments(unittest.TestCase): def testUpdate(self): - self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'})) + self.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Lorem ipsum ...'})) self.put('/id/1', data=json.dumps({ 'text': 'Hello World', 'author': 'me', 'website': 'http://example.com/'})) @@ -203,7 +215,8 @@ class TestComments(unittest.TestCase): def testDelete(self): - self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'})) + self.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Lorem ipsum ...'})) r = self.delete('/id/1') self.assertEqual(r.status_code, 200) self.assertEqual(loads(r.data), None) @@ -213,7 +226,8 @@ class TestComments(unittest.TestCase): client = JSONClient(self.app, Response) client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'First'})) - client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'First', 'parent': 1})) + client.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'First', 'parent': 1})) r = client.delete('/id/1') self.assertEqual(r.status_code, 200) @@ -242,8 +256,10 @@ class TestComments(unittest.TestCase): client = JSONClient(self.app, Response) client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'First'})) - client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Second', 'parent': 1})) - client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Third', 'parent': 1})) + client.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Second', 'parent': 1})) + client.post('/new?uri=%2Fpath%2F', + data=json.dumps({'text': 'Third', 'parent': 1})) client.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Last'})) client.delete('/id/1') @@ -261,10 +277,11 @@ class TestComments(unittest.TestCase): for path in paths: self.assertEqual(self.post('/new?' + urlencode({'uri': path}), - data=json.dumps({'text': '...'})).status_code, 201) + data=json.dumps({'text': '...'})).status_code, 201) for i, path in enumerate(paths): - self.assertEqual(self.get('/?' + urlencode({'uri': path})).status_code, 200) + self.assertEqual( + self.get('/?' + urlencode({'uri': path})).status_code, 200) self.assertEqual(self.get('/id/%i' % (i + 1)).status_code, 200) def testDeleteAndCreateByDifferentUsersButSamePostId(self): @@ -283,7 +300,8 @@ class TestComments(unittest.TestCase): a = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "Aaa"})) b = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "Bbb"})) - c = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "Ccc", "email": "..."})) + c = self.post('/new?uri=%2Fpath%2F', + data=json.dumps({"text": "Ccc", "email": "..."})) a = loads(a.data) b = loads(b.data) @@ -295,7 +313,8 @@ class TestComments(unittest.TestCase): def testVisibleFields(self): - rv = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "...", "invalid": "field"})) + rv = self.post('/new?uri=%2Fpath%2F', + data=json.dumps({"text": "...", "invalid": "field"})) self.assertEqual(rv.status_code, 201) rv = loads(rv.data) @@ -333,7 +352,8 @@ class TestComments(unittest.TestCase): for uri, count in iteritems(expected): for _ in range(count): - self.post('/new?uri=%s' % uri, data=json.dumps({"text": "..."})) + self.post('/new?uri=%s' % + uri, data=json.dumps({"text": "..."})) rv = self.post('/count', data=json.dumps(list(expected.keys()))) self.assertEqual(loads(rv.data), list(expected.values())) @@ -362,22 +382,26 @@ class TestComments(unittest.TestCase): self.post('/new?uri=%2F', data=json.dumps({"text": "..."})) # no header is fine (default for XHR) - self.assertEqual(self.post('/id/1/dislike', content_type="").status_code, 200) + self.assertEqual( + self.post('/id/1/dislike', content_type="").status_code, 200) # x-www-form-urlencoded is definitely not RESTful - self.assertEqual(self.post('/id/1/dislike', content_type=form).status_code, 403) + self.assertEqual( + self.post('/id/1/dislike', content_type=form).status_code, 403) self.assertEqual(self.post('/new?uri=%2F', data=json.dumps({"text": "..."}), - content_type=form).status_code, 403) + content_type=form).status_code, 403) # just for the record - self.assertEqual(self.post('/id/1/dislike', content_type=js).status_code, 200) + self.assertEqual( + self.post('/id/1/dislike', content_type=js).status_code, 200) def testPreview(self): - response = self.post('/preview', data=json.dumps({'text': 'This is **mark***down*'})) + response = self.post( + '/preview', data=json.dumps({'text': 'This is **mark***down*'})) self.assertEqual(response.status_code, 200) rv = loads(response.data) - self.assertEqual(rv["text"], '
This is markdown
') - + self.assertEqual( + rv["text"], 'This is markdown
') class TestModeratedComments(unittest.TestCase): @@ -402,7 +426,8 @@ class TestModeratedComments(unittest.TestCase): def testAddComment(self): - rv = self.client.post('/new?uri=test', data=json.dumps({"text": "..."})) + rv = self.client.post( + '/new?uri=test', data=json.dumps({"text": "..."})) self.assertEqual(rv.status_code, 202) self.assertEqual(self.client.get('/id/1').status_code, 200) diff --git a/isso/tests/test_cors.py b/isso/tests/test_cors.py index 415ea51..c1d22a0 100644 --- a/isso/tests/test_cors.py +++ b/isso/tests/test_cors.py @@ -19,31 +19,37 @@ class CORSTest(unittest.TestCase): def test_simple(self): app = CORSMiddleware(hello_world, - origin=origin([ - "https://example.tld/", - "http://example.tld/", - ]), - allowed=("Foo", "Bar"), exposed=("Spam", )) + origin=origin([ + "https://example.tld/", + "http://example.tld/", + ]), + allowed=("Foo", "Bar"), exposed=("Spam", )) client = Client(app, Response) rv = client.get("/", headers={"Origin": "https://example.tld"}) - self.assertEqual(rv.headers["Access-Control-Allow-Origin"], "https://example.tld") - self.assertEqual(rv.headers["Access-Control-Allow-Credentials"], "true") - self.assertEqual(rv.headers["Access-Control-Allow-Methods"], "HEAD, GET, POST, PUT, DELETE") - self.assertEqual(rv.headers["Access-Control-Allow-Headers"], "Foo, Bar") + self.assertEqual( + rv.headers["Access-Control-Allow-Origin"], "https://example.tld") + self.assertEqual( + rv.headers["Access-Control-Allow-Credentials"], "true") + self.assertEqual( + rv.headers["Access-Control-Allow-Methods"], "HEAD, GET, POST, PUT, DELETE") + self.assertEqual( + rv.headers["Access-Control-Allow-Headers"], "Foo, Bar") self.assertEqual(rv.headers["Access-Control-Expose-Headers"], "Spam") a = client.get("/", headers={"Origin": "http://example.tld"}) - self.assertEqual(a.headers["Access-Control-Allow-Origin"], "http://example.tld") + self.assertEqual( + a.headers["Access-Control-Allow-Origin"], "http://example.tld") b = client.get("/", headers={"Origin": "http://example.tld"}) - self.assertEqual(b.headers["Access-Control-Allow-Origin"], "http://example.tld") + self.assertEqual( + b.headers["Access-Control-Allow-Origin"], "http://example.tld") c = client.get("/", headers={"Origin": "http://foo.other"}) - self.assertEqual(c.headers["Access-Control-Allow-Origin"], "https://example.tld") - + self.assertEqual( + c.headers["Access-Control-Allow-Origin"], "https://example.tld") def test_preflight(self): @@ -51,10 +57,12 @@ class CORSTest(unittest.TestCase): allowed=("Foo", ), exposed=("Bar", )) client = Client(app, Response) - rv = client.open(method="OPTIONS", path="/", headers={"Origin": "http://example.tld"}) + rv = client.open(method="OPTIONS", path="/", + headers={"Origin": "http://example.tld"}) self.assertEqual(rv.status_code, 200) for hdr in ("Origin", "Headers", "Credentials", "Methods"): self.assertIn("Access-Control-Allow-%s" % hdr, rv.headers) - self.assertEqual(rv.headers["Access-Control-Allow-Origin"], "http://example.tld") + self.assertEqual( + rv.headers["Access-Control-Allow-Origin"], "http://example.tld") diff --git a/isso/tests/test_db.py b/isso/tests/test_db.py index 74e0831..e3e7926 100644 --- a/isso/tests/test_db.py +++ b/isso/tests/test_db.py @@ -66,10 +66,10 @@ class TestDBMigration(unittest.TestCase): tree = { 1: None, 2: None, - 3: 2, - 4: 3, - 7: 3, - 5: 2, + 3: 2, + 4: 3, + 7: 3, + 5: 2, 6: None } @@ -91,7 +91,8 @@ class TestDBMigration(unittest.TestCase): " dislikes INTEGER DEFAULT 0," " voters BLOB)") - con.execute("INSERT INTO threads (uri, title) VALUES (?, ?)", ("/", "Test")) + con.execute( + "INSERT INTO threads (uri, title) VALUES (?, ?)", ("/", "Test")) for (id, parent) in iteritems(tree): con.execute("INSERT INTO comments (" " id, parent, created)" @@ -108,13 +109,14 @@ class TestDBMigration(unittest.TestCase): flattened = list(iteritems({ 1: None, 2: None, - 3: 2, - 4: 2, - 5: 2, + 3: 2, + 4: 2, + 5: 2, 6: None, 7: 2 })) with sqlite3.connect(self.path) as con: - rv = con.execute("SELECT id, parent FROM comments ORDER BY created").fetchall() + rv = con.execute( + "SELECT id, parent FROM comments ORDER BY created").fetchall() self.assertEqual(flattened, rv) diff --git a/isso/tests/test_guard.py b/isso/tests/test_guard.py index a9ae365..4bbb50e 100644 --- a/isso/tests/test_guard.py +++ b/isso/tests/test_guard.py @@ -68,7 +68,8 @@ class TestGuard(unittest.TestCase): alice = self.makeClient("1.2.3.4", 2) for i in range(2): - self.assertEqual(alice.post("/new?uri=test", data=self.data).status_code, 201) + self.assertEqual(alice.post( + "/new?uri=test", data=self.data).status_code, 201) bob.application.db.execute([ "UPDATE comments SET", @@ -76,7 +77,8 @@ class TestGuard(unittest.TestCase): "WHERE remote_addr = '127.0.0.0'" ]) - self.assertEqual(bob.post("/new?uri=test", data=self.data).status_code, 201) + self.assertEqual( + bob.post("/new?uri=test", data=self.data).status_code, 201) def testDirectReply(self): @@ -95,11 +97,13 @@ class TestGuard(unittest.TestCase): def testSelfReply(self): - payload = lambda id: json.dumps({"text": "...", "parent": id}) + def payload(id): return json.dumps({"text": "...", "parent": id}) client = self.makeClient("127.0.0.1", self_reply=False) - self.assertEqual(client.post("/new?uri=test", data=self.data).status_code, 201) - self.assertEqual(client.post("/new?uri=test", data=payload(1)).status_code, 403) + self.assertEqual(client.post( + "/new?uri=test", data=self.data).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload(1)).status_code, 403) client.application.db.execute([ "UPDATE comments SET", @@ -107,39 +111,55 @@ class TestGuard(unittest.TestCase): "WHERE id = 1" ], (client.application.conf.getint("general", "max-age"), )) - self.assertEqual(client.post("/new?uri=test", data=payload(1)).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload(1)).status_code, 201) client = self.makeClient("128.0.0.1", ratelimit=3, self_reply=False) - self.assertEqual(client.post("/new?uri=test", data=self.data).status_code, 201) - self.assertEqual(client.post("/new?uri=test", data=payload(1)).status_code, 201) - self.assertEqual(client.post("/new?uri=test", data=payload(2)).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=self.data).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload(1)).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload(2)).status_code, 201) def testRequireEmail(self): - payload = lambda email: json.dumps({"text": "...", "email": email}) + def payload(email): return json.dumps({"text": "...", "email": email}) client = self.makeClient("127.0.0.1", ratelimit=4, require_email=False) - client_strict = self.makeClient("127.0.0.2", ratelimit=4, require_email=True) + client_strict = self.makeClient( + "127.0.0.2", ratelimit=4, require_email=True) # if we don't require email - self.assertEqual(client.post("/new?uri=test", data=payload("")).status_code, 201) - self.assertEqual(client.post("/new?uri=test", data=payload("test@me.more")).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload("")).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload("test@me.more")).status_code, 201) # if we do require email - self.assertEqual(client_strict.post("/new?uri=test", data=payload("")).status_code, 403) - self.assertEqual(client_strict.post("/new?uri=test", data=payload("test@me.more")).status_code, 201) + self.assertEqual(client_strict.post( + "/new?uri=test", data=payload("")).status_code, 403) + self.assertEqual(client_strict.post( + "/new?uri=test", data=payload("test@me.more")).status_code, 201) def testRequireAuthor(self): - payload = lambda author: json.dumps({"text": "...", "author": author}) + def payload(author): return json.dumps( + {"text": "...", "author": author}) - client = self.makeClient("127.0.0.1", ratelimit=4, require_author=False) - client_strict = self.makeClient("127.0.0.2", ratelimit=4, require_author=True) + client = self.makeClient( + "127.0.0.1", ratelimit=4, require_author=False) + client_strict = self.makeClient( + "127.0.0.2", ratelimit=4, require_author=True) # if we don't require author - self.assertEqual(client.post("/new?uri=test", data=payload("")).status_code, 201) - self.assertEqual(client.post("/new?uri=test", data=payload("pipo author")).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload("")).status_code, 201) + self.assertEqual(client.post( + "/new?uri=test", data=payload("pipo author")).status_code, 201) # if we do require author - self.assertEqual(client_strict.post("/new?uri=test", data=payload("")).status_code, 403) - self.assertEqual(client_strict.post("/new?uri=test", data=payload("pipo author")).status_code, 201) + self.assertEqual(client_strict.post( + "/new?uri=test", data=payload("")).status_code, 403) + self.assertEqual(client_strict.post( + "/new?uri=test", data=payload("pipo author")).status_code, 201) diff --git a/isso/tests/test_html.py b/isso/tests/test_html.py index 362e239..316fbf8 100644 --- a/isso/tests/test_html.py +++ b/isso/tests/test_html.py @@ -64,7 +64,8 @@ class TestHTML(unittest.TestCase): sanitizer = html.Sanitizer(elements=[], attributes=[]) examples = [ ('Look: ', 'Look: '), - ('Ha', 'Ha'), + ('Ha', + 'Ha'), ('Ha', 'Ha'), (' ', 'Test
'), ('', 'alert("Onoe")')] diff --git a/isso/tests/test_migration.py b/isso/tests/test_migration.py index 8a876ee..151f27b 100644 --- a/isso/tests/test_migration.py +++ b/isso/tests/test_migration.py @@ -29,7 +29,8 @@ class TestMigration(unittest.TestCase): db = SQLite3(xxx.name, conf) Disqus(db, xml).migrate() - self.assertEqual(len(db.execute("SELECT id FROM comments").fetchall()), 2) + self.assertEqual( + len(db.execute("SELECT id FROM comments").fetchall()), 2) self.assertEqual(db.threads["/"]["title"], "Hello, World!") self.assertEqual(db.threads["/"]["id"], 1) @@ -57,8 +58,10 @@ class TestMigration(unittest.TestCase): self.assertEqual(db.threads["/?p=4"]["title"], "...") self.assertEqual(db.threads["/?p=4"]["id"], 2) - self.assertEqual(len(db.execute("SELECT id FROM threads").fetchall()), 2) - self.assertEqual(len(db.execute("SELECT id FROM comments").fetchall()), 7) + self.assertEqual( + len(db.execute("SELECT id FROM threads").fetchall()), 2) + self.assertEqual( + len(db.execute("SELECT id FROM comments").fetchall()), 7) first = db.comments.get(1) self.assertEqual(first["author"], "Ohai") diff --git a/isso/tests/test_utils.py b/isso/tests/test_utils.py index 0504c15..253328c 100644 --- a/isso/tests/test_utils.py +++ b/isso/tests/test_utils.py @@ -12,7 +12,8 @@ class TestUtils(unittest.TestCase): examples = [ (u'12.34.56.78', u'12.34.56.0'), - (u'1234:5678:90ab:cdef:fedc:ba09:8765:4321', u'1234:5678:90ab:0000:0000:0000:0000:0000'), + (u'1234:5678:90ab:cdef:fedc:ba09:8765:4321', + u'1234:5678:90ab:0000:0000:0000:0000:0000'), (u'::ffff:127.0.0.1', u'127.0.0.0')] for (addr, anonymized) in examples: @@ -56,4 +57,4 @@ class TestParse(unittest.TestCase): """), ('test', 'Test')) self.assertEqual(parse.thread('