From b309e895adb39130e9109b558c5cbc20f4e79428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 13:09:12 +0200 Subject: [PATCH 1/9] Add subprocess32 --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 80a06ea..266be9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,4 @@ uncertainties>=2.4.1 unittest2>=0.5.1 montage-wrapper>=0.9.7 requests>=2.0.1 +subprocess32>=3.2.6 From 28fdeacb1073d08ada1cba6eb56158e002d823db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 13:11:42 +0200 Subject: [PATCH 2/9] Use subprocess32 in Python versions < 3.3 --- astrometry.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/astrometry.py b/astrometry.py index 9438f09..f9e14a3 100644 --- a/astrometry.py +++ b/astrometry.py @@ -25,11 +25,19 @@ import optparse import os import shutil -import subprocess import sys import tempfile import warnings +# The 'timeout' argument of subprocess.call() was added in version 3.3. +# In previous versions we need to use 'subprocess32', a backport of the +# subprocess module from Python 3.2/3.3 for use on 2.x. + +if sys.version_info < (3, 3): + import subprocess32 as subprocess +else: + import subprocess + # LEMON modules import customparser import defaults From 02cefc67469c448caa6e5d635e190187ec1a2ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 13:52:48 +0200 Subject: [PATCH 3/9] Add AstrometryNetTimeoutExpired exception --- astrometry.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/astrometry.py b/astrometry.py index f9e14a3..aab594c 100644 --- a/astrometry.py +++ b/astrometry.py @@ -75,6 +75,17 @@ def __init__(self, path): def __str__(self): return "%s: could not solve field" % self.path +class AstrometryNetTimeoutExpired(AstrometryNetUnsolvedField): + """ Raised if the Astrometry.net timeout was reached """ + + def __init__(self, path, timeout): + self.path = path + self.timeout = timeout + + def __str__(self): + msg = "%s: could not solve field in less than %d seconds" + return msg % (self.path, self.timeout) + def astrometry_net(path, ra = None, dec = None, radius = 1, verbosity = 0): """ Do astrometry on a FITS image using Astrometry.net. From 309305f614fa54658e84752273841919e8c561c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 14:18:19 +0200 Subject: [PATCH 4/9] Add 'timeout' argument to astrometry_net() --- astrometry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/astrometry.py b/astrometry.py index aab594c..1fdf6a2 100644 --- a/astrometry.py +++ b/astrometry.py @@ -86,7 +86,8 @@ def __str__(self): msg = "%s: could not solve field in less than %d seconds" return msg % (self.path, self.timeout) -def astrometry_net(path, ra = None, dec = None, radius = 1, verbosity = 0): +def astrometry_net(path, ra = None, dec = None, + radius = 1, verbosity = 0, timeout = None): """ Do astrometry on a FITS image using Astrometry.net. Use a local build of the amazing Astrometry.net software [1] in order to @@ -190,7 +191,7 @@ def astrometry_net(path, ra = None, dec = None, radius = 1, verbosity = 0): args.append('-%s' % ('v' * verbosity)) try: - subprocess.check_call(args) + subprocess.check_call(args, timeout = timeout) # .solved file must exist and contain a binary one with open(solved_file, 'rb') as fd: @@ -204,6 +205,8 @@ def astrometry_net(path, ra = None, dec = None, radius = 1, verbosity = 0): # If .solved file doesn't exist or contain one except (IOError, AstrometryNetUnsolvedField): raise AstrometryNetUnsolvedField(path) + except subprocess.TimeoutExpired: + raise AstrometryNetTimeoutExpired(path, timeout) finally: methods.clean_tmp_files(output_dir) From 19ecbe730d458c2fa941c0a6cc445a7693c783c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 14:20:20 +0200 Subject: [PATCH 5/9] Update docstring of astrometry_net() --- astrometry.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/astrometry.py b/astrometry.py index 1fdf6a2..5fe28eb 100644 --- a/astrometry.py +++ b/astrometry.py @@ -129,10 +129,14 @@ def astrometry_net(path, ra = None, dec = None, Both the right ascension and declination must be given in order for this feature to work. The three arguments must be expressed in degrees. - verbosity - the verbosity level. The higher this value, the 'chattier' Astrometry.net will be. Most of the time, a verbosity other than zero, the default value, is only needed for debugging. + timeout - the maximum number of seconds that Astrometry.net spends on the + image before giving up and raising AstrometryNetTimeoutExpired. + Note that the backend configuration file (astrometry.cfg) puts a + limit on the CPU time that is spent on an image: this can reduce + that value but not increase it. """ From 1fa1fd1f9060cf2f323660b12626a9de6edf9842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 14:29:42 +0200 Subject: [PATCH 6/9] Add --timeout option --- astrometry.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/astrometry.py b/astrometry.py index 5fe28eb..9a34b7a 100644 --- a/astrometry.py +++ b/astrometry.py @@ -234,6 +234,17 @@ def astrometry_net(path, ra = None, dec = None, "pointing, or when they do but it is deemed to be " "entirely unreliable") +parser.add_option('--timeout', action = 'store', type = 'int', + dest = 'timeout', default = 600, + help = "the maximum number of seconds that may be spent " + "attempting to find the astrometric solution of a FITS " + "image. If this time limit is exceeded, we give up and no " + "solution for the image is saved to the output directory. " + "Note, however, that Astrometry.net's backend configuration " + "file (astrometry.cfg) puts a limit on the CPU time that is " + "spent on an image: this option can reduce this value but " + "not increase it. [default: %default]") + parser.add_option('--suffix', action = 'store', type = 'str', dest = 'suffix', default = 'a', help = "string to be appended to output images, before " From cba2a5a46e732c0c6883f9c5216d0ccde2597d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 14:31:05 +0200 Subject: [PATCH 7/9] Add Bash completion for --timeout --- lemon-completion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemon-completion.sh b/lemon-completion.sh index eab8b51..0df3c1a 100644 --- a/lemon-completion.sh +++ b/lemon-completion.sh @@ -67,7 +67,7 @@ _lemon_mosaic() _lemon_astrometry() { local opts - opts="--radius --blind --suffix --verbose --rak --deck" + opts="--radius --blind --timeout --suffix --verbose --rak --deck" if [[ ${cur} == -* ]]; then _match "${opts}" From f54662bf94fcf4848c90d8e52dccc7ea0131dd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 16:26:53 +0200 Subject: [PATCH 8/9] Update call to astrometry_net() --- astrometry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/astrometry.py b/astrometry.py index 9a34b7a..11b6751 100644 --- a/astrometry.py +++ b/astrometry.py @@ -371,7 +371,8 @@ def main(arguments = None): kwargs = dict(ra = ra, dec = dec, radius = options.radius, - verbosity = options.verbose) + verbosity = options.verbose, + timeout = options.timeout) try: output_path = astrometry_net(img.path, **kwargs) From c64d98d16e4952eb8175118e664a58c5eeb90137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Terr=C3=B3n?= Date: Thu, 4 Sep 2014 16:33:11 +0200 Subject: [PATCH 9/9] Use a different message when AstrometryNetTimeoutExpired is emitted --- astrometry.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/astrometry.py b/astrometry.py index 11b6751..2a63f1c 100644 --- a/astrometry.py +++ b/astrometry.py @@ -376,8 +376,16 @@ def main(arguments = None): try: output_path = astrometry_net(img.path, **kwargs) - except AstrometryNetUnsolvedField: - msg = "%s did not solve. Ignored." % img.path + + except AstrometryNetUnsolvedField, e: + + # A subclass of AstrometryNetUnsolvedField + if isinstance(e, AstrometryNetTimeoutExpired): + msg = "%s exceeded the timeout limit. Ignored." + else: + msg = "%s did not solve. Ignored." + + msg %= img.path print style.prefix + msg warnings.warn(msg, RuntimeWarning) continue