Commit 550c74b6 authored by ale's avatar ale

Python 3 compatibility

Ensure the tests pass with Python 2 and Python 3.
parent 6abe6458
include: "https://git.autistici.org/ai3/build-deb/raw/master/ci-common.yml"
stages:
- test
- build_src
- build_pkgsrc
- build_pkg
- upload
- upload_pkg
run_tests:
stage: test
image: "ai/test:base"
image: "debian/stretch"
script:
- "apt-get install -q -y crm114 libevent-dev"
- "tox -e py27"
build:src:
stage: build_src
image: "ai/build:base"
script: "build-dsc"
artifacts:
paths:
- build-deb/
build:pkg:
stage: build_pkg
image: "ai/build:base"
script: "build-deb"
dependencies:
- build:src
artifacts:
paths:
- output-deb/
upload:pkg:
stage: upload
image: "ai/pkg:base"
script: "upload-packages"
dependencies:
- build:pkg
- "apt-get install -q -y crm114 tox"
- "tox -e py27,py3"
......@@ -3,7 +3,11 @@ import logging
import os
import time
import traceback
import urlparse
try:
import urlparse
except ImportError:
from urllib import parse as urlparse
from nospam import dbshelve
from nospam import plugin_base
from nospam import urls
......@@ -38,12 +42,15 @@ class NoSpam(object):
for filename in glob.glob(plugin_dir + '/*.py'):
ctx = {}
try:
execfile(filename, ctx)
except Exception, e:
# Used to be just an execfile().
with open(filename, 'r') as f:
code = compile(f.read(), filename, 'exec')
exec(code, ctx, ctx)
except Exception as e:
log.error('could not load plugin %s: %s' % (filename, str(e)))
continue
# Find subclasses of BasePlugin.
for key, value in ctx.iteritems():
for key, value in ctx.items():
if (isinstance(value, type)
and issubclass(value, plugin_base.BasePlugin)
and value != plugin_base.BasePlugin):
......@@ -53,7 +60,7 @@ class NoSpam(object):
try:
log.debug('enabling %s plugin' % (key,))
self._plugins.append(value(self._config))
except Exception, e:
except Exception as e:
log.error('could not initialize plugin %s: %s' % (
key, str(e)))
......@@ -139,7 +146,7 @@ class BlogSpamCompatibleAPI(object):
return 'SPAM:%s' % msg
else:
return 'OK'
except Exception, e:
except Exception as e:
log.error('testComment: %s\n%s' % (e, traceback.format_exc()))
return 'ERROR:%s' % str(e)
......@@ -151,7 +158,7 @@ class BlogSpamCompatibleAPI(object):
raise Exception('missing required attribute %s' % attr)
self._ns.classify(comment)
return 'OK'
except Exception, e:
except Exception as e:
log.error('classifyComment: %s\n%s' % (e, traceback.format_exc()))
return 'ERROR:%s' % str(e)
......
#!/usr/bin/python
from __future__ import print_function
"""
$Id$
......@@ -89,11 +91,11 @@ class Classifier:
# ask the classifier what category best matches some text
def classify(self, text):
path = string.replace(self.path, "/", "\\/") # need to escape path separator for the regexp matching
path = self.path.replace("/", "\\/") # need to escape path separator for the regexp matching
command = kCrmPath + (kClassifyCommand % (kClassificationType, self.getFileListString(), path, kClassificationExtension))
output = popen.popen_nb(command, text)
list = string.split(output.split('\n')[0])
if list == None:
list = output.decode('utf-8').split('\n')[0].split()
if not list:
return ("", 0.0)
else:
category = list[0]
......@@ -122,14 +124,14 @@ class Classifier:
# return a list of classification files as a string
def getFileListString(self):
return string.join(self.getFileList(), " ")
return ' '.join(self.getFileList())
# perform some self tests
def test(self):
print self.getFileList()
print(self.getFileList())
self.learn("good", "this is a test")
self.learn("bad", "this is very bad")
print "class was: %s, prob was:%f" % (self.classify("this is a test"))
print("class was: %s, prob was:%f" % (self.classify("this is a test")))
if __name__ == "__main__":
......
......@@ -21,6 +21,11 @@ log = logging.getLogger(__name__)
BLOCKSIZE = 4096
try:
unicode
except NameError:
unicode = str
def popen_nb(args, data=''):
"""Communicate with the process."""
......@@ -41,7 +46,7 @@ def popen_nb(args, data=''):
# p.stdin.write() doesn't return anything, so use os.write.
bytes_written += os.write(p.stdin.fileno(), data[bytes_written:])
log.debug('%s written %d (out of %d) to PID %d', time.strftime('%H:%M:%S'), bytes_written, bytes_total, p.pid)
except IOError, ex:
except IOError as ex:
if ex[0] != errno.EAGAIN:
raise
sys.exc_clear()
......@@ -60,7 +65,7 @@ def popen_nb(args, data=''):
break
log.debug('%s read %d from PID %d', time.strftime('%H:%M:%S'), len(chunk), p.pid)
chunks.append(chunk)
except IOError, ex:
except IOError as ex:
if ex[0] != errno.EAGAIN:
raise
sys.exc_clear()
......@@ -68,5 +73,5 @@ def popen_nb(args, data=''):
p.stdout.close()
log.debug('%s closed stdout for PID %d', time.strftime('%H:%M:%S'), p.pid)
return ''.join(chunks)
return b''.join(chunks)
import os
import cPickle
import pickle
import logging
import shutil
from UserDict import DictMixin
import shelve
import shutil
try:
from UserDict import DictMixin
except ImportError:
from collections import UserDict as DictMixin
try:
from bsddb3 import db
bsddb_available = True
......@@ -44,7 +47,7 @@ class connect_db(object):
flags = 0
if create:
flags |= db.DB_CREATE
_db.open("%s.db" % dbname, db.DB_BTREE, flags, 0600)
_db.open("%s.db" % dbname, db.DB_BTREE, flags, 0o600)
return _db
def close(self):
......@@ -52,19 +55,21 @@ class connect_db(object):
class dbshelve(DictMixin):
def open(self, data_file):
if os.path.isdir(data_file):
raise IOError('Datafile cannot be a directory')
data_dir = os.path.dirname(data_file)
self._env = connect_db(data_dir)
self._db = self._env.open(os.path.basename(data_file))
def __getitem__(self, attr):
if not isinstance(self._env, connect_db):
raise IOError('shelve store is not opened')
if not self._db.exists(attr):
raise KeyError
try:
value = cPickle.loads(self._db.get(attr))
value = pickle.loads(self._db.get(attr))
except :
value = None
return value
......@@ -75,7 +80,7 @@ class dbshelve(DictMixin):
if self._db.exists(attr):
self._db.delete(attr)
try:
value = cPickle.dumps(value)
value = pickle.dumps(value)
except:
value = None
self._db.put(attr, value)
......@@ -98,12 +103,13 @@ class dbshelve(DictMixin):
while cur.next():
(key, rawvalue) = cur.current()
try:
value = cPickle.loads(rawvalue)
except cPickle.UnpicklingError:
value = pickle.loads(rawvalue)
except pickle.UnpicklingError:
value = None
yield (key, value)
finally:
cur.close()
def keys(self):
return [key for key, _ in self.iteritems()]
......
......@@ -9,11 +9,19 @@ Adds support for creating XML-RPC APIs to Flask.
:license: MIT, see LICENSE for more details.
"""
from flask import request, current_app
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher as Dispatcher
import sys
import xmlrpclib
from flask import request, current_app
try:
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher as Dispatcher
import xmlrpclib
except ImportError:
from xmlrpc.server import SimpleXMLRPCDispatcher as Dispatcher
from xmlrpc import client as xmlrpclib
try:
basestring
except NameError:
basestring = str
Fault = xmlrpclib.Fault
class XMLRPCHandler(Dispatcher):
......@@ -249,7 +257,7 @@ def load_method_response(response):
"""
try:
return xmlrpclib.loads(response)[0][0]
except Fault, fault:
except Fault as fault:
return fault
......
import six
import time
import resource
......@@ -30,7 +31,7 @@ def incr_counter(key, amount=1):
def get_vars():
out = []
for key, value in _vars.iteritems():
for key, value in six.iteritems(_vars):
if callable(value):
value = value()
out.append('%s: %s\n' % (key, value))
......
......@@ -3,7 +3,11 @@ import os
import re
import socket
from nospam.dbshelve import get_shelve
import urlparse
try:
import urlparse
except ImportError:
from urllib import parse as urlparse
from nospam.plugin_base import BasePlugin
from nospam import urls
......
......@@ -2,8 +2,12 @@ import gevent
import os
import re
import socket
try:
import urlparse
except ImportError:
from urllib import parse as urlparse
from nospam.dbshelve import get_shelve
import urlparse
from nospam.plugin_base import BasePlugin
from nospam import urls
from nospam import utils
......
......@@ -23,7 +23,7 @@ class ClassifiedPluginTest(unittest.TestCase):
def test_too_many_links(self):
comment = {'_urls': ['http://www.blah.com'], 'train': 'spam'}
for i in xrange(11):
for i in range(11):
self.cp.classifyComment(comment)
score, msg = self.cp.testComment(comment)
self.assert_(score > 0)
......@@ -39,7 +39,7 @@ class HttpTest(unittest.TestCase):
def test_index(self):
resp = self.app.get('/')
self.assertEquals(200, resp.status_code)
self.assert_('NoSpam' in resp.data)
self.assert_(b'NoSpam' in resp.data)
def test_404(self):
resp = self.app.get('/does-not-exist')
......@@ -48,7 +48,7 @@ class HttpTest(unittest.TestCase):
def test_doc(self):
resp = self.app.get('/docs/about')
self.assertEquals(200, resp.status_code)
self.assert_('About' in resp.data)
self.assert_(b'About' in resp.data)
def test_vars(self):
resp = self.app.get('/vars')
......
......@@ -15,7 +15,7 @@ class UtilsTest(unittest.TestCase):
def test_read_file(self):
tmpfd, tmpfile = tempfile.mkstemp()
os.write(tmpfd, '# a comment\n\n\nline\n')
os.write(tmpfd, '# a comment\n\n\nline\n'.encode('utf-8'))
os.close(tmpfd)
self.tmpfiles.append(tmpfile)
......
import os
import re
import urlparse
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from nospam import utils
......
......@@ -9,7 +9,7 @@ setup(
author="ale",
author_email="ale@incal.net",
url="http://code.autistici.org/p/nospam",
install_requires=["gevent", "Jinja2", "Flask"],
install_requires=["gevent", "Jinja2", "Flask", "six"],
setup_requires=[],
zip_safe=False,
packages=find_packages(),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment