Skip to content

Commit c161f08

Browse files
committed
Merge branch 'Issue505-option-to-disable-printing-error-logs-on-failed-job'
2 parents fed61cc + bd9160c commit c161f08

File tree

7 files changed

+101
-18
lines changed

7 files changed

+101
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Added `show_error_logs` argument to `cube.execute_batch()`/`job.start_and_wait()`/... to toggle the automatic printing of error logs on failure ([#505](https://github.com/Open-EO/openeo-python-client/issues/505))
13+
1214
### Changed
1315

1416
### Removed

docs/batch_jobs.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ When using
292292
:py:meth:`job.start_and_wait() <openeo.rest.job.BatchJob.start_and_wait>`
293293
or :py:meth:`cube.execute_batch() <openeo.rest.datacube.DataCube.execute_batch>`
294294
to run a batch job and it fails,
295-
the openEO Python client library will automatically
296-
print the batch job logs and instructions to help with further investigation:
295+
the openEO Python client library will print (by default)
296+
the batch job's error logs and instructions to help with further investigation:
297297
298298
.. code-block:: pycon
299299

openeo/rest/datacube.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2477,6 +2477,7 @@ def execute_batch(
24772477
job_options: Optional[dict] = None,
24782478
validate: Optional[bool] = None,
24792479
auto_add_save_result: bool = True,
2480+
show_error_logs: bool = True,
24802481
# TODO: deprecate `format_options` as keyword arguments
24812482
**format_options,
24822483
) -> BatchJob:
@@ -2494,12 +2495,16 @@ def execute_batch(
24942495
:param validate: Optional toggle to enable/prevent validation of the process graphs before execution
24952496
(overruling the connection's ``auto_validate`` setting).
24962497
:param auto_add_save_result: Automatically add a ``save_result`` node to the process graph if there is none yet.
2498+
:param show_error_logs: whether to automatically print error logs when the batch job failed.
24972499
24982500
.. versionchanged:: 0.32.0
24992501
Added ``auto_add_save_result`` option
25002502
25012503
.. versionadded:: 0.36.0
25022504
Added argument ``additional``.
2505+
2506+
.. versionchanged:: 0.37.0
2507+
Added argument ``show_error_logs``.
25032508
"""
25042509
# TODO: start showing deprecation warnings about these inconsistent argument names
25052510
if "format" in format_options and not out_format:
@@ -2529,7 +2534,10 @@ def execute_batch(
25292534
)
25302535
return job.run_synchronous(
25312536
outputfile=outputfile,
2532-
print=print, max_poll_interval=max_poll_interval, connection_retry_interval=connection_retry_interval
2537+
print=print,
2538+
max_poll_interval=max_poll_interval,
2539+
connection_retry_interval=connection_retry_interval,
2540+
show_error_logs=show_error_logs,
25332541
)
25342542

25352543
def create_job(

openeo/rest/job.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -235,20 +235,43 @@ def logs(
235235
return VisualList("logs", data=entries)
236236

237237
def run_synchronous(
238-
self, outputfile: Union[str, Path, None] = None,
239-
print=print, max_poll_interval=60, connection_retry_interval=30
238+
self,
239+
outputfile: Union[str, Path, None] = None,
240+
print=print,
241+
max_poll_interval=60,
242+
connection_retry_interval=30,
243+
show_error_logs: bool = True,
240244
) -> BatchJob:
241-
"""Start the job, wait for it to finish and download result"""
245+
"""
246+
Start the job, wait for it to finish and download result
247+
248+
:param outputfile: The path of a file to which a result can be written
249+
:param print: print/logging function to show progress/status
250+
:param max_poll_interval: maximum number of seconds to sleep between status polls
251+
:param connection_retry_interval: how long to wait when status poll failed due to connection issue
252+
:param show_error_logs: whether to automatically print error logs when the batch job failed.
253+
254+
.. versionchanged:: 0.37.0
255+
Added argument ``show_error_logs``.
256+
"""
242257
self.start_and_wait(
243-
print=print, max_poll_interval=max_poll_interval, connection_retry_interval=connection_retry_interval
258+
print=print,
259+
max_poll_interval=max_poll_interval,
260+
connection_retry_interval=connection_retry_interval,
261+
show_error_logs=show_error_logs,
244262
)
245263
# TODO #135 support multi file result sets too?
246264
if outputfile is not None:
247265
self.download_result(outputfile)
248266
return self
249267

250268
def start_and_wait(
251-
self, print=print, max_poll_interval: int = 60, connection_retry_interval: int = 30, soft_error_max=10
269+
self,
270+
print=print,
271+
max_poll_interval: int = 60,
272+
connection_retry_interval: int = 30,
273+
soft_error_max=10,
274+
show_error_logs: bool = True,
252275
) -> BatchJob:
253276
"""
254277
Start the batch job, poll its status and wait till it finishes (or fails)
@@ -257,7 +280,10 @@ def start_and_wait(
257280
:param max_poll_interval: maximum number of seconds to sleep between status polls
258281
:param connection_retry_interval: how long to wait when status poll failed due to connection issue
259282
:param soft_error_max: maximum number of soft errors (e.g. temporary connection glitches) to allow
260-
:return:
283+
:param show_error_logs: whether to automatically print error logs when the batch job failed.
284+
285+
.. versionchanged:: 0.37.0
286+
Added argument ``show_error_logs``.
261287
"""
262288
# TODO rename `connection_retry_interval` to something more generic?
263289
start_time = time.time()
@@ -314,13 +340,13 @@ def soft_error(message: str):
314340
poll_interval = min(1.25 * poll_interval, max_poll_interval)
315341

316342
if status != "finished":
317-
# TODO: allow to disable this printing logs (e.g. in non-interactive contexts)?
318343
# TODO: render logs jupyter-aware in a notebook context?
319-
print(f"Your batch job {self.job_id!r} failed. Error logs:")
320-
print(self.logs(level=logging.ERROR))
321-
print(
322-
f"Full logs can be inspected in an openEO (web) editor or with `connection.job({self.job_id!r}).logs()`."
323-
)
344+
if show_error_logs:
345+
print(f"Your batch job {self.job_id!r} failed. Error logs:")
346+
print(self.logs(level=logging.ERROR))
347+
print(
348+
f"Full logs can be inspected in an openEO (web) editor or with `connection.job({self.job_id!r}).logs()`."
349+
)
324350
raise JobFailedException(
325351
f"Batch job {self.job_id!r} didn't finish successfully. Status: {status} (after {elapsed()}).",
326352
job=self,

openeo/rest/mlmodel.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,13 @@ def execute_batch(
7171
connection_retry_interval=30,
7272
additional: Optional[dict] = None,
7373
job_options: Optional[dict] = None,
74+
show_error_logs: bool = True,
7475
) -> BatchJob:
7576
"""
7677
Evaluate the process graph by creating a batch job, and retrieving the results when it is finished.
7778
This method is mostly recommended if the batch job is expected to run in a reasonable amount of time.
7879
79-
For very long running jobs, you probably do not want to keep the client running.
80+
For very long-running jobs, you probably do not want to keep the client running.
8081
8182
:param job_options:
8283
:param outputfile: The path of a file to which a result can be written
@@ -85,9 +86,13 @@ def execute_batch(
8586
:param additional: additional (top-level) properties to set in the request body
8687
:param job_options: dictionary of job options to pass to the backend
8788
(under top-level property "job_options")
89+
:param show_error_logs: whether to automatically print error logs when the batch job failed.
8890
8991
.. versionadded:: 0.36.0
9092
Added argument ``additional``.
93+
94+
.. versionchanged:: 0.37.0
95+
Added argument ``show_error_logs``.
9196
"""
9297
job = self.create_job(
9398
title=title,
@@ -100,7 +105,10 @@ def execute_batch(
100105
return job.run_synchronous(
101106
# TODO #135 support multi file result sets too
102107
outputfile=outputfile,
103-
print=print, max_poll_interval=max_poll_interval, connection_retry_interval=connection_retry_interval
108+
print=print,
109+
max_poll_interval=max_poll_interval,
110+
connection_retry_interval=connection_retry_interval,
111+
show_error_logs=show_error_logs,
104112
)
105113

106114
def create_job(

openeo/rest/vectorcube.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def execute_batch(
259259
job_options: Optional[dict] = None,
260260
validate: Optional[bool] = None,
261261
auto_add_save_result: bool = True,
262+
show_error_logs: bool = True,
262263
# TODO: avoid using kwargs as format options
263264
**format_options,
264265
) -> BatchJob:
@@ -277,6 +278,7 @@ def execute_batch(
277278
:param validate: Optional toggle to enable/prevent validation of the process graphs before execution
278279
(overruling the connection's ``auto_validate`` setting).
279280
:param auto_add_save_result: Automatically add a ``save_result`` node to the process graph if there is none yet.
281+
:param show_error_logs: whether to automatically print error logs when the batch job failed.
280282
281283
.. versionchanged:: 0.21.0
282284
When not specified explicitly, output format is guessed from output file extension.
@@ -286,6 +288,9 @@ def execute_batch(
286288
287289
.. versionadded:: 0.36.0
288290
Added argument ``additional``.
291+
292+
.. versionchanged:: 0.37.0
293+
Added argument ``show_error_logs``.
289294
"""
290295
cube = self
291296
if auto_add_save_result:
@@ -310,7 +315,10 @@ def execute_batch(
310315
return job.run_synchronous(
311316
# TODO #135 support multi file result sets too
312317
outputfile=outputfile,
313-
print=print, max_poll_interval=max_poll_interval, connection_retry_interval=connection_retry_interval
318+
print=print,
319+
max_poll_interval=max_poll_interval,
320+
connection_retry_interval=connection_retry_interval,
321+
show_error_logs=show_error_logs,
314322
)
315323

316324
def create_job(

tests/rest/test_job.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,37 @@ def test_execute_batch_with_error(con100, requests_mock, tmpdir):
151151
]
152152

153153

154+
@pytest.mark.parametrize("show_error_logs", [True, False])
155+
def test_execute_batch_show_error_logs(con100, requests_mock, show_error_logs):
156+
requests_mock.get(API_URL + "/file_formats", json={"output": {"GTiff": {"gis_data_types": ["raster"]}}})
157+
requests_mock.get(API_URL + "/collections/SENTINEL2", json={"foo": "bar"})
158+
requests_mock.post(API_URL + "/jobs", status_code=201, headers={"OpenEO-Identifier": "f00ba5"})
159+
requests_mock.post(API_URL + "/jobs/f00ba5/results", status_code=202)
160+
requests_mock.get(API_URL + "/jobs/f00ba5", json={"status": "error", "progress": 100})
161+
requests_mock.get(
162+
API_URL + "/jobs/f00ba5/logs",
163+
json={"logs": [{"id": "34", "level": "error", "message": "nope"}]},
164+
)
165+
166+
stdout = []
167+
with fake_time(), pytest.raises(JobFailedException):
168+
con100.load_collection("SENTINEL2").execute_batch(
169+
max_poll_interval=0.1, print=stdout.append, show_error_logs=show_error_logs
170+
)
171+
172+
expected = [
173+
"0:00:01 Job 'f00ba5': send 'start'",
174+
"0:00:02 Job 'f00ba5': error (progress 100%)",
175+
]
176+
if show_error_logs:
177+
expected += [
178+
"Your batch job 'f00ba5' failed. Error logs:",
179+
[{"id": "34", "level": "error", "message": "nope"}],
180+
"Full logs can be inspected in an openEO (web) editor or with `connection.job('f00ba5').logs()`.",
181+
]
182+
assert stdout == expected
183+
184+
154185
@pytest.mark.parametrize(["error_response", "expected"], [
155186
(
156187
{"exc": requests.ConnectionError("time out")},

0 commit comments

Comments
 (0)