Browse Source

Py3: First 2to3 conversion

pull/1161/head
Safihre 8 years ago
parent
commit
c6054e622c
  1. 10
      SABHelper.py
  2. 125
      SABnzbd.py
  3. 24
      sabnzbd/__init__.py
  4. 30
      sabnzbd/api.py
  5. 10
      sabnzbd/assembler.py
  6. 36
      sabnzbd/bpsmeter.py
  7. 32
      sabnzbd/config.py
  8. 10
      sabnzbd/database.py
  9. 20
      sabnzbd/decoder.py
  10. 2
      sabnzbd/dirscanner.py
  11. 20
      sabnzbd/downloader.py
  12. 2
      sabnzbd/emailer.py
  13. 30
      sabnzbd/encoding.py
  14. 6
      sabnzbd/getipaddress.py
  15. 14
      sabnzbd/interface.py
  16. 12
      sabnzbd/lang.py
  17. 78
      sabnzbd/misc.py
  18. 47
      sabnzbd/newsunpack.py
  19. 6
      sabnzbd/newswrapper.py
  20. 27
      sabnzbd/notifier.py
  21. 8
      sabnzbd/nzbqueue.py
  22. 48
      sabnzbd/nzbstuff.py
  23. 4
      sabnzbd/osxmenu.py
  24. 2
      sabnzbd/panic.py
  25. 16
      sabnzbd/postproc.py
  26. 8
      sabnzbd/powersup.py
  27. 24
      sabnzbd/rating.py
  28. 26
      sabnzbd/rss.py
  29. 10
      sabnzbd/scheduler.py
  30. 14
      sabnzbd/tvsort.py
  31. 20
      sabnzbd/urlgrabber.py
  32. 12
      sabnzbd/utils/certgen.py
  33. 20
      sabnzbd/utils/checkdir.py
  34. 18
      sabnzbd/utils/diskspeed.py
  35. 8
      sabnzbd/utils/getperformance.py
  36. 32
      sabnzbd/utils/happyeyeballs.py
  37. 16
      sabnzbd/utils/json.py
  38. 18
      sabnzbd/utils/kronos.py
  39. 4
      sabnzbd/utils/pathbrowser.py
  40. 16
      sabnzbd/utils/pybonjour.py
  41. 16
      sabnzbd/utils/pystone.py
  42. 14
      sabnzbd/utils/rarfile.py
  43. 2
      sabnzbd/utils/rsslib.py
  44. 12
      sabnzbd/utils/servertests.py
  45. 6
      sabnzbd/utils/systrayiconthread.py
  46. 4
      sabnzbd/utils/upload.py
  47. 36
      scripts/Deobfuscate.py
  48. 2
      tests/conftest.py
  49. 2
      tests/testhelper.py
  50. 8
      tools/extract_pot.py
  51. 30
      tools/make_mo.py
  52. 139
      tools/msgfmt.py
  53. 60
      util/apireg.py
  54. 8
      util/mailslot.py

10
SABHelper.py

@ -17,7 +17,7 @@
import sys import sys
if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0): if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
print "Sorry, requires Python 2.6 or 2.7." print("Sorry, requires Python 2.6 or 2.7.")
sys.exit(1) sys.exit(1)
import os import os
@ -34,7 +34,7 @@ try:
import win32service import win32service
import pywintypes import pywintypes
except ImportError: except ImportError:
print "Sorry, requires Python module PyWin32." print("Sorry, requires Python module PyWin32.")
sys.exit(1) sys.exit(1)
from util.mailslot import MailSlot from util.mailslot import MailSlot
@ -137,14 +137,14 @@ class SABHelper(win32serviceutil.ServiceFramework):
win32evtlogutil.ReportEvent(self._svc_display_name_, win32evtlogutil.ReportEvent(self._svc_display_name_,
state, 0, state, 0,
servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.EVENTLOG_INFORMATION_TYPE,
(self._svc_name_, unicode(msg))) (self._svc_name_, str(msg)))
def ErrLogger(self, msg, text): def ErrLogger(self, msg, text):
win32evtlogutil.ReportEvent(self._svc_display_name_, win32evtlogutil.ReportEvent(self._svc_display_name_,
servicemanager.PYS_SERVICE_STOPPED, 0, servicemanager.PYS_SERVICE_STOPPED, 0,
servicemanager.EVENTLOG_ERROR_TYPE, servicemanager.EVENTLOG_ERROR_TYPE,
(self._svc_name_, unicode(msg)), (self._svc_name_, str(msg)),
unicode(text)) str(text))
############################################################################## ##############################################################################

125
SABnzbd.py

@ -16,18 +16,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import sys import sys
import imp
if sys.version_info[:2] < (2, 7) or sys.version_info[:2] >= (3, 0): if sys.version_info[:2] < (2, 7) or sys.version_info[:2] >= (3, 0):
print "Sorry, requires Python 2.7." print("Sorry, requires Python 2.7.")
sys.exit(1) sys.exit(1)
# Make sure UTF-8 is default 8bit encoding # Make sure UTF-8 is default 8bit encoding
if not hasattr(sys, "setdefaultencoding"): if not hasattr(sys, "setdefaultencoding"):
reload(sys) imp.reload(sys)
try: try:
sys.setdefaultencoding('utf-8') sys.setdefaultencoding('utf-8')
except: except:
print 'Sorry, you MUST add the SABnzbd folder to the PYTHONPATH environment variable' print('Sorry, you MUST add the SABnzbd folder to the PYTHONPATH environment variable')
print 'or find another way to force Python to use UTF-8 for text encoding.' print('or find another way to force Python to use UTF-8 for text encoding.')
sys.exit(1) sys.exit(1)
import logging import logging
@ -47,10 +48,10 @@ try:
if Cheetah.Version[0] != '2': if Cheetah.Version[0] != '2':
raise ValueError raise ValueError
except ValueError: except ValueError:
print "Sorry, requires Python module Cheetah 2.0rc7 or higher." print("Sorry, requires Python module Cheetah 2.0rc7 or higher.")
sys.exit(1) sys.exit(1)
except: except:
print "The Python module Cheetah is required" print("The Python module Cheetah is required")
sys.exit(1) sys.exit(1)
import cherrypy import cherrypy
@ -64,20 +65,20 @@ except:
from pysqlite2.dbapi2 import version as sqlite3_version from pysqlite2.dbapi2 import version as sqlite3_version
except: except:
if os.name != 'nt': if os.name != 'nt':
print "Sorry, requires Python module sqlite3" print("Sorry, requires Python module sqlite3")
print "Try: apt-get install python-pysqlite2" print("Try: apt-get install python-pysqlite2")
sys.exit(1) sys.exit(1)
else: else:
SQLITE_DLL = False SQLITE_DLL = False
import locale import locale
import __builtin__ import builtins
try: try:
locale.setlocale(locale.LC_ALL, "") locale.setlocale(locale.LC_ALL, "")
__builtin__.__dict__['codepage'] = locale.getlocale()[1] or 'cp1252' builtins.__dict__['codepage'] = locale.getlocale()[1] or 'cp1252'
except: except:
# Work-around for Python-ports with bad "locale" support # Work-around for Python-ports with bad "locale" support
__builtin__.__dict__['codepage'] = 'cp1252' builtins.__dict__['codepage'] = 'cp1252'
import sabnzbd import sabnzbd
import sabnzbd.lang import sabnzbd.lang
@ -117,7 +118,7 @@ except ImportError:
class MailSlot: class MailSlot:
pass pass
if sabnzbd.WIN32: if sabnzbd.WIN32:
print "Sorry, requires Python module PyWin32." print("Sorry, requires Python module PyWin32.")
sys.exit(1) sys.exit(1)
@ -170,44 +171,44 @@ class guiHandler(logging.Handler):
def print_help(): def print_help():
print print()
print "Usage: %s [-f <configfile>] <other options>" % sabnzbd.MY_NAME print(("Usage: %s [-f <configfile>] <other options>" % sabnzbd.MY_NAME))
print print()
print "Options marked [*] are stored in the config file" print("Options marked [*] are stored in the config file")
print print()
print "Options:" print("Options:")
print " -f --config-file <ini> Location of config file" print(" -f --config-file <ini> Location of config file")
print " -s --server <srv:port> Listen on server:port [*]" print(" -s --server <srv:port> Listen on server:port [*]")
print " -t --templates <templ> Template directory [*]" print(" -t --templates <templ> Template directory [*]")
print print()
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]" print(" -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]")
print " -w --weblogging Enable cherrypy access logging" print(" -w --weblogging Enable cherrypy access logging")
print print()
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]" print(" -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]")
if sabnzbd.WIN32: if sabnzbd.WIN32:
print " -d --daemon Use when run as a service" print(" -d --daemon Use when run as a service")
else: else:
print " -d --daemon Fork daemon process" print(" -d --daemon Fork daemon process")
print " --pid <path> Create a PID file in the given folder (full path)" print(" --pid <path> Create a PID file in the given folder (full path)")
print " --pidfile <path> Create a PID file with the given name (full path)" print(" --pidfile <path> Create a PID file with the given name (full path)")
print print()
print " -h --help Print this message" print(" -h --help Print this message")
print " -v --version Print version information" print(" -v --version Print version information")
print " -c --clean Remove queue, cache and logs" print(" -c --clean Remove queue, cache and logs")
print " -p --pause Start in paused mode" print(" -p --pause Start in paused mode")
print " --repair Add orphaned jobs from the incomplete folder to the queue" print(" --repair Add orphaned jobs from the incomplete folder to the queue")
print " --repair-all Try to reconstruct the queue from the incomplete folder" print(" --repair-all Try to reconstruct the queue from the incomplete folder")
print " with full data reconstruction" print(" with full data reconstruction")
print " --https <port> Port to use for HTTPS server" print(" --https <port> Port to use for HTTPS server")
print " --ipv6_hosting <0|1> Listen on IPv6 address [::1] [*]" print(" --ipv6_hosting <0|1> Listen on IPv6 address [::1] [*]")
print " --no-login Start with username and password reset" print(" --no-login Start with username and password reset")
print " --log-all Log all article handling (for developers)" print(" --log-all Log all article handling (for developers)")
print " --console Force console logging for OSX app" print(" --console Force console logging for OSX app")
print " --new Run a new instance of SABnzbd" print(" --new Run a new instance of SABnzbd")
def print_version(): def print_version():
print """ print(("""
%s-%s %s-%s
Copyright (C) 2008-2017, The SABnzbd-Team <team@sabnzbd.org> Copyright (C) 2008-2017, The SABnzbd-Team <team@sabnzbd.org>
@ -216,7 +217,7 @@ This is free software, and you are welcome to redistribute it
under certain conditions. It is licensed under the under certain conditions. It is licensed under the
GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version. GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version.
""" % (sabnzbd.MY_NAME, sabnzbd.__version__) """ % (sabnzbd.MY_NAME, sabnzbd.__version__)))
def daemonize(): def daemonize():
@ -225,7 +226,7 @@ def daemonize():
if pid > 0: if pid > 0:
sys.exit(0) sys.exit(0)
except OSError: except OSError:
print "fork() failed" print("fork() failed")
sys.exit(1) sys.exit(1)
os.chdir(sabnzbd.DIR_PROG) os.chdir(sabnzbd.DIR_PROG)
@ -239,7 +240,7 @@ def daemonize():
if pid > 0: if pid > 0:
sys.exit(0) sys.exit(0)
except OSError: except OSError:
print "fork() failed" print("fork() failed")
sys.exit(1) sys.exit(1)
dev_null = file('/dev/null', 'r') dev_null = file('/dev/null', 'r')
@ -484,7 +485,7 @@ def all_localhosts():
for item in info: for item in info:
item = item[4][0] item = item[4][0]
# Avoid problems on strange Linux settings # Avoid problems on strange Linux settings
if not isinstance(item, basestring): if not isinstance(item, str):
continue continue
# Only return IPv6 when enabled # Only return IPv6 when enabled
if item not in ips and ('::1' not in item or sabnzbd.cfg.ipv6_hosting()): if item not in ips and ('::1' not in item or sabnzbd.cfg.ipv6_hosting()):
@ -996,13 +997,13 @@ def main():
if enable_https and https_port: if enable_https and https_port:
try: try:
portend.free(cherryhost, https_port, timeout=0.05) portend.free(cherryhost, https_port, timeout=0.05)
except IOError, error: except IOError as error:
Bail_Out(browserhost, cherryport) Bail_Out(browserhost, cherryport)
except: except:
Bail_Out(browserhost, cherryport, '49') Bail_Out(browserhost, cherryport, '49')
try: try:
portend.free(cherryhost, cherryport, timeout=0.05) portend.free(cherryhost, cherryport, timeout=0.05)
except IOError, error: except IOError as error:
Bail_Out(browserhost, cherryport) Bail_Out(browserhost, cherryport)
except: except:
Bail_Out(browserhost, cherryport, '49') Bail_Out(browserhost, cherryport, '49')
@ -1019,7 +1020,7 @@ def main():
port = https_port or cherryport port = https_port or cherryport
try: try:
portend.free(browserhost, port, timeout=0.05) portend.free(browserhost, port, timeout=0.05)
except IOError, error: except IOError as error:
if str(error) == 'Port not bound.': if str(error) == 'Port not bound.':
pass pass
else: else:
@ -1048,7 +1049,7 @@ def main():
if not (enable_https and not https_port): if not (enable_https and not https_port):
try: try:
portend.free(browserhost, cherryport, timeout=0.05) portend.free(browserhost, cherryport, timeout=0.05)
except IOError, error: except IOError as error:
if str(error) == 'Port not bound.': if str(error) == 'Port not bound.':
pass pass
else: else:
@ -1077,8 +1078,8 @@ def main():
logdir = sabnzbd.cfg.log_dir.get_path() logdir = sabnzbd.cfg.log_dir.get_path()
if fork and not logdir: if fork and not logdir:
print "Error:" print("Error:")
print "I refuse to fork without a log directory!" print("I refuse to fork without a log directory!")
sys.exit(1) sys.exit(1)
if clean_up: if clean_up:
@ -1108,8 +1109,8 @@ def main():
logger.setLevel(LOGLEVELS[logging_level + 1]) logger.setLevel(LOGLEVELS[logging_level + 1])
except IOError: except IOError:
print "Error:" print("Error:")
print "Can't write to logfile" print("Can't write to logfile")
exit_sab(2) exit_sab(2)
if fork: if fork:
@ -1651,15 +1652,15 @@ def HandleCommandLine(allow_service=True):
service, sab_opts, serv_opts, _upload_nzbs = commandline_handler() service, sab_opts, serv_opts, _upload_nzbs = commandline_handler()
if service and not allow_service: if service and not allow_service:
# The other frozen apps don't support Services # The other frozen apps don't support Services
print "For service support, use SABnzbd-service.exe" print("For service support, use SABnzbd-service.exe")
return True return True
elif service: elif service:
if service in ('install', 'update'): if service in ('install', 'update'):
# In this case check for required parameters # In this case check for required parameters
path = get_f_option(sab_opts) path = get_f_option(sab_opts)
if not path: if not path:
print 'The -f <path> parameter is required.\n' \ print(('The -f <path> parameter is required.\n' \
'Use: -f <path> %s' % service 'Use: -f <path> %s' % service))
return True return True
# First run the service installed, because this will # First run the service installed, because this will
@ -1669,9 +1670,9 @@ def HandleCommandLine(allow_service=True):
# Add our own parameter to the Registry # Add our own parameter to the Registry
sab_opts = prep_service_parms(sab_opts) sab_opts = prep_service_parms(sab_opts)
if set_serv_parms(SABnzbd._svc_name_, sab_opts): if set_serv_parms(SABnzbd._svc_name_, sab_opts):
print SERVICE_MSG print(SERVICE_MSG)
else: else:
print 'Cannot set required Registry info.' print('Cannot set required Registry info.')
else: else:
# Other service commands need no manipulation # Other service commands need no manipulation
win32serviceutil.HandleCommandLine(SABnzbd) win32serviceutil.HandleCommandLine(SABnzbd)

24
sabnzbd/__init__.py

@ -22,7 +22,7 @@ import os
import logging import logging
import datetime import datetime
import tempfile import tempfile
import cPickle import pickle
import pickle import pickle
import gzip import gzip
import subprocess import subprocess
@ -638,7 +638,7 @@ def add_nzbfile(nzbfile, pp=None, script=None, cat=None, priority=NORMAL_PRIORIT
if cat and cat.lower() == 'default': if cat and cat.lower() == 'default':
cat = None cat = None
if isinstance(nzbfile, basestring): if isinstance(nzbfile, str):
# File coming from queue repair # File coming from queue repair
filename = nzbfile filename = nzbfile
keep = True keep = True
@ -669,7 +669,7 @@ def add_nzbfile(nzbfile, pp=None, script=None, cat=None, priority=NORMAL_PRIORIT
logging.info('Adding %s', filename) logging.info('Adding %s', filename)
if isinstance(nzbfile, basestring): if isinstance(nzbfile, str):
path = nzbfile path = nzbfile
else: else:
try: try:
@ -841,7 +841,7 @@ def keep_awake():
# set ES_SYSTEM_REQUIRED # set ES_SYSTEM_REQUIRED
KERNEL32.SetThreadExecutionState(ctypes.c_int(0x00000001)) KERNEL32.SetThreadExecutionState(ctypes.c_int(0x00000001))
else: else:
sleepless.keep_awake(u'SABnzbd is busy downloading and/or post-processing') sleepless.keep_awake('SABnzbd is busy downloading and/or post-processing')
if not awake and sleepless: if not awake and sleepless:
sleepless.allow_sleep() sleepless.allow_sleep()
@ -854,7 +854,7 @@ def get_new_id(prefix, folder, check_list=None):
""" Return unique prefixed admin identifier within folder """ Return unique prefixed admin identifier within folder
optionally making sure that id is not in the check_list. optionally making sure that id is not in the check_list.
""" """
for n in xrange(10000): for n in range(10000):
try: try:
if not os.path.exists(folder): if not os.path.exists(folder):
os.makedirs(folder) os.makedirs(folder)
@ -878,14 +878,14 @@ def save_data(data, _id, path, do_pickle=True, silent=False):
path = os.path.join(path, _id) path = os.path.join(path, _id)
# We try 3 times, to avoid any dict or access problems # We try 3 times, to avoid any dict or access problems
for t in xrange(3): for t in range(3):
try: try:
with open(path, 'wb') as data_file: with open(path, 'wb') as data_file:
if do_pickle: if do_pickle:
if cfg.use_pickle(): if cfg.use_pickle():
pickle.dump(data, data_file) pickle.dump(data, data_file)
else: else:
cPickle.dump(data, data_file) pickle.dump(data, data_file)
else: else:
data_file.write(data) data_file.write(data)
break break
@ -918,7 +918,7 @@ def load_data(_id, path, remove=True, do_pickle=True, silent=False):
if cfg.use_pickle(): if cfg.use_pickle():
data = pickle.load(data_file) data = pickle.load(data_file)
else: else:
data = cPickle.load(data_file) data = pickle.load(data_file)
else: else:
data = data_file.read() data = data_file.read()
@ -948,13 +948,13 @@ def save_admin(data, _id):
logging.debug("[%s] Saving data for %s in %s", misc.caller_name(), _id, path) logging.debug("[%s] Saving data for %s in %s", misc.caller_name(), _id, path)
# We try 3 times, to avoid any dict or access problems # We try 3 times, to avoid any dict or access problems
for t in xrange(3): for t in range(3):
try: try:
with open(path, 'wb') as data_file: with open(path, 'wb') as data_file:
if cfg.use_pickle(): if cfg.use_pickle():
data = pickle.dump(data, data_file) data = pickle.dump(data, data_file)
else: else:
data = cPickle.dump(data, data_file) data = pickle.dump(data, data_file)
break break
except: except:
if t == 2: if t == 2:
@ -979,7 +979,7 @@ def load_admin(_id, remove=False, silent=False):
if cfg.use_pickle(): if cfg.use_pickle():
data = pickle.load(data_file) data = pickle.load(data_file)
else: else:
data = cPickle.load(data_file) data = pickle.load(data_file)
if remove: if remove:
misc.remove_file(path) misc.remove_file(path)
except: except:
@ -1183,5 +1183,5 @@ def history_updated():
""" To make sure we always have a fresh history """ """ To make sure we always have a fresh history """
sabnzbd.LAST_HISTORY_UPDATE += 1 sabnzbd.LAST_HISTORY_UPDATE += 1
# Never go over the limit # Never go over the limit
if sabnzbd.LAST_HISTORY_UPDATE+1 >= sys.maxint: if sabnzbd.LAST_HISTORY_UPDATE+1 >= sys.maxsize:
sabnzbd.LAST_HISTORY_UPDATE = 1 sabnzbd.LAST_HISTORY_UPDATE = 1

30
sabnzbd/api.py

@ -317,10 +317,10 @@ def _api_translate(name, output, kwargs):
def _api_addfile(name, output, kwargs): def _api_addfile(name, output, kwargs):
""" API: accepts name, output, pp, script, cat, priority, nzbname """ """ API: accepts name, output, pp, script, cat, priority, nzbname """
# When uploading via flash it will send the nzb in a kw arg called Filedata # When uploading via flash it will send the nzb in a kw arg called Filedata
if name is None or isinstance(name, basestring): if name is None or isinstance(name, str):
name = kwargs.get('Filedata') name = kwargs.get('Filedata')
# Normal upload will send the nzb in a kw arg called nzbfile # Normal upload will send the nzb in a kw arg called nzbfile
if name is None or isinstance(name, basestring): if name is None or isinstance(name, str):
name = kwargs.get('nzbfile') name = kwargs.get('nzbfile')
if hasattr(name, 'getvalue'): if hasattr(name, 'getvalue'):
# Side effect of next line is that attribute .value is created # Side effect of next line is that attribute .value is created
@ -354,10 +354,10 @@ def _api_retry(name, output, kwargs):
""" API: accepts name, output, value(=nzo_id), nzbfile(=optional NZB), password (optional) """ """ API: accepts name, output, value(=nzo_id), nzbfile(=optional NZB), password (optional) """
value = kwargs.get('value') value = kwargs.get('value')
# When uploading via flash it will send the nzb in a kw arg called Filedata # When uploading via flash it will send the nzb in a kw arg called Filedata
if name is None or isinstance(name, basestring): if name is None or isinstance(name, str):
name = kwargs.get('Filedata') name = kwargs.get('Filedata')
# Normal upload will send the nzb in a kw arg called nzbfile # Normal upload will send the nzb in a kw arg called nzbfile
if name is None or isinstance(name, basestring): if name is None or isinstance(name, str):
name = kwargs.get('nzbfile') name = kwargs.get('nzbfile')
password = kwargs.get('password') password = kwargs.get('password')
password = password[0] if isinstance(password, list) else password password = password[0] if isinstance(password, list) else password
@ -557,7 +557,7 @@ def _api_addurl(names, output, kwargs):
nzbnames = [nzbnames] nzbnames = [nzbnames]
nzo_ids = [] nzo_ids = []
for n in xrange(len(names)): for n in range(len(names)):
name = names[n] name = names[n]
if n < len(nzbnames): if n < len(nzbnames):
nzbname = nzbnames[n] nzbname = nzbnames[n]
@ -734,9 +734,9 @@ def _api_test_email(name, output, kwargs):
pack = {} pack = {}
pack['download'] = ['action 1', 'action 2'] pack['download'] = ['action 1', 'action 2']
pack['unpack'] = ['action 1', 'action 2'] pack['unpack'] = ['action 1', 'action 2']
res = sabnzbd.emailer.endjob(u'I had a d\xe8ja vu', 'unknown', True, res = sabnzbd.emailer.endjob('I had a d\xe8ja vu', 'unknown', True,
os.path.normpath(os.path.join(cfg.complete_dir.get_path(), u'/unknown/I had a d\xe8ja vu')), os.path.normpath(os.path.join(cfg.complete_dir.get_path(), '/unknown/I had a d\xe8ja vu')),
123 * MEBI, None, pack, 'my_script', u'Line 1\nLine 2\nLine 3\nd\xe8ja vu\n', 0, 123 * MEBI, None, pack, 'my_script', 'Line 1\nLine 2\nLine 3\nd\xe8ja vu\n', 0,
test=kwargs) test=kwargs)
if res == 'Email succeeded': if res == 'Email succeeded':
res = None res = None
@ -1060,8 +1060,8 @@ def report(output, error=None, keyword='value', data=None, callback=None, compat
# Special handling for list/tuple (backward compatibility) # Special handling for list/tuple (backward compatibility)
data = [str(val) for val in data] data = [str(val) for val in data]
data = ' '.join(data) data = ' '.join(data)
if isinstance(data, unicode): if isinstance(data, str):
response = u'%s\n' % data response = '%s\n' % data
else: else:
response = '%s\n' % str(data) response = '%s\n' % str(data)
@ -1088,7 +1088,7 @@ class xml_factory(object):
def _dict(self, keyw, lst): def _dict(self, keyw, lst):
text = [] text = []
for key in lst.keys(): for key in list(lst.keys()):
text.append(self.run(key, lst[key])) text.append(self.run(key, lst[key]))
if keyw: if keyw:
return '<%s>%s</%s>\n' % (keyw, ''.join(text), keyw) return '<%s>%s</%s>\n' % (keyw, ''.join(text), keyw)
@ -1105,7 +1105,7 @@ class xml_factory(object):
elif isinstance(cat, tuple): elif isinstance(cat, tuple):
text.append(self._tuple(plural_to_single(keyw, 'tuple'), cat)) text.append(self._tuple(plural_to_single(keyw, 'tuple'), cat))
else: else:
if not isinstance(cat, basestring): if not isinstance(cat, str):
cat = str(cat) cat = str(cat)
name = plural_to_single(keyw, 'item') name = plural_to_single(keyw, 'item')
text.append('<%s>%s</%s>\n' % (name, xml_name(cat, encoding='utf-8'), name)) text.append('<%s>%s</%s>\n' % (name, xml_name(cat, encoding='utf-8'), name))
@ -1482,7 +1482,7 @@ def rss_qstatus():
percentage = "%s%%" % (int(((mb - mbleft) / mb) * 100)) percentage = "%s%%" % (int(((mb - mbleft) / mb) * 100))
filename = xml_name(filename) filename = xml_name(filename)
name = u'%s (%s)' % (filename, percentage) name = '%s (%s)' % (filename, percentage)
item = Item() item = Item()
item.title = name item.title = name
@ -1813,7 +1813,7 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
for item in items: for item in items:
for key in item: for key in item:
value = item[key] value = item[key]
if isinstance(value, basestring): if isinstance(value, str):
item[key] = converter(value) item[key] = converter(value)
if details_show_all: if details_show_all:
@ -1973,7 +1973,7 @@ def list_cats(default=True):
def remove_callable(dic): def remove_callable(dic):
""" Remove all callable items from dictionary """ """ Remove all callable items from dictionary """
for key, value in dic.items(): for key, value in list(dic.items()):
if callable(value): if callable(value):
del dic[key] del dic[key]
return dic return dic

10
sabnzbd/assembler.py

@ -20,7 +20,7 @@ sabnzbd.assembler - threaded assembly/decoding of files
""" """
import os import os
import Queue import queue
import logging import logging
import re import re
from threading import Thread from threading import Thread
@ -51,7 +51,7 @@ class Assembler(Thread):
if queue: if queue:
self.queue = queue self.queue = queue
else: else:
self.queue = Queue.Queue() self.queue = queue.Queue()
Assembler.do = self Assembler.do = self
def stop(self): def stop(self):
@ -96,11 +96,11 @@ class Assembler(Thread):
logging.info('Decoding %s %s', filepath, nzf.type) logging.info('Decoding %s %s', filepath, nzf.type)
try: try:
filepath = self.assemble(nzf, filepath) filepath = self.assemble(nzf, filepath)
except IOError, (errno, strerror): except IOError as err:
# If job was deleted or in active post-processing, ignore error # If job was deleted or in active post-processing, ignore error
if not nzo.is_gone() and not nzo.pp_active: if not nzo.is_gone() and not nzo.pp_active:
# 28 == disk full => pause downloader # 28 == disk full => pause downloader
if errno == 28: if err.errno == 28:
logging.error(T('Disk full! Forcing Pause')) logging.error(T('Disk full! Forcing Pause'))
else: else:
logging.error(T('Disk error on creating file %s'), clip_path(filepath)) logging.error(T('Disk error on creating file %s'), clip_path(filepath))
@ -217,7 +217,7 @@ def is_cloaked(nzo, path, names):
for name in names: for name in names:
name = get_filename(name.lower()) name = get_filename(name.lower())
name, ext = os.path.splitext(unicoder(name)) name, ext = os.path.splitext(unicoder(name))
if ext == u'.rar' and fname.startswith(name) and (len(fname) - len(name)) < 8 and len(names) < 3 and not RE_SUBS.search(fname): if ext == '.rar' and fname.startswith(name) and (len(fname) - len(name)) < 8 and len(names) < 3 and not RE_SUBS.search(fname):
# Only warn once # Only warn once
if nzo.encrypted == 0: if nzo.encrypted == 0:
logging.warning(T('Job "%s" is probably encrypted due to RAR with same name inside this RAR'), nzo.final_name) logging.warning(T('Job "%s" is probably encrypted due to RAR with same name inside this RAR'), nzo.final_name)

36
sabnzbd/bpsmeter.py

@ -89,7 +89,7 @@ def fix_keys(data):
""" Convert keys of each dictionary in tuple 'data' to unicode """ """ Convert keys of each dictionary in tuple 'data' to unicode """
new_data = [] new_data = []
if isinstance(data, list): if isinstance(data, list):
for n in xrange(len(data)): for n in range(len(data)):
if isinstance(data[n], dict): if isinstance(data[n], dict):
new = {} new = {}
for key in data[n]: for key in data[n]:
@ -128,7 +128,7 @@ class BPSMeter(object):
self.q_period = 'm' # Daily/Weekly/Monthly quota = d/w/m self.q_period = 'm' # Daily/Weekly/Monthly quota = d/w/m
self.quota = self.left = 0.0 # Quota and remaining quota self.quota = self.left = 0.0 # Quota and remaining quota
self.have_quota = False # Flag for quota active self.have_quota = False # Flag for quota active
self.q_time = 0L # Next reset time for quota self.q_time = 0 # Next reset time for quota
self.q_hour = 0 # Quota reset hour self.q_hour = 0 # Quota reset hour
self.q_minute = 0 # Quota reset minute self.q_minute = 0 # Quota reset minute
self.quota_enabled = True # Scheduled quota enable/disable self.quota_enabled = True # Scheduled quota enable/disable
@ -190,7 +190,7 @@ class BPSMeter(object):
self.defaults() self.defaults()
# Force update of counters and validate data # Force update of counters and validate data
try: try:
for server in self.grand_total.keys(): for server in list(self.grand_total.keys()):
self.update(server) self.update(server)
except TypeError: except TypeError:
self.defaults() self.defaults()
@ -219,25 +219,25 @@ class BPSMeter(object):
if server: if server:
if server not in self.day_total: if server not in self.day_total:
self.day_total[server] = 0L self.day_total[server] = 0
self.day_total[server] += amount self.day_total[server] += amount
if server not in self.week_total: if server not in self.week_total:
self.week_total[server] = 0L self.week_total[server] = 0
self.week_total[server] += amount self.week_total[server] += amount
if server not in self.month_total: if server not in self.month_total:
self.month_total[server] = 0L self.month_total[server] = 0
self.month_total[server] += amount self.month_total[server] += amount
if server not in self.grand_total: if server not in self.grand_total:
self.grand_total[server] = 0L self.grand_total[server] = 0
self.grand_total[server] += amount self.grand_total[server] += amount
if server not in self.timeline_total: if server not in self.timeline_total:
self.timeline_total[server] = {} self.timeline_total[server] = {}
if self.day_label not in self.timeline_total[server]: if self.day_label not in self.timeline_total[server]:
self.timeline_total[server][self.day_label]= 0L self.timeline_total[server][self.day_label]= 0
self.timeline_total[server][self.day_label] += amount self.timeline_total[server][self.day_label] += amount
# Quota check # Quota check
@ -292,18 +292,18 @@ class BPSMeter(object):
def get_sums(self): def get_sums(self):
""" return tuple of grand, month, week, day totals """ """ return tuple of grand, month, week, day totals """
return (sum([v for v in self.grand_total.values()]), return (sum([v for v in list(self.grand_total.values())]),
sum([v for v in self.month_total.values()]), sum([v for v in list(self.month_total.values())]),
sum([v for v in self.week_total.values()]), sum([v for v in list(self.week_total.values())]),
sum([v for v in self.day_total.values()]) sum([v for v in list(self.day_total.values())])
) )
def amounts(self, server): def amounts(self, server):
""" Return grand, month, week, day totals for specified server """ """ Return grand, month, week, day totals for specified server """
return self.grand_total.get(server, 0L), \ return self.grand_total.get(server, 0), \
self.month_total.get(server, 0L), \ self.month_total.get(server, 0), \
self.week_total.get(server, 0L), \ self.week_total.get(server, 0), \
self.day_total.get(server, 0L), \ self.day_total.get(server, 0), \
self.timeline_total.get(server, {}) self.timeline_total.get(server, {})
def clear_server(self, server): def clear_server(self, server):
@ -422,7 +422,7 @@ class BPSMeter(object):
self.left = quota - self.left self.left = quota - self.left
self.quota = quota self.quota = quota
else: else:
self.quota = self.left = 0L self.quota = self.left = 0
self.update(0) self.update(0)
self.next_reset() self.next_reset()
if self.left > 0.5 and allow_resume: if self.left > 0.5 and allow_resume:
@ -472,7 +472,7 @@ class BPSMeter(object):
def midnight(self): def midnight(self):
""" Midnight action: dummy update for all servers """ """ Midnight action: dummy update for all servers """
for server in self.day_total.keys(): for server in list(self.day_total.keys()):
self.update(server) self.update(server)

32
sabnzbd/config.py

@ -27,7 +27,7 @@ import shutil
import time import time
import random import random
from hashlib import md5 from hashlib import md5
from urlparse import urlparse from urllib.parse import urlparse
import sabnzbd.misc import sabnzbd.misc
from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER
import configobj import configobj
@ -275,7 +275,7 @@ class OptionList(Option):
def get_string(self): def get_string(self):
""" Return the list as a comma-separated string """ """ Return the list as a comma-separated string """
lst = self.get() lst = self.get()
if isinstance(lst, basestring): if isinstance(lst, str):
return lst return lst
else: else:
return ', '.join(lst) return ', '.join(lst)
@ -283,7 +283,7 @@ class OptionList(Option):
def default_string(self): def default_string(self):
""" Return the default list as a comma-separated string """ """ Return the default list as a comma-separated string """
lst = self.default() lst = self.default()
if isinstance(lst, basestring): if isinstance(lst, str):
return lst return lst
else: else:
return ', '.join(lst) return ', '.join(lst)
@ -308,7 +308,7 @@ class OptionStr(Option):
def set(self, value): def set(self, value):
""" Set stripped value """ """ Set stripped value """
error = None error = None
if isinstance(value, basestring) and self.__strip: if isinstance(value, str) and self.__strip:
value = value.strip() value = value.strip()
if self.__validation: if self.__validation:
error, val = self.__validation(value) error, val = self.__validation(value)
@ -411,7 +411,7 @@ class ConfigServer(object):
value = values[kw] value = values[kw]
except KeyError: except KeyError:
continue continue
exec 'self.%s.set(value)' % kw exec('self.%s.set(value)' % kw)
if not self.displayname(): if not self.displayname():
self.displayname.set(self.__name) self.displayname.set(self.__name)
return True return True
@ -476,7 +476,7 @@ class ConfigCat(object):
value = values[kw] value = values[kw]
except KeyError: except KeyError:
continue continue
exec 'self.%s.set(value)' % kw exec('self.%s.set(value)' % kw)
return True return True
def get_dict(self, safe=False): def get_dict(self, safe=False):
@ -545,7 +545,7 @@ class OptionFilters(Option):
def set_dict(self, values): def set_dict(self, values):
""" Create filter list from dictionary with keys 'filter[0-9]+' """ """ Create filter list from dictionary with keys 'filter[0-9]+' """
filters = [] filters = []
for n in xrange(len(values)): for n in range(len(values)):
kw = 'filter%d' % n kw = 'filter%d' % n
val = values.get(kw) val = values.get(kw)
if val is not None: if val is not None:
@ -589,7 +589,7 @@ class ConfigRSS(object):
value = values[kw] value = values[kw]
except KeyError: except KeyError:
continue continue
exec 'self.%s.set(value)' % kw exec('self.%s.set(value)' % kw)
self.filters.set_dict(values) self.filters.set_dict(values)
return True return True
@ -623,7 +623,7 @@ def get_dconfig(section, keyword, nested=False):
""" """
data = {} data = {}
if not section: if not section:
for section in database.keys(): for section in list(database.keys()):
res, conf = get_dconfig(section, None, True) res, conf = get_dconfig(section, None, True)
data.update(conf) data.update(conf)
@ -634,12 +634,12 @@ def get_dconfig(section, keyword, nested=False):
return False, {} return False, {}
if section in ('servers', 'categories', 'rss'): if section in ('servers', 'categories', 'rss'):
data[section] = [] data[section] = []
for keyword in sect.keys(): for keyword in list(sect.keys()):
res, conf = get_dconfig(section, keyword, True) res, conf = get_dconfig(section, keyword, True)
data[section].append(conf) data[section].append(conf)
else: else:
data[section] = {} data[section] = {}
for keyword in sect.keys(): for keyword in list(sect.keys()):
res, conf = get_dconfig(section, keyword, True) res, conf = get_dconfig(section, keyword, True)
data[section].update(conf) data[section].update(conf)
@ -717,7 +717,7 @@ def _read_config(path, try_backup=False):
# No file found, create default INI file # No file found, create default INI file
try: try:
if not sabnzbd.WIN32: if not sabnzbd.WIN32:
prev = os.umask(077) prev = os.umask(0o77)
fp = open(path, "w") fp = open(path, "w")
fp.write("__version__=%s\n[misc]\n[logging]\n" % CONFIG_VERSION) fp.write("__version__=%s\n[misc]\n[logging]\n" % CONFIG_VERSION)
fp.close() fp.close()
@ -746,7 +746,7 @@ def _read_config(path, try_backup=False):
# INI file is still in 8bit ASCII encoding, so try Latin-1 instead # INI file is still in 8bit ASCII encoding, so try Latin-1 instead
CFG = configobj.ConfigObj(lines, default_encoding='cp1252', encoding='cp1252') CFG = configobj.ConfigObj(lines, default_encoding='cp1252', encoding='cp1252')
except (IOError, configobj.ConfigObjError, UnicodeEncodeError), strerror: except (IOError, configobj.ConfigObjError, UnicodeEncodeError) as strerror:
if try_backup: if try_backup:
if isinstance(strerror, UnicodeEncodeError): if isinstance(strerror, UnicodeEncodeError):
strerror = 'Character encoding of the file is inconsistent' strerror = 'Character encoding of the file is inconsistent'
@ -764,8 +764,8 @@ def _read_config(path, try_backup=False):
CFG.filename = path CFG.filename = path
CFG.encoding = 'utf-8' CFG.encoding = 'utf-8'
CFG['__encoding__'] = u'utf-8' CFG['__encoding__'] = 'utf-8'
CFG['__version__'] = unicode(CONFIG_VERSION) CFG['__version__'] = str(CONFIG_VERSION)
# Use CFG data to set values for all static options # Use CFG data to set values for all static options
for section in database: for section in database:
@ -951,7 +951,7 @@ def get_ordered_categories():
# Transform to list and sort # Transform to list and sort
categories = [] categories = []
for cat in database_cats.keys(): for cat in list(database_cats.keys()):
if cat != '*': if cat != '*':
categories.append(database_cats[cat].get_dict()) categories.append(database_cats[cat].get_dict())

10
sabnzbd/database.py

@ -119,7 +119,7 @@ class HistoryDB(object):
def execute(self, command, args=(), save=False): def execute(self, command, args=(), save=False):
''' Wrapper for executing SQL commands ''' ''' Wrapper for executing SQL commands '''
for tries in xrange(5, 0, -1): for tries in range(5, 0, -1):
try: try:
if args and isinstance(args, tuple): if args and isinstance(args, tuple):
self.c.execute(command, args) self.c.execute(command, args)
@ -129,7 +129,7 @@ class HistoryDB(object):
self.con.commit() self.con.commit()
return True return True
except: except:
error = str(sys.exc_value) error = str(sys.exc_info()[1])
if tries >= 0 and 'is locked' in error: if tries >= 0 and 'is locked' in error:
logging.debug('Database locked, wait and retry') logging.debug('Database locked, wait and retry')
time.sleep(0.5) time.sleep(0.5)
@ -479,7 +479,7 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
# Pack the dictionary up into a single string # Pack the dictionary up into a single string
# Stage Name is separated by ::: stage lines by ; and stages by \r\n # Stage Name is separated by ::: stage lines by ; and stages by \r\n
lines = [] lines = []
for key, results in stages.iteritems(): for key, results in list(stages.items()):
lines.append('%s:::%s' % (key, ';'.join(results))) lines.append('%s:::%s' % (key, ';'.join(results)))
stage_log = '\r\n'.join(lines) stage_log = '\r\n'.join(lines)
@ -487,11 +487,11 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
report = 'future' if nzo.futuretype else '' report = 'future' if nzo.futuretype else ''
# Analyze series info only when job is finished # Analyze series info only when job is finished
series = u'' series = ''
if postproc_time: if postproc_time:
seriesname, season, episode, dummy = sabnzbd.newsunpack.analyse_show(nzo.final_name) seriesname, season, episode, dummy = sabnzbd.newsunpack.analyse_show(nzo.final_name)
if seriesname and season and episode: if seriesname and season and episode:
series = u'%s/%s/%s' % (seriesname.lower(), season, episode) series = '%s/%s/%s' % (seriesname.lower(), season, episode)
return (completed, name, nzb_name, category, pp, script, report, url, status, nzo_id, storage, path, return (completed, name, nzb_name, category, pp, script, report, url, status, nzo_id, storage, path,
script_log, script_line, download_time, postproc_time, stage_log, downloaded, completeness, script_log, script_line, download_time, postproc_time, stage_log, downloaded, completeness,

20
sabnzbd/decoder.py

@ -70,7 +70,7 @@ class BadYenc(Exception):
Exception.__init__(self) Exception.__init__(self)
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)]) YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in range(256)])
class Decoder(Thread): class Decoder(Thread):
@ -125,7 +125,7 @@ class Decoder(Thread):
nzf.article_count += 1 nzf.article_count += 1
found = True found = True
except IOError, e: except IOError as e:
logme = T('Decoding %s failed') % art_id logme = T('Decoding %s failed') % art_id
logging.warning(logme) logging.warning(logme)
logging.info("Traceback: ", exc_info=True) logging.info("Traceback: ", exc_info=True)
@ -135,7 +135,7 @@ class Decoder(Thread):
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo) sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
register = False register = False
except MemoryError, e: except MemoryError as e:
logme = T('Decoder failure: Out of memory') logme = T('Decoder failure: Out of memory')
logging.warning(logme) logging.warning(logme)
anfo = sabnzbd.articlecache.ArticleCache.do.cache_info() anfo = sabnzbd.articlecache.ArticleCache.do.cache_info()
@ -147,7 +147,7 @@ class Decoder(Thread):
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo) sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
register = False register = False
except CrcError, e: except CrcError as e:
logme = 'CRC Error in %s' % art_id logme = 'CRC Error in %s' % art_id
logging.info(logme) logging.info(logme)
@ -236,7 +236,7 @@ class Decoder(Thread):
# Continue for _yenc or Python-yEnc # Continue for _yenc or Python-yEnc
# Filter out empty ones # Filter out empty ones
data = filter(None, data) data = [_f for _f in data if _f]
# No point in continuing if we don't have any data left # No point in continuing if we don't have any data left
if data: if data:
nzf = article.nzf nzf = article.nzf
@ -248,7 +248,7 @@ class Decoder(Thread):
if not ybegin: if not ybegin:
found = False found = False
try: try:
for i in xrange(min(40, len(data))): for i in range(min(40, len(data))):
if data[i].startswith('begin '): if data[i].startswith('begin '):
nzf.type = 'uu' nzf.type = 'uu'
found = True found = True
@ -277,7 +277,7 @@ class Decoder(Thread):
# Decode data # Decode data
if HAVE_YENC: if HAVE_YENC:
decoded_data, crc = _yenc.decode_string(''.join(data))[:2] decoded_data, crc = _yenc.decode_string(''.join(data))[:2]
partcrc = '%08X' % ((crc ^ -1) & 2 ** 32L - 1) partcrc = '%08X' % ((crc ^ -1) & 2 ** 32 - 1)
else: else:
data = ''.join(data) data = ''.join(data)
for i in (0, 9, 10, 13, 27, 32, 46, 61): for i in (0, 9, 10, 13, 27, 32, 46, 61):
@ -285,7 +285,7 @@ class Decoder(Thread):
data = data.replace(j, chr(i)) data = data.replace(j, chr(i))
decoded_data = data.translate(YDEC_TRANS) decoded_data = data.translate(YDEC_TRANS)
crc = binascii.crc32(decoded_data) crc = binascii.crc32(decoded_data)
partcrc = '%08X' % (crc & 2 ** 32L - 1) partcrc = '%08X' % (crc & 2 ** 32 - 1)
if ypart: if ypart:
crcname = 'pcrc32' crcname = 'pcrc32'
@ -350,7 +350,7 @@ def yCheck(data):
yend = None yend = None
# Check head # Check head
for i in xrange(min(40, len(data))): for i in range(min(40, len(data))):
try: try:
if data[i].startswith('=ybegin '): if data[i].startswith('=ybegin '):
splits = 3 splits = 3
@ -372,7 +372,7 @@ def yCheck(data):
break break
# Check tail # Check tail
for i in xrange(-1, -11, -1): for i in range(-1, -11, -1):
try: try:
if data[i].startswith('=yend '): if data[i].startswith('=yend '):
yend = ySplit(data[i]) yend = ySplit(data[i])

2
sabnzbd/dirscanner.py

@ -380,7 +380,7 @@ class DirScanner(threading.Thread):
# Wait until the attributes are stable for 1 second # Wait until the attributes are stable for 1 second
# but give up after 3 sec # but give up after 3 sec
stable = False stable = False
for n in xrange(3): for n in range(3):
time.sleep(1.0) time.sleep(1.0)
try: try:
stat_tuple_tmp = os.stat(path) stat_tuple_tmp = os.stat(path)

20
sabnzbd/downloader.py

@ -27,7 +27,7 @@ from nntplib import NNTPPermanentError
import socket import socket
import random import random
import sys import sys
import Queue import queue
import sabnzbd import sabnzbd
from sabnzbd.decorators import synchronized, NzbQueueLocker, DOWNLOADER_CV from sabnzbd.decorators import synchronized, NzbQueueLocker, DOWNLOADER_CV
@ -198,7 +198,7 @@ class Downloader(Thread):
for server in config.get_servers(): for server in config.get_servers():
self.init_server(None, server) self.init_server(None, server)
self.decoder_queue = Queue.Queue() self.decoder_queue = queue.Queue()
# Initialize decoders, only 1 for non-SABYenc # Initialize decoders, only 1 for non-SABYenc
self.decoder_workers = [] self.decoder_workers = []
@ -236,7 +236,7 @@ class Downloader(Thread):
create = True create = True
if oldserver: if oldserver:
for n in xrange(len(self.servers)): for n in range(len(self.servers)):
if self.servers[n].id == oldserver: if self.servers[n].id == oldserver:
# Server exists, do re-init later # Server exists, do re-init later
create = False create = False
@ -363,7 +363,7 @@ class Downloader(Thread):
return True return True
def nzo_servers(self, nzo): def nzo_servers(self, nzo):
return filter(nzo.server_in_try_list, self.servers) return list(filter(nzo.server_in_try_list, self.servers))
def maybe_block_server(self, server): def maybe_block_server(self, server):
if server.optional and server.active and (server.bad_cons / server.threads) > 3: if server.optional and server.active and (server.bad_cons / server.threads) > 3:
@ -521,8 +521,8 @@ class Downloader(Thread):
self.force_disconnect = False self.force_disconnect = False
# => Select # => Select
readkeys = self.read_fds.keys() readkeys = list(self.read_fds.keys())
writekeys = self.write_fds.keys() writekeys = list(self.write_fds.keys())
if readkeys or writekeys: if readkeys or writekeys:
read, write, error = select.select(readkeys, writekeys, (), 1.0) read, write, error = select.select(readkeys, writekeys, (), 1.0)
@ -618,7 +618,7 @@ class Downloader(Thread):
if sabnzbd.LOG_ALL: if sabnzbd.LOG_ALL:
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data)) logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
nw.clear_data() nw.clear_data()
except NNTPPermanentError, error: except NNTPPermanentError as error:
# Handle login problems # Handle login problems
block = False block = False
penalty = 0 penalty = 0
@ -824,7 +824,7 @@ class Downloader(Thread):
fileno = nw.nntp.sock.fileno() fileno = nw.nntp.sock.fileno()
if fileno not in self.read_fds: if fileno not in self.read_fds:
self.read_fds[fileno] = nw self.read_fds[fileno] = nw
except socket.error, err: except socket.error as err:
logging.info('Looks like server closed connection: %s', err) logging.info('Looks like server closed connection: %s', err)
self.__reset_nw(nw, "server broke off connection", quit=False) self.__reset_nw(nw, "server broke off connection", quit=False)
except: except:
@ -879,7 +879,7 @@ class Downloader(Thread):
break break
def unblock_all(self): def unblock_all(self):
for server_id in self._timers.keys(): for server_id in list(self._timers.keys()):
self.unblock(server_id) self.unblock(server_id)
@NzbQueueLocker @NzbQueueLocker
@ -889,7 +889,7 @@ class Downloader(Thread):
# Clean expired timers # Clean expired timers
now = time.time() now = time.time()
kicked = [] kicked = []
for server_id in self._timers.keys(): for server_id in list(self._timers.keys()):
if not [stamp for stamp in self._timers[server_id] if stamp >= now]: if not [stamp for stamp in self._timers[server_id] if stamp >= now]:
logging.debug('Forcing re-evaluation of server %s', server_id) logging.debug('Forcing re-evaluation of server %s', server_id)
del self._timers[server_id] del self._timers[server_id]

2
sabnzbd/emailer.py

@ -85,7 +85,7 @@ def send(message, email_to, test=None):
logging.debug("Connected to server %s:%s", server, port) logging.debug("Connected to server %s:%s", server, port)
except Exception, errorcode: except Exception as errorcode:
if errorcode[0]: if errorcode[0]:
# Non SSL mail server # Non SSL mail server

30
sabnzbd/encoding.py

@ -78,7 +78,7 @@ def special_fixer(p):
if p: if p:
# Remove \" constructions from incoming headers # Remove \" constructions from incoming headers
p = p.replace(r'\"', r'"') p = p.replace(r'\"', r'"')
if not p or isinstance(p, unicode): if not p or isinstance(p, str):
return p return p
try: try:
# First see if it isn't just UTF-8 # First see if it isn't just UTF-8
@ -95,7 +95,7 @@ def unicoder(p, force=False):
""" Make sure a Unicode string is returned """ Make sure a Unicode string is returned
When `force` is True, ignore filesystem encoding When `force` is True, ignore filesystem encoding
""" """
if isinstance(p, unicode): if isinstance(p, str):
return p return p
if isinstance(p, str): if isinstance(p, str):
if gUTF or force: if gUTF or force:
@ -105,13 +105,13 @@ def unicoder(p, force=False):
return p.decode(codepage, 'replace') return p.decode(codepage, 'replace')
return p.decode(codepage, 'replace') return p.decode(codepage, 'replace')
else: else:
return unicode(str(p)) return str(str(p))
def xml_name(p, keep_escape=False, encoding=None): def xml_name(p, keep_escape=False, encoding=None):
""" Prepare name for use in HTML/XML contect """ """ Prepare name for use in HTML/XML contect """
if isinstance(p, unicode): if isinstance(p, str):
pass pass
elif isinstance(p, str): elif isinstance(p, str):
if sabnzbd.DARWIN or encoding == 'utf-8': if sabnzbd.DARWIN or encoding == 'utf-8':
@ -133,9 +133,9 @@ class LatinFilter(Filter):
""" Make sure Cheetah gets only Unicode strings """ """ Make sure Cheetah gets only Unicode strings """
def filter(self, val, str=str, **kw): def filter(self, val, str=str, **kw):
if isinstance(val, unicode): if isinstance(val, str):
return val return val
elif isinstance(val, basestring): elif isinstance(val, str):
try: try:
if sabnzbd.WIN32: if sabnzbd.WIN32:
return val.decode(codepage) return val.decode(codepage)
@ -144,9 +144,9 @@ class LatinFilter(Filter):
except: except:
return val.decode(codepage, 'replace') return val.decode(codepage, 'replace')
elif val is None: elif val is None:
return u'' return ''
else: else:
return unicode(str(val)) return str(str(val))
class EmailFilter(Filter): class EmailFilter(Filter):
@ -155,17 +155,17 @@ class EmailFilter(Filter):
""" """
def filter(self, val, str=str, **kw): def filter(self, val, str=str, **kw):
if isinstance(val, unicode): if isinstance(val, str):
return val return val
elif isinstance(val, basestring): elif isinstance(val, str):
try: try:
return val.decode('utf-8') return val.decode('utf-8')
except: except:
return val.decode(codepage, 'replace') return val.decode(codepage, 'replace')
elif val is None: elif val is None:
return u'' return ''
else: else:
return unicode(str(val)) return str(str(val))
################################################################################ ################################################################################
@ -241,7 +241,7 @@ def fixup_ff4(p):
if ch.isdigit(): if ch.isdigit():
num += ch num += ch
elif ch == ';': elif ch == ';':
name.append(unichr(int(num)).encode('utf8')) name.append(chr(int(num)).encode('utf8'))
start = False start = False
else: else:
name.append('&#%s%s' % (num, ch)) name.append('&#%s%s' % (num, ch))
@ -282,12 +282,12 @@ def deunicode(p):
""" Return the correct 8bit ASCII encoding for the platform: """ Return the correct 8bit ASCII encoding for the platform:
Latin-1 for Windows/Posix-non-UTF and UTF-8 for OSX/Posix-UTF Latin-1 for Windows/Posix-non-UTF and UTF-8 for OSX/Posix-UTF
""" """
if isinstance(p, unicode): if isinstance(p, str):
if gUTF: if gUTF:
return p.encode('utf-8') return p.encode('utf-8')
else: else:
return p.encode(codepage, 'replace') return p.encode(codepage, 'replace')
elif isinstance(p, basestring): elif isinstance(p, str):
if gUTF: if gUTF:
try: try:
p.decode('utf-8') p.decode('utf-8')

6
sabnzbd/getipaddress.py

@ -72,7 +72,7 @@ def publicipv4():
# Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention, # Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention,
# meaning forcing IPv4 connections, and not allowing IPv6 connections # meaning forcing IPv4 connections, and not allowing IPv6 connections
try: try:
import urllib2 import urllib.request, urllib.error, urllib.parse
ipv4_found = False ipv4_found = False
# we only want IPv4 resolving, so socket.AF_INET: # we only want IPv4 resolving, so socket.AF_INET:
result = addresslookup4(sabnzbd.cfg.selftest_host()) result = addresslookup4(sabnzbd.cfg.selftest_host())
@ -85,13 +85,13 @@ def publicipv4():
selftest_ipv4 = item[4][0] # get next IPv4 address of sabnzbd.cfg.selftest_host() selftest_ipv4 = item[4][0] # get next IPv4 address of sabnzbd.cfg.selftest_host()
try: try:
# put the selftest_host's IPv4 address into the URL # put the selftest_host's IPv4 address into the URL
req = urllib2.Request("http://" + selftest_ipv4 + "/") req = urllib.request.Request("http://" + selftest_ipv4 + "/")
# specify the User-Agent, because certain sites refuse connections with "python urllib2" as User-Agent: # specify the User-Agent, because certain sites refuse connections with "python urllib2" as User-Agent:
req.add_header('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__ ) req.add_header('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__ )
# specify the Host, because we only provide the IPv4 address in the URL: # specify the Host, because we only provide the IPv4 address in the URL:
req.add_header('Host', sabnzbd.cfg.selftest_host()) req.add_header('Host', sabnzbd.cfg.selftest_host())
# get the response # get the response
public_ipv4 = urllib2.urlopen(req, timeout=2).read() # timeout 2 seconds, in case the website is not accessible public_ipv4 = urllib.request.urlopen(req, timeout=2).read() # timeout 2 seconds, in case the website is not accessible
# ... check the response is indeed an IPv4 address: # ... check the response is indeed an IPv4 address:
socket.inet_aton(public_ipv4) # if we got anything else than a plain IPv4 address, this will raise an exception socket.inet_aton(public_ipv4) # if we got anything else than a plain IPv4 address, this will raise an exception
# if we get here without exception, we're done: # if we get here without exception, we're done:

14
sabnzbd/interface.py

@ -23,7 +23,7 @@ import os
import time import time
import cherrypy import cherrypy
import logging import logging
import urllib import urllib.request, urllib.parse, urllib.error
import json import json
import re import re
import hashlib import hashlib
@ -124,7 +124,7 @@ def Raiser(root='', **kwargs):
args[key] = val args[key] = val
# Add extras # Add extras
if args: if args:
root = '%s?%s' % (root, urllib.urlencode(args)) root = '%s?%s' % (root, urllib.parse.urlencode(args))
# Optionally add the leading /sabnzbd/ (or what the user set) # Optionally add the leading /sabnzbd/ (or what the user set)
if not root.startswith(cfg.url_base()): if not root.startswith(cfg.url_base()):
root = cherrypy.request.script_name + root root = cherrypy.request.script_name + root
@ -233,7 +233,7 @@ def set_auth(conf):
def check_session(kwargs): def check_session(kwargs):
""" Check session key """ """ Check session key """
if not check_access(): if not check_access():
return u'Access denied' return 'Access denied'
key = kwargs.get('session') key = kwargs.get('session')
if not key: if not key:
key = kwargs.get('apikey') key = kwargs.get('apikey')
@ -1606,7 +1606,7 @@ class ConfigServer(object):
new = [] new = []
servers = config.get_servers() servers = config.get_servers()
server_names = sorted(servers.keys(), key=lambda svr: '%d%02d%s' % (int(not servers[svr].enable()), servers[svr].priority(), servers[svr].displayname().lower())) server_names = sorted(list(servers.keys()), key=lambda svr: '%d%02d%s' % (int(not servers[svr].enable()), servers[svr].priority(), servers[svr].displayname().lower()))
for svr in server_names: for svr in server_names:
new.append(servers[svr].get_dict(safe=True)) new.append(servers[svr].get_dict(safe=True))
t, m, w, d, timeline = BPSMeter.do.amounts(svr) t, m, w, d, timeline = BPSMeter.do.amounts(svr)
@ -1725,7 +1725,7 @@ def handle_server(kwargs, root=None, new_svr=False):
server = unique_svr_name(server) server = unique_svr_name(server)
for kw in ('ssl', 'send_group', 'enable', 'optional'): for kw in ('ssl', 'send_group', 'enable', 'optional'):
if kw not in kwargs.keys(): if kw not in list(kwargs.keys()):
kwargs[kw] = None kwargs[kw] = None
if svr and not new_svr: if svr and not new_svr:
svr.set_dict(kwargs) svr.set_dict(kwargs)
@ -1788,7 +1788,7 @@ class ConfigRss(object):
rss[feed]['pick_cat'] = pick_cat rss[feed]['pick_cat'] = pick_cat
rss[feed]['pick_script'] = pick_script rss[feed]['pick_script'] = pick_script
rss[feed]['link'] = urllib.quote_plus(feed.encode('utf-8')) rss[feed]['link'] = urllib.parse.quote_plus(feed.encode('utf-8'))
rss[feed]['baselink'] = [get_base_url(uri) for uri in rss[feed]['uri']] rss[feed]['baselink'] = [get_base_url(uri) for uri in rss[feed]['uri']]
rss[feed]['uris'] = feeds[feed].uri.get_string() rss[feed]['uris'] = feeds[feed].uri.get_string()
@ -2688,7 +2688,7 @@ def GetRssLog(feed):
return job return job
jobs = sabnzbd.rss.show_result(feed).values() jobs = list(sabnzbd.rss.show_result(feed).values())
good, bad, done = ([], [], []) good, bad, done = ([], [], [])
for job in jobs: for job in jobs:
if job['status'][0] == 'G': if job['status'][0] == 'G':

12
sabnzbd/lang.py

@ -35,7 +35,7 @@ sabnzbd.lang - Language support
import gettext import gettext
import __builtin__ import builtins
import glob import glob
import os import os
import operator import operator
@ -67,11 +67,11 @@ def set_language(language=None):
lng = gettext.translation(_DOMAIN, _LOCALEDIR, [language], fallback=True, codeset='latin-1') lng = gettext.translation(_DOMAIN, _LOCALEDIR, [language], fallback=True, codeset='latin-1')
# The unicode flag will make _() return Unicode # The unicode flag will make _() return Unicode
lng.install(unicode=True, names=['lgettext']) lng.install(str=True, names=['lgettext'])
__builtin__.__dict__['T'] = __builtin__.__dict__['_'] # Unicode builtins.__dict__['T'] = builtins.__dict__['_'] # Unicode
__builtin__.__dict__['Ta'] = __builtin__.__dict__['_'] # Unicode (Used to Latin-1, compatibility support) builtins.__dict__['Ta'] = builtins.__dict__['_'] # Unicode (Used to Latin-1, compatibility support)
__builtin__.__dict__['Tx'] = __builtin__.__dict__['_'] # Dynamic translation (unicode) builtins.__dict__['Tx'] = builtins.__dict__['_'] # Dynamic translation (unicode)
__builtin__.__dict__['TT'] = lambda x: unicode(x) # Use in text tables builtins.__dict__['TT'] = lambda x: str(x) # Use in text tables
def list_languages(): def list_languages():

78
sabnzbd/misc.py

@ -22,7 +22,7 @@ sabnzbd.misc - misc classes
import os import os
import sys import sys
import logging import logging
import urllib import urllib.request, urllib.parse, urllib.error
import re import re
import shutil import shutil
import threading import threading
@ -99,7 +99,7 @@ def calc_age(date, trans=False):
def monthrange(start, finish): def monthrange(start, finish):
""" Calculate months between 2 dates, used in the Config template """ """ Calculate months between 2 dates, used in the Config template """
months = (finish.year - start.year) * 12 + finish.month + 1 months = (finish.year - start.year) * 12 + finish.month + 1
for i in xrange(start.month, months): for i in range(start.month, months):
year = (i - 1) / 12 + start.year year = (i - 1) / 12 + start.year
month = (i - 1) % 12 + 1 month = (i - 1) % 12 + 1
yield datetime.date(year, month, 1) yield datetime.date(year, month, 1)
@ -123,7 +123,7 @@ def safe_fnmatch(f, pattern):
return False return False
def globber(path, pattern=u'*'): def globber(path, pattern='*'):
""" Return matching base file/folder names in folder `path` """ """ Return matching base file/folder names in folder `path` """
# Cannot use glob.glob() because it doesn't support Windows long name notation # Cannot use glob.glob() because it doesn't support Windows long name notation
if os.path.exists(path): if os.path.exists(path):
@ -131,7 +131,7 @@ def globber(path, pattern=u'*'):
return [] return []
def globber_full(path, pattern=u'*'): def globber_full(path, pattern='*'):
""" Return matching full file/folder names in folder `path` """ """ Return matching full file/folder names in folder `path` """
# Cannot use glob.glob() because it doesn't support Windows long name notation # Cannot use glob.glob() because it doesn't support Windows long name notation
if os.path.exists(path): if os.path.exists(path):
@ -335,7 +335,7 @@ def sanitize_foldername(name, limit=True):
uFL_ILLEGAL = FL_ILLEGAL.decode('cp1252') uFL_ILLEGAL = FL_ILLEGAL.decode('cp1252')
uFL_LEGAL = FL_LEGAL.decode('cp1252') uFL_LEGAL = FL_LEGAL.decode('cp1252')
if isinstance(name, unicode): if isinstance(name, str):
illegal = uFL_ILLEGAL illegal = uFL_ILLEGAL
legal = uFL_LEGAL legal = uFL_LEGAL
else: else:
@ -380,11 +380,11 @@ def sanitize_and_trim_path(path):
path = path.strip() path = path.strip()
new_path = '' new_path = ''
if sabnzbd.WIN32: if sabnzbd.WIN32:
if path.startswith(u'\\\\?\\UNC\\'): if path.startswith('\\\\?\\UNC\\'):
new_path = u'\\\\?\\UNC\\' new_path = '\\\\?\\UNC\\'
path = path[8:] path = path[8:]
elif path.startswith(u'\\\\?\\'): elif path.startswith('\\\\?\\'):
new_path = u'\\\\?\\' new_path = '\\\\?\\'
path = path[4:] path = path[4:]
path = path.replace('\\', '/') path = path.replace('\\', '/')
@ -456,7 +456,7 @@ def create_all_dirs(path, umask=False):
mask = cfg.umask() mask = cfg.umask()
if mask: if mask:
try: try:
os.chmod(path, int(mask, 8) | 0700) os.chmod(path, int(mask, 8) | 0o700)
except: except:
pass pass
return result return result
@ -544,7 +544,7 @@ def windows_variant():
""" """
from win32api import GetVersionEx from win32api import GetVersionEx
from win32con import VER_PLATFORM_WIN32_NT from win32con import VER_PLATFORM_WIN32_NT
import _winreg import winreg
vista_plus = x64 = False vista_plus = x64 = False
maj, _minor, _buildno, plat, _csd = GetVersionEx() maj, _minor, _buildno, plat, _csd = GetVersionEx()
@ -556,14 +556,14 @@ def windows_variant():
# This does *not* work: # This does *not* work:
# return os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' # return os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64'
# because the Python runtime returns 'X86' even on an x64 system! # because the Python runtime returns 'X86' even on an x64 system!
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment") r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")
for n in xrange(_winreg.QueryInfoKey(key)[1]): for n in range(winreg.QueryInfoKey(key)[1]):
name, value, _val_type = _winreg.EnumValue(key, n) name, value, _val_type = winreg.EnumValue(key, n)
if name == 'PROCESSOR_ARCHITECTURE': if name == 'PROCESSOR_ARCHITECTURE':
x64 = value.upper() == u'AMD64' x64 = value.upper() == 'AMD64'
break break
_winreg.CloseKey(key) winreg.CloseKey(key)
return vista_plus, x64 return vista_plus, x64
@ -574,35 +574,35 @@ _SERVICE_PARM = 'CommandLine'
def get_serv_parms(service): def get_serv_parms(service):
""" Get the service command line parameters from Registry """ """ Get the service command line parameters from Registry """
import _winreg import winreg
value = [] value = []
try: try:
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, _SERVICE_KEY + service) key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _SERVICE_KEY + service)
for n in xrange(_winreg.QueryInfoKey(key)[1]): for n in range(winreg.QueryInfoKey(key)[1]):
name, value, _val_type = _winreg.EnumValue(key, n) name, value, _val_type = winreg.EnumValue(key, n)
if name == _SERVICE_PARM: if name == _SERVICE_PARM:
break break
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
pass pass
for n in xrange(len(value)): for n in range(len(value)):
value[n] = value[n] value[n] = value[n]
return value return value
def set_serv_parms(service, args): def set_serv_parms(service, args):
""" Set the service command line parameters in Registry """ """ Set the service command line parameters in Registry """
import _winreg import winreg
uargs = [] uargs = []
for arg in args: for arg in args:
uargs.append(unicoder(arg)) uargs.append(unicoder(arg))
try: try:
key = _winreg.CreateKey(_winreg.HKEY_LOCAL_MACHINE, _SERVICE_KEY + service) key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, _SERVICE_KEY + service)
_winreg.SetValueEx(key, _SERVICE_PARM, None, _winreg.REG_MULTI_SZ, uargs) winreg.SetValueEx(key, _SERVICE_PARM, None, winreg.REG_MULTI_SZ, uargs)
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
return False return False
return True return True
@ -663,7 +663,7 @@ def check_latest_version():
# to bad file content. # to bad file content.
try: try:
fn = urllib.urlretrieve('https://raw.githubusercontent.com/sabnzbd/sabnzbd.github.io/master/latest.txt')[0] fn = urllib.request.urlretrieve('https://raw.githubusercontent.com/sabnzbd/sabnzbd.github.io/master/latest.txt')[0]
f = open(fn, 'r') f = open(fn, 'r')
data = f.read() data = f.read()
f.close() f.close()
@ -866,7 +866,7 @@ def check_mount(path):
m = re.search(r'^(/(?:mnt|media)/[^/]+)/', path) m = re.search(r'^(/(?:mnt|media)/[^/]+)/', path)
if m: if m:
for n in xrange(cfg.wait_ext_drive() or 1): for n in range(cfg.wait_ext_drive() or 1):
if os.path.exists(m.group(1)): if os.path.exists(m.group(1)):
return True return True
logging.debug('Waiting for %s to come online', m.group(1)) logging.debug('Waiting for %s to come online', m.group(1))
@ -1018,7 +1018,7 @@ def get_filepath(path, nzo, filename):
# download_dir is equal to the complete_dir. # download_dir is equal to the complete_dir.
dName = nzo.work_name dName = nzo.work_name
if not nzo.created: if not nzo.created:
for n in xrange(200): for n in range(200):
dName = dirname dName = dirname
if n: if n:
dName += '.' + str(n) dName += '.' + str(n)
@ -1074,7 +1074,7 @@ def renamer(old, new):
try: try:
shutil.move(old, new) shutil.move(old, new)
return return
except WindowsError, err: except WindowsError as err:
logging.debug('Error renaming "%s" to "%s" <%s>', old, new, err) logging.debug('Error renaming "%s" to "%s" <%s>', old, new, err)
if err[0] == 32: if err[0] == 32:
logging.debug('Retry rename %s to %s', old, new) logging.debug('Retry rename %s to %s', old, new)
@ -1096,7 +1096,7 @@ def remove_dir(path):
try: try:
remove_dir(path) remove_dir(path)
return return
except WindowsError, err: except WindowsError as err:
if err[0] == 32: if err[0] == 32:
logging.debug('Retry delete %s', path) logging.debug('Retry delete %s', path)
retries -= 1 retries -= 1
@ -1319,11 +1319,11 @@ else:
try: try:
s = os.statvfs(_dir) s = os.statvfs(_dir)
if s.f_blocks < 0: if s.f_blocks < 0:
disk_size = float(sys.maxint) * float(s.f_frsize) disk_size = float(sys.maxsize) * float(s.f_frsize)
else: else:
disk_size = float(s.f_blocks) * float(s.f_frsize) disk_size = float(s.f_blocks) * float(s.f_frsize)
if s.f_bavail < 0: if s.f_bavail < 0:
available = float(sys.maxint) * float(s.f_frsize) available = float(sys.maxsize) * float(s.f_frsize)
else: else:
available = float(s.f_bavail) * float(s.f_frsize) available = float(s.f_bavail) * float(s.f_frsize)
return disk_size / GIGI, available / GIGI return disk_size / GIGI, available / GIGI
@ -1383,7 +1383,7 @@ def create_https_certificates(ssl_cert, ssl_key):
try: try:
from sabnzbd.utils.certgen import generate_key, generate_local_cert from sabnzbd.utils.certgen import generate_key, generate_local_cert
private_key = generate_key(key_size=2048, output_file=ssl_key) private_key = generate_key(key_size=2048, output_file=ssl_key)
generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost') generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN='SABnzbd', ON='SABnzbd', CN='localhost')
logging.info('Self-signed certificates generated successfully') logging.info('Self-signed certificates generated successfully')
except: except:
logging.error(T('Error creating SSL key and certificate')) logging.error(T('Error creating SSL key and certificate'))
@ -1448,7 +1448,7 @@ def find_on_path(targets):
else: else:
paths = os.getenv('PATH').split(':') paths = os.getenv('PATH').split(':')
if isinstance(targets, basestring): if isinstance(targets, str):
targets = (targets, ) targets = (targets, )
for path in paths: for path in paths:
@ -1582,19 +1582,19 @@ def set_permissions(path, recursive=True):
def clip_path(path): def clip_path(path):
r""" Remove \\?\ or \\?\UNC\ prefix from Windows path """ r""" Remove \\?\ or \\?\UNC\ prefix from Windows path """
if sabnzbd.WIN32 and path and '?' in path: if sabnzbd.WIN32 and path and '?' in path:
path = path.replace(u'\\\\?\\UNC\\', u'\\\\', 1).replace(u'\\\\?\\', u'', 1) path = path.replace('\\\\?\\UNC\\', '\\\\', 1).replace('\\\\?\\', '', 1)
return path return path
def long_path(path): def long_path(path):
""" For Windows, convert to long style path; others, return same path """ """ For Windows, convert to long style path; others, return same path """
if sabnzbd.WIN32 and path and not path.startswith(u'\\\\?\\'): if sabnzbd.WIN32 and path and not path.startswith('\\\\?\\'):
if path.startswith('\\\\'): if path.startswith('\\\\'):
# Special form for UNC paths # Special form for UNC paths
path = path.replace(u'\\\\', u'\\\\?\\UNC\\', 1) path = path.replace('\\\\', '\\\\?\\UNC\\', 1)
else: else:
# Normal form for local paths # Normal form for local paths
path = u'\\\\?\\' + path path = '\\\\?\\' + path
return path return path

47
sabnzbd/newsunpack.py

@ -707,7 +707,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
elif line.startswith('Cannot find volume') and not inrecovery: elif line.startswith('Cannot find volume') and not inrecovery:
filename = os.path.basename(TRANS(line[19:])) filename = os.path.basename(TRANS(line[19:]))
nzo.fail_msg = T('Unpacking failed, unable to find %s') % unicoder(filename) nzo.fail_msg = T('Unpacking failed, unable to find %s') % unicoder(filename)
msg = (u'[%s] ' + T('Unpacking failed, unable to find %s')) % (setname, filename) msg = ('[%s] ' + T('Unpacking failed, unable to find %s')) % (setname, filename)
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
logging.warning(T('ERROR: unable to find "%s"'), filename) logging.warning(T('ERROR: unable to find "%s"'), filename)
fail = 1 fail = 1
@ -715,14 +715,14 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
elif line.endswith('- CRC failed'): elif line.endswith('- CRC failed'):
filename = TRANS(line[:-12].strip()) filename = TRANS(line[:-12].strip())
nzo.fail_msg = T('Unpacking failed, CRC error') nzo.fail_msg = T('Unpacking failed, CRC error')
msg = (u'[%s] ' + T('ERROR: CRC failed in "%s"')) % (setname, filename) msg = ('[%s] ' + T('ERROR: CRC failed in "%s"')) % (setname, filename)
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
logging.warning(T('ERROR: CRC failed in "%s"'), setname) logging.warning(T('ERROR: CRC failed in "%s"'), setname)
fail = 2 # Older unrar versions report a wrong password as a CRC error fail = 2 # Older unrar versions report a wrong password as a CRC error
elif line.startswith('File too large'): elif line.startswith('File too large'):
nzo.fail_msg = T('Unpacking failed, file too large for filesystem (FAT?)') nzo.fail_msg = T('Unpacking failed, file too large for filesystem (FAT?)')
msg = (u'[%s] ' + T('Unpacking failed, file too large for filesystem (FAT?)')) % setname msg = ('[%s] ' + T('Unpacking failed, file too large for filesystem (FAT?)')) % setname
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
# ERROR: File too large for file system (bigfile-5000MB) # ERROR: File too large for file system (bigfile-5000MB)
logging.error(T('ERROR: File too large for filesystem (%s)'), setname) logging.error(T('ERROR: File too large for filesystem (%s)'), setname)
@ -730,7 +730,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
elif line.startswith('Write error'): elif line.startswith('Write error'):
nzo.fail_msg = T('Unpacking failed, write error or disk is full?') nzo.fail_msg = T('Unpacking failed, write error or disk is full?')
msg = (u'[%s] ' + T('Unpacking failed, write error or disk is full?')) % setname msg = ('[%s] ' + T('Unpacking failed, write error or disk is full?')) % setname
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
logging.error(T('ERROR: write error (%s)'), line[11:]) logging.error(T('ERROR: write error (%s)'), line[11:])
fail = 1 fail = 1
@ -739,11 +739,11 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
line2 = proc.readline() line2 = proc.readline()
if 'must not exceed 260' in line2: if 'must not exceed 260' in line2:
nzo.fail_msg = T('Unpacking failed, path is too long') nzo.fail_msg = T('Unpacking failed, path is too long')
msg = u'[%s] %s: %s' % (T('Unpacking failed, path is too long'), setname, unicoder(line[13:])) msg = '[%s] %s: %s' % (T('Unpacking failed, path is too long'), setname, unicoder(line[13:]))
logging.error(T('ERROR: path too long (%s)'), unicoder(line[13:])) logging.error(T('ERROR: path too long (%s)'), unicoder(line[13:]))
else: else:
nzo.fail_msg = T('Unpacking failed, write error or disk is full?') nzo.fail_msg = T('Unpacking failed, write error or disk is full?')
msg = u'[%s] %s: %s' % (T('Unpacking failed, write error or disk is full?'), setname, unicoder(line[13:])) msg = '[%s] %s: %s' % (T('Unpacking failed, write error or disk is full?'), setname, unicoder(line[13:]))
logging.error(T('ERROR: write error (%s)'), unicoder(line[13:])) logging.error(T('ERROR: write error (%s)'), unicoder(line[13:]))
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
fail = 1 fail = 1
@ -753,7 +753,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
elif line.startswith('ERROR: '): elif line.startswith('ERROR: '):
nzo.fail_msg = T('Unpacking failed, see log') nzo.fail_msg = T('Unpacking failed, see log')
logging.warning(T('ERROR: %s'), (unicoder(line[7:]))) logging.warning(T('ERROR: %s'), (unicoder(line[7:])))
msg = (u'[%s] ' + T('ERROR: %s')) % (setname, line[7:]) msg = ('[%s] ' + T('ERROR: %s')) % (setname, line[7:])
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
fail = 1 fail = 1
@ -772,7 +772,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
else: else:
filename = os.path.split(rarfile_path)[1] filename = os.path.split(rarfile_path)[1]
nzo.fail_msg = T('Unpacking failed, archive requires a password') nzo.fail_msg = T('Unpacking failed, archive requires a password')
msg = (u'[%s][%s] ' + T('Unpacking failed, archive requires a password')) % (setname, filename) msg = ('[%s][%s] ' + T('Unpacking failed, archive requires a password')) % (setname, filename)
nzo.set_unpack_info('Unpack', unicoder(msg)) nzo.set_unpack_info('Unpack', unicoder(msg))
fail = 2 fail = 2
@ -1329,7 +1329,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
else: else:
msg = T('Invalid par2 files or invalid PAR2 parameters, cannot verify or repair') msg = T('Invalid par2 files or invalid PAR2 parameters, cannot verify or repair')
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1348,7 +1348,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
# Failed # Failed
msg = T('Repair failed, not enough repair blocks (%s short)') % str(needed_blocks) msg = T('Repair failed, not enough repair blocks (%s short)') % str(needed_blocks)
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1414,7 +1414,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
elif ' cannot be renamed to ' in line: elif ' cannot be renamed to ' in line:
msg = unicoder(line.strip()) msg = unicoder(line.strip())
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1422,7 +1422,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
# Oops, disk is full! # Oops, disk is full!
msg = T('Repairing failed, %s') % T('Disk full') msg = T('Repairing failed, %s') % T('Disk full')
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1447,7 +1447,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
elif 'No details available for recoverable file' in line: elif 'No details available for recoverable file' in line:
msg = unicoder(line.strip()) msg = unicoder(line.strip())
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1482,7 +1482,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
verifytotal = int(m.group(1)) verifytotal = int(m.group(1))
p.wait() p.wait()
except WindowsError, err: except WindowsError as err:
raise WindowsError(err) raise WindowsError(err)
logging.debug('PAR2 output was\n%s', '\n'.join(lines)) logging.debug('PAR2 output was\n%s', '\n'.join(lines))
@ -1626,14 +1626,14 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
else: else:
msg = T('Invalid par2 files or invalid PAR2 parameters, cannot verify or repair') msg = T('Invalid par2 files or invalid PAR2 parameters, cannot verify or repair')
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
elif line.startswith('There is not enough space on the disk'): elif line.startswith('There is not enough space on the disk'):
msg = T('Repairing failed, %s') % T('Disk full') msg = T('Repairing failed, %s') % T('Disk full')
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1774,7 +1774,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
# Failed # Failed
msg = T('Repair failed, not enough repair blocks (%s short)') % str(needed_blocks) msg = T('Repair failed, not enough repair blocks (%s short)') % str(needed_blocks)
nzo.fail_msg = msg nzo.fail_msg = msg
msg = u'[%s] %s' % (unicoder(setname), msg) msg = '[%s] %s' % (unicoder(setname), msg)
nzo.set_unpack_info('Repair', msg) nzo.set_unpack_info('Repair', msg)
nzo.status = Status.FAILED nzo.status = Status.FAILED
@ -1849,7 +1849,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
if renames: if renames:
# If succes, we also remove the possibly previously renamed ones # If succes, we also remove the possibly previously renamed ones
if finished: if finished:
reconstructed.extend(renames.values()) reconstructed.extend(list(renames.values()))
# Adding to the collection # Adding to the collection
nzo.renamed_file(renames) nzo.renamed_file(renames)
@ -2309,6 +2309,17 @@ def list2cmdline(lst):
return ' '.join(nlst) return ' '.join(nlst)
def get_from_url(url):
""" Retrieve URL and return content
`timeout` sets non-standard timeout
"""
import urllib.request, urllib.error, urllib.parse
try:
return urllib.request.urlopen(url).read()
except:
return None
def is_sevenfile(path): def is_sevenfile(path):
""" Return True if path has proper extension and 7Zip is installed """ """ Return True if path has proper extension and 7Zip is installed """
return SEVEN_COMMAND and os.path.splitext(path)[1].lower() == '.7z' return SEVEN_COMMAND and os.path.splitext(path)[1].lower() == '.7z'

6
sabnzbd/newswrapper.py

@ -131,7 +131,7 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
except (ssl.SSLError, CertificateError) as e: except (ssl.SSLError, CertificateError) as e:
nntp.error(e) nntp.error(e)
except socket.error, e: except socket.error as e:
try: try:
# socket.error can either return a string or a tuple # socket.error can either return a string or a tuple
if isinstance(e, tuple): if isinstance(e, tuple):
@ -231,7 +231,7 @@ class NNTP(object):
except (ssl.SSLError, CertificateError) as e: except (ssl.SSLError, CertificateError) as e:
self.error(e) self.error(e)
except socket.error, e: except socket.error as e:
try: try:
# socket.error can either return a string or a tuple # socket.error can either return a string or a tuple
if isinstance(e, tuple): if isinstance(e, tuple):
@ -460,7 +460,7 @@ class NewsWrapper(object):
self.last_line = new_lines.pop() self.last_line = new_lines.pop()
# Already remove the starting dots # Already remove the starting dots
for i in xrange(len(new_lines)): for i in range(len(new_lines)):
if new_lines[i][:2] == '..': if new_lines[i][:2] == '..':
new_lines[i] = new_lines[i][1:] new_lines[i] = new_lines[i][1:]
self.lines.extend(new_lines) self.lines.extend(new_lines)

27
sabnzbd/notifier.py

@ -20,13 +20,14 @@
sabnzbd.notifier - Send notifications to any notification services sabnzbd.notifier - Send notifications to any notification services
""" """
from __future__ import with_statement
import os.path import os.path
import logging import logging
import socket import socket
import urllib2 import urllib.request, urllib.error, urllib.parse
import httplib import http.client
import urllib import urllib.request, urllib.parse, urllib.error
import time
import subprocess import subprocess
import json import json
from threading import Thread from threading import Thread
@ -268,7 +269,7 @@ def send_growl(title, msg, gtype, test=None):
_GROWL, error = register_growl(growl_server, growl_password) _GROWL, error = register_growl(growl_server, growl_password)
if _GROWL: if _GROWL:
_GROWL_REG = True _GROWL_REG = True
if isinstance(msg, unicode): if isinstance(msg, str):
msg = msg.decode('utf-8') msg = msg.decode('utf-8')
elif not isinstance(msg, str): elif not isinstance(msg, str):
msg = str(msg) msg = str(msg)
@ -408,8 +409,8 @@ def send_prowl(title, msg, gtype, force=False, test=None):
return T('Cannot send, missing required data') return T('Cannot send, missing required data')
title = Tx(NOTIFICATION.get(gtype, 'other')) title = Tx(NOTIFICATION.get(gtype, 'other'))
title = urllib2.quote(title.encode('utf8')) title = urllib.parse.quote(title.encode('utf8'))
msg = urllib2.quote(msg.encode('utf8')) msg = urllib.parse.quote(msg.encode('utf8'))
prio = get_prio(gtype, 'prowl') prio = get_prio(gtype, 'prowl')
if force: if force:
@ -419,7 +420,7 @@ def send_prowl(title, msg, gtype, force=False, test=None):
url = 'https://api.prowlapp.com/publicapi/add?apikey=%s&application=SABnzbd' \ url = 'https://api.prowlapp.com/publicapi/add?apikey=%s&application=SABnzbd' \
'&event=%s&description=%s&priority=%d' % (apikey, title, msg, prio) '&event=%s&description=%s&priority=%d' % (apikey, title, msg, prio)
try: try:
urllib2.urlopen(url) urllib.request.urlopen(url)
return '' return ''
except: except:
logging.warning(T('Failed to send Prowl message')) logging.warning(T('Failed to send Prowl message'))
@ -473,8 +474,8 @@ def send_pushover(title, msg, gtype, force=False, test=None):
def do_send_pushover(body): def do_send_pushover(body):
try: try:
conn = httplib.HTTPSConnection("api.pushover.net:443") conn = http.client.HTTPSConnection("api.pushover.net:443")
conn.request("POST", "/1/messages.json", urllib.urlencode(body), conn.request("POST", "/1/messages.json", urllib.parse.urlencode(body),
{"Content-type": "application/x-www-form-urlencoded"}) {"Content-type": "application/x-www-form-urlencoded"})
res = conn.getresponse() res = conn.getresponse()
if res.status != 200: if res.status != 200:
@ -499,10 +500,10 @@ def send_pushbullet(title, msg, gtype, force=False, test=None):
if not apikey: if not apikey:
return T('Cannot send, missing required data') return T('Cannot send, missing required data')
title = u'SABnzbd: ' + Tx(NOTIFICATION.get(gtype, 'other')) title = 'SABnzbd: ' + Tx(NOTIFICATION.get(gtype, 'other'))
try: try:
conn = httplib.HTTPSConnection('api.pushbullet.com:443') conn = http.client.HTTPSConnection('api.pushbullet.com:443')
conn.request('POST', '/v2/pushes', conn.request('POST', '/v2/pushes',
json.dumps({ json.dumps({
'type': 'note', 'type': 'note',
@ -534,7 +535,7 @@ def send_nscript(title, msg, gtype, force=False, test=None):
parameters = sabnzbd.cfg.nscript_parameters() parameters = sabnzbd.cfg.nscript_parameters()
if not script: if not script:
return T('Cannot send, missing required data') return T('Cannot send, missing required data')
title = u'SABnzbd: ' + Tx(NOTIFICATION.get(gtype, 'other')) title = 'SABnzbd: ' + Tx(NOTIFICATION.get(gtype, 'other'))
if force or check_classes(gtype, 'nscript'): if force or check_classes(gtype, 'nscript'):
script_path = make_script_path(script) script_path = make_script_path(script)

8
sabnzbd/nzbqueue.py

@ -458,7 +458,7 @@ class NzbQueue(object):
if search: if search:
search = search.lower() search = search.lower()
removed = [] removed = []
for nzo_id in self.__nzo_table.keys(): for nzo_id in list(self.__nzo_table.keys()):
if (not search) or search in self.__nzo_table[nzo_id].final_name_pw_clean.lower(): if (not search) or search in self.__nzo_table[nzo_id].final_name_pw_clean.lower():
nzo = self.__nzo_table.pop(nzo_id) nzo = self.__nzo_table.pop(nzo_id)
nzo.deleted = True nzo.deleted = True
@ -562,7 +562,7 @@ class NzbQueue(object):
nzo1.priority = nzo2_priority nzo1.priority = nzo2_priority
item_id_pos1 = -1 item_id_pos1 = -1
item_id_pos2 = -1 item_id_pos2 = -1
for i in xrange(len(self.__nzo_list)): for i in range(len(self.__nzo_list)):
if item_id_1 == self.__nzo_list[i].nzo_id: if item_id_1 == self.__nzo_list[i].nzo_id:
item_id_pos1 = i item_id_pos1 = i
elif item_id_2 == self.__nzo_list[i].nzo_id: elif item_id_2 == self.__nzo_list[i].nzo_id:
@ -614,7 +614,7 @@ class NzbQueue(object):
self.__nzo_list = sort_queue_function(self.__nzo_list, _nzo_size_cmp, reverse) self.__nzo_list = sort_queue_function(self.__nzo_list, _nzo_size_cmp, reverse)
def sort_queue(self, field, reverse=None): def sort_queue(self, field, reverse=None):
if isinstance(reverse, basestring): if isinstance(reverse, str):
if reverse.lower() == 'desc': if reverse.lower() == 'desc':
reverse = True reverse = True
else: else:
@ -645,7 +645,7 @@ class NzbQueue(object):
return return
# Get the current position in the queue # Get the current position in the queue
for i in xrange(len(self.__nzo_list)): for i in range(len(self.__nzo_list)):
if nzo_id == self.__nzo_list[i].nzo_id: if nzo_id == self.__nzo_list[i].nzo_id:
nzo_id_pos1 = i nzo_id_pos1 = i
break break

48
sabnzbd/nzbstuff.py

@ -33,9 +33,9 @@ import hashlib
import difflib import difflib
try: try:
from cStringIO import StringIO from io import StringIO
except ImportError: except ImportError:
from StringIO import StringIO from io import StringIO
# SABnzbd modules # SABnzbd modules
import sabnzbd import sabnzbd
@ -494,7 +494,7 @@ class NzbParser(xml.sax.handler.ContentHandler):
# Check if file was added with same name # Check if file was added with same name
if cfg.reject_duplicate_files(): if cfg.reject_duplicate_files():
nzo_matches = filter(lambda x: (x.filename == nzf.filename), self.nzo.files) nzo_matches = [x for x in self.nzo.files if (x.filename == nzf.filename)]
if nzo_matches: if nzo_matches:
logging.info('File %s occured twice in NZB, discarding smaller file', nzf.filename) logging.info('File %s occured twice in NZB, discarding smaller file', nzf.filename)
@ -743,14 +743,14 @@ class NzbObject(TryList):
inpsrc.setByteStream(StringIO(nzb)) inpsrc.setByteStream(StringIO(nzb))
try: try:
parser.parse(inpsrc) parser.parse(inpsrc)
except xml.sax.SAXParseException, err: except xml.sax.SAXParseException as err:
self.incomplete = True self.incomplete = True
if '</nzb>' not in nzb: if '</nzb>' not in nzb:
logging.warning(T('Incomplete NZB file %s'), filename) logging.warning(T('Incomplete NZB file %s'), filename)
else: else:
logging.warning(T('Invalid NZB file %s, skipping (reason=%s, line=%s)'), logging.warning(T('Invalid NZB file %s, skipping (reason=%s, line=%s)'),
filename, err.getMessage(), err.getLineNumber()) filename, err.getMessage(), err.getLineNumber())
except Exception, err: except Exception as err:
self.incomplete = True self.incomplete = True
logging.warning(T('Invalid NZB file %s, skipping (reason=%s, line=%s)'), filename, err, 0) logging.warning(T('Invalid NZB file %s, skipping (reason=%s, line=%s)'), filename, err, 0)
@ -1002,7 +1002,7 @@ class NzbObject(TryList):
# If we couldn't parse it, we ignore it # If we couldn't parse it, we ignore it
if pack: if pack:
if pack not in self.md5packs.values(): if pack not in list(self.md5packs.values()):
logging.debug('Got md5pack for set %s', nzf.setname) logging.debug('Got md5pack for set %s', nzf.setname)
self.md5packs[setname] = pack self.md5packs[setname] = pack
# See if we need to postpone some pars # See if we need to postpone some pars
@ -1277,7 +1277,7 @@ class NzbObject(TryList):
return self.final_name return self.final_name
def set_final_name_pw(self, name, password=None): def set_final_name_pw(self, name, password=None):
if isinstance(name, basestring): if isinstance(name, str):
if password is not None: if password is not None:
name = platform_encode(name) name = platform_encode(name)
self.password = platform_encode(password) self.password = platform_encode(password)
@ -1348,7 +1348,7 @@ class NzbObject(TryList):
if not nzf.is_par2: if not nzf.is_par2:
# We have to find the right par-set # We have to find the right par-set
blocks_new = 0 blocks_new = 0
for parset in self.extrapars.keys(): for parset in list(self.extrapars.keys()):
if (parset in nzf.filename or parset in original_filename) and self.extrapars[parset]: if (parset in nzf.filename or parset in original_filename) and self.extrapars[parset]:
for new_nzf in self.extrapars[parset]: for new_nzf in self.extrapars[parset]:
self.add_parfile(new_nzf) self.add_parfile(new_nzf)
@ -1381,9 +1381,9 @@ class NzbObject(TryList):
return True, 200 return True, 200
# Do the full check # Do the full check
need = 0L need = 0
pars = 0L pars = 0
short = 0L short = 0
anypars = False anypars = False
for nzf_id in self.files_table: for nzf_id in self.files_table:
nzf = self.files_table[nzf_id] nzf = self.files_table[nzf_id]
@ -1419,7 +1419,7 @@ class NzbObject(TryList):
complete_time = format_time_string(seconds, timecompleted.days) complete_time = format_time_string(seconds, timecompleted.days)
msg1 = T('Downloaded in %s at an average of %sB/s') % (complete_time, to_units(avg_bps * 1024, dec_limit=1)) msg1 = T('Downloaded in %s at an average of %sB/s') % (complete_time, to_units(avg_bps * 1024, dec_limit=1))
msg1 += u'<br/>' + T('Age') + ': ' + calc_age(self.avg_date, True) msg1 += '<br/>' + T('Age') + ': ' + calc_age(self.avg_date, True)
bad = self.nzo_info.get('bad_articles', 0) bad = self.nzo_info.get('bad_articles', 0)
miss = self.nzo_info.get('missing_articles', 0) miss = self.nzo_info.get('missing_articles', 0)
@ -1427,14 +1427,14 @@ class NzbObject(TryList):
dups = self.nzo_info.get('duplicate_articles', 0) dups = self.nzo_info.get('duplicate_articles', 0)
msg2 = msg3 = msg4 = msg5 = '' msg2 = msg3 = msg4 = msg5 = ''
if bad: if bad:
msg2 = (u'<br/>' + T('%s articles were malformed')) % bad msg2 = ('<br/>' + T('%s articles were malformed')) % bad
if miss: if miss:
msg3 = (u'<br/>' + T('%s articles were missing')) % miss msg3 = ('<br/>' + T('%s articles were missing')) % miss
if dups: if dups:
msg4 = (u'<br/>' + T('%s articles had non-matching duplicates')) % dups msg4 = ('<br/>' + T('%s articles had non-matching duplicates')) % dups
if killed: if killed:
msg5 = (u'<br/>' + T('%s articles were removed')) % killed msg5 = ('<br/>' + T('%s articles were removed')) % killed
msg = u''.join((msg1, msg2, msg3, msg4, msg5, )) msg = ''.join((msg1, msg2, msg3, msg4, msg5, ))
self.set_unpack_info('Download', msg, unique=True) self.set_unpack_info('Download', msg, unique=True)
if self.url: if self.url:
self.set_unpack_info('Source', self.url, unique=True) self.set_unpack_info('Source', self.url, unique=True)
@ -1442,7 +1442,7 @@ class NzbObject(TryList):
if len(self.servercount) > 0: if len(self.servercount) > 0:
# Sort the servers first # Sort the servers first
servers = config.get_servers() servers = config.get_servers()
server_names = sorted(servers.keys(), key=lambda svr: '%d%02d%s' % (int(not servers[svr].enable()), servers[svr].priority(), servers[svr].displayname().lower())) server_names = sorted(list(servers.keys()), key=lambda svr: '%d%02d%s' % (int(not servers[svr].enable()), servers[svr].priority(), servers[svr].displayname().lower()))
msgs = ['%s=%sB' % (servers[server_name].displayname(), to_units(self.servercount[server_name])) for server_name in server_names if server_name in self.servercount] msgs = ['%s=%sB' % (servers[server_name].displayname(), to_units(self.servercount[server_name])) for server_name in server_names if server_name in self.servercount]
self.set_unpack_info('Servers', ', '.join(msgs), unique=True) self.set_unpack_info('Servers', ', '.join(msgs), unique=True)
@ -1500,14 +1500,14 @@ class NzbObject(TryList):
def move_top_bulk(self, nzf_ids): def move_top_bulk(self, nzf_ids):
self.cleanup_nzf_ids(nzf_ids) self.cleanup_nzf_ids(nzf_ids)
if nzf_ids: if nzf_ids:
target = range(len(nzf_ids)) target = list(range(len(nzf_ids)))
while 1: while 1:
self.move_up_bulk(nzf_ids, cleanup=False) self.move_up_bulk(nzf_ids, cleanup=False)
pos_nzf_table = self.build_pos_nzf_table(nzf_ids) pos_nzf_table = self.build_pos_nzf_table(nzf_ids)
keys = pos_nzf_table.keys() keys = list(pos_nzf_table.keys())
keys.sort() keys.sort()
if target == keys: if target == keys:
@ -1517,14 +1517,14 @@ class NzbObject(TryList):
def move_bottom_bulk(self, nzf_ids): def move_bottom_bulk(self, nzf_ids):
self.cleanup_nzf_ids(nzf_ids) self.cleanup_nzf_ids(nzf_ids)
if nzf_ids: if nzf_ids:
target = range(len(self.files) - len(nzf_ids), len(self.files)) target = list(range(len(self.files) - len(nzf_ids), len(self.files)))
while 1: while 1:
self.move_down_bulk(nzf_ids, cleanup=False) self.move_down_bulk(nzf_ids, cleanup=False)
pos_nzf_table = self.build_pos_nzf_table(nzf_ids) pos_nzf_table = self.build_pos_nzf_table(nzf_ids)
keys = pos_nzf_table.keys() keys = list(pos_nzf_table.keys())
keys.sort() keys.sort()
if target == keys: if target == keys:
@ -1961,9 +1961,9 @@ def get_attrib_file(path, size):
try: try:
f = open(path, 'r') f = open(path, 'r')
except: except:
return [None for unused in xrange(size)] return [None for unused in range(size)]
for unused in xrange(size): for unused in range(size):
line = f.readline().strip('\r\n ') line = f.readline().strip('\r\n ')
if line: if line:
if line.lower() == 'none': if line.lower() == 'none':

4
sabnzbd/osxmenu.py

@ -80,7 +80,7 @@ class SABnzbdDelegate(NSObject):
# logging.info("building menu") # logging.info("building menu")
status_bar = NSStatusBar.systemStatusBar() status_bar = NSStatusBar.systemStatusBar()
self.status_item = status_bar.statusItemWithLength_(NSVariableStatusItemLength) self.status_item = status_bar.statusItemWithLength_(NSVariableStatusItemLength)
for i in status_icons.keys(): for i in list(status_icons.keys()):
self.icons[i] = NSImage.alloc().initByReferencingFile_(status_icons[i]) self.icons[i] = NSImage.alloc().initByReferencingFile_(status_icons[i])
if sabnzbd.DARWIN_VERSION > 9: if sabnzbd.DARWIN_VERSION > 9:
# Support for Yosemite Dark Mode # Support for Yosemite Dark Mode
@ -709,7 +709,7 @@ class SABnzbdDelegate(NSObject):
def openFolderAction_(self, sender): def openFolderAction_(self, sender):
folder2open = sender.representedObject() folder2open = sender.representedObject()
if isinstance(folder2open, unicode): if isinstance(folder2open, str):
folder2open = folder2open.encode("utf-8") folder2open = folder2open.encode("utf-8")
if debug == 1: if debug == 1:
NSLog("[osx] %@", folder2open) NSLog("[osx] %@", folder2open)

2
sabnzbd/panic.py

@ -225,7 +225,7 @@ def show_error_dialog(msg):
""" """
if sabnzbd.WIN32: if sabnzbd.WIN32:
ctypes.windll.user32.MessageBoxW(0, unicoder(msg), T('Fatal error'), 0) ctypes.windll.user32.MessageBoxW(0, unicoder(msg), T('Fatal error'), 0)
print msg print(msg)
def error_page_401(status, message, traceback, version): def error_page_401(status, message, traceback, version):

16
sabnzbd/postproc.py

@ -20,7 +20,7 @@ sabnzbd.postproc - threaded post-processing of jobs
""" """
import os import os
import Queue import queue
import logging import logging
import sabnzbd import sabnzbd
import xml.sax.saxutils import xml.sax.saxutils
@ -67,7 +67,7 @@ class PostProcessor(Thread):
if self.history_queue is None: if self.history_queue is None:
self.history_queue = [] self.history_queue = []
self.queue = Queue.Queue() self.queue = queue.Queue()
for nzo in self.history_queue: for nzo in self.history_queue:
self.process(nzo) self.process(nzo)
self.__stop = False self.__stop = False
@ -185,7 +185,7 @@ class PostProcessor(Thread):
try: try:
nzo = self.queue.get(timeout=1) nzo = self.queue.get(timeout=1)
except Queue.Empty: except queue.Empty:
if check_eoq: if check_eoq:
check_eoq = False check_eoq = False
handle_empty_queue() handle_empty_queue()
@ -479,11 +479,11 @@ def process_job(nzo):
script_ret = '' script_ret = ''
if len(script_log.rstrip().split('\n')) > 1: if len(script_log.rstrip().split('\n')) > 1:
nzo.set_unpack_info('Script', nzo.set_unpack_info('Script',
u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, script_line, '%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, script_line,
xml.sax.saxutils.escape(script_output), T('More')), unique=True) xml.sax.saxutils.escape(script_output), T('More')), unique=True)
else: else:
# No '(more)' button needed # No '(more)' button needed
nzo.set_unpack_info('Script', u'%s%s ' % (script_ret, script_line), unique=True) nzo.set_unpack_info('Script', '%s%s ' % (script_ret, script_line), unique=True)
# Cleanup again, including NZB files # Cleanup again, including NZB files
if all_ok: if all_ok:
@ -497,7 +497,7 @@ def process_job(nzo):
if nzo.encrypted > 0: if nzo.encrypted > 0:
Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_ENCRYPTED) Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_ENCRYPTED)
if empty: if empty:
hosts = map(lambda s: s.host, sabnzbd.downloader.Downloader.do.nzo_servers(nzo)) hosts = [s.host for s in sabnzbd.downloader.Downloader.do.nzo_servers(nzo)]
if not hosts: if not hosts:
hosts = [None] hosts = [None]
for host in hosts: for host in hosts:
@ -631,7 +631,7 @@ def parring(nzo, workdir):
# Get verification status of sets # Get verification status of sets
verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath, remove=False) or {} verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath, remove=False) or {}
repair_sets = nzo.extrapars.keys() repair_sets = list(nzo.extrapars.keys())
re_add = False re_add = False
par_error = False par_error = False
@ -947,7 +947,7 @@ def del_marker(path):
def remove_from_list(name, lst): def remove_from_list(name, lst):
if name: if name:
for n in xrange(len(lst)): for n in range(len(lst)):
if lst[n].endswith(name): if lst[n].endswith(name):
logging.debug('Popping %s', lst[n]) logging.debug('Popping %s', lst[n])
lst.pop(n) lst.pop(n)

8
sabnzbd/powersup.py

@ -165,7 +165,7 @@ def _get_systemproxy(method):
try: try:
bus = dbus.SystemBus() bus = dbus.SystemBus()
return bus.get_object(name, path), interface, pinterface return bus.get_object(name, path), interface, pinterface
except dbus.exceptions.DBusException, msg: except dbus.exceptions.DBusException as msg:
logging.info('DBus not reachable (%s)', msg) logging.info('DBus not reachable (%s)', msg)
return None, None, None return None, None, None
@ -193,7 +193,7 @@ def linux_shutdown():
proxy.Stop(dbus_interface=interface) proxy.Stop(dbus_interface=interface)
else: else:
logging.info('DBus does not support Stop (shutdown)') logging.info('DBus does not support Stop (shutdown)')
except dbus.exceptions.DBusException, msg: except dbus.exceptions.DBusException as msg:
logging.error('Received a DBus exception %s', msg) logging.error('Received a DBus exception %s', msg)
os._exit(0) os._exit(0)
@ -223,7 +223,7 @@ def linux_hibernate():
else: else:
logging.info('DBus does not support Hibernate') logging.info('DBus does not support Hibernate')
time.sleep(10) time.sleep(10)
except dbus.exceptions.DBusException, msg: except dbus.exceptions.DBusException as msg:
logging.error('Received a DBus exception %s', msg) logging.error('Received a DBus exception %s', msg)
@ -252,5 +252,5 @@ def linux_standby():
else: else:
logging.info('DBus does not support Suspend (standby)') logging.info('DBus does not support Suspend (standby)')
time.sleep(10) time.sleep(10)
except dbus.exceptions.DBusException, msg: except dbus.exceptions.DBusException as msg:
logging.error('Received a DBus exception %s', msg) logging.error('Received a DBus exception %s', msg)

24
sabnzbd/rating.py

@ -19,14 +19,14 @@
sabnzbd.rating - Rating support functions sabnzbd.rating - Rating support functions
""" """
import httplib import http.client
import urllib import urllib.request, urllib.parse, urllib.error
import urlparse import urllib.parse
import time import time
import logging import logging
import copy import copy
import socket import socket
import Queue import queue
import collections import collections
from threading import RLock, Thread from threading import RLock, Thread
import sabnzbd import sabnzbd
@ -34,7 +34,7 @@ from sabnzbd.decorators import synchronized
import sabnzbd.cfg as cfg import sabnzbd.cfg as cfg
# A queue which ignores duplicates but maintains ordering # A queue which ignores duplicates but maintains ordering
class OrderedSetQueue(Queue.Queue): class OrderedSetQueue(queue.Queue):
def _init(self, maxsize): def _init(self, maxsize):
self.maxsize = maxsize self.maxsize = maxsize
self.queue = collections.OrderedDict() self.queue = collections.OrderedDict()
@ -122,7 +122,7 @@ class Rating(Thread):
silent=not cfg.rating_enable()) silent=not cfg.rating_enable())
if self.version == 1: if self.version == 1:
ratings = {} ratings = {}
for k, v in self.ratings.iteritems(): for k, v in list(self.ratings.items()):
ratings[k] = NzbRatingV2().to_v2(v) ratings[k] = NzbRatingV2().to_v2(v)
self.ratings = ratings self.ratings = ratings
self.version = 2 self.version = 2
@ -276,7 +276,7 @@ class Rating(Thread):
_headers = {'User-agent': 'SABnzbd+/%s' % sabnzbd.version.__version__, 'Content-type': 'application/x-www-form-urlencoded'} _headers = {'User-agent': 'SABnzbd+/%s' % sabnzbd.version.__version__, 'Content-type': 'application/x-www-form-urlencoded'}
rating = self._get_rating_by_indexer(indexer_id) # Requesting info here ensures always have latest information even on retry rating = self._get_rating_by_indexer(indexer_id) # Requesting info here ensures always have latest information even on retry
if hasattr(rating, 'host') and rating.host: if hasattr(rating, 'host') and rating.host:
host_parsed = urlparse.urlparse(rating.host) host_parsed = urllib.parse.urlparse(rating.host)
rating_host = host_parsed.netloc rating_host = host_parsed.netloc
# Is it an URL or just a HOST? # Is it an URL or just a HOST?
if host_parsed.path and host_parsed.path != '/': if host_parsed.path and host_parsed.path != '/':
@ -303,19 +303,19 @@ class Rating(Thread):
requests.append(self._flag_request(rating.auto_flag.get('val'), rating.auto_flag.get('detail'), 1)) requests.append(self._flag_request(rating.auto_flag.get('val'), rating.auto_flag.get('detail'), 1))
try: try:
conn = httplib.HTTPSConnection(rating_host) conn = http.client.HTTPSConnection(rating_host)
for request in filter(lambda r: r is not None, requests): for request in [r for r in requests if r is not None]:
if api_key: if api_key:
request['apikey'] = api_key request['apikey'] = api_key
request['i'] = indexer_id request['i'] = indexer_id
conn.request('POST', rating_url, urllib.urlencode(request), headers=_headers) conn.request('POST', rating_url, urllib.parse.urlencode(request), headers=_headers)
response = conn.getresponse() response = conn.getresponse()
response.read() response.read()
if response.status == httplib.UNAUTHORIZED: if response.status == http.client.UNAUTHORIZED:
_warn('Ratings server unauthorized user') _warn('Ratings server unauthorized user')
return False return False
elif response.status != httplib.OK: elif response.status != http.client.OK:
_warn('Ratings server failed to process request (%s, %s)' % (response.status, response.reason)) _warn('Ratings server failed to process request (%s, %s)' % (response.status, response.reason))
return False return False
self.ratings[indexer_id].changed = self.ratings[indexer_id].changed & ~rating.changed self.ratings[indexer_id].changed = self.ratings[indexer_id].changed & ~rating.changed

26
sabnzbd/rss.py

@ -150,7 +150,7 @@ def remove_obsolete(jobs, new_jobs):
""" """
now = time.time() now = time.time()
limit = now - 259200 # 3days (3x24x3600) limit = now - 259200 # 3days (3x24x3600)
olds = jobs.keys() olds = list(jobs.keys())
for old in olds: for old in olds:
tm = jobs[old]['time'] tm = jobs[old]['time']
if old not in new_jobs: if old not in new_jobs:
@ -175,7 +175,7 @@ class RSSQueue(object):
self.jobs = sabnzbd.load_admin(RSS_FILE_NAME) self.jobs = sabnzbd.load_admin(RSS_FILE_NAME)
if self.jobs: if self.jobs:
for feed in self.jobs: for feed in self.jobs:
remove_obsolete(self.jobs[feed], self.jobs[feed].keys()) remove_obsolete(self.jobs[feed], list(self.jobs[feed].keys()))
except: except:
logging.warning(T('Cannot read %s'), RSS_FILE_NAME) logging.warning(T('Cannot read %s'), RSS_FILE_NAME)
logging.info("Traceback: ", exc_info=True) logging.info("Traceback: ", exc_info=True)
@ -321,7 +321,7 @@ class RSSQueue(object):
if not entries: if not entries:
return unicoder(msg) return unicoder(msg)
else: else:
entries = jobs.keys() entries = list(jobs.keys())
# Filter out valid new links # Filter out valid new links
for entry in entries: for entry in entries:
@ -333,8 +333,8 @@ class RSSQueue(object):
link, category, size, age, season, episode = _get_link(uri, entry) link, category, size, age, season, episode = _get_link(uri, entry)
except (AttributeError, IndexError): except (AttributeError, IndexError):
link = None link = None
category = u'' category = ''
size = 0L size = 0
age = None age = None
logging.info(T('Incompatible feed') + ' ' + uri) logging.info(T('Incompatible feed') + ' ' + uri)
logging.info("Traceback: ", exc_info=True) logging.info("Traceback: ", exc_info=True)
@ -344,7 +344,7 @@ class RSSQueue(object):
# If there's multiple feeds, remove the duplicates based on title and size # If there's multiple feeds, remove the duplicates based on title and size
if len(uris) > 1: if len(uris) > 1:
skip_job = False skip_job = False
for job_link, job in jobs.items(): for job_link, job in list(jobs.items()):
# Allow 5% size deviation because indexers might have small differences for same release # Allow 5% size deviation because indexers might have small differences for same release
if job.get('title') == title and link != job_link and (job.get('size')*0.95) < size < (job.get('size')*1.05): if job.get('title') == title and link != job_link and (job.get('size')*0.95) < size < (job.get('size')*1.05):
logging.info("Ignoring job %s from other feed", title) logging.info("Ignoring job %s from other feed", title)
@ -358,7 +358,7 @@ class RSSQueue(object):
if category in ('', '*'): if category in ('', '*'):
category = None category = None
title = jobs[link].get('title', '') title = jobs[link].get('title', '')
size = jobs[link].get('size', 0L) size = jobs[link].get('size', 0)
age = jobs[link].get('age') age = jobs[link].get('age')
season = jobs[link].get('season', 0) season = jobs[link].get('season', 0)
episode = jobs[link].get('episode', 0) episode = jobs[link].get('episode', 0)
@ -387,7 +387,7 @@ class RSSQueue(object):
# Match against all filters until an positive or negative match # Match against all filters until an positive or negative match
logging.debug('Size %s', size) logging.debug('Size %s', size)
for n in xrange(regcount): for n in range(regcount):
if reEnabled[n]: if reEnabled[n]:
if category and reTypes[n] == 'C': if category and reTypes[n] == 'C':
found = re.search(regexes[n], category) found = re.search(regexes[n], category)
@ -504,14 +504,14 @@ class RSSQueue(object):
if self.next_run < time.time(): if self.next_run < time.time():
self.next_run = time.time() + cfg.rss_rate.get() * 60 self.next_run = time.time() + cfg.rss_rate.get() * 60
feeds = config.get_rss() feeds = config.get_rss()
for feed in feeds.keys(): for feed in list(feeds.keys()):
try: try:
if feeds[feed].enable.get(): if feeds[feed].enable.get():
logging.info('Starting scheduled RSS read-out for "%s"', feed) logging.info('Starting scheduled RSS read-out for "%s"', feed)
active = True active = True
self.run_feed(feed, download=True, ignoreFirst=True) self.run_feed(feed, download=True, ignoreFirst=True)
# Wait 15 seconds, else sites may get irritated # Wait 15 seconds, else sites may get irritated
for unused in xrange(15): for unused in range(15):
if self.shutdown: if self.shutdown:
return return
else: else:
@ -633,7 +633,7 @@ def _get_link(uri, entry):
""" """
link = None link = None
category = '' category = ''
size = 0L size = 0
uri = uri.lower() uri = uri.lower()
age = datetime.datetime.now() age = datetime.datetime.now()
@ -648,7 +648,7 @@ def _get_link(uri, entry):
except: except:
pass pass
if size == 0L: if size == 0:
_RE_SIZE1 = re.compile(r'Size:\s*(\d+\.\d+\s*[KMG]{0,1})B\W*', re.I) _RE_SIZE1 = re.compile(r'Size:\s*(\d+\.\d+\s*[KMG]{0,1})B\W*', re.I)
_RE_SIZE2 = re.compile(r'\W*(\d+\.\d+\s*[KMG]{0,1})B\W*', re.I) _RE_SIZE2 = re.compile(r'\W*(\d+\.\d+\s*[KMG]{0,1})B\W*', re.I)
# Try to find size in Description # Try to find size in Description
@ -700,7 +700,7 @@ def _get_link(uri, entry):
return link, category, size, age, season, episode return link, category, size, age, season, episode
else: else:
logging.warning(T('Empty RSS entry found (%s)'), link) logging.warning(T('Empty RSS entry found (%s)'), link)
return None, '', 0L, None, 0, 0 return None, '', 0, None, 0, 0
def special_rss_site(url): def special_rss_site(url):

10
sabnzbd/scheduler.py

@ -92,7 +92,7 @@ def init():
if d.isdigit(): if d.isdigit():
d = [int(i) for i in d] d = [int(i) for i in d]
else: else:
d = range(1, 8) d = list(range(1, 8))
if action_name == 'resume': if action_name == 'resume':
action = scheduled_resume action = scheduled_resume
@ -196,16 +196,16 @@ def init():
action, hour, minute = sabnzbd.bpsmeter.BPSMeter.do.get_quota() action, hour, minute = sabnzbd.bpsmeter.BPSMeter.do.get_quota()
if action: if action:
logging.info('Setting schedule for quota check daily at %s:%s', hour, minute) logging.info('Setting schedule for quota check daily at %s:%s', hour, minute)
__SCHED.add_daytime_task(action, 'quota_reset', range(1, 8), None, (hour, minute), __SCHED.add_daytime_task(action, 'quota_reset', list(range(1, 8)), None, (hour, minute),
kronos.method.sequential, [], None) kronos.method.sequential, [], None)
if sabnzbd.misc.int_conv(cfg.history_retention()) > 0: if sabnzbd.misc.int_conv(cfg.history_retention()) > 0:
logging.info('Setting schedule for midnight auto history-purge') logging.info('Setting schedule for midnight auto history-purge')
__SCHED.add_daytime_task(sabnzbd.database.midnight_history_purge, 'midnight_history_purge', range(1, 8), None, (0, 0), __SCHED.add_daytime_task(sabnzbd.database.midnight_history_purge, 'midnight_history_purge', list(range(1, 8)), None, (0, 0),
kronos.method.sequential, [], None) kronos.method.sequential, [], None)
logging.info('Setting schedule for midnight BPS reset') logging.info('Setting schedule for midnight BPS reset')
__SCHED.add_daytime_task(sabnzbd.bpsmeter.midnight_action, 'midnight_bps', range(1, 8), None, (0, 0), __SCHED.add_daytime_task(sabnzbd.bpsmeter.midnight_action, 'midnight_bps', list(range(1, 8)), None, (0, 0),
kronos.method.sequential, [], None) kronos.method.sequential, [], None)
# Subscribe to special schedule changes # Subscribe to special schedule changes
@ -458,7 +458,7 @@ def pause_int():
val = abs(val) val = abs(val)
else: else:
sign = '' sign = ''
min = int(val / 60L) min = int(val / 60)
sec = int(val - min * 60) sec = int(val - min * 60)
return "%s%d:%02d" % (sign, min, sec) return "%s%d:%02d" % (sign, min, sec)

14
sabnzbd/tvsort.py

@ -360,7 +360,7 @@ class SeriesSorter(object):
# Replace elements # Replace elements
path = path_subst(sorter, mapping) path = path_subst(sorter, mapping)
for key, name in REPLACE_AFTER.iteritems(): for key, name in list(REPLACE_AFTER.items()):
path = path.replace(key, name) path = path.replace(key, name)
# Lowercase all characters wrapped in {} # Lowercase all characters wrapped in {}
@ -458,7 +458,7 @@ def check_for_sequence(regex, files):
prefix = name[:match1.start()] prefix = name[:match1.start()]
# Don't do anything if only one or no files matched # Don't do anything if only one or no files matched
if len(matches.keys()) < 2: if len(list(matches.keys())) < 2:
return {} return {}
key_prev = 0 key_prev = 0
@ -609,7 +609,7 @@ class GenericSorter(object):
path = path_subst(sorter, mapping) path = path_subst(sorter, mapping)
for key, name in REPLACE_AFTER.iteritems(): for key, name in list(REPLACE_AFTER.items()):
path = path.replace(key, name) path = path.replace(key, name)
# Lowercase all characters wrapped in {} # Lowercase all characters wrapped in {}
@ -675,8 +675,8 @@ class GenericSorter(object):
# rename files marked as in a set # rename files marked as in a set
if matched_files: if matched_files:
logging.debug("Renaming a series of generic files (%s)", matched_files) logging.debug("Renaming a series of generic files (%s)", matched_files)
renamed = matched_files.values() renamed = list(matched_files.values())
for index, file in matched_files.iteritems(): for index, file in list(matched_files.items()):
filepath = os.path.join(current_path, file) filepath = os.path.join(current_path, file)
renamed.append(filepath) renamed.append(filepath)
self.fname, ext = os.path.splitext(os.path.split(file)[1]) self.fname, ext = os.path.splitext(os.path.split(file)[1])
@ -828,7 +828,7 @@ class DateSorter(object):
path = path_subst(sorter, mapping) path = path_subst(sorter, mapping)
for key, name in REPLACE_AFTER.iteritems(): for key, name in list(REPLACE_AFTER.items()):
path = path.replace(key, name) path = path.replace(key, name)
# Lowercase all characters wrapped in {} # Lowercase all characters wrapped in {}
@ -896,7 +896,7 @@ def path_subst(path, mapping):
break break
newpath.append(result) newpath.append(result)
n += 1 n += 1
return u''.join([unicoder(x) for x in newpath]) return ''.join([unicoder(x) for x in newpath])
def get_titles(nzo, match, name, titleing=False): def get_titles(nzo, match, name, titleing=False):

20
sabnzbd/urlgrabber.py

@ -24,9 +24,9 @@ import sys
import time import time
import re import re
import logging import logging
import Queue import queue
import urllib2 import urllib.request, urllib.error, urllib.parse
from httplib import IncompleteRead from http.client import IncompleteRead
from threading import Thread from threading import Thread
import sabnzbd import sabnzbd
@ -50,7 +50,7 @@ class URLGrabber(Thread):
def __init__(self): def __init__(self):
Thread.__init__(self) Thread.__init__(self)
self.queue = Queue.Queue() self.queue = queue.Queue()
for tup in NzbQueue.do.get_urls(): for tup in NzbQueue.do.get_urls():
url, nzo = tup url, nzo = tup
self.queue.put((url, nzo)) self.queue.put((url, nzo))
@ -115,7 +115,7 @@ class URLGrabber(Thread):
logging.info('Grabbing URL %s', url) logging.info('Grabbing URL %s', url)
try: try:
fn = _build_request(url) fn = _build_request(url)
except Exception, e: except Exception as e:
# Cannot list exceptions here, because of unpredictability over platforms # Cannot list exceptions here, because of unpredictability over platforms
error0 = str(sys.exc_info()[0]).lower() error0 = str(sys.exc_info()[0]).lower()
error1 = str(sys.exc_info()[1]).lower() error1 = str(sys.exc_info()[1]).lower()
@ -281,15 +281,15 @@ class URLGrabber(Thread):
def _build_request(url): def _build_request(url):
# Detect basic auth # Detect basic auth
# Adapted from python-feedparser # Adapted from python-feedparser
urltype, rest = urllib2.splittype(url) urltype, rest = urllib.parse.splittype(url)
realhost, rest = urllib2.splithost(rest) realhost, rest = urllib.parse.splithost(rest)
if realhost: if realhost:
user_passwd, realhost = urllib2.splituser(realhost) user_passwd, realhost = urllib.parse.splituser(realhost)
if user_passwd: if user_passwd:
url = '%s://%s%s' % (urltype, realhost, rest) url = '%s://%s%s' % (urltype, realhost, rest)
# Start request # Start request
req = urllib2.Request(url) req = urllib.request.Request(url)
# Add headers # Add headers
req.add_header('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__) req.add_header('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__)
@ -297,7 +297,7 @@ def _build_request(url):
req.add_header('Accept-encoding', 'gzip') req.add_header('Accept-encoding', 'gzip')
if user_passwd: if user_passwd:
req.add_header('Authorization', 'Basic ' + user_passwd.encode('base64').strip()) req.add_header('Authorization', 'Basic ' + user_passwd.encode('base64').strip())
return urllib2.urlopen(req) return urllib.request.urlopen(req)
def _analyse(fn, url): def _analyse(fn, url):

12
sabnzbd/utils/certgen.py

@ -51,7 +51,7 @@ def generate_key(key_size=2048, output_file='key.pem'):
# Ported from cryptography docs/x509/tutorial.rst # Ported from cryptography docs/x509/tutorial.rst
def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost'): def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', LN='SABnzbd', ON='SABnzbd', CN='localhost'):
# Various details about who we are. For a self-signed certificate the # Various details about who we are. For a self-signed certificate the
# subject and issuer are always the same. # subject and issuer are always the same.
subject = issuer = x509.Name([ subject = issuer = x509.Name([
@ -62,13 +62,13 @@ def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', L
# build SubjectAltName list since we are not using a common name # build SubjectAltName list since we are not using a common name
san_list = [ san_list = [
x509.DNSName(u"localhost"), x509.DNSName("localhost"),
x509.DNSName(u"127.0.0.1"), x509.DNSName("127.0.0.1"),
] ]
# append local v4 ip (functions already has try/catch logic) # append local v4 ip (functions already has try/catch logic)
mylocalipv4 = localipv4() mylocalipv4 = localipv4()
if mylocalipv4: if mylocalipv4:
san_list.append(x509.DNSName(u"" + mylocalipv4)) san_list.append(x509.DNSName("" + mylocalipv4))
cert = x509.CertificateBuilder().subject_name( cert = x509.CertificateBuilder().subject_name(
subject subject
@ -94,7 +94,7 @@ def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', L
return cert return cert
if __name__ == '__main__': if __name__ == '__main__':
print 'Making key' print('Making key')
private_key = generate_key() private_key = generate_key()
print 'Making cert' print('Making cert')
cert = generate_local_cert(private_key) cert = generate_local_cert(private_key)

20
sabnzbd/utils/checkdir.py

@ -37,19 +37,19 @@ def isFAT(dir):
if thisline.find('/') == 0: if thisline.find('/') == 0:
# Starts with /, so a real, local device # Starts with /, so a real, local device
fstype = thisline.split()[1] fstype = thisline.split()[1]
if debug: print "File system type:", fstype if debug: print(("File system type:", fstype))
if fstype.lower().find('fat') >= 0: if fstype.lower().find('fat') >= 0:
FAT = True FAT = True
if debug: print "FAT found" if debug: print("FAT found")
break break
elif 'win32' in sys.platform: elif 'win32' in sys.platform:
import win32api import win32api
if '?' in dir: if '?' in dir:
# Remove \\?\ or \\?\UNC\ prefix from Windows path # Remove \\?\ or \\?\UNC\ prefix from Windows path
dir = dir.replace(u'\\\\?\\UNC\\', u'\\\\', 1).replace(u'\\\\?\\', u'', 1) dir = dir.replace('\\\\?\\UNC\\', '\\\\', 1).replace('\\\\?\\', '', 1)
try: try:
result = win32api.GetVolumeInformation(os.path.splitdrive(dir)[0]) result = win32api.GetVolumeInformation(os.path.splitdrive(dir)[0])
if debug: print result if debug: print(result)
if(result[4].startswith("FAT")): if(result[4].startswith("FAT")):
FAT = True FAT = True
except: except:
@ -74,12 +74,12 @@ def isFAT(dir):
device = '' device = ''
for thisline in os.popen(dfcmd).readlines(): for thisline in os.popen(dfcmd).readlines():
if thisline.find('/')==0: if thisline.find('/')==0:
if debug: print thisline if debug: print(thisline)
# Starts with /, so a real, local device # Starts with /, so a real, local device
device = thisline.split()[0] device = thisline.split()[0]
mountcmd = "mount | grep " + device mountcmd = "mount | grep " + device
mountoutput = os.popen(mountcmd).readline().strip() mountoutput = os.popen(mountcmd).readline().strip()
if debug: print mountoutput if debug: print(mountoutput)
if 'msdos' in mountoutput.split('(')[1]: if 'msdos' in mountoutput.split('(')[1]:
FAT = True FAT = True
break break
@ -91,15 +91,15 @@ def isFAT(dir):
if __name__ == "__main__": if __name__ == "__main__":
if debug: print sys.platform if debug: print((sys.platform))
try: try:
dir = sys.argv[1] dir = sys.argv[1]
except: except:
print "Specify dir on the command line" print("Specify dir on the command line")
sys.exit(0) sys.exit(0)
if isFAT(dir): if isFAT(dir):
print dir, "is on FAT" print((dir, "is on FAT"))
else: else:
print dir, "is not on FAT" print((dir, "is not on FAT"))

18
sabnzbd/utils/diskspeed.py

@ -18,7 +18,7 @@ def writetofile(filename, mysizeMB):
return False return False
try: try:
for x in xrange(writeloops): for x in range(writeloops):
f.write(_DUMP_DATA) f.write(_DUMP_DATA)
except: except:
logging.debug('Cannot write to file %s', filename) logging.debug('Cannot write to file %s', filename)
@ -61,27 +61,27 @@ def diskspeedmeasure(dirname):
if __name__ == "__main__": if __name__ == "__main__":
print "Let's go" print("Let's go")
if len(sys.argv) >= 2: if len(sys.argv) >= 2:
dirname = sys.argv[1] dirname = sys.argv[1]
if not os.path.isdir(dirname): if not os.path.isdir(dirname):
print "Specified argument is not a directory. Bailing out" print("Specified argument is not a directory. Bailing out")
sys.exit(1) sys.exit(1)
else: else:
# no argument, so use current working directory # no argument, so use current working directory
dirname = os.getcwd() dirname = os.getcwd()
print "Using current working directory" print("Using current working directory")
try: try:
speed = diskspeedmeasure(dirname) speed = diskspeedmeasure(dirname)
print "Disk writing speed: %.2f Mbytes per second" % speed print(("Disk writing speed: %.2f Mbytes per second" % speed))
except IOError, e: except IOError as e:
# print "IOError:", e # print "IOError:", e
if e.errno == 13: if e.errno == 13:
print "Could not create test file. Check that you have write rights to directory", dirname print(("Could not create test file. Check that you have write rights to directory", dirname))
except: except:
print "Something else went wrong" print("Something else went wrong")
raise raise
print "Done" print("Done")

8
sabnzbd/utils/getperformance.py

@ -9,7 +9,7 @@ def getcpu():
try: try:
if platform.system() == "Windows": if platform.system() == "Windows":
import _winreg as winreg # needed on Python 2 import winreg as winreg # needed on Python 2
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0")
cputype = winreg.QueryValueEx(key, "ProcessorNameString")[0] cputype = winreg.QueryValueEx(key, "ProcessorNameString")[0]
winreg.CloseKey(key) winreg.CloseKey(key)
@ -42,7 +42,7 @@ def getpystone():
value = None value = None
for pystonemodule in ['test.pystone', 'pystone']: for pystonemodule in ['test.pystone', 'pystone']:
try: try:
exec "from " + pystonemodule + " import pystones" exec("from " + pystonemodule + " import pystones")
value = int(pystones(1000)[1]) value = int(pystones(1000)[1])
break # import and calculation worked, so we're done. Get out of the for loop break # import and calculation worked, so we're done. Get out of the for loop
except: except:
@ -51,5 +51,5 @@ def getpystone():
if __name__ == '__main__': if __name__ == '__main__':
print getpystone() print((getpystone()))
print getcpu() print((getcpu()))

32
sabnzbd/utils/happyeyeballs.py

@ -21,7 +21,7 @@ print happyeyeballs('newszilla.xs4all.nl', port=119)
import socket import socket
import ssl import ssl
import Queue import queue
import threading import threading
import time import time
import logging import logging
@ -115,7 +115,7 @@ def happyeyeballs(HOST, **kwargs):
except: except:
if DEBUG: logging.debug("No IPv6 address found for %s", HOST) if DEBUG: logging.debug("No IPv6 address found for %s", HOST)
myqueue = Queue.Queue() # queue used for threads giving back the results myqueue = queue.Queue() # queue used for threads giving back the results
try: try:
# Get all IP (IPv4 and IPv6) addresses: # Get all IP (IPv4 and IPv6) addresses:
@ -157,22 +157,22 @@ if __name__ == '__main__':
if DEBUG: logger.setLevel(logging.DEBUG) if DEBUG: logger.setLevel(logging.DEBUG)
# plain HTTP/HTTPS sites: # plain HTTP/HTTPS sites:
print happyeyeballs('www.google.com') print((happyeyeballs('www.google.com')))
print happyeyeballs('www.google.com', port=443, ssl=True) print((happyeyeballs('www.google.com', port=443, ssl=True)))
print happyeyeballs('www.nu.nl') print((happyeyeballs('www.nu.nl')))
# newsservers: # newsservers:
print happyeyeballs('newszilla6.xs4all.nl', port=119) print((happyeyeballs('newszilla6.xs4all.nl', port=119)))
print happyeyeballs('newszilla.xs4all.nl', port=119) print((happyeyeballs('newszilla.xs4all.nl', port=119)))
print happyeyeballs('block.cheapnews.eu', port=119) print((happyeyeballs('block.cheapnews.eu', port=119)))
print happyeyeballs('block.cheapnews.eu', port=443, ssl=True) print((happyeyeballs('block.cheapnews.eu', port=443, ssl=True)))
print happyeyeballs('sslreader.eweka.nl', port=563, ssl=True) print((happyeyeballs('sslreader.eweka.nl', port=563, ssl=True)))
print happyeyeballs('news.thundernews.com', port=119) print((happyeyeballs('news.thundernews.com', port=119)))
print happyeyeballs('news.thundernews.com', port=119, preferipv6=False) print((happyeyeballs('news.thundernews.com', port=119, preferipv6=False)))
print happyeyeballs('secure.eu.thundernews.com', port=563, ssl=True) print((happyeyeballs('secure.eu.thundernews.com', port=563, ssl=True)))
# Strange cases # Strange cases
print happyeyeballs('does.not.resolve', port=443, ssl=True) print((happyeyeballs('does.not.resolve', port=443, ssl=True)))
print happyeyeballs('www.google.com', port=119) print((happyeyeballs('www.google.com', port=119)))
print happyeyeballs('216.58.211.164') print((happyeyeballs('216.58.211.164')))

16
sabnzbd/utils/json.py

@ -45,10 +45,10 @@ class JsonWriter(object):
def _write(self, obj): def _write(self, obj):
global LATIN_COUNTER global LATIN_COUNTER
ty = type(obj) ty = type(obj)
if ty is types.DictType: if ty is dict:
n = len(obj) n = len(obj)
self._append("{") self._append("{")
for k, v in obj.items(): for k, v in list(obj.items()):
self._write(k) self._write(k)
self._append(":") self._append(":")
self._write(v) self._write(v)
@ -56,7 +56,7 @@ class JsonWriter(object):
if n > 0: if n > 0:
self._append(",") self._append(",")
self._append("}") self._append("}")
elif ty is types.ListType or ty is types.TupleType: elif ty is list or ty is tuple:
n = len(obj) n = len(obj)
self._append("[") self._append("[")
for item in obj: for item in obj:
@ -65,9 +65,9 @@ class JsonWriter(object):
if n > 0: if n > 0:
self._append(",") self._append(",")
self._append("]") self._append("]")
elif ty is types.StringType or ty is types.UnicodeType: elif ty is bytes or ty is str:
self._append('"') self._append('"')
if ty is types.UnicodeType: if ty is str:
obj = obj.encode('utf-8', 'replace') obj = obj.encode('utf-8', 'replace')
else: else:
try: try:
@ -88,9 +88,9 @@ class JsonWriter(object):
obj = obj.replace('\t', r'\t') obj = obj.replace('\t', r'\t')
self._append(obj) self._append(obj)
self._append('"') self._append('"')
elif ty is types.IntType or ty is types.LongType: elif ty is int or ty is int:
self._append(str(obj)) self._append(str(obj))
elif ty is types.FloatType: elif ty is float:
self._append("%f" % obj) self._append("%f" % obj)
elif obj is True: elif obj is True:
self._append("true") self._append("true")
@ -99,4 +99,4 @@ class JsonWriter(object):
elif obj is None: elif obj is None:
self._append("null") self._append("null")
else: else:
raise WriteException, "Cannot write in JSON: %s" % repr(obj) raise WriteException("Cannot write in JSON: %s" % repr(obj))

18
sabnzbd/utils/kronos.py

@ -282,7 +282,7 @@ class Scheduler:
while self.running: while self.running:
try: try:
self.sched.run() self.sched.run()
except Exception,x: except Exception as x:
logging.error("ERROR DURING SCHEDULER EXECUTION %s" % str(x), exc_info=True) logging.error("ERROR DURING SCHEDULER EXECUTION %s" % str(x), exc_info=True)
# queue is empty; sleep a short while before checking again # queue is empty; sleep a short while before checking again
if self.running: if self.running:
@ -303,7 +303,7 @@ class Task:
"""Execute the task action in the scheduler's thread.""" """Execute the task action in the scheduler's thread."""
try: try:
self.execute() self.execute()
except Exception,x: except Exception as x:
self.handle_exception(x) self.handle_exception(x)
self.reschedule(schedulerref()) self.reschedule(schedulerref())
@ -469,7 +469,7 @@ try:
# do the execute() call and exception handling here. # do the execute() call and exception handling here.
try: try:
self.execute() self.execute()
except Exception,x: except Exception as x:
self.handle_exception(x) self.handle_exception(x)
class ThreadedIntervalTask(ThreadedTaskMixin, IntervalTask): class ThreadedIntervalTask(ThreadedTaskMixin, IntervalTask):
@ -536,7 +536,7 @@ if hasattr(os, "fork"):
# we are the child # we are the child
try: try:
self.execute() self.execute()
except Exception,x: except Exception as x:
self.handle_exception(x) self.handle_exception(x)
os._exit(0) os._exit(0)
else: else:
@ -564,18 +564,18 @@ if hasattr(os, "fork"):
if __name__=="__main__": if __name__=="__main__":
def testaction(arg): def testaction(arg):
print ">>>TASK",arg,"sleeping 3 seconds" print((">>>TASK",arg,"sleeping 3 seconds"))
time.sleep(3) time.sleep(3)
print "<<<END_TASK",arg print(("<<<END_TASK",arg))
s=ThreadedScheduler() s=ThreadedScheduler()
s.add_interval_task( testaction, "test action 1", 0, 4, method.threaded, ["task 1"], None ) s.add_interval_task( testaction, "test action 1", 0, 4, method.threaded, ["task 1"], None )
s.start() s.start()
print "Scheduler started, waiting 15 sec...." print("Scheduler started, waiting 15 sec....")
time.sleep(15) time.sleep(15)
print "STOP SCHEDULER" print("STOP SCHEDULER")
s.stop() s.stop()
print "EXITING" print("EXITING")

4
sabnzbd/utils/pathbrowser.py

@ -98,8 +98,8 @@ def folders_at_path(path, include_parent = False, show_hidden = False):
doit = False doit = False
if doit: if doit:
file_list.append({ 'name': unicoder(filename), 'path': unicoder(fpath) }) file_list.append({ 'name': unicoder(filename), 'path': unicoder(fpath) })
file_list = filter(lambda entry: os.path.isdir(entry['path']), file_list) file_list = [entry for entry in file_list if os.path.isdir(entry['path'])]
file_list = filter(lambda entry: entry['name'].lower() not in _JUNKFOLDERS, file_list) file_list = [entry for entry in file_list if entry['name'].lower() not in _JUNKFOLDERS]
file_list = sorted(file_list, lambda x, y: cmp(os.path.basename(x['name']).lower(), os.path.basename(y['path']).lower())) file_list = sorted(file_list, lambda x, y: cmp(os.path.basename(x['name']).lower(), os.path.basename(y['path']).lower()))
except: except:
# No access, ignore # No access, ignore

16
sabnzbd/utils/pybonjour.py

@ -305,10 +305,10 @@ class _utf8_char_p(ctypes.c_char_p):
@classmethod @classmethod
def from_param(cls, obj): def from_param(cls, obj):
if (obj is not None) and (not isinstance(obj, cls)): if (obj is not None) and (not isinstance(obj, cls)):
if not isinstance(obj, basestring): if not isinstance(obj, str):
raise TypeError('parameter must be a string type instance') raise TypeError('parameter must be a string type instance')
if not isinstance(obj, unicode): if not isinstance(obj, str):
obj = unicode(obj) obj = str(obj)
obj = obj.encode('utf-8') obj = obj.encode('utf-8')
return ctypes.c_char_p.from_param(obj) return ctypes.c_char_p.from_param(obj)
@ -803,7 +803,7 @@ def _create_function_bindings():
} }
for name, (restype, errcheck, outparam, argtypes) in specs.iteritems(): for name, (restype, errcheck, outparam, argtypes) in list(specs.items()):
prototype = _CFunc(restype, *argtypes) prototype = _CFunc(restype, *argtypes)
paramflags = [1] * len(argtypes) paramflags = [1] * len(argtypes)
@ -854,7 +854,7 @@ def _string_to_length_and_void_p(string):
def _length_and_void_p_to_string(length, void_p): def _length_and_void_p_to_string(length, void_p):
char_p = ctypes.cast(void_p, ctypes.POINTER(ctypes.c_char)) char_p = ctypes.cast(void_p, ctypes.POINTER(ctypes.c_char))
return ''.join(char_p[i] for i in xrange(length)) return ''.join(char_p[i] for i in range(length))
@ -1941,7 +1941,7 @@ class TXTRecord(object):
self._names = [] self._names = []
self._items = {} self._items = {}
for name, value in items.iteritems(): for name, value in list(items.items()):
self[name] = value self[name] = value
def __contains__(self, name): def __contains__(self, name):
@ -1957,7 +1957,7 @@ class TXTRecord(object):
'Return the number of name/value pairs' 'Return the number of name/value pairs'
return len(self._names) return len(self._names)
def __nonzero__(self): def __bool__(self):
'Return False if the record is empty, True otherwise' 'Return False if the record is empty, True otherwise'
return bool(self._items) return bool(self._items)
@ -2017,7 +2017,7 @@ class TXTRecord(object):
length = len(name) length = len(name)
if value is not None: if value is not None:
if isinstance(value, unicode): if isinstance(value, str):
value = value.encode('utf-8') value = value.encode('utf-8')
else: else:
value = str(value) value = str(value)

16
sabnzbd/utils/pystone.py

@ -1,4 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python3
""" """
"PYSTONE" Benchmark Program "PYSTONE" Benchmark Program
@ -59,9 +59,9 @@ FALSE = 0
def main(loops=LOOPS): def main(loops=LOOPS):
benchtime, stones = pystones(loops) benchtime, stones = pystones(loops)
print "Pystone(%s) time for %d passes = %g" % \ print("Pystone(%s) time for %d passes = %g" % \
(__version__, loops, benchtime) (__version__, loops, benchtime))
print "This machine benchmarks at %g pystones/second" % stones print("This machine benchmarks at %g pystones/second" % stones)
def pystones(loops=LOOPS): def pystones(loops=LOOPS):
@ -72,7 +72,7 @@ BoolGlob = FALSE
Char1Glob = '\0' Char1Glob = '\0'
Char2Glob = '\0' Char2Glob = '\0'
Array1Glob = [0]*51 Array1Glob = [0]*51
Array2Glob = map(lambda x: x[:], [Array1Glob]*51) Array2Glob = [x[:] for x in [Array1Glob]*51]
PtrGlb = None PtrGlb = None
PtrGlbNext = None PtrGlbNext = None
@ -255,8 +255,8 @@ def Func3(EnumParIn):
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
def error(msg): def error(msg):
print >>sys.stderr, msg, print(msg, end=' ', file=sys.stderr)
print >>sys.stderr, "usage: %s [number_of_loops]" % sys.argv[0] print("usage: %s [number_of_loops]" % sys.argv[0], file=sys.stderr)
sys.exit(100) sys.exit(100)
nargs = len(sys.argv) - 1 nargs = len(sys.argv) - 1
if nargs > 1: if nargs > 1:
@ -267,4 +267,4 @@ if __name__ == '__main__':
error("Invalid argument %r;" % sys.argv[1]) error("Invalid argument %r;" % sys.argv[1])
else: else:
loops = LOOPS loops = LOOPS
main(loops) main(loops)

14
sabnzbd/utils/rarfile.py

@ -61,7 +61,7 @@ For more details, refer to source.
""" """
from __future__ import division, print_function
## ##
## Imports and compat - support both Python 2.x and 3.x ## Imports and compat - support both Python 2.x and 3.x
@ -163,7 +163,7 @@ else: # pragma: no cover
"""Return hex string.""" """Return hex string."""
return hexlify(data).decode('ascii') return hexlify(data).decode('ascii')
rar_crc32 = crc32 rar_crc32 = crc32
unicode = str str = str
_byte_code = int # noqa _byte_code = int # noqa
@ -778,7 +778,7 @@ class RarFile(object):
def printdir(self): def printdir(self):
"""Print archive file list to stdout.""" """Print archive file list to stdout."""
for f in self.infolist(): for f in self.infolist():
print(f.filename) print((f.filename))
def extract(self, member, path=None, pwd=None): def extract(self, member, path=None, pwd=None):
"""Extract single file into current directory. """Extract single file into current directory.
@ -986,7 +986,7 @@ class CommonParser(object):
self._fd = fd self._fd = fd
sig = fd.read(len(self._expect_sig)) sig = fd.read(len(self._expect_sig))
if sig != self._expect_sig: if sig != self._expect_sig:
if isinstance(self._rarfile, (str, unicode)): if isinstance(self._rarfile, str):
raise NotRarFile("Not a Rar archive: {}".format(self._rarfile)) raise NotRarFile("Not a Rar archive: {}".format(self._rarfile))
raise NotRarFile("Not a Rar archive") raise NotRarFile("Not a Rar archive")
@ -1596,7 +1596,7 @@ class RAR5Parser(CommonParser):
if kdf_count > 24: if kdf_count > 24:
raise BadRarFile('Too large kdf_count') raise BadRarFile('Too large kdf_count')
psw = self._password psw = self._password
if isinstance(psw, unicode): if isinstance(psw, str):
psw = psw.encode('utf8') psw = psw.encode('utf8')
key = pbkdf2_sha256(psw, salt, 1 << kdf_count) key = pbkdf2_sha256(psw, salt, 1 << kdf_count)
self._last_aes256_key = (kdf_count, salt, key) self._last_aes256_key = (kdf_count, salt, key)
@ -2684,7 +2684,7 @@ def _parse_xtime(flag, data, pos, basetime=None):
def is_filelike(obj): def is_filelike(obj):
"""Filename or file object? """Filename or file object?
""" """
if isinstance(obj, str) or isinstance(obj, unicode): if isinstance(obj, str) or isinstance(obj, str):
return False return False
res = True res = True
for a in ('read', 'tell', 'seek'): for a in ('read', 'tell', 'seek'):
@ -2696,7 +2696,7 @@ def is_filelike(obj):
def rar3_s2k(psw, salt): def rar3_s2k(psw, salt):
"""String-to-key hash for RAR3. """String-to-key hash for RAR3.
""" """
if not isinstance(psw, unicode): if not isinstance(psw, str):
psw = psw.decode('utf8') psw = psw.decode('utf8')
seed = psw.encode('utf-16le') + salt seed = psw.encode('utf-16le') + salt
iv = EMPTY iv = EMPTY

2
sabnzbd/utils/rsslib.py

@ -208,7 +208,7 @@ class RSS:
c+= "length=\"" + str(i.enclosure.length )+ "\" " c+= "length=\"" + str(i.enclosure.length )+ "\" "
c+= "type=\"" + i.enclosure.type + "\"/>\n" c+= "type=\"" + i.enclosure.type + "\"/>\n"
for k in i.nsItems.keys(): for k in list(i.nsItems.keys()):
c += self.optionalWrite( k , i.nsItems[ k ] ) c += self.optionalWrite( k , i.nsItems[ k ] )
c += "</item>\n\n" c += "</item>\n\n"

12
sabnzbd/utils/servertests.py

@ -90,20 +90,20 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
nw.recv_chunk(block=True) nw.recv_chunk(block=True)
nw.finish_connect(nw.status_code) nw.finish_connect(nw.status_code)
except socket.timeout, e: except socket.timeout as e:
if port != 119 and not ssl: if port != 119 and not ssl:
return False, T('Timed out: Try enabling SSL or connecting on a different port.') return False, T('Timed out: Try enabling SSL or connecting on a different port.')
else: else:
return False, T('Timed out') return False, T('Timed out')
except socket.error, e: except socket.error as e:
# Trying SSL on non-SSL port? # Trying SSL on non-SSL port?
if 'unknown protocol' in str(e).lower(): if 'unknown protocol' in str(e).lower():
return False, T('Unknown SSL protocol: Try disabling SSL or connecting on a different port.') return False, T('Unknown SSL protocol: Try disabling SSL or connecting on a different port.')
return False, unicode(e) return False, str(e)
except TypeError, e: except TypeError as e:
return False, T('Invalid server address.') return False, T('Invalid server address.')
except IndexError: except IndexError:
@ -111,7 +111,7 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
return False, T('Server quit during login sequence.') return False, T('Server quit during login sequence.')
except: except:
return False, unicode(sys.exc_info()[1]) return False, str(sys.exc_info()[1])
if not username or not password: if not username or not password:
nw.nntp.sock.sendall('ARTICLE <test@home>\r\n') nw.nntp.sock.sendall('ARTICLE <test@home>\r\n')
@ -119,7 +119,7 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
nw.clear_data() nw.clear_data()
nw.recv_chunk(block=True) nw.recv_chunk(block=True)
except: except:
return False, unicode(sys.exc_info()[1]) return False, str(sys.exc_info()[1])
if nw.status_code == '480': if nw.status_code == '480':
return False, T('Server requires username and password.') return False, T('Server requires username and password.')

6
sabnzbd/utils/systrayiconthread.py

@ -123,7 +123,7 @@ class SysTrayIconThread(Thread):
self._add_ids_to_menu_options(option_action), self._add_ids_to_menu_options(option_action),
self._next_action_id)) self._next_action_id))
else: else:
print 'Unknown item', option_text, option_icon, option_action print(('Unknown item', option_text, option_icon, option_action))
self._next_action_id += 1 self._next_action_id += 1
return result return result
@ -143,7 +143,7 @@ class SysTrayIconThread(Thread):
0, 0,
icon_flags) icon_flags)
else: else:
print "Can't find icon file - using default." print("Can't find icon file - using default.")
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION) hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
self.icons[path] = hicon self.icons[path] = hicon
@ -284,4 +284,4 @@ def non_string_iterable(obj):
except TypeError: except TypeError:
return False return False
else: else:
return not isinstance(obj, basestring) return not isinstance(obj, str)

4
sabnzbd/utils/upload.py

@ -19,7 +19,7 @@
sabnzbd.utils.upload - File association functions for adding nzb files to sabnzbd sabnzbd.utils.upload - File association functions for adding nzb files to sabnzbd
""" """
import urllib import urllib.request, urllib.parse, urllib.error
import logging import logging
import os import os
from sabnzbd.encoding import unicoder from sabnzbd.encoding import unicoder
@ -35,7 +35,7 @@ def upload_file(url, fp):
""" Function for uploading nzbs to a running sabnzbd instance """ """ Function for uploading nzbs to a running sabnzbd instance """
try: try:
fp = unicoder(fp).encode('utf-8') fp = unicoder(fp).encode('utf-8')
fp = urllib.quote_plus(fp) fp = urllib.parse.quote_plus(fp)
url = '%s&mode=addlocalfile&name=%s' % (url, fp) url = '%s&mode=addlocalfile&name=%s' % (url, fp)
# Add local apikey if it wasn't already in the registered URL # Add local apikey if it wasn't already in the registered URL
apikey = cfg.api_key() apikey = cfg.api_key()

36
scripts/Deobfuscate.py

@ -53,12 +53,12 @@ MIN_FILE_SIZE = 40*1024*1024
# Are we being called from SABnzbd? # Are we being called from SABnzbd?
if not os.environ.get('SAB_VERSION'): if not os.environ.get('SAB_VERSION'):
print "This script needs to be called from SABnzbd as post-processing script." print("This script needs to be called from SABnzbd as post-processing script.")
sys.exit(1) sys.exit(1)
def print_splitter(): def print_splitter():
""" Simple helper function """ """ Simple helper function """
print '\n------------------------\n' print('\n------------------------\n')
# Windows or others? # Windows or others?
par2_command = os.environ['SAB_PAR2_COMMAND'] par2_command = os.environ['SAB_PAR2_COMMAND']
@ -67,9 +67,9 @@ if os.environ['SAB_MULTIPAR_COMMAND']:
# Diagnostic info # Diagnostic info
print_splitter() print_splitter()
print 'SABnzbd version: ', os.environ['SAB_VERSION'] print(('SABnzbd version: ', os.environ['SAB_VERSION']))
print 'Job location: ', os.environ['SAB_COMPLETE_DIR'] print(('Job location: ', os.environ['SAB_COMPLETE_DIR']))
print 'Par2-command: ', par2_command print(('Par2-command: ', par2_command))
print_splitter() print_splitter()
# Search for par2 files # Search for par2 files
@ -77,12 +77,12 @@ matches = []
for root, dirnames, filenames in os.walk(os.environ['SAB_COMPLETE_DIR']): for root, dirnames, filenames in os.walk(os.environ['SAB_COMPLETE_DIR']):
for filename in fnmatch.filter(filenames, '*.par2'): for filename in fnmatch.filter(filenames, '*.par2'):
matches.append(os.path.join(root, filename)) matches.append(os.path.join(root, filename))
print 'Found file:', os.path.join(root, filename) print(('Found file:', os.path.join(root, filename)))
# Found any par2 files we can use? # Found any par2 files we can use?
run_renamer = True run_renamer = True
if not matches: if not matches:
print "No par2 files found to process." print("No par2 files found to process.")
# Run par2 from SABnzbd on them # Run par2 from SABnzbd on them
for par2_file in matches: for par2_file in matches:
@ -92,7 +92,7 @@ for par2_file in matches:
# Start command # Start command
print_splitter() print_splitter()
print 'Starting command: ', repr(command) print(('Starting command: ', repr(command)))
try: try:
result = subprocess.check_output(command) result = subprocess.check_output(command)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
@ -101,23 +101,23 @@ for par2_file in matches:
# Show output # Show output
print_splitter() print_splitter()
print result print(result)
print_splitter() print_splitter()
# Last status-line for the History # Last status-line for the History
# Check if the magic words are there # Check if the magic words are there
if 'Repaired successfully' in result or 'All files are correct' in result or \ if 'Repaired successfully' in result or 'All files are correct' in result or \
'Repair complete' in result or 'All Files Complete' in result or 'PAR File(s) Incomplete' in result: 'Repair complete' in result or 'All Files Complete' in result or 'PAR File(s) Incomplete' in result:
print 'Recursive repair/verify finished.' print('Recursive repair/verify finished.')
run_renamer = False run_renamer = False
else: else:
print 'Recursive repair/verify did not complete!' print('Recursive repair/verify did not complete!')
# No matches? Then we try to rename the largest file to the job-name # No matches? Then we try to rename the largest file to the job-name
if run_renamer: if run_renamer:
print_splitter() print_splitter()
print 'Trying to see if there are large files to rename' print('Trying to see if there are large files to rename')
print_splitter() print_splitter()
# If there are more larger files, we don't rename # If there are more larger files, we don't rename
@ -130,10 +130,10 @@ if run_renamer:
if file_size > MIN_FILE_SIZE and os.path.splitext(filename)[1].lower() not in EXCLUDED_FILE_EXTS: if file_size > MIN_FILE_SIZE and os.path.splitext(filename)[1].lower() not in EXCLUDED_FILE_EXTS:
# Did we already found one? # Did we already found one?
if largest_file: if largest_file:
print 'Found:', largest_file print(('Found:', largest_file))
print 'Found:', full_path print(('Found:', full_path))
print_splitter() print_splitter()
print 'Found multiple larger files, aborting.' print('Found multiple larger files, aborting.')
largest_file = None largest_file = None
break break
largest_file = full_path largest_file = full_path
@ -143,18 +143,18 @@ if run_renamer:
# We don't need to do any cleaning of dir-names # We don't need to do any cleaning of dir-names
# since SABnzbd already did that! # since SABnzbd already did that!
new_name = '%s%s' % (os.path.join(os.environ['SAB_COMPLETE_DIR'], os.environ['SAB_FINAL_NAME']), os.path.splitext(largest_file)[1].lower()) new_name = '%s%s' % (os.path.join(os.environ['SAB_COMPLETE_DIR'], os.environ['SAB_FINAL_NAME']), os.path.splitext(largest_file)[1].lower())
print 'Renaming %s to %s' % (largest_file, new_name) print(('Renaming %s to %s' % (largest_file, new_name)))
# With retries for Windows # With retries for Windows
for r in range(3): for r in range(3):
try: try:
os.rename(largest_file, new_name) os.rename(largest_file, new_name)
print 'Renaming done!' print('Renaming done!')
break break
except: except:
time.sleep(1) time.sleep(1)
else: else:
print 'No par2 files or large files found' print('No par2 files or large files found')
# Always exit with succes-code # Always exit with succes-code
sys.exit(0) sys.exit(0)

2
tests/conftest.py

@ -21,7 +21,7 @@ tests.conftest - Wrappers to start SABnzbd for testing
import os import os
import itertools import itertools
import urllib2 import urllib.request, urllib.error, urllib.parse
import pytest import pytest
import shutil import shutil
import time import time

2
tests/testhelper.py

@ -19,7 +19,7 @@
tests.testhelper - Basic helper functions tests.testhelper - Basic helper functions
""" """
import urllib2 import urllib.request, urllib.error, urllib.parse
import json import json
import requests import requests

8
tools/extract_pot.py

@ -141,11 +141,11 @@ else:
cmd = '%s %s %s' % (TOOL, PARMS, FILES) cmd = '%s %s %s' % (TOOL, PARMS, FILES)
print 'Create POT file' print('Create POT file')
#print cmd #print cmd
os.system(cmd) os.system(cmd)
print 'Post-process the POT file' print('Post-process the POT file')
src = open('%s/%s.pot.tmp' % (PO_DIR, DOMAIN), 'r') src = open('%s/%s.pot.tmp' % (PO_DIR, DOMAIN), 'r')
dst = open('%s/%s.pot' % (PO_DIR, DOMAIN), 'wb') dst = open('%s/%s.pot' % (PO_DIR, DOMAIN), 'wb')
dst.write(HEADER.replace('__TYPE__', 'MAIN')) dst.write(HEADER.replace('__TYPE__', 'MAIN'))
@ -169,7 +169,7 @@ dst.close()
os.remove('%s/%s.pot.tmp' % (PO_DIR, DOMAIN)) os.remove('%s/%s.pot.tmp' % (PO_DIR, DOMAIN))
print 'Create the email POT file' print('Create the email POT file')
if not os.path.exists(POE_DIR): if not os.path.exists(POE_DIR):
os.makedirs(POE_DIR) os.makedirs(POE_DIR)
dst = open(os.path.join(POE_DIR, DOMAIN_EMAIL + '.pot'), 'wb') dst = open(os.path.join(POE_DIR, DOMAIN_EMAIL + '.pot'), 'wb')
@ -185,7 +185,7 @@ NSIS = 'NSIS_Installer.nsi'
RE_NSIS = re.compile(r'LangString\s+\w+\s+\$\{LANG_ENGLISH\}\s+(".*)', re.I) RE_NSIS = re.compile(r'LangString\s+\w+\s+\$\{LANG_ENGLISH\}\s+(".*)', re.I)
if os.path.exists(NSIS): if os.path.exists(NSIS):
print 'Creating the NSIS POT file' print('Creating the NSIS POT file')
if not os.path.exists(PON_DIR): if not os.path.exists(PON_DIR):
os.makedirs(PON_DIR) os.makedirs(PON_DIR)
src = open(NSIS, 'r') src = open(NSIS, 'r')

30
tools/make_mo.py

@ -158,13 +158,13 @@ def process_po_folder(domain, folder, extra=''):
# Create the MO file # Create the MO file
mo_file = os.path.join(mo_path, mo_name) mo_file = os.path.join(mo_path, mo_name)
print 'Compile %s' % mo_file print(('Compile %s' % mo_file))
ret, output = run('%s %s -o "%s" "%s"' % (TOOL, extra, mo_file, fname)) ret, output = run('%s %s -o "%s" "%s"' % (TOOL, extra, mo_file, fname))
if ret != 0: if ret != 0:
print '\nMissing %s. Please install this package first.' % TOOL print(('\nMissing %s. Please install this package first.' % TOOL))
exit(1) exit(1)
if 'WARNING:' in output: if 'WARNING:' in output:
print output print(output)
result = False result = False
return result return result
@ -197,10 +197,10 @@ def make_templates():
for path in glob.glob(os.path.join(MO_DIR, '*')): for path in glob.glob(os.path.join(MO_DIR, '*')):
lng = os.path.split(path)[1] lng = os.path.split(path)[1]
if lng != 'en' and os.path.exists(os.path.join(POE_DIR,lng+'.po')): if lng != 'en' and os.path.exists(os.path.join(POE_DIR,lng+'.po')):
print 'Create email template for %s' % lng print(('Create email template for %s' % lng))
trans = gettext.translation(DOMAIN_E, MO_DIR, [lng], fallback=False, codeset='latin-1') trans = gettext.translation(DOMAIN_E, MO_DIR, [lng], fallback=False, codeset='latin-1')
# The unicode flag will make _() return Unicode # The unicode flag will make _() return Unicode
trans.install(unicode=True, names=['lgettext']) trans.install(str=True, names=['lgettext'])
translate_tmpl('email', lng) translate_tmpl('email', lng)
translate_tmpl('rss', lng) translate_tmpl('rss', lng)
@ -241,14 +241,14 @@ def patch_nsis():
else: else:
trans = gettext.translation(DOMAIN_N, MO_DIR, [lcode], fallback=False, codeset='latin-1') trans = gettext.translation(DOMAIN_N, MO_DIR, [lcode], fallback=False, codeset='latin-1')
# The unicode flag will make _() return Unicode # The unicode flag will make _() return Unicode
trans.install(unicode=True, names=['lgettext']) trans.install(str=True, names=['lgettext'])
trans = _(text).encode('utf-8') trans = _(text).encode('utf-8')
trans = trans.replace('\r', '').replace('\n', '\\r\\n') trans = trans.replace('\r', '').replace('\n', '\\r\\n')
trans = trans.replace('\\', '$\\').replace('"', '$\\"') trans = trans.replace('\\', '$\\').replace('"', '$\\"')
line = '%s%s%s%s} "%s"\n' % (leader, item, rest, lng, trans) line = '%s%s%s%s} "%s"\n' % (leader, item, rest, lng, trans)
new.append(line) new.append(line)
elif lng is None: elif lng is None:
print 'Warning: unsupported language %s (%s), add to table in this script' % (langname, lcode) print(('Warning: unsupported language %s (%s), add to table in this script' % (langname, lcode)))
else: else:
new.append(line) new.append(line)
src.close() src.close()
@ -271,29 +271,29 @@ if os.path.exists(tl):
result = True result = True
if len(sys.argv) > 1 and sys.argv[1] == 'all': if len(sys.argv) > 1 and sys.argv[1] == 'all':
print 'NSIS MO file' print('NSIS MO file')
result = result and process_po_folder(DOMAIN_N, PON_DIR) result = result and process_po_folder(DOMAIN_N, PON_DIR)
print "Patch NSIS script" print("Patch NSIS script")
patch_nsis() patch_nsis()
print 'Email MO files' print('Email MO files')
result = result and process_po_folder(DOMAIN_E, POE_DIR) result = result and process_po_folder(DOMAIN_E, POE_DIR)
print "Create email templates from MO files" print("Create email templates from MO files")
make_templates() make_templates()
print 'Main program MO files' print('Main program MO files')
# -n option added to remove all newlines from the translations # -n option added to remove all newlines from the translations
result = result and process_po_folder(DOMAIN, PO_DIR, '-n') result = result and process_po_folder(DOMAIN, PO_DIR, '-n')
print "Remove temporary templates" print("Remove temporary templates")
remove_mo_files() remove_mo_files()
print print()
if result: if result:
exit(0) exit(0)
else: else:
print 'WARNINGS present!' print('WARNINGS present!')
exit(1) exit(1)

139
tools/msgfmt.py

@ -1,8 +1,7 @@
#! /usr/bin/env python #! /usr/bin/env python3
# -*- coding: iso-8859-1 -*- # Written by Martin v. Löwis <loewis@informatik.hu-berlin.de>
# Written by Martin v. L�wis <loewis@informatik.hu-berlin.de>
r"""Generate binary message catalog from textual translation description. """Generate binary message catalog from textual translation description.
This program converts a textual Uniforum-style message catalog (.po file) into This program converts a textual Uniforum-style message catalog (.po file) into
a binary GNU catalog (.mo file). This is essentially the same function as the a binary GNU catalog (.mo file). This is essentially the same function as the
@ -16,8 +15,6 @@ Options:
Specify the output file to write to. If omitted, output will go to a Specify the output file to write to. If omitted, output will go to a
file named filename.mo (based off the input file name). file named filename.mo (based off the input file name).
-n Remove all newlines (\r\n) from translations
-h -h
--help --help
Print this message and exit. Print this message and exit.
@ -27,63 +24,51 @@ Options:
Display version information and exit. Display version information and exit.
""" """
import sys
import os import os
import sys
import ast
import getopt import getopt
import struct import struct
import array import array
import re from email.parser import HeaderParser
__version__ = "1.1" __version__ = "1.1"
MESSAGES = {} MESSAGES = {}
nonewlines = False
# Detector for HTML elements
RE_HTML = re.compile('<[^>]+>')
def usage(code, msg=''): def usage(code, msg=''):
print >> sys.stderr, __doc__ print(__doc__, file=sys.stderr)
if msg: if msg:
print >> sys.stderr, msg print(msg, file=sys.stderr)
sys.exit(code) sys.exit(code)
def add(id, str, fuzzy): def add(id, str, fuzzy):
""" Add a non-fuzzy translation to the dictionary. """ "Add a non-fuzzy translation to the dictionary."
global MESSAGES, nonewlines, RE_HTML global MESSAGES
if not fuzzy and str: if not fuzzy and str:
if id.count('%s') == str.count('%s'): MESSAGES[id] = str
if nonewlines and id and ('\r' in str or '\n' in str) and RE_HTML.search(str):
MESSAGES[id] = str.replace('\n', '').replace('\r', '')
else:
MESSAGES[id] = str
else:
print 'WARNING: %s mismatch, skipping!'
print ' %s' % id
print ' %s' % str
def generate(): def generate():
""" Return the generated output. """ "Return the generated output."
global MESSAGES global MESSAGES
keys = MESSAGES.keys()
# the keys are sorted in the .mo file # the keys are sorted in the .mo file
keys.sort() keys = sorted(MESSAGES.keys())
offsets = [] offsets = []
ids = strs = '' ids = strs = b''
for id in keys: for id in keys:
# For each string, we need size and file offset. Each string is NUL # For each string, we need size and file offset. Each string is NUL
# terminated; the NUL does not count into the size. # terminated; the NUL does not count into the size.
offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id]))) offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id])))
ids += id + '\0' ids += id + b'\0'
strs += MESSAGES[id] + '\0' strs += MESSAGES[id] + b'\0'
output = '' output = ''
# The header is 7 32-bit unsigned integers. We don't use hash tables, so # The header is 7 32-bit unsigned integers. We don't use hash tables, so
# the keys start right after the index tables. # the keys start right after the index tables.
# translated string. # translated string.
keystart = 7 * 4 + 16 * len(keys) keystart = 7*4+16*len(keys)
# and the values start after the keys # and the values start after the keys
valuestart = keystart + len(ids) valuestart = keystart + len(ids)
koffsets = [] koffsets = []
@ -91,15 +76,15 @@ def generate():
# The string table first has the list of keys, then the list of values. # The string table first has the list of keys, then the list of values.
# Each entry has first the size of the string, then the file offset. # Each entry has first the size of the string, then the file offset.
for o1, l1, o2, l2 in offsets: for o1, l1, o2, l2 in offsets:
koffsets += [l1, o1 + keystart] koffsets += [l1, o1+keystart]
voffsets += [l2, o2 + valuestart] voffsets += [l2, o2+valuestart]
offsets = koffsets + voffsets offsets = koffsets + voffsets
output = struct.pack("Iiiiiii", output = struct.pack("Iiiiiii",
0x950412deL, # Magic 0x950412de, # Magic
0, # Version 0, # Version
len(keys), # # of entries len(keys), # # of entries
7 * 4, # start of key index 7*4, # start of key index
7 * 4 + len(keys) * 8, # start of value index 7*4+len(keys)*8, # start of value index
0, 0) # size and offset of hash table 0, 0) # size and offset of hash table
output += array.array("i", offsets).tostring() output += array.array("i", offsets).tostring()
output += ids output += ids
@ -120,17 +105,22 @@ def make(filename, outfile):
outfile = os.path.splitext(infile)[0] + '.mo' outfile = os.path.splitext(infile)[0] + '.mo'
try: try:
lines = open(infile).readlines() lines = open(infile, 'rb').readlines()
except IOError, msg: except IOError as msg:
print >> sys.stderr, msg print(msg, file=sys.stderr)
sys.exit(1) sys.exit(1)
section = None section = None
fuzzy = 0 fuzzy = 0
# Start off assuming Latin-1, so everything decodes without failure,
# until we know the exact encoding
encoding = 'latin-1'
# Parse the catalog # Parse the catalog
lno = 0 lno = 0
for l in lines: for l in lines:
l = l.decode(encoding)
lno += 1 lno += 1
# If we get a comment line after a msgstr, this is a new entry # If we get a comment line after a msgstr, this is a new entry
if l[0] == '#' and section == STR: if l[0] == '#' and section == STR:
@ -144,30 +134,58 @@ def make(filename, outfile):
if l[0] == '#': if l[0] == '#':
continue continue
# Now we are in a msgid section, output previous section # Now we are in a msgid section, output previous section
if l.startswith('msgid'): if l.startswith('msgid') and not l.startswith('msgid_plural'):
if section == STR: if section == STR:
add(msgid, msgstr, fuzzy) add(msgid, msgstr, fuzzy)
if not msgid:
# See whether there is an encoding declaration
p = HeaderParser()
charset = p.parsestr(msgstr.decode(encoding)).get_content_charset()
if charset:
encoding = charset
section = ID section = ID
l = l[5:] l = l[5:]
msgid = msgstr = '' msgid = msgstr = b''
is_plural = False
# This is a message with plural forms
elif l.startswith('msgid_plural'):
if section != ID:
print('msgid_plural not preceded by msgid on %s:%d' % (infile, lno),
file=sys.stderr)
sys.exit(1)
l = l[12:]
msgid += b'\0' # separator of singular and plural
is_plural = True
# Now we are in a msgstr section # Now we are in a msgstr section
elif l.startswith('msgstr'): elif l.startswith('msgstr'):
section = STR section = STR
l = l[6:] if l.startswith('msgstr['):
if not is_plural:
print('plural without msgid_plural on %s:%d' % (infile, lno),
file=sys.stderr)
sys.exit(1)
l = l.split(']', 1)[1]
if msgstr:
msgstr += b'\0' # Separator of the various plural forms
else:
if is_plural:
print('indexed msgstr required for plural on %s:%d' % (infile, lno),
file=sys.stderr)
sys.exit(1)
l = l[6:]
# Skip empty lines # Skip empty lines
l = l.strip() l = l.strip()
if not l: if not l:
continue continue
# XXX: Does this always follow Python escape semantics? l = ast.literal_eval(l)
l = eval(l)
if section == ID: if section == ID:
msgid += l msgid += l.encode(encoding)
elif section == STR: elif section == STR:
msgstr += l msgstr += l.encode(encoding)
else: else:
print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ print('Syntax error on %s:%d' % (infile, lno), \
'before:' 'before:', file=sys.stderr)
print >> sys.stderr, l print(l, file=sys.stderr)
sys.exit(1) sys.exit(1)
# Add last entry # Add last entry
if section == STR: if section == STR:
@ -177,17 +195,16 @@ def make(filename, outfile):
output = generate() output = generate()
try: try:
open(outfile, "wb").write(output) open(outfile,"wb").write(output)
except IOError, msg: except IOError as msg:
print >> sys.stderr, msg print(msg, file=sys.stderr)
def main(): def main():
global nonewlines
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'nhVo:', opts, args = getopt.getopt(sys.argv[1:], 'hVo:',
['help', 'version', 'output-file=']) ['help', 'version', 'output-file='])
except getopt.error, msg: except getopt.error as msg:
usage(1, msg) usage(1, msg)
outfile = None outfile = None
@ -196,16 +213,14 @@ def main():
if opt in ('-h', '--help'): if opt in ('-h', '--help'):
usage(0) usage(0)
elif opt in ('-V', '--version'): elif opt in ('-V', '--version'):
print >> sys.stderr, "msgfmt.py", __version__ print("msgfmt.py", __version__)
sys.exit(0) sys.exit(0)
elif opt in ('-o', '--output-file'): elif opt in ('-o', '--output-file'):
outfile = arg outfile = arg
elif opt in ('-n', ):
nonewlines = True
# do it # do it
if not args: if not args:
print >> sys.stderr, 'No input file given' print('No input file given', file=sys.stderr)
print >> sys.stderr, "Try `msgfmt --help' for more information." print("Try `msgfmt --help' for more information.", file=sys.stderr)
return return
for filename in args: for filename in args:
@ -213,4 +228,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()

60
util/apireg.py

@ -19,18 +19,18 @@
util.apireg - Registration of API connection info util.apireg - Registration of API connection info
""" """
import _winreg import winreg
def reg_info(user): def reg_info(user):
""" Return the reg key for API """ """ Return the reg key for API """
if user: if user:
# Normally use the USER part of the registry # Normally use the USER part of the registry
section = _winreg.HKEY_CURRENT_USER section = winreg.HKEY_CURRENT_USER
keypath = r"Software\SABnzbd" keypath = r"Software\SABnzbd"
else: else:
# A Windows Service will use the service key instead # A Windows Service will use the service key instead
section = _winreg.HKEY_LOCAL_MACHINE section = winreg.HKEY_LOCAL_MACHINE
keypath = r"SYSTEM\CurrentControlSet\Services\SABnzbd" keypath = r"SYSTEM\CurrentControlSet\Services\SABnzbd"
return section, keypath return section, keypath
@ -43,18 +43,18 @@ def get_connection_info(user=True):
url = None url = None
try: try:
hive = _winreg.ConnectRegistry(None, section) hive = winreg.ConnectRegistry(None, section)
key = _winreg.OpenKey(hive, keypath + r'\api') key = winreg.OpenKey(hive, keypath + r'\api')
for i in range(0, _winreg.QueryInfoKey(key)[1]): for i in range(0, winreg.QueryInfoKey(key)[1]):
name, value, val_type = _winreg.EnumValue(key, i) name, value, val_type = winreg.EnumValue(key, i)
if name == 'url': if name == 'url':
url = value url = value
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
pass pass
finally: finally:
_winreg.CloseKey(hive) winreg.CloseKey(hive)
# Nothing in user's registry, try system registry # Nothing in user's registry, try system registry
if user and not url: if user and not url:
@ -67,60 +67,60 @@ def set_connection_info(url, user=True):
""" Set API info in register """ """ Set API info in register """
section, keypath = reg_info(user) section, keypath = reg_info(user)
try: try:
hive = _winreg.ConnectRegistry(None, section) hive = winreg.ConnectRegistry(None, section)
try: try:
key = _winreg.CreateKey(hive, keypath) key = winreg.CreateKey(hive, keypath)
except: except:
pass pass
key = _winreg.OpenKey(hive, keypath) key = winreg.OpenKey(hive, keypath)
mykey = _winreg.CreateKey(key, 'api') mykey = winreg.CreateKey(key, 'api')
_winreg.SetValueEx(mykey, 'url', None, _winreg.REG_SZ, url) winreg.SetValueEx(mykey, 'url', None, winreg.REG_SZ, url)
_winreg.CloseKey(mykey) winreg.CloseKey(mykey)
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
if user: if user:
set_connection_info(url, user=False) set_connection_info(url, user=False)
pass pass
finally: finally:
_winreg.CloseKey(hive) winreg.CloseKey(hive)
def del_connection_info(user=True): def del_connection_info(user=True):
""" Remove API info from register """ """ Remove API info from register """
section, keypath = reg_info(user) section, keypath = reg_info(user)
try: try:
hive = _winreg.ConnectRegistry(None, section) hive = winreg.ConnectRegistry(None, section)
key = _winreg.OpenKey(hive, keypath) key = winreg.OpenKey(hive, keypath)
_winreg.DeleteKey(key, 'api') winreg.DeleteKey(key, 'api')
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
if user: if user:
del_connection_info(user=False) del_connection_info(user=False)
pass pass
finally: finally:
_winreg.CloseKey(hive) winreg.CloseKey(hive)
def get_install_lng(): def get_install_lng():
""" Return language-code used by the installer """ """ Return language-code used by the installer """
lng = 0 lng = 0
try: try:
hive = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) hive = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
key = _winreg.OpenKey(hive, r"Software\SABnzbd") key = winreg.OpenKey(hive, r"Software\SABnzbd")
for i in range(0, _winreg.QueryInfoKey(key)[1]): for i in range(0, winreg.QueryInfoKey(key)[1]):
name, value, val_type = _winreg.EnumValue(key, i) name, value, val_type = winreg.EnumValue(key, i)
if name == 'Installer Language': if name == 'Installer Language':
lng = value lng = value
_winreg.CloseKey(key) winreg.CloseKey(key)
except WindowsError: except WindowsError:
pass pass
finally: finally:
_winreg.CloseKey(hive) winreg.CloseKey(hive)
return lng return lng
if __name__ == '__main__': if __name__ == '__main__':
print 'URL = %s' % get_connection_info() print(('URL = %s' % get_connection_info()))
print 'Language = %s' % get_install_lng() print(('Language = %s' % get_install_lng()))
# del_connection_info() # del_connection_info()
# set_connection_info('localhost', '8080', 'blabla', user=False) # set_connection_info('localhost', '8080', 'blabla', user=False)

8
util/mailslot.py

@ -92,7 +92,7 @@ if __name__ == '__main__':
from time import sleep from time import sleep
if not __debug__: if not __debug__:
print 'Run this test in non-optimized mode' print('Run this test in non-optimized mode')
exit(1) exit(1)
if len(sys.argv) > 1 and 'server' in sys.argv[1]: if len(sys.argv) > 1 and 'server' in sys.argv[1]:
@ -103,7 +103,7 @@ if __name__ == '__main__':
while True: while True:
data = recv.receive() data = recv.receive()
if data is not None: if data is not None:
print data print(data)
if data.startswith('stop'): if data.startswith('stop'):
break break
sleep(2.0) sleep(2.0)
@ -114,7 +114,7 @@ if __name__ == '__main__':
send = MailSlot() send = MailSlot()
ret = send.connect() ret = send.connect()
assert ret, 'Failed to connect' assert ret, 'Failed to connect'
for n in xrange(5): for n in range(5):
ret = send.send('restart') ret = send.send('restart')
assert ret, 'Failed to send' assert ret, 'Failed to send'
sleep(2.0) sleep(2.0)
@ -123,4 +123,4 @@ if __name__ == '__main__':
send.disconnect() send.disconnect()
else: else:
print 'Usage: mailslot.py server|client' print('Usage: mailslot.py server|client')

Loading…
Cancel
Save