From 5ef3ee117945b41a21941f4f57c474911092fa95 Mon Sep 17 00:00:00 2001 From: Tux Date: Wed, 19 Jul 2023 22:46:19 +0200 Subject: [PATCH] 2to3 conversion --- FAHControl | 28 +- SConstruct | 73 ++-- ez_setup.py | 151 ++++---- fah/Client.py | 198 +++++----- fah/ClientConfig.py | 272 +++++++------- fah/Connection.py | 169 +++++---- fah/FAHControl.py | 783 ++++++++++++++++++++-------------------- fah/Icon.py | 55 +-- fah/SlotConfig.py | 87 +++-- fah/WidgetMap.py | 59 +-- fah/__init__.py | 58 +-- fah/db/Column.py | 61 ++-- fah/db/Database.py | 118 +++--- fah/db/Table.py | 63 ++-- fah/db/__init__.py | 46 +-- fah/util/OrderedDict.py | 8 +- fah/util/PYONDecoder.py | 14 +- fah/util/SingleApp.py | 8 +- fah/util/__init__.py | 18 +- fah/wraplabel.py | 53 ++- osx/scripts/onquit | 2 +- setup.py | 2 +- 22 files changed, 1184 insertions(+), 1142 deletions(-) diff --git a/FAHControl b/FAHControl index e99f0bf..857292b 100755 --- a/FAHControl +++ b/FAHControl @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 ''' Folding@Home Client Control (FAHControl) Copyright (C) 2010-2020 foldingathome.org @@ -36,16 +36,17 @@ def set_proc_name(name): libc.prctl(15, byref(buff), 0, 0, 0) -if sys.platform.startswith('linux'): set_proc_name('FAHControl') +if sys.platform.startswith('linux'): + set_proc_name('FAHControl') # If present, remove the Launch Services -psn_xxx_xxx argument if len(sys.argv) > 1 and sys.argv[1][:4] == '-psn': del sys.argv[1] -parser = OptionParser(usage = 'Usage: %prog [options]') +parser = OptionParser(usage='Usage: %prog [options]') -parser.add_option('--exit', help = 'Tell the running application to exit', - action = 'store_true', dest = 'exit') +parser.add_option('--exit', help='Tell the running application to exit', + action='store_true', dest='exit') options, args = parser.parse_args() # Tell app to exit @@ -54,24 +55,29 @@ if options.exit: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(single_app_addr) sock.send('EXIT') - if sock.recv(1024).strip() == 'OK': print ('Ok') + if sock.recv(1024).strip() == 'OK': + print('Ok') except Exception as e: pass sys.exit(0) # Add executable path to PATH -if getattr(sys, 'frozen', False): path = os.path.dirname(sys.executable) -else: path = os.path.dirname(__file__) +if getattr(sys, 'frozen', False): + path = os.path.dirname(sys.executable) +else: + path = os.path.dirname(__file__) path = os.path.realpath(path) os.environ['PATH'] = path + os.pathsep + os.environ['PATH'] # Load Glade dir = os.path.dirname(inspect.getfile(inspect.currentframe())) -if not dir: dir = '.' +if not dir: + dir = '.' glade = dir + '/fah/FAHControl.glade' -if os.path.exists(glade): app = FAHControl(glade) +if os.path.exists(glade): + app = FAHControl(glade) else: from fah.FAHControl_glade import glade_data app = FAHControl(glade_data) @@ -79,4 +85,4 @@ else: try: app.run() except Exception as e: - print (e) + print(e) diff --git a/SConstruct b/SConstruct index 89d1223..a75c6ae 100644 --- a/SConstruct +++ b/SConstruct @@ -1,8 +1,8 @@ # Setup import os -env = Environment(ENV = os.environ) +env = Environment(ENV=os.environ) try: - env.Tool('config', toolpath = [os.environ.get('CBANG_HOME')]) + env.Tool('config', toolpath=[os.environ.get('CBANG_HOME')]) except Exception as e: raise Exception('CBANG_HOME not set?\n' + str(e)) @@ -17,13 +17,14 @@ try: except Exception as e: print(e) version = '0.0.0' - env.Replace(PACKAGE_VERSION = version) + env.Replace(PACKAGE_VERSION=version) f = open('version.txt', 'w') f.write(version) f.close() -if env['PLATFORM'] != 'darwin': env['package_arch'] = 'noarch' +if env['PLATFORM'] != 'darwin': + env['package_arch'] = 'noarch' # Build target_dir = None @@ -37,10 +38,11 @@ if env['PLATFORM'] == 'darwin': elif env['PLATFORM'] == 'win32' or int(env.get('cross_mingw', 0)): env['RUN_DISTUTILSOPTS'] = 'build' target_dir = 'gui' - target_pat = '' # Not packaged here + target_pat = '' # Not packaged here elif env.GetPackageType() == 'deb': - env['RUN_DISTUTILSOPTS'] = ['--command-packages=stdeb.command', 'bdist_deb'] + env['RUN_DISTUTILSOPTS'] = ['--command-packages=stdeb.command', + 'bdist_deb'] target_dir = 'deb_dist' target_pat = 'deb_dist/fahcontrol_%s-*.deb' % version @@ -67,38 +69,39 @@ if env['PLATFORM'] == 'darwin' or env.GetPackageType() == 'rpm': pkg = env.Packager( 'FAHControl', - version = version, - maintainer = 'Joseph Coffland ', - vendor = 'Folding@home', - url = 'https://foldingathome.org/', - license = 'LICENSE.txt', - bug_url = 'https://apps.foldingathome.org/bugs/', - summary = 'Folding@home Control', - description = \ - 'Control and monitor local and remote Folding@home clients', - prefix = '/usr', + version=version, + maintainer='Joseph Coffland ', + vendor='Folding@home', + url='https://foldingathome.org/', + license='LICENSE.txt', + bug_url='https://apps.foldingathome.org/bugs/', + summary='Folding@home Control', + description='Control and monitor local \ + and remote Folding@home clients', + prefix='/usr', - documents = ['README.md', 'CHANGELOG.md', 'LICENSE.txt'], - desktop_menu = ['FAHControl.desktop'], - icons = ['images/FAHControl.png'], + documents=['README.md', 'CHANGELOG.md', 'LICENSE.txt'], + desktop_menu=['FAHControl.desktop'], + icons=['images/FAHControl.png'], - rpm_license = 'GPL v3+', - rpm_group = 'Applications/Internet', - rpm_requires = 'python, pygtk2', - rpm_build = 'rpm/build', - rpm_filelist = 'filelist.txt', + rpm_license='GPL v3+', + rpm_group='Applications/Internet', + rpm_requires='python, pygtk2', + rpm_build='rpm/build', + rpm_filelist='filelist.txt', - pkg_id = 'org.foldingathome.fahcontrol.pkg', - pkg_resources = 'osx/Resources', - pkg_apps = [['dist/FAHControl.app', 'Folding@home/FAHControl.app']], - pkg_scripts = 'osx/scripts', - pkg_target = '10.6', - pkg_distribution = 'osx/distribution.xml', + pkg_id='org.foldingathome.fahcontrol.pkg', + pkg_resources='osx/Resources', + pkg_apps=[['dist/FAHControl.app', 'Folding@home/FAHControl.app']], + pkg_scripts='osx/scripts', + pkg_target='10.6', + pkg_distribution='osx/distribution.xml', ) AlwaysBuild(pkg) env.Alias('package', pkg) - if gui is not None: Depends(pkg, gui) + if gui is not None: + Depends(pkg, gui) else: # Write package.txt @@ -107,10 +110,12 @@ else: filename = str(Glob(target_pat)[0]) open(str(target[0]), 'w').write(filename) - bld = Builder(action = write_filename) - env.Append(BUILDERS = {'WriteFilename' : bld}) + bld = Builder(action=write_filename) + env.Append(BUILDERS={'WriteFilename': + bld}) cmd = env.WriteFilename('package.txt', []) AlwaysBuild(cmd) - if gui is not None: Depends(cmd, gui) + if gui is not None: + Depends(cmd, gui) env.Alias('package', [cmd]) diff --git a/ez_setup.py b/ez_setup.py index 97f56a4..6a97d81 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -13,11 +13,14 @@ the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ -from __future__ import print_function + import sys +import os + DEFAULT_VERSION = "0.6c11" -DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % \ + sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', @@ -64,12 +67,12 @@ md5_data = { 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', } -import sys, os try: from hashlib import md5 except ImportError: from md5 import md5 + def _validate_md5(egg_name, data): if egg_name in md5_data: digest = md5(data).hexdigest() @@ -81,6 +84,7 @@ def _validate_md5(egg_name, data): sys.exit(2) return data + def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 @@ -96,25 +100,34 @@ def use_setuptools( this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ - was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + was_imported = 'pkg_resources' in sys.modules \ + or 'setuptools' in sys.modules + def do_download(): - egg = download_setuptools(version, download_base, to_dir, download_delay) + egg = download_setuptools(version, + download_base, + to_dir, + download_delay) sys.path.insert(0, egg) - import setuptools; setuptools.bootstrap_install_from = egg + import setuptools + setuptools.bootstrap_install_from = egg try: import pkg_resources except ImportError: - return do_download() + return do_download() try: - pkg_resources.require("setuptools>="+version); return + pkg_resources.require("setuptools>="+version) + return except pkg_resources.VersionConflict as e: if was_imported: - print ( - "The required version of setuptools (>=%s) is not available, and\n" - " can't be installed while this script is running. Please install\n" - " a more recent version first, using 'easy_install -U setuptools'." - "\n\n(Currently using %r)" - % (version, e.args[0]), file=sys.stderr) + print("The required version of setuptools (>=%s) \ + is not available, and\n" + " can't be installed while this script is running. \ + Please install\n" + " a more recent version first, using \ + 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + % (version, e.args[0]), file=sys.stderr) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok @@ -122,19 +135,26 @@ def use_setuptools( except pkg_resources.DistributionNotFound: return do_download() + def download_setuptools( - version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, - delay = 15 + version=DEFAULT_VERSION, + download_base=DEFAULT_URL, + to_dir=os.curdir, + delay=15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. - `delay` is the number of seconds to pause before an actual download attempt. + `delay` is the number of seconds to pause before an actual download \ + attempt. """ - import urllib2, shutil - egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + import urllib.request + import urllib.error + import urllib.parse + import shutil + egg_name = "setuptools-%s-py%s.egg" % (version, sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None @@ -156,54 +176,26 @@ I will start the download in %d seconds. and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", - version, download_base, delay, url - ); from time import sleep; sleep(delay) + version, + download_base, + delay, url) + from time import sleep + sleep(delay) log.warn("Downloading %s", url) - src = urllib2.urlopen(url) + src = urllib.request.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) - dst = open(saveto,"wb"); dst.write(data) + dst = open(saveto, "wb") + dst.write(data) finally: - if src: src.close() - if dst: dst.close() + if src: + src.close() + if dst: + dst.close() return os.path.realpath(saveto) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: @@ -212,7 +204,7 @@ def main(argv, version=DEFAULT_VERSION): egg = None try: egg = download_setuptools(version, delay=0) - sys.path.insert(0,egg) + sys.path.insert(0, egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: @@ -220,10 +212,11 @@ def main(argv, version=DEFAULT_VERSION): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': - print ( - "You have an obsolete version of setuptools installed. Please\n" - " remove it from your system entirely before rerunning this script.", - file=sys.stderr) + print("You have an obsolete version of setuptools installed. \ + Please\n" + " remove it from your system entirely before rerunning this \ + script.", + file=sys.stderr) sys.exit(2) req = "setuptools>="+version @@ -236,14 +229,16 @@ def main(argv, version=DEFAULT_VERSION): except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) - sys.exit(0) # try to force an exit + sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: - print ("Setuptools version %s or greater has been installed." % version) - print ('(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)') + print("Setuptools version %s or greater has been installed." + % version) + print('(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)') + def update_md5(filenames): """Update our built-in md5 registry""" @@ -252,37 +247,33 @@ def update_md5(filenames): for name in filenames: base = os.path.basename(name) - f = open(name,'rb') + f = open(name, 'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() - data = [" %r: %r,\n" % it for it in md5_data.items()] + data = [" %r: %r,\n" % it for it in list(md5_data.items())] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) - f = open(srcfile, 'rb'); src = f.read(); f.close() + f = open(srcfile, 'rb') + src = f.read() + f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: - print ("Internal error!", file=sys.stderr) + print("Internal error!", file=sys.stderr) sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] - f = open(srcfile,'w') + f = open(srcfile, 'w') f.write(src) f.close() -if __name__=='__main__': - if len(sys.argv)>2 and sys.argv[1]=='--md5update': +if __name__ == '__main__': + if len(sys.argv) > 2 and sys.argv[1] == '--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) - - - - - - diff --git a/fah/Client.py b/fah/Client.py index fd64eff..7c652c5 100644 --- a/fah/Client.py +++ b/fah/Client.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import traceback import time @@ -40,7 +40,8 @@ debug = False class Client: def __init__(self, app, name, address, port, password): - if debug: print('Client.__init__()') + if debug: + print('Client.__init__()') self.name = name self.address = address @@ -54,12 +55,13 @@ class Client: self.error_messages = set() - if not name: self.name = self.get_address() + if not name: + self.name = self.get_address() # Option names - names = app.client_option_widgets.keys() - self.option_names = map(lambda name: name.replace('_', '-'), names) - self.option_names.append('power') # Folding power + names = list(app.client_option_widgets.keys()) + self.option_names = [name.replace('_', '-') for name in names] + self.option_names.append('power') # Folding power # Init commands self.inactive_cmds = [ @@ -82,45 +84,39 @@ class Client: self.conn = Connection(self.address, self.port, self.password) self.conn.set_init_commands(self.inactive_cmds) - # Class special functions def __str__(self): return self.name def __cmp__(self, other): return str.__cmp__(self.name, other.name) def __hash__(self): return self.name.__hash__() - # Getters @staticmethod def make_address(address, port): return '%s:%d' % (address, port) def get_address(self): return Client.make_address(self.address, self.port) def get_password(self): return self.conn.password - def get_status(self): status = self.conn.get_status() if status == 'Online' and not self.is_updated() and self.selected: return 'Updating' return status - def get_selected_slot(self, app): return self.config.get_selected_slot(app) - # Setters def set_address(self, address, port): self.conn.address = self.address = address self.conn.port = self.port = port - def set_password(self, password): if self.conn.is_connected(): self.conn.queue_command('option password "%s"' % password) self.conn.password = self.password = password - # State functions def is_local(self): return self.address == '127.0.0.1' and self.name == 'local' + def is_online(self): return self.conn.get_status() == 'Online' def set_selected(self, selected): @@ -128,28 +124,25 @@ class Client: if selected: self.set_updated(False) self.conn.set_init_commands(self.active_cmds) - else: self.conn.set_init_commands(self.inactive_cmds) + else: + self.conn.set_init_commands(self.inactive_cmds) self.selected = selected - def set_updated(self, updated): self.options_updated = updated self.info_updated = updated self.slots_updated = updated self.units_updated = updated - def is_updated(self): return self.options_updated and self.info_updated and \ self.slots_updated and self.units_updated - # Log functions def refresh_log(self): self.conn.queue_command('log-updates restart') - # GUI functions def load_dialog(self, app): app.client_entries['name'].set_text(self.name) @@ -169,48 +162,40 @@ class Client: 'address': self.get_address()} return list(make_row(app.client_cols, keys)) - def update_status_ui(self, app): self.config.update_status_ui(app) - def reset_status_ui(self, app): self.config.reset_status_ui(app) - # Slot control - def unpause(self, slot = ''): + def unpause(self, slot=''): self.conn.queue_command('unpause %s' % str(slot)) - - def pause(self, slot = ''): + def pause(self, slot=''): self.conn.queue_command('pause %s' % str(slot)) - - def finish(self, slot = ''): + def finish(self, slot=''): self.conn.queue_command('finish %s' % str(slot)) - - def on_idle(self, slot = ''): + def on_idle(self, slot=''): self.conn.queue_command('on_idle %s' % str(slot)) - - def always_on(self, slot = ''): + def always_on(self, slot=''): self.conn.queue_command('always_on %s' % str(slot)) - # Save functions def save(self, db): - db.insert('clients', name = self.name, address = self.address, - port = self.port, password = self.password) - + db.insert('clients', name=self.name, address=self.address, + port=self.port, password=self.password) def save_options(self, options): - if not options: return + if not options: + return cmd = 'options' - for name, value in options.items(): + for name, value in list(options.items()): cmd += ' ' + name if name[-1] != '!': cmd += "='%s'" % value.encode('string_escape') @@ -218,11 +203,11 @@ class Client: cmd += ' %s *' % ' '.join(self.option_names) self.conn.queue_command(cmd) - self.options_updated = False # Reload - + self.options_updated = False # Reload def save_slots(self, slots): - if not slots or slots == ([], [], []): return + if not slots or slots == ([], [], []): + return deleted, added, modified = slots @@ -233,23 +218,25 @@ class Client: # Modified for id, type, options in modified: cmd = 'slot-modify %d %s' % (id, type) - for name, value in options.items(): - if name[-1] == '!': cmd += ' ' + name - else: cmd += ' %s="%s"' % (name, value) + for name, value in list(options.items()): + if name[-1] == '!': + cmd += ' ' + name + else: + cmd += ' %s="%s"' % (name, value) self.conn.queue_command(cmd) # Added for type, options in added: cmd = 'slot-add %s' % type - for name, value in options.items(): + for name, value in list(options.items()): cmd += ' %s="%s"' % (name, value) self.conn.queue_command(cmd) - self.slots_updated = False # Reload - + self.slots_updated = False # Reload def save_config(self, options, slots): - if not options and slots == ([], [], []): return + if not options and slots == ([], [], []): + return self.save_options(options) self.save_slots(slots) @@ -257,14 +244,12 @@ class Client: self.conn.queue_command('save') self.conn.queue_command('updates reset') - def set_power(self, power): power = power.lower().replace(' ', '_') if power != self.power: self.power = power self.conn.queue_command('option power ' + power) - # Message processing def process_options(self, app, data): self.options_updated = True @@ -274,26 +259,26 @@ class Client: self.config.update_user_info(app) self.config.update_ppd(app, self.ppd) - def process_info(self, app, data): self.info_updated = True self.config.info = data - if self.selected: self.config.update_info(app) - + if self.selected: + self.config.update_info(app) def process_slots(self, app, data): self.slots_updated = True slots = [] - for slot in data: slots.append(SlotConfig(**slot)) + for slot in data: + slots.append(SlotConfig(**slot)) self.config.slots = slots - if self.selected: self.config.update_status_ui(app) - + if self.selected: + self.config.update_status_ui(app) def process_units(self, app, data): self.units_updated = True self.config.update_queue(data) - if self.selected: self.config.update_status_ui(app) - + if self.selected: + self.config.update_status_ui(app) def process_log_update(self, app, data): # Remove color codes @@ -301,54 +286,64 @@ class Client: self.config.log_add(app, data) - def process_log_restart(self, app, data): self.config.log_clear(app) self.process_log_update(app, data) - def process_ppd(self, app, ppd): self.ppd = ppd - if self.selected: self.config.update_ppd(app, ppd) - + if self.selected: + self.config.update_ppd(app, ppd) def process_error(self, app, data): msg = 'On client "%s" %s:%d: %s' % ( self.name, self.address, self.port, data) # Only popup dialog once for each error - if not msg in self.error_messages: + if msg not in self.error_messages: self.error_messages.add(msg) app.error(msg) - else: print('ERROR: %s' % msg) + else: + print(('ERROR: %s' % msg)) app.set_status(msg) def process_configured(self, app, configured): - if configured: return + if configured: + return app.configure_dialog.show() - def process_message(self, app, type, data): - if debug: print('message: %s %s' % (type, data)) + if debug: + print(('message: %s %s' % (type, data))) - if type == 'heartbeat': return - if type == 'ppd': self.process_ppd(app, data) + if type == 'heartbeat': + return + if type == 'ppd': + self.process_ppd(app, data) - if not self.selected: return + if not self.selected: + return - if type == 'options': self.process_options(app, data) - elif type == 'info': self.process_info(app, data) - elif type == 'slots': self.process_slots(app, data) - elif type == 'units': self.process_units(app, data) - elif type == 'log-restart': self.process_log_restart(app, data) - elif type == 'log-update': self.process_log_update(app, data) - elif type == 'error': self.process_error(app, data) - elif type == 'configured': self.process_configured(app, data) + if type == 'options': + self.process_options(app, data) + elif type == 'info': + self.process_info(app, data) + elif type == 'slots': + self.process_slots(app, data) + elif type == 'units': + self.process_units(app, data) + elif type == 'log-restart': + self.process_log_restart(app, data) + elif type == 'log-update': + self.process_log_update(app, data) + elif type == 'error': + self.process_error(app, data) + elif type == 'configured': + self.process_configured(app, data) # Ignore other message types - def update(self, app): prevStatus = self.get_status() @@ -385,18 +380,19 @@ class Client: iter = list.iter_next(iter) - if not self.is_online(): self.set_updated(False) + if not self.is_online(): + self.set_updated(False) # Update client status label - if self.selected: app.update_client_status() - + if self.selected: + app.update_client_status() def reconnect(self): self.conn.close() - def close(self): - if debug: print('Client closing') + if debug: + print('Client closing') # Avoid broken pipe on OSX if sys.platform == 'darwin': diff --git a/fah/ClientConfig.py b/fah/ClientConfig.py index bfba39b..5a7084e 100644 --- a/fah/ClientConfig.py +++ b/fah/ClientConfig.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import sys import gtk @@ -41,18 +41,20 @@ def get_option_mods(old_options, new_options): changes[name + '!'] = None # Added and modified - for name, value in new_options.items(): + for name, value in list(new_options.items()): if name not in old_options or value != old_options[name]: changes[name] = value return changes + def get_buffer_text(buffer): return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()) def get_model_column(model, iter, column): - if iter is not None: return model.get_value(iter, column) + if iter is not None: + return model.get_value(iter, column) def get_selected_tree_column(tree, column): @@ -84,39 +86,38 @@ class ClientConfig: self.log_filter_re = None self.updating = False - def get(self, name): - if name in self.options: return self.options[name] + if name in self.options: + return self.options[name] + def set(self, name, value): self.options[name] = value + def have(self, name): return name in self.options and self.options[name] is not None - def get_prcg(self, row): return '%s (%s, %s, %s)' % ( row['project'], row['run'], row['clone'], row['gen']) - def update_power(self, app): power = self.get('power').lower() for i in range(len(app.folding_power_levels)): if power == app.folding_power_levels[i].lower(): app.folding_power.set_value(i) - def update_ppd(self, app, ppd): - if ppd: s = '%d' % int(ppd) - else: s = 'Unknown' + if ppd: + s = '%d' % int(ppd) + else: + s = 'Unknown' app.client_ppd.set_text(s) - def update_queue(self, queue): self.queue = queue self.queue_map = {} for values in self.queue: self.queue_map[values['id']] = values - def update_user_info(self, app): # User user = self.options['user'] @@ -126,22 +127,20 @@ class ClientConfig: team = self.options['team'] app.team_info.set_label(team) - def reset_user_info(self, app): app.donor_info.set_label('') app.team_info.set_label('') - def get_selected_queue_entry(self, app): return get_selected_tree_column(app.queue_tree, 1) - def get_selected_slot(self, app): id = get_selected_tree_column(app.slot_status_tree, 0) if id is not None: id = int(id) for slot in self.slots: - if slot.id == id: return slot + if slot.id == id: + return slot def update_queue_ui(self, app): if not self.queue: @@ -171,19 +170,25 @@ class ClientConfig: progress = values['percentdone'] percent = float(progress[:-1]) eta = values['eta'] - if eta == '0.00 secs': eta = 'Unknown' + if eta == '0.00 secs': + eta = 'Unknown' credit = values['creditestimate'] - if float(credit) == 0: credit = 'Unknown' + if float(credit) == 0: + credit = 'Unknown' prcg = self.get_prcg(values) iter = app.queue_list.append([unit_id, queue_id, status, color, - progress, percent, eta, credit, prcg]) + progress, percent, eta, credit, + prcg]) - if queue_id == selected: selected_row = iter - if queue_id == log_filter_selected: log_filter_row = iter + if queue_id == selected: + selected_row = iter + if queue_id == log_filter_selected: + log_filter_row = iter # Select the first item if nothing is selected - if selected_row is None: selected_row = app.queue_list.get_iter_first() + if selected_row is None: + selected_row = app.queue_list.get_iter_first() if log_filter_row is None: log_filter_row = app.queue_list.get_iter_first() @@ -191,7 +196,6 @@ class ClientConfig: app.queue_tree.get_selection().select_iter(selected_row) app.log_unit.set_active_iter(log_filter_row) - def update_work_unit_info(self, app): if not self.queue: self.reset_work_unit_info(app) @@ -199,15 +203,19 @@ class ClientConfig: # Get selected queue entry selected = self.get_selected_queue_entry(app) - if selected is None: return + if selected is None: + return entry = self.queue_map[selected] # Load info - for name, value in entry.items(): + for name, value in list(entry.items()): if name in app.queue_widgets: - if (name in ['basecredit', 'creditestimate', 'ppd'] and \ - float(value) == 0) or value == '' or \ - value == '0.00 secs': value = 'Unknown' + if (name in ['basecredit', + 'creditestimate', + 'ppd'] and float(value) == 0) \ + or value == '' \ + or value == '0.00 secs': + value = 'Unknown' widget = app.queue_widgets[name] set_widget_str_value(widget, value) @@ -229,24 +237,27 @@ class ClientConfig: entry['project'], entry['run'], entry['clone'], entry['gen']) set_widget_str_value(app.queue_widgets['prcg'], prcg) - def select_slot(self, app): # Get selected slot slot = self.get_selected_slot(app) - if slot is None: return + if slot is None: + return # Get associated queue ID first_id = None first_running_id = None for entry in self.queue: if int(entry['slot']) == slot.id: - if first_id is None: first_id = entry['unit'] + if first_id is None: + first_id = entry['unit'] if entry['state'].upper() in ['RUNNING', 'FINISHING'] and \ first_running_id is None: first_running_id = entry['unit'] - if first_running_id is not None: unit_id = first_running_id - else: unit_id = first_id + if first_running_id is not None: + unit_id = first_running_id + else: + unit_id = first_id if unit_id is not None: # Find unit_id in the queue list entry and select row @@ -260,15 +271,16 @@ class ClientConfig: # Update the UI self.update_work_unit_info(app) - else: app.queue_tree.get_selection().unselect_all() - + else: + app.queue_tree.get_selection().unselect_all() def select_queue_slot(self, app): # Get unit ID of selected queue entry selected = self.get_selected_queue_entry(app) - if selected is None: return + if selected is None: + return - # Get associated slot ID + # Get associated slot ID entry = self.queue_map[selected] slot = int(entry['slot']) @@ -284,17 +296,16 @@ class ClientConfig: # Update the UI self.update_work_unit_info(app) - def reset_work_unit_info(self, app): - for widget in app.queue_widgets.values(): + for widget in list(app.queue_widgets.values()): set_widget_str_value(widget, None) - def update_info(self, app): port = app.info # Clear - for child in port.get_children(): port.remove(child) + for child in port.get_children(): + port.remove(child) # Alignment align = gtk.Alignment(0, 0, 1, 1) @@ -327,7 +338,8 @@ class ClientConfig: row = 0 for name, value in category: - if not value: continue + if not value: + continue # Name label = gtk.Label('%s' % name) @@ -341,30 +353,30 @@ class ClientConfig: label.set_relief(gtk.RELIEF_NONE) label.set_property('can-focus', False) - else: label = gtk.Label(value) + else: + label = gtk.Label(value) label.set_alignment(0, 0.5) label.modify_font(app.mono_font) - table.attach(label, 1, 2, row, row + 1, yoptions = gtk.FILL) + table.attach(label, 1, 2, row, row + 1, yoptions=gtk.FILL) row += 1 port.realize() port.show_all() - def update_options(self, app): used = set() - for name, widget in app.client_option_widgets.items(): + for name, widget in list(app.client_option_widgets.items()): name = name.replace('_', '-') used.add(name) try: set_widget_str_value(widget, self.options[name]) - except Exception as e: # Don't let one bad widget kill everything - print('WARNING: failed to set widget "%s": %s' % (name, e)) + except Exception as e: # Don't let one bad widget kill everything + print(('WARNING: failed to set widget "%s": %s' % (name, e))) # Setup passkey and password entries app.passkey_validator.set_good() @@ -384,9 +396,12 @@ class ClientConfig: if self.have('proxy'): proxy = self.get('proxy') - if ':' in proxy: proxy_addr, proxy_port = proxy.split(':', 1) - else: proxy_addr, proxy_port = proxy, '8080' - set_widget_str_value(app.client_option_widgets['proxy'], proxy_addr) + if ':' in proxy: + proxy_addr, proxy_port = proxy.split(':', 1) + else: + proxy_addr, proxy_port = proxy, '8080' + set_widget_str_value(app.client_option_widgets['proxy'], + proxy_addr) set_widget_str_value(app.proxy_port, proxy_port) # Set core priority radio button @@ -401,19 +416,20 @@ class ClientConfig: used.add('extra-core-args') args = self.get('extra-core-args').split() - for arg in args: app.core_option_list.append([arg]) + for arg in args: + app.core_option_list.append([arg]) # Remaining options app.option_list.clear() - for name, value in self.options.items(): + for name, value in list(self.options.items()): if name not in used: app.option_list.append([name, value]) - def update_status_slots(self, app): # Save selection selected = get_selected_tree_column(app.slot_status_tree, 0) - if selected is not None: selected = selected + if selected is not None: + selected = selected selected_row = None log_filter_selected = get_active_combo_column(app.log_slot, 0) log_filter_row = None @@ -434,10 +450,15 @@ class ClientConfig: status += ':' + slot.reason status = get_span_markup(status, color) description = slot.description.replace('"', '') - iter = app.slot_status_list.append((id, status, color, description)) + iter = app.slot_status_list.append((id, + status, + color, + description)) - if id == selected: selected_row = iter - if id == log_filter_selected: log_filter_row = iter + if id == selected: + selected_row = iter + if id == log_filter_selected: + log_filter_row = iter # Selected the first item if nothing is selected if selected_row is None: @@ -454,25 +475,22 @@ class ClientConfig: if log_filter_row is not None: app.log_slot.set_active_iter(log_filter_row) - def update_slots_ui(self, app): app.slot_list.clear() for slot in self.slots: slot.add_to_ui(app) - def scroll_log_to_end(self, app): - if not app.log_follow.get_active(): return + if not app.log_follow.get_active(): + return mark = app.log.get_mark('end') app.log.move_mark(mark, app.log.get_end_iter()) app.log_view.scroll_mark_onscreen(mark) - def log_clear(self, app): app.log.set_text('') self.log = [] - def log_filter_str(self, app): f = [] @@ -491,19 +509,17 @@ class ClientConfig: f.append(r'FS%s' % id) if len(f): - f = map(lambda x: '.*(^|:)%s' % x, f) - return '(^\*)|(%s):' % ''.join(f) + f = ['.*(^|:)%s' % x for x in f] + return '(^\\*)|(%s):' % ''.join(f) return None - def log_filter(self, line): return self.log_filter_re is None or \ self.log_filter_re.match(line) is not None - def log_add_lines(self, app, lines): - filtered = filter(self.log_filter, lines) + filtered = list(filter(self.log_filter, lines)) if len(filtered): text = '\n'.join(filtered) @@ -511,41 +527,42 @@ class ClientConfig: app.log.insert(app.log.get_end_iter(), text + '\n') self.scroll_log_to_end(app) - def log_add(self, app, text): # TODO deal with split lines lines = [] for line in text.split('\n'): - if not line: continue + if not line: + continue lines.append(line) self.log.append(line) self.log_add_lines(app, lines) - def update_log(self, app): - if self.updating: return # Don't refilter during updates + if self.updating: + return # Don't refilter during updates # Check if filter has changed log_filter = self.log_filter_str(app) - if log_filter == self.last_log_filter: return + if log_filter == self.last_log_filter: + return # Update filter self.last_log_filter = log_filter - if log_filter is not None: self.log_filter_re = re.compile(log_filter) - else: self.log_filter_re = None + if log_filter is not None: + self.log_filter_re = re.compile(log_filter) + else: + self.log_filter_re = None # Reload log app.log.set_text('') self.log_add_lines(app, self.log) - def update_status_ui(self, app): self.update_queue_ui(app) self.update_status_slots(app) self.update_work_unit_info(app) - app.update_client_status() # TODO this should probably be moved here - + app.update_client_status() # TODO this should probably be moved here def reset_status_ui(self, app): self.reset_work_unit_info(app) @@ -553,25 +570,25 @@ class ClientConfig: app.slot_status_list.clear() app.log.set_text('') - def get_running(self): for unit in self.queue: - if unit['state'].upper() == 'RUNNING': return True + if unit['state'].upper() == 'RUNNING': + return True return False - def get_option_changes(self, app): used = set() options = {} - used.add('power') # Don't set power here + used.add('power') # Don't set power here # Proxy options used.add('proxy') proxy_addr = get_widget_str_value(app.client_option_widgets['proxy']) proxy_port = get_widget_str_value(app.proxy_port) proxy = '%s:%s' % (proxy_addr, proxy_port) - if self.get('proxy') != proxy: options['proxy'] = proxy + if self.get('proxy') != proxy: + options['proxy'] = proxy # Core priority radio button used.add('core-priority') @@ -586,77 +603,90 @@ class ClientConfig: used.add('extra-core-args') if self.have('extra-core-args'): old_args = self.get('extra-core-args').split() - else: old_args = [] + else: + old_args = [] new_args = [] + def add_arg(model, path, iter, data): new_args.append(model.get(iter, 0)[0]) app.core_option_list.foreach(add_arg, None) if old_args != new_args: - if new_args: options['extra-core-args'] = ' '.join(new_args) - else: options['extra-core-args!'] = None + if new_args: + options['extra-core-args'] = ' '.join(new_args) + else: + options['extra-core-args!'] = None # Extra options def check_option(model, path, iter, data): name, value = model.get(iter, 0, 1) used.add(name) - if self.get(name) != value: options[name] = value + if self.get(name) != value: + options[name] = value app.option_list.foreach(check_option, None) # Main options - for name, widget in app.client_option_widgets.items(): + for name, widget in list(app.client_option_widgets.items()): name = name.replace('_', '-') - if name in used: continue + if name in used: + continue value = self.get(name) used.add(name) try: value = get_widget_str_value(widget) old_value = self.get(name) - if value == '' and old_value is None: value = None + if value == '' and old_value is None: + value = None if value != old_value: - if value is None: options[name + '!'] = None - else: options[name] = value + if value is None: + options[name + '!'] = None + else: + options[name] = value - except Exception as e: # Don't let one bad widget kill everything - print('WARNING: failed to save widget "%s": %s' % (name, e)) + except Exception as e: # Don't let one bad widget kill everything + print(('WARNING: failed to save widget "%s": %s' % (name, e))) # Removed options for name in self.options: - if not name in used: + if name not in used: options[name + '!'] = None return options - def get_slot_changes(self, app): # Get new slots new_slots = [] - def add_slot(model, path, iter, data = None): + + def add_slot(model, path, iter, data=None): new_slots.append(model.get(iter, 2)[0].slot) app.slot_list.foreach(add_slot) # Get old slot IDs old_slot_map = {} - for slot in self.slots: old_slot_map[slot.id] = slot + for slot in self.slots: + old_slot_map[slot.id] = slot # Get new slot IDs new_slot_ids = set() - for slot in new_slots: new_slot_ids.add(slot.id) + for slot in new_slots: + new_slot_ids.add(slot.id) # Find deleted slot IDs deleted = [] for id in old_slot_map: - if id not in new_slot_ids: deleted.append(id) + if id not in new_slot_ids: + deleted.append(id) # Find added and modified slots added = [] modified = [] for slot in new_slots: # Added - if slot.id == -1: added.append((slot.type, slot.options)) + if slot.id == -1: + added.append((slot.type, slot.options)) else: old_slot = old_slot_map[slot.id] options = get_option_mods(old_slot.options, slot.options) @@ -665,11 +695,9 @@ class ClientConfig: return (deleted, added, modified) - def get_changes(self, app): return self.get_option_changes(app), self.get_slot_changes(app) - def has_changes(self, app): options, slots = self.get_changes(app) return options or slots != ([], [], []) diff --git a/fah/Connection.py b/fah/Connection.py index 22c6b11..e440362 100644 --- a/fah/Connection.py +++ b/fah/Connection.py @@ -1,25 +1,25 @@ #!/usr/bin/env python2 -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import socket import select @@ -41,8 +41,11 @@ WSAEWOULDBLOCK = 10035 class Connection: - def __init__(self, address = 'localhost', port = 36330, password = None, - retry_rate = 5): + def __init__(self, + address='localhost', + port=36330, + password=None, + retry_rate=5): self.address = address self.port = int(port) self.password = password @@ -52,44 +55,45 @@ class Connection: self.socket = None self.reset() - def set_init_commands(self, commands): self.init_commands = commands if self.is_connected(): - map(self.queue_command, self.init_commands) - + list(map(self.queue_command, self.init_commands)) def get_status(self): - if self.connected: return 'Online' - #if self.socket is None: return 'Offline' + if self.connected: + return 'Online' + # if self.socket is None: return 'Offline' return 'Connecting' - def is_connected(self): - if self.socket is None: return False - if self.connected: return True + if self.socket is None: + return False + if self.connected: + return True - rlist, wlist, xlist = select.select([], [self.socket], [self.socket], 0) + rlist, wlist, xlist = select.select([], + [self.socket], + [self.socket], + 0) - if len(wlist) != 0: self.connected = True + if len(wlist) != 0: + self.connected = True elif len(xlist) != 0: self.fail_reason = 'refused' self.close() return self.connected - def can_write(self): rlist, wlist, xlist = select.select([], [self.socket], [], 0) return len(wlist) != 0 - def can_read(self): rlist, wlist, xlist = select.select([self.socket], [], [], 0) return len(rlist) != 0 - def reset(self): self.close() self.messages = [] @@ -99,9 +103,9 @@ class Connection: self.last_message = 0 self.last_connect = 0 - def open(self): - if debug: print('Connection.open()') + if debug: + print('Connection.open()') self.reset() self.last_connect = time.time() @@ -110,45 +114,47 @@ class Connection: self.socket.setblocking(0) err = self.socket.connect_ex((self.address, self.port)) - if err != 0 and not err in [ - errno.EINPROGRESS, errno.EWOULDBLOCK, WSAEWOULDBLOCK]: + if err != 0 and err not in [ + errno.EINPROGRESS, errno.EWOULDBLOCK, WSAEWOULDBLOCK]: self.fail_reason = 'connect' raise Exception('Connection failed: ' + errno.errorcode[err]) - if self.password: self.queue_command('auth "%s"' % self.password) - map(self.queue_command, self.init_commands) - + if self.password: + self.queue_command('auth "%s"' % self.password) + list(map(self.queue_command, self.init_commands)) def close(self): - if debug: print('Connection.close()') + if debug: + print('Connection.close()') if self.socket is not None: try: self.socket.shutdown(socket.SHUT_RDWR) - except: pass + except socket: + pass try: self.socket.close() - except: pass + except socket: + pass self.socket = None self.connected = False - def connection_lost(self): print('Connection lost') self.close() self.fail_reason = 'closed' raise Exception('Lost connection') - def connection_error(self, err, msg): - print('Connection Error: %d: %s' % (err, msg)) + print(('Connection Error: %d: %s' % (err, msg))) self.close() - if err == errno.ECONNREFUSED: self.fail_reason = 'refused' + if err == errno.ECONNREFUSED: + self.fail_reason = 'refused' elif err in [errno.ETIMEDOUT, errno.ENETDOWN, errno.ENETUNREACH]: self.fail_reason = 'connect' - else: self.fail_reason = 'error' - + else: + self.fail_reason = 'error' def read_some(self): bytesRead = 0 @@ -156,26 +162,30 @@ class Connection: while True: buffer = self.socket.recv(10 * 1024 * 1024) if len(buffer): - #if debug: print 'BUFFER:', buffer + # if debug: print 'BUFFER:', buffer self.readBuf += buffer bytesRead += len(buffer) else: - if bytesRead: return bytesRead + if bytesRead: + return bytesRead self.connection_lost() return 0 - except socket.error as (err, msg): + except socket.error as xxx_todo_changeme: + # Error codes for nothing to read + (err, msg) = xxx_todo_changeme.args # Error codes for nothing to read if err not in [errno.EAGAIN, errno.EWOULDBLOCK, WSAEWOULDBLOCK]: - if bytesRead: return bytesRead + if bytesRead: + return bytesRead self.connection_error(err, msg) raise return bytesRead - def write_some(self): - if len(self.writeBuf) == 0: return 0 + if len(self.writeBuf) == 0: + return 0 bytesWritten = 0 try: @@ -185,35 +195,37 @@ class Connection: self.writeBuf = self.writeBuf[count:] bytesWritten += count else: - if bytesWritten: return bytesWritten + if bytesWritten: + return bytesWritten self.connection_lost() return 0 - except socket.error as (err, msg): + except socket.error as xxx_todo_changeme1: + # Error codes for write buffer full + (err, msg) = xxx_todo_changeme1.args # Error codes for write buffer full if err not in [errno.EAGAIN, errno.EWOULDBLOCK, WSAEWOULDBLOCK]: - if bytesWritten: return bytesWritten + if bytesWritten: + return bytesWritten self.connection_error(err, msg) raise return bytesWritten - def queue_command(self, command): - if debug: print('command: ' + command) + if debug: + print(('command: ' + command)) self.writeBuf += command + '\n' - def parse_message(self, version, type, data): try: - msg = json.loads(data, cls = PYONDecoder) - #if debug: print 'MSG:', type, msg + msg = json.loads(data, cls=PYONDecoder) + # if debug: print 'MSG:', type, msg self.messages.append((version, type, msg)) self.last_message = time.time() except Exception as e: - print('ERROR parsing PyON message: %s: %s' - % (str(e), data.encode('string_escape'))) - + print(('ERROR parsing PyON message: %s: %s' + % (str(e), data.encode('string_escape')))) def parse(self): start = self.readBuf.find('\nPyON ') @@ -240,7 +252,6 @@ class Connection: return False - def update(self): try: try: @@ -250,13 +261,15 @@ class Connection: self.open() elif self.last_connect + 60 < time.time(): - self.close() # Retry connect + self.close() # Retry connect - if not self.is_connected(): return + if not self.is_connected(): + return self.write_some() if self.read_some(): - while self.parse(): continue + while self.parse(): + continue # Handle special case for OSX disconnect except socket.error as e: @@ -264,11 +277,12 @@ class Connection: self.fail_reason = 'refused' self.close() - else: raise + else: + raise except Exception as e: - print('ERROR on connection to %s:%d: %s' % ( - self.address, self.port, e)) + print(('ERROR on connection to %s:%d: %s' % ( + self.address, self.port, e))) # Timeout connection if self.connected and self.last_message and \ @@ -277,18 +291,17 @@ class Connection: self.close() - if __name__ == '__main__': init = ['updates add 0 1 $options', 'updates add 1 1 $queue-info', 'updates add 2 1 $slot-info'] - conn = Connection(init_commands = init) + conn = Connection(init_commands=init) while True: conn.update() for version, type, data in conn.messages: - print('PyON %d %s:\n' % (version, type), data) + print(('PyON %d %s:\n' % (version, type), data)) conn.messages = [] time.sleep(0.1) diff --git a/fah/FAHControl.py b/fah/FAHControl.py index 223f5a8..fb2ea4b 100644 --- a/fah/FAHControl.py +++ b/fah/FAHControl.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import sys import time @@ -25,16 +25,23 @@ import re import traceback import platform import urllib +import request +import urllib.parse +import urllib.error import gtk import glib import pygtk -pygtk.require("2.0") import pango import webbrowser import shlex import subprocess -from wraplabel import WrapLabel +from .wraplabel import WrapLabel +from fah import * +from fah.db import * +from fah.util import * + +pygtk.require("2.0") # OSX integration if sys.platform == 'darwin': @@ -46,10 +53,6 @@ if sys.platform == 'darwin': from gtkosx_application import gtkosx_application_get_resource_path \ as quartz_application_get_resource_path -from fah import * -from fah.db import * -from fah.util import * - def set_tree_view_font(widget, font): for widget in iterate_container(widget): @@ -67,15 +70,17 @@ def get_tree_selection(tree_view): return selection -def remove_tree_selection(tree_view, callback = None): +def remove_tree_selection(tree_view, callback=None): for path, iter in get_tree_selection(tree_view): - if callback is not None: callback(path, iter) + if callback is not None: + callback(path, iter) tree_view.get_model().remove(iter) def osx_version(): """ returns osx version as tuple of integers """ - if sys.platform != 'darwin': return None + if sys.platform != 'darwin': + return None try: ver = tuple([int(x) for x in platform.mac_ver()[0].split('.')]) except Exception as e: @@ -91,17 +96,18 @@ def osx_add_GtkApplicationDelegate_methods(): # reopen event or dock icon clicked # restore windows if hidden controller = FAHControl.instance - if controller is not None: controller.restore() + if controller is not None: + controller.restore() return True try: import objc cls = objc.lookUpClass('GtkApplicationDelegate') sig1 = '%s%s%s%s%s' % (objc._C_NSBOOL, objc._C_ID, objc._C_SEL, - objc._C_ID, objc._C_NSBOOL) + objc._C_ID, objc._C_NSBOOL) objc.classAddMethods(cls, [ objc.selector( applicationShouldHandleReopen_hasVisibleWindows_, - signature = sig1) + signature=sig1) ]) except Exception as e: print(e) @@ -148,7 +154,7 @@ class FAHControl(SingleAppServer): instance = None - def __init__(self, glade = 'FAHControl.glade'): + def __init__(self, glade='FAHControl.glade'): SingleAppServer.__init__(self) self.__class__.instance = self @@ -183,7 +189,7 @@ class FAHControl(SingleAppServer): self.osx_app = OSXApplication() self.osx_app.set_use_quartz_accelerators(True) self.osx_version = osx_version() - self.is_old_gtk = gtk.gtk_version < (2,24) + self.is_old_gtk = gtk.gtk_version < (2, 24) osx_add_GtkApplicationDelegate_methods() # URI hook @@ -196,9 +202,11 @@ class FAHControl(SingleAppServer): # Load standard key bindings for Mac and disable mnemonics resources = quartz_application_get_resource_path() rcfile = os.path.join(resources, 'themes/Mac/gtk-2.0-key/gtkrc') - if os.path.exists(rcfile): gtk.rc_parse(rcfile) + if os.path.exists(rcfile): + gtk.rc_parse(rcfile) rcfile = os.path.join(os.path.expanduser("~"), '.FAHClient/gtkrc') - if os.path.exists(rcfile): gtk.rc_parse(rcfile) + if os.path.exists(rcfile): + gtk.rc_parse(rcfile) self.mono_font = pango.FontDescription('Monospace') small_font = pango.FontDescription('Sans 8') @@ -206,14 +214,16 @@ class FAHControl(SingleAppServer): gtk.window_set_default_icon(get_icon('small')) # Filter glade - if len(glade) < 1024: glade = open(glade, 'r').read() + if len(glade) < 1024: + glade = open(glade, 'r').read() glade = re.subn('class="GtkLabel" id="wlabel', 'class="WrapLabel" id="wlabel', glade)[0] if sys.platform == 'darwin': # glade editor strips accel modifiers. add if missing glade = re.subn('accelerator *key="comma" *signal', - 'accelerator key="comma" modifiers="GDK_META_MASK" signal', - glade)[0] + 'accelerator key="comma" \ + modifiers="GDK_META_MASK" signal', + glade)[0] # Build GUI self.builder = builder = gtk.Builder() @@ -233,13 +243,14 @@ class FAHControl(SingleAppServer): # Panes self.panes = WidgetMap(self.window, 'paned') - for name, pane in self.panes.items(): + for name, pane in list(self.panes.items()): prop = name + '_position' # Load current value if self.db.has(prop): value = int(self.db.get(prop)) - if value and value < 100: value = 100 # mimimum if not hidden + if value and value < 100: + value = 100 # mimimum if not hidden pane.set_position(value) pane.connect('notify::position', self.store_property, prop) @@ -270,20 +281,25 @@ class FAHControl(SingleAppServer): 'about': self.about_dialog, 'main': self.window, } - for name, win in self.windows.items(): + for name, win in list(self.windows.items()): if self.db.has(name + '_width'): width = int(self.db.get(name + '_width')) - else: width = -1 + else: + width = -1 if self.db.has(name + '_height'): height = int(self.db.get(name + '_height')) - else: height = -1 + else: + height = -1 if name == 'main': - if 0 < width and width < 600: width = 600 - if 0 < height and height < 400: height = 400 + if 0 < width and width < 600: + width = 600 + if 0 < height and height < 400: + height = 400 - if 100 <= width and 100 <= height: win.resize(width, height) + if 100 <= width and 100 <= height: + win.resize(width, height) win.connect('configure_event', self.store_dimensions, name) @@ -301,7 +317,8 @@ class FAHControl(SingleAppServer): # Preferences self.theme_list = self.load_themes() widget = builder.get_object('theme_list') - for theme in self.theme_list: widget.append(theme) + for theme in self.theme_list: + widget.append(theme) # Client list self.client_hpane = builder.get_object('client_hpane') @@ -397,23 +414,28 @@ class FAHControl(SingleAppServer): self.log_follow = builder.get_object('log_follow') # Widget maps - self.client_entries = WidgetMap(self.client_dialog, '_entry') + self.client_entries = \ + WidgetMap(self.client_dialog, '_entry') self.client_option_widgets = \ WidgetMap(self.client_config_notebook, '_option') - self.client_config_tabs = WidgetMap(self.client_config_notebook, '_tab') - self.slot_option_widgets = WidgetMap(self.slot_dialog, '_option') + self.client_config_tabs = \ + WidgetMap(self.client_config_notebook, '_tab') + self.slot_option_widgets = \ + WidgetMap(self.slot_dialog, '_option') roots = [self.window, self.client_dialog] - self.preference_widgets = WidgetMap(self.preferences_dialog, '_pref') + self.preference_widgets = \ + WidgetMap(self.preferences_dialog, '_pref') widget = builder.get_object('advanced_unit_frame') - self.queue_widgets = WidgetMap(widget, None, 'queue_') + self.queue_widgets = \ + WidgetMap(widget, None, 'queue_') # Stats prefs self.donor_stats_pref = self.preference_widgets['donor_stats'] self.team_stats_pref = self.preference_widgets['team_stats'] self.donor_stats_list = builder.get_object('donor_stats_links_list') - map(self.donor_stats_list.append, self.donor_stats_links) + list(map(self.donor_stats_list.append, self.donor_stats_links)) self.team_stats_list = builder.get_object('team_stats_links_list') - map(self.team_stats_list.append, self.team_stats_links) + list(map(self.team_stats_list.append, self.team_stats_links)) # OSX integration if sys.platform == 'darwin': @@ -439,7 +461,7 @@ class FAHControl(SingleAppServer): widget = builder.get_object(name) widget.set_property('visible', False) - if self.osx_version >= (10,7): + if self.osx_version >= (10, 7): # remove broken window resize grip self.status_bar.set_property('has-resize-grip', False) self.time_label.set_property('xpad', 6) @@ -478,7 +500,7 @@ class FAHControl(SingleAppServer): # Connect signals builder.connect_signals(self) - self.builder = builder = None # Discard builder + self.builder = builder = None # Discard builder self.client_dialog.client = None @@ -500,13 +522,12 @@ class FAHControl(SingleAppServer): self.window.connect('notify::is-active', self.on_window_is_active) - # Main loop def run(self): self.quitting = False if sys.platform != 'darwin': - self.check_clients() # Slightly faster load? + self.check_clients() # Slightly faster load? self.restore() @@ -526,7 +547,7 @@ class FAHControl(SingleAppServer): self.osx_app.ready() - if self.osx_version >= (10,7) and self.osx_version < (10,9): + if self.osx_version >= (10, 7) and self.osx_version < (10, 9): self.osx_window_focus_workaround() try: @@ -546,7 +567,6 @@ class FAHControl(SingleAppServer): gtk.main() - # Util def osx_add_to_menu(self, widget): if isinstance(widget, gtk.SeparatorMenuItem): @@ -563,7 +583,6 @@ class FAHControl(SingleAppServer): item.connect('activate', activate_item, widget) self.osx_app.add_app_menu_item(self.osx_group, item) - def osx_create_app_menu(self, widgets): i = 0 for widget in widgets: @@ -577,7 +596,6 @@ class FAHControl(SingleAppServer): self.osx_app.insert_app_menu_item(widget, i) i += 1 - def osx_create_dock_menu(self, widgets): menu = gtk.Menu() for widget in widgets: @@ -595,7 +613,6 @@ class FAHControl(SingleAppServer): # retain menu, or it won't work self.osx_dock_menu = menu - def osx_window_focus_workaround(self): # osx 10.7+, part of Trac #793, not resolved by gtk 2.24.10 # only thing that works is clicking FAHControl icon in Dock @@ -606,14 +623,12 @@ class FAHControl(SingleAppServer): try: subprocess.Popen(cmd) except Exception as e: - print(e, ':', ' '.join(cmd)) - + print((e, ':', ' '.join(cmd))) def connect_option_cell(self, name, model, col): cell = self.builder.get_object(name) cell.connect('edited', self.on_option_edit, model, col) - def connect_option_view(self, prefix): tree = self.builder.get_object(prefix + 'option_tree_view') model = self.builder.get_object(prefix + 'option_list') @@ -624,40 +639,42 @@ class FAHControl(SingleAppServer): return tree, model # Timer functions - def set_update_timer_interval(self, interval = None): + def set_update_timer_interval(self, interval=None): if self.timer_id is not None: glib.source_remove(self.timer_id) self.timer_id = None if interval and int(interval) > 0: self.timer_id = gobject.timeout_add(interval, self.on_timer) - return False # stop if timer callback - + return False # stop if timer callback def check_clients(self): # Make sure there is a selected client - if not len(self.selected_clients): self.select_first_client() + if not len(self.selected_clients): + self.select_first_client() # Update clients - for client in self.clients.values(): client.update(self) + for client in list(self.clients.values()): + client.update(self) # (De)activate client if self.active_client: if not self.active_client.is_updated(): self.deactivate_client() - else: self.activate_client() # Try to activate + else: + self.activate_client() # Try to activate # Check if active and online if self.active_client and self.active_client.is_online(): self.client_notebook.set_sensitive(True) - else: self.client_notebook.set_sensitive(False) + else: + self.client_notebook.set_sensitive(False) # Update status bar if self.status_clear_time and self.status_clear_time < time.time(): self.status_bar.pop(0) self.status_clear_time = None - def on_timer(self): try: # Update clock @@ -668,10 +685,13 @@ class FAHControl(SingleAppServer): # Update ppd ppd = 0 - for client in self.clients.values(): ppd += client.ppd + for client in list(self.clients.values()): + ppd += client.ppd label = 'Total Estimated Points Per Day: ' - if int(ppd): label += '%d' % int(ppd) - else: label += 'Unknown' + if int(ppd): + label += '%d' % int(ppd) + else: + label += 'Unknown' self.ppd_label.set_text(label) self.last_clock = now @@ -693,12 +713,12 @@ class FAHControl(SingleAppServer): except: traceback.print_exc() - return True # Keep running - + return True # Keep running # Actions def quit(self): - if self.quitting: return + if self.quitting: + return self.quitting = True gtk.main_quit() @@ -707,44 +727,40 @@ class FAHControl(SingleAppServer): self.set_update_timer_interval(0) - for client in self.clients.values(): client.close() + for client in list(self.clients.values()): + client.close() try: self.db.flush_queued() except Exception as e: print(e) - sys.exit(0) # Force shutdown - + sys.exit(0) # Force shutdown def set_status(self, text): self.status_bar.pop(0) self.status_bar.push(0, text) self.status_clear_time = time.time() + 10 - # OSX signals def app_did_become_active(self, app): self.restore() - def app_will_terminate(self, app): # Calxalot: Probably don't need both this and should_block, but I can # imagine the app quitting without asking. Note that if we reach this, # we exit after quit() and never return to run() self.quit() - def app_should_block_terminate(self, app): self.quit() return False - # Preference methods def load_theme(self, theme): for name, rc in self.theme_list: if theme == name: - print('Loading theme %r' % theme) + print(('Loading theme %r' % theme)) settings = gtk.settings_get_default() @@ -760,7 +776,6 @@ class FAHControl(SingleAppServer): break - def load_themes(self): paths = get_theme_dirs() unique = set() @@ -768,42 +783,50 @@ class FAHControl(SingleAppServer): default_rc = None for path in paths: - if not os.path.exists(path): continue + if not os.path.exists(path): + continue for name in os.listdir(path): - if name in unique: continue + if name in unique: + continue rc = path + '/' + name + '/gtk-2.0/gtkrc' if os.path.exists(rc): unique.add(name) if sys.platform == 'win32' and \ name == 'Windows-Default' and default_rc is None: default_rc = rc - else: list.append([name, rc]) + else: + list.append([name, rc]) - list.sort(key = lambda x: x[0]) + list.sort(key=lambda x: x[0]) return [['Default', default_rc]] + list - def get_pref(self, name): widget = self.preference_widgets[name] return get_widget_str_value(widget) - def get_viz_render_mode(self): value = self.get_pref('viz_render_mode') - if value == 'Space Filling': return 1 - if value == 'Ball And Stick': return 2 - if value == 'Stick': return 3 - if value == 'Advanced Space Filling': return 4 - if value == 'Advanced Ball And Stick': return 5 - if value == 'Advanced Stick': return 6 - if value == 'Cartoon Space Filling': return 7 - if value == 'Cartoon Ball And Stick': return 8 + if value == 'Space Filling': + return 1 + if value == 'Ball And Stick': + return 2 + if value == 'Stick': + return 3 + if value == 'Advanced Space Filling': + return 4 + if value == 'Advanced Ball And Stick': + return 5 + if value == 'Advanced Stick': + return 6 + if value == 'Cartoon Space Filling': + return 7 + if value == 'Cartoon Ball And Stick': + return 8 return 1 - def preferences_set(self): # URLs for pref in ['donor', 'team']: @@ -815,7 +838,8 @@ class FAHControl(SingleAppServer): custom_uri = self.get_pref(pref + '_stats') entry.set_text(custom_uri) - if link == 'Custom': button.set_uri(custom_uri) + if link == 'Custom': + button.set_uri(custom_uri) else: model = combo.get_model() iter = model.get_iter_first() @@ -826,37 +850,49 @@ class FAHControl(SingleAppServer): break iter = model.iter_next(iter) - def preferences_load(self): # Preferences dialog - for name, widget in self.preference_widgets.items(): + for name, widget in list(self.preference_widgets.items()): value = None if self.db.has(name): value = self.db.get(name) - if name == 'theme': self.load_theme(value) + if name == 'theme': + self.load_theme(value) - elif name == 'theme': value = 'Default' - elif name == 'viz_command': value = 'FAHViewer' - elif name == 'viz_fullscreen': value = 'False' - elif name == 'viz_width': value = '800' - elif name == 'viz_height': value = '600' - elif name == 'viz_render_mode': value = 'Advanced Space Filling' - elif name == 'viz_cycle_snapshots': value = 'True' - elif name == 'donor_stats': value = '' - elif name == 'team_stats': value = '' - elif name == 'donor_stats_link': value = 'Folding@home' - elif name == 'team_stats_link': value = 'Folding@home' - else: raise Exception('Unknown preference widget "%s"' % name) + elif name == 'theme': + value = 'Default' + elif name == 'viz_command': + value = 'FAHViewer' + elif name == 'viz_fullscreen': + value = 'False' + elif name == 'viz_width': + value = '800' + elif name == 'viz_height': + value = '600' + elif name == 'viz_render_mode': + value = 'Advanced Space Filling' + elif name == 'viz_cycle_snapshots': + value = 'True' + elif name == 'donor_stats': + value = '' + elif name == 'team_stats': + value = '' + elif name == 'donor_stats_link': + value = 'Folding@home' + elif name == 'team_stats_link': + value = 'Folding@home' + else: + raise Exception('Unknown preference widget "%s"' % name) - if value is not None: set_widget_str_value(widget, value) + if value is not None: + set_widget_str_value(widget, value) # Update self.preferences_set() - def preferences_dialog_init(self): - for name, widget in self.preference_widgets.items(): + for name, widget in list(self.preference_widgets.items()): if self.db.has(name): value = self.db.get(name) set_widget_str_value(widget, value) @@ -866,40 +902,42 @@ class FAHControl(SingleAppServer): combo = self.preference_widgets[pref + '_stats_link'] entry.set_sensitive(combo.get_active_text() == 'Custom') - def preferences_save(self): - for name, widget in self.preference_widgets.items(): + for name, widget in list(self.preference_widgets.items()): value = get_widget_str_value(widget) - if value is None: self.db.clear(name, False) - else: self.db.set(name, value, False) + if value is None: + self.db.clear(name, False) + else: + self.db.set(name, value, False) self.db.commit() - # Client methods def select_first_client(self): iter = self.client_list.get_iter_first() - if iter: self.client_tree.get_selection().select_iter(iter) - + if iter: + self.client_tree.get_selection().select_iter(iter) def activate_client(self): - if self.active_client: return - if not len(self.selected_clients): return + if self.active_client: + return + if not len(self.selected_clients): + return # Check that all selected clients are active for client in self.selected_clients: - if not client.is_updated(): return + if not client.is_updated(): + return # Activate client(s) for client in self.selected_clients: self.active_client = client self.active_client.update_status_ui(self) - break # TODO only supporting one active client right now + break # TODO only supporting one active client right now self.last_clients_update = 0 self.update_client_status() - def deactivate_client(self): if self.active_client: self.active_client.reset_status_ui(self) @@ -908,7 +946,6 @@ class FAHControl(SingleAppServer): self.client_label.set_markup('Client: inactive') self.update_client_status() - def update_client_status(self): if len(self.selected_clients): client = list(self.selected_clients)[0] @@ -920,24 +957,24 @@ class FAHControl(SingleAppServer): if self.active_client and self.active_client.config.get_running(): text += ' Running' - else: text += ' Inactive' + else: + text += ' Inactive' - else: text = 'Client: no client selected' + else: + text = 'Client: no client selected' self.client_label.set_markup(text) if self.client_dialog.client is not None: self.client_config_label.set_markup(text) - def save_clients(self): self.db.delete('clients') - for client in self.clients.values(): + for client in list(self.clients.values()): client.save(self.db) self.db.commit() - def update_client_list(self): # update all rows, whether selected/active or not try: @@ -954,8 +991,7 @@ class FAHControl(SingleAppServer): iter = self.client_list.iter_next(iter) except Exception as e: print(e) - return False # no timer repeat - + return False # no timer repeat def resort_client_list(self): ibyname_old = {} @@ -971,30 +1007,29 @@ class FAHControl(SingleAppServer): name = client.name i = ibyname_old.get(name) if i is None: - print('unable to resort client list: unknown name %s' % name) + print(('unable to resort client list: unknown name %s' % name)) return new_order.append(i) self.client_list.reorder(new_order) - return False # don't repeat if timer callback + return False # don't repeat if timer callback - - def sorted_clients(self, unsorted_clients = None): + def sorted_clients(self, unsorted_clients=None): if unsorted_clients is None: - unsorted_clients = self.clients.values() + unsorted_clients = list(self.clients.values()) # pre-sort by client.name clients = sorted(unsorted_clients, key=lambda c: c.name) # sort local clients first - group0 = [] # client "local" (should only be one, currently) - group1 = [] # other is_local clients (should not be any, currently) - group2 = [] # localhost clients starting with "local" - group3 = [] # other localhost clients - group4 = [] # remote clients (and any local referenced by host name) + group0 = [] # client "local" (should only be one, currently) + group1 = [] # other is_local clients (should not be any, currently) + group2 = [] # localhost clients starting with "local" + group3 = [] # other localhost clients + group4 = [] # remote clients (and any local referenced by host name) for client in clients: is_local = client.is_local() - is_local_addr = client.address in ['localhost','127.0.0.1'] + is_local_addr = client.address in ['localhost', '127.0.0.1'] if is_local and client.name == 'local': group0.append(client) @@ -1009,29 +1044,31 @@ class FAHControl(SingleAppServer): return group0 + group1 + group2 + group3 + group4 - def load_clients(self): clients = [] - for row in self.db.select('clients', orderby = 'name'): + for row in self.db.select('clients', orderby='name'): client = Client(self, - row['name'], row['address'], int(row['port']), row['password']) + row['name'], + row['address'], + int(row['port']), + row['password']) clients.append(client) for client in self.sorted_clients(clients): self.add_client(client) - def clear_clients(self): - for client in self.clients: client.close() + for client in self.clients: + client.close() self.clients.clear() self.clientsByName.clear() self.client_list.clear() - def save_client_config(self, client): try: options, slots = client.config.get_changes(self) - if not (options or slots != ([], [], [])): return True + if not (options or slots != ([], [], [])): + return True # Validate passkey if not self.passkey_validator.is_good(): @@ -1052,47 +1089,49 @@ class FAHControl(SingleAppServer): for client in self.selected_clients: # TODO check returned error count client.save_config(options, slots) - client.update(self) # Slightly faster save + client.update(self) # Slightly faster save # Update password if changed on client if 'password' in options: client.set_password(options['password']) - if 'password!' in options: client.set_password('') + if 'password!' in options: + client.set_password('') return True - except Exception, msg: + except Exception as msg: self.set_status('Save Failed') self.error(msg) return False - def check_duplicate_client_name(self, name): if name in self.clients: - self.error('Client with name "%s" is already in client list' % name) + self.error('Client with name "%s" \ + is already in client list' % name) return True return False - def check_duplicate_client_address(self, address): if address in self.clientsByAddress: - self.error('Client with address "%s" is already in client list' % + self.error('Client with address "%s" \ + is already in client list' % address) return True return False - def update_client(self, client, name, address, port, password): reload = False old_name = client.name - # Check for duplicates + # Check for duplicates if client.name != name: - if self.check_duplicate_client_name(name): return False + if self.check_duplicate_client_name(name): + return False new_address = Client.make_address(address, port) if client.get_address() != new_address: - if self.check_duplicate_client_address(new_address): return False + if self.check_duplicate_client_address(new_address): + return False # Update if client.name != name: @@ -1134,14 +1173,15 @@ class FAHControl(SingleAppServer): return True - def add_client(self, client): name = client.name address = client.get_address() # Check for duplicates - if self.check_duplicate_client_name(name): return False - if self.check_duplicate_client_address(address): return False + if self.check_duplicate_client_name(name): + return False + if self.check_duplicate_client_address(address): + return False # Add it self.clients[name] = client @@ -1150,42 +1190,40 @@ class FAHControl(SingleAppServer): return True - def remove_client(self, client): client.close() del self.clients[client.name] del self.clientsByAddress[client.get_address()] - if client is self.active_client: self.active_client = None - + if client is self.active_client: + self.active_client = None def remove_client_path(self, path, iter): name = self.client_list.get(iter, 0)[0] self.remove_client(self.clients[name]) - def get_selected_clients(self): selection = get_tree_selection(self.client_tree) - names = map(lambda item: self.client_list.get(item[1], 0)[0], selection) + names = [self.client_list.get(item[1], 0)[0] for item in selection] return set(map(self.clients.get, names)) - def edit_client(self, client): self.client_dialog.client = client self.update_client_status() client.load_dialog(self) if self.active_client and self.active_client.is_online(): - for name, widget in self.client_config_tabs.items(): widget.show() + for name, widget in list(self.client_config_tabs.items()): + widget.show() self.client_dialog.config_hidden = False else: - for name, widget in self.client_config_tabs.items(): - if name != 'connection': widget.hide() + for name, widget in list(self.client_config_tabs.items()): + if name != 'connection': + widget.hide() self.client_dialog.config_hidden = True self.open_dialog(self.client_dialog) - def open_dialog(self, dialog): # Hack to make WrapLabel work dims = dialog.get_size() @@ -1193,15 +1231,12 @@ class FAHControl(SingleAppServer): dialog.present() dialog.resize(*dims) - # Slot methods def get_selected_slot_ids(self): selection = get_tree_selection(self.slot_status_tree) - ids = map(lambda x: int(self.slot_status_list.get(x[1], 0)[0]), - selection) + ids = [int(self.slot_status_list.get(x[1], 0)[0]) for x in selection] return ids - # Window methods def get_visible_dialogs(self): dialogs = [] @@ -1211,14 +1246,13 @@ class FAHControl(SingleAppServer): return dialogs - def hide_all_windows(self): self.restore_dialogs = self.get_visible_dialogs() - for dialog in self.restore_dialogs: dialog.hide() + for dialog in self.restore_dialogs: + dialog.hide() self.window.hide() self.window_visible = False - def restore(self): self.window.present() self.window.deiconify() @@ -1230,33 +1264,36 @@ class FAHControl(SingleAppServer): dialog.present() # Restore dialogs - for dialog in self.restore_dialogs: dialog.present() + for dialog in self.restore_dialogs: + dialog.present() self.restore_dialogs = [] - # Messages - def close_error_dialog(self, dialog, id = None, data = None): + def close_error_dialog(self, dialog, id=None, data=None): dialog.destroy() self.error_dialog = None - - def error(self, message, buttons = gtk.BUTTONS_OK, on_response = None, - on_response_data = None): + def error(self, message, buttons=gtk.BUTTONS_OK, on_response=None, + on_response_data=None): message = str(message) # log to terminal window - if sys.exc_info()[2]: traceback.print_exc() - print('ERROR: %s' % message) + if sys.exc_info()[2]: + traceback.print_exc() + print(('ERROR: %s' % message)) # Don't open more than one - if self.error_dialog is not None: return False + if self.error_dialog is not None: + return False - if sys.exc_info()[1]: message += '\n%s' % sys.exc_info()[1] + if sys.exc_info()[1]: + message += '\n%s' % sys.exc_info()[1] # create an error message dialog and display modally to the user dialog = \ gtk.MessageDialog(None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.DIALOG_MODAL | + gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, buttons, message) dialog.connect('close', self.close_error_dialog) @@ -1270,53 +1307,50 @@ class FAHControl(SingleAppServer): return True - # Property signals def store_property(self, widget, property, name): - self.db.set(name, widget.get_property(property.name), queue = True) - + self.db.set(name, widget.get_property(property.name), queue=True) def store_dimensions(self, widget, event, name): x, y, width, height = widget.get_allocation() if 0 <= width and 0 <= height: - self.db.set(name + '_width', width, queue = True); - self.db.set(name + '_height', height, queue = True); - + self.db.set(name + '_width', width, queue=True) + self.db.set(name + '_height', height, queue=True) # Action signals - def on_quit(self, widget, data = None): + def on_quit(self, widget, data=None): self.quit() - - def on_preferences(self, widget, data = None): + def on_preferences(self, widget, data=None): # OSX crashes with out this, but it's a good idea anyway - if not self.window_visible: self.restore() + if not self.window_visible: + self.restore() - if self.get_visible_dialogs(): return + if self.get_visible_dialogs(): + return self.preferences_dialog_init() self.preferences_dialog.present() - def viewer_check(self): if self.viewer is not None and self.viewer.poll() is not None: if self.viewer.returncode and sys.platform == 'darwin': self.error('Failed to launch viewer:\n\n' + self.viewer.stderr.read()) - self.viewer = None # Viewer exited - + self.viewer = None # Viewer exited def viewer_close(self): if self.viewer is not None: try: self.viewer.kill() - self.viewer.wait() # Note: This could cause a hang kill() fails - except: pass + self.viewer.wait() + # Note: This could cause a hang kill() fails + except: + pass self.viewer = None - - def on_viewer(self, widget, data = None): + def on_viewer(self, widget, data=None): self.viewer_close() # Get preferences @@ -1330,13 +1364,16 @@ class FAHControl(SingleAppServer): # Create command line cmd = shlex.split(command) - if not (len(cmd) and len(cmd[0])): cmd = ['FAHViewer'] + if not (len(cmd) and len(cmd[0])): + cmd = ['FAHViewer'] if sys.platform == 'darwin': - if not cmd[0].endswith('.app'): cmd[0] += '.app' + if not cmd[0].endswith('.app'): + cmd[0] += '.app' cmd = ['/usr/bin/open', '-a', cmd[0], '--args'] + cmd[1:] - if fullscreen: cmd.append('--fullscreen') + if fullscreen: + cmd.append('--fullscreen') cmd.append('--width=' + width) cmd.append('--height=' + height) cmd.append('--mode=%d' % mode) @@ -1348,97 +1385,99 @@ class FAHControl(SingleAppServer): cmd.append('--connect=%s:%d' % (address, port)) password = self.active_client.password - if password: cmd.append('--password="%s"' % password) + if password: + cmd.append('--password="%s"' % password) slot = self.active_client.get_selected_slot(self) - if slot is not None: cmd.append('--slot=%d' % slot.id) + if slot is not None: + cmd.append('--slot=%d' % slot.id) debug = True - if debug: print(cmd) + if debug: + print(cmd) try: if sys.platform == 'darwin': - self.viewer = subprocess.Popen(cmd, cwd = get_home_dir(), - bufsize = 4096, stderr=subprocess.PIPE) + self.viewer = subprocess.Popen(cmd, cwd=get_home_dir(), + bufsize=4096, + stderr=subprocess.PIPE) else: - self.viewer = subprocess.Popen(cmd, cwd = get_home_dir()) + self.viewer = subprocess.Popen(cmd, cwd=get_home_dir()) except Exception: self.error('Failed to launch viewer with command:\n\n' + ' '.join(cmd)) - - def on_about(self, widget, data = None): + def on_about(self, widget, data=None): # OSX crashes with out this, but it's a good idea anyway - if not self.window_visible: self.restore() + if not self.window_visible: + self.restore() - if self.get_visible_dialogs(): return False + if self.get_visible_dialogs(): + return False self.open_dialog(self.about_dialog) - - def on_about_close(self, widget, data = None): + def on_about_close(self, widget, data=None): self.about_dialog.hide() - return True # Cancel event - + return True # Cancel event # Window signals - def on_window_destroy(self, widget, data = None): + def on_window_destroy(self, widget, data=None): if sys.platform == 'darwin': self.hide_all_windows() - return True # prevent destroy + return True # prevent destroy self.quit() - - def on_window_delete(self, widget, event, data = None): + def on_window_delete(self, widget, event, data=None): return self.on_window_destroy(widget) - def on_window_is_active(self, window, *args): try: - if window.is_active(): self.update_client_list() - except Exception as e: print(e) - + if window.is_active(): + self.update_client_list() + except Exception as e: + print(e) # Preferences signals - def on_preferences_ok(self, widget, data = None): + def on_preferences_ok(self, widget, data=None): self.preferences_set() self.preferences_save() self.preferences_dialog.hide() - - def on_preferences_cancel(self, widget, data = None): + def on_preferences_cancel(self, widget, data=None): # Reset theme - if self.db.has('theme'): current_theme = self.db.get('theme') - else: current_theme = 'Default' + if self.db.has('theme'): + current_theme = self.db.get('theme') + else: + current_theme = 'Default' if self.get_pref('theme') != current_theme: self.load_theme(current_theme) # FIXME workaround for defect: get_pref takes from dialog widgets self.preferences_dialog_init() self.preferences_dialog.hide() - return True # Cancel event + return True # Cancel event - - def on_theme_pref_changed(self, widget, data = None): + def on_theme_pref_changed(self, widget, data=None): iter = widget.get_active_iter() theme = widget.get_model().get_value(iter, 0) self.load_theme(theme) - # Proxy signals - def on_proxy_enable_toggled(self, widget, data = None): + def on_proxy_enable_toggled(self, widget, data=None): self.proxy_frame.set_sensitive(widget.get_active()) self.proxy_auth_frame.set_sensitive(widget.get_active()) - # Client list signals - def on_client_add_button_clicked(self, widget, data = None): - if self.get_visible_dialogs(): return + def on_client_add_button_clicked(self, widget, data=None): + if self.get_visible_dialogs(): + return # Make client name - for i in xrange(sys.maxint): + for i in range(sys.maxsize): name = 'client%d' % i - if not name in self.clients: break + if name not in self.clients: + break self.client_entries['name'].set_text(name) @@ -1449,48 +1488,49 @@ class FAHControl(SingleAppServer): # Reset dialog self.client_entries['name'].set_sensitive(True) self.client_entries['address'].set_sensitive(True) - for name, widget in self.client_config_tabs.items(): - if name != 'connection': widget.hide() + for name, widget in list(self.client_config_tabs.items()): + if name != 'connection': + widget.hide() self.open_dialog(self.client_dialog) - - def on_client_remove_button_clicked(self, widget, data = None): + def on_client_remove_button_clicked(self, widget, data=None): remove_tree_selection(self.client_tree, self.remove_client_path) self.save_clients() - - def on_configure(self, widget, data = None): - if self.get_visible_dialogs(): return + def on_configure(self, widget, data=None): + if self.get_visible_dialogs(): + return selection = get_tree_selection(self.client_tree) - if not len(selection): return + if not len(selection): + return name = self.client_list.get(selection[0][1], 0)[0] client = self.clients[name] self.edit_client(client) - - def on_client_tree_view_row_activated(self, widget, path, col, data = None): - if self.get_visible_dialogs(): return + def on_client_tree_view_row_activated(self, widget, path, col, data=None): + if self.get_visible_dialogs(): + return iter = self.client_list.get_iter(path) name = self.client_list.get(iter, 0)[0] client = self.clients[name] self.edit_client(client) - - def on_client_selection_changed(self, widget, data = None): + def on_client_selection_changed(self, widget, data=None): self.deactivate_client() self.selected_clients = self.get_selected_clients() # Modify selection - for client in self.clients.values(): + for client in list(self.clients.values()): client.set_selected(client in self.selected_clients) self.update_client_status() - if len(self.clients): self.activate_client() + if len(self.clients): + self.activate_client() self.update_client_list() gobject.timeout_add(5000, self.update_client_list) @@ -1500,28 +1540,23 @@ class FAHControl(SingleAppServer): self.set_update_timer_interval(100) gobject.timeout_add(10000, self.set_update_timer_interval, 500) - # Client options list signals - def on_client_options_add_button_clicked(self, widget, data = None): + def on_client_options_add_button_clicked(self, widget, data=None): self.option_present(self.option_list, self.client_dialog) - - def on_client_options_remove_button_clicked(self, widget, data = None): + def on_client_options_remove_button_clicked(self, widget, data=None): remove_tree_selection(self.option_tree) - # Core options list signals - def on_core_options_add_button_clicked(self, widget, data = None): + def on_core_options_add_button_clicked(self, widget, data=None): self.core_option_entry.set_text('') self.core_options_dialog.set_transient_for(self.client_dialog) self.core_options_dialog.present() - - def on_core_options_remove_button_clicked(self, widget, data = None): + def on_core_options_remove_button_clicked(self, widget, data=None): remove_tree_selection(self.core_option_tree) - - def on_core_options_ok(self, widget, data = None): + def on_core_options_ok(self, widget, data=None): option = self.core_option_entry.get_text().strip() if not option: self.error('Invalid option') @@ -1530,14 +1565,12 @@ class FAHControl(SingleAppServer): self.core_option_list.append([option]) self.core_options_dialog.hide() - - def on_core_options_cancel(self, widget, data = None): + def on_core_options_cancel(self, widget, data=None): self.core_options_dialog.hide() - return True # Cancel event - + return True # Cancel event # Client dialog signals - def on_client_ok(self, widget, data = None): + def on_client_ok(self, widget, data=None): name = self.client_entries['name'].get_text() address = self.client_entries['address'].get_text() port = self.client_entries['port'].get_text() @@ -1557,7 +1590,7 @@ class FAHControl(SingleAppServer): port = int(port) - if self.client_dialog.client: # Existing client + if self.client_dialog.client: # Existing client client = self.client_dialog.client config_hidden = self.client_dialog.config_hidden @@ -1569,91 +1602,77 @@ class FAHControl(SingleAppServer): self.client_dialog.hide() self.resort_client_list() - else: # New client + else: # New client client = Client(self, name, address, port, password) if self.add_client(client): self.save_clients() self.client_dialog.hide() self.resort_client_list() - - def on_client_cancel(self, widget, data = None): + def on_client_cancel(self, widget, data=None): self.client_dialog.hide() - return True # Cancel event - + return True # Cancel event # Folding power signals - def on_fold_button_clicked(self, widget, data = None): + def on_fold_button_clicked(self, widget, data=None): self.active_client.unpause() - - def on_pause_button_clicked(self, widget, data = None): + def on_pause_button_clicked(self, widget, data=None): self.active_client.pause() - - def on_finish_button_clicked(self, widget, data = None): + def on_finish_button_clicked(self, widget, data=None): self.active_client.finish() - - def on_folding_power_change_value(self, widget, scroll, value, data = None): + def on_folding_power_change_value(self, widget, scroll, value, data=None): # Clamp slider to integer increments value = int(round(value)) if self.folding_power.get_value() != value: self.folding_power.set_value(value) return True - - def on_folding_power_value_changed(self, widget, data = None): + def on_folding_power_value_changed(self, widget, data=None): if not self.folding_power_changing and self.active_client: power = self.folding_power_levels[int(widget.get_value())] self.active_client.set_power(power) - - def on_folding_power_button_press(self, widget, data = None): + def on_folding_power_button_press(self, widget, data=None): self.folding_power_changing = True - - def on_folding_power_button_release(self, widget, data = None): + def on_folding_power_button_release(self, widget, data=None): if self.active_client: power = self.folding_power_levels[int(widget.get_value())] self.active_client.set_power(power) self.folding_power_changing = False - # User status signals - def on_team_stats_link_changed(self, widget, data = None): + def on_team_stats_link_changed(self, widget, data=None): iter = widget.get_active_iter() model = widget.get_model() text = model.get_value(iter, 0) self.team_stats_pref.set_sensitive(text == 'Custom') - - def on_donor_stats_link_changed(self, widget, data = None): + def on_donor_stats_link_changed(self, widget, data=None): iter = widget.get_active_iter() model = widget.get_model() text = model.get_value(iter, 0) self.donor_stats_pref.set_sensitive(text == 'Custom') - # Queue tree signals - def on_queue_tree_cursor_changed(self, widget, data = None): + def on_queue_tree_cursor_changed(self, widget, data=None): if self.active_client: self.active_client.config.select_queue_slot(self) - # Slot list signals - def on_slot_add_button_clicked(self, widget, data = None): + def on_slot_add_button_clicked(self, widget, data=None): SlotConfig.clear_dialog(self) self.slot_dialog.slot_iter = None self.open_dialog(self.slot_dialog) - - def on_slot_remove_button_clicked(self, widget, data = None): + def on_slot_remove_button_clicked(self, widget, data=None): remove_tree_selection(self.slot_tree) - - def on_slot_edit_button_clicked(self, widget, data = None): + def on_slot_edit_button_clicked(self, widget, data=None): selection = get_tree_selection(self.slot_tree) if selection: @@ -1663,47 +1682,41 @@ class FAHControl(SingleAppServer): self.slot_dialog.slot_iter = iter self.open_dialog(self.slot_dialog) - - def on_slot_tree_view_row_activated(self, widget, path, col, data = None): + def on_slot_tree_view_row_activated(self, widget, path, col, data=None): iter = self.slot_list.get_iter(path) slot = self.slot_list.get(iter, 2)[0].slot slot.load_dialog(self) self.slot_dialog.slot_iter = iter self.open_dialog(self.slot_dialog) - - # Slot dialog signals - def on_slot_ok(self, widget, data = None): + def on_slot_ok(self, widget, data=None): if self.slot_dialog.slot_iter is None: slot = SlotConfig() slot.save_dialog(self) - slot.add_to_ui(self) # Add to list + slot.add_to_ui(self) # Add to list else: iter = self.slot_dialog.slot_iter id, type, wrapper = self.slot_list.get(iter, 0, 1, 2) slot = wrapper.slot slot.save_dialog(self) - if slot.type != type: self.slot_list.set(iter, 1, slot.type) + if slot.type != type: + self.slot_list.set(iter, 1, slot.type) self.slot_dialog.hide() - - def on_slot_cancel(self, widget, data = None): + def on_slot_cancel(self, widget, data=None): self.slot_dialog.hide() - return True # Cancel event - + return True # Cancel event # Slot options list signals - def on_slot_options_add_button_clicked(self, widget, data = None): + def on_slot_options_add_button_clicked(self, widget, data=None): self.option_present(self.slot_option_list, self.slot_dialog) - - def on_slot_options_remove_button_clicked(self, widget, data = None): + def on_slot_options_remove_button_clicked(self, widget, data=None): remove_tree_selection(self.slot_option_tree) - # Options edit def check_option_name(self, name): if not re.match(r'^[a-zA-Z][\w-]*$', name): @@ -1711,20 +1724,19 @@ class FAHControl(SingleAppServer): return False return True - def on_option_edit(self, cell, path, text, model, column): - if column == 0 and not self.check_option_name(text): return - if model[path][column] != text: model[path][column] = text + if column == 0 and not self.check_option_name(text): + return + if model[path][column] != text: + model[path][column] = text - - def on_core_option_cell_edited(self, cell, path, text, data = None): + def on_core_option_cell_edited(self, cell, path, text, data=None): if not text: self.error('Invalid option') return if self.core_option_list[path][0] != text: self.core_option_list[path][0] = text - # Options dialog signals def option_present(self, model, parent): self.option_name_entry.set_text('') @@ -1733,106 +1745,95 @@ class FAHControl(SingleAppServer): self.options_dialog.set_transient_for(parent) self.options_dialog.present() - - def on_options_ok(self, widget, data = None): + def on_options_ok(self, widget, data=None): name = self.option_name_entry.get_text().strip() value = self.option_value_entry.get_text() - if not self.check_option_name(name): return + if not self.check_option_name(name): + return self.options_dialog.option_model.append([name, value]) self.options_dialog.hide() - - def on_options_cancel(self, widget, data = None): + def on_options_cancel(self, widget, data=None): self.options_dialog.hide() - return True # Cancel event - + return True # Cancel event # Configure dialog signals - def on_configure_ok(self, widget, data = None): + def on_configure_ok(self, widget, data=None): self.configure_dialog.hide() if self.active_client: # Select identity tab self.client_config_notebook.set_current_page(1) self.edit_client(self.active_client) - return True # Cancel event + return True # Cancel event - - def on_configure_cancel(self, widget, data = None): + def on_configure_cancel(self, widget, data=None): self.configure_dialog.hide() if self.active_client: # Fold anonymously self.active_client.conn.queue_command('save') - return True # Cancel event - + return True # Cancel event # Slot status tree signals - def on_slot_status_tree_cursor_changed(self, widget, data = None): + def on_slot_status_tree_cursor_changed(self, widget, data=None): if self.active_client: self.active_client.config.select_slot(self) - def on_slot_status_tree_view_button_release_event(self, widget, event, - data = None): - if event.button != 3: return + data=None): + if event.button != 3: + return idle = self.active_client.get_selected_slot(self).idle self.idle_slot_item.set_active(idle) - self.slot_menu.popup(None, None, None, button = event.button, - activate_time = event.time, data = data) + self.slot_menu.popup(None, None, None, button=event.button, + activate_time=event.time, data=data) - - def on_unpause_slot_item_activate(self, widget, data = None): + def on_unpause_slot_item_activate(self, widget, data=None): for id in self.get_selected_slot_ids(): self.active_client.unpause(id) - - def on_pause_slot_item_activate(self, widget, data = None): + def on_pause_slot_item_activate(self, widget, data=None): for id in self.get_selected_slot_ids(): self.active_client.pause(id) - - def on_idle_slot_item_toggled(self, widget, data = None): + def on_idle_slot_item_toggled(self, widget, data=None): for id in self.get_selected_slot_ids(): - if widget.get_active(): self.active_client.on_idle(id) - else: self.active_client.always_on(id) + if widget.get_active(): + self.active_client.on_idle(id) + else: + self.active_client.always_on(id) - - def on_finish_slot_item_activate(self, widget, data = None): + def on_finish_slot_item_activate(self, widget, data=None): for id in self.get_selected_slot_ids(): self.active_client.finish(id) - # Log signals - def on_download_log_clicked(self, widget, data = None): + def on_download_log_clicked(self, widget, data=None): self.active_client.refresh_log() self.log.set_text('') - - def on_copy_log_clicked(self, widget, data = None): + def on_copy_log_clicked(self, widget, data=None): log = self.log text = log.get_text(log.get_start_iter(), log.get_end_iter()) gtk.Clipboard().set_text(text) + def on_clear_log_clicked(self, widget, data=None): + if self.active_client: + self.active_client.config.log_clear(self) - def on_clear_log_clicked(self, widget, data = None): - if self.active_client: self.active_client.config.log_clear(self) - - - def on_update_log(self, widget, data = None): - if self.active_client: self.active_client.config.update_log(self) - + def on_update_log(self, widget, data=None): + if self.active_client: + self.active_client.config.update_log(self) # Scale value formatting - def on_cpu_usage_scale_format_value(self, widget, value, data = None): + def on_cpu_usage_scale_format_value(self, widget, value, data=None): return '%d%%' % value - - def on_checkpoint_scale_format_value(self, widget, value, data = None): + def on_checkpoint_scale_format_value(self, widget, value, data=None): return '%d min.' % value - - def on_uri_hook(self, widget, url, data = None): - keys = {'donor': urllib.quote(self.donor_info.get_label()), - 'team': urllib.quote(self.team_info.get_label())} + def on_uri_hook(self, widget, url, data=None): + keys = {'donor': urllib.parse.quote(self.donor_info.get_label()), + 'team': urllib.parse.quote(self.team_info.get_label())} webbrowser.open(url % keys) diff --git a/fah/Icon.py b/fah/Icon.py index 3de57f4..9ad1fbc 100644 --- a/fah/Icon.py +++ b/fah/Icon.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import gtk @@ -34,10 +34,14 @@ def get_icon(name): global icons if icons[name] is None: - if name == 'tiny': icons[name] = load_rgba_pixbuf(icon_24_raw, 24) - elif name == 'small': icons[name] = load_rgba_pixbuf(icon_32_raw, 32) - elif name == 'medium': icons[name] = load_rgba_pixbuf(icon_64_raw, 64) - else: icons[name] = load_rgba_pixbuf(icon_128_raw, 128) + if name == 'tiny': + icons[name] = load_rgba_pixbuf(icon_24_raw, 24) + elif name == 'small': + icons[name] = load_rgba_pixbuf(icon_32_raw, 32) + elif name == 'medium': + icons[name] = load_rgba_pixbuf(icon_64_raw, 64) + else: + icons[name] = load_rgba_pixbuf(icon_128_raw, 128) return icons[name] @@ -60,7 +64,8 @@ def get_viewer_icon(name): viewer_icons[name] = load_rgba_pixbuf(viewer_icon_32_raw, 32) elif name == 'medium': viewer_icons[name] = load_rgba_pixbuf(viewer_icon_64_raw, 64) - else: viewer_icons[name] = load_rgba_pixbuf(viewer_icon_128_raw, 128) + else: + viewer_icons[name] = load_rgba_pixbuf(viewer_icon_128_raw, 128) return viewer_icons[name] diff --git a/fah/SlotConfig.py b/fah/SlotConfig.py index 55bcd01..6781831 100644 --- a/fah/SlotConfig.py +++ b/fah/SlotConfig.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import gtk import gobject @@ -29,8 +29,8 @@ from fah.util import status_to_color class SlotConfig: def __init__( - self, id = -1, status = None, description = None, reason = None, - idle = False, options = {}, **kw): + self, id=-1, status=None, description=None, reason=None, + idle=False, options={}, **kw): self.id = int(id) self.status = status @@ -39,28 +39,30 @@ class SlotConfig: self.idle = idle self.options = options - if status is not None: self.status = status.title() - if description is None: self.description = 'cpu' + if status is not None: + self.status = status.title() + if description is None: + self.description = 'cpu' # Type - if self.description.startswith('cpu'): self.type = 'cpu' - elif self.description.startswith('gpu'): self.type = 'gpu' - else: raise Exception('Invalid slot type "%s"' % description) - + if self.description.startswith('cpu'): + self.type = 'cpu' + elif self.description.startswith('gpu'): + self.type = 'gpu' + else: + raise Exception('Invalid slot type "%s"' % description) def add_to_ui(self, app): wrapper = gobject.GObject() wrapper.slot = copy.deepcopy(self) app.slot_list.append((self.id, self.type, wrapper)) - @staticmethod def clear_dialog(app): app.slot_option_list.clear() app.slot_type_cpu.set_active(True) app.slot_option_widgets['cpus'].set_value(-1) - def save_dialog(self, app): self.options = {} @@ -68,37 +70,42 @@ class SlotConfig: self.type = 'cpu' cpus = str(int(app.slot_option_widgets['cpus'].get_value())) - if int(cpus): self.options['cpus'] = cpus + if int(cpus): + self.options['cpus'] = cpus elif app.slot_type_gpu.get_active(): self.type = 'gpu' # Extra options - def add_option(model, path, iter, data = None): + def add_option(model, path, iter, data=None): name = model.get(iter, 0)[0] value = model.get(iter, 1)[0] self.options[name] = value app.slot_option_list.foreach(add_option) - def load_dialog(self, app): used = set() # Type - if self.type == 'cpu': app.slot_type_cpu.set_active(True) - elif self.type == 'gpu': app.slot_type_gpu.set_active(True) - else: raise Exception('Invalid slot type "%s"' % self.type) + if self.type == 'cpu': + app.slot_type_cpu.set_active(True) + elif self.type == 'gpu': + app.slot_type_gpu.set_active(True) + else: + raise Exception('Invalid slot type "%s"' % self.type) used.add('gpu') # SMP - if 'cpus' in self.options: cpus = float(self.options['cpus']) - else: cpus = -1 + if 'cpus' in self.options: + cpus = float(self.options['cpus']) + else: + cpus = -1 app.slot_option_widgets['cpus'].set_value(cpus) used.add('cpus') # Options app.slot_option_list.clear() - for name, value in self.options.items(): - if not name in used: + for name, value in list(self.options.items()): + if name not in used: app.slot_option_list.append((name, value)) diff --git a/fah/WidgetMap.py b/fah/WidgetMap.py index ff85ac6..a919e30 100644 --- a/fah/WidgetMap.py +++ b/fah/WidgetMap.py @@ -1,43 +1,48 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import gtk class WidgetMap(dict): - def __init__(self, widgets, suffix = None, prefix = None): + def __init__(self, widgets, suffix=None, prefix=None): self.suffix = suffix self.prefix = prefix self.list = [] try: - for widget in widgets: self.find(widget) - except: self.find(widgets) + for widget in widgets: + self.find(widget) + except: + self.find(widgets) def add(self, widget): - if widget is None: return + if widget is None: + return name = gtk.Buildable.get_name(widget) - if self.suffix is not None: name = name[0:-len(self.suffix)] - if self.prefix is not None: name = name[len(self.prefix):] + if self.suffix is not None: + name = name[0:-len(self.suffix)] + if self.prefix is not None: + name = name[len(self.prefix):] self[name] = widget self.list.append(widget) @@ -45,7 +50,7 @@ class WidgetMap(dict): name = gtk.Buildable.get_name(widget) if (name and (self.suffix is None or name.endswith(self.suffix)) and - (self.prefix is None or name.startswith(self.prefix))): + (self.prefix is None or name.startswith(self.prefix))): self.add(widget) if isinstance(widget, gtk.Container): diff --git a/fah/__init__.py b/fah/__init__.py index 3c6c615..2e16f9c 100644 --- a/fah/__init__.py +++ b/fah/__init__.py @@ -1,34 +1,34 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### # fah -import db -import util +from . import db +from . import util from Version import * -from Icon import * -from SlotConfig import * -from ClientConfig import * -from Connection import * -from Client import * -from WidgetMap import * -from FAHControl import * +from .Icon import * +from .SlotConfig import * +from .ClientConfig import * +from .Connection import * +from .Client import * +from .WidgetMap import * +from .FAHControl import * diff --git a/fah/db/Column.py b/fah/db/Column.py index 8e243d9..f9f3e0c 100644 --- a/fah/db/Column.py +++ b/fah/db/Column.py @@ -1,49 +1,50 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### class Column: - def __init__(self, name, dbType, constraits, auto = False): + def __init__(self, name, dbType, constraits, auto=False): self.name = name self.dbType = dbType.lower() self.constraints = constraits self.auto = auto - def get_name(self): return self.name def is_auto(self): return self.auto - def get_db_value(self, value): - if self.dbType == 'text': return "'%s'" % str(value).replace("'", "''") - if self.dbType == 'integer': return '%d' % value - if self.dbType == 'real': return '%f' % value + if self.dbType == 'text': + return "'%s'" % str(value).replace("'", "''") + if self.dbType == 'integer': + return '%d' % value + if self.dbType == 'real': + return '%f' % value if self.dbType == 'boolean': - if value: return '1' - else: return '0' - + if value: + return '1' + else: + return '0' def get_sql(self): return '"%s" %s %s' % (self.name, self.dbType, self.constraints) - def __hash__(self): return self.name.__hash__() def __cmp__(self, other): return self.name.__cmp__(other.name) def __str__(self): return self.name diff --git a/fah/db/Database.py b/fah/db/Database.py index 2cd162b..29509c8 100644 --- a/fah/db/Database.py +++ b/fah/db/Database.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### from fah.db import Column, Table @@ -43,131 +43,121 @@ class Database: 'PRIMARY KEY (name)'), ] - def __init__(self, filename): self.filename = filename self.conn = sqlite3.connect(filename) self.conn.row_factory = sqlite3.Row self.queue = {} - def get_table(self, name): for table in self.tables: - if table.name == name: return table + if table.name == name: + return table raise Exception('Table "%s" not found' % name) - def get_version(self): return 6 - def get_current_version(self): return int(self.execute_one('PRAGMA user_version')[0]) - def set_current_version(self, version): self.write('PRAGMA user_version=%d' % version, True) - - def set(self, name, value, commit = True, queue = False): - if queue: self.queue[name] = value + def set(self, name, value, commit=True, queue=False): + if queue: + self.queue[name] = value else: - self.insert('config', name = name, value = value) - if commit: self.commit() - - - def clear(self, name, commit = True): - self.delete('config', name = name) - if commit: self.commit() + self.insert('config', name=name, value=value) + if commit: + self.commit() + def clear(self, name, commit=True): + self.delete('config', name=name) + if commit: + self.commit() def get(self, name): - c = self.get_table('config').select(self, 'value', name = name) + c = self.get_table('config').select(self, 'value', name=name) result = c.fetchone() c.close() - if result: return result[0] - + if result: + return result[0] def has(self, name): - return self.get(name) != None - - - def default(self, name, default, commit = True): - if not self.has(name): self.set(name, default, commit) + return self.get(name) is not None + def default(self, name, default, commit=True): + if not self.has(name): + self.set(name, default, commit) def flush_queued(self): - if len(self.queue) == 0: return + if len(self.queue) == 0: + return - for name, value in self.queue.items(): - self.set(name, value, commit = False) + for name, value in list(self.queue.items()): + self.set(name, value, commit=False) self.commit() self.queue.clear() - def execute(self, sql): - #print 'SQL:', sql + # print 'SQL:', sql c = self.conn.cursor() c.execute(sql) return c - def execute_one(self, sql): c = self.execute(sql) result = c.fetchone() c.close() return result - - def write(self, sql, commit = False): + def write(self, sql, commit=False): self.execute(sql).close() - if commit: self.commit() - + if commit: + self.commit() def commit(self): self.conn.commit() - def rollback(self): self.conn.rollback() - def insert(self, table, **kwargs): self.get_table(table).insert(self, **kwargs) - def delete(self, table, **kwargs): self.get_table(table).delete(self, **kwargs) - - def select(self, table, cols = None, **kwargs): + def select(self, table, cols=None, **kwargs): return self.get_table(table).select(self, cols, **kwargs) - def create(self): for table in self.tables: table.create(self) self.commit() - def validate(self): current = self.get_current_version() if self.get_version() < current: - raise Exception('Configuration database "%s" version %d is newer than is supported %d' + raise Exception('Configuration database "%s" version %d is newer \ + than is supported %d' % (self.filename, current, self.get_version())) elif self.get_version() != current: # Create or upgrade DB - if current == 0: self.create() + if current == 0: + self.create() else: if current <= 2: # Just drop and recreate the clients table self.execute('DROP TABLE IF EXISTS clients') for table in self.tables: - if table.name == 'clients': table.create(self) + if table.name == 'clients': + table.create(self) if current <= 5: self.execute('DROP TABLE IF EXISTS projects') diff --git a/fah/db/Table.py b/fah/db/Table.py index 25102a9..377c17b 100644 --- a/fah/db/Table.py +++ b/fah/db/Table.py @@ -1,36 +1,36 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### from fah.db import Column class Table: - def __init__(self, name, cols, constraints = ''): + def __init__(self, name, cols, constraints=''): self.name = name self.cols = cols self.constraints = constraints - def where(self, **kwargs): - if len(kwargs) == 0: return '' + if len(kwargs) == 0: + return '' sql = 'WHERE ' if len(kwargs) == 1 and 'where' in kwargs: @@ -38,38 +38,37 @@ class Table: else: sql +=\ - ' AND '.join(map(lambda i: '"%s"=\'%s\'' % i, kwargs.items())) + ' AND '.join(['"%s"=\'%s\'' % i for i in list(kwargs.items())]) return sql - def create(self, db): sql = 'CREATE TABLE IF NOT EXISTS "%s" (%s' % ( self.name, ','.join(map(Column.get_sql, self.cols))) - if self.constraints: sql += ',%s' % self.constraints + if self.constraints: + sql += ',%s' % self.constraints sql += ')' db.execute(sql).close() - def insert(self, db, **kwargs): - cols = filter(lambda col: col.name in kwargs, self.cols) + cols = [col for col in self.cols if col.name in kwargs] # Error checking if len(cols) != len(kwargs): col_names = set(map(Column.get_name, cols)) - missing = filter(lambda kw: not kw in col_names, kwargs.keys()) + missing = [kw for kw in list(kwargs.keys()) if kw not in col_names] raise Exception('Table %s does not have column(s) %s' % (self.name, ', '.join(missing))) sql = 'REPLACE INTO "%s" ("%s") VALUES (%s)' % ( self.name, '","'.join(map(Column.get_name, cols)), - ','.join(map(lambda col: col.get_db_value(kwargs[col.name]), cols))) + ','.join([col.get_db_value(kwargs[col.name]) for col in cols])) db.execute(sql).close() - def select(self, db, cols = None, **kwargs): + def select(self, db, cols=None, **kwargs): if cols is None: cols = '"' + '","'.join(map(str, self.cols)) + '"' @@ -82,11 +81,9 @@ class Table: return db.execute(sql) - def delete(self, db, **kwargs): sql = 'DELETE FROM %s %s' % (self.name, self.where(**kwargs)) db.execute(sql).close() - def drop(self, db): db.execute('DROP TABLE IF EXISTS ' + self.name).close() diff --git a/fah/db/__init__.py b/fah/db/__init__.py index 666c25d..e91d333 100644 --- a/fah/db/__init__.py +++ b/fah/db/__init__.py @@ -1,26 +1,26 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### # fah.db -from Column import * -from Table import * -from Database import * +from .Column import * +from .Table import * +from .Database import * diff --git a/fah/util/OrderedDict.py b/fah/util/OrderedDict.py index 372d51c..63cbcf2 100644 --- a/fah/util/OrderedDict.py +++ b/fah/util/OrderedDict.py @@ -69,9 +69,9 @@ class OrderedDict(dict, DictMixin): if not self: raise KeyError('dictionary is empty') if last: - key = reversed(self).next() + key = next(reversed(self)) else: - key = iter(self).next() + key = next(iter(self)) value = self.pop(key) return key, value @@ -100,7 +100,7 @@ class OrderedDict(dict, DictMixin): def __repr__(self): if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) def copy(self): return self.__class__(self) @@ -114,7 +114,7 @@ class OrderedDict(dict, DictMixin): def __eq__(self, other): if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() + return len(self)==len(other) and list(self.items()) == list(other.items()) return dict.__eq__(self, other) def __ne__(self, other): diff --git a/fah/util/PYONDecoder.py b/fah/util/PYONDecoder.py index 9a77406..b44de85 100644 --- a/fah/util/PYONDecoder.py +++ b/fah/util/PYONDecoder.py @@ -29,8 +29,8 @@ NUMBER_RE = re.compile(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?', FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) BACKSLASH = { - '"': u'"', '\\': u'\\', '/': u'/', - 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t', + '"': '"', '\\': '\\', '/': '/', + 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', } DEFAULT_ENCODING = "utf-8" @@ -94,8 +94,8 @@ def pyon_scanstring(s, end, encoding = None, strict = True, # Content is contains zero or more unescaped string characters if content: - if not isinstance(content, unicode): - content = unicode(content, encoding) + if not isinstance(content, str): + content = str(content, encoding) _append(content) # Terminator is the end of string, a literal control character, @@ -127,7 +127,7 @@ def pyon_scanstring(s, end, encoding = None, strict = True, # Hex escape sequence code = s[end + 1: end + 3] try: - char = unichr(int(code, 16)) + char = chr(int(code, 16)) except: raise ValueError(errmsg('Invalid \\escape: ' + repr(code), s, end)) @@ -144,12 +144,12 @@ def pyon_scanstring(s, end, encoding = None, strict = True, if 0xdc00 <= uni2 <= 0xdfff: uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) end += 6 - char = unichr(uni) + char = chr(uni) # Append the unescaped character _append(char) - return u''.join(chunks), end + return ''.join(chunks), end diff --git a/fah/util/SingleApp.py b/fah/util/SingleApp.py index fb9a6c7..9fd66ef 100644 --- a/fah/util/SingleApp.py +++ b/fah/util/SingleApp.py @@ -22,7 +22,7 @@ import sys import socket import threading -import SocketServer +import socketserver import gtk @@ -33,7 +33,7 @@ single_app_port = 32455 single_app_addr = (single_app_host, single_app_port) -class SingleAppRequestHandler(SocketServer.BaseRequestHandler): +class SingleAppRequestHandler(socketserver.BaseRequestHandler): def handle(self): cmd = self.request.recv(1024).strip() @@ -47,7 +47,7 @@ class SingleAppRequestHandler(SocketServer.BaseRequestHandler): -class SingleAppServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): +class SingleAppServer(socketserver.ThreadingMixIn, socketserver.TCPServer): allow_reuse_address = True def __init__(self): @@ -57,7 +57,7 @@ class SingleAppServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): self.ping = threading.Event() self.exit_requested = threading.Event() - SocketServer.TCPServer.__init__( + socketserver.TCPServer.__init__( self, single_app_addr, SingleAppRequestHandler) thread = threading.Thread(target = self.serve_forever) diff --git a/fah/util/__init__.py b/fah/util/__init__.py index 16b8a85..8623b23 100644 --- a/fah/util/__init__.py +++ b/fah/util/__init__.py @@ -32,11 +32,11 @@ if sys.platform == 'darwin': from gtkosx_application import gtkosx_application_get_resource_path \ as quartz_application_get_resource_path -from SingleApp import * -from EntryValidator import * -from PasswordValidator import * -from OrderedDict import * -from PYONDecoder import * +from .SingleApp import * +from .EntryValidator import * +from .PasswordValidator import * +from .OrderedDict import * +from .PYONDecoder import * def parse_bool(x): @@ -125,7 +125,7 @@ def get_widget_str_value(widget): return widget.get_active_text() else: - print ('ERROR: unsupported widget type %s' % type(widget)) + print(('ERROR: unsupported widget type %s' % type(widget))) def set_widget_str_value(widget, value): @@ -159,7 +159,7 @@ def set_widget_str_value(widget, value): widget.set_active(i) return - print ('ERROR: Invalid value "%s"' % value) + print(('ERROR: Invalid value "%s"' % value)) elif isinstance(widget, gtk.ProgressBar): widget.set_text(value) @@ -172,7 +172,7 @@ def set_widget_str_value(widget, value): widget.set_fraction(fraction) else: - print ('ERROR: unsupported option widget type %s' % type(widget)) + print(('ERROR: unsupported option widget type %s' % type(widget))) def set_widget_change_action(widget, action): @@ -192,7 +192,7 @@ def set_widget_change_action(widget, action): widget.connect('rows_reordered', action) else: - print ('ERROR: unsupported option widget type %s' % type(widget)) + print(('ERROR: unsupported option widget type %s' % type(widget))) def get_home_dir(): diff --git a/fah/wraplabel.py b/fah/wraplabel.py index 63fa9cd..5b5da0c 100644 --- a/fah/wraplabel.py +++ b/fah/wraplabel.py @@ -1,23 +1,23 @@ -################################################################################ -# # -# Folding@Home Client Control (FAHControl) # -# Copyright (C) 2016-2020 foldingathome.org # -# Copyright (C) 2010-2016 Stanford University # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -################################################################################ +############################################################################### +# # +# Folding@Home Client Control (FAHControl) # +# Copyright (C) 2016-2020 foldingathome.org # +# Copyright (C) 2010-2016 Stanford University # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### import gtk import gobject @@ -27,42 +27,39 @@ import pango class WrapLabel(gtk.Label): __gtype_name__ = 'WrapLabel' - def __init__(self, str = None): + def __init__(self, str=None): gtk.Label.__init__(self) self.__wrap_width = 0 self.layout = self.get_layout() self.layout.set_wrap(pango.WRAP_WORD_CHAR) - if str != None: self.set_text(str) + if str is not None: + self.set_text(str) self.set_alignment(0, 0) - def do_size_request(self, requisition): layout = self.get_layout() width, height = layout.get_pixel_size() requisition.width = 0 requisition.height = height - def do_size_allocate(self, allocation): gtk.Label.do_size_allocate(self, allocation) self.__set_wrap_width(allocation.width) - def set_text(self, str): gtk.Label.set_text(self, str) self.__set_wrap_width(self.__wrap_width) - def set_markup(self, str): gtk.Label.set_markup(self, str) self.__set_wrap_width(self.__wrap_width) - def __set_wrap_width(self, width): - if width == 0: return + if width == 0: + return layout = self.get_layout() layout.set_width(width * pango.SCALE) if self.__wrap_width != width: diff --git a/osx/scripts/onquit b/osx/scripts/onquit index a312e49..d0d5ef5 100755 --- a/osx/scripts/onquit +++ b/osx/scripts/onquit @@ -48,7 +48,7 @@ if [ -d "$A2" ]; then chmod -R go-w "$A2"; fi A3="/Applications/Folding@home/FAHControl/FAHControl.app" F1="/Applications/Folding@home/FAHControl/.DS_Store" D1="/Applications/Folding@home/FAHControl" -[ -d "$A3" ] && [ ! -d "$A2"] && mv "$A3" "$A2" || true +[ -d "$A3" ] && [ ! -d "$A2" ] && mv "$A3" "$A2" || true [ -f "$F1" ] && rm -f "$F1" || true [ -d "$D1" ] && rmdir "$D1" || true diff --git a/setup.py b/setup.py index f75b114..3425dac 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import inspect dir = os.path.dirname(inspect.getfile(inspect.currentframe())) if dir == '': dir = '.' -print ('dir = %s' % dir) +print(('dir = %s' % dir)) os.chdir(dir)