When sorting fails first try fallback by sorting bad values as the first value

This is closer to the python 2 behavior
This commit is contained in:
Kovid Goyal 2020-10-21 09:55:10 +05:30
parent 8b3b008c3c
commit 7fe84df779
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 56 additions and 20 deletions

View File

@ -6,36 +6,45 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, traceback, random, shutil, operator, sys
import operator
import os
import random
import shutil
import sys
import traceback
from collections import MutableSet, Set, defaultdict
from functools import partial, wraps
from io import BytesIO
from collections import defaultdict, Set, MutableSet
from functools import wraps, partial
from polyglot.builtins import iteritems, itervalues, unicode_type, zip, string_or_bytes, cmp
from time import time
from calibre import isbytestring, as_unicode
from calibre import as_unicode, isbytestring
from calibre.constants import iswindows, preferred_encoding
from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimport, run_plugins_on_postadd
from calibre.customize.ui import (
run_plugins_on_import, run_plugins_on_postadd, run_plugins_on_postimport
)
from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
from calibre.db.annotations import merge_annotations
from calibre.db.categories import get_categories
from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock
from calibre.db.errors import NoSuchFormat, NoSuchBook
from calibre.db.fields import create_field, IDENTITY, InvalidLinkTable
from calibre.db.errors import NoSuchBook, NoSuchFormat
from calibre.db.fields import IDENTITY, InvalidLinkTable, create_field
from calibre.db.lazy import FormatMetadata, FormatsList, ProxyMetadata
from calibre.db.locking import DowngradeLockError, SafeReadLock, create_locks
from calibre.db.search import Search
from calibre.db.tables import VirtualTable
from calibre.db.utils import type_safe_sort_key_function
from calibre.db.write import get_series_values, uniq
from calibre.db.lazy import FormatMetadata, FormatsList, ProxyMetadata
from calibre.ebooks import check_ebook_format
from calibre.ebooks.metadata import string_to_authors, author_to_author_sort
from calibre.ebooks.metadata import author_to_author_sort, string_to_authors
from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.ptempfile import (base_dir, PersistentTemporaryFile,
SpooledTemporaryFile)
from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile, base_dir
from calibre.utils.config import prefs, tweaks
from calibre.utils.date import now as nowf, utcnow, UNDEFINED_DATE
from calibre.utils.date import UNDEFINED_DATE, now as nowf, utcnow
from calibre.utils.icu import sort_key
from calibre.utils.localization import canonicalize_lang
from polyglot.builtins import (
cmp, iteritems, itervalues, string_or_bytes, unicode_type, zip
)
def api(f):
@ -975,12 +984,17 @@ class Cache(object):
fields = uniq(fields, operator.itemgetter(0))
if len(fields) == 1:
keyfunc = sort_key_func(fields[0][0])
reverse = not fields[0][1]
try:
return sorted(ids_to_sort, key=sort_key_func(fields[0][0]),
reverse=not fields[0][1])
return sorted(ids_to_sort, key=keyfunc, reverse=reverse)
except Exception as err:
print('Failed to sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr)
return sorted(ids_to_sort, reverse=not fields[0][1])
try:
return sorted(ids_to_sort, key=type_safe_sort_key_function(keyfunc), reverse=reverse)
except Exception as err:
print('Failed to type-safe sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr)
return sorted(ids_to_sort, reverse=reverse)
sort_key_funcs = tuple(sort_key_func(field) for field, order in fields)
orders = tuple(1 if order else -1 for _, order in fields)
Lazy = object() # Lazy load the sort keys for sub-sort fields
@ -2204,9 +2218,9 @@ class Cache(object):
def embed_metadata(self, book_ids, only_fmts=None, report_error=None, report_progress=None):
''' Update metadata in all formats of the specified book_ids to current metadata in the database. '''
field = self.fields['formats']
from calibre.ebooks.metadata.opf2 import pretty_print
from calibre.customize.ui import apply_null_metadata
from calibre.ebooks.metadata.meta import set_metadata
from calibre.ebooks.metadata.opf2 import pretty_print
if only_fmts:
only_fmts = {f.lower() for f in only_fmts}
@ -2356,8 +2370,8 @@ class Cache(object):
@write_api
def restore_annotations(self, book_id, annotations):
from calibre.utils.iso8601 import parse_iso8601
from calibre.utils.date import EPOCH
from calibre.utils.iso8601 import parse_iso8601
umap = defaultdict(list)
for adata in annotations:
key = adata['user_type'], adata['user'], adata['format']
@ -2373,8 +2387,8 @@ class Cache(object):
@write_api
def merge_annotations_for_book(self, book_id, fmt, annots_list, user_type='local', user='viewer'):
from calibre.utils.iso8601 import parse_iso8601
from calibre.utils.date import EPOCH
from calibre.utils.iso8601 import parse_iso8601
amap = self._annotations_map_for_book(book_id, fmt, user_type=user_type, user=user)
merge_annotations(annots_list, amap)
alist = []

View File

@ -405,3 +405,25 @@ def atof(string):
d = d.decode('utf-8', 'ignore') or '.'
number_separators = t, d
return float(string.replace(number_separators[1], '.').replace(number_separators[0], ''))
def type_safe_sort_key_function(keyfunc=None):
if keyfunc is None:
keyfunc = lambda x: x
sentinel = object()
first_value = sentinel
def key(x):
nonlocal first_value
ans = keyfunc(x)
if first_value is sentinel:
first_value = ans
else:
try:
ans < first_value
first_value < ans
except TypeError:
ans = first_value
return ans
return key