py3: Misc fixes

Fixes #953 (py3: misc fixes trying to start calibre.gui_launch)
This commit is contained in:
Kovid Goyal 2019-04-02 09:31:37 +05:30
parent 0e8543030a
commit 3bece11b09
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
23 changed files with 83 additions and 115 deletions

View File

@ -88,7 +88,7 @@ def build_linkcheck(base):
def build_man_pages(language, 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) sphinx_build(language, base, builder='man', bdir=language, very_quiet=True)

View File

@ -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( paths = r'{0}\private\python\DLLs {0}\private\python\Lib\site-packages\pywin32_system32 {0}\bin {0}\qt\bin C:\Windows\System32'.format(
sw sw
).split() + needed_paths ).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']) print('PATH:', os.environ[b'PATH'])

View File

@ -112,57 +112,28 @@ def confirm_config_name(name):
return name + '_again' return name + '_again'
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') _filename_sanitize_unicode = frozenset((u'\\', u'|', u'?', u'*', u'<',
_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<', u'"', u':', u'>', u'+', u'/') + tuple(map(codepoint_to_chr, range(32))))
u'"', u':', u'>', u'+', u'/'] + list(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`. 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, 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.
*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.
**WARNING:** This function also replaces path separators, so only pass file names **WARNING:** This function also replaces path separators, so only pass file names
and not full paths to it. and not full paths to it.
''' '''
if isbytestring(name): if isbytestring(name):
return sanitize_file_name(name, substitute=substitute, as_unicode=True) name = name.decode(filesystem_encoding, 'replace')
chars = [substitute if c in _filename_sanitize_unicode else c for c in if isbytestring(substitute):
name] 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 = u''.join(chars)
one = re.sub(r'\s', ' ', one).strip() one = re.sub(r'\s', u' ', one).strip()
bname, ext = os.path.splitext(one) bname, ext = os.path.splitext(one)
one = re.sub(r'^\.+$', '_', bname) one = re.sub(r'^\.+$', u'_', bname)
one = one.replace('..', substitute) one = one.replace(u'..', substitute)
one += ext one += ext
# Windows doesn't like path components that end with a period or space # Windows doesn't like path components that end with a period or space
if one and one[-1] in ('.', ' '): if one and one[-1] in ('.', ' '):
@ -173,14 +144,7 @@ def sanitize_file_name_unicode(name, substitute='_'):
return one return one
def sanitize_file_name2(name, substitute='_'): sanitize_file_name2 = sanitize_file_name_unicode = sanitize_file_name
'''
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)
def prints(*args, **kwargs): def prints(*args, **kwargs):

View File

@ -1759,7 +1759,7 @@ class OPFTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.stream = io.BytesIO( self.stream = io.BytesIO(
'''\ b'''\
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<package version="2.0" xmlns="http://www.idpf.org/2007/opf" > <package version="2.0" xmlns="http://www.idpf.org/2007/opf" >
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">

View File

@ -11,7 +11,7 @@ import os, tempfile, time
from threading import Event from threading import Event
from calibre.customize.ui import all_metadata_plugins 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 import check_isbn
from calibre.ebooks.metadata.sources.base import create_log, get_cached_cover_urls from calibre.ebooks.metadata.sources.base import create_log, get_cached_cover_urls
from calibre.ebooks.metadata.sources.prefs import msprefs 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: elif results:
cdata = results[0] cdata = results[0]
cover = os.path.join(tdir, plugin.name.replace(' ', 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: with open(cover, 'wb') as f:
f.write(cdata[-1]) f.write(cdata[-1])

View File

@ -17,7 +17,7 @@ from io import BytesIO
from multiprocessing.dummy import Pool from multiprocessing.dummy import Pool
from tempfile import NamedTemporaryFile 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.base import OEB_DOCS, OEB_STYLES, barename, iterlinks
from calibre.ebooks.oeb.polish.utils import guess_type from calibre.ebooks.oeb.polish.utils import guess_type
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
@ -97,7 +97,7 @@ class ProgressTracker(object):
def sanitize_file_name(x): def sanitize_file_name(x):
from calibre.ebooks.oeb.polish.check.parsing import make_filename_safe 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: while '..' in x:
x = x.replace('..', '.') x = x.replace('..', '.')
return make_filename_safe(x) return make_filename_safe(x)

View File

@ -12,7 +12,7 @@ from polyglot.builtins import iteritems, itervalues, map
from functools import partial from functools import partial
from collections import Counter, defaultdict 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.chardet import strip_encoding_declarations
from calibre.ebooks.oeb.polish.css import iter_declarations, remove_property_value from calibre.ebooks.oeb.polish.css import iter_declarations, remove_property_value
from calibre.ebooks.oeb.polish.utils import extract 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): def replace_file(container, name, path, basename, force_mt=None):
dirname, base = name.rpartition('/')[0::2] dirname, base = name.rpartition('/')[0::2]
nname = sanitize_file_name_unicode(basename) nname = sanitize_file_name(basename)
if dirname: if dirname:
nname = dirname + '/' + nname nname = dirname + '/' + nname
with open(path, 'rb') as src: with open(path, 'rb') as src:

View File

@ -13,7 +13,7 @@ from calibre.gui2 import choose_dir, error_dialog, warning_dialog
from calibre.gui2.tools import generate_catalog from calibre.gui2.tools import generate_catalog
from calibre.utils.config import dynamic from calibre.utils.config import dynamic
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
from calibre import sanitize_file_name_unicode from calibre import sanitize_file_name
from polyglot.builtins import range from polyglot.builtins import range
@ -96,7 +96,7 @@ class GenerateCatalogAction(InterfaceAction):
title=job.catalog_title, fmt=job.fmt.lower())) title=job.catalog_title, fmt=job.fmt.lower()))
if export_dir: if export_dir:
destination = os.path.join(export_dir, '%s.%s' % ( 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: try:
shutil.copyfile(job.catalog_file_path, destination) shutil.copyfile(job.catalog_file_path, destination)
except EnvironmentError as err: except EnvironmentError as err:

View File

@ -13,7 +13,7 @@ from PyQt5.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize,
QCoreApplication, pyqtSignal, QVBoxLayout, QTimer, QAction) 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.constants import (filesystem_encoding, iswindows, get_portable_base, isportable)
from calibre.utils.config import prefs, tweaks from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -418,7 +418,7 @@ class ChooseLibraryAction(InterfaceAction):
'Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_( 'Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_(
'Note that the actual library folder will be renamed.'), 'Note that the actual library folder will be renamed.'),
text=old_name) 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: if not ok or not newname or newname == old_name:
return return
newloc = os.path.join(base, newname) newloc = os.path.join(base, newname)

View File

@ -254,7 +254,7 @@
<customwidget> <customwidget>
<class>RegexEdit</class> <class>RegexEdit</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>regex_builder.h</header> <header>calibre/gui2/convert/regex_builder.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>

View File

@ -24,7 +24,7 @@ from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
warning_dialog, info_dialog, choose_dir, FunctionDispatcher, warning_dialog, info_dialog, choose_dir, FunctionDispatcher,
show_restart_warning, gprefs, question_dialog) show_restart_warning, gprefs, question_dialog)
from calibre.ebooks.metadata import authors_to_string 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.utils.filenames import ascii_filename
from calibre.devices.errors import (FreeSpaceError, WrongDestinationError, from calibre.devices.errors import (FreeSpaceError, WrongDestinationError,
BlacklistedDevice) BlacklistedDevice)
@ -627,7 +627,7 @@ class DeviceManager(Thread): # {{{
def _save_books(self, paths, target): def _save_books(self, paths, target):
'''Copy books from device to disk''' '''Copy books from device to disk'''
for path in paths: 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) dest = os.path.join(target, name)
if os.path.abspath(dest) != os.path.abspath(path): if os.path.abspath(dest) != os.path.abspath(path):
with lopen(dest, 'wb') as f: with lopen(dest, 'wb') as f:

View File

@ -9,7 +9,7 @@ from PyQt5.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont,
QRegExp, QApplication, QTextCharFormat, QColor, QCursor, QRegExp, QApplication, QTextCharFormat, QColor, QCursor,
QIcon, QSize) QIcon, QSize)
from calibre import sanitize_file_name_unicode from calibre import sanitize_file_name
from calibre.constants import config_dir from calibre.constants import config_dir
from calibre.gui2 import gprefs from calibre.gui2 import gprefs
from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog 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) all_files=False, select_only_single_file=True)
if path: if path:
icon_path = path[0] icon_path = path[0]
icon_name = sanitize_file_name_unicode( icon_name = sanitize_file_name(
os.path.splitext( os.path.splitext(
os.path.basename(icon_path))[0]+'.png') os.path.basename(icon_path))[0]+'.png')
if icon_name not in self.icon_file_names: if icon_name not in self.icon_file_names:

View File

@ -357,7 +357,7 @@ def run_in_debug_mode():
import tempfile, subprocess import tempfile, subprocess
fd, logpath = tempfile.mkstemp('.txt') fd, logpath = tempfile.mkstemp('.txt')
os.close(fd) os.close(fd)
os.environ[b'CALIBRE_RESTARTING_FROM_GUI'] = b'1' os.environ['CALIBRE_RESTARTING_FROM_GUI'] = environ_item('1')
run_calibre_debug( run_calibre_debug(
'--gui-debug', logpath, stdout=lopen(logpath, 'w'), '--gui-debug', logpath, stdout=lopen(logpath, 'w'),
stderr=subprocess.STDOUT, stdin=lopen(os.devnull, 'r')) 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) prints('Restarting with:', app)
subprocess.Popen('sleep 3s; open ' + shellquote(app), shell=True) subprocess.Popen('sleep 3s; open ' + shellquote(app), shell=True)
else: 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'): if iswindows and hasattr(winutil, 'prepare_for_restart'):
winutil.prepare_for_restart() winutil.prepare_for_restart()
args = ['-g'] if os.path.splitext(e)[0].endswith('-debug') else [] args = ['-g'] if os.path.splitext(e)[0].endswith('-debug') else []
@ -509,7 +509,7 @@ def create_listener():
def main(args=sys.argv): 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 time.sleep(2) # give the parent process time to cleanup and close
if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ: if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ:
windows_repair() windows_repair()

View File

@ -15,7 +15,7 @@ from PyQt5.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem, QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
QApplication, QStandardItem, QStandardItemModel, QCheckBox, QMenu) 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.constants import config_dir
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.gui2 import error_dialog, choose_files, pixmap_to_data, gprefs, choose_save_file 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'))) '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample text')))
def sanitize_icon_file_name(self, icon_path): def sanitize_icon_file_name(self, icon_path):
n = lower(sanitize_file_name_unicode( n = lower(sanitize_file_name(
os.path.splitext( os.path.splitext(
os.path.basename(icon_path))[0]+'.png')) os.path.basename(icon_path))[0]+'.png'))
return n.replace("'", '_') return n.replace("'", '_')

View File

@ -16,7 +16,7 @@ from PyQt5.Qt import (
QLinearGradient, QPalette, QColor, QPen, QBrush, QFont 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.constants import config_dir
from calibre.ebooks.metadata import rating_to_stars from calibre.ebooks.metadata import rating_to_stars
from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES, 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') d = os.path.join(config_dir, 'tb_icons')
if not os.path.exists(d): if not os.path.exists(d):
os.makedirs(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')) f.write(pixmap_to_data(p, format='PNG'))
path = os.path.basename(f.name) path = os.path.basename(f.name)
self._model.set_custom_category_icon(key, unicode_type(path)) self._model.set_custom_category_icon(key, unicode_type(path))

View File

@ -18,7 +18,7 @@ from PyQt5.Qt import (
QVBoxLayout, QWidget, pyqtSignal 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.base import OEB_DOCS, OEB_STYLES
from calibre.ebooks.oeb.polish.container import OEB_FONTS, guess_type from calibre.ebooks.oeb.polish.container import OEB_FONTS, guess_type
from calibre.ebooks.oeb.polish.cover import ( from calibre.ebooks.oeb.polish.cover import (
@ -73,7 +73,7 @@ def name_is_ok(name, show_error):
norm = name.replace('\\', '/') norm = name.replace('\\', '/')
parts = name.split('/') parts = name.split('/')
for x in parts: 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 return show_error(_('The file name contains invalid characters')) and False
if current_container().has_name(norm): if current_container().has_name(norm):
return show_error(_('This file name already exists in the book')) and False 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 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): # {{{ leading_zeros=True, prefix=None, category='text', allow_spine_order=False): # {{{
d = QDialog(parent) d = QDialog(parent)
d.setWindowTitle(_('Bulk rename items')) d.setWindowTitle(_('Bulk rename items'))

View File

@ -13,7 +13,7 @@ from PyQt5.Qt import (
QFormLayout, QLineEdit, QToolButton, QHBoxLayout, QLabel, QIcon, QPrinter, QFormLayout, QLineEdit, QToolButton, QHBoxLayout, QLabel, QIcon, QPrinter,
QPageSize, QComboBox, QDoubleSpinBox, QCheckBox, QProgressDialog, QTimer) 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.ptempfile import PersistentTemporaryFile
from calibre.ebooks.conversion.plugins.pdf_output import PAPER_SIZES 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 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): def __init__(self, book_title, parent=None, prefs=vprefs):
self.book_title = book_title 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} 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) Dialog.__init__(self, _('Print to PDF'), 'print-to-pdf', prefs=prefs, parent=parent)
@ -91,7 +91,7 @@ class PrintDialog(Dialog):
def data(self): def data(self):
fpath = self.file_name.text().strip() fpath = self.file_name.text().strip()
head, tail = os.path.split(fpath) head, tail = os.path.split(fpath)
tail = sanitize_file_name2(tail) tail = sanitize_file_name(tail)
fpath = tail fpath = tail
if head: if head:
fpath = os.path.join(head, tail) fpath = os.path.join(head, tail)

View File

@ -17,7 +17,7 @@ from calibre.constants import preferred_encoding
from calibre.ebooks.metadata import fmt_sidx from calibre.ebooks.metadata import fmt_sidx
from calibre.ebooks.metadata import title_sort from calibre.ebooks.metadata import title_sort
from calibre.utils.date import as_local_time 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 calibre.db.lazy import FormatsList
from polyglot.builtins import unicode_type 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): def get_path_components(opts, mi, book_id, path_length):
try: try:
components = get_components(opts.template, mi, book_id, opts.timefmt, path_length, 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, to_lowercase=opts.to_lowercase,
replace_whitespace=opts.replace_whitespace, safe_format=False, replace_whitespace=opts.replace_whitespace, safe_format=False,
last_has_extension=False, single_dir=opts.single_dir) last_has_extension=False, single_dir=opts.single_dir)

View File

@ -8,7 +8,7 @@ import os
from functools import partial from functools import partial
from io import BytesIO 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.db.cli import module_for_cmd
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.srv.changes import books_added, books_deleted, 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') raise HTTPForbidden('Cannot use the add book interface with a user who has per library restrictions')
if not filename: if not filename:
raise HTTPBadRequest('An empty filename is not allowed') 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 = os.path.splitext(sfilename)[1]
fmt = fmt[1:] if fmt else None fmt = fmt[1:] if fmt else None
if not fmt: if not fmt:

View File

@ -12,7 +12,7 @@ from threading import Lock
from polyglot.builtins import map from polyglot.builtins import map
from functools import partial 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.constants import config_dir, iswindows
from calibre.db.errors import NoSuchFormat from calibre.db.errors import NoSuchFormat
from calibre.ebooks.covers import cprefs, override_prefs, scale_cover, generate_cover, set_use_roman 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) fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext)
if as_encoded_unicode: if as_encoded_unicode:
# See https://tools.ietf.org/html/rfc6266 # 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') fname = quote(fname).decode('ascii')
else: else:
fname = ascii_filename(fname).replace('"', '_') fname = ascii_filename(fname).replace('"', '_')

View File

@ -21,22 +21,21 @@ def ascii_text(orig):
udc = get_udc() udc = get_udc()
try: try:
ascii = udc.decode(orig) ascii = udc.decode(orig)
except: except Exception:
if isinstance(orig, unicode_type): if isinstance(orig, unicode_type):
orig = orig.encode('ascii', 'replace') orig = orig.encode('ascii', 'replace')
ascii = orig.decode(preferred_encoding, ascii = orig.decode(preferred_encoding, 'replace')
'replace').encode('ascii', 'replace') if isinstance(ascii, bytes):
ascii = ascii.decode('ascii', 'replace')
return ascii return ascii
def ascii_filename(orig, substitute='_'): def ascii_filename(orig, substitute=u'_'):
ans = [] if isinstance(substitute, bytes):
orig = ascii_text(orig).replace('?', '_') substitute = substitute.decode(filesystem_encoding)
for x in orig: orig = ascii_text(orig).replace(u'?', u'_')
if ord(x) < 32: ans = u''.join(x if ord(x) >= 32 else substitute for x in orig)
x = substitute return sanitize_file_name(ans, substitute=substitute)
ans.append(x)
return sanitize_file_name(''.join(ans), substitute=substitute)
def shorten_component(s, by_what): def shorten_component(s, by_what):

View File

@ -10,10 +10,10 @@ import os, errno
from threading import Thread from threading import Thread
from calibre import force_unicode 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 from calibre.utils.filenames import ascii_filename
ADDRESS = VADDRESS = None VADDRESS = None
def eintr_retry_call(func, *args, **kwargs): def eintr_retry_call(func, *args, **kwargs):
@ -27,10 +27,9 @@ def eintr_retry_call(func, *args, **kwargs):
def gui_socket_address(): def gui_socket_address():
global ADDRESS if gui_socket_address.ans is None:
if ADDRESS is None:
if iswindows: if iswindows:
ADDRESS = r'\\.\pipe\CalibreGUI' gui_socket_address.ans = r'\\.\pipe\CalibreGUI'
try: try:
user = get_windows_username() user = get_windows_username()
except: except:
@ -38,25 +37,26 @@ def gui_socket_address():
if user: if user:
user = ascii_filename(user).replace(' ', '_') user = ascii_filename(user).replace(' ', '_')
if user: if user:
ADDRESS += '-' + user[:100] + 'x' gui_socket_address.ans += '-' + user[:100] + 'x'
else: else:
user = os.environ.get('USER', '') user = os.environ.get('USER', '')
if not user: if not user:
user = os.path.basename(os.path.expanduser('~')) user = os.path.basename(os.path.expanduser('~'))
if islinux: 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: else:
from tempfile import gettempdir from tempfile import gettempdir
tmp = gettempdir() tmp = gettempdir()
ADDRESS = os.path.join(tmp, user+'-calibre-gui.socket') gui_socket_address.ans = os.path.join(tmp, user+'-calibre-gui.socket')
return ADDRESS 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(): def viewer_socket_address():
global VADDRESS if viewer_socket_address.ans is None:
if VADDRESS is None:
if iswindows: if iswindows:
VADDRESS = r'\\.\pipe\CalibreViewer' viewer_socket_address.ans = r'\\.\pipe\CalibreViewer'
try: try:
user = get_windows_username() user = get_windows_username()
except: except:
@ -64,18 +64,23 @@ def viewer_socket_address():
if user: if user:
user = ascii_filename(user).replace(' ', '_') user = ascii_filename(user).replace(' ', '_')
if user: if user:
VADDRESS += '-' + user[:100] + 'x' viewer_socket_address.ans += '-' + user[:100] + 'x'
else: else:
user = os.environ.get('USER', '') user = os.environ.get('USER', '')
if not user: if not user:
user = os.path.basename(os.path.expanduser('~')) user = os.path.basename(os.path.expanduser('~'))
if islinux: 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: else:
from tempfile import gettempdir from tempfile import gettempdir
tmp = gettempdir() tmp = gettempdir()
VADDRESS = os.path.join(tmp, user+'-calibre-viewer.socket') viewer_socket_address.ans = os.path.join(tmp, user+'-calibre-viewer.socket')
return VADDRESS 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): class RC(Thread):

View File

@ -8,7 +8,7 @@ import binascii
from contextlib import closing from contextlib import closing
from tempfile import SpooledTemporaryFile from tempfile import SpooledTemporaryFile
from calibre import sanitize_file_name2 from calibre import sanitize_file_name
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
from calibre.ebooks.chardet import detect from calibre.ebooks.chardet import detect
from polyglot.builtins import unicode_type, string_or_bytes from polyglot.builtins import unicode_type, string_or_bytes
@ -1137,7 +1137,7 @@ class ZipFile:
os.makedirs(upperdirs) os.makedirs(upperdirs)
except: # Added by Kovid except: # Added by Kovid
targetpath = os.path.join(base_target, targetpath = os.path.join(base_target,
sanitize_file_name2(fname)) sanitize_file_name(fname))
upperdirs = os.path.dirname(targetpath) upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs): if upperdirs and not os.path.exists(upperdirs):
os.makedirs(upperdirs) os.makedirs(upperdirs)
@ -1158,7 +1158,7 @@ class ZipFile:
except: except:
# Try sanitizing the file name to remove invalid characters # Try sanitizing the file name to remove invalid characters
components = list(os.path.split(targetpath)) 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) targetpath = os.sep.join(components)
with open(targetpath, 'wb') as target: with open(targetpath, 'wb') as target:
shutil.copyfileobj(source, target) shutil.copyfileobj(source, target)