Merge branch 'refactor-fstring' of https://github.com/page-down/kitty

This commit is contained in:
Kovid Goyal 2022-01-30 09:26:17 +05:30
commit 2e790a119a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
56 changed files with 231 additions and 242 deletions

View File

@ -48,7 +48,7 @@ def install_deps():
def build_kitty():
python = shutil.which('python3') if is_bundle else sys.executable
cmd = '{} setup.py build --verbose'.format(python)
cmd = f'{python} setup.py build --verbose'
if os.environ.get('KITTY_SANITIZE') == '1':
cmd += ' --debug --sanitize'
run(cmd)
@ -59,8 +59,8 @@ def test_kitty():
def package_kitty():
py = 'python3' if is_macos else 'python'
run(py + ' setup.py linux-package --update-check-interval=0 --verbose')
python = 'python3' if is_macos else 'python'
run(f'{python} setup.py linux-package --update-check-interval=0 --verbose')
if is_macos:
run('python3 setup.py kitty.app --update-check-interval=0 --verbose')
run('kitty.app/Contents/MacOS/kitty +runpy "from kitty.constants import *; print(kitty_exe())"')
@ -76,11 +76,11 @@ def replace_in_file(path, src, dest):
def setup_bundle_env():
global SW
os.environ['SW'] = SW = '/Users/Shared/kitty-build/sw/sw' if is_macos else os.path.join(os.environ['GITHUB_WORKSPACE'], 'sw')
os.environ['PKG_CONFIG_PATH'] = SW + '/lib/pkgconfig'
os.environ['PKG_CONFIG_PATH'] = os.path.join(SW, 'lib', 'pkgconfig')
if is_macos:
os.environ['PATH'] = '{}:{}'.format('/usr/local/opt/sphinx-doc/bin', os.environ['PATH'])
else:
os.environ['LD_LIBRARY_PATH'] = SW + '/lib'
os.environ['LD_LIBRARY_PATH'] = os.path.join(SW, 'lib')
os.environ['PYTHONHOME'] = SW
os.environ['PATH'] = '{}:{}'.format(os.path.join(SW, 'bin'), os.environ['PATH'])
@ -111,7 +111,7 @@ def main():
setup_bundle_env()
else:
if not is_macos and 'pythonLocation' in os.environ:
os.environ['LD_LIBRARY_PATH'] = '{}/lib'.format(os.environ['pythonLocation'])
os.environ['LD_LIBRARY_PATH'] = os.path.join(os.environ['pythonLocation'], 'lib')
action = sys.argv[-1]
if action in ('build', 'package'):
install_deps()
@ -122,7 +122,7 @@ def main():
elif action == 'test':
test_kitty()
else:
raise SystemExit('Unknown action: ' + action)
raise SystemExit(f'Unknown action: {action}')
if __name__ == '__main__':

View File

@ -13,7 +13,7 @@ import tempfile
def compile_terminfo(base):
with tempfile.TemporaryDirectory() as tdir:
proc = subprocess.run(['tic', '-x', '-o' + tdir, 'terminfo/kitty.terminfo'], check=True, stderr=subprocess.PIPE)
proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], check=True, stderr=subprocess.PIPE)
regex = '^"terminfo/kitty.terminfo", line [0-9]+, col [0-9]+, terminal \'xterm-kitty\': older tic versions may treat the description field as an alias$'
for error in proc.stderr.decode('utf-8').splitlines():
if not re.match(regex, error):

View File

@ -26,7 +26,7 @@ def initialize_constants():
kitty_constants = {}
src = read_src_file('constants.py')
nv = re.search(r'Version\((\d+), (\d+), (\d+)\)', src)
kitty_constants['version'] = '%s.%s.%s' % (nv.group(1), nv.group(2), nv.group(3))
kitty_constants['version'] = f'{nv.group(1)}.{nv.group(2)}.{nv.group(3)}'
kitty_constants['appname'] = re.search(
r'appname: str\s+=\s+(u{0,1})[\'"]([^\'"]+)[\'"]', src
).group(2)

View File

@ -38,7 +38,7 @@ def binary_includes():
'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec'
))) + (
get_dll_path('bz2', 2), get_dll_path('ssl', 2), get_dll_path('crypto', 2),
get_dll_path('python' + py_ver, 2),
get_dll_path(f'python{py_ver}', 2),
)
@ -47,7 +47,7 @@ class Env:
def __init__(self, package_dir):
self.base = package_dir
self.lib_dir = j(self.base, 'lib')
self.py_dir = j(self.lib_dir, 'python' + py_ver)
self.py_dir = j(self.lib_dir, f'python{py_ver}')
os.makedirs(self.py_dir)
self.bin_dir = j(self.base, 'bin')
self.obj_dir = mkdtemp('launchers-')
@ -107,7 +107,7 @@ def add_ca_certs(env):
def copy_python(env):
print('Copying python...')
srcdir = j(PREFIX, 'lib/python' + py_ver)
srcdir = j(PREFIX, f'lib/python{py_ver}')
for x in os.listdir(srcdir):
y = j(srcdir, x)
@ -187,11 +187,11 @@ def strip_files(files, argv_max=(256 * 1024)):
def strip_binaries(files):
print('Stripping %d files...' % len(files))
print(f'Stripping {len(files)} files...')
before = sum(os.path.getsize(x) for x in files)
strip_files(files)
after = sum(os.path.getsize(x) for x in files)
print('Stripped %.1f MB' % ((before - after) / (1024 * 1024.)))
print('Stripped {:.1f} MB'.format((before - after) / (1024 * 1024.)))
def create_tarfile(env, compression_level='9'):
@ -203,7 +203,7 @@ def create_tarfile(env, compression_level='9'):
if err.errno != errno.ENOENT:
raise
os.mkdir(base)
dist = os.path.join(base, '%s-%s-%s.tar' % (kitty_constants['appname'], kitty_constants['version'], arch))
dist = os.path.join(base, f'{kitty_constants["appname"]}-{kitty_constants["version"]}-{arch}.tar')
with tarfile.open(dist, mode='w', format=tarfile.PAX_FORMAT) as tf:
cwd = os.getcwd()
os.chdir(env.base)
@ -213,13 +213,13 @@ def create_tarfile(env, compression_level='9'):
finally:
os.chdir(cwd)
print('Compressing archive...')
ans = dist.rpartition('.')[0] + '.txz'
ans = f'{dist.rpartition(".")[0]}.txz'
start_time = time.time()
subprocess.check_call(['xz', '--threads=0', '-f', '-' + compression_level, dist])
subprocess.check_call(['xz', '--threads=0', '-f', f'-{compression_level}', dist])
secs = time.time() - start_time
print('Compressed in %d minutes %d seconds' % (secs // 60, secs % 60))
os.rename(dist + '.xz', ans)
print('Archive %s created: %.2f MB' % (
print('Compressed in {} minutes {} seconds'.format(secs // 60, secs % 60))
os.rename(f'{dist}.xz', ans)
print('Archive {} created: {:.2f} MB'.format(
os.path.basename(ans), os.stat(ans).st_size / (1024.**2)))

View File

@ -90,7 +90,7 @@ def strip_files(files, argv_max=(256 * 1024)):
def files_in(folder):
for record in os.walk(folder):
for f in record[-1]:
yield os.path.join(record[0], f)
yield join(record[0], f)
def expand_dirs(items, exclude=lambda x: x.endswith('.so')):
@ -103,7 +103,7 @@ def expand_dirs(items, exclude=lambda x: x.endswith('.so')):
def do_sign(app_dir):
with current_dir(os.path.join(app_dir, 'Contents')):
with current_dir(join(app_dir, 'Contents')):
# Sign all .so files
so_files = {x for x in files_in('.') if x.endswith('.so')}
codesign(so_files)
@ -153,7 +153,7 @@ class Freeze(object):
self.to_strip = []
self.warnings = []
self.py_ver = py_ver
self.python_stdlib = join(self.resources_dir, 'Python', 'lib', 'python' + self.py_ver)
self.python_stdlib = join(self.resources_dir, 'Python', 'lib', f'python{self.py_ver}')
self.site_packages = self.python_stdlib # hack to avoid needing to add site-packages to path
self.obj_dir = mkdtemp('launchers-')
@ -177,7 +177,7 @@ class Freeze(object):
self.run_tests()
# self.run_shell()
ret = self.makedmg(self.build_dir, APPNAME + '-' + VERSION)
ret = self.makedmg(self.build_dir, f'{APPNAME}-{VERSION}')
return ret
@ -186,7 +186,7 @@ class Freeze(object):
print('\nDownloading CA certs...')
from urllib.request import urlopen
cdata = urlopen(kitty_constants['cacerts_url']).read()
dest = os.path.join(self.contents_dir, 'Resources', 'cacert.pem')
dest = join(self.contents_dir, 'Resources', 'cacert.pem')
with open(dest, 'wb') as f:
f.write(cdata)
@ -197,7 +197,7 @@ class Freeze(object):
@flush
def run_tests(self):
iv['run_tests'](os.path.join(self.contents_dir, 'MacOS', 'kitty'))
iv['run_tests'](join(self.contents_dir, 'MacOS', 'kitty'))
@flush
def set_id(self, path_to_lib, new_id):
@ -222,10 +222,10 @@ class Freeze(object):
@flush
def get_local_dependencies(self, path_to_lib):
for x, is_id in self.get_dependencies(path_to_lib):
for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/', '@rpath/'):
for y in (f'{PREFIX}/lib/', f'{PREFIX}/python/Python.framework/', '@rpath/'):
if x.startswith(y):
if y == PREFIX + '/python/Python.framework/':
y = PREFIX + '/python/'
if y == f'{PREFIX}/python/Python.framework/':
y = f'{PREFIX}/python/'
yield x, x[len(y):], is_id
break
@ -239,7 +239,7 @@ class Freeze(object):
self.to_strip.append(path_to_lib)
old_mode = flipwritable(path_to_lib)
for dep, bname, is_id in self.get_local_dependencies(path_to_lib):
ndep = self.FID + '/' + bname
ndep = f'{self.FID}/{bname}'
self.change_dep(dep, ndep, is_id, path_to_lib)
ldeps = list(self.get_local_dependencies(path_to_lib))
if ldeps:
@ -252,7 +252,7 @@ class Freeze(object):
@flush
def add_python_framework(self):
print('\nAdding Python framework')
src = join(PREFIX + '/python', 'Python.framework')
src = join(f'{PREFIX}/python', 'Python.framework')
x = join(self.frameworks_dir, 'Python.framework')
curr = os.path.realpath(join(src, 'Versions', 'Current'))
currd = join(x, 'Versions', basename(curr))
@ -262,12 +262,12 @@ class Freeze(object):
shutil.copy2(join(curr, 'Python'), currd)
self.set_id(
join(currd, 'Python'),
self.FID + '/Python.framework/Versions/%s/Python' % basename(curr))
f'{self.FID}/Python.framework/Versions/{basename(curr)}/Python')
# The following is needed for codesign
with current_dir(x):
os.symlink(basename(curr), 'Versions/Current')
for y in ('Python', 'Resources'):
os.symlink('Versions/Current/%s' % y, y)
os.symlink(f'Versions/Current/{y}', y)
@flush
def install_dylib(self, path, set_id=True):
@ -275,7 +275,7 @@ class Freeze(object):
if set_id:
self.set_id(
join(self.frameworks_dir, basename(path)),
self.FID + '/' + basename(path))
f'{self.FID}/{basename(path)}')
self.fix_dependencies_in_lib(join(self.frameworks_dir, basename(path)))
@flush
@ -291,11 +291,11 @@ class Freeze(object):
'rsync.2',
):
print('\nAdding', x)
x = 'lib%s.dylib' % x
x = f'lib{x}.dylib'
src = join(PREFIX, 'lib', x)
shutil.copy2(src, self.frameworks_dir)
dest = join(self.frameworks_dir, x)
self.set_id(dest, self.FID + '/' + x)
self.set_id(dest, f'{self.FID}/{x}')
self.fix_dependencies_in_lib(dest)
@flush
@ -321,7 +321,7 @@ class Freeze(object):
@flush
def add_stdlib(self):
print('\nAdding python stdlib')
src = PREFIX + '/python/Python.framework/Versions/Current/lib/python' + self.py_ver
src = f'{PREFIX}/python/Python.framework/Versions/Current/lib/python{self.py_ver}'
dest = self.python_stdlib
if not os.path.exists(dest):
os.makedirs(dest)
@ -345,19 +345,19 @@ class Freeze(object):
kitty_dir = join(self.resources_dir, 'kitty')
bases = ('kitty', 'kittens', 'kitty_tests')
for x in bases:
dest = os.path.join(self.python_stdlib, x)
os.rename(os.path.join(kitty_dir, x), dest)
dest = join(self.python_stdlib, x)
os.rename(join(kitty_dir, x), dest)
if x == 'kitty':
shutil.rmtree(os.path.join(dest, 'launcher'))
os.rename(os.path.join(kitty_dir, '__main__.py'), os.path.join(self.python_stdlib, 'kitty_main.py'))
shutil.rmtree(os.path.join(kitty_dir, '__pycache__'))
pdir = os.path.join(dirname(self.python_stdlib), 'kitty-extensions')
shutil.rmtree(join(dest, 'launcher'))
os.rename(join(kitty_dir, '__main__.py'), join(self.python_stdlib, 'kitty_main.py'))
shutil.rmtree(join(kitty_dir, '__pycache__'))
pdir = join(dirname(self.python_stdlib), 'kitty-extensions')
os.mkdir(pdir)
print('Extracting extension modules from', self.python_stdlib, 'to', pdir)
ext_map = extract_extension_modules(self.python_stdlib, pdir)
shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(self.python_stdlib, 'site.py'))
shutil.copy(join(os.path.dirname(self_dir), 'site.py'), join(self.python_stdlib, 'site.py'))
for x in bases:
iv['sanitize_source_folder'](os.path.join(self.python_stdlib, x))
iv['sanitize_source_folder'](join(self.python_stdlib, x))
self.compile_py_modules()
freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True)
iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir])
@ -434,23 +434,23 @@ class Freeze(object):
''' Copy a directory d into a dmg named volname '''
print('\nMaking dmg...')
sys.stdout.flush()
destdir = os.path.join(SW, 'dist')
destdir = join(SW, 'dist')
try:
shutil.rmtree(destdir)
except FileNotFoundError:
pass
os.mkdir(destdir)
dmg = os.path.join(destdir, volname + '.dmg')
dmg = join(destdir, f'{volname}.dmg')
if os.path.exists(dmg):
os.unlink(dmg)
tdir = tempfile.mkdtemp()
appdir = os.path.join(tdir, os.path.basename(d))
appdir = join(tdir, os.path.basename(d))
shutil.copytree(d, appdir, symlinks=True)
if self.sign_installers:
with timeit() as times:
sign_app(appdir, self.notarize)
print('Signing completed in %d minutes %d seconds' % tuple(times))
os.symlink('/Applications', os.path.join(tdir, 'Applications'))
print('Signing completed in {} minutes {} seconds'.format(*times))
os.symlink('/Applications', join(tdir, 'Applications'))
size_in_mb = int(
subprocess.check_output(['du', '-s', '-k', tdir]).decode('utf-8')
.split()[0]) / 1024.
@ -466,10 +466,10 @@ class Freeze(object):
print('\nCreating dmg...')
with timeit() as times:
subprocess.check_call(cmd + [dmg])
print('dmg created in %d minutes and %d seconds' % tuple(times))
print('dmg created in {} minutes and {} seconds'.format(*times))
shutil.rmtree(tdir)
size = os.stat(dmg).st_size / (1024 * 1024.)
print('\nInstaller size: %.2fMB\n' % size)
print(f'\nInstaller size: {size:.2f}MB\n')
return dmg
@ -477,7 +477,7 @@ def main():
args = globals()['args']
ext_dir = globals()['ext_dir']
Freeze(
os.path.join(ext_dir, kitty_constants['appname'] + '.app'),
join(ext_dir, f'{kitty_constants["appname"]}.app'),
dont_strip=args.dont_strip,
sign_installers=args.sign_installers,
notarize=args.notarize,

View File

@ -13,7 +13,7 @@ import subprocess
import sys
import time
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Match, Optional, Tuple
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
from docutils import nodes
from docutils.parsers.rst.roles import set_classes
@ -28,7 +28,7 @@ kitty_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if kitty_src not in sys.path:
sys.path.insert(0, kitty_src)
from kitty.conf.types import Definition # noqa
from kitty.conf.types import Definition, expand_opt_references # noqa
from kitty.constants import str_version, website_url # noqa
# config {{{
@ -217,7 +217,8 @@ if you specify a program-to-run you can use the special placeholder
from kitty.remote_control import cli_msg, global_options_spec
with open('generated/cli-kitty-at.rst', 'w') as f:
p = partial(print, file=f)
p('kitty @\n' + '-' * 80)
p('kitty @')
p('-' * 80)
p('.. program::', 'kitty @')
p('\n\n' + as_rst(
global_options_spec, message=cli_msg, usage='command ...', appname='kitty @'))
@ -225,7 +226,8 @@ if you specify a program-to-run you can use the special placeholder
for cmd_name in sorted(all_command_names()):
func = command_for_name(cmd_name)
p(f'.. _at_{func.name}:\n')
p('kitty @', func.name + '\n' + '-' * 120)
p('kitty @', func.name)
p('-' * 120)
p('.. program::', 'kitty @', func.name)
p('\n\n' + as_rst(*cli_params_for(func)))
from kittens.runner import get_kitten_cli_docs
@ -234,12 +236,12 @@ if you specify a program-to-run you can use the special placeholder
if data:
with open(f'generated/cli-kitten-{kitten}.rst', 'w') as f:
p = partial(print, file=f)
p('.. program::', f'kitty +kitten {kitten}')
p(f'\nSource code for {kitten}')
p('.. program::', 'kitty +kitten', kitten)
p('\nSource code for', kitten)
p('-' * 72)
p(f'\nThe source code for this kitten is `available on GitHub <https://github.com/kovidgoyal/kitty/tree/master/kittens/{kitten}>`_.')
p('\nCommand Line Interface')
p('-' * 72, file=f)
p('-' * 72)
p('\n\n' + option_spec_as_rst(
data['options'], message=data['help_text'], usage=data['usage'], appname=f'kitty +kitten {kitten}',
heading_char='^'))
@ -364,19 +366,6 @@ def link_role(
return [node], []
def expand_opt_references(conf_name: str, text: str) -> str:
conf_name += '.'
def expand(m: Match[str]) -> str:
ref = m.group(1)
if '<' not in ref and '.' not in ref:
full_ref = conf_name + ref
return f':opt:`{ref} <{full_ref}>`'
return str(m.group())
return re.sub(r':opt:`(.+?)`', expand, text)
opt_aliases: Dict[str, str] = {}
shortcut_slugs: Dict[str, Tuple[str, str]] = {}
@ -414,7 +403,7 @@ def process_opt_link(env: Any, refnode: Any, has_explicit_title: bool, title: st
conf_name, opt = target.partition('.')[::2]
if not opt:
conf_name, opt = 'kitty', conf_name
full_name = conf_name + '.' + opt
full_name = f'{conf_name}.{opt}'
return title, opt_aliases.get(full_name, full_name)
@ -422,7 +411,7 @@ def process_shortcut_link(env: Any, refnode: Any, has_explicit_title: bool, titl
conf_name, slug = target.partition('.')[::2]
if not slug:
conf_name, slug = 'kitty', conf_name
full_name = conf_name + '.' + slug
full_name = f'{conf_name}.{slug}'
try:
target, stitle = shortcut_slugs[full_name]
except KeyError:

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os
import subprocess
from collections import defaultdict
from typing import Any, DefaultDict, Dict, FrozenSet, List, Tuple, Union
@ -42,7 +43,7 @@ def parse_flag(keymap: KeymapType, type_map: Dict[str, Any], command_class: str)
lines = []
for ch in type_map['flag']:
attr, allowed_values = keymap[ch]
q = ' && '.join(f"g.{attr} != '{x}'" for x in allowed_values)
q = ' && '.join(f"g.{attr} != '{x}'" for x in sorted(allowed_values))
lines.append(f'''
case {attr}: {{
g.{attr} = screen->parser_buf[pos++] & 0xff;
@ -67,7 +68,7 @@ def cmd_for_report(report_name: str, keymap: KeymapType, type_map: Dict[str, Any
flag_fmt, flag_attrs = [], []
cv = {'flag': 'c', 'int': 'i', 'uint': 'I'}[atype]
for ch in type_map[atype]:
flag_fmt.append('s' + cv)
flag_fmt.append(f's{cv}')
attr = keymap[ch][0]
flag_attrs.append(f'"{attr}", {conv}g.{attr}')
return ' '.join(flag_fmt), ', '.join(flag_attrs)
@ -240,7 +241,7 @@ static inline void
def write_header(text: str, path: str) -> None:
with open(path, 'w') as f:
print(f'// This file is generated by {__file__} do not edit!', file=f, end='\n\n')
print(f'// This file is generated by {os.path.basename(__file__)} do not edit!', file=f, end='\n\n')
print('#pragma once', file=f)
print(text, file=f)
subprocess.check_call(['clang-format', '-i', path])

View File

@ -238,7 +238,7 @@ def patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_m
end = raw.index(end_q)
except ValueError:
raise SystemExit(f'Failed to find "{end_q}" in {path}')
raw = raw[:start] + start_q + '\n' + text + '\n' + raw[end:]
raw = f'{raw[:start]}{start_q}\n{text}\n{raw[end:]}'
f.seek(0)
f.truncate(0)
f.write(raw)
@ -368,7 +368,7 @@ def generate_ctrl_mapping() -> None:
val = str(ctrl_mapping[k])
items.append(val)
if k in "\\'":
k = '\\' + k
k = f'\\{k}'
mi.append(f" case '{k}': return {val};")
for line_items in chunks(items, 6):

View File

@ -80,7 +80,7 @@ def init_env(
) -> Env:
ans = env.copy()
ans.cflags.append('-fPIC')
ans.cppflags.append('-D_GLFW_' + module.upper())
ans.cppflags.append(f'-D_GLFW_{module.upper()}')
ans.cppflags.append('-D_GLFW_BUILD_DLL')
with open(os.path.join(base, 'source-info.json')) as f:

View File

@ -101,7 +101,7 @@ def remote_hostname(path: str) -> Tuple[Optional[str], Optional[str]]:
def resolve_remote_name(path: str, default: str) -> str:
remote_dir, rh = remote_hostname(path)
if remote_dir and rh:
return rh + ':' + os.path.relpath(path, remote_dir)
return f'{rh}:{os.path.relpath(path, remote_dir)}'
return default

View File

@ -97,7 +97,7 @@ def highlight_data(code: str, filename: str, aliases: Optional[Dict[str, str]] =
base, ext = os.path.splitext(filename)
alias = aliases.get(ext[1:])
if alias is not None:
filename = base + '.' + alias
filename = f'{base}.{alias}'
try:
lexer = get_lexer_for_filename(filename, stripnl=False)
except ClassNotFound:

View File

@ -347,7 +347,7 @@ class DiffHandler(Handler):
text = line.text
if line.image_data is not None:
image_involved = True
self.write('\r\x1b[K' + text + '\x1b[0m')
self.write(f'\r\x1b[K{text}\x1b[0m')
if self.current_search is not None:
self.current_search.highlight_line(self.write, lpos)
if i < num - 1:
@ -465,7 +465,7 @@ class DiffHandler(Handler):
)
else:
counts = styled(f'{len(self.current_search)} matches', fg=self.opts.margin_fg)
suffix = counts + ' ' + scroll_frac
suffix = f'{counts} {scroll_frac}'
prefix = styled(':', fg=self.opts.margin_fg)
filler = self.screen_size.cols - wcswidth(prefix) - wcswidth(suffix)
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)

View File

@ -244,14 +244,14 @@ class Differ:
except Exception as e:
return f'Running git diff for {left_path} vs. {right_path} generated an exception: {e}'
if not ok:
return output + f'\nRunning git diff for {left_path} vs. {right_path} failed'
return f'{output}\nRunning git diff for {left_path} vs. {right_path} failed'
left_lines = lines_for_path(left_path)
right_lines = lines_for_path(right_path)
try:
patch = parse_patch(output)
except Exception:
import traceback
return traceback.format_exc() + f'\nParsing diff for {left_path} vs. {right_path} failed'
return f'{traceback.format_exc()}\nParsing diff for {left_path} vs. {right_path} failed'
else:
ans[key] = patch
return ans

View File

@ -101,7 +101,7 @@ def human_readable(size: int, sep: str = ' ') -> str:
s = s[:s.find(".")+2]
if s.endswith('.0'):
s = s[:-2]
return s + sep + suffix
return f'{s}{sep}{suffix}'
def fit_in(text: str, count: int) -> str:
@ -110,7 +110,7 @@ def fit_in(text: str, count: int) -> str:
return text
if count > 1:
p = truncate_point_for_length(text, count - 1)
return text[:p] + ''
return f'{text[:p]}'
def fill_in(text: str, sz: int) -> str:
@ -127,8 +127,8 @@ def place_in(text: str, sz: int) -> str:
def format_func(which: str) -> Callable[[str], str]:
def formatted(text: str) -> str:
fmt = formats[which]
return '\x1b[' + fmt + 'm' + text + '\x1b[0m'
formatted.__name__ = which + '_format'
return f'\x1b[{fmt}m{text}\x1b[0m'
formatted.__name__ = f'{which}_format'
return formatted
@ -148,8 +148,8 @@ highlight_map = {'remove': ('removed_highlight', 'removed'), 'add': ('added_high
def highlight_boundaries(ltype: str) -> Tuple[str, str]:
s, e = highlight_map[ltype]
start = '\x1b[' + formats[s] + 'm'
stop = '\x1b[' + formats[e] + 'm'
start = f'\x1b[{formats[s]}m'
stop = f'\x1b[{formats[e]}m'
return start, stop

View File

@ -729,7 +729,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
else:
import shlex
text = ' '.join(shlex.quote(arg) for arg in cmd)
w.paste_bytes(text + '\r')
w.paste_bytes(f'{text}\r')
elif action == 'background':
import subprocess
subprocess.Popen(cmd, cwd=data['cwd'])

View File

@ -245,7 +245,7 @@ def main(args: List[str] = sys.argv) -> None:
raise SystemExit(f'Unknown queries: {", ".join(extra)}')
for key, val in do_queries(queries, cli_opts).items():
print(key + ':', val)
print(f'{key}:', val)
if __name__ == '__main__':

View File

@ -52,7 +52,7 @@ class KeysHandler(Handler):
self.cmd.colored(etype + ' ', 'yellow')
self.cmd.styled(key_event.text, italic=True)
self.print()
rep = 'CSI ' + encode_key_event(key_event)[2:]
rep = f'CSI {encode_key_event(key_event)[2:]}'
rep = rep.replace(';', ' ; ').replace(':', ' : ')[:-1] + ' ' + rep[-1]
self.cmd.styled(rep, fg='magenta')
if (key_event.shifted_key or key_event.alternate_key):

View File

@ -17,7 +17,7 @@ def print_key(raw: bytearray) -> None:
unix = ''
for ch in raw:
if ch < len(ctrl_keys):
unix += '^' + ctrl_keys[ch]
unix += f'^{ctrl_keys[ch]}'
elif ch == 127:
unix += '^?'
else:

View File

@ -281,7 +281,7 @@ def complete_choices(ans: Completions, prefix: str, title: str, choices: Iterabl
if q.startswith(effective_prefix):
if comma_separated:
tq = q
q = hidden_prefix + q + ','
q = f'{hidden_prefix}{q},'
word_transforms[q] = tq
matches[q] = ''
ans.add_match_group(title, matches, trailing_space=not comma_separated, word_transforms=word_transforms)

View File

@ -131,7 +131,7 @@ def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
other_ssh_args: Set[str] = set()
boolean_ssh_args: Set[str] = set()
for k, v in ssh_options().items():
k = '-' + k
k = f'-{k}'
if v:
other_ssh_args.add(k)
else:
@ -213,7 +213,7 @@ class InvalidSSHArgs(ValueError):
def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
boolean_ssh_args, other_ssh_args = get_ssh_cli()
passthrough_args = {'-' + x for x in 'Nnf'}
passthrough_args = {f'-{x}' for x in 'Nnf'}
ssh_args = []
server_args: List[str] = []
expecting_option_val = False
@ -230,7 +230,7 @@ def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
# could be a multi-character option
all_args = argument[1:]
for i, arg in enumerate(all_args):
arg = '-' + arg
arg = f'-{arg}'
if arg in passthrough_args:
passthrough = True
if arg in boolean_ssh_args:

View File

@ -376,7 +376,7 @@ def fetch_themes(
needs_delete = False
try:
with tempfile.NamedTemporaryFile(suffix='-' + os.path.basename(dest_path), dir=os.path.dirname(dest_path), delete=False) as f:
with tempfile.NamedTemporaryFile(suffix=f'-{os.path.basename(dest_path)}', dir=os.path.dirname(dest_path), delete=False) as f:
needs_delete = True
shutil.copyfileobj(res, f)
f.flush()
@ -405,7 +405,7 @@ def theme_name_from_file_name(fname: str) -> str:
ans = ans.replace('_', ' ')
def camel_case(m: 'Match[str]') -> str:
return str(m.group(1) + ' ' + m.group(2))
return f'{m.group(1)} {m.group(2)}'
ans = re.sub(r'([a-z])([A-Z])', camel_case, ans)
return ' '.join(x.capitalize() for x in filter(None, ans.split()))
@ -533,7 +533,7 @@ class Theme:
raw = ''
nraw = patch_conf(raw, self.name)
if raw:
with open(confpath + '.bak', 'w') as f:
with open(f'{confpath}.bak', 'w') as f:
f.write(raw)
atomic_save(nraw.encode('utf-8'), confpath)
if reload_in == 'parent':

View File

@ -37,7 +37,7 @@ def limit_length(text: str, limit: int = 32) -> str:
x = truncate_point_for_length(text, limit - 1)
if x >= len(text):
return text
return text[:x] + ''
return f'{text[:x]}'
class State(Enum):
@ -332,7 +332,7 @@ class ThemesHandler(Handler):
for line, width, is_current in self.themes_list.lines(num_rows):
num_rows -= 1
if is_current:
line = line.replace(MARK_AFTER, '\033[' + color_code('green') + 'm')
line = line.replace(MARK_AFTER, f'\033[{color_code("green")}m')
self.cmd.styled('>' if is_current else ' ', fg='green')
self.cmd.styled(line, bold=is_current, fg='green' if is_current else None)
self.cmd.move_cursor_by(mw - width, 'right')

View File

@ -155,12 +155,12 @@ def develop() -> None:
import sys
src = sys.argv[-1]
sig_loader = LoadSignature()
with open(src + '.sig', 'wb') as f:
with open(f'{src}.sig', 'wb') as f:
for chunk in signature_of_file(src):
sig_loader.add_chunk(chunk)
f.write(chunk)
sig_loader.commit()
with open(src + '.delta', 'wb') as f, PatchFile(src, src + '.output') as patcher:
with open(f'{src}.delta', 'wb') as f, PatchFile(src, f'{src}.output') as patcher:
for chunk in delta_for_file(src, sig_loader.signature):
f.write(chunk)
patcher.write(chunk)

View File

@ -45,7 +45,7 @@ def render_path_in_width(path: str, width: int) -> str:
if wcswidth(path) <= width:
return path
x = truncate_point_for_length(path, width - 1)
return path[:x] + ''
return f'{path[:x]}'
def render_seconds(val: float) -> str:

View File

@ -331,7 +331,7 @@ class Dircolors:
# change .xyz to *.xyz
yield '*' + pair[0], pair[1]
return ':'.join('%s=%s' % pair for pair in gen_pairs())
return ':'.join('{}={}'.format(*pair) for pair in gen_pairs())
def _format_code(self, text: str, code: str) -> str:
val = self.codes.get(code)

View File

@ -163,7 +163,7 @@ def set_scrolling_region(screen_size: Optional['ScreenSize'] = None, top: Option
@cmd
def scroll_screen(amt: int = 1) -> str:
return '\033[' + str(abs(amt)) + ('T' if amt < 0 else 'S')
return f'\033[{abs(amt)}{"T" if amt < 0 else "S"}'
STANDARD_COLORS = {'black': 0, 'red': 1, 'green': 2, 'yellow': 3, 'blue': 4, 'magenta': 5, 'cyan': 6, 'gray': 7, 'white': 7}
@ -465,7 +465,7 @@ def as_type_stub() -> str:
for name, func in all_cmds.items():
args = ', '.join(func_sig(func))
if args:
args = ', ' + args
args = f', {args}'
methods.append(f' def {name}(self{args}) -> str: pass')
ans += ['', '', 'class CMD:'] + methods

View File

@ -192,7 +192,7 @@ class Table:
if w < 2:
text += ' ' * (2 - w)
if len(desc) > space_for_desc:
text += desc[:space_for_desc - 1] + ''
text += f'{desc[:space_for_desc - 1]}'
else:
text += desc
extra = space_for_desc - len(desc)

View File

@ -62,7 +62,7 @@ else:
except Exception:
continue
try:
with open('/proc/' + x + '/stat', 'rb') as f:
with open(f'/proc/{x}/stat', 'rb') as f:
raw = f.read().decode('utf-8')
except OSError:
continue
@ -268,7 +268,7 @@ class Child:
# https://github.com/kovidgoyal/kitty/issues/1870
# xterm, urxvt, konsole and gnome-terminal do not do it in my
# testing.
argv[0] = ('-' + exe.split('/')[-1])
argv[0] = (f'-{exe.split("/")[-1]}')
exe = which(exe) or exe
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
os.close(slave)

View File

@ -82,7 +82,7 @@ def generate_stub() -> None:
for cmd_name in all_command_names():
cmd = command_for_name(cmd_name)
if cmd.options_spec:
do(cmd.options_spec, cmd.__class__.__name__ + 'RCOptions')
do(cmd.options_spec, f'{cmd.__class__.__name__}RCOptions')
save_type_stub(text, __file__)

View File

@ -12,8 +12,8 @@ from contextlib import suppress
from typing import Any
CSI = '\033['
OSC = '\033]'
CSI = '\x1b['
OSC = '\x1b]'
def write(x: str) -> None:
@ -42,11 +42,11 @@ def screen_alternate_keypad_mode() -> None:
def screen_cursor_position(y: int, x: int) -> None:
write(CSI + f'{y};{x}H')
write(f'{CSI}{y};{x}H')
def screen_cursor_forward(amt: int) -> None:
write(CSI + '%sC' % amt)
write(f'{CSI}{amt}C')
def screen_save_cursor() -> None:
@ -58,105 +58,105 @@ def screen_restore_cursor() -> None:
def screen_cursor_back1(amt: int) -> None:
write(CSI + '%sD' % amt)
write(f'{CSI}{amt}D')
def screen_save_modes() -> None:
write(CSI + '?s')
write(f'{CSI}?s')
def screen_restore_modes() -> None:
write(CSI + '?r')
write(f'{CSI}?r')
def screen_designate_charset(which: int, to: int) -> None:
w = '()'[int(which)]
t = chr(int(to))
write('\033' + w + t)
write(f'\x1b{w}{t}')
def select_graphic_rendition(*a: int) -> None:
write(CSI + '%sm' % ';'.join(map(str, a)))
write(f'{CSI}{";".join(map(str, a))}m')
def screen_cursor_to_column(c: int) -> None:
write(CSI + '%dG' % c)
write(f'{CSI}{c}G')
def screen_cursor_to_line(ln: int) -> None:
write(CSI + '%dd' % ln)
write(f'{CSI}{ln}d')
def screen_set_mode(x: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(x) + 'h')
write(f'{CSI}{"?" if private else ""}{x}h')
def screen_save_mode(x: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(x) + 's')
write(f'{CSI}{"?" if private else ""}{x}s')
def screen_reset_mode(x: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(x) + 'l')
write(f'{CSI}{"?" if private else ""}{x}l')
def screen_restore_mode(x: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(x) + 'r')
write(f'{CSI}{"?" if private else ""}{x}r')
def screen_set_margins(t: int, b: int) -> None:
write(CSI + '%d;%dr' % (t, b))
write(f'{CSI}{t};{b}r')
def screen_indexn(n: int) -> None:
write(CSI + '%dS' % n)
write(f'{CSI}{n}S')
def screen_delete_characters(count: int) -> None:
write(CSI + '%dP' % count)
write(f'{CSI}{count}P')
def screen_push_colors(which: int) -> None:
write(CSI + '%d#P' % which)
write(f'{CSI}{which}#P')
def screen_pop_colors(which: int) -> None:
write(CSI + '%d#Q' % which)
write(f'{CSI}{which}#Q')
def screen_report_colors() -> None:
write(CSI + '#R')
write(f'{CSI}#R')
def screen_repeat_character(num: int) -> None:
write(CSI + '%db' % num)
write(f'{CSI}{num}b')
def screen_insert_characters(count: int) -> None:
write(CSI + '%d@' % count)
write(f'{CSI}{count}@')
def screen_scroll(count: int) -> None:
write(CSI + '%dS' % count)
write(f'{CSI}{count}S')
def screen_erase_in_display(how: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(how) + 'J')
write(f'{CSI}{"?" if private else ""}{how}J')
def screen_erase_in_line(how: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(how) + 'K')
write(f'{CSI}{"?" if private else ""}{how}K')
def screen_delete_lines(num: int) -> None:
write(CSI + str(num) + 'M')
write(f'{CSI}{num}M')
def screen_cursor_up2(count: int) -> None:
write(CSI + '%dA' % count)
write(f'{CSI}{count}A')
def screen_cursor_down(count: int) -> None:
write(CSI + '%dB' % count)
write(f'{CSI}{count}B')
def screen_carriage_return() -> None:
@ -176,11 +176,11 @@ def screen_backspace() -> None:
def screen_set_cursor(mode: int, secondary: int) -> None:
write(CSI + '%d q' % secondary)
write(f'{CSI}{secondary} q')
def screen_insert_lines(num: int) -> None:
write(CSI + '%dL' % num)
write(f'{CSI}{num}L')
def draw(*a: str) -> None:
@ -188,7 +188,7 @@ def draw(*a: str) -> None:
def screen_manipulate_title_stack(op: int, which: int) -> None:
write(CSI + '%d;%dt' % (op, which))
write(f'{CSI}{op};{which}t')
def report_device_attributes(mode: int, char: int) -> None:
@ -197,21 +197,22 @@ def report_device_attributes(mode: int, char: int) -> None:
x += chr(char)
if mode:
x += str(mode)
write(CSI + x + 'c')
write(f'{CSI}{x}c')
def screen_decsace(mode: int) -> None:
write(CSI + str(mode) + '*x')
write(f'{CSI}{mode}*x')
def screen_set_8bit_controls(mode: int) -> None:
write('\x1b ' + ('G' if mode else 'F'))
write(f'\x1b {"G" if mode else "F"}')
def write_osc(code: int, string: str = '') -> None:
if string:
string = ';' + string
write(OSC + str(code) + string + '\x07')
write(f'{OSC}{code};{string}\x07')
else:
write(f'{OSC}{code}\x07')
set_dynamic_color = set_color_table_color = process_cwd_notification = write_osc
@ -235,7 +236,7 @@ def clipboard_control(payload: str) -> None:
clipboard_control_pending += data.lstrip(';')
payload = clipboard_control_pending
clipboard_control_pending = ''
write(OSC + payload + '\x07')
write(f'{OSC}{payload}\x07')
def replay(raw: str) -> None:

View File

@ -270,7 +270,7 @@ def zsh_output_serializer(ans: Completions) -> str:
yield ans
for description, matches in ans.match_groups.items():
cmd = ['compadd', '-U', '-J', shlex.quote(description), '-X', shlex.quote('%B' + description + '%b')]
cmd = ['compadd', '-U', '-J', shlex.quote(description), '-X', shlex.quote(f'%B{description}%b')]
if not matches.trailing_space:
cmd += ['-S', '""']
if matches.is_files:
@ -350,10 +350,10 @@ def fish2_output_serializer(ans: Completions) -> str:
def completions_for_first_word(ans: Completions, prefix: str, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> None:
cmds = ['@' + c for c in remote_control_command_names()]
cmds = [f'@{c}' for c in remote_control_command_names()]
ans.add_match_group('Entry points', {
k: '' for k in
list(entry_points) + cmds + ['+' + k for k in namespaced_entry_points]
list(entry_points) + cmds + [f'+{k}' for k in namespaced_entry_points]
if not prefix or k.startswith(prefix)
})
if prefix:
@ -443,7 +443,7 @@ def complete_alias_map(
long_opt = option_map.get(parts[0])
if long_opt is not None and complete_args is not None:
complete_args(ans, long_opt, parts[1], Delegate())
ans.add_prefix(parts[0] + '=')
ans.add_prefix(f'{parts[0]}=')
return
opt = option_map.get(w)
if w is last_word and not new_word:
@ -682,7 +682,7 @@ def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterabl
if words[0].startswith('@'):
if len(words) == 1 and not new_word:
prefix = words[0]
ans.add_match_group('Remote control commands', {'@' + c: '' for c in remote_control_command_names() if c.startswith(prefix)})
ans.add_match_group('Remote control commands', {f'@{c}': '' for c in remote_control_command_names() if c.startswith(prefix)})
else:
complete_remote_command(ans, words[0][1:], words[1:], new_word)
if words[0] == '+':

View File

@ -418,9 +418,9 @@ def generate_c_conversion(loc: str, ctypes: List[Option]) -> str:
def write_output(loc: str, defn: Definition) -> None:
cls, tc = generate_class(defn, loc)
with open(os.path.join(*loc.split('.'), 'options', 'types.py'), 'w') as f:
f.write(cls + '\n')
f.write(f'{cls}\n')
with open(os.path.join(*loc.split('.'), 'options', 'parse.py'), 'w') as f:
f.write(tc + '\n')
f.write(f'{tc}\n')
ctypes = []
for opt in defn.root_group.iter_all_non_groups():
if isinstance(opt, Option) and opt.ctype:
@ -428,7 +428,7 @@ def write_output(loc: str, defn: Definition) -> None:
if ctypes:
c = generate_c_conversion(loc, ctypes)
with open(os.path.join(*loc.split('.'), 'options', 'to-c-generated.h'), 'w') as f:
f.write(c + '\n')
f.write(f'{c}\n')
def main() -> None:
@ -454,6 +454,6 @@ def main() -> None:
loc = package_name
cls, tc = generate_class(defn, loc)
with open(os.path.join(os.path.dirname(path), 'kitten_options_types.py'), 'w') as f:
f.write(cls + '\n')
f.write(f'{cls}\n')
with open(os.path.join(os.path.dirname(path), 'kitten_options_parse.py'), 'w') as f:
f.write(tc + '\n')
f.write(f'{tc}\n')

View File

@ -37,8 +37,8 @@ def expand_opt_references(conf_name: str, text: str) -> str:
def expand(m: 'Match[str]') -> str:
ref = m.group(1)
if '<' not in ref and '.' not in ref:
full_ref = conf_name + ref
return f':opt:`{ref} <{full_ref}>`'
# full ref
return f':opt:`{ref} <{conf_name}{ref}>`'
return str(m.group())
return re.sub(r':opt:`(.+?)`', expand, text)
@ -214,7 +214,7 @@ class Option:
if not self.documented:
return ans
mopts = [self] + option_group
a('.. opt:: ' + ', '.join(conf_name + '.' + mo.name for mo in mopts))
a('.. opt:: ' + ', '.join(f'{conf_name}.{mo.name}' for mo in mopts))
a('.. code-block:: conf')
a('')
sz = max(len(x.name) for x in mopts)
@ -330,7 +330,7 @@ class Mapping:
raise ValueError(f'The shortcut for {self.name} has no short_text')
sc_text = f'{conf_name}.{self.short_text}'
shortcut_slugs[f'{conf_name}.{self.name}'] = (sc_text, self.key_text.replace('kitty_mod', kitty_mod))
a('.. shortcut:: ' + sc_text)
a(f'.. shortcut:: {sc_text}')
block_started = False
for sc in [self] + action_group:
if sc.add_to_default and sc.documented:
@ -534,7 +534,7 @@ class Group:
ans[i] = ' '.join(parts)
if commented:
ans = [x if x.startswith('#') or not x.strip() else ('# ' + x) for x in ans]
ans = [x if x.startswith('#') or not x.strip() else (f'# {x}') for x in ans]
return ans

View File

@ -54,7 +54,7 @@ def atomic_save(data: bytes, path: str) -> None:
@contextmanager
def cached_values_for(name: str) -> Generator[Dict[str, Any], None, None]:
cached_path = os.path.join(cache_dir(), name + '.json')
cached_path = os.path.join(cache_dir(), f'{name}.json')
cached_values: Dict[str, Any] = {}
try:
with open(cached_path, 'rb') as f:

View File

@ -29,7 +29,7 @@ def create_font_map(all_fonts: Iterable[CoreTextFont]) -> FontMap:
ps = (x['postscript_name'] or '').lower()
ans['family_map'].setdefault(f, []).append(x)
ans['ps_map'].setdefault(ps, []).append(x)
ans['full_map'].setdefault(f + ' ' + s, []).append(x)
ans['full_map'].setdefault(f'{f} {s}', []).append(x)
return ans
@ -45,7 +45,7 @@ def list_fonts() -> Generator[ListedFont, None, None]:
for fd in coretext_all_fonts():
f = fd['family']
if f:
fn = (f + ' ' + (fd['style'] or '')).strip()
fn = f'{f} {fd.get("style", "")}'.strip()
is_mono = bool(fd['monospace'])
yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono}

View File

@ -58,7 +58,7 @@ def list_fonts() -> Generator[ListedFont, None, None]:
if fn_:
fn = str(fn_)
else:
fn = (f + ' ' + str(fd.get('style', ''))).strip()
fn = f'{f} {fd.get("style", "")}'.strip()
is_mono = fd.get('spacing') in ('MONO', 'DUAL')
yield {'family': f, 'full_name': fn, 'postscript_name': str(fd.get('postscript_name', '')), 'is_monospace': is_mono}

View File

@ -28,13 +28,13 @@ def main(argv: Sequence[str]) -> None:
groups = create_family_groups()
for k in sorted(groups, key=lambda x: x.lower()):
if isatty:
print('\033[1;32m' + k + '\033[m')
print(f'\033[1;32m{k}\033[m')
else:
print(k)
for f in sorted(groups[k], key=lambda x: x['full_name'].lower()):
p = f['full_name']
if isatty:
p = '\033[3m' + p + '\033[m'
p = f'\033[3m{p}\033[m'
if psnames:
p += ' ({})'.format(f['postscript_name'])
print(' ', p)

View File

@ -515,7 +515,7 @@ def test_fallback_font(qtext: Optional[str] = None, bold: bool = False, italic:
try:
print(text, f)
except UnicodeEncodeError:
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
sys.stdout.buffer.write(f'{text} {f}\n'.encode('utf-8'))
def showcase() -> None:

View File

@ -58,7 +58,7 @@ def guess_type(path: str, allow_filesystem_access: bool = False) -> Optional[str
ext = path.rpartition('.')[-1].lower()
mt = known_extensions.get(ext)
if mt in text_mimes:
mt = 'text/' + mt.split('/', 1)[-1]
mt = f'text/{mt.split("/", 1)[-1]}'
if not mt and is_rc_file(path):
mt = 'text/plain'
if not mt and is_folder(path):

View File

@ -219,7 +219,6 @@ encode_function_key(const KeyEvent *ev, char *output) {
case GLFW_FKEY_PAGE_UP: S(5, '~');
case GLFW_FKEY_PAGE_DOWN: S(6, '~');
case GLFW_FKEY_HOME: S(1, 'H');
case GLFW_FKEY_KP_BEGIN: S(1, 'E');
case GLFW_FKEY_END: S(1, 'F');
case GLFW_FKEY_F1: S(1, 'P');
case GLFW_FKEY_F2: S(1, 'Q');
@ -233,6 +232,7 @@ encode_function_key(const KeyEvent *ev, char *output) {
case GLFW_FKEY_F10: S(21, '~');
case GLFW_FKEY_F11: S(23, '~');
case GLFW_FKEY_F12: S(24, '~');
case GLFW_FKEY_KP_BEGIN: S(1, 'E');
/* end special numbers */
case GLFW_FKEY_MENU:
// use the same encoding as xterm for this key in legacy mode (F16)

2
kitty/key_encoding.py generated
View File

@ -181,7 +181,7 @@ class EventType(IntEnum):
@lru_cache(maxsize=128)
def parse_shortcut(spec: str) -> ParsedShortcut:
if spec.endswith('+'):
spec = spec[:-1] + 'plus'
spec = f'{spec[:-1]}plus'
parts = spec.split('+')
key_name = parts[-1]
key_name = functional_key_name_aliases.get(key_name.upper(), key_name)

View File

@ -63,7 +63,7 @@ else:
import ctypes
for suffix in ('.0', ''):
with suppress(Exception):
lib = ctypes.CDLL('libxkbcommon.so' + suffix)
lib = ctypes.CDLL(f'libxkbcommon.so{suffix}')
break
else:
from ctypes.util import find_library

View File

@ -369,7 +369,7 @@ def launch(
if opts.stdin_add_formatting:
if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback',
'@first_cmd_output_on_screen', '@last_cmd_output', '@last_visited_cmd_output'):
q = '@ansi_' + q[1:]
q = f'@ansi_{q[1:]}'
if opts.stdin_add_line_wrap_markers:
q += '_wrap'
penv, stdin = boss.process_stdin_source(window=active, stdin=q, copy_pipe_data=pipe_data)

View File

@ -217,7 +217,7 @@ class Layout:
self.blank_rects: List[Rect] = []
self.layout_opts = self.parse_layout_opts(layout_opts)
assert self.name is not None
self.full_name = self.name + ((':' + layout_opts) if layout_opts else '')
self.full_name = f'{self.name}:{layout_opts}' if layout_opts else self.name
self.remove_all_biases()
def bias_increment_for_cell(self, is_horizontal: bool) -> float:

View File

@ -44,7 +44,7 @@ def set_custom_ibeam_cursor() -> None:
data = f.read()
rgba_data, width, height = load_png_data(data)
c2x = os.path.splitext(beam_cursor_data_file)
with open(c2x[0] + '@2x' + c2x[1], 'rb') as f:
with open(f'{c2x[0]}@2x{c2x[1]}', 'rb') as f:
data = f.read()
rgba_data2, width2, height2 = load_png_data(data)
images = (rgba_data, width, height), (rgba_data2, width2, height2)
@ -138,7 +138,7 @@ def get_macos_shortcut_for(
def set_x11_window_icon() -> None:
# max icon size on X11 64bits is 128x128
path, ext = os.path.splitext(logo_png_file)
set_default_window_icon(path + '-128' + ext)
set_default_window_icon(f'{path}-128{ext}')
def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None:
@ -220,7 +220,7 @@ def ensure_macos_locale() -> None:
lang = 'en_US'
else:
log_error(f'Could not set LANG Cocoa returns language as: {lang}')
os.environ['LANG'] = lang + '.UTF-8'
os.environ['LANG'] = f'{lang}.UTF-8'
@contextmanager

View File

@ -113,7 +113,7 @@ def parse_osc_99(raw: str) -> NotificationCommand:
elif k == 'd':
cmd.done = v != '0'
elif k == 'a':
cmd.actions += ',' + v
cmd.actions += f',{v}'
if payload_type not in ('body', 'title'):
log_error(f'Malformed OSC 99: unknown payload type: {payload_type}')
return NotificationCommand()
@ -139,7 +139,7 @@ def limit_size(x: str) -> str:
def merge_osc_99(prev: NotificationCommand, cmd: NotificationCommand) -> NotificationCommand:
if prev.done or prev.identifier != cmd.identifier:
return cmd
cmd.actions = limit_size(prev.actions + ',' + cmd.actions)
cmd.actions = limit_size(f'{prev.actions},{cmd.actions}')
cmd.title = limit_size(prev.title + cmd.title)
cmd.body = limit_size(prev.body + cmd.body)
return cmd
@ -198,7 +198,7 @@ def notify_with_command(cmd: NotificationCommand, window_id: int, notify_impleme
title = cmd.title or cmd.body
body = cmd.body if cmd.title else ''
if title:
identifier = 'i' + str(next(id_counter))
identifier = f'i{next(id_counter)}'
notify_implementation(title, body, identifier)
register_identifier(identifier, cmd, window_id)

View File

@ -114,7 +114,7 @@ def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc:
path = unquoted_path.lower()
for ext in mc.value.split(','):
ext = ext.strip()
if path.endswith('.' + ext):
if path.endswith(f'.{ext}'):
return True
return False

View File

@ -379,7 +379,7 @@ def parse_mods(parts: Iterable[str], sc: str) -> Optional[int]:
mods = 0
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
mods |= getattr(defines, f'GLFW_MOD_{map_mod(m.upper())}')
except AttributeError:
if m.upper() != 'NONE':
log_error(f'Shortcut: {sc} has unknown modifier, ignoring')
@ -394,7 +394,7 @@ def to_modifiers(val: str) -> int:
def parse_shortcut(sc: str) -> SingleKey:
if sc.endswith('+') and len(sc) > 1:
sc = sc[:-1] + 'plus'
sc = f'{sc[:-1]}plus'
parts = sc.split('+')
mods = 0
if len(parts) > 1:
@ -895,13 +895,13 @@ def resolve_aliases_and_parse_actions(
parts = rest.split(maxsplit=1)
if parts[0] != alias.name:
continue
new_defn = possible_alias + ' ' + alias.value + ((' ' + parts[1]) if len(parts) > 1 else '')
new_defn = f'{possible_alias} {alias.value} {f" {parts[1]}" if len(parts) > 1 else ""}'
new_aliases = aliases.copy()
new_aliases[possible_alias] = [a for a in aliases[possible_alias] if a is not alias]
yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)
return
else: # action_alias
new_defn = alias.value + ((' ' + rest) if rest else '')
new_defn = f'{alias.value} {rest}' if rest else alias.value
new_aliases = aliases.copy()
new_aliases.pop(possible_alias)
yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)
@ -909,7 +909,7 @@ def resolve_aliases_and_parse_actions(
if possible_alias == 'combine':
sep, rest = rest.split(maxsplit=1)
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
parts = re.split(fr'\s*{re.escape(sep)}\s*', rest)
for x in parts:
if x:
yield from resolve_aliases_and_parse_actions(x, aliases, map_type)

View File

@ -1,5 +1,4 @@
// This file is generated by /home/kovid/work/kitty/./gen-apc-parsers.py do not
// edit!
// This file is generated by gen-apc-parsers.py do not edit!
#pragma once
@ -148,9 +147,9 @@ static inline void parse_graphics_code(Screen *screen,
case action: {
g.action = screen->parser_buf[pos++] & 0xff;
if (g.action != 'q' && g.action != 'p' && g.action != 't' &&
g.action != 'd' && g.action != 'c' && g.action != 'a' &&
g.action != 'T' && g.action != 'f') {
if (g.action != 'T' && g.action != 'a' && g.action != 'c' &&
g.action != 'd' && g.action != 'f' && g.action != 'p' &&
g.action != 'q' && g.action != 't') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for action: 0x%x",
g.action);
@ -160,16 +159,16 @@ static inline void parse_graphics_code(Screen *screen,
case delete_action: {
g.delete_action = screen->parser_buf[pos++] & 0xff;
if (g.delete_action != 'q' && g.delete_action != 'Q' &&
g.delete_action != 'c' && g.delete_action != 'C' &&
g.delete_action != 'N' && g.delete_action != 'i' &&
g.delete_action != 'A' && g.delete_action != 'y' &&
g.delete_action != 'a' && g.delete_action != 'I' &&
g.delete_action != 'F' && g.delete_action != 'p' &&
g.delete_action != 'z' && g.delete_action != 'x' &&
g.delete_action != 'n' && g.delete_action != 'X' &&
g.delete_action != 'Y' && g.delete_action != 'P' &&
g.delete_action != 'Z' && g.delete_action != 'f') {
if (g.delete_action != 'A' && g.delete_action != 'C' &&
g.delete_action != 'F' && g.delete_action != 'I' &&
g.delete_action != 'N' && g.delete_action != 'P' &&
g.delete_action != 'Q' && g.delete_action != 'X' &&
g.delete_action != 'Y' && g.delete_action != 'Z' &&
g.delete_action != 'a' && g.delete_action != 'c' &&
g.delete_action != 'f' && g.delete_action != 'i' &&
g.delete_action != 'n' && g.delete_action != 'p' &&
g.delete_action != 'q' && g.delete_action != 'x' &&
g.delete_action != 'y' && g.delete_action != 'z') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for delete_action: 0x%x",
g.delete_action);
@ -179,8 +178,8 @@ static inline void parse_graphics_code(Screen *screen,
case transmission_type: {
g.transmission_type = screen->parser_buf[pos++] & 0xff;
if (g.transmission_type != 's' && g.transmission_type != 't' &&
g.transmission_type != 'd' && g.transmission_type != 'f') {
if (g.transmission_type != 'd' && g.transmission_type != 'f' &&
g.transmission_type != 's' && g.transmission_type != 't') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for transmission_type: 0x%x",
g.transmission_type);

View File

@ -107,20 +107,20 @@ Do not send text to the active window, even if it is one of the matched windows.
if '\x04' in decoded_data:
decoded_data = decoded_data[:decoded_data.index('\x04')]
keep_going = False
ret['data'] = 'text:' + decoded_data
ret['data'] = f'text:{decoded_data}'
yield ret
else:
while True:
data = sys.stdin.buffer.read(limit)
if not data:
break
ret['data'] = 'base64:' + base64.standard_b64encode(data).decode('ascii')
ret['data'] = f'base64:{base64.standard_b64encode(data).decode("ascii")}'
yield ret
def chunks(text: str) -> CmdGenerator:
data = parse_send_text_bytes(text).decode('utf-8')
while data:
ret['data'] = 'text:' + data[:limit]
ret['data'] = f'text:{data[:limit]}'
yield ret
data = data[limit:]
@ -130,7 +130,7 @@ Do not send text to the active window, even if it is one of the matched windows.
data = f.read(limit)
if not data:
break
ret['data'] = 'base64:' + base64.standard_b64encode(data).decode('ascii')
ret['data'] = f'base64:{base64.standard_b64encode(data).decode("ascii")}'
yield ret
sources = []

2
kitty/rgb.py generated
View File

@ -28,7 +28,7 @@ def parse_single_color(c: str) -> int:
def parse_sharp(spec: str) -> Optional[Color]:
if len(spec) in (3, 6, 9, 12):
part_len = len(spec) // 3
colors = re.findall(r'[a-fA-F0-9]{%d}' % part_len, spec)
colors = re.findall(fr'[a-fA-F0-9]{{{part_len}}}', spec)
return Color(*map(parse_single_color, colors))
return None

View File

@ -221,7 +221,7 @@ def real_main(global_opts: RCOptions) -> None:
if e.code != 0:
print(end=output_prefix, flush=True)
print_err(e)
print_err('Use "{}" to see how to use this command.'.format(emph('help ' + cmd)))
print_err('Use "{}" to see how to use this command.'.format(emph(f'help {cmd}')))
continue
except Exception:
print(end=output_prefix, flush=True)

View File

@ -102,14 +102,14 @@ class ColorFormatter:
ans = '9'
elif q == 'tab':
col = color_from_int((self.draw_data.tab_bg if self.which == '4' else self.draw_data.tab_fg)(self.tab_data))
ans = '8' + color_as_sgr(col)
ans = f'8{color_as_sgr(col)}'
else:
if name.startswith('_'):
q = '#' + name[1:]
q = f'#{name[1:]}'
c = to_color(q)
if c is None:
raise AttributeError(f'{name} is not a valid color')
ans = '8' + color_as_sgr(c)
ans = f'8{color_as_sgr(c)}'
return f'\x1b[{self.which}{ans}m'

View File

@ -471,8 +471,8 @@ def get_capabilities(query_string: str, opts: 'Options') -> Generator[str, None,
def result(encoded_query_name: str, x: Optional[str] = None) -> str:
if x is None:
return '0+r' + encoded_query_name
return '1+r' + encoded_query_name + '=' + hexlify(str(x).encode('utf-8')).decode('ascii')
return f'0+r{encoded_query_name}'
return f'1+r{encoded_query_name}={hexlify(str(x).encode("utf-8")).decode("ascii")}'
for encoded_query_name in query_string.split(';'):
name = qname = unhexlify(encoded_query_name).decode('utf-8')

View File

@ -40,7 +40,7 @@ def echo_cmd(cmd: Iterable[str]) -> None:
isatty = sys.stdout.isatty()
end = '\n'
if isatty:
end = '\x1b[m' + end
end = f'\x1b[m{end}'
print('\x1b[92m', end='')
print(shlex.join(cmd), end=end, flush=True)
@ -146,11 +146,11 @@ def run_website(args: Any) -> None:
def sign_file(path: str) -> None:
dest = path + '.sig'
dest = f'{path}.sig'
with suppress(FileNotFoundError):
os.remove(dest)
subprocess.check_call([
os.environ['PENV'] + '/gpg-as-kovid', '--output', path + '.sig',
os.environ['PENV'] + '/gpg-as-kovid', '--output', f'{path}.sig',
'--detach-sig', path
])
@ -159,7 +159,7 @@ def run_sdist(args: Any) -> None:
with tempfile.TemporaryDirectory() as tdir:
base = os.path.join(tdir, f'kitty-{version}')
os.mkdir(base)
subprocess.check_call('git archive HEAD | tar -x -C ' + base, shell=True)
subprocess.check_call(f'git archive HEAD | tar -x -C {base}', shell=True)
dest = os.path.join(base, 'docs', '_build')
os.mkdir(dest)
for x in 'html man'.split():
@ -167,9 +167,9 @@ def run_sdist(args: Any) -> None:
dest = os.path.abspath(os.path.join('build', f'kitty-{version}.tar'))
subprocess.check_call(['tar', '-cf', dest, os.path.basename(base)], cwd=tdir)
with suppress(FileNotFoundError):
os.remove(dest + '.xz')
os.remove(f'{dest}.xz')
subprocess.check_call(['xz', '-9', dest])
sign_file(dest + '.xz')
sign_file(f'{dest}.xz')
class ReadFileWithProgressReporting(io.FileIO): # {{{
@ -231,7 +231,7 @@ class Base: # {{{
class GitHub(Base): # {{{
API = 'https://api.github.com/'
API = 'https://api.github.com'
def __init__(
self,
@ -244,12 +244,12 @@ class GitHub(Base): # {{{
):
self.files, self.reponame, self.version, self.username, self.password, self.replace = (
files, reponame, version, username, password, replace)
self.current_tag_name = self.version if self.version == 'nightly' else ('v' + self.version)
self.current_tag_name = self.version if self.version == 'nightly' else f'v{self.version}'
self.is_nightly = self.current_tag_name == 'nightly'
self.requests = s = requests.Session()
s.auth = (self.username, self.password)
s.headers.update({'Accept': 'application/vnd.github.v3+json'})
self.url_base = f'{self.API}repos/{self.username}/{self.reponame}/releases/'
self.url_base = f'{self.API}/repos/{self.username}/{self.reponame}/releases'
def patch(self, url: str, fail_msg: str, **data: Any) -> None:
rdata = json.dumps(data)
@ -262,7 +262,7 @@ class GitHub(Base): # {{{
self.fail(r, fail_msg)
def update_nightly_description(self, release_id: int) -> None:
url = self.url_base + str(release_id)
url = f'{self.url_base}/{release_id}'
now = str(datetime.datetime.utcnow()).split('.')[0] + ' UTC'
with open('.git/refs/heads/master') as f:
commit = f.read().strip()
@ -276,7 +276,7 @@ class GitHub(Base): # {{{
# self.clean_older_releases(releases)
release = self.create_release()
upload_url = release['upload_url'].partition('{')[0]
asset_url = self.url_base + 'assets/{}'
asset_url = f'{self.url_base}/assets/{{}}'
existing_assets = self.existing_assets(release['id'])
if self.is_nightly:
for fname in existing_assets:
@ -308,7 +308,7 @@ class GitHub(Base): # {{{
self.info(f'\nDeleting old released installers from: {release["tag_name"]}')
for asset in release['assets']:
r = self.requests.delete(
f'{self.API}repos/{self.username}/{self.reponame}/releases/assets/{asset["id"]}')
f'{self.url_base}/assets/{asset["id"]}')
if r.status_code != 204:
self.fail(r, f'Failed to delete obsolete asset: {asset["name"]} for release: {release["tag_name"]}')
@ -336,7 +336,7 @@ class GitHub(Base): # {{{
return bool(error_code == 'already_exists')
def existing_assets(self, release_id: str) -> Dict[str, str]:
url = f'{self.API}repos/{self.username}/{self.reponame}/releases/{release_id}/assets'
url = f'{self.url_base}/{release_id}/assets'
r = self.requests.get(url)
if r.status_code != 200:
self.fail(r, 'Failed to get assets for release')
@ -345,15 +345,14 @@ class GitHub(Base): # {{{
def create_release(self) -> Dict[str, Any]:
' Create a release on GitHub or if it already exists, return the existing release '
# Check for existing release
url = f'{self.API}repos/{self.username}/{self.reponame}/releases/tags/{self.current_tag_name}'
url = f'{self.url_base}/tags/{self.current_tag_name}'
r = self.requests.get(url)
if r.status_code == 200:
return dict(r.json())
if self.is_nightly:
raise SystemExit('No existing nightly release found on GitHub')
url = f'{self.API}repos/{self.username}/{self.reponame}/releases'
r = self.requests.post(
url,
self.url_base,
data=json.dumps({
'tag_name': self.current_tag_name,
'target_commitish': 'master',
@ -394,7 +393,7 @@ def files_for_upload() -> Dict[str, str]:
files[f'build/kitty-{version}.tar.xz.sig'] = 'Source code GPG signature'
for path, desc in signatures.items():
sign_file(path)
files[path + '.sig'] = desc
files[f'{path}.sig'] = desc
for f in files:
if not os.path.exists(f):
raise SystemExit(f'The release artifact {f} does not exist')
@ -460,7 +459,7 @@ def exec_actions(actions: Iterable[str], args: Any) -> None:
for action in actions:
print('Running', action)
cwd = os.getcwd()
globals()['run_' + action](args)
globals()[f'run_{action}'](args)
os.chdir(cwd)

View File

@ -710,7 +710,7 @@ def compile_c_extension(
def on_success() -> None:
os.rename(dest, real_dest)
compilation_database.add_command(desc, cmd, partial(newer, real_dest, *objects), on_success=on_success, key=CompileKey('', module + '.so'))
compilation_database.add_command(desc, cmd, partial(newer, real_dest, *objects), on_success=on_success, key=CompileKey('', f'{module}.so'))
def find_c_files() -> Tuple[List[str], List[str]]:
@ -1112,7 +1112,7 @@ def create_macos_app_icon(where: str = 'Resources') -> None:
'iconutil', '-c', 'icns', iconset_dir, '-o', icns_dir
])
except FileNotFoundError:
print(error('iconutil not found') + ', using png2icns (without retina support) to convert the logo', file=sys.stderr)
print(f'{error("iconutil not found")}, using png2icns (without retina support) to convert the logo', file=sys.stderr)
subprocess.check_call([
'png2icns', icns_dir
] + [os.path.join(iconset_dir, logo) for logo in [