support for Python 3.3
This commit is contained in:
parent
f9133b984e
commit
61a486d2ea
@ -33,8 +33,6 @@ dist = pkg_resources.get_distribution("isso")
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
import httplib
|
||||
import urlparse
|
||||
|
||||
from os.path import dirname, join
|
||||
from argparse import ArgumentParser
|
||||
@ -73,7 +71,7 @@ rules = Map([
|
||||
|
||||
class Isso(object):
|
||||
|
||||
salt = "Eech7co8Ohloopo9Ol6baimi"
|
||||
salt = b"Eech7co8Ohloopo9Ol6baimi"
|
||||
|
||||
def __init__(self, conf):
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
# from isso import compat
|
||||
# from isso.compat import text_type as str, string_types
|
||||
|
||||
str = unicode
|
||||
string_types = (unicode, str)
|
||||
from isso.compat import text_type as str, string_types
|
||||
|
||||
|
||||
# @compat.implements_to_string
|
||||
|
21
isso/compat.py
Normal file
21
isso/compat.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
PY2K = sys.version_info[0] == 2
|
||||
|
||||
if not PY2K:
|
||||
# iterkeys = lambda d: iter(d.keys())
|
||||
# iteritems = lambda d: iter(d.items())
|
||||
|
||||
text_type = str
|
||||
string_types = (str, )
|
||||
|
||||
buffer = memoryview
|
||||
else:
|
||||
# iterkeys = lambda d: d.iterkeys()
|
||||
# iteritems = lambda d: d.iteritems()
|
||||
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
|
||||
buffer = buffer
|
18
isso/core.py
18
isso/core.py
@ -6,16 +6,11 @@ import io
|
||||
import os
|
||||
import time
|
||||
import binascii
|
||||
|
||||
import thread
|
||||
import threading
|
||||
|
||||
import socket
|
||||
import smtplib
|
||||
|
||||
import httplib
|
||||
import urlparse
|
||||
|
||||
from configparser import ConfigParser
|
||||
|
||||
try:
|
||||
@ -23,6 +18,19 @@ try:
|
||||
except ImportError:
|
||||
uwsgi = None
|
||||
|
||||
from isso.compat import PY2K
|
||||
|
||||
if PY2K:
|
||||
import thread
|
||||
|
||||
import httplib
|
||||
import urlparse
|
||||
else:
|
||||
import _thread as thread
|
||||
|
||||
import http.client as httplib
|
||||
import urllib.parse as urlparse
|
||||
|
||||
from isso import notify, colors
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@ import hashlib
|
||||
import binascii
|
||||
import operator
|
||||
|
||||
from functools import reduce
|
||||
|
||||
|
||||
def _bin_to_long(x):
|
||||
"""
|
||||
@ -76,7 +78,7 @@ def _pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
def F(i):
|
||||
def U():
|
||||
u = salt + struct.pack(b'>I', i)
|
||||
for j in xrange(int(iterations)):
|
||||
for j in range(int(iterations)):
|
||||
u = _fast_hmac(password, u, digest).digest()
|
||||
yield _bin_to_long(u)
|
||||
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
|
||||
|
@ -4,6 +4,7 @@ import time
|
||||
|
||||
from isso.db import spam
|
||||
from isso.utils import Bloomfilter
|
||||
from isso.compat import buffer
|
||||
|
||||
|
||||
class Comments:
|
||||
@ -77,7 +78,7 @@ class Comments:
|
||||
'UPDATE comments SET',
|
||||
','.join(key + '=' + '?' for key in data),
|
||||
'WHERE id=?;'],
|
||||
data.values() + [id])
|
||||
list(data.values()) + [id])
|
||||
|
||||
return self.get(id)
|
||||
|
||||
|
@ -11,9 +11,13 @@ import sys
|
||||
import os
|
||||
|
||||
from time import mktime, strptime
|
||||
from urlparse import urlparse
|
||||
from collections import defaultdict
|
||||
|
||||
try:
|
||||
from urlparse import urlparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from xml.etree import ElementTree
|
||||
|
||||
|
||||
|
@ -3,13 +3,18 @@
|
||||
from __future__ import division
|
||||
|
||||
import socket
|
||||
import httplib
|
||||
|
||||
try:
|
||||
import httplib
|
||||
from urlparse import urlparse
|
||||
except ImportError:
|
||||
import http.client as httplib
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
from string import ascii_letters, digits
|
||||
from urlparse import urlparse
|
||||
from contextlib import closing
|
||||
|
||||
import html5lib
|
||||
|
@ -12,6 +12,8 @@ from itsdangerous import SignatureExpired, BadSignature
|
||||
from werkzeug.wrappers import Response
|
||||
from werkzeug.exceptions import abort, BadRequest
|
||||
|
||||
from isso.compat import text_type as str
|
||||
|
||||
from isso import utils, notify, db
|
||||
from isso.crypto import pbkdf2
|
||||
|
||||
@ -48,7 +50,7 @@ def new(app, environ, request, uri):
|
||||
return Response('URI does not exist', 404)
|
||||
|
||||
try:
|
||||
data = json.loads(request.data)
|
||||
data = json.loads(request.get_data().decode('utf-8'))
|
||||
except ValueError:
|
||||
return Response("No JSON object could be decoded", 400)
|
||||
|
||||
@ -65,7 +67,7 @@ def new(app, environ, request, uri):
|
||||
if data.get(field):
|
||||
data[field] = cgi.escape(data[field])
|
||||
|
||||
data['remote_addr'] = utils.anonymize(unicode(request.remote_addr))
|
||||
data['remote_addr'] = utils.anonymize(str(request.remote_addr))
|
||||
|
||||
with app.lock:
|
||||
if uri not in app.db.threads:
|
||||
@ -82,14 +84,14 @@ def new(app, environ, request, uri):
|
||||
abort(403)
|
||||
|
||||
href = (app.conf.get('general', 'host').rstrip("/") + uri + "#isso-%i" % rv["id"])
|
||||
app.notify(title, notify.format(rv, href, utils.anonymize(unicode(request.remote_addr))))
|
||||
app.notify(title, notify.format(rv, href, utils.anonymize(str(request.remote_addr))))
|
||||
|
||||
# save checksum of text into cookie, so mallory can't modify/delete a comment, if
|
||||
# he add a comment, then removed it but not the signed cookie.
|
||||
checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
|
||||
|
||||
rv["text"] = app.markdown(rv["text"])
|
||||
rv["hash"] = pbkdf2(rv.get('email') or rv['remote_addr'], app.salt, 1000, 6)
|
||||
rv["hash"] = str(pbkdf2(rv.get('email') or rv['remote_addr'], app.salt, 1000, 6))
|
||||
|
||||
for key in set(rv.keys()) - FIELDS:
|
||||
rv.pop(key)
|
||||
@ -132,7 +134,7 @@ def single(app, environ, request, id):
|
||||
|
||||
if request.method == 'PUT':
|
||||
try:
|
||||
data = json.loads(request.data)
|
||||
data = json.loads(request.get_data().decode('utf-8'))
|
||||
except ValueError:
|
||||
return Response("No JSON object could be decoded", 400)
|
||||
|
||||
@ -181,7 +183,7 @@ def fetch(app, environ, request, uri):
|
||||
|
||||
for item in rv:
|
||||
|
||||
item['hash'] = pbkdf2(item['email'] or item['remote_addr'], app.salt, 1000, 6)
|
||||
item['hash'] = str(pbkdf2(item['email'] or item['remote_addr'], app.salt, 1000, 6))
|
||||
|
||||
for key in set(item.keys()) - FIELDS:
|
||||
item.pop(key)
|
||||
@ -195,13 +197,13 @@ def fetch(app, environ, request, uri):
|
||||
|
||||
def like(app, environ, request, id):
|
||||
|
||||
nv = app.db.comments.vote(True, id, utils.anonymize(unicode(request.remote_addr)))
|
||||
nv = app.db.comments.vote(True, id, utils.anonymize(str(request.remote_addr)))
|
||||
return Response(json.dumps(nv), 200)
|
||||
|
||||
|
||||
def dislike(app, environ, request, id):
|
||||
|
||||
nv = app.db.comments.vote(False, id, utils.anonymize(unicode(request.remote_addr)))
|
||||
nv = app.db.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))
|
||||
return Response(json.dumps(nv), 200)
|
||||
|
||||
|
||||
@ -217,4 +219,4 @@ def count(app, environ, request, uri):
|
||||
|
||||
|
||||
def checkip(app, env, req):
|
||||
return Response(utils.anonymize(unicode(req.remote_addr)), 200)
|
||||
return Response(utils.anonymize(str(req.remote_addr)), 200)
|
||||
|
6
setup.py
6
setup.py
@ -7,6 +7,9 @@ from setuptools import setup, find_packages
|
||||
|
||||
requires = ['Jinja2>=2.7', 'werkzeug>=0.9', 'itsdangerous', 'misaka', 'html5lib']
|
||||
|
||||
if (3, 0) <= sys.version_info < (3, 3):
|
||||
raise SystemExit("Python 3.0, 3.1 and 3.2 are not supported")
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
requires += ['ipaddress', 'configparser']
|
||||
|
||||
@ -28,7 +31,8 @@ setup(
|
||||
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7"
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3.3"
|
||||
],
|
||||
install_requires=requires,
|
||||
entry_points={
|
||||
|
@ -3,10 +3,14 @@ from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import json
|
||||
import urllib
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
try:
|
||||
from urllib.parse import urlencode
|
||||
except ImportError:
|
||||
from urllib import urlencode
|
||||
|
||||
from werkzeug.test import Client
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
@ -16,6 +20,8 @@ from isso.views import comment
|
||||
utils.heading = lambda *args: "Untitled."
|
||||
utils.urlexists = lambda *args: True
|
||||
|
||||
loads = lambda data: json.loads(data.decode('utf-8'))
|
||||
|
||||
|
||||
class FakeIP(object):
|
||||
|
||||
@ -56,7 +62,7 @@ class TestComments(unittest.TestCase):
|
||||
r = self.get('/id/1')
|
||||
assert r.status_code == 200
|
||||
|
||||
rv = json.loads(r.data)
|
||||
rv = loads(r.data)
|
||||
|
||||
assert rv['id'] == 1
|
||||
assert rv['text'] == '<p>Lorem ipsum ...</p>\n'
|
||||
@ -66,9 +72,9 @@ class TestComments(unittest.TestCase):
|
||||
rv = self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'}))
|
||||
|
||||
assert rv.status_code == 201
|
||||
assert len(filter(lambda header: header[0] == 'Set-Cookie', rv.headers)) == 1
|
||||
assert any(filter(lambda header: header[0] == 'Set-Cookie', rv.headers))
|
||||
|
||||
rv = json.loads(rv.data)
|
||||
rv = loads(rv.data)
|
||||
|
||||
assert rv["mode"] == 1
|
||||
assert rv["text"] == '<p>Lorem ipsum ...</p>\n'
|
||||
@ -79,9 +85,9 @@ class TestComments(unittest.TestCase):
|
||||
b = self.post('/new?uri=test', data=json.dumps({'text': '...'}))
|
||||
c = self.post('/new?uri=test', data=json.dumps({'text': '...'}))
|
||||
|
||||
assert json.loads(a.data)["id"] == 1
|
||||
assert json.loads(b.data)["id"] == 2
|
||||
assert json.loads(c.data)["id"] == 3
|
||||
assert loads(a.data)["id"] == 1
|
||||
assert loads(b.data)["id"] == 2
|
||||
assert loads(c.data)["id"] == 3
|
||||
|
||||
def testCreateAndGetMultiple(self):
|
||||
|
||||
@ -91,7 +97,7 @@ class TestComments(unittest.TestCase):
|
||||
r = self.get('/?uri=%2Fpath%2F')
|
||||
assert r.status_code == 200
|
||||
|
||||
rv = json.loads(r.data)
|
||||
rv = loads(r.data)
|
||||
assert len(rv) == 20
|
||||
|
||||
def testGetInvalid(self):
|
||||
@ -109,7 +115,7 @@ class TestComments(unittest.TestCase):
|
||||
r = self.get('/id/1?plain=1')
|
||||
assert r.status_code == 200
|
||||
|
||||
rv = json.loads(r.data)
|
||||
rv = loads(r.data)
|
||||
assert rv['text'] == 'Hello World'
|
||||
assert rv['author'] == 'me'
|
||||
assert rv['website'] == 'http://example.com/'
|
||||
@ -120,7 +126,7 @@ class TestComments(unittest.TestCase):
|
||||
self.post('/new?uri=%2Fpath%2F', data=json.dumps({'text': 'Lorem ipsum ...'}))
|
||||
r = self.delete('/id/1')
|
||||
assert r.status_code == 200
|
||||
assert json.loads(r.data) == None
|
||||
assert loads(r.data) == None
|
||||
assert self.get('/id/1').status_code == 404
|
||||
|
||||
def testDeleteWithReference(self):
|
||||
@ -131,8 +137,7 @@ class TestComments(unittest.TestCase):
|
||||
|
||||
r = client.delete('/id/1')
|
||||
assert r.status_code == 200
|
||||
print r.data
|
||||
assert json.loads(r.data)['mode'] == 4
|
||||
assert loads(r.data)['mode'] == 4
|
||||
|
||||
assert self.get('/?uri=%2Fpath%2F&id=1').status_code == 200
|
||||
assert self.get('/?uri=%2Fpath%2F&id=2').status_code == 200
|
||||
@ -175,11 +180,11 @@ class TestComments(unittest.TestCase):
|
||||
paths = ['/sub/path/', '/path.html', '/sub/path.html', 'path', '/']
|
||||
|
||||
for path in paths:
|
||||
assert self.post('/new?' + urllib.urlencode({'uri': path}),
|
||||
assert self.post('/new?' + urlencode({'uri': path}),
|
||||
data=json.dumps({'text': '...'})).status_code == 201
|
||||
|
||||
for i, path in enumerate(paths):
|
||||
assert self.get('/?' + urllib.urlencode({'uri': path})).status_code == 200
|
||||
assert self.get('/?' + urlencode({'uri': path})).status_code == 200
|
||||
assert self.get('/id/%i' % (i + 1)).status_code == 200
|
||||
|
||||
def testDeleteAndCreateByDifferentUsersButSamePostId(self):
|
||||
@ -201,9 +206,9 @@ class TestComments(unittest.TestCase):
|
||||
c = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "Ccc", "email": "..."}))
|
||||
|
||||
assert a.status_code == b.status_code == c.status_code == 201
|
||||
a = json.loads(a.data)
|
||||
b = json.loads(b.data)
|
||||
c = json.loads(c.data)
|
||||
a = loads(a.data)
|
||||
b = loads(b.data)
|
||||
c = loads(c.data)
|
||||
|
||||
assert a['hash'] != '192.168.1.1'
|
||||
assert a['hash'] == b['hash']
|
||||
@ -214,12 +219,12 @@ class TestComments(unittest.TestCase):
|
||||
rv = self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "..."}))
|
||||
assert rv.status_code == 201
|
||||
|
||||
rv = json.loads(rv.data)
|
||||
rv = loads(rv.data)
|
||||
|
||||
for key in comment.FIELDS:
|
||||
rv.pop(key)
|
||||
|
||||
assert rv.keys() == []
|
||||
assert not any(rv.keys())
|
||||
|
||||
def testCounts(self):
|
||||
|
||||
@ -228,14 +233,14 @@ class TestComments(unittest.TestCase):
|
||||
|
||||
rv = self.get('/count?uri=%2Fpath%2F')
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data) == 1
|
||||
assert loads(rv.data) == 1
|
||||
|
||||
for x in range(3):
|
||||
self.post('/new?uri=%2Fpath%2F', data=json.dumps({"text": "..."}))
|
||||
|
||||
rv = self.get('/count?uri=%2Fpath%2F')
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data) == 4
|
||||
assert loads(rv.data) == 4
|
||||
|
||||
for x in range(4):
|
||||
self.delete('/id/%i' % (x + 1))
|
||||
@ -247,7 +252,7 @@ class TestComments(unittest.TestCase):
|
||||
self.post('/new?uri=test', data=json.dumps({"text": "Tpyo"}))
|
||||
|
||||
self.put('/id/1', data=json.dumps({"text": "Tyop"}))
|
||||
assert json.loads(self.get('/id/1').data)["text"] == "<p>Tyop</p>\n"
|
||||
assert loads(self.get('/id/1').data)["text"] == "<p>Tyop</p>\n"
|
||||
|
||||
self.put('/id/1', data=json.dumps({"text": "Typo"}))
|
||||
assert json.loads(self.get('/id/1').data)["text"] == "<p>Typo</p>\n"
|
||||
assert loads(self.get('/id/1').data)["text"] == "<p>Typo</p>\n"
|
||||
|
@ -14,6 +14,8 @@ from isso import Isso, notify, utils, core
|
||||
utils.heading = lambda *args: "Untitled."
|
||||
utils.urlexists = lambda *args: True
|
||||
|
||||
loads = lambda data: json.loads(data.decode('utf-8'))
|
||||
|
||||
|
||||
class FakeIP(object):
|
||||
|
||||
@ -51,7 +53,7 @@ class TestVote(unittest.TestCase):
|
||||
def testZeroLikes(self):
|
||||
|
||||
rv = self.makeClient("127.0.0.1").post("/new?uri=test", data=json.dumps({"text": "..."}))
|
||||
assert json.loads(rv.data)['likes'] == json.loads(rv.data)['dislikes'] == 0
|
||||
assert loads(rv.data)['likes'] == loads(rv.data)['dislikes'] == 0
|
||||
|
||||
def testSingleLike(self):
|
||||
|
||||
@ -59,7 +61,7 @@ class TestVote(unittest.TestCase):
|
||||
rv = self.makeClient("0.0.0.0").post("/id/1/like")
|
||||
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data)["likes"] == 1
|
||||
assert loads(rv.data)["likes"] == 1
|
||||
|
||||
def testSelfLike(self):
|
||||
|
||||
@ -68,7 +70,7 @@ class TestVote(unittest.TestCase):
|
||||
rv = bob.post('/id/1/like')
|
||||
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data)["likes"] == 0
|
||||
assert loads(rv.data)["likes"] == 0
|
||||
|
||||
def testMultipleLikes(self):
|
||||
|
||||
@ -76,12 +78,12 @@ class TestVote(unittest.TestCase):
|
||||
for num in range(15):
|
||||
rv = self.makeClient("1.2.%i.0" % num).post('/id/1/like')
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data)["likes"] == num + 1
|
||||
assert loads(rv.data)["likes"] == num + 1
|
||||
|
||||
def testVoteOnNonexistentComment(self):
|
||||
rv = self.makeClient("1.2.3.4").post('/id/1/like')
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data) == None
|
||||
assert loads(rv.data) == None
|
||||
|
||||
def testTooManyLikes(self):
|
||||
|
||||
@ -91,14 +93,14 @@ class TestVote(unittest.TestCase):
|
||||
assert rv.status_code == 200
|
||||
|
||||
if num >= 142:
|
||||
assert json.loads(rv.data)["likes"] == 142
|
||||
assert loads(rv.data)["likes"] == 142
|
||||
else:
|
||||
assert json.loads(rv.data)["likes"] == num + 1
|
||||
assert loads(rv.data)["likes"] == num + 1
|
||||
|
||||
def testDislike(self):
|
||||
self.makeClient("127.0.0.1").post("/new?uri=test", data=json.dumps({"text": "..."}))
|
||||
rv = self.makeClient("1.2.3.4").post('/id/1/dislike')
|
||||
|
||||
assert rv.status_code == 200
|
||||
assert json.loads(rv.data)['likes'] == 0
|
||||
assert json.loads(rv.data)['dislikes'] == 1
|
||||
assert loads(rv.data)['likes'] == 0
|
||||
assert loads(rv.data)['dislikes'] == 1
|
||||
|
Loading…
Reference in New Issue
Block a user