From 3bece11b09bc61380a438c7580dc9faf7463015e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 2 Apr 2019 09:31:37 +0530 Subject: [PATCH] py3: Misc fixes Fixes #953 (py3: misc fixes trying to start calibre.gui_launch) --- manual/build.py | 2 +- setup/win-ci.py | 2 +- src/calibre/__init__.py | 60 ++++--------------- src/calibre/ebooks/metadata/opf2.py | 2 +- src/calibre/ebooks/metadata/sources/test.py | 4 +- src/calibre/ebooks/oeb/polish/download.py | 4 +- src/calibre/ebooks/oeb/polish/replace.py | 4 +- src/calibre/gui2/actions/catalog.py | 4 +- src/calibre/gui2/actions/choose_library.py | 4 +- .../gui2/convert/search_and_replace.ui | 2 +- src/calibre/gui2/device.py | 4 +- src/calibre/gui2/dialogs/template_dialog.py | 4 +- src/calibre/gui2/main.py | 6 +- src/calibre/gui2/preferences/coloring.py | 4 +- src/calibre/gui2/tag_browser/view.py | 4 +- src/calibre/gui2/tweak_book/file_list.py | 6 +- src/calibre/gui2/viewer/printing.py | 6 +- src/calibre/library/save_to_disk.py | 4 +- src/calibre/srv/cdb.py | 4 +- src/calibre/srv/content.py | 4 +- src/calibre/utils/filenames.py | 21 ++++--- src/calibre/utils/ipc/__init__.py | 37 +++++++----- src/calibre/utils/zipfile.py | 6 +- 23 files changed, 83 insertions(+), 115 deletions(-) diff --git a/manual/build.py b/manual/build.py index 3d0b9ad5e9..f43ff901ba 100755 --- a/manual/build.py +++ b/manual/build.py @@ -88,7 +88,7 @@ def build_linkcheck(base): def build_man_pages(language, base): - os.environ[b'CALIBRE_BUILD_MAN_PAGES'] = b'1' + os.environ['CALIBRE_BUILD_MAN_PAGES'] = '1' sphinx_build(language, base, builder='man', bdir=language, very_quiet=True) diff --git a/setup/win-ci.py b/setup/win-ci.py index c159acc715..59bf4a1299 100644 --- a/setup/win-ci.py +++ b/setup/win-ci.py @@ -197,7 +197,7 @@ def sanitize_path(): paths = r'{0}\private\python\DLLs {0}\private\python\Lib\site-packages\pywin32_system32 {0}\bin {0}\qt\bin C:\Windows\System32'.format( sw ).split() + needed_paths - os.environ[b'PATH'] = os.pathsep.join(paths).encode('ascii') + os.environ['PATH'] = os.pathsep.join(paths) print('PATH:', os.environ[b'PATH']) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index e9b10df6c8..7fc64c0e5f 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -112,57 +112,28 @@ def confirm_config_name(name): return name + '_again' -_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') -_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<', - u'"', u':', u'>', u'+', u'/'] + list(map(codepoint_to_chr, range(32)))) +_filename_sanitize_unicode = frozenset((u'\\', u'|', u'?', u'*', u'<', + u'"', u':', u'>', u'+', u'/') + tuple(map(codepoint_to_chr, range(32)))) -def sanitize_file_name(name, substitute='_', as_unicode=False): +def sanitize_file_name(name, substitute=u'_'): ''' Sanitize the filename `name`. All invalid characters are replaced by `substitute`. The set of invalid characters is the union of the invalid characters in Windows, - OS X and Linux. Also removes leading and trailing whitespace. - **WARNING:** This function also replaces path separators, so only pass file names - and not full paths to it. - *NOTE:* This function always returns byte strings, not unicode objects. The byte strings - are encoded in the filesystem encoding of the platform, or UTF-8. - ''' - if isinstance(name, unicode_type): - name = name.encode(filesystem_encoding, 'ignore') - one = _filename_sanitize.sub(substitute, name) - one = re.sub(r'\s', ' ', one).strip() - bname, ext = os.path.splitext(one) - one = re.sub(r'^\.+$', '_', bname) - if as_unicode: - one = one.decode(filesystem_encoding) - one = one.replace('..', substitute) - one += ext - # Windows doesn't like path components that end with a period - if one and one[-1] in ('.', ' '): - one = one[:-1]+'_' - # Names starting with a period are hidden on Unix - if one.startswith('.'): - one = '_' + one[1:] - return one - - -def sanitize_file_name_unicode(name, substitute='_'): - ''' - Sanitize the filename `name`. All invalid characters are replaced by `substitute`. - The set of invalid characters is the union of the invalid characters in Windows, - OS X and Linux. Also removes leading and trailing whitespace. + macOS and Linux. Also removes leading and trailing whitespace. **WARNING:** This function also replaces path separators, so only pass file names and not full paths to it. ''' if isbytestring(name): - return sanitize_file_name(name, substitute=substitute, as_unicode=True) - chars = [substitute if c in _filename_sanitize_unicode else c for c in - name] + name = name.decode(filesystem_encoding, 'replace') + if isbytestring(substitute): + substitute = substitute.decode(filesystem_encoding, 'replace') + chars = (substitute if c in _filename_sanitize_unicode else c for c in name) one = u''.join(chars) - one = re.sub(r'\s', ' ', one).strip() + one = re.sub(r'\s', u' ', one).strip() bname, ext = os.path.splitext(one) - one = re.sub(r'^\.+$', '_', bname) - one = one.replace('..', substitute) + one = re.sub(r'^\.+$', u'_', bname) + one = one.replace(u'..', substitute) one += ext # Windows doesn't like path components that end with a period or space if one and one[-1] in ('.', ' '): @@ -173,14 +144,7 @@ def sanitize_file_name_unicode(name, substitute='_'): return one -def sanitize_file_name2(name, substitute='_'): - ''' - Sanitize filenames removing invalid chars. Keeps unicode names as unicode - and bytestrings as bytestrings - ''' - if isbytestring(name): - return sanitize_file_name(name, substitute=substitute) - return sanitize_file_name_unicode(name, substitute=substitute) +sanitize_file_name2 = sanitize_file_name_unicode = sanitize_file_name def prints(*args, **kwargs): diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 97227867eb..e0cbbd304a 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -1759,7 +1759,7 @@ class OPFTest(unittest.TestCase): def setUp(self): self.stream = io.BytesIO( -'''\ +b'''\ diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py index fcd4df3a42..90d81f54ac 100644 --- a/src/calibre/ebooks/metadata/sources/test.py +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -11,7 +11,7 @@ import os, tempfile, time from threading import Event from calibre.customize.ui import all_metadata_plugins -from calibre import prints, sanitize_file_name2 +from calibre import prints, sanitize_file_name from calibre.ebooks.metadata import check_isbn from calibre.ebooks.metadata.sources.base import create_log, get_cached_cover_urls from calibre.ebooks.metadata.sources.prefs import msprefs @@ -320,7 +320,7 @@ def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None, # {{{ elif results: cdata = results[0] cover = os.path.join(tdir, plugin.name.replace(' ', - '')+'-%s-cover.jpg'%sanitize_file_name2(mi.title.replace(' ', + '')+'-%s-cover.jpg'%sanitize_file_name(mi.title.replace(' ', '_'))) with open(cover, 'wb') as f: f.write(cdata[-1]) diff --git a/src/calibre/ebooks/oeb/polish/download.py b/src/calibre/ebooks/oeb/polish/download.py index ce624b8d0c..b8cf0fc902 100644 --- a/src/calibre/ebooks/oeb/polish/download.py +++ b/src/calibre/ebooks/oeb/polish/download.py @@ -17,7 +17,7 @@ from io import BytesIO from multiprocessing.dummy import Pool from tempfile import NamedTemporaryFile -from calibre import as_unicode, sanitize_file_name2 +from calibre import as_unicode, sanitize_file_name as sanitize_file_name_base from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, barename, iterlinks from calibre.ebooks.oeb.polish.utils import guess_type from calibre.ptempfile import TemporaryDirectory @@ -97,7 +97,7 @@ class ProgressTracker(object): def sanitize_file_name(x): from calibre.ebooks.oeb.polish.check.parsing import make_filename_safe - x = sanitize_file_name2(x) + x = sanitize_file_name_base(x) while '..' in x: x = x.replace('..', '.') return make_filename_safe(x) diff --git a/src/calibre/ebooks/oeb/polish/replace.py b/src/calibre/ebooks/oeb/polish/replace.py index 4fc7f372f7..4a88cb884e 100644 --- a/src/calibre/ebooks/oeb/polish/replace.py +++ b/src/calibre/ebooks/oeb/polish/replace.py @@ -12,7 +12,7 @@ from polyglot.builtins import iteritems, itervalues, map from functools import partial from collections import Counter, defaultdict -from calibre import sanitize_file_name_unicode +from calibre import sanitize_file_name from calibre.ebooks.chardet import strip_encoding_declarations from calibre.ebooks.oeb.polish.css import iter_declarations, remove_property_value from calibre.ebooks.oeb.polish.utils import extract @@ -207,7 +207,7 @@ def rename_files(container, file_map): def replace_file(container, name, path, basename, force_mt=None): dirname, base = name.rpartition('/')[0::2] - nname = sanitize_file_name_unicode(basename) + nname = sanitize_file_name(basename) if dirname: nname = dirname + '/' + nname with open(path, 'rb') as src: diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index 0bf4f620b5..087710f997 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -13,7 +13,7 @@ from calibre.gui2 import choose_dir, error_dialog, warning_dialog from calibre.gui2.tools import generate_catalog from calibre.utils.config import dynamic from calibre.gui2.actions import InterfaceAction -from calibre import sanitize_file_name_unicode +from calibre import sanitize_file_name from polyglot.builtins import range @@ -96,7 +96,7 @@ class GenerateCatalogAction(InterfaceAction): title=job.catalog_title, fmt=job.fmt.lower())) if export_dir: destination = os.path.join(export_dir, '%s.%s' % ( - sanitize_file_name_unicode(job.catalog_title), job.fmt.lower())) + sanitize_file_name(job.catalog_title), job.fmt.lower())) try: shutil.copyfile(job.catalog_file_path, destination) except EnvironmentError as err: diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index a562958470..3ea3a6ddd9 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -13,7 +13,7 @@ from PyQt5.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize, QCoreApplication, pyqtSignal, QVBoxLayout, QTimer, QAction) -from calibre import isbytestring, sanitize_file_name_unicode +from calibre import isbytestring, sanitize_file_name from calibre.constants import (filesystem_encoding, iswindows, get_portable_base, isportable) from calibre.utils.config import prefs, tweaks from calibre.utils.icu import sort_key @@ -418,7 +418,7 @@ class ChooseLibraryAction(InterfaceAction): 'Choose a new name for the library %s. ')%name + '

'+_( 'Note that the actual library folder will be renamed.'), text=old_name) - newname = sanitize_file_name_unicode(unicode_type(newname)) + newname = sanitize_file_name(unicode_type(newname)) if not ok or not newname or newname == old_name: return newloc = os.path.join(base, newname) diff --git a/src/calibre/gui2/convert/search_and_replace.ui b/src/calibre/gui2/convert/search_and_replace.ui index 4ad7351055..8af21120c1 100644 --- a/src/calibre/gui2/convert/search_and_replace.ui +++ b/src/calibre/gui2/convert/search_and_replace.ui @@ -254,7 +254,7 @@ RegexEdit QWidget -

regex_builder.h
+
calibre/gui2/convert/regex_builder.h
1 diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 13717fdc49..096cec358d 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -24,7 +24,7 @@ from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic, warning_dialog, info_dialog, choose_dir, FunctionDispatcher, show_restart_warning, gprefs, question_dialog) from calibre.ebooks.metadata import authors_to_string -from calibre import preferred_encoding, prints, force_unicode, as_unicode, sanitize_file_name2 +from calibre import preferred_encoding, prints, force_unicode, as_unicode, sanitize_file_name from calibre.utils.filenames import ascii_filename from calibre.devices.errors import (FreeSpaceError, WrongDestinationError, BlacklistedDevice) @@ -627,7 +627,7 @@ class DeviceManager(Thread): # {{{ def _save_books(self, paths, target): '''Copy books from device to disk''' for path in paths: - name = sanitize_file_name2(os.path.basename(path)) + name = sanitize_file_name(os.path.basename(path)) dest = os.path.join(target, name) if os.path.abspath(dest) != os.path.abspath(path): with lopen(dest, 'wb') as f: diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 0383f047b8..7f8838933a 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -9,7 +9,7 @@ from PyQt5.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont, QRegExp, QApplication, QTextCharFormat, QColor, QCursor, QIcon, QSize) -from calibre import sanitize_file_name_unicode +from calibre import sanitize_file_name from calibre.constants import config_dir from calibre.gui2 import gprefs from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog @@ -359,7 +359,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): all_files=False, select_only_single_file=True) if path: icon_path = path[0] - icon_name = sanitize_file_name_unicode( + icon_name = sanitize_file_name( os.path.splitext( os.path.basename(icon_path))[0]+'.png') if icon_name not in self.icon_file_names: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index e57bc06bc7..6ce5f0be95 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -357,7 +357,7 @@ def run_in_debug_mode(): import tempfile, subprocess fd, logpath = tempfile.mkstemp('.txt') os.close(fd) - os.environ[b'CALIBRE_RESTARTING_FROM_GUI'] = b'1' + os.environ['CALIBRE_RESTARTING_FROM_GUI'] = environ_item('1') run_calibre_debug( '--gui-debug', logpath, stdout=lopen(logpath, 'w'), stderr=subprocess.STDOUT, stdin=lopen(os.devnull, 'r')) @@ -405,7 +405,7 @@ def run_gui(opts, args, listener, app, gui_debug=None): prints('Restarting with:', app) subprocess.Popen('sleep 3s; open ' + shellquote(app), shell=True) else: - os.environ[b'CALIBRE_RESTARTING_FROM_GUI'] = b'1' + os.environ['CALIBRE_RESTARTING_FROM_GUI'] = environ_item('1') if iswindows and hasattr(winutil, 'prepare_for_restart'): winutil.prepare_for_restart() args = ['-g'] if os.path.splitext(e)[0].endswith('-debug') else [] @@ -509,7 +509,7 @@ def create_listener(): def main(args=sys.argv): - if os.environ.pop(b'CALIBRE_RESTARTING_FROM_GUI', None) == b'1': + if os.environ.pop('CALIBRE_RESTARTING_FROM_GUI', None) == environ_item('1'): time.sleep(2) # give the parent process time to cleanup and close if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ: windows_repair() diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py index 16ed941465..43aa34edd1 100644 --- a/src/calibre/gui2/preferences/coloring.py +++ b/src/calibre/gui2/preferences/coloring.py @@ -15,7 +15,7 @@ from PyQt5.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize, QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem, QApplication, QStandardItem, QStandardItemModel, QCheckBox, QMenu) -from calibre import prepare_string_for_xml, sanitize_file_name_unicode, as_unicode +from calibre import prepare_string_for_xml, sanitize_file_name, as_unicode from calibre.constants import config_dir from calibre.utils.icu import sort_key from calibre.gui2 import error_dialog, choose_files, pixmap_to_data, gprefs, choose_save_file @@ -501,7 +501,7 @@ class RuleEditor(QDialog): # {{{ '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample text'))) def sanitize_icon_file_name(self, icon_path): - n = lower(sanitize_file_name_unicode( + n = lower(sanitize_file_name( os.path.splitext( os.path.basename(icon_path))[0]+'.png')) return n.replace("'", '_') diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index 5f438a3ba0..8a9d697ceb 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -16,7 +16,7 @@ from PyQt5.Qt import ( QLinearGradient, QPalette, QColor, QPen, QBrush, QFont ) -from calibre import sanitize_file_name_unicode +from calibre import sanitize_file_name from calibre.constants import config_dir from calibre.ebooks.metadata import rating_to_stars from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES, @@ -375,7 +375,7 @@ class TagsView(QTreeView): # {{{ d = os.path.join(config_dir, 'tb_icons') if not os.path.exists(d): os.makedirs(d) - with open(os.path.join(d, 'icon_' + sanitize_file_name_unicode(key)+'.png'), 'wb') as f: + with open(os.path.join(d, 'icon_' + sanitize_file_name(key)+'.png'), 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) path = os.path.basename(f.name) self._model.set_custom_category_icon(key, unicode_type(path)) diff --git a/src/calibre/gui2/tweak_book/file_list.py b/src/calibre/gui2/tweak_book/file_list.py index e2a53a35d1..16857998db 100644 --- a/src/calibre/gui2/tweak_book/file_list.py +++ b/src/calibre/gui2/tweak_book/file_list.py @@ -18,7 +18,7 @@ from PyQt5.Qt import ( QVBoxLayout, QWidget, pyqtSignal ) -from calibre import human_readable, plugins, sanitize_file_name_unicode +from calibre import human_readable, plugins, sanitize_file_name from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES from calibre.ebooks.oeb.polish.container import OEB_FONTS, guess_type from calibre.ebooks.oeb.polish.cover import ( @@ -73,7 +73,7 @@ def name_is_ok(name, show_error): norm = name.replace('\\', '/') parts = name.split('/') for x in parts: - if sanitize_file_name_unicode(x) != x: + if sanitize_file_name(x) != x: return show_error(_('The file name contains invalid characters')) and False if current_container().has_name(norm): return show_error(_('This file name already exists in the book')) and False @@ -81,7 +81,7 @@ def name_is_ok(name, show_error): return True -def get_bulk_rename_settings(parent, number, msg=None, sanitize=sanitize_file_name_unicode, +def get_bulk_rename_settings(parent, number, msg=None, sanitize=sanitize_file_name, leading_zeros=True, prefix=None, category='text', allow_spine_order=False): # {{{ d = QDialog(parent) d.setWindowTitle(_('Bulk rename items')) diff --git a/src/calibre/gui2/viewer/printing.py b/src/calibre/gui2/viewer/printing.py index 56b80a6d35..30d4cba5b0 100644 --- a/src/calibre/gui2/viewer/printing.py +++ b/src/calibre/gui2/viewer/printing.py @@ -13,7 +13,7 @@ from PyQt5.Qt import ( QFormLayout, QLineEdit, QToolButton, QHBoxLayout, QLabel, QIcon, QPrinter, QPageSize, QComboBox, QDoubleSpinBox, QCheckBox, QProgressDialog, QTimer) -from calibre import sanitize_file_name2 +from calibre import sanitize_file_name from calibre.ptempfile import PersistentTemporaryFile from calibre.ebooks.conversion.plugins.pdf_output import PAPER_SIZES from calibre.gui2 import elided_text, error_dialog, choose_save_file, Application, open_local_file, dynamic @@ -31,7 +31,7 @@ class PrintDialog(Dialog): def __init__(self, book_title, parent=None, prefs=vprefs): self.book_title = book_title - self.default_file_name = sanitize_file_name2(book_title[:75] + '.pdf') + self.default_file_name = sanitize_file_name(book_title[:75] + '.pdf') self.paper_size_map = {a:getattr(QPageSize, a.capitalize()) for a in PAPER_SIZES} Dialog.__init__(self, _('Print to PDF'), 'print-to-pdf', prefs=prefs, parent=parent) @@ -91,7 +91,7 @@ class PrintDialog(Dialog): def data(self): fpath = self.file_name.text().strip() head, tail = os.path.split(fpath) - tail = sanitize_file_name2(tail) + tail = sanitize_file_name(tail) fpath = tail if head: fpath = os.path.join(head, tail) diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 930c275a17..338903af0b 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -17,7 +17,7 @@ from calibre.constants import preferred_encoding from calibre.ebooks.metadata import fmt_sidx from calibre.ebooks.metadata import title_sort from calibre.utils.date import as_local_time -from calibre import strftime, prints, sanitize_file_name_unicode +from calibre import strftime, prints, sanitize_file_name from calibre.db.lazy import FormatsList from polyglot.builtins import unicode_type @@ -279,7 +279,7 @@ def save_book_to_disk(book_id, db, root, opts, length): def get_path_components(opts, mi, book_id, path_length): try: components = get_components(opts.template, mi, book_id, opts.timefmt, path_length, - ascii_filename if opts.asciiize else sanitize_file_name_unicode, + ascii_filename if opts.asciiize else sanitize_file_name, to_lowercase=opts.to_lowercase, replace_whitespace=opts.replace_whitespace, safe_format=False, last_has_extension=False, single_dir=opts.single_dir) diff --git a/src/calibre/srv/cdb.py b/src/calibre/srv/cdb.py index 0f79010be2..12aa557021 100644 --- a/src/calibre/srv/cdb.py +++ b/src/calibre/srv/cdb.py @@ -8,7 +8,7 @@ import os from functools import partial from io import BytesIO -from calibre import as_unicode, sanitize_file_name_unicode +from calibre import as_unicode, sanitize_file_name from calibre.db.cli import module_for_cmd from calibre.ebooks.metadata.meta import get_metadata from calibre.srv.changes import books_added, books_deleted, metadata @@ -77,7 +77,7 @@ def cdb_add_book(ctx, rd, job_id, add_duplicates, filename, library_id): raise HTTPForbidden('Cannot use the add book interface with a user who has per library restrictions') if not filename: raise HTTPBadRequest('An empty filename is not allowed') - sfilename = sanitize_file_name_unicode(filename) + sfilename = sanitize_file_name(filename) fmt = os.path.splitext(sfilename)[1] fmt = fmt[1:] if fmt else None if not fmt: diff --git a/src/calibre/srv/content.py b/src/calibre/srv/content.py index aa54b122a4..b32e3fc0c6 100644 --- a/src/calibre/srv/content.py +++ b/src/calibre/srv/content.py @@ -12,7 +12,7 @@ from threading import Lock from polyglot.builtins import map from functools import partial -from calibre import fit_image, sanitize_file_name_unicode +from calibre import fit_image, sanitize_file_name from calibre.constants import config_dir, iswindows from calibre.db.errors import NoSuchFormat from calibre.ebooks.covers import cprefs, override_prefs, scale_cover, generate_cover, set_use_roman @@ -166,7 +166,7 @@ def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False): fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext) if as_encoded_unicode: # See https://tools.ietf.org/html/rfc6266 - fname = sanitize_file_name_unicode(fname).encode('utf-8') + fname = sanitize_file_name(fname).encode('utf-8') fname = quote(fname).decode('ascii') else: fname = ascii_filename(fname).replace('"', '_') diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 7fc69c0123..a355c6aa56 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -21,22 +21,21 @@ def ascii_text(orig): udc = get_udc() try: ascii = udc.decode(orig) - except: + except Exception: if isinstance(orig, unicode_type): orig = orig.encode('ascii', 'replace') - ascii = orig.decode(preferred_encoding, - 'replace').encode('ascii', 'replace') + ascii = orig.decode(preferred_encoding, 'replace') + if isinstance(ascii, bytes): + ascii = ascii.decode('ascii', 'replace') return ascii -def ascii_filename(orig, substitute='_'): - ans = [] - orig = ascii_text(orig).replace('?', '_') - for x in orig: - if ord(x) < 32: - x = substitute - ans.append(x) - return sanitize_file_name(''.join(ans), substitute=substitute) +def ascii_filename(orig, substitute=u'_'): + if isinstance(substitute, bytes): + substitute = substitute.decode(filesystem_encoding) + orig = ascii_text(orig).replace(u'?', u'_') + ans = u''.join(x if ord(x) >= 32 else substitute for x in orig) + return sanitize_file_name(ans, substitute=substitute) def shorten_component(s, by_what): diff --git a/src/calibre/utils/ipc/__init__.py b/src/calibre/utils/ipc/__init__.py index 2dca6bf5f9..12aab4226b 100644 --- a/src/calibre/utils/ipc/__init__.py +++ b/src/calibre/utils/ipc/__init__.py @@ -10,10 +10,10 @@ import os, errno from threading import Thread from calibre import force_unicode -from calibre.constants import iswindows, get_windows_username, islinux +from calibre.constants import iswindows, get_windows_username, islinux, filesystem_encoding, ispy3 from calibre.utils.filenames import ascii_filename -ADDRESS = VADDRESS = None +VADDRESS = None def eintr_retry_call(func, *args, **kwargs): @@ -27,10 +27,9 @@ def eintr_retry_call(func, *args, **kwargs): def gui_socket_address(): - global ADDRESS - if ADDRESS is None: + if gui_socket_address.ans is None: if iswindows: - ADDRESS = r'\\.\pipe\CalibreGUI' + gui_socket_address.ans = r'\\.\pipe\CalibreGUI' try: user = get_windows_username() except: @@ -38,25 +37,26 @@ def gui_socket_address(): if user: user = ascii_filename(user).replace(' ', '_') if user: - ADDRESS += '-' + user[:100] + 'x' + gui_socket_address.ans += '-' + user[:100] + 'x' else: user = os.environ.get('USER', '') if not user: user = os.path.basename(os.path.expanduser('~')) if islinux: - ADDRESS = (u'\0%s-calibre-gui.socket' % ascii_filename(force_unicode(user))).encode('ascii') + gui_socket_address.ans = (u'\0%s-calibre-gui.socket' % ascii_filename(force_unicode(user))) else: from tempfile import gettempdir tmp = gettempdir() - ADDRESS = os.path.join(tmp, user+'-calibre-gui.socket') - return ADDRESS + gui_socket_address.ans = os.path.join(tmp, user+'-calibre-gui.socket') + if not ispy3 and not isinstance(gui_socket_address.ans, bytes): + gui_socket_address.ans = gui_socket_address.ans.encode(filesystem_encoding) + return gui_socket_address.ans def viewer_socket_address(): - global VADDRESS - if VADDRESS is None: + if viewer_socket_address.ans is None: if iswindows: - VADDRESS = r'\\.\pipe\CalibreViewer' + viewer_socket_address.ans = r'\\.\pipe\CalibreViewer' try: user = get_windows_username() except: @@ -64,18 +64,23 @@ def viewer_socket_address(): if user: user = ascii_filename(user).replace(' ', '_') if user: - VADDRESS += '-' + user[:100] + 'x' + viewer_socket_address.ans += '-' + user[:100] + 'x' else: user = os.environ.get('USER', '') if not user: user = os.path.basename(os.path.expanduser('~')) if islinux: - VADDRESS = (u'\0%s-calibre-viewer.socket' % ascii_filename(force_unicode(user))).encode('ascii') + viewer_socket_address.ans = (u'\0%s-calibre-viewer.socket' % ascii_filename(force_unicode(user))) else: from tempfile import gettempdir tmp = gettempdir() - VADDRESS = os.path.join(tmp, user+'-calibre-viewer.socket') - return VADDRESS + viewer_socket_address.ans = os.path.join(tmp, user+'-calibre-viewer.socket') + if not ispy3 and not isinstance(viewer_socket_address.ans, bytes): + viewer_socket_address.ans = viewer_socket_address.ans.encode(filesystem_encoding) + return viewer_socket_address.ans + + +gui_socket_address.ans = viewer_socket_address.ans = None class RC(Thread): diff --git a/src/calibre/utils/zipfile.py b/src/calibre/utils/zipfile.py index 4547070193..808812625c 100644 --- a/src/calibre/utils/zipfile.py +++ b/src/calibre/utils/zipfile.py @@ -8,7 +8,7 @@ import binascii from contextlib import closing from tempfile import SpooledTemporaryFile -from calibre import sanitize_file_name2 +from calibre import sanitize_file_name from calibre.constants import filesystem_encoding from calibre.ebooks.chardet import detect from polyglot.builtins import unicode_type, string_or_bytes @@ -1137,7 +1137,7 @@ class ZipFile: os.makedirs(upperdirs) except: # Added by Kovid targetpath = os.path.join(base_target, - sanitize_file_name2(fname)) + sanitize_file_name(fname)) upperdirs = os.path.dirname(targetpath) if upperdirs and not os.path.exists(upperdirs): os.makedirs(upperdirs) @@ -1158,7 +1158,7 @@ class ZipFile: except: # Try sanitizing the file name to remove invalid characters components = list(os.path.split(targetpath)) - components[-1] = sanitize_file_name2(components[-1]) + components[-1] = sanitize_file_name(components[-1]) targetpath = os.sep.join(components) with open(targetpath, 'wb') as target: shutil.copyfileobj(source, target)