mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 01:15:33 +00:00
We can't initialize Sentry during module initialization because our `initialize_sentry` logs some status info. Setting the logging level later on is then a no-op, breaking verbose and very-verbose logging. This change fixes this by putting Sentry initialization into a separate method that gets called after we set the log level. Also refactor some common code into methods that appear in all our executables.
206 lines
8.2 KiB
Python
Executable file
206 lines
8.2 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
import datetime
|
|
import logging
|
|
import os
|
|
import requests
|
|
import requests_cache
|
|
import sys
|
|
import time
|
|
from pkg_resources import parse_version
|
|
from zipfile import is_zipfile
|
|
|
|
from keyman_config import KeymanApiUrl, add_standard_arguments, initialize_logging, initialize_sentry, secure_lookup
|
|
from keyman_config.get_kmp import get_keyboard_data, get_kmp, keyman_cache_dir
|
|
from keyman_config.install_kmp import extract_kmp, InstallError, InstallStatus, install_kmp
|
|
from keyman_config.kmpmetadata import get_metadata
|
|
from keyman_config.list_installed_kmp import get_kmp_version_shared, get_kmp_version_user
|
|
from keyman_config.uninstall_kmp import uninstall_kmp
|
|
|
|
|
|
def get_languages():
|
|
logging.info("Getting language list from search")
|
|
api_url = f"{KeymanApiUrl}/search?q=l:*&platform=linux"
|
|
logging.debug("At URL %s", api_url)
|
|
cache_dir = keyman_cache_dir()
|
|
current_dir = os.getcwd()
|
|
expire_after = datetime.timedelta(days=7)
|
|
if not os.path.isdir(cache_dir):
|
|
os.makedirs(cache_dir)
|
|
os.chdir(cache_dir)
|
|
requests_cache.install_cache(cache_name='keyman_cache', backend='sqlite', expire_after=expire_after)
|
|
now = time.ctime(int(time.time()))
|
|
try:
|
|
response = requests.get(api_url)
|
|
logging.debug("Time: {0} / Used Cache: {1}".format(now, response.from_cache))
|
|
os.chdir(current_dir)
|
|
requests_cache.uninstall_cache()
|
|
return response.json() if response.status_code == 200 else None
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def get_keyboard_list():
|
|
languages = get_languages()
|
|
keyboards = []
|
|
if not languages:
|
|
return []
|
|
|
|
if "languages" in languages:
|
|
for lang in languages["languages"]:
|
|
if "keyboards" in lang:
|
|
for kb in lang["keyboards"]:
|
|
kbnospace = kb.replace(" ", "%20")
|
|
if kbnospace not in keyboards:
|
|
keyboards.append(kbnospace)
|
|
if keyboards:
|
|
keyboards.sort()
|
|
return keyboards
|
|
|
|
|
|
def write_kmpdirlist(kmpdirfile):
|
|
try:
|
|
with open(kmpdirfile, 'wt') as kmpdirlist:
|
|
if keyboards := get_keyboard_list():
|
|
for kb in keyboards:
|
|
print(kb, file=kmpdirlist)
|
|
except Exception as e:
|
|
logging.warning('Exception %s writing %s %s', type(e), kmpdirfile, e.args)
|
|
|
|
|
|
def list_keyboards():
|
|
kmpdirfile = os.path.join(keyman_cache_dir(), 'kmpdirlist')
|
|
if not os.path.exists(kmpdirfile):
|
|
write_kmpdirlist(kmpdirfile)
|
|
else:
|
|
logging.debug("kmpdirlist already exists")
|
|
# file empty
|
|
if os.path.getsize(kmpdirfile) == 0:
|
|
write_kmpdirlist(kmpdirfile)
|
|
|
|
|
|
def _list_languages_for_keyboard_impl(packageId, packageDir):
|
|
kmpfile = os.path.join(keyman_cache_dir(), packageId)
|
|
if not os.path.exists(kmpfile):
|
|
kmpfile = f'{kmpfile}.kmp'
|
|
if not os.path.exists(kmpfile):
|
|
return ''
|
|
extract_kmp(kmpfile, packageDir)
|
|
info, system, options, keyboards, files = get_metadata(packageDir)
|
|
if not keyboards:
|
|
return ''
|
|
firstKeyboard = keyboards[0]
|
|
if not secure_lookup(firstKeyboard, 'languages') or len(firstKeyboard['languages']) <= 0:
|
|
return ''
|
|
result = ''
|
|
for lang in firstKeyboard['languages']:
|
|
if result:
|
|
result += '\n'
|
|
result += lang['id']
|
|
return result
|
|
|
|
|
|
def list_languages_for_keyboard(packageId, packageDir):
|
|
result = _list_languages_for_keyboard_impl(packageId, packageDir)
|
|
print(result)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Install a Keyman keyboard package, either a local .kmp file or specify a ' +
|
|
'keyboard id to download and install, optionally specifying a language for which to ' +
|
|
'install the keyboard.',
|
|
epilog='Example: km-package-install -s -p sil_el_ethiopian_latin --bcp47 ssy-latn')
|
|
parser.add_argument('-s', '--shared', action='store_true', help='Install to shared area /usr/local')
|
|
parser.add_argument('-f', '--file', metavar='KMPFILE', help='Keyman kmp file')
|
|
parser.add_argument('-p', '--package', metavar='PACKAGE', help='Keyman package id')
|
|
parser.add_argument('-l', '--bcp47', metavar='LANGTAG', help='bcp47 language tag')
|
|
parser.add_argument('--force', action='store_true', help='force installation of keyboard even if it is ' +
|
|
'a downgrade to an older version of the keyboard')
|
|
add_standard_arguments(parser)
|
|
|
|
args = parser.parse_args()
|
|
|
|
initialize_logging(args)
|
|
initialize_sentry()
|
|
|
|
if args.package and args.file:
|
|
parser.print_usage()
|
|
logging.error(
|
|
"km-package-install: error: too many arguments: either install a local kmp file or " +
|
|
"specify a keyboard id to download and install.")
|
|
sys.exit(2)
|
|
|
|
if os.path.exists(os.path.join(keyman_cache_dir(), 'kmpdirlist')):
|
|
os.remove(os.path.join(keyman_cache_dir(), 'kmpdirlist'))
|
|
|
|
def try_install_kmp(inputfile, arg, language=None, sharedarea=False):
|
|
try:
|
|
install_kmp(inputfile, sharedarea, language)
|
|
except InstallError as e:
|
|
if e.status == InstallStatus.Abort:
|
|
logging.error("km-package-install: error: Failed to install %s", arg)
|
|
logging.error(e.message)
|
|
sys.exit(3)
|
|
else:
|
|
logging.warning(e.message)
|
|
|
|
if args.file:
|
|
name, ext = os.path.splitext(args.file)
|
|
if ext != ".kmp" or not is_zipfile(args.file):
|
|
logging.error("km-package-install: Input file %s is not a kmp file.", args.file)
|
|
logging.error("km-package-install -f <kmpfile>")
|
|
sys.exit(2)
|
|
|
|
if not os.path.isfile(args.file):
|
|
logging.error("km-package-install: Keyman kmp file %s not found.", args.file)
|
|
logging.error("km-package-install -f <kmpfile>")
|
|
sys.exit(2)
|
|
try_install_kmp(args.file, f"file {args.file}", args.bcp47, args.shared)
|
|
elif args.package:
|
|
if args.shared:
|
|
if get_kmp_version_user(args.package):
|
|
logging.warning(f'km-package-install: Warning: Keyboard {args.package} is ' +
|
|
'also installed for the current user.')
|
|
installed_kmp_ver = get_kmp_version_shared(args.package)
|
|
else:
|
|
if get_kmp_version_shared(args.package):
|
|
logging.warning(f'km-package-install: Warning: Keyboard {args.package} is ' +
|
|
'already installed in the shared area.')
|
|
installed_kmp_ver = get_kmp_version_user(args.package)
|
|
pkgdata = get_keyboard_data(args.package)
|
|
if not pkgdata:
|
|
logging.error("km-package-install: error: Could not download keyboard data for %s", args.package)
|
|
sys.exit(3)
|
|
pkg_version = secure_lookup(pkgdata, 'version')
|
|
if installed_kmp_ver and pkg_version:
|
|
if parse_version(installed_kmp_ver) > parse_version(pkg_version):
|
|
if args.force:
|
|
logging.warning(f'km-package-install: warning: The {args.package} keyboard ' +
|
|
f'is already installed with a newer version {installed_kmp_ver}. ' +
|
|
f'Downgrading to version {pkg_version}.')
|
|
else:
|
|
logging.error(f'km-package-install: error: The {args.package} keyboard is ' +
|
|
f'already installed with a newer version {installed_kmp_ver}. ' +
|
|
f'Not installing version {pkg_version}. Use `--force` to downgrade.')
|
|
sys.exit(1)
|
|
if args.force:
|
|
uninstall_kmp(args.package, args.shared, False)
|
|
|
|
kmpfile = get_kmp(args.package)
|
|
if kmpfile:
|
|
try_install_kmp(kmpfile, f"keyboard package {args.package}", args.bcp47, args.shared)
|
|
else:
|
|
logging.error("km-package-install: error: Could not download keyboard package %s", args.package)
|
|
sys.exit(2)
|
|
else:
|
|
parser.print_usage()
|
|
logging.error(
|
|
"km-package-install: error: no arguments: either install a local kmp file " +
|
|
"or specify a keyboard package id to download and install.")
|
|
sys.exit(2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|