diff --git a/_build.py b/_build.py index fdb094d..916e099 100644 --- a/_build.py +++ b/_build.py @@ -3,7 +3,7 @@ # # _build.py : Tools to make packages for different platforms # -# Copyleft (C) 2013 - huhamhire hosts team +# Copyleft (C) 2013 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -20,9 +20,9 @@ import sys import shutil -from gui.hostsutil import __version__ +from hoststool import __version__ -SCRIPT = "hostsutil.py" +SCRIPT = "hoststool.py" SCRIPT_DIR = os.getcwd() + '/' RELEASE_DIR = "../release/" @@ -31,10 +31,10 @@ VERSION = __version__ DESCRIPTION = "HostsUtl - Hosts Setup Utility" AUTHOR = "Hamhire Hu" -AUTHOR_EMAIL ="develop@huhamhire.com", +AUTHOR_EMAIL = "hosts@huhamhire.com", LICENSE = "Public Domain, Python, BSD, GPLv3 (see LICENSE)", URL = "https://hosts.huhamhire.com", -CLASSIFIERS = [ +CLASSIFIERS = [ "Development Status :: 4 - Beta", "Environment :: MacOS X", "Environment :: Win32 (MS Windows)", @@ -62,46 +62,67 @@ "Topic :: CommonUtil", ] DATA_FILES = [ - ("lang", [ - "lang/en_US.qm", - "lang/zh_CN.qm", - "lang/zh_TW.qm", - ] - ), - ("theme", [ - "theme/default.qss", - ] - ), - "LICENSE", - "README.md", - "network.conf", + ("gui/lang", [ + "gui/lang/en_US.qm", + "gui/lang/zh_CN.qm", + "gui/lang/zh_TW.qm", + ]), + ("gui/theme", [ + "gui/theme/default.qss", + ]), + (".", [ + "LICENSE", + "README.md", + "network.conf", + ]), ] if sys.argv > 1: tar_flag = 0 + includes = [] + excludes = [] + file_path = lambda rel_path: SCRIPT_DIR + rel_path if sys.argv[1] == "py2tar": # Pack up script package for Linux users - file_path = lambda rel_path: SCRIPT_DIR + rel_path includes = [ - "*.py", "lang/*.qm", "theme/*.qss", "LICENSE", "README.md", - "network.conf"] - excludes = ["_*.py", ".gitattributes"] + "*.py", + "gui/lang/*.qm", + "gui/theme/*.qss", + "*/*.py", + "LICENSE", + "README.md", + "network.conf", + ] + excludes = [ + "_build.py", + "_pylupdate4.py", + "_pyuic4.py", + ".gitattributes", + ".gitignore", + ] ex_files = [] prefix = "HostsUtl-x11-gpl-" tar_flag = 1 elif sys.argv[1] == "py2source": # Pack up source package for Linux users - file_path = lambda rel_path: SCRIPT_DIR + rel_path includes = ["*"] - excludes = [".gitattributes"] + excludes = [ + ".gitattributes", + ".gitignore", + "hostslist.data", + ] ex_files = [] prefix = "HostsUtl-source-gpl-" tar_flag = 1 + else: + prefix = "Error" + ex_files = [] if tar_flag: import glob import tarfile + TAR_NAME = prefix + VERSION + ".tar.gz" RELEASE_PATH = RELEASE_DIR + TAR_NAME if not os.path.exists(RELEASE_DIR): @@ -116,23 +137,26 @@ files = glob.glob(file_path(name_format)) for src_file in files: if src_file not in ex_files: - tar_path = os.path.join(prefix + VERSION, src_file[rel_len:]) + tar_path = os.path.join(prefix + VERSION, + src_file[rel_len:]) tar.add(src_file, tar_path) - print src_file + print "compressing: %s" % src_file tar.close() exit(1) -from util.common import CommonUtil +from util import CommonUtil + system = CommonUtil.check_platform()[0] if system == "Windows": # Build binary executables for Windows import struct import zipfile from distutils.core import setup + import py2exe # Set working directories WORK_DIR = SCRIPT_DIR + "work/" - DIR_NAME = "HostsUtl" + DIR_NAME = "HostsUtil" DIST_DIR = WORK_DIR + DIR_NAME + '/' WIN_OPTIONS = { "includes": ["sip"], @@ -147,23 +171,30 @@ shutil.rmtree(DIST_DIR) # Build Executable print " Building Executable ".center(78, '=') + EXE_NAME = SCRIPT.split(".")[0] setup( - name = NAME, - version = VERSION, - options = {"py2exe": WIN_OPTIONS}, - windows = [ + name=NAME, + version=VERSION, + options={"py2exe": WIN_OPTIONS}, + console=[ + {"script": SCRIPT, + "dest_base": "command_line_tool", + }, + ], + windows=[ {"script": SCRIPT, - "icon_resources": [(1, "img/icons/hosts_utl.ico")] + "icon_resources": [(1, "res/img/icons/hosts_utl.ico")], + "dest_base": EXE_NAME, }, ], - description = DESCRIPTION, - author = AUTHOR, - author_email = AUTHOR_EMAIL, - license = LICENSE, - url = URL, - zipfile = None, - data_files = DATA_FILES, - classifiers = CLASSIFIERS, + description=DESCRIPTION, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + license=LICENSE, + url=URL, + zipfile="lib/shared.zip", + data_files=DATA_FILES, + classifiers=CLASSIFIERS, ) # Clean work directory after build shutil.rmtree(SCRIPT_DIR + "build/") @@ -173,6 +204,8 @@ PLAT = "x64" elif struct.calcsize("P") * 8 == 32: PLAT = "x86" + else: + PLAT = "unknown" DIR_NAME = DIR_NAME + '-win-gpl-' + VERSION + '-' + PLAT ZIP_NAME = DIR_NAME + ".zip" ZIP_FILE = WORK_DIR + ZIP_NAME @@ -206,7 +239,7 @@ DIST_DIR = APP_PATH + "/Contents/" # Set build configuration MAC_OPTIONS = { - "iconfile": "img/icons/hosts_utl.icns", + "iconfile": "res/img/icons/hosts_utl.icns", "includes": ["sip", "PyQt4.QtCore", "PyQt4.QtGui"], "excludes": [ "PyQt4.QtDBus", @@ -239,24 +272,24 @@ shutil.rmtree(APP_PATH) if not os.path.exists(WORK_DIR): os.mkdir(WORK_DIR) - # Make daemon APP + # Make daemon APP OSAC_CMD = "osacompile -o %s %sHostsUtl.scpt" % (APP_PATH, RES_DIR) os.system(OSAC_CMD) # Build APP print " Building Application ".center(78, '=') setup( - app = [SCRIPT], - name = NAME, - version = VERSION, - options = {"py2app": MAC_OPTIONS}, - setup_requires = ["py2app"], - description = DESCRIPTION, - author = AUTHOR, - author_email = AUTHOR_EMAIL, - license = LICENSE, - url = URL, - data_files = DATA_FILES, - classifiers = CLASSIFIERS, + app=[SCRIPT], + name=NAME, + version=VERSION, + options={"py2app": MAC_OPTIONS}, + setup_requires=["py2app"], + description=DESCRIPTION, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + license=LICENSE, + url=URL, + data_files=DATA_FILES, + classifiers=CLASSIFIERS, ) # Clean work directory after build os.remove(DIST_DIR + "Resources/applet.icns") @@ -279,7 +312,7 @@ os.remove(DMG_TMP) if os.path.isfile(DMG_PATH): os.remove(DMG_PATH) - # Prepare files in DMG package + # Prepare files in DMG package os.mkdir(VDMG_DIR) shutil.move(APP_PATH, VDMG_DIR) os.symlink("/Applications", VDMG_DIR + " ") diff --git a/gui/__list_trans.py b/gui/__list_trans.py index 56689b1..058d819 100644 --- a/gui/__list_trans.py +++ b/gui/__list_trans.py @@ -3,7 +3,7 @@ # # __list_trans.py : Name of items from the function list to be localized # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with diff --git a/gui/_checkconn.py b/gui/_checkconn.py index 025b594..80797a5 100644 --- a/gui/_checkconn.py +++ b/gui/_checkconn.py @@ -46,7 +46,7 @@ def __init__(self, parent=None): from. """ super(QSubChkConnection, self).__init__(parent) - self.link = parent.mirrors[parent._mirr_id]["test_url"] + self.link = parent.mirrors[parent.mirror_id]["test_url"] def run(self): """Check connection - Public Method diff --git a/gui/_checkupdate.py b/gui/_checkupdate.py index d5c8aaf..fb3abd2 100644 --- a/gui/_checkupdate.py +++ b/gui/_checkupdate.py @@ -48,7 +48,7 @@ def __init__(self, parent=None): from. """ super(QSubChkUpdate, self).__init__(parent) - self.url = parent.mirrors[parent._mirr_id]["update"] + parent.infofile + self.url = parent.mirrors[parent.mirror_id]["update"] + parent.infofile def run(self): """Check update - Public Method diff --git a/gui/_make.py b/gui/_make.py index aeab163..5d01a56 100644 --- a/gui/_make.py +++ b/gui/_make.py @@ -80,17 +80,17 @@ def __init__(self, parent=None): """ super(QSubMakeHosts, self).__init__(parent) self.count = 0 - self.make_cfg = parent._make_cfg - self.make_mode = parent._make_mode - make_path = parent._make_path + self.make_cfg = parent.make_cfg + self.make_mode = parent.make_mode + make_path = parent.make_path self.hostname = parent.hostname - if parent._make_mode == "system": - self.eol = parent._sys_eol + if parent.make_mode == "system": + self.eol = parent.sys_eol self.hosts_file = open("hosts", "wb") - elif parent._make_mode == "ansi": + elif parent.make_mode == "ansi": self.eol = "\r\n" self.hosts_file = open(unicode(make_path), "wb") - elif parent._make_mode == "utf-8": + elif parent.make_mode == "utf-8": self.eol = "\n" self.hosts_file = open(unicode(make_path), "wb") diff --git a/gui/_update.py b/gui/_update.py index 5fc0aa6..b83f545 100644 --- a/gui/_update.py +++ b/gui/_update.py @@ -66,7 +66,8 @@ def __init__(self, parent=None): from. """ super(QSubFetchUpdate, self).__init__(parent) - self.url = parent.mirrors[parent._mirr_id]["update"] + parent.filename + self.url = parent.mirrors[parent.mirror_id]["update"] + \ + parent.filename self.path = "./" + parent.filename self.tmp_path = self.path + ".download" self.filesize = parent._update["size"] diff --git a/gui/hostsutil.py b/gui/hostsutil.py index fc7a7cb..46d5510 100644 --- a/gui/hostsutil.py +++ b/gui/hostsutil.py @@ -3,7 +3,7 @@ # # hostsutil.py : Main parts of Hosts Setup Utility # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -14,8 +14,6 @@ # PURPOSE. # ===================================================================== -__version__ = "1.9.8" -__revision__ = "$Id$" __author__ = "huhamhire " import sys @@ -24,7 +22,6 @@ from zipfile import BadZipfile from qdialog_slots import QDialogSlots -from util_ui import _translate, _fromUtf8 sys.path.append("..") from util import RetrieveData, CommonUtil @@ -47,27 +44,20 @@ class HostsUtil(QDialogSlots): methods dealing with the user interface is also given by this class. Attributes: - initd (int): An integer indicating how many times has the main dialog - been initialized. This value would be referenced for translator - to set the language of the main dialog. - _mirr_id (int): An integer indicating current index number of mirrors. - mirrors (list): A dictionary containing tag, test url, and update url - of mirrors. + init_flag (int): An integer indicating how many times has the main + dialog been initialized. This value would be referenced for + translator to set the language of the main dialog. filename (str): A string indicating the filename of the data file containing data to make a hosts file. infofile (str): A string indicating the filename of the info file containing metadata of the data file in JSON format. """ - initd = 0 - # Mirror related configuration - _mirr_id = 0 - mirrors = [] + init_flag = 0 # Data file related configuration filename = "hostslist.data" infofile = "hostsinfo.json" def __init__(self): - self.app = QtGui.QApplication(sys.argv) super(HostsUtil, self).__init__() def __del__(self): @@ -78,7 +68,7 @@ def __del__(self): pass def start(self): - if not self.initd: + if not self.init_flag: self.init_main() self.show() sys.exit(self.app.exec_()) @@ -89,14 +79,10 @@ def init_main(self): Set up the elements on the main dialog. Check the environment of current operating system and current session. """ - self.Ui.SelectMirror.clear() + self.ui.SelectMirror.clear() # Set mirrors self.mirrors = CommonUtil.set_network("network.conf") - for i, mirror in enumerate(self.mirrors): - self.Ui.SelectMirror.addItem(_fromUtf8("")) - self.Ui.SelectMirror.setItemText( - i, _translate("Util", mirror["tag"], None)) - self.set_platform_label() + self.set_mirrors() # Read data file and set function list try: RetrieveData.unpack() @@ -110,7 +96,7 @@ def init_main(self): self.warning_incorrect_datafile() # Check if current session have root privileges self.check_writable() - self.initd += 1 + self.init_flag += 1 if __name__ == "__main__": diff --git a/gui/language.py b/gui/language.py index 58281d3..8b6721d 100644 --- a/gui/language.py +++ b/gui/language.py @@ -3,7 +3,7 @@ # # common.py : Basic utilities used by Hosts Setup Utility # -# Copyleft (C) 2013 - huhamhire hosts team +# Copyleft (C) 2013 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with diff --git a/gui/pyqt/_pylupdate4.py b/gui/pyqt/_pylupdate4.py index cbab5e8..73387b8 100644 --- a/gui/pyqt/_pylupdate4.py +++ b/gui/pyqt/_pylupdate4.py @@ -3,7 +3,7 @@ # # _pylupdate4.py : Tools to update the language files for UI # -# Copyleft (C) 2013 - huhamhire hosts team +# Copyleft (C) 2013 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with diff --git a/gui/pyqt/_pyuic4.py b/gui/pyqt/_pyuic4.py index fbcedca..5e09438 100644 --- a/gui/pyqt/_pyuic4.py +++ b/gui/pyqt/_pyuic4.py @@ -3,7 +3,7 @@ # # _pyuic4.py : Tools update the UI code from UI design # -# Copyleft (C) 2013 - huhamhire hosts team +# Copyleft (C) 2013 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with diff --git a/gui/qdialog_d.py b/gui/qdialog_d.py index 3ab29ed..14397a0 100644 --- a/gui/qdialog_d.py +++ b/gui/qdialog_d.py @@ -3,7 +3,7 @@ # # qdialog_d.py : # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -41,11 +41,6 @@ class QDialogDaemon(QDialogUI): current session. 1 represents data file is being downloaded. _funcs (list): A list containing two lists with the information of function list for IPv4 and IPv6 environment. - _make_cfg (dict): A dictionary containing the selection control bytes - to make a hosts file. - _make_mode (str): A string indicating the operation mode for making - hosts file. - _sys_eol (str): A string indicating the End-Of-Line marker. _update (dict): A dictionary containing the update information of the current data file on server. _writable (int): An integer indicating whether the program is run with @@ -60,13 +55,15 @@ class QDialogDaemon(QDialogUI): system. This attribute would be used for linux clients. hosts_path (str): A string indicating the absolute path of the hosts file on current operating system. + make_cfg (dict): A dictionary containing the selection control bytes + to make a hosts file. + make_mode (str): A string indicating the operation mode for making + hosts file. + sys_eol (str): A string indicating the End-Of-Line marker. """ _down_flag = 0 _funcs = [[], []] - _make_cfg = {} - _make_mode = "" - _sys_eol = "" _update = {} _writable = 0 @@ -74,6 +71,9 @@ class QDialogDaemon(QDialogUI): slices = [[], []] hostname = '' hosts_path = '' + make_cfg = {} + make_mode = "" + sys_eol = "" def __init__(self): super(QDialogDaemon, self).__init__() @@ -127,7 +127,7 @@ def check_update(self): a server. """ self.set_update_start_btns() - self.set_label_text(self.Ui.labelLatestData, unicode( + self.set_label_text(self.ui.labelLatestData, unicode( _translate("Util", "Checking...", None))) thread = QSubChkUpdate(self) thread.trigger.connect(self.finish_update) @@ -191,7 +191,7 @@ def make_hosts(self, mode="system"): "Util", "Building hosts file...", None)), 1) # Avoid conflict while making hosts file RetrieveData.disconnect_db() - self._make_mode = mode + self.make_mode = mode self.set_config_bytes(mode) thread = QSubMakeHosts(self) thread.info_trigger.connect(self.set_make_progress) @@ -238,9 +238,9 @@ def set_platform(self): self.hosts_path = path self.plat_flag = flag if encode == "win_ansi": - self._sys_eol = "\r\n" + self.sys_eol = "\r\n" else: - self._sys_eol = "\n" + self.sys_eol = "\n" def set_config_bytes(self, mode): """Set configuration byte words - Public Method @@ -269,7 +269,7 @@ def set_config_bytes(self, mode): for i, cfg in enumerate(part_cfg): part_word += cfg << i selection[part] = part_word - self._make_cfg = selection + self.make_cfg = selection def refresh_info(self, refresh=0): """Refresh data file information - Public Method @@ -283,7 +283,7 @@ def refresh_info(self, refresh=0): needs to be reloaded or not. 1: reload, 0: do not reload. Default by 0. """ - if refresh and RetrieveData.conn is None: + if refresh and RetrieveData.conn is not None: RetrieveData.clear() try: RetrieveData.unpack() @@ -328,7 +328,7 @@ def finish_update(self, update): hosts file from the server. """ self._update = update - self.set_label_text(self.Ui.labelLatestData, update["version"]) + self.set_label_text(self.ui.labelLatestData, update["version"]) if self._update["version"] == \ unicode(_translate("Util", "[Error]", None)): self.set_conn_status(0) diff --git a/gui/qdialog_slots.py b/gui/qdialog_slots.py index 179fea3..9f0a4fd 100644 --- a/gui/qdialog_slots.py +++ b/gui/qdialog_slots.py @@ -3,7 +3,7 @@ # # qdialog_slots.py : # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -35,11 +35,16 @@ class QDialogSlots(QDialogDaemon): Attributes: _ipv_id (int): An integer indicating current IP version setting. The value could be 1 or 0. 1 represents IPv6 while 1 represents IPv4. - _make_path (str): A string indicating the path to store the hosts file + + make_path (str): A string indicating the path to store the hosts file in export mode. + mirror_id (int): An integer indicating current index number of + mirrors. """ _ipv_id = 0 - _make_path = "./hosts" + + make_path = "./hosts" + mirror_id = 0 def __init__(self): """Initialize a new instance of this class - Private Method @@ -85,7 +90,7 @@ def on_Mirror_changed(self, mirr_id): mirr_id (int): An integer indicating current index number of mirrors. """ - self._mirr_id = mirr_id + self.mirror_id = mirr_id self.check_connection() def on_IPVersion_changed(self, ipv_id): @@ -129,7 +134,6 @@ def on_Selection_changed(self, item): if c[0] == self.choice[ip_flag][func_id][0]: if c[1] in mutex and self._funcs[ip_flag][c_id] == 1: self._funcs[ip_flag][c_id] = 0 - item = self.Ui.Functionlist.item(c_id) self.refresh_func_list() def on_Lang_changed(self, lang): @@ -148,10 +152,10 @@ def on_Lang_changed(self, lang): trans = QtCore.QTranslator() from hostsutil import LANG_DIR trans.load(LANG_DIR + new_lang) - QtGui.QApplication.removeTranslator(self._trans) - QtGui.QApplication.installTranslator(trans) + self.app.removeTranslator(self._trans) + self.app.installTranslator(trans) self._trans = trans - self.Ui.retranslateUi(self) + self.ui.retranslateUi(self) self.init_main() self.check_connection() @@ -168,7 +172,7 @@ def on_MakeHosts_clicked(self): self.warning_permission() return if self.question_apply(): - self._make_path = "./hosts" + self.make_path = "./hosts" self.make_hosts("system") else: return @@ -180,8 +184,8 @@ def on_MakeANSI_clicked(self): button is clicked. This method would call operations to export a hosts file encoding in ANSI. """ - self._make_path = self.export_hosts() - if unicode(self._make_path) != u'': + self.make_path = self.export_hosts() + if unicode(self.make_path) != u'': self.make_hosts("ansi") def on_MakeUTF8_clicked(self): @@ -191,8 +195,8 @@ def on_MakeUTF8_clicked(self): button is clicked. This method would call operations to export a hosts file encoding in UTF-8. """ - self._make_path = self.export_hosts() - if unicode(self._make_path) != u'': + self.make_path = self.export_hosts() + if unicode(self.make_path) != u'': self.make_hosts("utf-8") def on_Backup_clicked(self): diff --git a/gui/qdialog_ui.py b/gui/qdialog_ui.py index c50a2a5..7308545 100644 --- a/gui/qdialog_ui.py +++ b/gui/qdialog_ui.py @@ -3,7 +3,7 @@ # # qdialog_ui.py : # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -24,6 +24,7 @@ from util_ui import Ui_Util, _translate, _fromUtf8 import sys + sys.path.append("..") from util import RetrieveData, CommonUtil @@ -38,37 +39,42 @@ class QDialogUI(QtGui.QDialog, object): file. _trans (obj): A QtCore.QTranslator object indicating the current UI language setting. + + mirrors (list): A dictionary containing tag, test url, and update url + of mirrors. platform (str): A string indicating the platform of current operating system. The value could be "Windows", "Linux", "Unix", "OS X", and of course "Unkown". plat_flag (bool): A boolean flag indicating whether the current os is supported or not. - Ui (str): A user interface object indicating the main dialog of this + ui (str): A user interface object indicating the main dialog of this program. """ _cur_ver = "" _trans = None - # OS related configuration + app = None + mirrors = [] platform = '' plat_flag = True - Ui = None + ui = None def __init__(self): """Initialize a new instance of this class - Private Method Set the UI object and current translator of the main dialog. """ + self.app = QtGui.QApplication(sys.argv) super(QDialogUI, self).__init__() - self.Ui = Ui_Util() - self.Ui.setupUi(self) + self.ui = Ui_Util() + self.ui.setupUi(self) self.set_style() self.set_stylesheet() # Set default UI language trans = QtCore.QTranslator() trans.load(LANG_DIR + "en_US") self._trans = trans - QtGui.QApplication.installTranslator(trans) + self.app.installTranslator(trans) self.set_languages() def set_stylesheet(self): @@ -76,9 +82,8 @@ def set_stylesheet(self): Define the style sheet of main dialog. """ - app = QtGui.QApplication.instance() with open("./gui/theme/default.qss", "r") as qss: - app.setStyleSheet(qss.read()) + self.app.setStyleSheet(qss.read()) def set_style(self): """Set window style - Public Method @@ -101,7 +106,7 @@ def set_languages(self): Set optional language selection items in the SelectLang widget. """ - self.Ui.SelectLang.clear() + self.ui.SelectLang.clear() langs = LangUtil.language langs_not_found = [] for locale in langs: @@ -111,9 +116,9 @@ def set_languages(self): langs.pop(locale) LangUtil.language = langs if len(langs) <= 1: - self.Ui.SelectLang.setEnabled(False) - # Block the signal while set the language selecions. - self.Ui.SelectLang.blockSignals(True) + self.ui.SelectLang.setEnabled(False) + # Block the signal while set the language selecions. + self.ui.SelectLang.blockSignals(True) sys_locale = LangUtil.get_locale() if sys_locale not in langs.keys(): sys_locale = "en_US" @@ -121,10 +126,17 @@ def set_languages(self): if sys_locale == locale: select = i lang = langs[locale] - self.Ui.SelectLang.addItem(_fromUtf8("")) - self.Ui.SelectLang.setItemText(i, lang) - self.Ui.SelectLang.blockSignals(False) - self.Ui.SelectLang.setCurrentIndex(select) + self.ui.SelectLang.addItem(_fromUtf8("")) + self.ui.SelectLang.setItemText(i, lang) + self.ui.SelectLang.blockSignals(False) + self.ui.SelectLang.setCurrentIndex(select) + + def set_mirrors(self): + for i, mirror in enumerate(self.mirrors): + self.ui.SelectMirror.addItem(_fromUtf8("")) + self.ui.SelectMirror.setItemText( + i, _translate("Util", mirror["tag"], None)) + self.set_platform_label() def set_label_color(self, label, color): """Set the color of a label - Public Method @@ -144,6 +156,8 @@ def set_label_color(self, label, color): rgb = "#e27867" elif color == "BLACK": rgb = "#b1b1b1" + else: + rgb = "#ffffff" label.setStyleSheet("QLabel {color: %s}" % rgb) def set_label_text(self, label, text): @@ -166,8 +180,8 @@ def set_conn_status(self, status): selected. """ if status == -1: - self.set_label_color(self.Ui.labelConnStat, "BLACK") - self.set_label_text(self.Ui.labelConnStat, unicode( + self.set_label_color(self.ui.labelConnStat, "BLACK") + self.set_label_text(self.ui.labelConnStat, unicode( _translate("Util", "Checking...", None))) elif status in [0, 1]: if status: @@ -176,8 +190,8 @@ def set_conn_status(self, status): else: color, stat = "RED", unicode(_translate( "Util", "[Failed]", None)) - self.set_label_color(self.Ui.labelConnStat, color) - self.set_label_text(self.Ui.labelConnStat, stat) + self.set_label_color(self.ui.labelConnStat, color) + self.set_label_text(self.ui.labelConnStat, stat) def set_info(self): """Set data file info - Public Method @@ -187,10 +201,10 @@ def set_info(self): info = RetrieveData.get_info() ver = info["Version"] self._cur_ver = ver - self.set_label_text(self.Ui.labelVersionData, ver) + self.set_label_text(self.ui.labelVersionData, ver) build = info["Buildtime"] build = CommonUtil.timestamp_to_date(build) - self.set_label_text(self.Ui.labelReleaseData, build) + self.set_label_text(self.ui.labelReleaseData, build) def set_down_progress(self, prog, msg): """Set progress bar - Public Method @@ -204,9 +218,9 @@ def set_down_progress(self, prog, msg): msg (str): A string indicating the message to be shown on the progress bar. """ - self.Ui.Prog.setProperty("value", prog) + self.ui.Prog.setProperty("value", prog) self.set_conn_status(1) - self.Ui.Prog.setFormat(msg) + self.ui.Prog.setFormat(msg) def set_platform_label(self): """Set label of OS info - Public Method @@ -215,8 +229,8 @@ def set_platform_label(self): platform. """ color = "GREEN" if self.plat_flag else "RED" - self.set_label_color(self.Ui.labelOSStat, color) - self.set_label_text(self.Ui.labelOSStat, "[%s]" % self.platform) + self.set_label_color(self.ui.labelOSStat, color) + self.set_label_text(self.ui.labelOSStat, "[%s]" % self.platform) def set_func_list(self, new=0): """Set the function list - Public Method @@ -229,9 +243,8 @@ def set_func_list(self, new=0): selection configuration or not. 0 -> user user config, 1 -> use default config. Default by 0. """ - ip_flag = self._ipv_id - self.Ui.Functionlist.clear() - self.Ui.FunctionsBox.setTitle(_translate( + self.ui.Functionlist.clear() + self.ui.FunctionsBox.setTitle(_translate( "Util", "Functions", None)) if new: for ip in range(2): @@ -240,7 +253,6 @@ def set_func_list(self, new=0): self.slices[ip] = slices funcs = [] for func in choice: - item = QtGui.QListWidgetItem() if func[1] in defaults[func[0]]: funcs.append(1) else: @@ -258,7 +270,7 @@ def set_list_item_unchecked(self, item_id): item in the function list. """ self._funcs[self._ipv_id][item_id] = 0 - item = self.Ui.Functionlist.item(item_id) + item = self.ui.Functionlist.item(item_id) item.setCheckState(QtCore.Qt.Unchecked) def refresh_func_list(self): @@ -267,7 +279,7 @@ def refresh_func_list(self): Refresh the items in the function list by user settings. """ ip_flag = self._ipv_id - self.Ui.Functionlist.clear() + self.ui.Functionlist.clear() for f_id, func in enumerate(self.choice[self._ipv_id]): item = QtGui.QListWidgetItem() if self._funcs[ip_flag][f_id] == 1: @@ -276,7 +288,7 @@ def refresh_func_list(self): check = QtCore.Qt.Unchecked item.setCheckState(check) item.setText(_translate("Util", func[3], None)) - self.Ui.Functionlist.addItem(item) + self.ui.Functionlist.addItem(item) def set_make_progress(self, mod_name, mod_num): """Operations to show progress while making hosts file - Public Method @@ -293,13 +305,12 @@ def set_make_progress(self, mod_name, mod_num): """ total_mods_num = self._funcs[self._ipv_id].count(1) + 1 prog = 100 * mod_num / total_mods_num - self.Ui.Prog.setProperty("value", prog) - format = unicode(_translate( - "Util", "Applying module: %s(%s/%s)", None)) % ( - mod_name, mod_num, total_mods_num) - self.Ui.Prog.setFormat(format) - self.set_make_message(format) - + self.ui.Prog.setProperty("value", prog) + message = unicode(_translate( + "Util", "Applying module: %s(%s/%s)", None) + ) % (mod_name, mod_num, total_mods_num) + self.ui.Prog.setFormat(message) + self.set_make_message(message) def set_message(self, title, msg): """Set a message box - Public Method @@ -312,12 +323,12 @@ def set_message(self, title, msg): msg (str): A string indicating the message to be shown in the message box. """ - self.Ui.FunctionsBox.setTitle(_translate("Util", title, None)) - self.Ui.Functionlist.clear() + self.ui.FunctionsBox.setTitle(_translate("Util", title, None)) + self.ui.Functionlist.clear() item = QtGui.QListWidgetItem() item.setText(msg) item.setFlags(QtCore.Qt.ItemIsEnabled) - self.Ui.Functionlist.addItem(item) + self.ui.Functionlist.addItem(item) def set_make_message(self, msg, start=0): """Operations to show making progress in function list - Public Method @@ -333,13 +344,13 @@ def set_make_message(self, msg, start=0): first. Default by 0. """ if start: - self.Ui.FunctionsBox.setTitle(_translate( + self.ui.FunctionsBox.setTitle(_translate( "Util", "Progress", None)) - self.Ui.Functionlist.clear() + self.ui.Functionlist.clear() item = QtGui.QListWidgetItem() item.setText("- " + msg) item.setFlags(QtCore.Qt.ItemIsEnabled) - self.Ui.Functionlist.addItem(item) + self.ui.Functionlist.addItem(item) def warning_permission(self): """Show permission error warning - Public Method @@ -349,10 +360,10 @@ def warning_permission(self): QtGui.QMessageBox.warning( self, _translate("Util", "Warning", None), _translate("Util", - "You do not have permissions to change the \n" - "hosts file.\n" - "Please run this program as Administrator/root\n" - "so it can modify your hosts file." + "You do not have permissions to change the \n" + "hosts file.\n" + "Please run this program as Administrator/root\n" + "so it can modify your hosts file." , None)) def warning_download(self): @@ -363,8 +374,8 @@ def warning_download(self): QtGui.QMessageBox.warning( self, _translate("Util", "Warning", None), _translate("Util", - "Error retrieving data from the server.\n" - "Please try another server.", None)) + "Error retrieving data from the server.\n" + "Please try another server.", None)) def warning_incorrect_datafile(self): """Show incorrect data file warning - Public Method @@ -373,13 +384,13 @@ def warning_incorrect_datafile(self): """ msg_title = "Warning" msg = unicode(_translate("Util", - "Incorrect Data file!\n" - "Please use the \"Download\" key to \n" - "fetch a new data file.", None)) + "Incorrect Data file!\n" + "Please use the \"Download\" key to \n" + "fetch a new data file.", None)) self.set_message(msg_title, msg) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) def warning_no_datafile(self): """Show no data file warning - Public Method @@ -388,13 +399,13 @@ def warning_no_datafile(self): """ msg_title = "Warning" msg = unicode(_translate("Util", - "Data file not found!\n" - "Please use the \"Download\" key to \n" - "fetch a new data file.", None)) + "Data file not found!\n" + "Please use the \"Download\" key to \n" + "fetch a new data file.", None)) self.set_message(msg_title, msg) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) def question_apply(self): """Show confirm make question - Public Method @@ -407,14 +418,14 @@ def question_apply(self): """ msg_title = unicode(_translate("Util", "Notice", None)) msg = unicode(_translate("Util", - "Are you sure you want to apply changes \n" - "to the hosts file on your system?\n\n" - "This operation could not be reverted if \n" - "you have not made a backup of your \n" - "current hosts file.", None)) + "Are you sure you want to apply changes \n" + "to the hosts file on your system?\n\n" + "This operation could not be reverted if \n" + "you have not made a backup of your \n" + "current hosts file.", None)) choice = QtGui.QMessageBox.question(self, msg_title, msg, - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, - QtGui.QMessageBox.No) + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.Yes: return True else: @@ -438,69 +449,67 @@ def info_complete(self): self, _translate("Util", "Complete", None), _translate("Util", "Operation completed", None)) - - def set_make_start_btns(self): - self.Ui.Functionlist.setEnabled(False) - self.Ui.SelectIP.setEnabled(False) - self.Ui.ButtonCheck.setEnabled(False) - self.Ui.ButtonUpdate.setEnabled(False) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) - self.Ui.ButtonExit.setEnabled(False) + self.ui.Functionlist.setEnabled(False) + self.ui.SelectIP.setEnabled(False) + self.ui.ButtonCheck.setEnabled(False) + self.ui.ButtonUpdate.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) + self.ui.ButtonExit.setEnabled(False) def set_make_finish_btns(self): - self.Ui.Functionlist.setEnabled(True) - self.Ui.SelectIP.setEnabled(True) - self.Ui.ButtonCheck.setEnabled(True) - self.Ui.ButtonUpdate.setEnabled(True) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) - self.Ui.ButtonExit.setEnabled(True) + self.ui.Functionlist.setEnabled(True) + self.ui.SelectIP.setEnabled(True) + self.ui.ButtonCheck.setEnabled(True) + self.ui.ButtonUpdate.setEnabled(True) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) + self.ui.ButtonExit.setEnabled(True) def set_update_click_btns(self): - self.Ui.ButtonApply.setEnabled(True) - self.Ui.ButtonANSI.setEnabled(True) - self.Ui.ButtonUTF.setEnabled(True) + self.ui.ButtonApply.setEnabled(True) + self.ui.ButtonANSI.setEnabled(True) + self.ui.ButtonUTF.setEnabled(True) def set_update_start_btns(self): - self.Ui.SelectMirror.setEnabled(False) - self.Ui.ButtonCheck.setEnabled(False) - self.Ui.ButtonUpdate.setEnabled(False) + self.ui.SelectMirror.setEnabled(False) + self.ui.ButtonCheck.setEnabled(False) + self.ui.ButtonUpdate.setEnabled(False) def set_update_finish_btns(self): - self.Ui.SelectMirror.setEnabled(True) - self.Ui.ButtonCheck.setEnabled(True) - self.Ui.ButtonUpdate.setEnabled(True) + self.ui.SelectMirror.setEnabled(True) + self.ui.ButtonCheck.setEnabled(True) + self.ui.ButtonUpdate.setEnabled(True) def set_fetch_click_btns(self): - self.Ui.Functionlist.setEnabled(False) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) + self.ui.Functionlist.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) def set_fetch_start_btns(self): - self.Ui.SelectMirror.setEnabled(False) - self.Ui.ButtonCheck.setEnabled(False) - self.Ui.ButtonUpdate.setEnabled(False) - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) - self.Ui.ButtonExit.setEnabled(False) + self.ui.SelectMirror.setEnabled(False) + self.ui.ButtonCheck.setEnabled(False) + self.ui.ButtonUpdate.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) + self.ui.ButtonExit.setEnabled(False) def set_fetch_finish_btns(self, error=0): if error: - self.Ui.ButtonApply.setEnabled(False) - self.Ui.ButtonANSI.setEnabled(False) - self.Ui.ButtonUTF.setEnabled(False) + self.ui.ButtonApply.setEnabled(False) + self.ui.ButtonANSI.setEnabled(False) + self.ui.ButtonUTF.setEnabled(False) else: - self.Ui.ButtonApply.setEnabled(True) - self.Ui.ButtonANSI.setEnabled(True) - self.Ui.ButtonUTF.setEnabled(True) - self.Ui.Functionlist.setEnabled(True) - self.Ui.SelectMirror.setEnabled(True) - self.Ui.ButtonCheck.setEnabled(True) - self.Ui.ButtonUpdate.setEnabled(True) - self.Ui.ButtonExit.setEnabled(True) + self.ui.ButtonApply.setEnabled(True) + self.ui.ButtonANSI.setEnabled(True) + self.ui.ButtonUTF.setEnabled(True) + self.ui.Functionlist.setEnabled(True) + self.ui.SelectMirror.setEnabled(True) + self.ui.ButtonCheck.setEnabled(True) + self.ui.ButtonUpdate.setEnabled(True) + self.ui.ButtonExit.setEnabled(True) diff --git a/guitest.py b/guitest.py deleted file mode 100644 index 497b749..0000000 --- a/guitest.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import gui - -if __name__ == "__main__": - main = gui.HostsUtil() - main.start() \ No newline at end of file diff --git a/hoststool.py b/hoststool.py new file mode 100644 index 0000000..90d898a --- /dev/null +++ b/hoststool.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# hoststool.py : +# +# Copyleft (C) 2014 - huhamhire hosts team +# ===================================================================== +# Licensed under the GNU General Public License, version 3. You should +# have received a copy of the GNU General Public License along with +# this program. If not, see . +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE. +# ===================================================================== + +__version__ = "1.9.8" +__revision__ = "$Id$" +__author__ = "huhamhire " + +from optparse import OptionParser + +import gui +import tui + + +class UtilLauncher(object): + + @classmethod + def launch(cls): + options, args = cls.set_commands() + if options.tui: + cls.launch_tui() + elif options.gui: + cls.launch_gui() + + @classmethod + def set_commands(cls): + usage = "usage: %prog [-g] [-t] [-h] [--version]" + version = "Hosts Setup Utility v" + __version__ + parser = OptionParser(usage=usage, version=version) + parser.add_option("-g", "--graphicui", dest="gui", + default=True, action="store_true", + help="launch in GUI(QT) mode") + parser.add_option("-t", "--textui", dest="tui", + default=False, action="store_true", + help="launch in TUI(Curses) mode ") + return parser.parse_args() + + @classmethod + def launch_gui(cls): + main = gui.HostsUtil() + main.start() + + @classmethod + def launch_tui(cls): + main = tui.HostsUtil() + main.start() + +if __name__ == "__main__": + UtilLauncher.launch() \ No newline at end of file diff --git a/test.py b/test.py deleted file mode 100644 index abdfbdd..0000000 --- a/test.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import tui - -if __name__ == "__main__": - main = tui.HostsUtil() - main.start() \ No newline at end of file diff --git a/tui/_make.py b/tui/_make.py index eb180fe..0540d0c 100644 --- a/tui/_make.py +++ b/tui/_make.py @@ -33,9 +33,9 @@ def __init__(self, parent=None): parent (obj): An instance of CursesDaemon object to get settings from. """ - self.make_cfg = parent._make_cfg + self.make_cfg = parent.make_cfg self.hostname = parent.hostname - self.eol = parent._sys_eol + self.eol = parent.sys_eol self.hosts_file = open("hosts", "wb") def make(self): diff --git a/tui/curses_d.py b/tui/curses_d.py index 609e35e..cb8a27a 100644 --- a/tui/curses_d.py +++ b/tui/curses_d.py @@ -31,12 +31,13 @@ class CursesDaemon(CursesUI): """ Attributes: - _make_cfg (dict): A dictionary containing the selection control bytes - to make a hosts file. _update (dict): A dictionary containing the update information of the current data file on server. _writable (int): An integer indicating whether the program is run with admin/root privileges. The value could be 1 or 0. + + make_cfg (dict): A dictionary containing the selection control bytes + to make a hosts file. platform (str): A string indicating the platform of current operating system. The value could be "Windows", "Linux", "Unix", "OS X", and of course "Unkown". @@ -45,10 +46,10 @@ class CursesDaemon(CursesUI): hosts_path (str): A string indicating the absolute path of the hosts file on current operating system. """ - _make_cfg = {} _update = {} _writable = 0 - # OS related configuration + + make_cfg = {} platform = '' hostname = '' hostspath = '' @@ -117,11 +118,14 @@ def session_daemon(self): elif key_in == curses.KEY_F5: self._update = self.check_update() elif key_in == curses.KEY_F6: - # TODO Check if data file up-to-date if self._update == {}: self._update = self.check_update() - self.fetch_update() - return 1 + # Check if data file up-to-date + if self.new_version(): + self.fetch_update() + return 1 + else: + self.messagebox("Data file is up-to-date!", 1) else: pass return 0 @@ -235,6 +239,28 @@ def check_update(self): self.status() return info + def new_version(self): + """Compare version of data file - Public Method + + Compare version of local data file to the version from the server. + + Returns: + A flag integer indicating whether the local data file is + up-to-date or not. + 1 -> The version of data file on server is newer. + 0 -> The local data file is up-to-date. + """ + local_ver = self.hostsinfo["Version"] + if local_ver == "N/A": + return 1 + server_ver = self._update["version"] + local_ver = local_ver.split('.') + server_ver = server_ver.split('.') + for i, ver_num in enumerate(local_ver): + if server_ver[i] > ver_num: + return 1 + return 0 + def fetch_update(self): self.messagebox("Downloading...") fetch_d = FetchUpdate(self) @@ -260,7 +286,7 @@ def set_config_bytes(self): for i, cfg in enumerate(part_cfg): part_word += cfg << i selection[part] = part_word - self._make_cfg = selection + self.make_cfg = selection def move_hosts(self): """Move hosts file to the system path after making - Public Method diff --git a/tui/curses_ui.py b/tui/curses_ui.py index 9fc4a36..ee682a8 100644 --- a/tui/curses_ui.py +++ b/tui/curses_ui.py @@ -18,15 +18,14 @@ import sys sys.path.append("..") from util import CommonUtil -from gui.hostsutil import __version__ +from hoststool import __version__ class CursesUI(object): """ Attributes: - _make_path (str): A string indicating the path to store the hosts file + make_path (str): A string indicating the path to store the hosts file in export mode. - _sys_eol (str): A string indicating the End-Of-Line marker. _funcs (list): A list containing two lists with the information of function list for IPv4 and IPv6 environment. choice (list): A list containing two lists with the selection of @@ -34,6 +33,7 @@ class CursesUI(object): slices (list): A list containing two lists with integers indicating the number of function items from different parts listed in the function list. + sys_eol (str): A string indicating the End-Of-Line marker. filename (str): A string indicating the filename of the data file containing data to make a hosts file. infofile (str): A string indicating the filename of the info file @@ -47,10 +47,10 @@ class CursesUI(object): _item_inf = 0 _make_path = "./hosts" - _sys_eol = "" _funcs = [[], []] choice = [[], []] slices = [[], []] + sys_eol = "" colorpairs = [(curses.COLOR_WHITE, curses.COLOR_BLUE), (curses.COLOR_WHITE, curses.COLOR_RED), diff --git a/tui/hostsutil.py b/tui/hostsutil.py index 1c434ef..24fd1ee 100644 --- a/tui/hostsutil.py +++ b/tui/hostsutil.py @@ -10,8 +10,6 @@ # this program. If not, see . # ===================================================================== -__version__ = "1.9.8" -__revision__ = "$Id$" __author__ = "huhamhire " from zipfile import BadZipfile @@ -74,9 +72,9 @@ def set_platform(self): self.hostspath = path self.statusinfo[1][2] = color if encode == "win_ansi": - self._sys_eol = "\r\n" + self.sys_eol = "\r\n" else: - self._sys_eol = "\n" + self.sys_eol = "\n" def set_func_list(self): for ip in range(2): diff --git a/util/common.py b/util/common.py index 2a189a5..f5519b9 100644 --- a/util/common.py +++ b/util/common.py @@ -3,7 +3,7 @@ # # common.py : Basic utilities used by Hosts Setup Utility # -# Copyleft (C) 2013 - huhamhire hosts team +# Copyleft (C) 2013 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with diff --git a/util/retrievedata.py b/util/retrievedata.py index dac20c3..d1a3af8 100644 --- a/util/retrievedata.py +++ b/util/retrievedata.py @@ -3,7 +3,7 @@ # # retrievedata.py : Read data from the hosts data file # -# Copyleft (C) 2014 - huhamhire hosts team +# Copyleft (C) 2014 - huhamhire hosts team # ===================================================================== # Licensed under the GNU General Public License, version 3. You should # have received a copy of the GNU General Public License along with @@ -194,7 +194,7 @@ def get_choice(cls, flag_v6=False): 'WHERE part_id=?', (ch_part, )) slices.append(cls._cur.fetchone()[0]) for s in range(1, len(slices)): - slices[s] = slices[s] + slices[s - 1] + slices[s] += slices[s - 1] return modules, defaults, slices @classmethod @@ -235,8 +235,8 @@ def unpack(cls, datafile=DATAFILE, database=DATABASE): ({dbfile}). """ datafile = zipfile.ZipFile(datafile, "r") - path, file = os.path.split(database) - datafile.extract(file, path) + path, filename = os.path.split(database) + datafile.extract(filename, path) @classmethod def clear(cls):