Skip to content

Commit 0dc5e83

Browse files
authored
Merge pull request #1468 from inclement/improve_fault_handling
Replaced many `exit(1)`s with exception raising
2 parents ea9f346 + bec18e1 commit 0dc5e83

File tree

10 files changed

+107
-91
lines changed

10 files changed

+107
-91
lines changed

pythonforandroid/archs.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import sys
44
from distutils.spawn import find_executable
55

6-
from pythonforandroid.logger import warning
76
from pythonforandroid.recipe import Recipe
7+
from pythonforandroid.util import BuildInterruptingException
88

99

1010
class Arch(object):
@@ -86,11 +86,11 @@ def get_env(self, with_flags_in_cc=True):
8686
command_prefix=command_prefix), path=environ['PATH'])
8787
if cc is None:
8888
print('Searching path are: {!r}'.format(environ['PATH']))
89-
warning('Couldn\'t find executable for CC. This indicates a '
90-
'problem locating the {} executable in the Android '
91-
'NDK, not that you don\'t have a normal compiler '
92-
'installed. Exiting.')
93-
exit(1)
89+
raise BuildInterruptingException(
90+
'Couldn\'t find executable for CC. This indicates a '
91+
'problem locating the {} executable in the Android '
92+
'NDK, not that you don\'t have a normal compiler '
93+
'installed. Exiting.')
9494

9595
if with_flags_in_cc:
9696
env['CC'] = '{ccache}{command_prefix}-gcc {cflags}'.format(

pythonforandroid/bdistapk.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ def finalize_options(self):
7373
sys.argv.append('--arch={}'.format(arch))
7474

7575
def run(self):
76-
7776
self.prepare_build_dir()
7877

7978
from pythonforandroid.toolchain import main

pythonforandroid/build.py

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
import sh
1111
import subprocess
1212

13-
from pythonforandroid.util import (ensure_dir, current_directory)
14-
from pythonforandroid.logger import (info, warning, error, info_notify,
15-
Err_Fore, info_main, shprint)
13+
from pythonforandroid.util import (ensure_dir, current_directory, BuildInterruptingException)
14+
from pythonforandroid.logger import (info, warning, info_notify, info_main, shprint)
1615
from pythonforandroid.archs import ArchARM, ArchARMv7_a, ArchAarch_64, Archx86, Archx86_64
1716
from pythonforandroid.recipe import Recipe
1817

@@ -224,8 +223,7 @@ def prepare_build_environment(self,
224223
'maintain your own SDK download.')
225224
sdk_dir = possible_dirs[0]
226225
if sdk_dir is None:
227-
warning('Android SDK dir was not specified, exiting.')
228-
exit(1)
226+
raise BuildInterruptingException('Android SDK dir was not specified, exiting.')
229227
self.sdk_dir = realpath(sdk_dir)
230228

231229
# Check what Android API we're using
@@ -244,11 +242,11 @@ def prepare_build_environment(self,
244242
self.android_api = android_api
245243

246244
if self.android_api >= 21 and self.archs[0].arch == 'armeabi':
247-
error('Asked to build for armeabi architecture with API '
248-
'{}, but API 21 or greater does not support armeabi'.format(
249-
self.android_api))
250-
error('You probably want to build with --arch=armeabi-v7a instead')
251-
exit(1)
245+
raise BuildInterruptingException(
246+
'Asked to build for armeabi architecture with API '
247+
'{}, but API 21 or greater does not support armeabi'.format(
248+
self.android_api),
249+
instructions='You probably want to build with --arch=armeabi-v7a instead')
252250

253251
if exists(join(sdk_dir, 'tools', 'bin', 'avdmanager')):
254252
avdmanager = sh.Command(join(sdk_dir, 'tools', 'bin', 'avdmanager'))
@@ -257,9 +255,9 @@ def prepare_build_environment(self,
257255
android = sh.Command(join(sdk_dir, 'tools', 'android'))
258256
targets = android('list').stdout.decode('utf-8').split('\n')
259257
else:
260-
error('Could not find `android` or `sdkmanager` binaries in '
261-
'Android SDK. Exiting.')
262-
exit(1)
258+
raise BuildInterruptingException(
259+
'Could not find `android` or `sdkmanager` binaries in Android SDK',
260+
instructions='Make sure the path to the Android SDK is correct')
263261
apis = [s for s in targets if re.match(r'^ *API level: ', s)]
264262
apis = [re.findall(r'[0-9]+', s) for s in apis]
265263
apis = [int(s[0]) for s in apis if s]
@@ -269,10 +267,9 @@ def prepare_build_environment(self,
269267
info(('Requested API target {} is available, '
270268
'continuing.').format(android_api))
271269
else:
272-
warning(('Requested API target {} is not available, install '
273-
'it with the SDK android tool.').format(android_api))
274-
warning('Exiting.')
275-
exit(1)
270+
raise BuildInterruptingException(
271+
('Requested API target {} is not available, install '
272+
'it with the SDK android tool.').format(android_api))
276273

277274
# Find the Android NDK
278275
# Could also use ANDROID_NDK, but doesn't look like many tools use this
@@ -305,8 +302,7 @@ def prepare_build_environment(self,
305302
'maintain your own NDK download.')
306303
ndk_dir = possible_dirs[0]
307304
if ndk_dir is None:
308-
warning('Android NDK dir was not specified, exiting.')
309-
exit(1)
305+
raise BuildInterruptingException('Android NDK dir was not specified')
310306
self.ndk_dir = realpath(ndk_dir)
311307

312308
# Find the NDK version, and check it against what the NDK dir
@@ -367,11 +363,11 @@ def prepare_build_environment(self,
367363
self.ndk_api = ndk_api
368364

369365
if self.ndk_api > self.android_api:
370-
error('Target NDK API is {}, higher than the target Android API {}.'.format(
371-
self.ndk_api, self.android_api))
372-
error('The NDK API is a minimum supported API number and must be lower '
373-
'than the target Android API')
374-
exit(1)
366+
raise BuildInterruptingException(
367+
'Target NDK API is {}, higher than the target Android API {}.'.format(
368+
self.ndk_api, self.android_api),
369+
instructions=('The NDK API is a minimum supported API number and must be lower '
370+
'than the target Android API'))
375371

376372
info('Using {} NDK {}'.format(self.ndk.capitalize(), self.ndk_ver))
377373

@@ -399,8 +395,7 @@ def prepare_build_environment(self,
399395
self.cython = cython
400396
break
401397
else:
402-
error('No cython binary found. Exiting.')
403-
exit(1)
398+
raise BuildInterruptingException('No cython binary found.')
404399
if not self.cython:
405400
ok = False
406401
warning("Missing requirement: cython is not installed")
@@ -474,9 +469,8 @@ def prepare_build_environment(self,
474469
executable))
475470

476471
if not ok:
477-
error('{}python-for-android cannot continue; aborting{}'.format(
478-
Err_Fore.RED, Err_Fore.RESET))
479-
sys.exit(1)
472+
raise BuildInterruptingException(
473+
'python-for-android cannot continue due to the missing executables above')
480474

481475
def __init__(self):
482476
super(Context, self).__init__()
@@ -524,8 +518,7 @@ def set_archs(self, arch_names):
524518
new_archs.add(match)
525519
self.archs = list(new_archs)
526520
if not self.archs:
527-
warning('Asked to compile for no Archs, so failing.')
528-
exit(1)
521+
raise BuildInterruptingException('Asked to compile for no Archs, so failing.')
529522
info('Will compile for the following archs: {}'.format(
530523
', '.join([arch.arch for arch in self.archs])))
531524

pythonforandroid/distribution.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
import glob
33
import json
44

5-
from pythonforandroid.logger import (info, info_notify, warning,
6-
Err_Style, Err_Fore, error)
7-
from pythonforandroid.util import current_directory
5+
from pythonforandroid.logger import (info, info_notify, warning, Err_Style, Err_Fore)
6+
from pythonforandroid.util import current_directory, BuildInterruptingException
87
from shutil import rmtree
98

109

@@ -132,17 +131,16 @@ def get_distribution(cls, ctx, name=None, recipes=[],
132131
# then the existing dist is incompatible with the requested
133132
# configuration and the build cannot continue
134133
if name_match_dist is not None and not allow_replace_dist:
135-
error('Asked for dist with name {name} with recipes ({req_recipes}) and '
136-
'NDK API {req_ndk_api}, but a dist '
137-
'with this name already exists and has either incompatible recipes '
138-
'({dist_recipes}) or NDK API {dist_ndk_api}'.format(
139-
name=name,
140-
req_ndk_api=ndk_api,
141-
dist_ndk_api=name_match_dist.ndk_api,
142-
req_recipes=', '.join(recipes),
143-
dist_recipes=', '.join(name_match_dist.recipes)))
144-
error('No compatible dist found, so exiting.')
145-
exit(1)
134+
raise BuildInterruptingException(
135+
'Asked for dist with name {name} with recipes ({req_recipes}) and '
136+
'NDK API {req_ndk_api}, but a dist '
137+
'with this name already exists and has either incompatible recipes '
138+
'({dist_recipes}) or NDK API {dist_ndk_api}'.format(
139+
name=name,
140+
req_ndk_api=ndk_api,
141+
dist_ndk_api=name_match_dist.ndk_api,
142+
req_recipes=', '.join(recipes),
143+
dist_recipes=', '.join(name_match_dist.recipes)))
146144

147145
# If we got this far, we need to build a new dist
148146
dist = Distribution(ctx)
@@ -172,9 +170,9 @@ def delete(self):
172170
def get_distributions(cls, ctx, extra_dist_dirs=[]):
173171
'''Returns all the distributions found locally.'''
174172
if extra_dist_dirs:
175-
warning('extra_dist_dirs argument to get_distributions '
176-
'is not yet implemented')
177-
exit(1)
173+
raise BuildInterruptingException(
174+
'extra_dist_dirs argument to get_distributions '
175+
'is not yet implemented')
178176
dist_dir = ctx.dist_dir
179177
folders = glob.glob(join(dist_dir, '*'))
180178
for dir in extra_dist_dirs:

pythonforandroid/graph.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from copy import deepcopy
22
from itertools import product
3-
from sys import exit
43

5-
from pythonforandroid.logger import (info, warning, error)
4+
from pythonforandroid.logger import (info, warning)
65
from pythonforandroid.recipe import Recipe
76
from pythonforandroid.bootstrap import Bootstrap
7+
from pythonforandroid.util import BuildInterruptingException
88

99

1010
class RecipeOrder(dict):
@@ -133,11 +133,10 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
133133
key=lambda order: -('python2' in order) - ('sdl2' in order))
134134

135135
if not orders:
136-
error('Didn\'t find any valid dependency graphs.')
137-
error('This means that some of your requirements pull in '
138-
'conflicting dependencies.')
139-
error('Exiting.')
140-
exit(1)
136+
raise BuildInterruptingException(
137+
'Didn\'t find any valid dependency graphs. This means that some of your '
138+
'requirements pull in conflicting dependencies.')
139+
141140
# It would be better to check against possible orders other
142141
# than the first one, but in practice clashes will be rare,
143142
# and can be resolved by specifying more parameters

pythonforandroid/recipe.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
from urlparse import urlparse
1717
except ImportError:
1818
from urllib.parse import urlparse
19-
from pythonforandroid.logger import (logger, info, warning, error, debug, shprint, info_main)
20-
from pythonforandroid.util import (urlretrieve, current_directory, ensure_dir)
19+
from pythonforandroid.logger import (logger, info, warning, debug, shprint, info_main)
20+
from pythonforandroid.util import (urlretrieve, current_directory, ensure_dir,
21+
BuildInterruptingException)
2122

2223
# this import is necessary to keep imp.load_source from complaining :)
2324
if PY2:
@@ -582,8 +583,8 @@ class IncludedFilesBehaviour(object):
582583

583584
def prepare_build_dir(self, arch):
584585
if self.src_filename is None:
585-
print('IncludedFilesBehaviour failed: no src_filename specified')
586-
exit(1)
586+
raise BuildInterruptingException(
587+
'IncludedFilesBehaviour failed: no src_filename specified')
587588
shprint(sh.rm, '-rf', self.get_build_dir(arch))
588589
shprint(sh.cp, '-a', join(self.get_recipe_dir(), self.src_filename),
589590
self.get_build_dir(arch))
@@ -1051,9 +1052,9 @@ def __init__(self, *args, **kwargs):
10511052
def prebuild_arch(self, arch):
10521053
super(TargetPythonRecipe, self).prebuild_arch(arch)
10531054
if self.from_crystax and self.ctx.ndk != 'crystax':
1054-
error('The {} recipe can only be built when '
1055-
'using the CrystaX NDK. Exiting.'.format(self.name))
1056-
exit(1)
1055+
raise BuildInterruptingException(
1056+
'The {} recipe can only be built when '
1057+
'using the CrystaX NDK. Exiting.'.format(self.name))
10571058
self.ctx.python_recipe = self
10581059

10591060
def include_root(self, arch):

pythonforandroid/recipes/python3/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from pythonforandroid.recipe import TargetPythonRecipe, Recipe
22
from pythonforandroid.toolchain import shprint, current_directory
3-
from pythonforandroid.logger import logger, info, error
4-
from pythonforandroid.util import ensure_dir, walk_valid_filens
3+
from pythonforandroid.logger import logger, info
4+
from pythonforandroid.util import ensure_dir, walk_valid_filens, BuildInterruptingException
55
from os.path import exists, join, dirname
66
from os import environ
77
import glob
@@ -67,9 +67,9 @@ def set_libs_flags(self, env, arch):
6767

6868
def build_arch(self, arch):
6969
if self.ctx.ndk_api < self.MIN_NDK_API:
70-
error('Target ndk-api is {}, but the python3 recipe supports only {}+'.format(
71-
self.ctx.ndk_api, self.MIN_NDK_API))
72-
exit(1)
70+
raise BuildInterruptingException(
71+
'Target ndk-api is {}, but the python3 recipe supports only {}+'.format(
72+
self.ctx.ndk_api, self.MIN_NDK_API))
7373

7474
recipe_build_dir = self.get_build_dir(arch.arch)
7575

pythonforandroid/toolchain.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from __future__ import print_function
1010
from pythonforandroid import __version__
1111
from pythonforandroid.build import DEFAULT_NDK_API, DEFAULT_ANDROID_API
12+
from pythonforandroid.util import BuildInterruptingException, handle_build_exception
1213

1314

1415
def check_python_dependencies():
@@ -58,7 +59,7 @@ def check_python_dependencies():
5859
ok = False
5960

6061
if not ok:
61-
print('python-for-android is exiting due to the errors above.')
62+
print('python-for-android is exiting due to the errors logged above')
6263
exit(1)
6364

6465

@@ -85,7 +86,7 @@ def check_python_dependencies():
8586
from pythonforandroid.recipe import Recipe
8687
from pythonforandroid.logger import (logger, info, warning, setup_color,
8788
Out_Style, Out_Fore,
88-
info_notify, info_main, shprint, error)
89+
info_notify, info_main, shprint)
8990
from pythonforandroid.util import current_directory
9091
from pythonforandroid.bootstrap import Bootstrap
9192
from pythonforandroid.distribution import Distribution, pretty_log_dists
@@ -638,7 +639,7 @@ def clean(self, args):
638639

639640
for component in components:
640641
if component not in component_clean_methods:
641-
raise ValueError((
642+
raise BuildInterruptingException((
642643
'Asked to clean "{}" but this argument is not '
643644
'recognised'.format(component)))
644645
component_clean_methods[component](args)
@@ -735,10 +736,10 @@ def export_dist(self, args):
735736
ctx = self.ctx
736737
dist = dist_from_args(ctx, args)
737738
if dist.needs_build:
738-
info('You asked to export a dist, but there is no dist '
739-
'with suitable recipes available. For now, you must '
740-
' create one first with the create argument.')
741-
exit(1)
739+
raise BuildInterruptingException(
740+
'You asked to export a dist, but there is no dist '
741+
'with suitable recipes available. For now, you must '
742+
' create one first with the create argument.')
742743
if args.symlink:
743744
shprint(sh.ln, '-s', dist.dist_dir, args.output_dir)
744745
else:
@@ -839,9 +840,8 @@ def apk(self, args):
839840
elif args.build_mode == "release":
840841
gradle_task = "assembleRelease"
841842
else:
842-
error("Unknown build mode {} for apk()".format(
843-
args.build_mode))
844-
exit(1)
843+
raise BuildInterruptingException(
844+
"Unknown build mode {} for apk()".format(args.build_mode))
845845
output = shprint(gradlew, gradle_task, _tail=20,
846846
_critical=True, _env=env)
847847

@@ -858,9 +858,9 @@ def apk(self, args):
858858
try:
859859
ant = sh.Command('ant')
860860
except sh.CommandNotFound:
861-
error('Could not find ant binary, please install it '
862-
'and make sure it is in your $PATH.')
863-
exit(1)
861+
raise BuildInterruptingException(
862+
'Could not find ant binary, please install it '
863+
'and make sure it is in your $PATH.')
864864
output = shprint(ant, args.build_mode, _tail=20,
865865
_critical=True, _env=env)
866866
apk_dir = join(dist.dist_dir, "bin")
@@ -894,7 +894,7 @@ def apk(self, args):
894894
apk_file = apks[-1]
895895
break
896896
else:
897-
raise ValueError('Couldn\'t find the built APK')
897+
raise BuildInterruptingException('Couldn\'t find the built APK')
898898

899899
info_main('# Found APK file: {}'.format(apk_file))
900900
if apk_add_version:
@@ -1028,7 +1028,10 @@ def build_status(self, _args):
10281028

10291029

10301030
def main():
1031-
ToolchainCL()
1031+
try:
1032+
ToolchainCL()
1033+
except BuildInterruptingException as exc:
1034+
handle_build_exception(exc)
10321035

10331036

10341037
if __name__ == "__main__":

0 commit comments

Comments
 (0)