diff --git a/pyproject.toml b/pyproject.toml index 93104cffa0..4fd42fcaf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ unfixable = ['PIE794', 'ISC001'] [tool.ruff.lint.per-file-ignores] "recipes/*" = ['UP'] "setup/changelog.py" = ['ISC001'] -"src/calibre/*" = ['UP031'] "src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501'] "src/calibre/ebooks/metadata/sources/*" = ['UP'] "src/calibre/gui2/store/stores/*" = ['UP'] diff --git a/src/calibre/db/cli/cmd_check_library.py b/src/calibre/db/cli/cmd_check_library.py index a0812a1040..3e4438ec15 100644 --- a/src/calibre/db/cli/cmd_check_library.py +++ b/src/calibre/db/cli/cmd_check_library.py @@ -86,7 +86,7 @@ def _print_check_library_results(checker, check, as_csv=False, out=sys.stdout): else: print(check[1], file=out) for i in list: - print(' %-40.40s - %-40.40s' % (i[0], i[1]), file=out) + print(f' {i[0]:<40.40} -{i[1]:<40.40}', file=out) def main(opts, args, dbctx): diff --git a/src/calibre/db/cli/cmd_list.py b/src/calibre/db/cli/cmd_list.py index a030c20bb6..ebe16113fc 100644 --- a/src/calibre/db/cli/cmd_list.py +++ b/src/calibre/db/cli/cmd_list.py @@ -231,7 +231,7 @@ def do_list( widths = list(base_widths) titles = map( - lambda x, y: '%-*s%s' % (x - len(separator), y, separator), widths, + lambda x, y: '%-*s%s' % (x - len(separator), y, separator), widths, # noqa: UP031 [template_title if v == 'template' else v for v in fields] ) with ColoredStream(sys.stdout, fg='green'): @@ -251,7 +251,7 @@ def do_list( ft = text[i][l] if l < len(text[i]) else '' stdout.write(ft.encode('utf-8')) if i < len(text) - 1: - filler = ('%*s' % (widths[i] - str_width(ft) - 1, '')) + filler = ' '*(widths[i] - str_width(ft) - 1) stdout.write((filler + separator).encode('utf-8')) stdout.write(linesep) diff --git a/src/calibre/db/cli/cmd_list_categories.py b/src/calibre/db/cli/cmd_list_categories.py index 0c5e7742a1..f096b922ab 100644 --- a/src/calibre/db/cli/cmd_list_categories.py +++ b/src/calibre/db/cli/cmd_list_categories.py @@ -99,7 +99,7 @@ def do_list(fields, data, opts): widths = list(base_widths) titles = map( - lambda x, y: '%-*s%s' % (x - len(separator), y, separator), widths, fields + lambda x, y: '%-*s%s' % (x - len(separator), y, separator), widths, fields # noqa: UP031 ) with ColoredStream(sys.stdout, fg='green'): prints(''.join(titles)) @@ -115,7 +115,7 @@ def do_list(fields, data, opts): for l in range(lines): for i, field in enumerate(text): ft = text[i][l] if l < len(text[i]) else '' - filler = '%*s' % (widths[i] - len(ft) - 1, '') + filler = ' '*(widths[i] - len(ft) - 1) print(ft.encode('utf-8') + filler.encode('utf-8'), end=separator) print() diff --git a/src/calibre/db/cli/cmd_set_metadata.py b/src/calibre/db/cli/cmd_set_metadata.py index aa18a6ce72..6887661e6c 100644 --- a/src/calibre/db/cli/cmd_set_metadata.py +++ b/src/calibre/db/cli/cmd_set_metadata.py @@ -118,9 +118,9 @@ def get_fields(dbctx): def main(opts, args, dbctx): if opts.list_fields: ans = get_fields(dbctx) - prints('%-40s' % _('Title'), _('Field name'), '\n') + prints('{:<40}'.format(_('Title')), _('Field name'), '\n') for key, m in ans: - prints('%-40s' % m['name'], key) + prints('{:<40}'.format(m['name']), key) return 0 def verify_int(x): diff --git a/src/calibre/devices/cli.py b/src/calibre/devices/cli.py index 461185cfc5..3d30a3ba4c 100755 --- a/src/calibre/devices/cli.py +++ b/src/calibre/devices/cli.py @@ -244,7 +244,7 @@ def main(): where = ('Memory', 'Card A', 'Card B') print('Filesystem\tSize \tUsed \tAvail \tUse%') for i in range(3): - print('%-10s\t%s\t%s\t%s\t%s'%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), + print('{:<10}\t{}\t{}\t{}\t{}'.format(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+'%')) elif command == 'eject': dev.eject() diff --git a/src/calibre/devices/kobo/bookmark.py b/src/calibre/devices/kobo/bookmark.py index 63269ef685..0d55c3e299 100644 --- a/src/calibre/devices/kobo/bookmark.py +++ b/src/calibre/devices/kobo/bookmark.py @@ -168,7 +168,7 @@ class Bookmark: # {{{ ans = ['Kobo bookmark:'] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') if self.contentId: fmt('ContentID', self.contentId) diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 451b5cea38..598fb48af1 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -101,7 +101,7 @@ class Book(Book_): ans = ['Kobo metadata:'] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') if self.contentID: fmt('Content ID', self.contentID) diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 7e0bdd6e2e..4ded9131fa 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -484,7 +484,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: today = time.localtime() date = (today[0], today[1], today[2]) - template = '{title}_%d-%d-%d' % date + template = f'{{title}}_{date[0]}-{date[1]}-{date[2]}' use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs from calibre.library.save_to_disk import config, get_components diff --git a/src/calibre/devices/utils.py b/src/calibre/devices/utils.py index ada41e79a8..0c8a10d329 100644 --- a/src/calibre/devices/utils.py +++ b/src/calibre/devices/utils.py @@ -91,7 +91,7 @@ def create_upload_path(mdata, fname, template, sanitize, except: today = time.localtime() date = (today[0], today[1], today[2]) - template = '{title}_%d-%d-%d' % date + template = f'{{title}}_{date[0]}-{date[1]}-{date[2]}' fname = sanitize(fname) ext = path_type.splitext(fname)[1] diff --git a/src/calibre/ebooks/conversion/plugins/rtf_input.py b/src/calibre/ebooks/conversion/plugins/rtf_input.py index 619207852b..b0b813406c 100644 --- a/src/calibre/ebooks/conversion/plugins/rtf_input.py +++ b/src/calibre/ebooks/conversion/plugins/rtf_input.py @@ -192,10 +192,8 @@ class RTFInput(InputFormatPlugin): return name def write_inline_css(self, ic, border_styles): - font_size_classes = ['span.fs%d { font-size: %spt }'%(i, x) for i, x in - enumerate(ic.font_sizes)] - color_classes = ['span.col%d { color: %s }'%(i, x) for i, x in - enumerate(ic.colors) if x != 'false'] + font_size_classes = [f'span.fs{i} {{ font-size: {x}pt }}' for i, x in enumerate(ic.font_sizes)] + color_classes = [f'span.col{i} {{ color: {x} }}' for i, x in enumerate(ic.colors) if x != 'false'] css = textwrap.dedent(''' span.none { text-decoration: none; font-weight: normal; diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 453462b179..37e29203d9 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -265,16 +265,16 @@ class Dehyphenator: self.format = format if format == 'html': intextmatch = re.compile(( - r'(?<=.{%i})(?P[^\W\-]+)(-|‐)\s*(?=<)(?P()?' + r'(?<=.{%i})(?P[^\W\-]+)(-|‐)\s*(?=<)(?P()?' # noqa: UP031 r'\s*(\s*){1,2}(?P<(p|div)[^>]*>\s*(]*>\s*

\s*)' r'?\s+){0,3}\s*(<[iubp][^>]*>\s*){1,2}(]*>)?)\s*(?P[\w\d]+)') % length) elif format == 'pdf': intextmatch = re.compile(( - r'(?<=.{%i})(?P[^\W\-]+)(-|‐)\s*(?P

|' + r'(?<=.{%i})(?P[^\W\-]+)(-|‐)\s*(?P

|' # noqa: UP031 r'\s*

\s*<[iub]>)\s*(?P[\w\d]+)')% length) elif format == 'txt': intextmatch = re.compile( - r'(?<=.{%i})(?P[^\W\-]+)(-|‐)( |\t)*(?P(\n( |\t)*)+)(?P[\w\d]+)'% length) + r'(?<=.{%i})(?P[^\W\-]+)(-|‐)( |\t)*(?P(\n( |\t)*)+)(?P[\w\d]+)'% length) # noqa: UP031 elif format == 'individual_words': intextmatch = re.compile( r'(?!<)(?P[^\W\-]+)(-|‐)\s*(?P\w+)(?![^<]*?>)', re.UNICODE) @@ -529,11 +529,11 @@ class HTMLPreProcessor: # print('The pdf line length returned is ' + str(length)) # unwrap em/en dashes end_rules.append((re.compile( - r'(?<=.{%i}[–—])\s*

\s*(?=[\[a-z\d])' % length), lambda match: '')) + r'(?<=.{%i}[–—])\s*

\s*(?=[\[a-z\d])' % length), lambda match: '')) # noqa: UP031 end_rules.append( # Un wrap using punctuation (re.compile(( - r'(?<=.{%i}([a-zäëïöüàèìòùáćéíĺóŕńśúýâêîôûçąężıãõñæøþðßěľščťžňďřů,:)\\IAß]' + r'(?<=.{%i}([a-zäëïöüàèìòùáćéíĺóŕńśúýâêîôûçąężıãõñæøþðßěľščťžňďřů,:)\\IAß]' # noqa: UP031 r'|(?)?\s*(

\s*

\s*)+\s*(?=(<(i|b|u)>)?' r'\s*[\w\d$(])') % length, re.UNICODE), wrap_lines), ) diff --git a/src/calibre/ebooks/docx/writer/container.py b/src/calibre/ebooks/docx/writer/container.py index 19c11404ab..9a0c96c303 100644 --- a/src/calibre/ebooks/docx/writer/container.py +++ b/src/calibre/ebooks/docx/writer/container.py @@ -209,7 +209,7 @@ class DOCX: E = ElementMaker(namespace=self.namespace.namespaces['ep'], nsmap={None:self.namespace.namespaces['ep']}) props = E.Properties( E.Application(__appname__), - E.AppVersion('%02d.%04d' % numeric_version[:2]), + E.AppVersion(f'{numeric_version[0]:02}.{numeric_version[1]:04}'), E.DocSecurity('0'), E.HyperlinksChanged('false'), E.LinksUpToDate('true'), diff --git a/src/calibre/ebooks/docx/writer/styles.py b/src/calibre/ebooks/docx/writer/styles.py index 2d1efef1e8..b49cdec164 100644 --- a/src/calibre/ebooks/docx/writer/styles.py +++ b/src/calibre/ebooks/docx/writer/styles.py @@ -769,8 +769,7 @@ class StylesManager: text_style.seq = i self.descendant_text_styles = sorted(descendant_style_map, key=attrgetter('seq')) - self.log.debug('%d Text Styles %d Combined styles' % tuple(map(len, ( - self.descendant_text_styles, self.combined_styles)))) + self.log.debug(f'{len(self.descendant_text_styles)} Text Styles {len(self.combined_styles)} Combined styles') self.primary_heading_style = None if heading_styles: diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 0a69c4110f..08e79b76cf 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -765,7 +765,7 @@ class Metadata: ans = [] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') fmt('Title', self.title) if self.title_sort: diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index 9c38d3699e..427096bfe5 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -286,7 +286,7 @@ class MetadataUpdater: s, src = src[:length],src[length:] hexa = ' '.join([f'{ord(x):02X}' for x in s]) s = s.translate(FILTER) - result += '%04X %-*s %s\n' % (N, length*3, hexa, s) + result += '%04X %-*s %s\n' % (N, length*3, hexa, s) # noqa: UP031 N+=length print(result) @@ -308,7 +308,7 @@ class MetadataUpdater: def dump_pdbrecords(self): # Diagnostic print('MetadataUpdater.dump_pdbrecords()') - print('%10s %10s %10s' % ('offset','flags','val')) + print(f"{'offset':>10} {'flags':>10} {'val':>10}") for i in range(len(self.pdbrecords)): pdbrecord = self.pdbrecords[i] print(f'{pdbrecord[0]:10X} {pdbrecord[1]:10X} {pdbrecord[2]:10X}') diff --git a/src/calibre/ebooks/metadata/topaz.py b/src/calibre/ebooks/metadata/topaz.py index 9a1383cae6..374da028e0 100644 --- a/src/calibre/ebooks/metadata/topaz.py +++ b/src/calibre/ebooks/metadata/topaz.py @@ -161,7 +161,7 @@ class MetadataUpdater: s, src = src[:length],src[length:] hexa = ' '.join([f'{ord(x):02X}' for x in s]) s = s.translate(FILTER) - result += '%04X %-*s %s\n' % (N, length*3, hexa, s) + result += '%04X %-*s %s\n' % (N, length*3, hexa, s) # noqa: UP031 N+=length print(result) diff --git a/src/calibre/ebooks/mobi/debug/index.py b/src/calibre/ebooks/mobi/debug/index.py index 5fecd43dca..c122db4293 100644 --- a/src/calibre/ebooks/mobi/debug/index.py +++ b/src/calibre/ebooks/mobi/debug/index.py @@ -96,13 +96,13 @@ class Index: a = ans.append if self.header is not None: for field in INDEX_HEADER_FIELDS: - a('%-12s: %r'%(FIELD_NAMES.get(field, field), self.header[field])) + a(f'{FIELD_NAMES.get(field, field):<12}: {self.header[field]!r}') ans.extend(['', '']) ans += ['*'*10 + f' Index Record Headers ({len(self.index_headers)} records) ' + '*'*10] for i, header in enumerate(self.index_headers): ans += ['*'*10 + f' Index Record {i} ' + '*'*10] for field in INDEX_HEADER_FIELDS: - a('%-12s: %r'%(FIELD_NAMES.get(field, field), header[field])) + a(f'{FIELD_NAMES.get(field, field):<12}: {header[field]!r}') if self.cncx: a('*'*10 + ' CNCX ' + '*'*10) diff --git a/src/calibre/ebooks/mobi/debug/mobi6.py b/src/calibre/ebooks/mobi/debug/mobi6.py index 99a55f6555..cd5cf871a1 100644 --- a/src/calibre/ebooks/mobi/debug/mobi6.py +++ b/src/calibre/ebooks/mobi/debug/mobi6.py @@ -98,8 +98,8 @@ class SecondaryIndexHeader: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, - len(w), not bool(w.replace(b'\0', b'')))) + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format( + w, len(w), not bool(w.replace(b'\0', b'')))) a(f'Header length: {self.header_length}') u(self.unknown1) @@ -199,7 +199,7 @@ class IndexHeader: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format(w, len(w), not bool(w.replace(b'\0', b'')))) a(f'Header length: {self.header_length}') @@ -411,7 +411,7 @@ class IndexRecord: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format(w, len(w), not bool(w.replace(b'\0', b'')))) for entry in self.indices: offset = entry.offset @@ -582,9 +582,7 @@ class TBSIndexing: # {{{ ans = [] ans.append(f"\nRecord #{r.idx}: Starts at: {dat['geom'][0]} Ends at: {dat['geom'][1]}") s, e, c = dat['starts'], dat['ends'], dat['complete'] - ans.append(('\tContains: %d index entries ' - '(%d ends, %d complete, %d starts)')%tuple(map(len, (s+e+c, e, - c, s)))) + ans.append(f'\tContains: {len(s+e+c)} index entries ({len(e)} ends, {len(c)} complete, {len(s)} starts)') byts = bytearray(r.trailing_data.get('indexing', b'')) ans.append(f'TBS bytes: {format_bytes(byts)}') for typ, entries in (('Ends', e), ('Complete', c), ('Starts', s)): diff --git a/src/calibre/ebooks/mobi/debug/mobi8.py b/src/calibre/ebooks/mobi/debug/mobi8.py index f7b20a0296..0fa06c5bdd 100644 --- a/src/calibre/ebooks/mobi/debug/mobi8.py +++ b/src/calibre/ebooks/mobi/debug/mobi8.py @@ -45,7 +45,7 @@ class FDST: a('Number of section records', self.num_sections) ans.append(f'**** {len(self.sections)} Sections ****') for sec in self.sections: - ans.append('Start: %20d End: %d'%sec) + ans.append(f'Start: {sec[0]:>20} End: {sec[1]}') return '\n'.join(ans) @@ -266,7 +266,7 @@ class MOBIFile: for entries in itervalues(strand): for e in entries: desc.append( - ' %s%d [%-9s] parent: %s (%d) Geometry: (%d, %d)'%( + ' {}{} [{:<9}] parent: {} ({}) Geometry: ({}, {})'.format( e.depth * (' ') + '- ', e.index, e.action, e.parent, e.index-(e.parent or 0), e.start-i*RECORD_SIZE, e.start+e.length-i*RECORD_SIZE)) diff --git a/src/calibre/ebooks/mobi/reader/mobi6.py b/src/calibre/ebooks/mobi/reader/mobi6.py index aac14c1bd1..0628c5c47b 100644 --- a/src/calibre/ebooks/mobi/reader/mobi6.py +++ b/src/calibre/ebooks/mobi/reader/mobi6.py @@ -662,7 +662,7 @@ class MobiReader: elif mi.cover is not None: opf.cover = mi.cover else: - opf.cover = 'images/%05d.jpg' % 1 + opf.cover = f'images/{1:05}.jpg' if not os.path.exists(os.path.join(os.path.dirname(htmlfile), * opf.cover.split('/'))): opf.cover = None diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 49dca6605c..9e41a1f1a5 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -344,7 +344,7 @@ class Container(ContainerBase): # {{{ item_id = 'id' while item_id in all_ids: c += 1 - item_id = 'id' + f'{c}' + item_id = f'id{c}' manifest = self.opf_xpath('//opf:manifest')[0] href = self.name_to_href(name, self.opf_name) item = manifest.makeelement(OPF('item'), diff --git a/src/calibre/ebooks/oeb/transforms/split.py b/src/calibre/ebooks/oeb/transforms/split.py index 85381a000e..85b5ce1ec8 100644 --- a/src/calibre/ebooks/oeb/transforms/split.py +++ b/src/calibre/ebooks/oeb/transforms/split.py @@ -366,10 +366,10 @@ class FlowSplitter: elif size <= self.max_flow_size: self.split_trees.append(t) self.log.debug( - f'\t\t\tCommitted sub-tree #{len(self.split_trees)} ({size / 1024.0} KB)') + f'\t\t\tCommitted sub-tree #{len(self.split_trees)} ({size//1024} KB)') else: self.log.debug( - f'\t\t\tSplit tree still too large: {size / 1024.0} KB') + f'\t\t\tSplit tree still too large: {size//1024} KB') self.split_to_size(t) def find_split_point(self, root): diff --git a/src/calibre/ebooks/pdf/html_writer.py b/src/calibre/ebooks/pdf/html_writer.py index 48abf79d19..ef4cba7de4 100644 --- a/src/calibre/ebooks/pdf/html_writer.py +++ b/src/calibre/ebooks/pdf/html_writer.py @@ -777,7 +777,7 @@ def add_pagenum_toc(root, toc, opts, page_number_display_map): .calibre-pdf-toc .level-%d td:first-of-type { padding-left: %.1gem } .calibre-pdf-toc .level-%d td:first-of-type { padding-left: %.1gem } .calibre-pdf-toc .level-%d td:first-of-type { padding-left: %.1gem } - ''' % tuple(indents) + (opts.extra_css or '') + ''' % tuple(indents) + (opts.extra_css or '') # noqa: UP031 style = body.makeelement(XHTML('style'), type='text/css') style.text = css body.append(style) diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index 581b1f81e1..c6cd31c845 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -68,7 +68,7 @@ class IndirectObjects: stream.write(b'xref'+EOL) stream.write(f'0 {1 + len(self._offsets)}') stream.write(EOL) - stream.write('%010d 65535 f '%0) + stream.write(f'{0:010} 65535 f ') stream.write(EOL) for offset in self._offsets: diff --git a/src/calibre/ebooks/readability/readability.py b/src/calibre/ebooks/readability/readability.py index 573597aa1c..67d05b7fd0 100644 --- a/src/calibre/ebooks/readability/readability.py +++ b/src/calibre/ebooks/readability/readability.py @@ -366,8 +366,7 @@ class Document: tag = el.tag if weight + content_score < 0: - self.debug('Cleaned %s with score %6.3f and weight %-3s' % - (describe(el), content_score, weight, )) + self.debug(f'Cleaned {describe(el)} with score {content_score:6.3f} and weight {weight:<3}') el.drop_tree() elif el.text_content().count(',') < 10: counts = {} diff --git a/src/calibre/ebooks/rtf/preprocess.py b/src/calibre/ebooks/rtf/preprocess.py index 460c4752b1..430f63fac8 100644 --- a/src/calibre/ebooks/rtf/preprocess.py +++ b/src/calibre/ebooks/rtf/preprocess.py @@ -322,10 +322,10 @@ class RtfTokenizer: l = l + 1 i = i + 1 if l > 10: - raise Exception('Error (at:%d): Too many digits in control word numeric argument.'%[tokenStart]) + raise Exception(f'Error (at:{tokenStart}): Too many digits in control word numeric argument.') if not consumed: - raise Exception('Error (at:%d): Control Word without numeric argument end.'%[tokenStart]) + raise Exception(f'Error (at:{tokenStart}): Control Word without numeric argument end.') separator = '' if isChar(self.rtfData[i], ' '): diff --git a/src/calibre/ebooks/rtf/rtfml.py b/src/calibre/ebooks/rtf/rtfml.py index 0aed5b31bc..6e28b2bfdc 100644 --- a/src/calibre/ebooks/rtf/rtfml.py +++ b/src/calibre/ebooks/rtf/rtfml.py @@ -177,7 +177,7 @@ class RTFMLizer: self.log.exception(f'Image {item.href} is corrupted, ignoring') repl = '\n\n' else: - repl = '\n\n{\\*\\shppict{\\pict\\jpegblip\\picw%i\\pich%i \n%s\n}}\n\n' % (width, height, data) + repl = '\n\n{\\*\\shppict{\\pict\\jpegblip\\picw%i\\pich%i \n%s\n}}\n\n' % (width, height, data) # noqa: UP031 text = text.replace(f'SPECIAL_IMAGE-{src}-REPLACE_ME', repl) return text diff --git a/src/calibre/ebooks/textile/functions.py b/src/calibre/ebooks/textile/functions.py index f4a1f57b9c..3ab0f91ab6 100644 --- a/src/calibre/ebooks/textile/functions.py +++ b/src/calibre/ebooks/textile/functions.py @@ -98,7 +98,7 @@ def getimagesize(url): break p.feed(s) if p.image: - return 'width="%i" height="%i"' % p.image.size + return f'width="{p.image.size[0]}" height="{p.image.size[1]}"' except (OSError, ValueError): return None diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 877fc25ccb..1028ce1e08 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -90,7 +90,7 @@ class RichTextDelegate(QStyledItemDelegate): # {{{ group = (QPalette.ColorGroup.Active if option.state & QStyle.StateFlag.State_Active else QPalette.ColorGroup.Inactive) c = p.color(group, QPalette.ColorRole.HighlightedText) - c = 'rgb(%d, %d, %d)'%c.getRgb()[:3] + c = 'rgb({}, {}, {})'.format(*c.getRgb()[:3]) doc.setDefaultStyleSheet(f' * {{ color: {c} }}') doc.setHtml(index.data() or '') return doc diff --git a/src/calibre/gui2/tweak_book/check.py b/src/calibre/gui2/tweak_book/check.py index c674c323d3..54865766a9 100644 --- a/src/calibre/gui2/tweak_book/check.py +++ b/src/calibre/gui2/tweak_book/check.py @@ -202,8 +202,7 @@ class Check(QSplitter): fix_tt = _('Try to fix all fixable errors automatically. Only works for some types of error.') fix_msg = _('Try to correct all fixable errors automatically') run_tt, run_msg = _('Re-run the check'), _('Re-run check') - header = '

%s [%d / %d]

' % ( - header, self.items.currentRow()+1, self.items.count()) + header = f'

{header} [{self.items.currentRow()+1} / {self.items.count()}]

' msg = '

%s

' footer = '
%s%s

%s
' if err.has_multiple_locations: diff --git a/src/calibre/gui2/tweak_book/check_links.py b/src/calibre/gui2/tweak_book/check_links.py index a7c25e4ed8..11451e0822 100644 --- a/src/calibre/gui2/tweak_book/check_links.py +++ b/src/calibre/gui2/tweak_book/check_links.py @@ -143,7 +143,7 @@ class CheckExternalLinks(Dialog): for i, (locations, err, url) in enumerate(self.errors): if i in self.fixed_errors: continue - text += '
  • %s \xa0[%s]
    %s
      ' % (url, i, _('Fix this link'), err) + text += '
    • {} \xa0[{}]
      {}
        '.format(url, i, _('Fix this link'), err) for name, href, lnum, col in locations: text += '
      • {name} \xa0[{line}: {lnum}]
      • '.format( name=name, lnum=lnum, line=_('line number')) diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index fbadc1f04f..926bccc508 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -547,7 +547,7 @@ class NamesDelegate(QStyledItemDelegate): positions = sorted(set(positions) - {-1}, reverse=True) text = f'{make_highlighted_text(emphasis_style(), text, positions)}' doc = QTextDocument() - c = 'rgb(%d, %d, %d)'%c.getRgb()[:3] + c = 'rgb({}, {}, {})'.format(*c.getRgb()[:3]) doc.setDefaultStyleSheet(f' body {{ color: {c} }}') doc.setHtml(text) doc.setDefaultFont(option.font) diff --git a/src/calibre/gui2/viewer/search.py b/src/calibre/gui2/viewer/search.py index 355e240f04..ef6384d1ed 100644 --- a/src/calibre/gui2/viewer/search.py +++ b/src/calibre/gui2/viewer/search.py @@ -154,7 +154,7 @@ class Search: flags = self.regex_flags flags |= regex.DOTALL match_any_word = r'(?:\b(?:' + '|'.join(words) + r')\b)' - joiner = '.{1,%d}' % interval + joiner = '.{1,%d}' % interval # noqa: UP031 full_pat = regex.compile(joiner.join(match_any_word for x in words), flags=flags) word_pats = tuple(regex.compile(rf'\b{x}\b', flags) for x in words) self._nsd = word_pats, full_pat diff --git a/src/calibre/library/catalogs/bibtex.py b/src/calibre/library/catalogs/bibtex.py index 0406a6c760..49edd025e7 100644 --- a/src/calibre/library/catalogs/bibtex.py +++ b/src/calibre/library/catalogs/bibtex.py @@ -399,7 +399,7 @@ class BIBTEX(CatalogPlugin): entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice'] # outfile.write('%%%Calibre catalog\n%%%{0} entries in catalog\n\n'.format(nb_entries)) - outfile.write('@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n' + outfile.write('@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n' # noqa: UP031 % (nb_entries, strftime('%A, %d. %B %Y %H:%M'))) for entry in data: diff --git a/src/calibre/library/catalogs/epub_mobi_builder.py b/src/calibre/library/catalogs/epub_mobi_builder.py index 684b5fbd8c..f72f716d02 100644 --- a/src/calibre/library/catalogs/epub_mobi_builder.py +++ b/src/calibre/library/catalogs/epub_mobi_builder.py @@ -264,7 +264,7 @@ class CatalogBuilder: (str): sort key ''' if not book['series']: - fs = '{:<%d}!{!s}' % longest_author_sort + fs = '{:<%d}!{!s}' % longest_author_sort # noqa: UP031 key = fs.format(capitalize(book['author_sort']), capitalize(book['title_sort'])) else: @@ -272,7 +272,7 @@ class CatalogBuilder: integer = int(index) fraction = index - integer series_index = f"{integer:04}{str(f'{fraction:0.4f}').lstrip('0')}" - fs = '{:<%d}~{!s}{!s}' % longest_author_sort + fs = '{:<%d}~{!s}{!s}' % longest_author_sort # noqa: UP031 key = fs.format(capitalize(book['author_sort']), self.generate_sort_title(book['series']), series_index) @@ -653,9 +653,8 @@ class CatalogBuilder: self.opts.log.info(' Custom fields:') all_custom_fields = self.db.custom_field_keys() for cf in all_custom_fields: - self.opts.log.info(' %-20s %-20s %s' % - (cf, "'{}'".format(self.db.metadata_for_field(cf)['name']), - self.db.metadata_for_field(cf)['datatype'])) + self.opts.log.info(' {:<20} {!r:<20} {}'.format( + cf, self.db.metadata_for_field(cf)['name'], self.db.metadata_for_field(cf)['datatype'])) def establish_equivalencies(self, item_list, key=None): ''' Return icu equivalent sort letter. @@ -786,7 +785,7 @@ class CatalogBuilder: if self.DEBUG and self.opts.verbose: tl = [i['title'] for i in books_by_author] lt = max(tl, key=len) - fs = '{:<6}{:<%d} {:<%d} {}' % (len(lt), len(las)) + fs = '{:<6}{:<%d} {:<%d} {}' % (len(lt), len(las)) # noqa: UP031 print(fs.format('', 'Title', 'Author', 'Series')) for i in books_by_author: print(fs.format('', i['title'], i['author_sort'], i['series'])) @@ -835,8 +834,7 @@ class CatalogBuilder: if self.DEBUG and self.opts.verbose: self.opts.log.info(f'\nfetch_books_by_author(): {len(unique_authors)} unique authors') for author in unique_authors: - self.opts.log.info((' %-50s %-25s %2d' % (author[0][0:45], author[1][0:20], - author[2])).encode('utf-8')) + self.opts.log.info(f' {author[0][0:45]:<50} {author[1][0:20]:<25} {author[2]:>2}') self.opts.log.info(f'\nfetch_books_by_author(): {len(individual_authors)} individual authors') for author in sorted(individual_authors): self.opts.log.info(f'{author}') @@ -865,10 +863,9 @@ class CatalogBuilder: if self.DEBUG and self.opts.verbose: self.opts.log.info(f'fetch_books_by_title(): {len(self.books_by_title)} books') - self.opts.log.info(' %-40s %-40s' % ('title', 'title_sort')) + self.opts.log.info(' {:<40} {:<40}'.format('title', 'title_sort')) for title in self.books_by_title: - self.opts.log.info((' %-40s %-40s' % (title['title'][0:40], - title['title_sort'][0:40])).encode('utf-8')) + self.opts.log.info(' {:<40} {:<40}'.format(title['title'][0:40], title['title_sort'][0:40])) else: error_msg = _("No books to catalog.\nCheck 'Excluded books' rules in the E-book options.\n") self.opts.log.error('*** ' + error_msg + ' ***') diff --git a/src/calibre/srv/auth.py b/src/calibre/srv/auth.py index 4bf13a0cf6..86d3cc4883 100644 --- a/src/calibre/srv/auth.py +++ b/src/calibre/srv/auth.py @@ -248,7 +248,7 @@ class AuthController: self.log = log self.secret = as_hex_unicode(os.urandom(random.randint(20, 30))) self.max_age_seconds = max_age_seconds - self.key_order = '{%d}:{%d}:{%d}' % random.choice(tuple(permutations((0,1,2)))) + self.key_order = '{%d}:{%d}:{%d}' % random.choice(tuple(permutations((0,1,2)))) # noqa: UP031 self.realm = realm if '"' in realm: raise ValueError('Double-quotes are not allowed in the authentication realm') diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index 2325efd545..2cd67e7549 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -59,8 +59,7 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): def format_heading(self, heading): from calibre.utils.terminal import colored - return '%*s%s:\n' % (self.current_indent, '', - colored(heading, fg='blue', bold=True)) + return ' '*self.current_indent + '{}:\n'.format(colored(heading, fg='blue', bold=True)) def format_option(self, option): import textwrap @@ -71,12 +70,10 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): opts = self.option_strings[option] opt_width = self.help_position - self.current_indent - 2 if len(opts) > opt_width: - opts = '%*s%s\n' % (self.current_indent, '', - colored(opts, fg='green')) + opts = ' '*self.current_indent + '{}\n'.format(colored(opts, fg='green')) indent_first = self.help_position else: # start help on same line as opts - opts = '%*s%-*s ' % (self.current_indent, '', opt_width + - len(colored('', fg='green')), colored(opts, fg='green')) + opts = ' '*self.current_indent + '%-*s ' % (opt_width+len(colored('', fg='green')), colored(opts, fg='green')) # noqa: UP031 indent_first = 0 result.append(opts) if option.help: @@ -85,9 +82,8 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): for line in help_text: help_lines.extend(textwrap.wrap(line, self.help_width)) - result.append('%*s%s\n' % (indent_first, '', help_lines[0])) - result.extend(['%*s%s\n' % (self.help_position, '', line) - for line in help_lines[1:]]) + result.append(' '*indent_first + f'{help_lines[0]}') + result.extend([' '*self.help_position + f'{line}' for line in help_lines[1:]]) elif opts[-1] != '\n': result.append('\n') return ''.join(result)+'\n' diff --git a/src/calibre/utils/fonts/sfnt/subset.py b/src/calibre/utils/fonts/sfnt/subset.py index c1a825fb85..2fa7d85262 100644 --- a/src/calibre/utils/fonts/sfnt/subset.py +++ b/src/calibre/utils/fonts/sfnt/subset.py @@ -220,8 +220,7 @@ def option_parser(): def print_stats(old_stats, new_stats): from calibre import prints prints('========= Table comparison (original vs. subset) =========') - prints('Table', ' ', '%10s'%'Size', ' ', 'Percent', ' ', '%10s'%'New Size', - ' New Percent') + prints('Table', ' ', f"{'Size':>10}", ' ', 'Percent', ' ', f"{'New Size':>10}", ' New Percent') prints('='*80) old_total = sum(itervalues(old_stats)) new_total = sum(itervalues(new_stats)) @@ -235,8 +234,8 @@ def print_stats(old_stats, new_stats): suffix = ' | same size' if nsz != osz: suffix = f' | reduced to {nsz/osz*100:.1f} %' - prints(f'{table:4}', ' ', f'{osz:10}', ' ', f'{op:5.1f} %', ' ', - f'{nsz:10}', ' ', f'{np:5.1f} %', suffix) + prints(f'{table:>4}', ' ', f'{osz:>10}', ' ', f'{op:5.1f} %', ' ', + f'{nsz:>10}', ' ', f'{np:5.1f} %', suffix) prints('='*80) diff --git a/src/calibre/utils/zipfile.py b/src/calibre/utils/zipfile.py index dccb818f44..912e54eb9c 100644 --- a/src/calibre/utils/zipfile.py +++ b/src/calibre/utils/zipfile.py @@ -971,10 +971,11 @@ class ZipFile: def printdir(self): '''Print a table of contents for the zip file.''' - print('%-46s %19s %12s' % ('File Name', 'Modified ', 'Size')) + print(f"{'File Name':<46} {'Modified ':>19} {'Size':>12}") for zinfo in self.filelist: - date = '%d-%02d-%02d %02d:%02d:%02d' % zinfo.date_time[:6] - print('%-46s %s %12d' % (zinfo.filename, date, zinfo.file_size)) + date = (f'{zinfo.date_time[0]}-{zinfo.date_time[1]:02}-{zinfo.date_time[2]:02} ' + f'{zinfo.date_time[3]:02}:{zinfo.date_time[4]:02}:{zinfo.date_time[5]:02}') + print(f'{zinfo.filename:<46} {date:>19} {zinfo.file_size:>12}') def testzip(self): '''Read all the files and check the CRC.''' diff --git a/src/calibre/web/feeds/__init__.py b/src/calibre/web/feeds/__init__.py index aa35a9ac35..11b0b67f46 100644 --- a/src/calibre/web/feeds/__init__.py +++ b/src/calibre/web/feeds/__init__.py @@ -250,7 +250,7 @@ class Feed: return len(self.articles) def __repr__(self): - res = [('%20s\n'%'').replace(' ', '_')+repr(art) for art in self] + res = ['_'*20 + f'\n{art!r}' for art in self] return '\n'+'\n'.join(res)+'\n' diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 2463e19c65..62cd532541 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -1693,7 +1693,7 @@ class BasicNewsRecipe(Recipe): f.title, play_order=po, description=desc, author=auth)) else: - entries.append('feed_%d/index.html'%0) + entries.append('feed_0/index.html') feed_index(0, toc) for i, p in enumerate(entries): diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index a1b14ce734..0435b466fe 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -427,7 +427,7 @@ class SchedulerConfig: schedule = 0.04 text = f'{schedule:f}' elif typ == 'day/time': - text = '%d:%d:%d'%schedule + text = f'{int(schedule[0])}:{int(schedule[1])}:{int(schedule[2])}' elif typ in ('days_of_week', 'days_of_month'): dw = ','.join(map(str, map(int, schedule[0]))) text = f'{dw}:{int(schedule[1])}:{int(schedule[2])}'