Use stable ordering when writing shell completions

Set ordering is unstable (uses the hash for ordering), so the output file
would be different every time. Sort the sets into lists before writing. In
various places there were unnecessary "conversions", e.g. sorted() always
returns a list, so 'list(sorted(…))' is not useful.

In one place output is fixed to say: "Choices are: doc, ereader, ztxt" instead
of "['doc', 'ereader', 'ztxt']".

For module constants, just create the object directly. The code is easier to
read/edit when the list is constructed directly with items that are ordered in
the same way that is shown in various outputs (instead of being sorted
dynamically).

There is some more syntax confusion in src/calibre/linux.py. For example,
'"%s" % (x)' is the same as '"%s" % x', because just putting something in
parentheses does not create a tuple. It'd be nice to clean up this whole file
to use f-strings and not the error-prone %-formatting, but that's a separate
issue.

In a test build of the Fedora package, the generated file is now stable.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2024-10-20 18:13:44 +02:00
parent 9beed389fe
commit 86494089df
3 changed files with 19 additions and 18 deletions

View File

@ -14,13 +14,13 @@ class PDBOutput(OutputFormatPlugin):
author = 'John Schember'
file_type = 'pdb'
commit_name = 'pdb_output'
ui_data = {'formats': tuple(ALL_FORMAT_WRITERS)}
ui_data = {'formats': ALL_FORMAT_WRITERS}
options = {
OptionRecommendation(name='format', recommended_value='doc',
level=OptionRecommendation.LOW,
short_switch='f', choices=list(ALL_FORMAT_WRITERS),
help=(_('Format to use inside the PDB container. Choices are:') + ' %s' % sorted(ALL_FORMAT_WRITERS))),
short_switch='f', choices=ALL_FORMAT_WRITERS,
help=(_('Format to use inside the PDB container. Choices are:') + ', '.join(ALL_FORMAT_WRITERS))),
OptionRecommendation(name='pdb_output_encoding', recommended_value='cp1252',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '

View File

@ -31,7 +31,7 @@ def _import_readers():
}
ALL_FORMAT_WRITERS = {'doc', 'ztxt', 'ereader'}
ALL_FORMAT_WRITERS = ('doc', 'ereader', 'ztxt') # keep sorted alphabetically
FORMAT_WRITERS = None

View File

@ -289,11 +289,11 @@ class ZshCompleter: # {{{
elif opt.choices:
arg += "(%s)"%'|'.join(opt.choices)
elif set(file_map).intersection(set(opt._long_opts)):
k = set(file_map).intersection(set(opt._long_opts))
exts = file_map[tuple(k)[0]]
k = list(set(file_map).intersection(set(opt._long_opts)))
exts = file_map[k[0]]
if exts:
arg += "'_files -g \"%s\"'"%(' '.join('*.%s'%x for x in
tuple(exts) + tuple(x.upper() for x in exts)))
exts = ('*.%s'%x for x in sorted(exts + [x.upper() for x in exts]))
arg += "'_files -g \"%s\"'" % ' '.join(exts)
else:
arg += "_files"
elif (opt.dest in {'pidfile', 'attachment'}):
@ -301,8 +301,8 @@ class ZshCompleter: # {{{
elif set(opf_opts).intersection(set(opt._long_opts)):
arg += "'_files -g \"*.opf\"'"
elif set(cover_opts).intersection(set(opt._long_opts)):
arg += "'_files -g \"%s\"'"%(' '.join('*.%s'%x for x in
tuple(pics) + tuple(x.upper() for x in pics)))
exts = ('*.%s'%x for x in sorted(pics + [x.upper() for x in pics]))
arg += "'_files -g \"%s\"'" % ' '.join(exts)
help_txt = '"[%s]"'%h
yield '%s%s%s%s '%(exclude, ostrings, help_txt, arg)
@ -332,10 +332,10 @@ class ZshCompleter: # {{{
from calibre.ebooks.conversion.plumber import supported_input_formats
from calibre.utils.logging import DevNull
from calibre.web.feeds.recipes.collection import get_builtin_recipe_titles
input_fmts = set(supported_input_formats())
output_fmts = set(available_output_formats())
iexts = {x.upper() for x in input_fmts}.union(input_fmts)
oexts = {x.upper() for x in output_fmts}.union(output_fmts)
input_fmts = sorted(set(supported_input_formats()))
output_fmts = sorted(set(available_output_formats()))
iexts = sorted({x.upper() for x in input_fmts}.union(input_fmts))
oexts = sorted({x.upper() for x in output_fmts}.union(output_fmts))
w = polyglot_write(f)
# Arg 1
w('\n_ebc_input_args() {')
@ -425,7 +425,7 @@ class ZshCompleter: # {{{
from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
from calibre.ebooks.oeb.polish.main import SUPPORTED
from calibre.gui2.tweak_book.main import option_parser
tweakable_fmts = SUPPORTED | IMPORTABLE
tweakable_fmts = sorted(SUPPORTED | IMPORTABLE)
parser = option_parser()
opt_lines = []
for opt in parser.option_list:
@ -499,7 +499,7 @@ _ebook_edit() {{
exts = [x.lower() for x in available_catalog_formats()]
elif command == 'set_metadata':
exts = ['opf']
exts = set(exts).union(x.upper() for x in exts)
exts = sorted(set(exts).union(x.upper() for x in exts))
pats = ('*.%s'%x for x in exts)
extra = ("'*:filename:_files -g \"%s\"' "%' '.join(pats),) if exts else ()
if command in {'add', 'add_format'}:
@ -917,6 +917,7 @@ class PostInstall:
if mt and 'chemical' not in mt and 'ctc-posml' not in mt:
mimetypes.add(mt)
mimetypes.discard('application/octet-stream')
mimetypes = sorted(mimetypes)
def write_mimetypes(f, extra=''):
line = 'MimeType={};'.format(';'.join(mimetypes))
@ -934,6 +935,7 @@ class PostInstall:
with open('calibre-ebook-edit.desktop', 'wb') as f:
f.write(ETWEAK.encode('utf-8'))
mt = {guess_type('a.' + x.lower())[0] for x in (SUPPORTED|IMPORTABLE)} - {None, 'application/octet-stream'}
mt = sorted(mt)
f.write(('MimeType=%s;\n'%';'.join(mt)).encode('utf-8'))
with open('calibre-gui.desktop', 'wb') as f:
f.write(GUI.encode('utf-8'))
@ -1032,8 +1034,7 @@ def opts_and_words(name, op, words, takes_files=False):
complete -F _'''%(opts, words) + fname + ' ' + name +"\n\n").encode('utf-8')
pics = {'jpg', 'jpeg', 'gif', 'png', 'bmp'}
pics = list(sorted(pics)) # for reproducibility
pics = ['bmp', 'gif', 'jpeg', 'jpg', 'png'] # keep sorted alphabetically
def opts_and_exts(name, op, exts, cover_opts=('--cover',), opf_opts=(),