5656from openeo .rest ._datacube import (
5757 THIS ,
5858 UDF ,
59- _ensure_save_result ,
6059 _ProcessGraphAbstraction ,
6160 build_child_callback ,
6261)
6362from openeo .rest .graph_building import CollectionProperty
6463from openeo .rest .job import BatchJob , RESTJob
6564from openeo .rest .mlmodel import MlModel
65+ from openeo .rest .result import SaveResult
6666from openeo .rest .service import Service
6767from openeo .rest .udp import RESTUserDefinedProcess
6868from openeo .rest .vectorcube import VectorCube
@@ -2330,22 +2330,46 @@ def atmospheric_correction(self, method: str = None, elevation_model: str = None
23302330 @openeo_process
23312331 def save_result (
23322332 self ,
2333+ # TODO: does it make sense for the client to define a (hard coded) default format here?
23332334 format : str = _DEFAULT_RASTER_FORMAT ,
23342335 options : Optional [dict ] = None ,
2335- ) -> DataCube :
2336+ ) -> SaveResult :
2337+ """
2338+ Materialize the processed data to the given file format.
2339+
2340+ :param format: an output format supported by the backend.
2341+ :param options: file format options
2342+
2343+ .. versionchanged:: 0.39.0
2344+ returns a :py:class:`~openeo.rest.result.SaveResult` instance instead
2345+ of another :py:class:`~openeo.rest.datacube.DataCube` instance.
2346+ """
23362347 if self ._connection :
23372348 formats = set (self ._connection .list_output_formats ().keys ())
23382349 # TODO: map format to correct casing too?
23392350 if format .lower () not in {f .lower () for f in formats }:
23402351 raise ValueError ("Invalid format {f!r}. Should be one of {s}" .format (f = format , s = formats ))
2341- return self .process (
2352+
2353+ pg = self ._build_pgnode (
23422354 process_id = "save_result" ,
23432355 arguments = {
2344- "data" : THIS ,
2356+ "data" : self ,
23452357 "format" : format ,
23462358 # TODO: leave out options if unset?
2347- "options" : options or {}
2348- }
2359+ "options" : options or {},
2360+ },
2361+ )
2362+ return SaveResult (pg , connection = self ._connection )
2363+
2364+ def _auto_save_result (
2365+ self ,
2366+ format : Optional [str ] = None ,
2367+ outputfile : Optional [Union [str , pathlib .Path ]] = None ,
2368+ options : Optional [dict ] = None ,
2369+ ) -> SaveResult :
2370+ return self .save_result (
2371+ format = format or (guess_format (outputfile ) if outputfile else None ) or self ._DEFAULT_RASTER_FORMAT ,
2372+ options = options ,
23492373 )
23502374
23512375 def download (
@@ -2365,12 +2389,12 @@ def download(
23652389 If outputfile is provided, the result is stored on disk locally, otherwise, a bytes object is returned.
23662390 The bytes object can be passed on to a suitable decoder for decoding.
23672391
2368- :param outputfile: Optional, an output file if the result needs to be stored on disk .
2392+ :param outputfile: Optional, output path to download to .
23692393 :param format: Optional, an output format supported by the backend.
23702394 :param options: Optional, file format options
23712395 :param validate: Optional toggle to enable/prevent validation of the process graphs before execution
23722396 (overruling the connection's ``auto_validate`` setting).
2373- :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph if there is none yet .
2397+ :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph.
23742398 :param additional: additional (top-level) properties to set in the request body
23752399 :param job_options: dictionary of job options to pass to the backend
23762400 (under top-level property "job_options")
@@ -2384,18 +2408,12 @@ def download(
23842408 Added arguments ``additional`` and ``job_options``.
23852409 """
23862410 # TODO #278 centralize download/create_job/execute_job logic in DataCube, VectorCube, MlModel, ...
2387- cube = self
23882411 if auto_add_save_result :
2389- cube = _ensure_save_result (
2390- cube = cube ,
2391- format = format ,
2392- options = options ,
2393- weak_format = guess_format (outputfile ) if outputfile else None ,
2394- default_format = self ._DEFAULT_RASTER_FORMAT ,
2395- method = "DataCube.download()" ,
2396- )
2412+ res = self ._auto_save_result (format = format , outputfile = outputfile , options = options )
2413+ else :
2414+ res = self
23972415 return self ._connection .download (
2398- cube .flat_graph (), outputfile , validate = validate , additional = additional , job_options = job_options
2416+ res .flat_graph (), outputfile = outputfile , validate = validate , additional = additional , job_options = job_options
23992417 )
24002418
24012419 def validate (self ) -> List [dict ]:
@@ -2510,19 +2528,35 @@ def execute_batch(
25102528 ** format_options ,
25112529 ) -> BatchJob :
25122530 """
2513- Evaluate the process graph by creating a batch job, and retrieving the results when it is finished.
2514- This method is mostly recommended if the batch job is expected to run in a reasonable amount of time.
2531+ Execute the underlying process graph at the backend in batch job mode:
25152532
2516- For very long-running jobs, you probably do not want to keep the client running.
2533+ - create the job (like :py:meth:`create_job`)
2534+ - start the job (like :py:meth:`BatchJob.start() <openeo.rest.job.BatchJob.start>`)
2535+ - track the job's progress with an active polling loop
2536+ (like :py:meth:`BatchJob.run_synchronous() <openeo.rest.job.BatchJob.run_synchronous>`)
2537+ - optionally (if ``outputfile`` is specified) download the job's results
2538+ when the job finished successfully
25172539
2518- :param outputfile: The path of a file to which a result can be written
2540+ .. note::
2541+ Because of the active polling loop,
2542+ which blocks any further progress of your script or application,
2543+ this :py:meth:`execute_batch` method is mainly recommended
2544+ for batch jobs that are expected to complete
2545+ in a time that is reasonable for your use case.
2546+
2547+ :param outputfile: Optional, output path to download to.
25192548 :param out_format: (optional) File format to use for the job result.
2549+ :param title: job title.
2550+ :param description: job description.
2551+ :param plan: The billing plan to process and charge the job with
2552+ :param budget: Maximum budget to be spent on executing the job.
2553+ Note that some backends do not honor this limit.
25202554 :param additional: additional (top-level) properties to set in the request body
25212555 :param job_options: dictionary of job options to pass to the backend
25222556 (under top-level property "job_options")
25232557 :param validate: Optional toggle to enable/prevent validation of the process graphs before execution
25242558 (overruling the connection's ``auto_validate`` setting).
2525- :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph if there is none yet .
2559+ :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph.
25262560 :param show_error_logs: whether to automatically print error logs when the batch job failed.
25272561 :param log_level: Optional minimum severity level for log entries that the back-end should keep track of.
25282562 One of "error" (highest severity), "warning", "info", and "debug" (lowest severity).
@@ -2546,27 +2580,23 @@ def execute_batch(
25462580 out_format = format_options ["format" ] # align with 'download' call arg name
25472581
25482582 # TODO #278 centralize download/create_job/execute_job logic in DataCube, VectorCube, MlModel, ...
2549- cube = self
25502583 if auto_add_save_result :
2551- cube = _ensure_save_result (
2552- cube = cube ,
2553- format = out_format ,
2554- options = format_options ,
2555- weak_format = guess_format (outputfile ) if outputfile else None ,
2556- default_format = self ._DEFAULT_RASTER_FORMAT ,
2557- method = "DataCube.execute_batch()" ,
2558- )
2584+ res = self ._auto_save_result (format = out_format , outputfile = outputfile , options = format_options )
2585+ create_kwargs = {}
2586+ else :
2587+ res = self
2588+ create_kwargs = {"auto_add_save_result" : False }
25592589
2560- job = cube .create_job (
2590+ job = res .create_job (
25612591 title = title ,
25622592 description = description ,
25632593 plan = plan ,
25642594 budget = budget ,
25652595 additional = additional ,
25662596 job_options = job_options ,
25672597 validate = validate ,
2568- auto_add_save_result = False ,
25692598 log_level = log_level ,
2599+ ** create_kwargs ,
25702600 )
25712601 return job .run_synchronous (
25722602 outputfile = outputfile ,
@@ -2593,25 +2623,27 @@ def create_job(
25932623 ** format_options ,
25942624 ) -> BatchJob :
25952625 """
2596- Sends the datacube's process graph as a batch job to the back-end
2597- and return a :py:class:`~openeo.rest.job.BatchJob` instance.
2626+ Send the underlying process graph to the backend
2627+ to create an openEO batch job
2628+ and return a corresponding :py:class:`~openeo.rest.job.BatchJob` instance.
25982629
2599- Note that the batch job will just be created at the back-end,
2600- it still needs to be started and tracked explicitly.
2601- Use :py:meth:`execute_batch` instead to have the openEO Python client take care of that job management.
2630+ Note that this method only *creates* the openEO batch job at the backend,
2631+ but it does not *start* it.
2632+ Use :py:meth:`execute_batch` instead to let the openEO Python client
2633+ take care of the full job life cycle: create, start and track its progress until completion.
26022634
26032635 :param out_format: output file format.
2604- :param title: job title
2605- :param description: job description
2606- :param plan: The billing plan to process and charge the job with
2636+ :param title: job title.
2637+ :param description: job description.
2638+ :param plan: The billing plan to process and charge the job with.
26072639 :param budget: Maximum budget to be spent on executing the job.
26082640 Note that some backends do not honor this limit.
26092641 :param additional: additional (top-level) properties to set in the request body
26102642 :param job_options: dictionary of job options to pass to the backend
26112643 (under top-level property "job_options")
26122644 :param validate: Optional toggle to enable/prevent validation of the process graphs before execution
26132645 (overruling the connection's ``auto_validate`` setting).
2614- :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph if there is none yet .
2646+ :param auto_add_save_result: Automatically add a ``save_result`` node to the process graph.
26152647 :param log_level: Optional minimum severity level for log entries that the back-end should keep track of.
26162648 One of "error" (highest severity), "warning", "info", and "debug" (lowest severity).
26172649
@@ -2629,17 +2661,13 @@ def create_job(
26292661 # TODO: add option to also automatically start the job?
26302662 # TODO: avoid using all kwargs as format_options
26312663 # TODO #278 centralize download/create_job/execute_job logic in DataCube, VectorCube, MlModel, ...
2632- cube = self
26332664 if auto_add_save_result :
2634- cube = _ensure_save_result (
2635- cube = cube ,
2636- format = out_format ,
2637- options = format_options or None ,
2638- default_format = self ._DEFAULT_RASTER_FORMAT ,
2639- method = "DataCube.create_job()" ,
2640- )
2665+ res = self ._auto_save_result (format = out_format , options = format_options )
2666+ else :
2667+ res = self
2668+
26412669 return self ._connection .create_job (
2642- process_graph = cube .flat_graph (),
2670+ process_graph = res .flat_graph (),
26432671 title = title ,
26442672 description = description ,
26452673 plan = plan ,
0 commit comments