forked from keymanapp/keyman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
km-package-install
executable file
·213 lines (185 loc) · 8.6 KB
/
km-package-install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/python3
import argparse
import datetime
import logging
import os
import sys
import time
from zipfile import is_zipfile
import packaging.version
import requests
import requests_cache
from keyman_config import KeymanApiUrl, add_standard_arguments, initialize_logging, initialize_sentry, secure_lookup, verify_dbus_running
from keyman_config.fcitx_util import is_fcitx_running
from keyman_config.get_kmp import get_keyboard_data, get_kmp, keyman_cache_dir
from keyman_config.ibus_util import IbusDaemon, verify_ibus_daemon
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()
verify_dbus_running()
if (not is_fcitx_running()) and (verify_ibus_daemon(True) != IbusDaemon.RUNNING):
logging.error("km-package-install: error: ibus-daemon is not running. You might have to reboot or logout and login again.")
sys.exit(4)
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 packaging.version.parse(installed_kmp_ver) > packaging.version.parse(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()