Update to python 3

This commit is contained in:
Christine Dodrill 2015-11-15 08:36:35 -08:00
parent ffc9d7ca0d
commit 919cc0f505
17 changed files with 100 additions and 100 deletions

18
bot.py
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import Queue import queue
import sys import sys
import traceback import traceback
import time import time
@ -21,7 +21,7 @@ def main():
sys.path += ['lib'] sys.path += ['lib']
os.chdir(sys.path[0] or '.') # do stuff relative to the install directory os.chdir(sys.path[0] or '.') # do stuff relative to the install directory
print 'Loading plugins' print('Loading plugins')
# bootstrap the reloader # bootstrap the reloader
eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(), eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(),
@ -29,30 +29,30 @@ def main():
globals()) globals())
reload(init=True) reload(init=True)
print 'Connecting to IRC' print('Connecting to IRC')
try: try:
config() config()
if not hasattr(bot, 'config'): if not hasattr(bot, 'config'):
exit() exit()
except Exception, e: except Exception as e:
print 'ERROR: malformed config file:', e print('ERROR: malformed config file:', e)
traceback.print_exc() traceback.print_exc()
sys.exit() sys.exit()
print 'Running main loop' print('Running main loop')
while True: while True:
reload() # these functions only do things reload() # these functions only do things
config() # if changes have occured config() # if changes have occured
for conn in bot.conns.itervalues(): for conn in bot.conns.values():
try: try:
out = conn.out.get_nowait() out = conn.out.get_nowait()
main(conn, out) main(conn, out)
except Queue.Empty: except queue.Empty:
pass pass
while all(conn.out.empty() for conn in bot.conns.itervalues()): while all(conn.out.empty() for conn in bot.conns.values()):
time.sleep(.1) time.sleep(.1)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -46,7 +46,7 @@ def config():
try: try:
bot.config = json.load(open('config')) bot.config = json.load(open('config'))
bot._config_mtime = config_mtime bot._config_mtime = config_mtime
for name, conf in bot.config['connections'].iteritems(): for name, conf in bot.config['connections'].items():
if name in bot.conns: if name in bot.conns:
bot.conns[name].set_conf(conf) bot.conns[name].set_conf(conf)
else: else:
@ -54,8 +54,8 @@ def config():
bot.conns[name] = SSLIRC(conf) bot.conns[name] = SSLIRC(conf)
else: else:
bot.conns[name] = IRC(conf) bot.conns[name] = IRC(conf)
except ValueError, e: except ValueError as e:
print 'ERROR: malformed config!', e print('ERROR: malformed config!', e)
bot._config_mtime = 0 bot._config_mtime = 0

View File

@ -1,8 +1,8 @@
import re import re
import socket import socket
import time import time
import thread import _thread
import Queue import queue
from ssl import wrap_socket, CERT_NONE, CERT_REQUIRED, SSLError from ssl import wrap_socket, CERT_NONE, CERT_REQUIRED, SSLError
@ -20,7 +20,7 @@ def censor(text):
text = text.replace('\n', '').replace('\r', '') text = text.replace('\n', '').replace('\r', '')
replacement = '[censored]' replacement = '[censored]'
if 'censored_strings' in bot.config: if 'censored_strings' in bot.config:
words = map(re.escape, bot.config['censored_strings']) words = list(map(re.escape, bot.config['censored_strings']))
regex = re.compile('(%s)' % "|".join(words)) regex = re.compile('(%s)' % "|".join(words))
text = regex.sub(replacement, text) text = regex.sub(replacement, text)
return text return text
@ -33,8 +33,8 @@ class crlf_tcp(object):
def __init__(self, host, port, timeout=300): def __init__(self, host, port, timeout=300):
self.ibuffer = "" self.ibuffer = ""
self.obuffer = "" self.obuffer = ""
self.oqueue = Queue.Queue() # lines to be sent out self.oqueue = queue.Queue() # lines to be sent out
self.iqueue = Queue.Queue() # lines that were received self.iqueue = queue.Queue() # lines that were received
self.socket = self.create_socket() self.socket = self.create_socket()
self.host = host self.host = host
self.port = port self.port = port
@ -45,8 +45,8 @@ class crlf_tcp(object):
def run(self): def run(self):
self.socket.connect((self.host, self.port)) self.socket.connect((self.host, self.port))
thread.start_new_thread(self.recv_loop, ()) _thread.start_new_thread(self.recv_loop, ())
thread.start_new_thread(self.send_loop, ()) _thread.start_new_thread(self.send_loop, ())
def recv_from_socket(self, nbytes): def recv_from_socket(self, nbytes):
return self.socket.recv(nbytes) return self.socket.recv(nbytes)
@ -87,7 +87,7 @@ class crlf_tcp(object):
def send_loop(self): def send_loop(self):
while True: while True:
line = self.oqueue.get().splitlines()[0][:500] line = self.oqueue.get().splitlines()[0][:500]
print ">>> %r" % line print(">>> %r" % line)
self.obuffer += line.encode('utf-8', 'replace') + '\r\n' self.obuffer += line.encode('utf-8', 'replace') + '\r\n'
while self.obuffer: while self.obuffer:
sent = self.socket.send(self.obuffer) sent = self.socket.send(self.obuffer)
@ -133,12 +133,12 @@ class IRC(object):
def __init__(self, conf): def __init__(self, conf):
self.set_conf(conf) self.set_conf(conf)
self.out = Queue.Queue() # responses from the server are placed here self.out = queue.Queue() # responses from the server are placed here
# format: [rawline, prefix, command, params, # format: [rawline, prefix, command, params,
# nick, user, host, paramlist, msg] # nick, user, host, paramlist, msg]
self.connect() self.connect()
thread.start_new_thread(self.parse_loop, ()) _thread.start_new_thread(self.parse_loop, ())
def set_conf(self, conf): def set_conf(self, conf):
self.conf = conf self.conf = conf
@ -150,7 +150,7 @@ class IRC(object):
def connect(self): def connect(self):
self.conn = self.create_connection() self.conn = self.create_connection()
thread.start_new_thread(self.conn.run, ()) _thread.start_new_thread(self.conn.run, ())
self.cmd("NICK", [self.nick]) self.cmd("NICK", [self.nick])
self.cmd("USER", self.cmd("USER",
[self.conf.get('user', 'h'), "3", "*", self.conf.get('realname', [self.conf.get('user', 'h'), "3", "*", self.conf.get('realname',
@ -203,18 +203,18 @@ class FakeIRC(IRC):
def __init__(self, conf): def __init__(self, conf):
self.set_conf(conf) self.set_conf(conf)
self.out = Queue.Queue() # responses from the server are placed here self.out = queue.Queue() # responses from the server are placed here
self.f = open(fn, 'rb') self.f = open(fn, 'rb')
thread.start_new_thread(self.parse_loop, ()) _thread.start_new_thread(self.parse_loop, ())
def parse_loop(self): def parse_loop(self):
while True: while True:
msg = decode(self.f.readline()[9:]) msg = decode(self.f.readline()[9:])
if msg == '': if msg == '':
print "!!!!DONE READING FILE!!!!" print("!!!!DONE READING FILE!!!!")
return return
if msg.startswith(":"): # has a prefix if msg.startswith(":"): # has a prefix

View File

@ -1,9 +1,9 @@
import fuckit import fuckit
import thread import _thread
import traceback import traceback
thread.stack_size(1024 * 512) # reduce vm size _thread.stack_size(1024 * 512) # reduce vm size
class Input(dict): class Input(dict):
@ -81,14 +81,14 @@ def run(func, input):
else: else:
out = func(input.inp) out = func(input.inp)
if out is not None: if out is not None:
input.reply(unicode(out)) input.reply(str(out))
def do_sieve(sieve, bot, input, func, type, args): def do_sieve(sieve, bot, input, func, type, args):
try: try:
return sieve(bot, input, func, type, args) return sieve(bot, input, func, type, args)
except Exception: except Exception:
print 'sieve error', print('sieve error', end=' ')
traceback.print_exc() traceback.print_exc()
return None return None
@ -100,7 +100,7 @@ class Handler(object):
def __init__(self, func): def __init__(self, func):
self.func = func self.func = func
self.input_queue = Queue.Queue() self.input_queue = Queue.Queue()
thread.start_new_thread(self.start, ()) _thread.start_new_thread(self.start, ())
def start(self): def start(self):
uses_db = 'db' in self.func._args uses_db = 'db' in self.func._args
@ -151,14 +151,14 @@ def dispatch(input, kind, func, args, autohelp=False):
if func._thread: if func._thread:
bot.threads[func].put(input) bot.threads[func].put(input)
else: else:
thread.start_new_thread(run, (func, input)) _thread.start_new_thread(run, (func, input))
def match_command(command): def match_command(command):
commands = list(bot.commands) commands = list(bot.commands)
# do some fuzzy matching # do some fuzzy matching
prefix = filter(lambda x: x.startswith(command), commands) prefix = [x for x in commands if x.startswith(command)]
if len(prefix) == 1: if len(prefix) == 1:
return prefix[0] return prefix[0]
elif prefix and command not in prefix: elif prefix and command not in prefix:

View File

@ -14,7 +14,7 @@ if 'lastfiles' not in globals():
def make_signature(f): def make_signature(f):
return f.func_code.co_filename, f.func_name, f.func_code.co_firstlineno return f.__code__.co_filename, f.__name__, f.__code__.co_firstlineno
def format_plug(plug, kind='', lpad=0, width=40): def format_plug(plug, kind='', lpad=0, width=40):
@ -63,14 +63,14 @@ def reload(init=False):
fileset = set(glob.glob(os.path.join('plugins', '*.py'))) fileset = set(glob.glob(os.path.join('plugins', '*.py')))
# remove deleted/moved plugins # remove deleted/moved plugins
for name, data in bot.plugs.iteritems(): for name, data in bot.plugs.items():
bot.plugs[name] = [x for x in data if x[0]._filename in fileset] bot.plugs[name] = [x for x in data if x[0]._filename in fileset]
for filename in list(mtimes): for filename in list(mtimes):
if filename not in fileset and filename not in core_fileset: if filename not in fileset and filename not in core_fileset:
mtimes.pop(filename) mtimes.pop(filename)
for func, handler in list(bot.threads.iteritems()): for func, handler in list(bot.threads.items()):
if func._filename not in fileset: if func._filename not in fileset:
handler.stop() handler.stop()
del bot.threads[func] del bot.threads[func]
@ -92,16 +92,16 @@ def reload(init=False):
continue continue
# remove plugins already loaded from this filename # remove plugins already loaded from this filename
for name, data in bot.plugs.iteritems(): for name, data in bot.plugs.items():
bot.plugs[name] = [x for x in data bot.plugs[name] = [x for x in data
if x[0]._filename != filename] if x[0]._filename != filename]
for func, handler in list(bot.threads.iteritems()): for func, handler in list(bot.threads.items()):
if func._filename == filename: if func._filename == filename:
handler.stop() handler.stop()
del bot.threads[func] del bot.threads[func]
for obj in namespace.itervalues(): for obj in namespace.values():
if hasattr(obj, '_hook'): # check for magic if hasattr(obj, '_hook'): # check for magic
if obj._thread: if obj._thread:
bot.threads[obj] = Handler(obj) bot.threads[obj] = Handler(obj)
@ -110,21 +110,21 @@ def reload(init=False):
bot.plugs[type] += [data] bot.plugs[type] += [data]
if not init: if not init:
print '### new plugin (type: %s) loaded:' % \ print('### new plugin (type: %s) loaded:' % \
type, format_plug(data) type, format_plug(data))
if changed: if changed:
bot.commands = {} bot.commands = {}
for plug in bot.plugs['command']: for plug in bot.plugs['command']:
name = plug[1]['name'].lower() name = plug[1]['name'].lower()
if not re.match(r'^\w+$', name): if not re.match(r'^\w+$', name):
print '### ERROR: invalid command name "%s" (%s)' % (name, print('### ERROR: invalid command name "%s" (%s)' % (name,
format_plug(plug)) format_plug(plug)))
continue continue
if name in bot.commands: if name in bot.commands:
print "### ERROR: command '%s' already registered (%s, %s)" % \ print("### ERROR: command '%s' already registered (%s, %s)" % \
(name, format_plug(bot.commands[name]), (name, format_plug(bot.commands[name]),
format_plug(plug)) format_plug(plug)))
continue continue
bot.commands[name] = plug bot.commands[name] = plug
@ -134,28 +134,28 @@ def reload(init=False):
bot.events[event].append((func, args)) bot.events[event].append((func, args))
if init: if init:
print ' plugin listing:' print(' plugin listing:')
if bot.commands: if bot.commands:
# hack to make commands with multiple aliases # hack to make commands with multiple aliases
# print nicely # print nicely
print ' command:' print(' command:')
commands = collections.defaultdict(list) commands = collections.defaultdict(list)
for name, (func, args) in bot.commands.iteritems(): for name, (func, args) in bot.commands.items():
commands[make_signature(func)].append(name) commands[make_signature(func)].append(name)
for sig, names in sorted(commands.iteritems()): for sig, names in sorted(commands.items()):
names.sort(key=lambda x: (-len(x), x)) # long names first names.sort(key=lambda x: (-len(x), x)) # long names first
out = ' ' * 6 + '%s:%s:%s' % sig out = ' ' * 6 + '%s:%s:%s' % sig
out += ' ' * (50 - len(out)) + ', '.join(names) out += ' ' * (50 - len(out)) + ', '.join(names)
print out print(out)
for kind, plugs in sorted(bot.plugs.iteritems()): for kind, plugs in sorted(bot.plugs.items()):
if kind == 'command': if kind == 'command':
continue continue
print ' %s:' % kind print(' %s:' % kind)
for plug in plugs: for plug in plugs:
print format_plug(plug, kind=kind, lpad=6) print(format_plug(plug, kind=kind, lpad=6))
print print()

View File

@ -6,7 +6,7 @@ import json
quotes = [] quotes = []
with open("./plugins/data/bobross.json", "r") as fin: with open("./plugins/data/bobross.json", "r") as fin:
print fin print(fin)
quotes = json.load(fin) quotes = json.load(fin)
@hook.regex("^[Bb]ob [Rr]oss fact$") @hook.regex("^[Bb]ob [Rr]oss fact$")

View File

@ -10,7 +10,7 @@ def help(inp, bot=None, pm=None):
funcs = {} funcs = {}
disabled = bot.config.get('disabled_plugins', []) disabled = bot.config.get('disabled_plugins', [])
disabled_comm = bot.config.get('disabled_commands', []) disabled_comm = bot.config.get('disabled_commands', [])
for command, (func, args) in bot.commands.iteritems(): for command, (func, args) in bot.commands.items():
fn = re.match(r'^plugins.(.+).py$', func._filename) fn = re.match(r'^plugins.(.+).py$', func._filename)
if fn.group(1).lower() not in disabled: if fn.group(1).lower() not in disabled:
if command not in disabled_comm: if command not in disabled_comm:
@ -21,7 +21,7 @@ def help(inp, bot=None, pm=None):
else: else:
funcs[func] = command funcs[func] = command
commands = dict((value, key) for key, value in funcs.iteritems()) commands = dict((value, key) for key, value in funcs.items())
if not inp: if not inp:
pm('available commands: ' + ' '.join(sorted(commands))) pm('available commands: ' + ' '.join(sorted(commands)))

View File

@ -100,4 +100,4 @@ def log(paraml, input=None, bot=None):
fd = get_log_fd(bot.persist_dir, input.server, input.chan) fd = get_log_fd(bot.persist_dir, input.server, input.chan)
fd.write(timestamp + ' ' + beau + '\n') fd.write(timestamp + ' ' + beau + '\n')
print timestamp, input.chan, beau.encode('utf8', 'ignore') print(timestamp, input.chan, beau.encode('utf8', 'ignore'))

View File

@ -7,7 +7,7 @@ import ponyapi
def get_time(ep): def get_time(ep):
now = datetime.datetime(2006, 1, 1) now = datetime.datetime(2006, 1, 1)
now = now.now() now = now.now()
then = now.fromtimestamp(int(ep[u"air_date"])) then = now.fromtimestamp(int(ep["air_date"]))
td = then-now td = then-now
return now, then, td return now, then, td
@ -21,15 +21,15 @@ def when(inp, say=None):
seasonep = "" seasonep = ""
if inp == "discord": if inp == "discord":
return "%s will air on %s" % (ep[u"name"], DDate(then)) return "%s will air on %s" % (ep["name"], DDate(then))
if ep[u"is_movie"]: if ep["is_movie"]:
seasonep = "(a movie)" seasonep = "(a movie)"
else: else:
seasonep = "(season %d episode %d)" % (ep[u"season"], ep[u"episode"]) seasonep = "(season %d episode %d)" % (ep["season"], ep["episode"])
reply = "%s %s will air on %s in %d days!" % ( reply = "%s %s will air on %s in %d days!" % (
ep[u"name"], seasonep, then.strftime("%a, %d %b %Y %H:%M:%S"), ep["name"], seasonep, then.strftime("%a, %d %b %Y %H:%M:%S"),
td.days) td.days)
return reply return reply
@ -41,9 +41,9 @@ def randomep(inp):
seasonep = "" seasonep = ""
if ep[u"is_movie"]: if ep["is_movie"]:
seasonep = "(a movie)" seasonep = "(a movie)"
else: else:
seasonep = "(season %d episode %d)" % (ep[u"season"], ep[u"episode"]) seasonep = "(season %d episode %d)" % (ep["season"], ep["episode"])
return "%s %s" % (ep[u"name"], seasonep) return "%s %s" % (ep["name"], seasonep)

View File

@ -28,11 +28,11 @@ def sieve_suite(bot, input, func, kind, args):
if acl is None: if acl is None:
continue continue
if 'deny-except' in acl: if 'deny-except' in acl:
allowed_channels = map(unicode.lower, acl['deny-except']) allowed_channels = list(map(str.lower, acl['deny-except']))
if input.chan.lower() not in allowed_channels: if input.chan.lower() not in allowed_channels:
return None return None
if 'allow-except' in acl: if 'allow-except' in acl:
denied_channels = map(unicode.lower, acl['allow-except']) denied_channels = list(map(str.lower, acl['allow-except']))
if input.chan.lower() in denied_channels: if input.chan.lower() in denied_channels:
return None return None
if 'whitelist' in acl: if 'whitelist' in acl:

View File

@ -5,5 +5,5 @@ from util import hook, http
def tinyurl(match): def tinyurl(match):
try: try:
return http.open(match.group()).url.strip() return http.open(match.group()).url.strip()
except http.URLError, e: except http.URLError as e:
pass pass

View File

@ -8,7 +8,7 @@ prefix = split_len(names, 3)
suffix = split_len(endings, 3) suffix = split_len(endings, 3)
def ipv4tourbit(ip): def ipv4tourbit(ip):
ip = map(lambda x: int(x), ip.split(".")) ip = [int(x) for x in ip.split(".")]
return "~%s%s-%s%s" % (prefix[ip[0]], suffix[ip[1]], prefix[ip[2]], suffix[ip[3]]) return "~%s%s-%s%s" % (prefix[ip[0]], suffix[ip[1]], prefix[ip[2]], suffix[ip[3]])

View File

@ -29,7 +29,7 @@ def get_history(db, chan, url):
def nicklist(nicks): def nicklist(nicks):
nicks = sorted(dict(nicks), key=unicode.lower) nicks = sorted(dict(nicks), key=str.lower)
if len(nicks) <= 2: if len(nicks) <= 2:
return ' and '.join(nicks) return ' and '.join(nicks)
else: else:

View File

@ -8,7 +8,7 @@ def _hook_add(func, add, name=''):
func._hook.append(add) func._hook.append(add)
if not hasattr(func, '_filename'): if not hasattr(func, '_filename'):
func._filename = func.func_code.co_filename func._filename = func.__code__.co_filename
if not hasattr(func, '_args'): if not hasattr(func, '_args'):
argspec = inspect.getargspec(func) argspec = inspect.getargspec(func)
@ -39,7 +39,7 @@ def _hook_add(func, add, name=''):
def sieve(func): def sieve(func):
if func.func_code.co_argcount != 5: if func.__code__.co_argcount != 5:
raise ValueError( raise ValueError(
'sieves must take 5 arguments: (bot, input, func, type, args)') 'sieves must take 5 arguments: (bot, input, func, type, args)')
_hook_add(func, ['sieve', (func,)]) _hook_add(func, ['sieve', (func,)])
@ -50,7 +50,7 @@ def command(arg=None, **kwargs):
args = {} args = {}
def command_wrapper(func): def command_wrapper(func):
args.setdefault('name', func.func_name) args.setdefault('name', func.__name__)
_hook_add(func, ['command', (func, args)], 'command') _hook_add(func, ['command', (func, args)], 'command')
return func return func
@ -67,7 +67,7 @@ def event(arg=None, **kwargs):
args = kwargs args = kwargs
def event_wrapper(func): def event_wrapper(func):
args['name'] = func.func_name args['name'] = func.__name__
args.setdefault('events', ['*']) args.setdefault('events', ['*'])
_hook_add(func, ['event', (func, args)], 'event') _hook_add(func, ['event', (func, args)], 'event')
return func return func
@ -96,7 +96,7 @@ def regex(regex, flags=0, **kwargs):
args = kwargs args = kwargs
def regex_wrapper(func): def regex_wrapper(func):
args['name'] = func.func_name args['name'] = func.__name__
args['regex'] = regex args['regex'] = regex
args['re'] = re.compile(regex, flags) args['re'] = re.compile(regex, flags)
_hook_add(func, ['regex', (func, args)], 'regex') _hook_add(func, ['regex', (func, args)], 'regex')

View File

@ -1,18 +1,18 @@
# convenience wrapper for urllib2 & friends # convenience wrapper for urllib2 & friends
import binascii import binascii
import cookielib import http.cookiejar
import hmac import hmac
import json import json
import random import random
import string import string
import time import time
import urllib import urllib.request, urllib.parse, urllib.error
import urllib2 import urllib.request, urllib.error, urllib.parse
import urlparse import urllib.parse
from hashlib import sha1 from hashlib import sha1
from urllib import quote, unquote, quote_plus as _quote_plus from urllib.parse import quote, unquote, quote_plus as _quote_plus
from urllib2 import HTTPError, URLError from urllib.error import HTTPError, URLError
from lxml import etree, html from lxml import etree, html
@ -23,7 +23,7 @@ ua_firefox = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) ' \
'Gecko/20070725 Firefox/2.0.0.6' 'Gecko/20070725 Firefox/2.0.0.6'
ua_internetexplorer = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)' ua_internetexplorer = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
jar = cookielib.CookieJar() jar = http.cookiejar.CookieJar()
def get(*args, **kwargs): def get(*args, **kwargs):
@ -51,13 +51,13 @@ def open(url, query_params=None, post_data=None,
url = prepare_url(url, query_params) url = prepare_url(url, query_params)
request = urllib2.Request(url, post_data) request = urllib.request.Request(url, post_data)
if get_method is not None: if get_method is not None:
request.get_method = lambda: get_method request.get_method = lambda: get_method
if headers is not None: if headers is not None:
for header_key, header_value in headers.iteritems(): for header_key, header_value in headers.items():
request.add_header(header_key, header_value) request.add_header(header_key, header_value)
if 'User-Agent' not in request.headers: if 'User-Agent' not in request.headers:
@ -78,28 +78,28 @@ def open(url, query_params=None, post_data=None,
request.add_header('Authorization', header) request.add_header('Authorization', header)
if cookies: if cookies:
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar)) opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
else: else:
opener = urllib2.build_opener() opener = urllib.request.build_opener()
return opener.open(request) return opener.open(request)
def prepare_url(url, queries): def prepare_url(url, queries):
if queries: if queries:
scheme, netloc, path, query, fragment = urlparse.urlsplit(url) scheme, netloc, path, query, fragment = urllib.parse.urlsplit(url)
query = dict(urlparse.parse_qsl(query)) query = dict(urllib.parse.parse_qsl(query))
query.update(queries) query.update(queries)
query = urllib.urlencode(dict((to_utf8(key), to_utf8(value)) query = urllib.parse.urlencode(dict((to_utf8(key), to_utf8(value))
for key, value in query.iteritems())) for key, value in query.items()))
url = urlparse.urlunsplit((scheme, netloc, path, query, fragment)) url = urllib.parse.urlunsplit((scheme, netloc, path, query, fragment))
return url return url
def to_utf8(s): def to_utf8(s):
if isinstance(s, unicode): if isinstance(s, str):
return s.encode('utf8', 'ignore') return s.encode('utf8', 'ignore')
else: else:
return str(s) return str(s)

View File

@ -69,7 +69,7 @@ def timesince(d, now=None):
since = delta.days * 24 * 60 * 60 + delta.seconds since = delta.days * 24 * 60 * 60 + delta.seconds
if since <= 0: if since <= 0:
# d is in the future compared to now, stop processing. # d is in the future compared to now, stop processing.
return u'0 ' + 'minutes' return '0 ' + 'minutes'
for i, (seconds, name) in enumerate(chunks): for i, (seconds, name) in enumerate(chunks):
count = since // seconds count = since // seconds
if count != 0: if count != 0:

View File

@ -25,8 +25,8 @@ __license__ = "Python"
import re import re
import unicodedata import unicodedata
import urlparse import urllib.parse
from urllib import quote, unquote from urllib.parse import quote, unquote
default_port = { default_port = {
'http': 80, 'http': 80,
@ -50,7 +50,7 @@ normalizers = ( Normalizer( re.compile(r'(?:https?://)?(?:[a-zA-Z0-9\-]+\.)?(?:a
def normalize(url): def normalize(url):
"""Normalize a URL.""" """Normalize a URL."""
scheme, auth, path, query, fragment = urlparse.urlsplit(url.strip()) scheme, auth, path, query, fragment = urllib.parse.urlsplit(url.strip())
userinfo, host, port = re.search('([^@]*@)?([^:]*):?(.*)', auth).groups() userinfo, host, port = re.search('([^@]*@)?([^:]*):?(.*)', auth).groups()
# Always provide the URI scheme in lowercase characters. # Always provide the URI scheme in lowercase characters.
@ -73,7 +73,7 @@ def normalize(url):
# Always use uppercase A-through-F characters when percent-encoding. # Always use uppercase A-through-F characters when percent-encoding.
# All portions of the URI must be utf-8 encoded NFC from Unicode strings # All portions of the URI must be utf-8 encoded NFC from Unicode strings
def clean(string): def clean(string):
string = unicode(unquote(string), 'utf-8', 'replace') string = str(unquote(string), 'utf-8', 'replace')
return unicodedata.normalize('NFC', string).encode('utf-8') return unicodedata.normalize('NFC', string).encode('utf-8')
path = quote(clean(path), "~:/?#[]@!$&'()*+,;=") path = quote(clean(path), "~:/?#[]@!$&'()*+,;=")
fragment = quote(clean(fragment), "~") fragment = quote(clean(fragment), "~")
@ -112,7 +112,7 @@ def normalize(url):
# For schemes that define a port, use an empty port if the default is # For schemes that define a port, use an empty port if the default is
# desired # desired
if port and scheme in default_port.keys(): if port and scheme in list(default_port.keys()):
if port.isdigit(): if port.isdigit():
port = str(int(port)) port = str(int(port))
if int(port) == default_port[scheme]: if int(port) == default_port[scheme]:
@ -124,7 +124,7 @@ def normalize(url):
auth += ":" + port auth += ":" + port
if url.endswith("#") and query == "" and fragment == "": if url.endswith("#") and query == "" and fragment == "":
path += "#" path += "#"
normal_url = urlparse.urlunsplit((scheme, auth, path, query, normal_url = urllib.parse.urlunsplit((scheme, auth, path, query,
fragment)).replace("http:///", "http://") fragment)).replace("http:///", "http://")
for norm in normalizers: for norm in normalizers:
m = norm.regex.match(normal_url) m = norm.regex.match(normal_url)