From 1b685921209bb4b11fdaa2ab50c2668f28c27237 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Thu, 7 May 2015 08:12:01 -0600 Subject: [PATCH] Added unicode support for projects. --- command_line.py | 121 +++++++++++++++++++++++++----------------------- main.py | 60 ++++++++++++------------ 2 files changed, 93 insertions(+), 88 deletions(-) diff --git a/command_line.py b/command_line.py index 03735c5..e8b59df 100644 --- a/command_line.py +++ b/command_line.py @@ -21,6 +21,7 @@ import subprocess import logging import logging.handlers as lh import plistlib +import codecs from semantic_version import Version @@ -63,8 +64,8 @@ handler = lh.RotatingFileHandler(LOG_FILENAME, maxBytes=100000, backupCount=2) logger.addHandler(handler) def my_excepthook(type_, value, tback): - output_err = ''.join(traceback.format_exception(type_, value, tback)) - logger.error('{}'.format(output_err)) + output_err = u''.join(traceback.format_exception(type_, value, tback)) + logger.error(u'{}'.format(output_err)) sys.__excepthook__(type_, value, tback) sys.excepthook = my_excepthook @@ -79,7 +80,7 @@ except: def get_base_url(): url = None try: - url = open(os.path.join(CWD, 'files', 'base_url.txt')).read().strip() + url = codecs.open(os.path.join(CWD, 'files', 'base_url.txt'), encoding='utf-8').read().strip() except (OSError, IOError): url = 'http://dl.node-webkit.org/v{}/' return url @@ -101,7 +102,7 @@ class Setting(object): self.default_value = kwargs.pop('default_value', None) self.button = kwargs.pop('button', None) self.button_callback = kwargs.pop('button_callback', None) - self.description = kwargs.pop('description', '') + self.description = kwargs.pop('description', u'') self.values = kwargs.pop('values', []) self.filter = kwargs.pop('filter', '.*') self.filter_action = kwargs.pop('filter_action', 'None') @@ -113,7 +114,7 @@ class Setting(object): if self.value is None: self.value = self.default_value - self.save_path = kwargs.pop('save_path', '') + self.save_path = kwargs.pop('save_path', u'') self.get_file_information_from_url() @@ -125,7 +126,7 @@ class Setting(object): def get_file_information_from_url(self): if hasattr(self, 'url'): - self.file_name = self.url.split('/')[-1] + self.file_name = self.url.split(u'/')[-1] self.full_file_path = os.path.join(self.save_path, self.file_name) self.file_ext = os.path.splitext(self.file_name)[1] if self.file_ext == '.zip': @@ -161,7 +162,7 @@ class Setting(object): def extract_file_path(self, version): if self.extract_file: return self.extract_file.format(version) - return '' + return u'' def set_extra_attributes_from_keyword_args(self, **kwargs): for undefined_key, undefined_value in kwargs.items(): @@ -224,9 +225,9 @@ class Setting(object): elif self.file_ext == '.zip': new_bytes = file.read(extract_p) except KeyError as e: - logger.error(str(e)) + logger.error(unicode(e)) # dirty hack to support old versions of nw - if 'no item named' in str(e): + if 'no item named' in unicode(e): extract_path = '/'.join(extract_path.split('/')[1:]) try: if self.file_ext == '.gz': @@ -234,7 +235,7 @@ class Setting(object): elif self.file_ext == '.zip': new_bytes = file.read(extract_path) except KeyError as e: - logger.error(str(e)) + logger.error(unicode(e)) if new_bytes is not None: fbytes.append((dest_path, new_bytes)) @@ -245,10 +246,10 @@ class Setting(object): url = '' if hasattr(self, 'url'): url = self.url - return ('Setting: (name={}, ' - 'display_name={}, ' - 'value={}, required={}, ' - 'type={}, url={})').format(self.name, + return (u'Setting: (name={}, ' + u'display_name={}, ' + u'value={}, required={}, ' + u'type={}, url={})').format(self.name, self.display_name, self.value, self.required, @@ -283,9 +284,10 @@ class CommandBase(object): def setup_nw_versions(self): nw_version = self.get_setting('nw_version') try: - f = open(os.path.join(CWD, 'files', 'nw-versions.txt')) + f = codecs.open(os.path.join(CWD, 'files', 'nw-versions.txt'), encoding='utf-8') for line in f: nw_version.values.append(line.strip()) + f.close() except IOError: nw_version.values.append(nw_version.default_value) @@ -295,8 +297,8 @@ class CommandBase(object): def get_settings(self): config_file = os.path.join(CWD, 'files', 'settings.cfg') - contents = open(config_file).read() - contents = contents.replace('{DEFAULT_DOWNLOAD_PATH}', + contents = codecs.open(config_file, encoding='utf-8').read() + contents = contents.replace(u'{DEFAULT_DOWNLOAD_PATH}', DEFAULT_DOWNLOAD_PATH) config_io = StringIO(contents) config = ConfigObj(config_io, unrepr=True).dict() @@ -373,11 +375,12 @@ class CommandBase(object): nw_version.values = versions f = None try: - f = open(os.path.join(CWD, 'files', 'nw-versions.txt'), 'w') + f = codecs.open(os.path.join(CWD, 'files', 'nw-versions.txt'), 'w', encoding='utf-8') for v in nw_version.values: f.write(v+os.linesep) + f.close() except IOError: - error = ''.join(traceback.format_exception(sys.exc_info()[0], + error = u''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) self.show_error(error) @@ -404,7 +407,7 @@ class CommandBase(object): if os.path.exists(setting.save_file_path(version, location)): os.remove(setting.save_file_path(version, location)) - error = ''.join(traceback.format_exception(sys.exc_info()[0], + error = u''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) self.show_error(error) @@ -420,13 +423,13 @@ class CommandBase(object): setting_list = [] if p_json: json_str = '' - with open(p_json[0], 'r') as f: + with codecs.open(p_json[0], 'r', encoding='utf-8') as f: json_str = f.read() try: setting_list = self.load_from_json(json_str) except ValueError as e: # Json file is invalid self.logger.warning('Warning: Json file invalid.') - self.progress_text = '{}\n'.format(e) + self.progress_text = u'{}\n'.format(e) return setting_list def generate_json(self): @@ -479,8 +482,8 @@ class CommandBase(object): @extract_error.setter def extract_error(self, value): if value is not None and not self.quiet and not inside_packed_exe: - self._extract_error = str(value) - sys.stderr.write('\r{}'.format(self._extract_error)) + self._extract_error = unicode(value) + sys.stderr.write(u'\r{}'.format(self._extract_error)) sys.stderr.flush() @property @@ -490,8 +493,8 @@ class CommandBase(object): @output_err.setter def output_err(self, value): if value is not None and not self.quiet and not inside_packed_exe: - self._output_err = str(value) - sys.stderr.write('\r{}'.format(self._output_err)) + self._output_err = unicode(value) + sys.stderr.write(u'\r{}'.format(self._output_err)) sys.stderr.flush() @property @@ -501,8 +504,8 @@ class CommandBase(object): @progress_text.setter def progress_text(self, value): if value is not None and not self.quiet and not inside_packed_exe: - self._progress_text = str(value) - sys.stdout.write('\r{}'.format(self._progress_text)) + self._progress_text = unicode(value) + sys.stdout.write(u'\r{}'.format(self._progress_text)) sys.stdout.flush() def load_from_json(self, json_str): @@ -561,7 +564,7 @@ class CommandBase(object): if os.path.exists(save_file_path): os.remove(save_file_path) self.extract_error = e - self.logger.error(str(self.extract_error)) + self.logger.error(unicode(self.extract_error)) # cannot use GUI in thread to notify user. Save it for later self.progress_text = '\nDone.\n' return True @@ -614,7 +617,7 @@ class CommandBase(object): json_file = os.path.join(self.project_dir(), 'package.json') if self.output_package_json: - with open(json_file, 'w+') as f: + with codecs.open(json_file, 'w+', encoding='utf-8') as f: f.write(self.generate_json()) zip_file = os.path.join(temp_dir, self.project_name()+'.nw') @@ -624,7 +627,7 @@ class CommandBase(object): if ex_setting.value: self.progress_text = '\n' name = ex_setting.display_name - self.progress_text = 'Making files for {}...'.format(name) + self.progress_text = u'Making files for {}...'.format(name) export_dest = os.path.join(output_dir, ex_setting.name) versions = re.findall('(\d+)\.(\d+)\.(\d+)', self.selected_version())[0] @@ -719,7 +722,7 @@ class CommandBase(object): exc = traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]) - self.output_err += ''.join(exc) + self.output_err += u''.join(exc) self.logger.error(exc) finally: shutil.rmtree(temp_dir) @@ -736,24 +739,24 @@ class CommandBase(object): pdir = self.project_dir() version = self.get_setting('version') desc = self.get_setting('description') - dfile_path = os.path.join(export_dest, name+'.desktop') + dfile_path = os.path.join(export_dest, u'{}.desktop'.format(name)) file_str = ( - '[Desktop Entry]\n' - 'Version={}\n' - 'Name={}\n' - 'Comment={}\n' - 'Exec={}\n' - 'Icon={}\n' - 'Terminal=false\n' - 'Type=Application\n' - 'Categories=Utility;Application;\n' + u'[Desktop Entry]\n' + u'Version={}\n' + u'Name={}\n' + u'Comment={}\n' + u'Exec={}\n' + u'Icon={}\n' + u'Terminal=false\n' + u'Type=Application\n' + u'Categories=Utility;Application;\n' ) file_str = file_str.format(version.value, name, desc.value, nw_path, icon_path) - with open(dfile_path, 'w+') as f: + with codecs.open(dfile_path, 'w+', encoding='utf-8') as f: f.write(file_str) os.chmod(dfile_path, 0755) @@ -776,7 +779,7 @@ class CommandBase(object): if upx_version is not None: upx_bin = os.path.join('files', 'compressors', upx_version) os.chmod(upx_bin, 0755) - cmd = [upx_bin, '--lzma', '-{}'.format(compression.value), str(nw_path)] + cmd = [upx_bin, '--lzma', u'-{}'.format(compression.value), unicode(nw_path)] if platform.system() == 'Windows': startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW @@ -799,7 +802,7 @@ class CommandBase(object): def copy_files_to_project_folder(self): old_dir = CWD os.chdir(self.project_dir()) - self.logger.info('Copying files to {}'.format(self.project_dir())) + self.logger.info(u'Copying files to {}'.format(self.project_dir())) for sgroup in self.settings['setting_groups']: for setting in sgroup.values(): if setting.type == 'file' and setting.value: @@ -807,9 +810,9 @@ class CommandBase(object): if os.path.isabs(f_path): try: shutil.copy(setting.value, self.project_dir()) - self.logger.info('Copying file {} to {}'.format(setting.value, self.project_dir())) + self.logger.info(u'Copying file {} to {}'.format(setting.value, self.project_dir())) except shutil.Error as e: # same file warning - self.logger.warning('Warning: {}'.format(e)) + self.logger.warning(u'Warning: {}'.format(e)) finally: setting.value = os.path.basename(setting.value) @@ -818,7 +821,7 @@ class CommandBase(object): def convert_val_to_str(self, val): if isinstance(val, (list, tuple)): return ', '.join(val) - return str(val).replace(self.project_dir()+os.path.sep, '') + return unicode(val).replace(self.project_dir()+os.path.sep, '') def export(self): self.get_files_to_download() @@ -826,7 +829,7 @@ class CommandBase(object): if res: self.make_output_dirs() self.progress_text = '\nDone!\n' - self.progress_text = 'Output directory is {}/{}.\n'.format(self.output_dir(), + self.progress_text = u'Output directory is {}/{}.\n'.format(self.output_dir(), self.project_name()) self.delete_files() @@ -849,7 +852,7 @@ class CommandBase(object): return self.extract_files() def download_file(self, path, setting): - self.logger.info('Downloading file {}.'.format(path)) + self.logger.info(u'Downloading file {}.'.format(path)) location = self.get_setting('download_dir').value @@ -874,7 +877,7 @@ class CommandBase(object): forced = self.get_setting('force_download').value if (archive_exists or dest_files_exist) and not forced: - self.logger.info('File {} already downloaded. Continuing...'.format(path)) + self.logger.info(u'File {} already downloaded. Continuing...'.format(path)) return self.continue_downloading_or_extract() elif tmp_exists and (os.stat(tmp_file).st_size > 0): tmp_size = os.stat(tmp_file).st_size @@ -893,9 +896,9 @@ class CommandBase(object): downloaded = '' if tmp_size: self.progress_text = 'Resuming previous download...\n' - self.progress_text = 'Already downloaded {:.2f} MB\n'.format(tmp_size/1000000.0) - self.progress_text = ('Downloading: {}, ' - 'Size: {:.2f} MB {}\n'.format(short_name, + self.progress_text = u'Already downloaded {:.2f} MB\n'.format(tmp_size/1000000.0) + self.progress_text = (u'Downloading: {}, ' + u'Size: {:.2f} MB {}\n'.format(short_name, MB, downloaded)) @@ -978,16 +981,16 @@ if __name__ == '__main__': else 'store_false') kwargs.update({'action': action}) if setting.default_value is True: - option_name = 'disable-{}'.format(option_name) + option_name = u'disable-{}'.format(option_name) else: if setting.values: kwargs.update({'choices': setting.values}) - setting.description += ' Possible values: {{{}}}'.format(', '.join([str(x) for x in setting.values])) + setting.description += u' Possible values: {{{}}}'.format(', '.join([unicode(x) for x in setting.values])) kwargs.update({'metavar': ''}) else: kwargs.update({'metavar': '<{}>'.format(setting.display_name)}) - parser.add_argument('--{}'.format(option_name), + parser.add_argument(u'--{}'.format(option_name), dest=setting_name, help=setting.description, **kwargs @@ -1022,8 +1025,8 @@ if __name__ == '__main__': logger.addHandler(handler) def my_excepthook(type_, value, tback): - output_err = ''.join(traceback.format_exception(type_, value, tback)) - logger.error('{}'.format(output_err)) + output_err = u''.join(traceback.format_exception(type_, value, tback)) + logger.error(u'{}'.format(output_err)) sys.__excepthook__(type_, value, tback) sys.excepthook = my_excepthook diff --git a/main.py b/main.py index aec4117..e95715f 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ import os import re import glob import sys +import codecs from PySide import QtGui, QtCore from PySide.QtGui import QApplication, QHBoxLayout, QVBoxLayout @@ -56,7 +57,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): files = [] history_file = get_file('files/recent_files.txt') - with open(history_file, 'a+') as f: + with codecs.open(history_file, 'a+', 'utf-8') as f: for line in f: line = line.strip() if line and os.path.exists(line): @@ -67,7 +68,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): def load_last_project_path(self): proj_path = '' proj_file = get_file('files/last_project_path.txt') - with open(proj_file, 'a+') as f: + with codecs.open(proj_file, 'a+', encoding='utf-8') as f: proj_path = f.read().strip() if not proj_path: @@ -77,28 +78,28 @@ class MainWindow(QtGui.QMainWindow, CommandBase): def save_project_path(self, path): proj_file = get_file('files/last_project_path.txt') - with open(proj_file, 'w+') as f: + with codecs.open(proj_file, 'w+', encoding='utf-8') as f: f.write(path) def save_recent_project(self, proj): recent_file_path = get_file('files/recent_files.txt') max_length = MAX_RECENT - recent_files = open(recent_file_path, 'a+').read().split('\n') + recent_files = codecs.open(recent_file_path, 'a+', encoding='utf-8').read().split(u'\n') try: recent_files.remove(proj) except ValueError: pass recent_files.append(proj) - with open(recent_file_path, 'w+') as f: + with codecs.open(recent_file_path, 'w+', encoding='utf-8') as f: for recent_file in recent_files[-max_length:]: if recent_file and os.path.exists(recent_file): - f.write(recent_file+'\n') + f.write(u'{}\n'.format(recent_file)) def update_recent_files(self): previous_files = self.load_recent_projects() self.recent_separator.setVisible(len(previous_files) > 0) for i in xrange(len(previous_files)): - text = '{} - {}'.format(i+1, os.path.basename(previous_files[i])) + text = u'{} - {}'.format(i+1, os.path.basename(previous_files[i])) action = self.recent_file_actions[i] action.setText(text) action.setData(previous_files[i]) @@ -169,7 +170,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): self.option_settings_enabled(False) - self.setWindowTitle("Web2Executable {}".format(__gui_version__)) + self.setWindowTitle(u"Web2Executable {}".format(__gui_version__)) self.update_nw_versions(None) def open_recent_file(self): @@ -180,9 +181,10 @@ class MainWindow(QtGui.QMainWindow, CommandBase): def setup_nw_versions(self): nw_version = self.get_setting('nw_version') try: - f = open(get_file('files/nw-versions.txt')) + f = codecs.open(get_file('files/nw-versions.txt'), encoding='utf-8') for line in f: nw_version.values.append(line.strip()) + f.close() except IOError: nw_version.values.append(nw_version.default_value) @@ -284,7 +286,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): self.cancel_button.setEnabled(False) def show_error(self, exception): - QtGui.QMessageBox.information(self, 'Error!', str(exception)) + QtGui.QMessageBox.information(self, 'Error!', unicode(exception)) def disable_ui_while_working(self): self.option_settings_enabled(False) @@ -315,7 +317,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): for sgroup in self.settings['setting_groups']: for sname, setting in sgroup.items(): setting_path = os.path.join(self.project_dir(), - str(setting.value)) + unicode(setting.value)) if setting.required and not setting.value: settings_valid = False @@ -334,7 +336,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): widget = self.find_child_by_name(setting.name) if widget is not None: widget.setStyleSheet(red_border) - widget.setToolTip('The file "{}" does not exist.'.format(os.path.join(self.project_dir(),setting.value))) + widget.setToolTip(u'The file "{}" does not exist.'.format(os.path.join(self.project_dir(),setting.value))) tab = self.get_tab_index_for_setting_name(setting.name) self.tab_widget.setTabIcon(tab, self.warning_settings_icon) @@ -345,7 +347,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): widget = self.find_child_by_name(setting.name) if widget is not None: widget.setStyleSheet(red_border) - widget.setToolTip('The folder "{}" does not exist'.format(os.path.join(self.project_dir(), setting.value))) + widget.setToolTip(u'The folder "{}" does not exist'.format(os.path.join(self.project_dir(), setting.value))) tab = self.get_tab_index_for_setting_name(setting.name) self.tab_widget.setTabIcon(tab, self.warning_settings_icon) if settings_valid: @@ -454,7 +456,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): def read_response_header(self, response_header): # Check for genuine error conditions. if response_header.statusCode() not in (200, 300, 301, 302, 303, 307): - self.show_error('Download failed: {}.'.format(response_header.reasonPhrase())) + self.show_error(u'Download failed: {}.'.format(response_header.reasonPhrase())) self.http_request_aborted = True self.http.abort() self.enable_ui_after_error() @@ -475,7 +477,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): if error: self.out_file.remove() - self.show_error('Download failed: {}.'.format(self.http.errorString())) + self.show_error(u'Download failed: {}.'.format(self.http.errorString())) self.enable_ui_after_error() else: self.continue_downloading_or_extract() @@ -499,7 +501,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): @progress_text.setter def progress_text(self, value): - self.progress_label.setText(str(value)) + self.progress_label.setText(unicode(value)) def run_in_background(self, method_name, callback): self.thread = BackgroundThread(self, method_name) @@ -581,7 +583,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): if minor >= 12: path = path.replace('node-webkit', 'nwjs') - self.progress_text = 'Downloading {}'.format(path.replace(version_file, '')) + self.progress_text = u'Downloading {}'.format(path.replace(version_file, '')) url = QUrl(path) file_name = setting.save_file_path(self.selected_version(), location) @@ -603,7 +605,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): self.out_file = QFile(file_name) if not self.out_file.open(QIODevice.WriteOnly): error = self.out_file.error().name - self.show_error('Unable to save the file {}: {}.'.format(file_name, + self.show_error(u'Unable to save the file {}: {}.'.format(file_name, error)) self.out_file = None self.enable_ui() @@ -618,9 +620,9 @@ class MainWindow(QtGui.QMainWindow, CommandBase): path = QUrl.toPercentEncoding(url.path(), "!$&'()*+,;=:@/") if path: - path = str(path) + path = unicode(path) else: - path = '/' + path = u'/' # Download the file. self.http_get_id = self.http.get(path, self.out_file) @@ -944,7 +946,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): text.textChanged.connect(self.call_with_object('setting_changed', text, setting)) if setting.value: - text.setText(str(setting.value)) + text.setText(unicode(setting.value)) text.setStatusTip(setting.description) text.setToolTip(setting.description) @@ -968,7 +970,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): text, setting)) if setting.value: - text.setText(str(setting.value)) + text.setText(unicode(setting.value)) text.setStatusTip(setting.description) text.setToolTip(setting.description) @@ -996,7 +998,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): text, setting)) if setting.value: - text.setText(str(setting.value)) + text.setText(unicode(setting.value)) text.setStatusTip(setting.description) text.setToolTip(setting.description) @@ -1024,7 +1026,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): old_val = setting.default_value setting.value = old_val.replace('\\', '\\\\') - widget.setText(str(old_val)) + widget.setText(unicode(old_val)) elif setting.type == 'check': old_val = False @@ -1045,10 +1047,10 @@ class MainWindow(QtGui.QMainWindow, CommandBase): def set_kiosk_emulation_options(self, is_checked): if is_checked: width_field = self.find_child_by_name('width') - width_field.setText(str(self.desktop_width)) + width_field.setText(unicode(self.desktop_width)) height_field = self.find_child_by_name('height') - height_field.setText(str(self.desktop_height)) + height_field.setText(unicode(self.desktop_height)) toolbar_field = self.find_child_by_name('toolbar') toolbar_field.setChecked(not is_checked) @@ -1094,7 +1096,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): if self.update_json: json_file = os.path.join(self.project_dir(), 'package.json') - with open(json_file, 'w+') as f: + with codecs.open(json_file, 'w+', encoding='utf-8') as f: f.write(self.generate_json()) self.ex_button.setEnabled(self.required_settings_filled()) @@ -1183,7 +1185,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): slider.setStatusTip(setting.description) slider.setToolTip(setting.description) - range_label = QtGui.QLabel(str(setting.default_value)) + range_label = QtGui.QLabel(unicode(setting.default_value)) range_label.setMaximumWidth(30) slider.valueChanged.connect(self.call_with_object('_update_range_label', @@ -1200,7 +1202,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase): return hlayout def _update_range_label(self, label, value): - label.setText(str(value)) + label.setText(unicode(value)) def load_package_json(self): setting_list = super(MainWindow, self).load_package_json()