blob: e543a91702d6ab8d645c368a7c6ee742953a0a44 [file] [log] [blame] [view]
<!--- AUTOGENERATED BY `./recipes.py test train` -->
# Repo documentation for [recipe\_engine](https://chromium.googlesource.com/infra/luci/recipes-py.git)
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
* [archive](#recipe_modules-archive) (Python3 ✅)
* [assertions](#recipe_modules-assertions) (Python3 ✅)
* [bcid_reporter](#recipe_modules-bcid_reporter) (Python3 ✅)
* [buildbucket](#recipe_modules-buildbucket) (Python3 ✅) &mdash; API for interacting with the buildbucket service.
* [cas](#recipe_modules-cas) (Python3 ✅) &mdash; API for interacting with cas client.
* [cas_input](#recipe_modules-cas_input) (Python3 ✅) &mdash; Simple API for handling CAS inputs to a recipe.
* [change_verifier](#recipe_modules-change_verifier) (Python3 ✅) &mdash; Recipe API for LUCI Change Verifier.
* [cipd](#recipe_modules-cipd) (Python3 ✅) &mdash; API for interacting with CIPD.
* [commit_position](#recipe_modules-commit_position) (Python3 ✅)
* [context](#recipe_modules-context) (Python3 ✅) &mdash; The context module provides APIs for manipulating a few pieces of 'ambient' data that affect how steps are run.
* [cq](#recipe_modules-cq) (Python3 ✅) &mdash; Recipe API for LUCI CQ, the pre-commit testing system.
* [file](#recipe_modules-file) (Python3 ✅) &mdash; File manipulation (read/write/delete/glob) methods.
* [futures](#recipe_modules-futures) (Python3 ✅) &mdash; Implements in-recipe concurrency via green threads.
* [generator_script](#recipe_modules-generator_script) (Python3 ✅) &mdash; A simplistic method for running steps generated by an external script.
* [golang](#recipe_modules-golang) (Python3 ✅)
* [json](#recipe_modules-json) (Python3 ✅) &mdash; Methods for producing and consuming JSON.
* [led](#recipe_modules-led) (Python3 ✅) &mdash; An interface to call the led tool.
* [legacy_annotation](#recipe_modules-legacy_annotation) (Python3 ✅) &mdash; Legacy Annotation module provides support for running a command emitting legacy @@@annotation@@@ in the new luciexe mode.
* [milo](#recipe_modules-milo) (Python3 ✅) &mdash; API for specifying Milo behavior.
* [nodejs](#recipe_modules-nodejs) (Python3 ✅)
* [path](#recipe_modules-path) (Python3 ✅) &mdash; All functions related to manipulating paths in recipes.
* [platform](#recipe_modules-platform) (Python3 ✅) &mdash; Mockable system platform identity functions.
* [properties](#recipe_modules-properties) (Python3 ✅) &mdash; Provides access to the recipes input properties.
* [proto](#recipe_modules-proto) (Python3 ✅) &mdash; Methods for producing and consuming protobuf data to/from steps and the filesystem.
* [python](#recipe_modules-python) (Python3 ✅) &mdash; Provides methods for running python scripts correctly.
* [random](#recipe_modules-random) (Python3 ✅) &mdash; Allows randomness in recipes.
* [raw_io](#recipe_modules-raw_io) (Python3 ✅) &mdash; Provides objects for reading and writing raw data to and from steps.
* [resultdb](#recipe_modules-resultdb) (Python3 ✅) &mdash; API for interacting with the ResultDB service.
* [runtime](#recipe_modules-runtime) (Python3 ✅)
* [scheduler](#recipe_modules-scheduler) (Python3 ✅) &mdash; API for interacting with the LUCI Scheduler service.
* [service_account](#recipe_modules-service_account) (Python3 ✅) &mdash; API for getting OAuth2 access tokens for LUCI tasks or private keys.
* [step](#recipe_modules-step) (Python3 ✅) &mdash; Step is the primary API for running steps (external programs, etc.
* [swarming](#recipe_modules-swarming) (Python3 ✅)
* [time](#recipe_modules-time) (Python3 ✅) &mdash; Allows mockable access to the current time.
* [tricium](#recipe_modules-tricium) (Python3 ✅) &mdash; API for Tricium analyzers to use.
* [url](#recipe_modules-url) (Python3 ✅) &mdash; Methods for interacting with HTTP(s) URLs.
* [uuid](#recipe_modules-uuid) (Python3 ✅) &mdash; Allows test-repeatable access to a random UUID.
* [version](#recipe_modules-version) (Python3 ✅) &mdash; Thin API for parsing semver strings into comparable object.
* [warning](#recipe_modules-warning) (Python3 ✅) &mdash; Allows recipe modules to issue warnings in simulation test.
**[Recipes](#Recipes)**
* [archive:examples/full](#recipes-archive_examples_full) (Python3 ✅)
* [assertions:tests/assert-raises](#recipes-assertions_tests_assert-raises) (Python3 ✅)
* [assertions:tests/assert_count_equal](#recipes-assertions_tests_assert_count_equal) (Python3 ✅)
* [assertions:tests/assertions](#recipes-assertions_tests_assertions) (Python3 ✅)
* [assertions:tests/attribute_error](#recipes-assertions_tests_attribute_error) (Python3 ✅)
* [assertions:tests/long_message](#recipes-assertions_tests_long_message) (Python3 ✅)
* [assertions:tests/max_diff](#recipes-assertions_tests_max_diff) (Python3 ✅)
* [bcid_reporter:examples/usage](#recipes-bcid_reporter_examples_usage) (Python3 ✅)
* [buildbucket:examples/full](#recipes-buildbucket_examples_full) (Python3 ✅) &mdash; This file is a recipe demonstrating the buildbucket recipe module.
* [buildbucket:run/multi](#recipes-buildbucket_run_multi) (Python3 ✅) &mdash; Launches multiple builds at the same revision.
* [buildbucket:tests/add_tags](#recipes-buildbucket_tests_add_tags) (Python3 ✅)
* [buildbucket:tests/build](#recipes-buildbucket_tests_build) (Python3 ✅)
* [buildbucket:tests/cancel](#recipes-buildbucket_tests_cancel) (Python3 ✅)
* [buildbucket:tests/collect](#recipes-buildbucket_tests_collect) (Python3 ✅)
* [buildbucket:tests/output_commit](#recipes-buildbucket_tests_output_commit) (Python3 ✅) &mdash; This recipe tests the buildbucket.
* [buildbucket:tests/schedule](#recipes-buildbucket_tests_schedule) (Python3 ✅)
* [buildbucket:tests/search](#recipes-buildbucket_tests_search) (Python3 ✅)
* [cas:examples/full](#recipes-cas_examples_full) (Python3 ✅)
* [cas_input:examples/full](#recipes-cas_input_examples_full) (Python3 ✅)
* [change_verifier:tests/search](#recipes-change_verifier_tests_search) (Python3 ✅)
* [cipd:examples/full](#recipes-cipd_examples_full) (Python3 ✅)
* [commit_position:examples/full](#recipes-commit_position_examples_full) (Python3 ✅)
* [context:examples/full](#recipes-context_examples_full) (Python3 ✅)
* [context:tests/cwd](#recipes-context_tests_cwd) (Python3 ✅)
* [context:tests/env](#recipes-context_tests_env) (Python3 ✅)
* [context:tests/greenlet](#recipes-context_tests_greenlet) (Python3 ✅)
* [context:tests/infra_step](#recipes-context_tests_infra_step) (Python3 ✅)
* [context:tests/luci_context](#recipes-context_tests_luci_context) (Python3 ✅)
* [cq:examples/ordered_cls](#recipes-cq_examples_ordered_cls) (Python3 ✅)
* [cq:examples/trigger_child_builds](#recipes-cq_examples_trigger_child_builds) (Python3 ✅)
* [cq:tests/cl_group_key](#recipes-cq_tests_cl_group_key) (Python3 ✅)
* [cq:tests/do_not_retry](#recipes-cq_tests_do_not_retry) (Python3 ✅)
* [cq:tests/experimental](#recipes-cq_tests_experimental) (Python3 ✅)
* [cq:tests/inactive](#recipes-cq_tests_inactive) (Python3 ✅)
* [cq:tests/mode_of_run](#recipes-cq_tests_mode_of_run) (Python3 ✅)
* [cq:tests/reuse](#recipes-cq_tests_reuse) (Python3 ✅)
* [cq:tests/triggered_build_ids](#recipes-cq_tests_triggered_build_ids) (Python3 ✅)
* [engine_tests/bad_subprocess](#recipes-engine_tests_bad_subprocess) (Python3 ✅) &mdash; Tests that daemons that hang on to STDOUT can't cause the engine to hang.
* [engine_tests/comprehensive_ui](#recipes-engine_tests_comprehensive_ui) (Python3 ✅) &mdash; A fast-running recipe which comprehensively covers all StepPresentation features available in the recipe engine.
* [engine_tests/config_operations](#recipes-engine_tests_config_operations) (Python3 ✅) &mdash; Tests that recipes can modify configuration options in various ways.
* [engine_tests/early_termination](#recipes-engine_tests_early_termination) (Python3 ✅) &mdash; Simple recipe which runs a bunch of subprocesses which react to early termination in different ways.
* [engine_tests/expect_exception](#recipes-engine_tests_expect_exception) (Python3 ✅) &mdash; Tests that step_data can accept multiple specs at once.
* [engine_tests/failure_results](#recipes-engine_tests_failure_results) (Python3 ✅) &mdash; Tests that run_steps is handling recipe failures correctly.
* [engine_tests/functools_partial](#recipes-engine_tests_functools_partial) (Python3 ✅) &mdash; Engine shouldn't explode when step_test_data gets functools.
* [engine_tests/incorrect_recipe_result](#recipes-engine_tests_incorrect_recipe_result) (Python3 ✅) &mdash; Tests that engine.
* [engine_tests/long_sleep](#recipes-engine_tests_long_sleep) (Python3 ✅) &mdash; Simple recipe which sleeps in a subprocess forever to facilitate early termination tests.
* [engine_tests/missing_start_dir](#recipes-engine_tests_missing_start_dir) (Python3 ✅) &mdash; Tests that deleting the current working directory doesn't immediately fail.
* [engine_tests/module_injection_site](#recipes-engine_tests_module_injection_site) (Python3 ✅) &mdash; This test serves to demonstrate that the ModuleInjectionSite object on recipe modules (i.
* [engine_tests/multi_test_data](#recipes-engine_tests_multi_test_data) (Python3 ✅) &mdash; Tests that step_data can accept multiple specs at once.
* [engine_tests/multiple_placeholders](#recipes-engine_tests_multiple_placeholders) (Python3 ✅) &mdash; Tests error checking around multiple placeholders in a single step.
* [engine_tests/nonexistent_command](#recipes-engine_tests_nonexistent_command) (Python3 ✅)
* [engine_tests/placeholder_exception](#recipes-engine_tests_placeholder_exception) (Python3 ✅) &mdash; Tests that placeholders can't wreck the world by exhausting the step stack.
* [engine_tests/proto_output_properties](#recipes-engine_tests_proto_output_properties) (Python3 ✅) &mdash; Tests that output properties can be a proto message.
* [engine_tests/proto_properties](#recipes-engine_tests_proto_properties) (Python3 ✅)
* [engine_tests/recipe_paths](#recipes-engine_tests_recipe_paths) (Python3 ✅) &mdash; Tests that recipes have access to names, resources and their repo.
* [engine_tests/sort_properties](#recipes-engine_tests_sort_properties) (Python3 ✅) &mdash; Tests that step presentation properties can be ordered.
* [engine_tests/undeclared_method](#recipes-engine_tests_undeclared_method) (Python3 ✅)
* [engine_tests/unicode](#recipes-engine_tests_unicode) (Python3 ✅)
* [engine_tests/whitelist_steps](#recipes-engine_tests_whitelist_steps) (Python3 ✅) &mdash; Tests that step_data can accept multiple specs at once.
* [file:examples/compute_hash](#recipes-file_examples_compute_hash) (Python3 ✅)
* [file:examples/copy](#recipes-file_examples_copy) (Python3 ✅)
* [file:examples/copytree](#recipes-file_examples_copytree) (Python3 ✅)
* [file:examples/error](#recipes-file_examples_error) (Python3 ✅)
* [file:examples/file_hash](#recipes-file_examples_file_hash) (Python3 ✅)
* [file:examples/flatten_single_directories](#recipes-file_examples_flatten_single_directories) (Python3 ✅)
* [file:examples/glob](#recipes-file_examples_glob) (Python3 ✅)
* [file:examples/handle_json_file](#recipes-file_examples_handle_json_file) (Python3 ✅)
* [file:examples/listdir](#recipes-file_examples_listdir) (Python3 ✅)
* [file:examples/raw_copy](#recipes-file_examples_raw_copy) (Python3 ✅)
* [file:examples/read_write_proto](#recipes-file_examples_read_write_proto) (Python3 ✅)
* [file:examples/symlink](#recipes-file_examples_symlink) (Python3 ✅)
* [file:examples/truncate](#recipes-file_examples_truncate) (Python3 ✅)
* [futures:examples/background_helper](#recipes-futures_examples_background_helper) (Python3 ✅)
* [futures:examples/extreme_namespaces](#recipes-futures_examples_extreme_namespaces) (Python3 ✅)
* [futures:examples/fan_out_in](#recipes-futures_examples_fan_out_in) (Python3 ✅)
* [futures:examples/lazy_fan_out_in](#recipes-futures_examples_lazy_fan_out_in) (Python3 ✅)
* [futures:examples/lazy_fan_out_in_early_abort](#recipes-futures_examples_lazy_fan_out_in_early_abort) (Python3 ✅)
* [futures:examples/lottasteps](#recipes-futures_examples_lottasteps) (Python3 ✅) &mdash; This tests the engine's ability to handle many simultaneously-started steps.
* [futures:examples/metadata](#recipes-futures_examples_metadata) (Python3 ✅) &mdash; This tests metadata features of the Future object.
* [futures:examples/result](#recipes-futures_examples_result) (Python3 ✅)
* [futures:examples/semaphore](#recipes-futures_examples_semaphore) (Python3 ✅)
* [generator_script:examples/full](#recipes-generator_script_examples_full) (Python3 ✅)
* [golang:examples/full](#recipes-golang_examples_full) (Python3 ✅)
* [json:examples/full](#recipes-json_examples_full) (Python3 ✅)
* [json:tests/add_json_log](#recipes-json_tests_add_json_log) (Python3 ✅)
* [led:tests/full](#recipes-led_tests_full) (Python3 ✅)
* [led:tests/no_exist](#recipes-led_tests_no_exist) (Python3 ✅)
* [led:tests/trigger_build](#recipes-led_tests_trigger_build) (Python3 ✅)
* [legacy_annotation:examples/full](#recipes-legacy_annotation_examples_full) (Python3 ✅)
* [milo:examples/full](#recipes-milo_examples_full) (Python3 ✅)
* [nodejs:examples/full](#recipes-nodejs_examples_full) (Python3 ✅)
* [path:examples/full](#recipes-path_examples_full) (Python3 ✅)
* [placeholder](#recipes-placeholder) (Python3 ✅)
* [platform:examples/full](#recipes-platform_examples_full) (Python3 ✅)
* [properties:examples/full](#recipes-properties_examples_full) (Python3 ✅)
* [proto:tests/encode_decode](#recipes-proto_tests_encode_decode) (Python3 ✅)
* [proto:tests/placeholders](#recipes-proto_tests_placeholders) (Python3 ✅)
* [random:tests/full](#recipes-random_tests_full) (Python3 ✅)
* [raw_io:examples/full](#recipes-raw_io_examples_full) (Python3 ✅)
* [raw_io:tests/output_mismatch](#recipes-raw_io_tests_output_mismatch) (Python3 ✅)
* [resultdb:examples/exonerate](#recipes-resultdb_examples_exonerate) (Python3 ✅)
* [resultdb:examples/get_included_invocations](#recipes-resultdb_examples_get_included_invocations) (Python3 ✅)
* [resultdb:examples/include](#recipes-resultdb_examples_include) (Python3 ✅)
* [resultdb:examples/query](#recipes-resultdb_examples_query) (Python3 ✅)
* [resultdb:examples/query_test_result_statistics](#recipes-resultdb_examples_query_test_result_statistics) (Python3 ✅)
* [resultdb:examples/result_history](#recipes-resultdb_examples_result_history) (Python3 ✅)
* [resultdb:examples/resultsink](#recipes-resultdb_examples_resultsink) (Python3 ✅)
* [resultdb:examples/test_presentation](#recipes-resultdb_examples_test_presentation) (Python3 ✅)
* [resultdb:examples/test_presentation_default](#recipes-resultdb_examples_test_presentation_default) (Python3 ✅)
* [resultdb:examples/upload_invocation_artifacts](#recipes-resultdb_examples_upload_invocation_artifacts) (Python3 ✅)
* [runtime:tests/full](#recipes-runtime_tests_full) (Python3 ✅)
* [scheduler:examples/emit_triggers](#recipes-scheduler_examples_emit_triggers) (Python3 ✅) &mdash; This file is a recipe demonstrating emitting triggers to LUCI Scheduler.
* [scheduler:examples/info](#recipes-scheduler_examples_info) (Python3 ✅) &mdash; This file is a recipe demonstrating reading/mocking scheduler host.
* [scheduler:examples/triggers](#recipes-scheduler_examples_triggers) (Python3 ✅) &mdash; This file is a recipe demonstrating reading triggers of the current build.
* [service_account:examples/full](#recipes-service_account_examples_full) (Python3 ✅)
* [step:examples/full](#recipes-step_examples_full) (Python3 ✅)
* [step:tests/active_result](#recipes-step_tests_active_result) (Python3 ✅)
* [step:tests/defer](#recipes-step_tests_defer) (Python3 ✅)
* [step:tests/empty](#recipes-step_tests_empty) (Python3 ✅)
* [step:tests/inject_paths](#recipes-step_tests_inject_paths) (Python3 ✅)
* [step:tests/nested](#recipes-step_tests_nested) (Python3 ✅)
* [step:tests/raise_on_failure](#recipes-step_tests_raise_on_failure) (Python3 ✅)
* [step:tests/stdio](#recipes-step_tests_stdio) (Python3 ✅)
* [step:tests/step_call_args](#recipes-step_tests_step_call_args) (Python3 ✅)
* [step:tests/step_cost](#recipes-step_tests_step_cost) (Python3 ✅)
* [step:tests/sub_build](#recipes-step_tests_sub_build) (Python3 ✅)
* [step:tests/timeout](#recipes-step_tests_timeout) (Python3 ✅)
* [swarming:examples/full](#recipes-swarming_examples_full) (Python3 ✅)
* [swarming:examples/this_task](#recipes-swarming_examples_this_task) (Python3 ✅)
* [swarming:tests/copy](#recipes-swarming_tests_copy) (Python3 ✅)
* [swarming:tests/realms](#recipes-swarming_tests_realms) (Python3 ✅)
* [swarming:tests/task_request_from_jsonish](#recipes-swarming_tests_task_request_from_jsonish) (Python3 ✅)
* [time:examples/full](#recipes-time_examples_full) (Python3 ✅)
* [tricium:examples/add_comment](#recipes-tricium_examples_add_comment) (Python3 ✅)
* [tricium:examples/wrapper](#recipes-tricium_examples_wrapper) (Python3 ✅) &mdash; An example of a recipe wrapping legacy analyzers.
* [tricium:tests/add_comment_validation](#recipes-tricium_tests_add_comment_validation) (Python3 ✅)
* [tricium:tests/enforce_comments_num_limit](#recipes-tricium_tests_enforce_comments_num_limit) (Python3 ✅)
* [url:examples/full](#recipes-url_examples_full) (Python3 ✅)
* [url:tests/join](#recipes-url_tests_join) (Python3 ✅)
* [url:tests/validate_url](#recipes-url_tests_validate_url) (Python3 ✅)
* [uuid:examples/full](#recipes-uuid_examples_full) (Python3 ✅)
* [version:examples/full](#recipes-version_examples_full) (Python3 ✅)
* [warning:tests/fakes](#recipes-warning_tests_fakes) (Python3 ✅) &mdash; This is a fake recipe to trick the simulation and make it believes that this module has tests.
## Recipe Modules
### *recipe_modules* / [archive](/recipe_modules/archive)
[DEPS](/recipe_modules/archive/__init__.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [ArchiveApi](/recipe_modules/archive/api.py#8)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Provides steps to manipulate archive files (tar, zip, etc.).
&mdash; **def [extract](/recipe_modules/archive/api.py#42)(self, step_name, archive_file, output, mode='safe', include_files=(), archive_type=None):**
Step to uncompress |archive_file| into |output| directory.
Archive will be unpacked to |output| so that root of an archive is in
|output|, i.e. archive.tar/file.txt will become |output|/file.txt.
Step will FAIL if |output| already exists.
Args:
* step_name (str): display name of a step.
* archive_file (Path): path to an archive file to uncompress, MUST exist.
* output (Path): path to a directory to unpack to. The output directory
MAY exist, in which case the extract will unpack on-top-of the existing
files. It's an error for one of the extracted files to overlap with an
already-present file, however.
* mode (str): Must be either 'safe' or 'unsafe'. In safe mode, if the
archive attempts to extract files which would escape the extraction
`output` location, the extraction will fail (raise StepException)
which contains a member `StepException.archive_skipped_files` (all
other files will be extracted normally). If 'unsafe', then tarfiles
containing paths escaping `output` will be extracted as-is.
* include_files (List[str]) - A list of globs matching files within the
archive. Any files not matching any of these globs will be skipped.
If omitted, all files are extracted (the default). Globs are matched
with the `fnmatch` module. If a file "filename" in the archive exists,
include_files with "file*" will match it. All paths for the matcher
are converted to posix style (forward slash).
* archive_type (str): archive_file's archive type ("zip" or "tar"). This
allows overriding the default detected type (based on file extension).
&mdash; **def [package](/recipe_modules/archive/api.py#13)(self, root):**
Returns Package object that can be used to compress a set of files.
Usage:
# Archive root/file and root/directory/**
(api.archive.package(root).
with_file(root.join('file')).
with_dir(root.join('directory')).
archive('archive step', output, 'tbz'))
# Archive root/**
zip_path = (
api.archive.package(root).
archive('archive step', api.path['start_dir'].join('output.zip'))
)
Args:
* root: a directory that would become root of a package, all files added
to an archive must be Paths which are under this directory. If no
files or directories are added with 'with_file' or 'with_dir', the
entire root directory is packaged.
Returns:
Package object.
### *recipe_modules* / [assertions](/recipe_modules/assertions)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [AssertionsApi](/recipe_modules/assertions/api.py#55)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Provides access to the assertion methods of the python unittest module.
Asserting non-step aspects of code (return values, non-step side effects) is
expressed more naturally by making assertions within the RunSteps function of
the test recipe. This api provides access to the assertion methods of
unittest.TestCase to be used within test recipes.
All non-deprecated assertion methods of unittest.TestCase can be used.
An enhancement to the assertion methods is that if a custom msg is used,
values for the non-msg arguments can be substituted into the message using
named substitution with the format method of strings.
e.g. self.AssertEqual(0, 1, '{first} should be {second}') will raise an
AssertionError with the message: '0 should be 1'.
The attributes longMessage and maxDiff are supported and have the same
behavior as the unittest module.
Example (.../recipe_modules/my_module/tests/foo.py):
```
DEPS = [
'my_module',
'recipe_engine/assertions',
'recipe_engine/properties',
'recipe_engine/runtime',
]
def RunSteps(api):
'''Behavior of foo depends on whether build is experimental'''
value = api.my_module.foo()
expected_value = api.properties.get('expected_value')
api.assertions.assertEqual(value, expected_value)
def GenTests(api):
yield (
api.test('basic')
+ api.properties(expected_value='normal value')
)
yield (
api.test('experimental')
+ api.properties(expected_value='experimental value')
+ api.runtime(is_experimental=True)
)
```
### *recipe_modules* / [bcid\_reporter](/recipe_modules/bcid_reporter)
[DEPS](/recipe_modules/bcid_reporter/__init__.py#7): [cipd](#recipe_modules-cipd), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [BcidReporterApi](/recipe_modules/bcid_reporter/api.py#13)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
API for interacting with Provenance server using the broker tool.
&emsp; **@property**<br>&mdash; **def [bcid\_reporter\_path](/recipe_modules/bcid_reporter/api.py#25)(self):**
Returns the path to the broker binary.
When the property is accessed the first time, the latest stable, released
broker will be installed using cipd.
&mdash; **def [report\_cipd](/recipe_modules/bcid_reporter/api.py#72)(self, digest, pkg, iid, server_url=None):**
Reports cipd digest to local local provenance server.
This is used to report produced artifacts hash and metadata to server, it is
used to generate provenance.
Args:
* digest (str) - The hash of the artifact.
* pkg (str) - Name of the cipd package built.
* iid (str) - Instance ID of the package.
* server_url (Optional[str]) - URL for the local proveance server, the
broker tool will use default if not specified.
&mdash; **def [report\_gcs](/recipe_modules/bcid_reporter/api.py#101)(self, digest, guri, server_url=None):**
Reports cipd digest to local local provenance server.
This is used to report produced artifacts hash and metadata to proveance, it
is used to generate provenance.
Args:
* digest (str) - The hash of the artifact.
* guri (str) - Name of the GCS artifact built. This is the unique GCS URI,
e.g. gs://bucket/path/to/binary.
* server_url (Optional[str]) - URL for the local proveance server, the
broker tool will use default if not specified.
&mdash; **def [report\_stage](/recipe_modules/bcid_reporter/api.py#41)(self, stage, server_url=None):**
Reports task stage to local provenance server.
Args:
* stage (str) - The stage at which task is executing currently, e.g.
"start". Concept of task stage is native to Provenance service, this is
a way of self-reporting phase of a task's lifecycle. This information is
used in conjunction with process-inspected data to make security policy
decisions.
Valid stages: (start, fetch, compile, upload, upload-complete, test).
* server_url (Optional[str]) - URL for the local proveance server, the
broker tool will use default if not specified.
### *recipe_modules* / [buildbucket](/recipe_modules/buildbucket)
[DEPS](/recipe_modules/buildbucket/__init__.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [raw\_io](#recipe_modules-raw_io), [resultdb](#recipe_modules-resultdb), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step), [uuid](#recipe_modules-uuid)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for interacting with the buildbucket service.
Requires `buildbucket` command in `$PATH`:
https://godoc.org/go.chromium.org/luci/buildbucket/client/cmd/buildbucket
`url_title_fn` parameter used in this module is a function that accepts a
`build_pb2.Build` and returns a link title.
If it returns `None`, the link is not reported. Default link title is build id.
#### **class [BuildbucketApi](/recipe_modules/buildbucket/api.py#32)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for interacting with buildbucket.
&mdash; **def [add\_tags\_to\_current\_build](/recipe_modules/buildbucket/api.py#244)(self, tags):**
Adds arbitrary tags during the runtime of a build.
Args:
* tags(list of common_pb2.StringPair): tags to add. May contain duplicates.
Empty tag values won't remove existing tags with matching keys, since tags
can only be added.
&emsp; **@property**<br>&mdash; **def [bucket\_v1](/recipe_modules/buildbucket/api.py#958)(self):**
Returns bucket name in v1 format.
Mostly useful for scheduling new builds using V1 API.
&emsp; **@property**<br>&mdash; **def [build](/recipe_modules/buildbucket/api.py#131)(self):**
Returns current build as a `buildbucket.v2.Build` protobuf message.
For value format, see `Build` message in
[build.proto](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto).
DO NOT MODIFY the returned value.
Do not implement conditional logic on returned tags; they are for indexing.
Use returned `build.input` instead.
Pure Buildbot support: to simplify transition to buildbucket, returns a
message even if the current build is not a buildbucket build. Provides as
much information as possible. Some fields may be left empty, violating
the rules described in the .proto files.
If the current build is not a buildbucket build, returned `build.id` is 0.
&emsp; **@property**<br>&mdash; **def [build\_id](/recipe_modules/buildbucket/api.py#969)(self):**
*** note
**DEPRECATED**: use build.id instead.
***
&emsp; **@property**<br>&mdash; **def [build\_input](/recipe_modules/buildbucket/api.py#974)(self):**
*** note
**DEPRECATED**: use build.input instead.
***
&mdash; **def [build\_url](/recipe_modules/buildbucket/api.py#168)(self, host=None, build_id=None):**
Returns url to a build. Defaults to current build.
&emsp; **@property**<br>&mdash; **def [builder\_cache\_path](/recipe_modules/buildbucket/api.py#269)(self):**
Path to the builder cache directory.
Such directory can be used to cache builder-specific data.
It remains on the bot from build to build.
See "Builder cache" in
https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/project_config.proto
&emsp; **@property**<br>&mdash; **def [builder\_id](/recipe_modules/buildbucket/api.py#979)(self):**
*** note
**DEPRECATED**: Use build.builder instead.
***
&emsp; **@property**<br>&mdash; **def [builder\_name](/recipe_modules/buildbucket/api.py#150)(self):**
Returns builder name. Shortcut for `.build.builder.builder`.
&emsp; **@property**<br>&mdash; **def [builder\_realm](/recipe_modules/buildbucket/api.py#155)(self):**
Returns the LUCI realm name of the current build.
Raises `InfraFailure` if the build proto doesn't have `project` or `bucket`
set. This can happen in tests that don't properly mock build proto.
&mdash; **def [cancel\_build](/recipe_modules/buildbucket/api.py#687)(self, build_id, reason=' ', step_name=None):**
Cancel the build associated with the provided build id.
Args:
* `build_id` (int|str): a buildbucket build ID.
It should be either an integer(e.g. 123456789 or '123456789')
or the numeric value in string format.
* `reason` (str): reason for canceling the given build.
Can't be None or Empty. Markdown is supported.
Returns:
None if build is successfully canceled. Otherwise, an InfraFailure will
be raised
&mdash; **def [collect\_build](/recipe_modules/buildbucket/api.py#789)(self, build_id, \*\*kwargs):**
Shorthand for `collect_builds` below, but for a single build only.
Args:
* build_id: Integer ID of the build to wait for.
Returns:
[Build](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto).
for the ended build.
&mdash; **def [collect\_builds](/recipe_modules/buildbucket/api.py#802)(self, build_ids, interval=None, timeout=None, step_name=None, raise_if_unsuccessful=False, url_title_fn=None, mirror_status=False, fields=DEFAULT_FIELDS):**
Waits for a set of builds to end and returns their details.
Args:
* `build_ids`: List of build IDs to wait for.
* `interval`: Delay (in secs) between requests while waiting for build to
end. Defaults to 1m.
* `timeout`: Maximum time to wait for builds to end. Defaults to 1h.
* `step_name`: Custom name for the generated step.
* `raise_if_unsuccessful`: if any build being collected did not succeed,
raise an exception.
* `url_title_fn`: generates build URL title. See module docstring.
* `mirror_status`: mark the step as failed/infra-failed if any of the builds
did not succeed. Ignored if raise_if_unsuccessful is True.
* `fields`: a list of fields to include in the response, names relative
to `build_pb2.Build` (e.g. ["tags", "infra.swarming"]).
Returns:
A map from integer build IDs to the corresponding
[Build](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto)
for all specified builds.
&mdash; **def [get](/recipe_modules/buildbucket/api.py#768)(self, build_id, url_title_fn=None, step_name=None, fields=DEFAULT_FIELDS):**
Gets a build.
Args:
* `build_id`: a buildbucket build ID.
* `url_title_fn`: generates build URL title. See module docstring.
* `step_name`: name for this step.
* `fields`: a list of fields to include in the response, names relative
to `build_pb2.Build` (e.g. ["tags", "infra.swarming"]).
Returns:
A build_pb2.Build.
&mdash; **def [get\_multi](/recipe_modules/buildbucket/api.py#725)(self, build_ids, url_title_fn=None, step_name=None, fields=DEFAULT_FIELDS):**
Gets multiple builds.
Args:
* `build_ids`: a list of build IDs.
* `url_title_fn`: generates build URL title. See module docstring.
* `step_name`: name for this step.
* `fields`: a list of fields to include in the response, names relative
to `build_pb2.Build` (e.g. ["tags", "infra.swarming"]).
Returns:
A dict {build_id: build_pb2.Build}.
&emsp; **@property**<br>&mdash; **def [gitiles\_commit](/recipe_modules/buildbucket/api.py#173)(self):**
Returns input gitiles commit. Shortcut for `.build.input.gitiles_commit`.
For value format, see
[`GitilesCommit` message](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto).
Never returns None, but sub-fields may be empty.
&mdash; **def [hide\_current\_build\_in\_gerrit](/recipe_modules/buildbucket/api.py#265)(self):**
Hides the build in UI
&emsp; **@host.setter**<br>&mdash; **def [host](/recipe_modules/buildbucket/api.py#99)(self, value):**
&mdash; **def [is\_critical](/recipe_modules/buildbucket/api.py#184)(self, build=None):**
Returns True if the build is critical. Build defaults to the current one.
&mdash; **def [run](/recipe_modules/buildbucket/api.py#290)(self, schedule_build_requests, collect_interval=None, timeout=None, url_title_fn=None, step_name=None, raise_if_unsuccessful=False):**
Runs builds and returns results.
A shortcut for schedule() and collect_builds().
See their docstrings.
Returns:
A list of completed
[Builds](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto)
in the same order as schedule_build_requests.
&mdash; **def [schedule](/recipe_modules/buildbucket/api.py#516)(self, schedule_build_requests, url_title_fn=None, step_name=None, include_sub_invs=True):**
Schedules a batch of builds.
Example:
```python
req = api.buildbucket.schedule_request(builder='linux')
api.buildbucket.schedule([req])
```
Hint: when scheduling builds for CQ, let CQ know about them:
```python
api.cq.record_triggered_builds(*api.buildbucket.schedule([req1, req2]))
```
Args:
* schedule_build_requests: a list of `buildbucket.v2.ScheduleBuildRequest`
protobuf messages. Create one by calling `schedule_request` method.
* url_title_fn: generates a build URL title. See module docstring.
* step_name: name for this step.
* include_sub_invs: flag for including the scheduled builds' ResultDB
invocations into the current build's invocation. Default is True.
Returns:
A list of
[`Build`](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto)
messages in the same order as requests.
Raises:
`InfraFailure` if any of the requests fail.
&mdash; **def [schedule\_request](/recipe_modules/buildbucket/api.py#318)(self, builder, project=INHERIT, bucket=INHERIT, properties=None, experimental=INHERIT, experiments=None, gitiles_commit=INHERIT, gerrit_changes=INHERIT, tags=None, inherit_buildsets=True, swarming_parent_run_id=None, dimensions=None, priority=INHERIT, critical=INHERIT, exe_cipd_version=None, fields=DEFAULT_FIELDS, can_outlive_parent=None):**
Creates a new `ScheduleBuildRequest` message with reasonable defaults.
This is a convenient function to create a `ScheduleBuildRequest` message.
Among args, messages can be passed as dicts of the same structure.
Example:
request = api.buildbucket.schedule_request(
builder='linux',
tags=api.buildbucket.tags(a='b'),
)
build = api.buildbucket.schedule([request])[0]
Args:
* builder (str): name of the destination builder.
* project (str|INHERIT): project containing the destinaiton builder.
Defaults to the project of the current build.
* bucket (str|INHERIT): bucket containing the destination builder.
Defaults to the bucket of the current build.
* properties (dict): input properties for the new build.
* experimental (common_pb2.Trinary|INHERIT): whether the build is allowed
to affect prod.
Defaults to the value of the current build.
Read more about
[`experimental` field](https://cs.chromium.org/chromium/infra/go/src/go.chromium.org/luci/buildbucket/proto/build.proto?q="bool experimental").
* experiments (Dict[str, bool]|None): enabled and disabled experiments
for the new build. Overrides the result computed from experiments defined
in builder config.
* gitiles_commit (common_pb2.GitilesCommit|INHERIT): input commit.
Defaults to the input commit of the current build.
Read more about
[`gitiles_commit`](https://cs.chromium.org/chromium/infra/go/src/go.chromium.org/luci/buildbucket/proto/build.proto?q=Input.gitiles_commit).
* gerrit_changes (list of common_pb2.GerritChange|INHERIT): list of input
CLs.
Defaults to gerrit changes of the current build.
Read more about
[`gerrit_changes`](https://cs.chromium.org/chromium/infra/go/src/go.chromium.org/luci/buildbucket/proto/build.proto?q=Input.gerrit_changes).
* tags (list of common_pb2.StringPair): tags for the new build.
* inherit_buildsets (bool): if `True` (default), the returned request will
include buildset tags from the current build.
* swarming_parent_run_id (str|NoneType): associate the new build as child of
the given swarming run id.
Defaults to `None` meaning no association.
If passed, must be a valid swarming *run* id (specific execution of a
task) for the swarming instance on which build will execute. Typically,
you'd want to set it to
[`api.swarming.task_id`](https://cs.chromium.org/chromium/infra/recipes-py/recipe_modules/swarming/api.py?type=cs&q=recipe_modules/swarming/api.py+%22def+task_id%22&sq=package:chromium&g=0&l=924).
Read more about
[`parent_run_id`](https://cs.chromium.org/chromium/infra/go/src/go.chromium.org/luci/buildbucket/proto/rpc.proto?type=cs&q="string+parent_run_id").
* dimensions (list of common_pb2.RequestedDimension): override dimensions
defined on the server.
* priority (int|NoneType|INHERIT): Swarming task priority.
The lower the more important. Valid values are `[20..255]`.
Defaults to the value of the current build.
Pass `None` to use the priority of the destination builder.
* critical (bool|common_pb2.Trinary|INHERIT): whether the build status
should not be used to assess correctness of the commit/CL.
Defaults to .build.critical.
See also Build.critical in
https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto
* exe_cipd_version (NoneType|str|INHERIT): CIPD version of the LUCI
Executable (e.g. recipe) to use. Pass `None` to use the server configured
one.
* fields (list of strs): a list of fields to include in the response, names
relative to `build_pb2.Build` (e.g. ["tags", "infra.swarming"]).
* can_outlive_parent: flag for if the scheduled child build can outlive
the current build or not (as enforced by Buildbucket;
swarming_parent_run_id currently ALSO applies).
Default is None. For now
* if `luci.buildbucket.manage_parent_child_relationship` is not in the
current build's experiments, can_outlive_parent is always True.
* Otherwise if can_outlive_parent is None,
ScheduleBuildRequest.can_outlive_parent will be determined by
swarming_parent_run_id.
TODO(crbug.com/1031205): remove swarming_parent_run_id.
&mdash; **def [search](/recipe_modules/buildbucket/api.py#611)(self, predicate, limit=None, url_title_fn=None, report_build=True, step_name=None, fields=DEFAULT_FIELDS):**
Searches for builds.
Example: find all builds of the current CL.
```python
from PB.go.chromium.org.luci.buildbucket.proto import rpc as builds_service_pb2
related_builds = api.buildbucket.search(builds_service_pb2.BuildPredicate(
gerrit_changes=list(api.buildbucket.build.input.gerrit_changes),
))
```
Args:
* predicate: a `builds_service_pb2.BuildPredicate` object or a list
thereof. If a list, the predicates are connected with logical OR.
* limit: max number of builds to return. Defaults to 1000.
* url_title_fn: generates a build URL title. See module docstring.
* report_build: whether to report build search results in step
presentation. Defaults to True.
* fields: a list of fields to include in the response, names relative
to `build_pb2.Build` (e.g. ["tags", "infra.swarming"]).
Returns:
A list of builds ordered newest-to-oldest.
&mdash; **def [set\_buildbucket\_host](/recipe_modules/buildbucket/api.py#103)(self, host):**
*** note
**DEPRECATED**: Use host property.
***
&mdash; **def [set\_output\_gitiles\_commit](/recipe_modules/buildbucket/api.py#190)(self, gitiles_commit):**
Sets `buildbucket.v2.Build.output.gitiles_commit` field.
This will tell other systems, consuming the build, what version of the code
was actually used in this build and what is the position of this build
relative to other builds of the same builder.
Args:
* gitiles_commit(buildbucket.common_pb2.GitilesCommit): the commit that was
actually checked out. Must have host, project and id.
ID must match r'^[0-9a-f]{40}$' (git revision).
If position is present, the build can be ordered along commits.
Position requires ref.
Ref, if not empty, must start with `refs/`.
Can be called at most once per build.
&emsp; **@staticmethod**<br>&mdash; **def [tags](/recipe_modules/buildbucket/api.py#239)(\*\*tags):**
Alias for tags in util.py. See doc there.
&mdash; **def [use\_service\_account\_key](/recipe_modules/buildbucket/api.py#117)(self, key_path):**
Tells this module to start using given service account key for auth.
Otherwise the module is using the default account (when running on LUCI or
locally), or no auth at all (when running on Buildbot).
Exists mostly to support Buildbot environment. Recipe for LUCI environment
should not use this.
Args:
* key_path (str): a path to JSON file with service account credentials.
&emsp; **@contextmanager**<br>&mdash; **def [with\_host](/recipe_modules/buildbucket/api.py#107)(self, host):**
Set the buildbucket host while in context, then reverts it.
### *recipe_modules* / [cas](/recipe_modules/cas)
[DEPS](/recipe_modules/cas/__init__.py#9): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [file](#recipe_modules-file), [path](#recipe_modules-path), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for interacting with cas client.
#### **class [CasApi](/recipe_modules/cas/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for interacting with cas client.
&mdash; **def [archive](/recipe_modules/cas/api.py#94)(self, step_name, root, \*paths, \*\*kwargs):**
Archives given paths to a cas server.
Args:
* step_name (str): name of the step.
* root (str|Path): root directory of archived tree, should be absolute
path.
* paths (list(str|Path)):
path to archived files/dirs, should be absolute path. If empty, [root]
will be used.
* log_level (str): logging level to use, rarely needed but helpful for
debugging.
Returns:
digest (str): digest of uploaded root directory.
&mdash; **def [download](/recipe_modules/cas/api.py#65)(self, step_name, digest, output_dir):**
Downloads a directory tree from a cas server.
Args:
* step_name (str): name of the step.
* digest (str): the digest of a cas tree.
* output_dir (Path): path to an output directory.
&emsp; **@property**<br>&mdash; **def [instance](/recipe_modules/cas/api.py#20)(self):**
&mdash; **def [viewer\_url](/recipe_modules/cas/api.py#85)(self, digest):**
Return URL of cas viewer.
### *recipe_modules* / [cas\_input](/recipe_modules/cas_input)
[DEPS](/recipe_modules/cas_input/__init__.py#9): [cas](#recipe_modules-cas), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Simple API for handling CAS inputs to a recipe.
Recipes sometimes need files as part of their execution which don't live in
source control (for example, they're generated elsewhere but tested in the
recipe). In that case, there needs to be an easy way to give these files as an
input to a recipe, so that the recipe can use them somehow. This module makes
this easy.
This module has input properties which contains a list of CAS inputs to
download. These can easily be download to disk with the 'download_caches'
method, and subsequently used by a recipe in whatever relevant manner.
#### **class [CasInputApi](/recipe_modules/cas_input/api.py#20)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for downloading CAS inputs to a recipe.
&mdash; **def [download\_caches](/recipe_modules/cas_input/api.py#32)(self, output_dir, caches=None):**
Downloads RBE-CAS caches and puts them in a given directory.
Args:
output_dir: The output directory to download the caches to. If you're
unsure of what directory to use, self.m.path['start_dir'] is a directory
the recipe engine sets up for you that you can use.
caches: A CasCache proto message containing the caches which should be
downloaded. See properties.proto for the message definition.
If unset, it uses the caches in this recipe module properties.
Returns:
The output directory as a Path object which contains all the cache data.
&emsp; **@property**<br>&mdash; **def [input\_caches](/recipe_modules/cas_input/api.py#28)(self):**
### *recipe_modules* / [change\_verifier](/recipe_modules/change_verifier)
[DEPS](/recipe_modules/change_verifier/__init__.py#7): [proto](#recipe_modules-proto), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Recipe API for LUCI Change Verifier.
LUCI Change Verifier is the pre-commit verification service that will replace
CQ daemon. See:
https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/cv
This recipe module depends on the prpc binary being available in $PATH:
https://godoc.org/go.chromium.org/luci/grpc/cmd/prpc
his recipe module depends on experimental API provided by LUCI CV and may
subject to change in the future. Please reach out to the LUCI team first if you
want to use this recipe module; file a ticket at:
https://bugs.chromium.org/p/chromium/issues/entry?components=Infra%3ELUCI%3EBuildService%3EPresubmit%3ECV
#### **class [ChangeVerifierApi](/recipe_modules/change_verifier/api.py#28)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
This module provides recipe API of LUCI Change Verifier.
&mdash; **def [search\_runs](/recipe_modules/change_verifier/api.py#34)(self, project, cls=None, limit=None, step_name=None, dev=False):**
Searches for Runs.
Args:
* project: LUCI project name.
* cls (list[tuple[str, int]]|tuple[str, int]|None): CLs, specified as
(host, change number) tuples. A single tuple may also be passed. All
Runs returned must include all of the given CLs, and Runs may also
contain other CLs.
* limit (int): max number of Runs to return. Defaults to 32.
* step_name (string): optional custom step name in RPC steps.
* dev (bool): whether to use the dev instance of Change Verifier.
Returns:
A list of CV Runs ordered newest to oldest that match the given criteria.
### *recipe_modules* / [cipd](/recipe_modules/cipd)
[DEPS](/recipe_modules/cipd/__init__.py#7): [context](#recipe_modules-context), [file](#recipe_modules-file), [futures](#recipe_modules-futures), [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step), [url](#recipe_modules-url)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for interacting with CIPD.
Depends on 'cipd' binary available in PATH:
https://godoc.org/go.chromium.org/luci/cipd/client/cmd/cipd
#### **class [CIPDApi](/recipe_modules/cipd/api.py#265)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
CIPDApi provides basic support for CIPD.
This assumes that `cipd` (or `cipd.exe` or `cipd.bat` on windows) has been
installed somewhere in $PATH.
Attributes:
* max_threads (int) - Number of worker threads for extracting packages.
If 0, uses CPU count.
&mdash; **def [acl\_check](/recipe_modules/cipd/api.py#358)(self, pkg_path, reader=True, writer=False, owner=False):**
Checks whether the caller has a given roles in a package.
Args:
* pkg_path (str) - The package subpath.
* reader (bool) - Check for READER role.
* writer (bool) - Check for WRITER role.
* owner (bool) - Check for OWNER role.
Returns True if the caller has given roles, False otherwise.
&mdash; **def [add\_instance\_link](/recipe_modules/cipd/api.py#619)(self, step_result):**
&mdash; **def [build](/recipe_modules/cipd/api.py#453)(self, input_dir, output_package, package_name, compression_level=None, install_mode=None, preserve_mtime=False, preserve_writable=False):**
Builds, but does not upload, a cipd package from a directory.
Args:
* input_dir (Path) - The directory to build the package from.
* output_package (Path) - The file to write the package to.
* package_name (str) - The name of the cipd package as it would appear
when uploaded to the cipd package server.
* compression_level (None|[0-9]) - Deflate compression level. If None,
defaults to 5 (0 - disable, 1 - best speed, 9 - best compression).
* install_mode (None|'copy'|'symlink') - The mechanism that the cipd
client should use when installing this package. If None, defaults to the
platform default ('copy' on windows, 'symlink' on everything else).
* preserve_mtime (bool) - Preserve file's modification time.
* preserve_writable (bool) - Preserve file's writable permission bit.
Returns the CIPDApi.Pin instance.
&mdash; **def [build\_from\_pkg](/recipe_modules/cipd/api.py#433)(self, pkg_def, output_package, compression_level=None):**
Builds a package based on a PackageDefinition object.
Args:
* pkg_def (PackageDefinition) - The description of the package we want to
create.
* output_package (Path) - The file to write the package to.
* compression_level (None|[0-9]) - Deflate compression level. If None,
defaults to 5 (0 - disable, 1 - best speed, 9 - best compression).
Returns the CIPDApi.Pin instance.
&mdash; **def [build\_from\_yaml](/recipe_modules/cipd/api.py#407)(self, pkg_def, output_package, pkg_vars=None, compression_level=None):**
Builds a package based on on-disk YAML package definition file.
Args:
* pkg_def (Path) - The path to the yaml file.
* output_package (Path) - The file to write the package to.
* pkg_vars (dict[str]str) - A map of var name -> value to use for vars
referenced in package definition file.
* compression_level (None|[0-9]) - Deflate compression level. If None,
defaults to 5 (0 - disable, 1 - best speed, 9 - best compression).
Returns the CIPDApi.Pin instance.
&emsp; **@contextlib.contextmanager**<br>&mdash; **def [cache\_dir](/recipe_modules/cipd/api.py#331)(self, directory):**
Sets the cache dir to use with CIPD by setting the $CIPD_CACHE_DIR
environment variable.
If directory is "None", will use no cache directory.
&mdash; **def [create\_from\_pkg](/recipe_modules/cipd/api.py#663)(self, pkg_def, refs=None, tags=None, metadata=None, compression_level=None, verification_timeout=None):**
Builds and uploads a package based on a PackageDefinition object.
This builds and uploads the package in one step.
Args:
* pkg_def (PackageDefinition) - The description of the package we want to
create.
* refs (list[str]) - A list of ref names to set for the package instance.
* tags (dict[str]str) - A map of tag name -> value to set for the
package instance.
* metadata (list[Metadata]) - A list of metadata entries to attach.
* compression_level (None|[0-9]) - Deflate compression level. If None,
defaults to 5 (0 - disable, 1 - best speed, 9 - best compression).
* verification_timeout (str) - Duration string that controls the time to
wait for backend-side package hash verification. Valid time units are
"s", "m", "h". Default is "5m".
Returns the CIPDApi.Pin instance.
&mdash; **def [create\_from\_yaml](/recipe_modules/cipd/api.py#625)(self, pkg_def, refs=None, tags=None, metadata=None, pkg_vars=None, compression_level=None, verification_timeout=None):**
Builds and uploads a package based on on-disk YAML package definition
file.
This builds and uploads the package in one step.
Args:
* pkg_def (Path) - The path to the yaml file.
* refs (list[str]) - A list of ref names to set for the package instance.
* tags (dict[str]str) - A map of tag name -> value to set for the
package instance.
* metadata (list[Metadata]) - A list of metadata entries to attach.
* pkg_vars (dict[str]str) - A map of var name -> value to use for vars
referenced in package definition file.
* compression_level (None|[0-9]) - Deflate compression level. If None,
defaults to 5 (0 - disable, 1 - best speed, 9 - best compression).
* verification_timeout (str) - Duration string that controls the time to
wait for backend-side package hash verification. Valid time units are
"s", "m", "h". Default is "5m".
Returns the CIPDApi.Pin instance.
&mdash; **def [describe](/recipe_modules/cipd/api.py#882)(self, package_name, version, test_data_refs=None, test_data_tags=None):**
Returns information about a package instance given its version:
who uploaded the instance and when and a list of attached tags.
Args:
* package_name (str) - The name of the cipd package.
* version (str) - The package version to point the ref to.
* test_data_refs (seq[str]) - The list of refs for this call to return
by default when in test mode.
* test_data_tags (seq[str]) - The list of tags (in 'name:val' form) for
this call to return by default when in test mode.
Returns the CIPDApi.Description instance describing the package.
&mdash; **def [ensure](/recipe_modules/cipd/api.py#698)(self, root, ensure_file, name='ensure_installed'):**
Ensures that packages are installed in a given root dir.
Args:
* root (Path) - Path to installation site root directory.
* ensure_file (EnsureFile|Path) - List of packages to install.
* name (str) - Step display name.
Returns the map of subdirectories to CIPDApi.Pin instances.
&mdash; **def [ensure\_file\_resolve](/recipe_modules/cipd/api.py#740)(self, ensure_file, name='cipd ensure-file-resolve'):**
Resolves versions of all packages for all verified platforms in an
ensure file.
Args:
* ensure_file (EnsureFile|Path) - Ensure file to resolve.
&mdash; **def [ensure\_tool](/recipe_modules/cipd/api.py#1008)(self, package, version, executable_path=None):**
Downloads an executable from CIPD.
Given a package named "name/of/some_exe/${platform}" and version
"someversion", this will install the package at the directory
"[START_DIR]/cipd_tool/name/of/some_exe/someversion". It will then return the
absolute path to the executable within that directory.
This operation is idempotent, and will only run steps to download the
package if it hasn't already been installed in the same build.
Args:
* package (str) - The full name of the CIPD package.
* version (str) - The version of the package to download.
* executable_path (str|None) - The path within the package of the desired
executable. Defaults to the basename of the package (the final
non-variable component of the package name). Must use forward-slashes,
even on Windows.
Returns a Path to the executable.
Future-safe; Multiple concurrent calls for the same (package, version) will
block on a single ensure step.
&emsp; **@property**<br>&mdash; **def [executable](/recipe_modules/cipd/api.py#343)(self):**
&mdash; **def [instances](/recipe_modules/cipd/api.py#917)(self, package_name, limit=None):**
Lists instances of a package, most recently uploaded first.
Args:
* package_name (str) - The name of the cipd package.
* limit (None|int) - The number of instances to return. 0 for all.
If None, default value of 'cipd' binary will be used (20).
Returns the list of CIPDApi.Instance instance.
&mdash; **def [pkg\_deploy](/recipe_modules/cipd/api.py#984)(self, root, package_file):**
Deploys the specified package to root.
ADVANCED METHOD: You shouldn't need this unless you're doing advanced
things with CIPD. Typically you should use the `ensure` method here to
fetch+install packages to the disk.
Args:
* package_file (Path) - Path to a package file to install.
* root (Path) - Path to a CIPD root.
Returns a Pin for the deployed package.
&mdash; **def [pkg\_fetch](/recipe_modules/cipd/api.py#954)(self, destination, package_name, version):**
Downloads the specified package to destination.
ADVANCED METHOD: You shouldn't need this unless you're doing advanced things
with CIPD. Typically you should use the `ensure` method here to
fetch+install packages to the disk.
Args:
* destination (Path) - Path to a file location which will be (over)written
with the package file contents.
* package_name (str) - The package name (or pattern with e.g.
${platform})
* version (str) - The CIPD version to fetch
Returns a Pin for the downloaded package.
&mdash; **def [register](/recipe_modules/cipd/api.py#549)(self, package_name, package_path, refs=None, tags=None, metadata=None, verification_timeout=None):**
Uploads and registers package instance in the package repository.
Args:
* package_name (str) - The name of the cipd package.
* package_path (Path) - The path to package instance file.
* refs (list[str]) - A list of ref names to set for the package instance.
* tags (dict[str]basestring) - A map of tag name -> value to set for the
package instance.
* metadata (list[Metadata]) - A list of metadata entries to attach.
* verification_timeout (str) - Duration string that controls the time to
wait for backend-side package hash verification. Valid time units are
"s", "m", "h". Default is "5m".
Returns:
The CIPDApi.Pin instance.
&mdash; **def [search](/recipe_modules/cipd/api.py#850)(self, package_name, tag, test_instances=None):**
Searches for package instances by tag, optionally constrained by package
name.
Args:
* package_name (str) - The name of the cipd package.
* tag (str) - The cipd package tag.
* test_instances (None|int|List[str]) - Default test data for this step:
* None - Search returns a single default pin.
* int - Search generates `test_instances` number of testing ids
`instance_id_%d` and returns pins for those.
* List[str] - Returns pins for the given testing ids.
Returns the list of CIPDApi.Pin instances.
&mdash; **def [set\_metadata](/recipe_modules/cipd/api.py#798)(self, package_name, version, metadata):**
Attaches metadata to a package instance.
Args:
* package_name (str) - The name of the cipd package.
* version (str) - The package version to attach metadata to.
* metadata (list[Metadata]) - A list of metadata entries to attach.
Returns the CIPDApi.Pin instance.
&mdash; **def [set\_ref](/recipe_modules/cipd/api.py#824)(self, package_name, version, refs):**
Moves a ref to point to a given version.
Args:
* package_name (str) - The name of the cipd package.
* version (str) - The package version to point the ref to.
* refs (list[str]) - A list of ref names to set for the package instance.
Returns the CIPDApi.Pin instance.
&mdash; **def [set\_tag](/recipe_modules/cipd/api.py#770)(self, package_name, version, tags):**
Tags package of a specific version.
Args:
* package_name (str) - The name of the cipd package.
* version (str) - The package version to resolve. Could also be itself a
tag or ref.
* tags (dict[str]str) - A map of tag name -> value to set for the
package instance.
Returns the CIPDApi.Pin instance.
### *recipe_modules* / [commit\_position](/recipe_modules/commit_position)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [CommitPositionApi](/recipe_modules/commit_position/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Recipe module providing commit position parsing and formatting.
&emsp; **@classmethod**<br>&mdash; **def [format](/recipe_modules/commit_position/api.py#26)(cls, ref, revision_number):**
Returns a commit position string.
ref must start with 'refs/'.
&emsp; **@classmethod**<br>&mdash; **def [parse](/recipe_modules/commit_position/api.py#16)(cls, value):**
Returns (ref, revision_number) tuple.
### *recipe_modules* / [context](/recipe_modules/context)
[DEPS](/recipe_modules/context/__init__.py#7): [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
The context module provides APIs for manipulating a few pieces of 'ambient'
data that affect how steps are run.
The pieces of information which can be modified are:
* cwd - The current working directory.
* env - The environment variables.
* infra_step - Whether or not failures should be treated as infrastructure
failures vs. normal failures.
The values here are all scoped using Python's `with` statement; there's no
mechanism to make an open-ended adjustment to these values (i.e. there's no way
to change the cwd permanently for a recipe, except by surrounding the entire
recipe with a with statement). This is done to avoid the surprises that
typically arise with things like os.environ or os.chdir in a normal python
program.
Example:
```python
with api.context(cwd=api.path['start_dir'].join('subdir')):
# this step is run inside of the subdir directory.
api.step("cat subdir/foo", ['cat', './foo'])
```
#### **class [ContextApi](/recipe_modules/context/api.py#80)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/recipe_modules/context/api.py#112)(self, cwd=None, env_prefixes=None, env_suffixes=None, env=None, infra_steps=None, luciexe=None, realm=None, deadline=None):**
Allows adjustment of multiple context values in a single call.
Args:
* cwd (Path) - the current working directory to use for all steps.
To 'reset' to the original cwd at the time recipes started, pass
`api.path['start_dir']`.
* env_prefixes (dict) - Environmental variable prefix augmentations. See
below for more info.
* env_suffixes (dict) - Environmental variable suffix augmentations. See
below for more info.
* env (dict) - Environmental variable overrides. See below for more info.
* infra_steps (bool) - if steps in this context should be considered
infrastructure steps. On failure, these will raise InfraFailure
exceptions instead of StepFailure exceptions.
* luciexe (sections_pb2.LUCIExe) - The override value for 'luciexe'
section in LUCI_CONTEXT. This is currently used to modify the
`cache_dir` for all launched LUCI Executable (via
`api.step.sub_build(...)`).
* realm (str) - allows changing the current LUCI realm. It is used when
creating new LUCI resources (e.g. spawning new Swarming tasks). Pass an
empty string to disassociate the context from a realm, emulating an
environment prior to LUCI realms. This is useful during the transitional
period.
* deadline (sections_pb2.Deadline) - Deadline information to set; See
LUCI_CONTEXT documentation for how this section works. Automatically
adjusted by steps with `timeout` set.
Environmental Variable Overrides:
Env is a mapping of environment variable name to the value you want that
environment variable to have. The value is one of:
* None, indicating that the environment variable should be removed from
the environment when the step runs.
* A string value. Note that string values will be %-formatted with the
current value of the environment at the time the step runs. This means
that you can have a value like:
"/path/to/my/stuff:%(PATH)s"
Which, at the time the step executes, will inject the current value of
$PATH.
"env_prefix" and "env_suffix" are a list of Path or strings that get
prefixed (or suffixed) to their respective environment variables, delimited
with the system's path separator. This can be used to add entries to
environment variables such as "PATH" and "PYTHONPATH". If prefixes are
specified and a value is also defined in "env", the value will be installed
as the last path component if it is not empty.
Look at the examples in "examples/" for examples of context module usage.
&emsp; **@property**<br>&mdash; **def [cwd](/recipe_modules/context/api.py#255)(self):**
Returns the current working directory that steps will run in.
**Returns (Path|None)** - The current working directory. A value of None is
equivalent to api.path['start_dir'], though only occurs if no cwd has been
set (e.g. in the outermost context of RunSteps).
&emsp; **@property**<br>&mdash; **def [deadline](/recipe_modules/context/api.py#348)(self):**
Returns the current value (sections_pb2.Deadline) of deadline section in
the current LUCI_CONTEXT. Returns `{grace_period: 30}` if deadline is not
defined, per LUCI_CONTEXT spec.
&emsp; **@property**<br>&mdash; **def [env](/recipe_modules/context/api.py#265)(self):**
Returns modifications to the environment.
By default this is empty. If you want to observe the program's startup
environment, see `ENV_PROPERTIES` in
https://chromium.googlesource.com/infra/luci/recipes-py/+/refs/heads/main/doc/user_guide.md#properties-and-env_properties
**Returns (dict)** - The env-key -> value mapping of current environment
modifications.
&emsp; **@property**<br>&mdash; **def [env\_prefixes](/recipe_modules/context/api.py#280)(self):**
Returns Path prefix modifications to the environment.
This will return a mapping of environment key to Path tuple for Path
prefixes registered with the environment.
**Returns (dict)** - The env-key -> value(Path) mapping of current
environment prefix modifications.
&emsp; **@property**<br>&mdash; **def [env\_suffixes](/recipe_modules/context/api.py#294)(self):**
Returns Path suffix modifications to the environment.
This will return a mapping of environment key to Path tuple for Path
suffixes registered with the environment.
**Returns (dict)** - The env-key -> value(Path) mapping of current
environment suffix modifications.
&emsp; **@property**<br>&mdash; **def [infra\_step](/recipe_modules/context/api.py#308)(self):**
Returns the current value of the infra_step setting.
**Returns (bool)** - True iff steps are currently considered infra steps.
&mdash; **def [initialize](/recipe_modules/context/api.py#90)(self):**
&emsp; **@property**<br>&mdash; **def [luci\_context](/recipe_modules/context/api.py#316)(self):**
Returns the currently tracked LUCI_CONTEXT sections as a dict of proto
messages.
Only contains `luciexe`, `realm`, 'resultdb' and `deadline`.
&emsp; **@property**<br>&mdash; **def [luciexe](/recipe_modules/context/api.py#328)(self):**
Returns the current value (sections_pb2.LUCIExe) of luciexe section in
the current LUCI_CONTEXT. Returns None if luciexe is not defined.
&emsp; **@property**<br>&mdash; **def [realm](/recipe_modules/context/api.py#338)(self):**
Returns the LUCI realm of the current context.
May return None if the task is not running in the realm-aware mode. This is
a transitional period. Eventually all tasks will be associated with realms.
&emsp; **@property**<br>&mdash; **def [resultdb\_invocation\_name](/recipe_modules/context/api.py#359)(self):**
Returns the ResultDB invocation name of the current context.
Returns None if resultdb is not defined.
### *recipe_modules* / [cq](/recipe_modules/cq)
[DEPS](/recipe_modules/cq/__init__.py#9): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Recipe API for LUCI CQ, the pre-commit testing system.
#### **class [CQApi](/recipe_modules/cq/api.py#18)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
This module provides recipe API of LUCI CQ, aka pre-commit testing system.
The CQ service is being replaced with a service now named LUCI Change
Verifier (CV); for more information see:
https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/cv
TODO(qyearsley): Rename parts of this from CQ -> CV as appropriate.
&emsp; **@property**<br>&mdash; **def [active](/recipe_modules/cq/api.py#52)(self):**
Returns whether CQ is active for this build.
&mdash; **def [allow\_reuse\_for](/recipe_modules/cq/api.py#223)(self, \*modes):**
Instructs CQ that this build can be reused in a future Run if
and only if its mode is in the provided modes.
Overwrites all previously set values.
&emsp; **@property**<br>&mdash; **def [allowed\_reuse\_modes](/recipe_modules/cq/api.py#219)(self):**
&emsp; **@property**<br>&mdash; **def [cl\_group\_key](/recipe_modules/cq/api.py#135)(self):**
Returns a string that is unique for a current set of Gerrit change
patchsets (or, equivalently, buildsets).
The same `cl_group_key` will be used if another Attempt is made for the
same set of changes at a different time.
Raises:
CQInactive if CQ is not active for this build.
&emsp; **@property**<br>&mdash; **def [do\_not\_retry\_build](/recipe_modules/cq/api.py#201)(self):**
&emsp; **@property**<br>&mdash; **def [equivalent\_cl\_group\_key](/recipe_modules/cq/api.py#148)(self):**
Returns a string that is unique for a given set of Gerrit changes
disregarding trivial patchset differences.
For example, when a new "trivial" patchset is uploaded, then the
cl_group_key will change but the equivalent_cl_group_key will stay the same.
Raises:
CQInactive if CQ is not active for this build.
&emsp; **@property**<br>&mdash; **def [experimental](/recipe_modules/cq/api.py#67)(self):**
Returns whether this build is triggered for a CQ experimental builder.
See `Builder.experiment_percentage` doc in [CQ
config](https://chromium.googlesource.com/infra/luci/luci-go/+/main/cv/api/config/v2/config.proto)
Raises:
CQInactive if CQ is not active for this build.
&mdash; **def [initialize](/recipe_modules/cq/api.py#42)(self):**
&emsp; **@property**<br>&mdash; **def [ordered\_gerrit\_changes](/recipe_modules/cq/api.py#92)(self):**
Returns list[bb_common_pb2.GerritChange] in order in which CLs should be
applied or submitted.
Raises:
CQInactive if CQ is not active for this build.
&emsp; **@property**<br>&mdash; **def [props\_for\_child\_build](/recipe_modules/cq/api.py#106)(self):**
Returns properties dict meant to be passed to child builds.
These will preserve the CQ context of the current build in the
about-to-be-triggered child build.
```python
properties = {'foo': bar, 'protolike': proto_message}
properties.update(api.cq.props_for_child_build)
req = api.buildbucket.schedule_request(
builder='child',
gerrit_changes=list(api.buildbucket.build.input.gerrit_changes),
properties=properties)
child_builds = api.buildbucket.schedule([req])
api.cq.record_triggered_builds(*child_builds)
```
The contents of returned dict should be treated as opaque blob,
it may be changed without notice.
&mdash; **def [record\_triggered\_build\_ids](/recipe_modules/cq/api.py#183)(self, \*build_ids):**
Adds given Buildbucket build ids to the list of triggered builds for CQ
to wait on corresponding build completion later.
Must be called after some step.
Args:
* build_id (int or string): Buildbucket build id.
&mdash; **def [record\_triggered\_builds](/recipe_modules/cq/api.py#166)(self, \*builds):**
Adds given Buildbucket builds to the list of triggered builds for CQ
to wait on corresponding build completion later.
Must be called after some step.
Expected usage:
```python
api.cq.record_triggered_builds(*api.buildbucket.schedule([req1, req2]))
```
Args:
* [`Build`](https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/build.proto)
objects, typically returned by `api.buildbucket.schedule`.
&emsp; **@property**<br>&mdash; **def [run\_mode](/recipe_modules/cq/api.py#57)(self):**
Returns the mode(str) of the CQ Run that triggers this build.
Raises:
CQInactive if CQ is not active for this build.
&mdash; **def [set\_do\_not\_retry\_build](/recipe_modules/cq/api.py#205)(self):**
Instruct CQ to not retry this build.
This mechanism is used to reduce duration of CQ attempt and save testing
capacity if retrying will likely return an identical result.
&emsp; **@property**<br>&mdash; **def [top\_level](/recipe_modules/cq/api.py#80)(self):**
Returns whether CQ triggered this build directly.
Can be spoofed. *DO NOT USE FOR SECURITY CHECKS.*
Raises:
CQInactive if CQ is not active for this build.
&emsp; **@property**<br>&mdash; **def [triggered\_build\_ids](/recipe_modules/cq/api.py#161)(self):**
Returns recorded Buildbucket build IDs as a list of integers.
### *recipe_modules* / [file](/recipe_modules/file)
[DEPS](/recipe_modules/file/__init__.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [proto](#recipe_modules-proto), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
File manipulation (read/write/delete/glob) methods.
#### **class [FileApi](/recipe_modules/file/api.py#85)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [compute\_hash](/recipe_modules/file/api.py#214)(self, name, paths, base_path, test_data=''):**
Computes hash of contents of a directory/file.
This function will compute hash by including following info of a file:
* str(len(path)) // path is relative to base_path
* path // path is relative to base_path
* str(len(file))
* file_content
Each of these components are separated by a newline character. For example,
for file = "hello" and the contents "world" the hash would be over:
```
5
hello
5
world
```
Args:
* name (str): The name of the step.
* paths (list[Path|str]): Path of directory/file(s) to compute hash.
* base_path (Path|str): Base directory to calculating hash relative to
absolute path. For e.g. `start_dir` of a recipe execution can be used.
* test_data (str): Some default data for this step to return when running
under simulation. If no test data is provided, we compute test_data as
sha256 of concatenated relative paths passed.
Returns (str):
Hex encoded hash of directory/file content.
Raises:
file.Error and ValueError if passed paths input is not str or Path.
&mdash; **def [copy](/recipe_modules/file/api.py#126)(self, name, source, dest):**
Copies a file (including mode bits) from source to destination on the
local filesystem.
Behaves identically to shutil.copy.
Args:
* name (str): The name of the step.
* source (Path|Placeholder): The path to the file you want to copy.
* dest (Path|Placeholder): The path to the destination file name. If this
path exists and is a directory, the basename of `source` will be
appended to derive a path to a destination file.
Raises: file.Error
&mdash; **def [copytree](/recipe_modules/file/api.py#146)(self, name, source, dest, symlinks=False):**
Recursively copies a directory tree.
Behaves identically to shutil.copytree.
`dest` must not exist.
Args:
* name (str): The name of the step.
* source (Path): The path of the directory to copy.
* dest (Path): The place where you want the recursive copy to show up.
This must not already exist.
* symlinks (bool): Preserve symlinks. No effect on Windows.
Raises: file.Error
&mdash; **def [ensure\_directory](/recipe_modules/file/api.py#541)(self, name, dest, mode=511):**
Ensures that `dest` exists and is a directory.
Args:
* name (str): The name of the step.
* dest (Path): The directory to ensure.
* mode (int): The mode to use if the directory doesn't exist. This method
does not ensure the mode if the directory already exists (if you need
that behaviour, file a bug).
Raises: file.Error if the path exists but is not a directory.
&mdash; **def [file\_hash](/recipe_modules/file/api.py#185)(self, file_path, test_data=''):**
Computes hash of contents of a single file.
Args:
* file_path (Path|str): Path of file to compute hash.
* test_data (str): Some default data for this step to return when running
under simulation. If no test data is provided, we compute test_data as
sha256 of path passed.
Returns (str):
Hex encoded hash of file content.
Raises:
file.Error and ValueError if passed paths input is not str or Path.
&mdash; **def [filesizes](/recipe_modules/file/api.py#557)(self, name, files, test_data=None):**
Returns list of filesizes for the given files.
Args:
* name (str): The name of the step.
* files (list[Path]): Paths to files.
Returns list[int], size of each file in bytes.
&mdash; **def [flatten\_single\_directories](/recipe_modules/file/api.py#697)(self, name, path):**
Flattens singular directories, starting at path.
Example:
$ mkdir -p dir/which_has/some/singular/subdirs/
$ touch dir/which_has/some/singular/subdirs/with
$ touch dir/which_has/some/singular/subdirs/files
$ flatten_single_directories(dir)
$ ls dir
with
files
This can be useful when you just want the 'meat' of a very sparse directory
structure. For example, some tarballs like `foo-1.2.tar.gz` extract all
their contents into a subdirectory `foo-1.2/`.
Using this function would essentially move all the actual contents of the
extracted archive up to the top level directory, removing the need to e.g.
hard-code/find the subfolder name after extraction (not all archives are
even named after the subfolder they extract to).
Args:
* name (str): The name of the step.
* path (Path|str): The absolute path to begin flattening.
Raises: file.Error
&mdash; **def [glob\_paths](/recipe_modules/file/api.py#449)(self, name, source, pattern, include_hidden=False, test_data=()):**
Performs glob expansion on `pattern`.
glob rules for `pattern` follow the same syntax as for the `python-glob2`
module, which supports '**' syntax.
```
e.g. 'a/**/*.py'
a/b/foo.py => MATCH
a/b/c/foo.py => MATCH
a/foo.py => MATCH
a/b/c/d/e/f/g/h/i/j/foo.py => MATCH
other/foo.py => NO MATCH
```
Args:
* name (str): The name of the step.
* source (Path): The directory whose contents should be globbed.
* pattern (str): The glob pattern to apply under `source`.
* include_hidden (bool): Include files beginning with `.`.
* test_data (iterable[str]): Some default data for this step to return
when running under simulation. This should be the list of file items
found in this directory.
Returns (list[Path]): All paths found.
Raises: file.Error.
&mdash; **def [listdir](/recipe_modules/file/api.py#511)(self, name, source, recursive=False, test_data=()):**
Lists all files inside a directory.
Args:
* name (str): The name of the step.
* source (Path): The directory to list.
* recursive (bool): If True, do not emit subdirectory entries but recurse
into them instead, emitting paths relative to `source`. Doesn't follow
symlinks. Very slow for large directories.
* test_data (iterable[str]): Some default data for this step to return
when running under simulation. This should be the list of relative paths
found in this directory.
Returns list[Path]
Raises: file.Error.
&mdash; **def [move](/recipe_modules/file/api.py#167)(self, name, source, dest):**
Moves a file or directory.
Behaves identically to shutil.move.
Args:
* name (str): The name of the step.
* source (Path): The path of the item to move.
* dest (Path): The new name of the item.
Raises: file.Error
&mdash; **def [read\_json](/recipe_modules/file/api.py#341)(self, name, source, test_data='', include_log=True):**
Reads a file as UTF-8 encoded json.
Args:
* name (str): The name of the step.
* source (Path): The path of the file to read.
* test_data (object): Some default json serializable data for this step
to return when running under simulation.
* include_log (bool): Include step log of read json.
Returns (object): The content of the file.
Raise file.Error
&mdash; **def [read\_proto](/recipe_modules/file/api.py#377)(self, name, source, msg_class, codec, test_proto=None, include_log=True, encoding_kwargs=None):**
Reads a file into a proto message.
Args:
* name (str): The name of the step.
* source (Path): The path of the file to read.
* msg_class (protobuf Message subclass): The message type to be read.
* codec ('BINARY'|'JSONPB'|'TEXTPB'): The encoder to use.
* test_proto (protobuf Message): A default proto message for this step to
return when running under simulation.
* include_log (bool): Include step log of read proto.
* encoding_kwargs (dict): Passed directly to the chosen encoder. See proto
module for details.
&mdash; **def [read\_raw](/recipe_modules/file/api.py#265)(self, name, source, test_data=''):**
Reads a file as raw data.
Args:
* name (str): The name of the step.
* source (Path): The path of the file to read.
* test_data (str): Some default data for this step to return when running
under simulation.
Returns (str): The unencoded (binary) contents of the file.
Raises: file.Error
&mdash; **def [read\_text](/recipe_modules/file/api.py#299)(self, name, source, test_data='', include_log=True):**
Reads a file as UTF-8 encoded text.
Args:
* name (str): The name of the step.
* source (Path): The path of the file to read.
* test_data (str): Some default data for this step to return when running
under simulation.
* include_log (bool): Include step log of read text.
Returns (str): The content of the file.
Raises: file.Error
&mdash; **def [remove](/recipe_modules/file/api.py#496)(self, name, source):**
Removes a file.
Does not raise Error if the file doesn't exist.
Args:
* name (str): The name of the step.
* source (Path): The file to remove.
Raises: file.Error.
&mdash; **def [rmcontents](/recipe_modules/file/api.py#596)(self, name, source):**
Similar to rmtree, but removes only contents not the directory.
This is useful e.g. when removing contents of current working directory.
Deleting current working directory makes all further getcwd calls fail
until chdir is called. chdir would be tricky in recipes, so we provide
a call that doesn't delete the directory itself.
Args:
* name (str): The name of the step.
* source (Path): The directory whose contents should be removed.
Raises: file.Error.
&mdash; **def [rmglob](/recipe_modules/file/api.py#614)(self, name, source, pattern, recursive=True, include_hidden=True):**
Removes all entries in `source` matching the glob `pattern`.
glob rules for `pattern` follow the same syntax as for the `python-glob2`
module, which supports '**' syntax.
```
e.g. 'a/**/*.py'
a/b/foo.py => MATCH
a/b/c/foo.py => MATCH
a/foo.py => MATCH
a/b/c/d/e/f/g/h/i/j/foo.py => MATCH
other/foo.py => NO MATCH
```
Args:
* name (str): The name of the step.
* source (Path): The directory whose contents should be filtered and
removed.
* pattern (str): The glob pattern to apply under `source`. Anything
matching this pattern will be removed.
* recursive (bool): Recursively remove entries under `source`.
TODO: Remove this option. Use `**` syntax instead.
* include_hidden (bool): Include files beginning with `.`.
TODO: Set to False by default to be consistent with file.glob.
Raises: file.Error.
&mdash; **def [rmtree](/recipe_modules/file/api.py#579)(self, name, source):**
Recursively removes a directory.
This uses a native python on Linux/Mac, and uses `rd` on Windows to avoid
issues w.r.t. path lengths and read-only attributes. If the directory is
gone already, this returns without error.
Args:
* name (str): The name of the step.
* source (Path): The directory to remove.
Raises: file.Error.
&mdash; **def [symlink](/recipe_modules/file/api.py#659)(self, name, source, linkname):**
Creates a symlink on the local filesystem.
Behaves identically to os.symlink.
Args:
* name (str): The name of the step.
* source (Path|Placeholder): The path to link from.
* linkname (Path|Placeholder): The destination to link to.
Raises: file.Error
&mdash; **def [symlink\_tree](/recipe_modules/file/api.py#676)(self, root):**
Creates a SymlinkTree, given a root directory.
Args:
* root (Path): root of a tree of symlinks.
&mdash; **def [truncate](/recipe_modules/file/api.py#684)(self, name, path, size_mb=100):**
Creates an empty file with path and size_mb on the local filesystem.
Args:
* name (str): The name of the step.
* path (Path|str): The absolute path to create.
* size_mb (int): The size of the file in megabytes. Defaults to 100
Raises: file.Error
&mdash; **def [write\_json](/recipe_modules/file/api.py#361)(self, name, dest, data, indent=None, include_log=True):**
Write the given json serializable `data` to `dest`.
Args:
* name (str): The name of the step.
* dest (Path): The path of the file to write.
* data (object): Json serializable data to write.
* indent (None|int|str): The indent of the written JSON. See
https://docs.python.org/3/library/json.html#json.dump for more details.
* include_log (bool): Include step log of written json.
Raises: file.Error.
&mdash; **def [write\_proto](/recipe_modules/file/api.py#418)(self, name, dest, proto_msg, codec, include_log=True, encoding_kwargs=None):**
Writes the given proto message to `dest`.
Args:
* name (str): The name of thhe step.
* dest (Path): The path of the file to write.
* proto_msg (protobuf Message): Message to write.
* codec ('BINARY'|'JSONPB'|'TEXTPB'): The encoder to use.
* include_log (bool): Include step log of written proto.
* encoding_kwargs (dict): Passed directly to the chosen encoder. See
proto module for details.
&mdash; **def [write\_raw](/recipe_modules/file/api.py#285)(self, name, dest, data):**
Write the given `data` to `dest`.
Args:
* name (str): The name of the step.
* dest (Path): The path of the file to write.
* data (str): The data to write.
Raises: file.Error.
&mdash; **def [write\_text](/recipe_modules/file/api.py#323)(self, name, dest, text_data, include_log=True):**
Write the given UTF-8 encoded `text_data` to `dest`.
Args:
* name (str): The name of the step.
* dest (Path): The path of the file to write.
* text_data (str): The UTF-8 encoded data to write.
* include_log (bool): Include step log of written text.
Raises: file.Error.
### *recipe_modules* / [futures](/recipe_modules/futures)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Implements in-recipe concurrency via green threads.
#### **class [FuturesApi](/recipe_modules/futures/api.py#43)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Provides access to the Recipe concurrency primitives.
&emsp; **@staticmethod**<br>&mdash; **def [iwait](/recipe_modules/futures/api.py#306)(futures, timeout=None, count=None):**
Iteratively yield up to `count` Futures as they become done.
This is analogous to `gevent.iwait`.
Usage:
for future in api.futures.iwait(futures):
# consume future
If you are not planning to consume the entire iwait iterator, you can
avoid the resource leak by doing, for example:
with api.futures.iwait(a, b, c) as iter:
for future in iter:
if future is a:
break
You might want to use `iwait` over `wait` if you want to process a group
of Futures in the order in which they complete. Compare:
for task in iwait(swarming_tasks):
# task is done, do something with it
vs
while swarming_tasks:
task = wait(swarming_tasks, count=1)[0] # some task is done
swarming_tasks.remove(task)
# do something with it
Args:
* futures (List[Future]) - The Future objects to wait for.
* timeout (None|seconds) - How long to wait for the Futures to be done.
* count (None|int) - The number of Futures to yield. If None,
yields all of them.
Yields futures in the order in which they complete until we hit the
timeout or count. May also be used with a context manager to avoid
leaking resources if you don't plan on consuming the entire iterable.
&mdash; **def [make\_bounded\_semaphore](/recipe_modules/futures/api.py#155)(self, value=1):**
Returns a gevent.BoundedSemaphore with depth `value`.
This can be used as a context-manager to create concurrency-limited sections
like:
def worker(api, sem, i):
with api.step.nest('worker %d' % i):
with sem:
api.step('one at a time', ...)
api.step('unrestricted concurrency' , ...)
sem = api.future.make_semaphore()
for i in xrange(100):
api.futures.spawn(fn, sem, i)
*** promo
NOTE: If you use the BoundedSemaphore without the context-manager syntax, it
could lead to difficult-to-debug deadlocks in your recipe.
***
*** promo
NOTE: This method will raise ValueError if used with @@@annotation@@@ mode.
***
&mdash; **def [make\_channel](/recipe_modules/futures/api.py#182)(self):**
Returns a single-slot communication device for passing data and control
between concurrent functions.
This is useful for running 'background helper' type concurrent processes.
*** promo
NOTE: It is strongly discouraged to pass Channel objects outside of a recipe
module. Access to the channel should be mediated via
a class/contextmanager/function which you return to the caller, and the
caller can call in a makes-sense-for-your-moudle's-API way.
***
See ./tests/background_helper.py for an example of how to use a Channel
correctly.
It is VERY RARE to need to use a Channel. You should avoid using this unless
you carefully consider and avoid the possibility of introducing deadlocks.
*** promo
NOTE: This method will raise ValueError if used with @@@annotation@@@ mode.
***
&emsp; **@escape_all_warnings**<br>&mdash; **def [spawn](/recipe_modules/futures/api.py#206)(self, func, \*args, \*\*kwargs):**
Prepares a Future to run `func(*args, **kwargs)` concurrently.
Any steps executed in `func` will only have manipulable StepPresentation
within the scope of the executed function.
Because this will spawn a greenlet on the same OS thread (and not,
for example a different OS thread or process), `func` can easily be an
inner function, closure, lambda, etc. In particular, func, args and kwargs
do not need to be pickle-able.
This function does NOT switch to the greenlet (you'll have to block on a
future/step for that to happen). In particular, this means that the
following pattern is safe:
# self._my_future check + spawn + assignment is atomic because
# no switch points occur.
if not self._my_future:
self._my_future = api.futures.spawn(func)
*** promo
NOTE: If used in @@@annotator@@@ mode, this will block on the completion of
the Future before returning it.
***
Kwargs:
* __name (str) - If provided, will assign this name to the spawned
greenlet. Useful if this greenlet ends up raising an exception, this
name will appear in the stderr logging for the engine. See
`Future.name` for more information.
* __meta (any) - If provided, will assign this metadata to the returned
Future. This field is for your exclusive use.
* Everything else is passed to `func`.
Returns a Future of `func`'s result.
&emsp; **@escape_all_warnings**<br>&mdash; **def [spawn\_immediate](/recipe_modules/futures/api.py#256)(self, func, \*args, \*\*kwargs):**
Returns a Future to the concurrently running `func(*args, **kwargs)`.
This is like `spawn`, except that it IMMEDIATELY switches to the new
Greenlet. You may want to use this if you want to e.g. launch a background
step and then another step which waits for the daemon.
Kwargs:
* __name (str) - If provided, will assign this name to the spawned
greenlet. Useful if this greenlet ends up raising an exception, this
name will appear in the stderr logging for the engine. See
`Future.name` for more information.
* __meta (any) - If provided, will assign this metadata to the returned
Future. This field is for your exclusive use.
* Everything else is passed to `func`.
Returns a Future of `func`'s result.
&emsp; **@staticmethod**<br>&mdash; **def [wait](/recipe_modules/futures/api.py#287)(futures, timeout=None, count=None):**
Blocks until `count` `futures` are done (or timeout occurs) then
returns the list of done futures.
This is analogous to `gevent.wait`.
Args:
* futures (List[Future]) - The Future objects to wait for.
* timeout (None|seconds) - How long to wait for the Futures to be done.
If we hit the timeout, wait will return even if we haven't reached
`count` Futures yet.
* count (None|int) - The number of Futures to wait to be done. If None,
waits for all of them.
Returns the list of done Futures, in the order in which they were done.
### *recipe_modules* / [generator\_script](/recipe_modules/generator_script)
[DEPS](/recipe_modules/generator_script/__init__.py#7): [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
A simplistic method for running steps generated by an external script.
This module was created before there was a way to put recipes directly into
another repo. It is not recommended to use this, and it will be removed in the
near future.
#### **class [GeneratorScriptApi](/recipe_modules/generator_script/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [\_\_call\_\_](/recipe_modules/generator_script/api.py#44)(self, path_to_script, \*args):**
Run a script and generate the steps emitted by that script.
The script will be invoked with --output-json /path/to/file.json. The script
is expected to exit 0 and write steps into that file. Once the script
outputs all of the steps to that file, the recipe will read the steps from
that file and execute them in order. Any *args specified will be
additionally passed to the script.
The step data is formatted as a list of JSON objects. Each object
corresponds to one step, and contains the following keys:
* name: the name of this step.
* cmd: a list of strings that indicate the command to run (e.g. argv)
* env: a {key:value} dictionary of the environment variables to override.
every value is formatted with the current environment with the python
% operator, so a value of "%(PATH)s:/some/other/path" would resolve to
the current PATH value, concatenated with ":/some/other/path"
* cwd: an absolute path to the current working directory for this script.
* always_run: a bool which indicates that this step should run, even if
some previous step failed.
* outputs_presentation_json: a bool which indicates that this step will
emit a presentation json file. If this is True, the cmd will be extended
with a `--presentation-json /path/to/file.json`. This file will be used
to update the step's presentation on the build status page. The file
will be expected to contain a single json object, with any of the
following keys:
* logs: {logname: [lines]} specifies one or more auxiliary logs.
* links: {link_name: link_content} to add extra links to the step.
* step_summary_text: A string to set as the step summary.
* step_text: A string to set as the step text.
* properties: {prop: value} build_properties to add to the build
status page. Note that these are write-only: The only way to read
them is via the status page. There is intentionally no mechanism to
read them back from inside of the recipes.
### *recipe_modules* / [golang](/recipe_modules/golang)
[DEPS](/recipe_modules/golang/__init__.py#7): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [path](#recipe_modules-path), [platform](#recipe_modules-platform)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [GolangApi](/recipe_modules/golang/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@contextlib.contextmanager**<br>&mdash; **def [\_\_call\_\_](/recipe_modules/golang/api.py#15)(self, version, path=None, cache=None):**
Installs a Golang SDK and activates it in the environment.
Installs it under the given `path`, defaulting to `[CACHE]/golang`. Various
cache directories used by Go are placed under `cache`, defaulting to
`[CACHE]/gocache`.
`version` will be used to construct CIPD package version for packages under
https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/go/.
To reuse the Go SDK deployment and caches across builds, declare the
corresponding named caches in Buildbucket configs. E.g. when using defaults:
luci.builder(
...
caches = [
swarming.cache("golang"),
swarming.cache("gocache"),
],
)
Note: CGO is disabled on Windows currently, since Windows doesn't have
a C compiler available by default.
Args:
* version (str) - a Go version to install (e.g. `1.16.10`).
* path (Path) - a path to install Go into.
* cache (Path) - a path to put Go caches under.
### *recipe_modules* / [json](/recipe_modules/json)
[DEPS](/recipe_modules/json/__init__.py#7): [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Methods for producing and consuming JSON.
#### **class [JsonApi](/recipe_modules/json/api.py#134)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@staticmethod**<br>&mdash; **def [dumps](/recipe_modules/json/api.py#135)(\*args, \*\*kwargs):**
Works like `json.dumps`.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&mdash; **def [input](/recipe_modules/json/api.py#158)(self, data):**
A placeholder which will expand to a file path containing <data>.
&mdash; **def [is\_serializable](/recipe_modules/json/api.py#150)(self, obj):**
Returns True if the object is JSON-serializable.
&emsp; **@staticmethod**<br>&mdash; **def [loads](/recipe_modules/json/api.py#140)(data, \*\*kwargs):**
Works like `json.loads`, but:
* strips out unicode objects (replacing them with utf8-encoded str
objects).
* replaces 'int-like' floats with ints. These are floats whose magnitude
is less than (2**53-1) and which don't have a decimal component.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&mdash; **def [output](/recipe_modules/json/api.py#163)(self, add_json_log=True, name=None, leak_to=None):**
A placeholder which will expand to '/tmp/file'.
If leak_to is provided, it must be a Path object. This path will be used in
place of a random temporary file, and the file will not be deleted at the
end of the step.
Args:
* add_json_log (True|False|'on_failure') - Log a copy of the output json
to a step link named `name`. If this is 'on_failure', only create this
log when the step has a non-SUCCESS status.
&mdash; **def [read](/recipe_modules/json/api.py#178)(self, name, path, add_json_log=True, output_name=None, \*\*kwargs):**
Returns a step that reads a JSON file.
*** note
**DEPRECATED**: Use file.read_json instead.
***
### *recipe_modules* / [led](/recipe_modules/led)
[DEPS](/recipe_modules/led/__init__.py#7): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [proto](#recipe_modules-proto), [step](#recipe_modules-step), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
An interface to call the led tool.
#### **class [LedApi](/recipe_modules/led/api.py#21)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Interface to the led tool.
"led" stands for LUCI editor. It allows users to debug and modify LUCI jobs.
It can be used to modify many aspects of a LUCI build, most commonly
including the recipes used.
The main interface this module provides is a direct call to the led binary:
led_result = api.led(
'get-builder', ['luci.chromium.try:chromium_presubmit'])
final_data = led_result.then('edit-recipe-bundle').result
See the led binary for full documentation of commands.
&mdash; **def [\_\_call\_\_](/recipe_modules/led/api.py#158)(self, \*cmd):**
Runs led with the given arguments. Wraps result in a `LedResult`.
&emsp; **@property**<br>&mdash; **def [cipd\_input](/recipe_modules/led/api.py#150)(self):**
The versioned CIPD package containing the recipes code being run.
If set, it will be an `InputProperties.CIPDInput` protobuf; otherwise None.
&mdash; **def [initialize](/recipe_modules/led/api.py#112)(self):**
&mdash; **def [inject\_input\_recipes](/recipe_modules/led/api.py#162)(self, led_result):**
Sets the version of recipes used by led to correspond to the version
currently being used.
If neither the `rbe_cas_input` nor the `cipd_input` property is set,
this is a no-op.
Args:
* led_result: The `LedResult` whose job.Definition will be passed into the
edit command.
&emsp; **@property**<br>&mdash; **def [launched\_by\_led](/recipe_modules/led/api.py#128)(self):**
Whether the current build is a led job.
&emsp; **@property**<br>&mdash; **def [rbe\_cas\_input](/recipe_modules/led/api.py#141)(self):**
The location of the rbe-cas containing the recipes code being run.
If set, it will be a `swarming.v1.CASReference` protobuf;
otherwise, None.
&emsp; **@property**<br>&mdash; **def [run\_id](/recipe_modules/led/api.py#133)(self):**
A unique string identifier for this led job.
If the current build is *not* a led job, value will be an empty string.
&mdash; **def [trigger\_builder](/recipe_modules/led/api.py#187)(self, project_name, bucket_name, builder_name, properties):**
Trigger a builder using led.
This can be used by recipes instead of buildbucket or scheduler triggers
in case the running build was triggered by led.
This is equivalent to:
led get-builder project/bucket:builder | <inject_input_recipes> | led edit <properties> | led launch
Args:
* project_name - The project that defines the builder.
* bucket_name - The bucket that configures the builder.
* builder_name - Name of the builder to trigger.
* properties - Dict with properties to pass to the triggered build.
### *recipe_modules* / [legacy\_annotation](/recipe_modules/legacy_annotation)
[DEPS](/recipe_modules/legacy_annotation/__init__.py#7): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Legacy Annotation module provides support for running a command emitting
legacy @@@annotation@@@ in the new luciexe mode.
The output annotations is converted to a build proto and all steps in the build
will appear as the child steps of the launched cmd/step in the current running
build (using the Merge Step feature from luciexe protocol). This is the
replacement for allow_subannotation feature in the legacy annotate mode.
#### **class [LegacyAnnotationApi](/recipe_modules/legacy_annotation/api.py#24)([RecipeApiPlain](/recipe_engine/recipe_api.py#738)):**
&mdash; **def [\_\_call\_\_](/recipe_modules/legacy_annotation/api.py#28)(self, name, cmd, timeout=None, step_test_data=None, cost=_ResourceCost(), legacy_global_namespace=False):**
Runs cmd that is emitting legacy @@@annotation@@@.
Currently, it will run the command as sub_build if running in luciexe
mode or simulation mode. Otherwise, it will fall back to launch a step
with allow_subannotation set to true.
If `legacy_global_namespace` is True, this enables an even more-legacy
global namespace merging mode. Do not enable this. See crbug.com/1310155.
### *recipe_modules* / [milo](/recipe_modules/milo)
[DEPS](/recipe_modules/milo/__init__.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step), [uuid](#recipe_modules-uuid)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for specifying Milo behavior.
#### **class [MiloApi](/recipe_modules/milo/api.py#17)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for interacting with Milo.
&mdash; **def [show\_blamelist\_for](/recipe_modules/milo/api.py#20)(self, gitiles_commits):**
Specifies which commits and repos Milo should show a blamelist for.
If not set, Milo will only show a blamelist for the main repo in which this
build was run.
Args:
gitiles_commits: A list of buildbucket.common_pb2.GitilesCommit messages
or dicts of the same structure.
Each commit must have host, project and id.
ID must match r'^[0-9a-f]{40}$' (git revision).
### *recipe_modules* / [nodejs](/recipe_modules/nodejs)
[DEPS](/recipe_modules/nodejs/__init__.py#7): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [path](#recipe_modules-path), [platform](#recipe_modules-platform)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [NodeJSApi](/recipe_modules/nodejs/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@contextlib.contextmanager**<br>&mdash; **def [\_\_call\_\_](/recipe_modules/nodejs/api.py#15)(self, version, path=None, cache=None):**
Installs a Node.js toolchain and activates it in the environment.
Installs it under the given `path`, defaulting to `[CACHE]/nodejs`. Various
cache directories used by npm are placed under `cache`, defaulting to
`[CACHE]/npmcache`.
`version` will be used to construct CIPD package version for packages under
https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/nodejs/.
To reuse the Node.js toolchain deployment and npm caches across builds,
declare the corresponding named caches in Buildbucket configs. E.g. when
using defaults:
luci.builder(
...
caches = [
swarming.cache("nodejs"),
swarming.cache("npmcache"),
],
)
Args:
* version (str) - a Node.js version to install (e.g. `17.1.0`).
* path (Path) - a path to install Node.js into.
* cache (Path) - a path to put Node.js caches under.
### *recipe_modules* / [path](/recipe_modules/path)
[DEPS](/recipe_modules/path/__init__.py#7): [platform](#recipe_modules-platform)
PYTHON_VERSION_COMPATIBILITY: PY2+3
All functions related to manipulating paths in recipes.
Recipes handle paths a bit differently than python does. All path manipulation
in recipes revolves around Path objects. These objects store a base path (always
absolute), plus a list of components to join with it. New paths can be derived
by calling the .join method with additional components.
In this way, all paths in Recipes are absolute, and are constructed from a small
collection of anchor points. The built-in anchor points are:
* `api.path['start_dir']` - This is the directory that the recipe started in.
it's similar to `cwd`, except that it's constant.
* `api.path['cache']` - This directory is provided by whatever's running the
recipe. Files and directories created under here /may/ be evicted in between
runs of the recipe (i.e. to relieve disk pressure).
* `api.path['cleanup']` - This directory is provided by whatever's running the
recipe. Files and directories created under here /are guaranteed/ to be
evicted in between runs of the recipe. Additionally, this directory is
guaranteed to be empty when the recipe starts.
* `api.path['tmp_base']` - This directory is the system-configured temp dir.
This is a weaker form of 'cleanup', and its use should be avoided. This may
be removed in the future (or converted to an alias of 'cleanup').
* `api.path['checkout']` - This directory is set by various 'checkout' modules
in recipes. It was originally intended to make recipes easier to read and
make code somewhat generic or homogeneous, but this was a mistake. New code
should avoid 'checkout', and instead just explicitly pass paths around. This
path may be removed in the future.
There are other anchor points which can be defined (e.g. by the
`depot_tools/infra_paths` module). Refer to those modules for additional
documentation.
#### **class [PathApi](/recipe_modules/path/api.py#225)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [\_\_getitem\_\_](/recipe_modules/path/api.py#464)(self, name):**
Gets the base path named `name`. See module docstring for more
information.
&mdash; **def [abs\_to\_path](/recipe_modules/path/api.py#391)(self, abs_string_path):**
Converts an absolute path string `string_path` to a real Path object,
using the most appropriate known base path.
* abs_string_path MUST be an absolute path
* abs_string_path MUST be rooted in one of the configured base paths known
to the path module.
This method will find the longest match in all the following:
* module resource paths
* recipe resource paths
* repo paths
* dynamic_paths
* base_paths
Example:
```
# assume [START_DIR] == "/basis/dir/for/recipe"
api.path.abs_to_path("/basis/dir/for/recipe/some/other/dir") ->
Path("[START_DIR]/some/other/dir")
```
Raises an ValueError if the preconditions are not met, otherwise returns the
Path object.
&mdash; **def [abspath](/recipe_modules/path/api.py#487)(self, path):**
Equivalent to os.path.abspath.
&mdash; **def [assert\_absolute](/recipe_modules/path/api.py#330)(self, path):**
Raises AssertionError if the given path is not an absolute path.
Args:
* path (Path|str) - The path to check.
&mdash; **def [basename](/recipe_modules/path/api.py#491)(self, path):**
Equivalent to os.path.basename.
&mdash; **def [dirname](/recipe_modules/path/api.py#495)(self, path):**
For "foo/bar/baz", return "foo/bar".
This corresponds to os.path.dirname().
The type of the return value matches the type of the argument.
Args:
path (Path or str): path to take directory name of
Returns dirname of path
&mdash; **def [exists](/recipe_modules/path/api.py#592)(self, path):**
Equivalent to os.path.exists.
The presence or absence of paths can be mocked during the execution of the
recipe by using the mock_* methods.
&mdash; **def [expanduser](/recipe_modules/path/api.py#583)(self, path):**
Do not use this, use `api.path['home']` instead.
This ONLY handles `path` == "~", and returns `str(api.path['home'])`.
&mdash; **def [get](/recipe_modules/path/api.py#457)(self, name, default=None):**
Gets the base path named `name`. See module docstring for more
information.
&mdash; **def [get\_config\_defaults](/recipe_modules/path/api.py#228)(self):**
Internal recipe implementation function.
&mdash; **def [initialize](/recipe_modules/path/api.py#294)(self):**
Internal recipe implementation function.
&mdash; **def [isdir](/recipe_modules/path/api.py#600)(self, path):**
Equivalent to os.path.isdir.
The presence or absence of paths can be mocked during the execution of the
recipe by using the mock_* methods.
&mdash; **def [isfile](/recipe_modules/path/api.py#608)(self, path):**
Equivalent to os.path.isfile.
The presence or absence of paths can be mocked during the execution of the
recipe by using the mock_* methods.
&mdash; **def [join](/recipe_modules/path/api.py#514)(self, path, \*paths):**
Equivalent to os.path.join.
Note that Path objects returned from this module (e.g.
api.path['start_dir']) have a built-in join method (e.g.
new_path = p.join('some', 'name')). Many recipe modules expect Path objects
rather than strings. Using this `join` method gives you raw path joining
functionality and returns a string.
If your path is rooted in one of the path module's root paths (i.e. those
retrieved with api.path[something]), then you can convert from a string path
back to a Path with the `abs_to_path` method.
&mdash; **def [mkdtemp](/recipe_modules/path/api.py#339)(self, prefix=tempfile.template):**
Makes a new temporary directory, returns Path to it.
Args:
* prefix (str) - a tempfile template for the directory name (defaults
to "tmp").
Returns a Path to the new directory.
&mdash; **def [mkstemp](/recipe_modules/path/api.py#364)(self, prefix=tempfile.template):**
Makes a new temporary file, returns Path to it.
Args:
* prefix (str) - a tempfile template for the file name (defaults to
"tmp").
Returns a Path to the new file. Unlike tempfile.mkstemp, the file's file
descriptor is closed.
&mdash; **def [mock\_add\_directory](/recipe_modules/path/api.py#625)(self, path):**
For testing purposes, mark that directory |path| exists.
&mdash; **def [mock\_add\_file](/recipe_modules/path/api.py#621)(self, path):**
For testing purposes, mark that file |path| exists.
&mdash; **def [mock\_add\_paths](/recipe_modules/path/api.py#616)(self, path, kind=FILE):**
For testing purposes, mark that |path| exists.
&mdash; **def [mock\_copy\_paths](/recipe_modules/path/api.py#629)(self, source, dest):**
For testing purposes, copy |source| to |dest|.
&mdash; **def [mock\_remove\_paths](/recipe_modules/path/api.py#634)(self, path, filt=(lambda p: True)):**
For testing purposes, assert that |path| doesn't exist.
Args:
* path (str|Path): The path to remove.
* filt (func[str] bool): Called for every candidate path. Return
True to remove this path.
&emsp; **@property**<br>&mdash; **def [pardir](/recipe_modules/path/api.py#472)(self):**
Equivalent to os.path.pardir.
&emsp; **@property**<br>&mdash; **def [pathsep](/recipe_modules/path/api.py#482)(self):**
Equivalent to os.path.pathsep.
&mdash; **def [realpath](/recipe_modules/path/api.py#571)(self, path):**
Equivalent to os.path.realpath.
&mdash; **def [relpath](/recipe_modules/path/api.py#575)(self, path, start):**
Roughly equivalent to os.path.relpath.
Unlike os.path.relpath, `start` is _required_. If you want the 'current
directory', use the `recipe_engine/context` module's `cwd` property.
&emsp; **@property**<br>&mdash; **def [sep](/recipe_modules/path/api.py#477)(self):**
Equivalent to os.path.sep.
&mdash; **def [split](/recipe_modules/path/api.py#529)(self, path):**
For "foo/bar/baz", return ("foo/bar", "baz").
This corresponds to os.path.split().
The type of the first item in the return value matches the type of the
argument.
Args:
path (Path or str): path to split into directory name and basename
Returns (dirname(path), basename(path)).
&mdash; **def [splitext](/recipe_modules/path/api.py#550)(self, path):**
For "foo/bar.baz", return ("foo/bar", ".baz").
This corresponds to os.path.splitext().
The type of the first item in the return value matches the type of the
argument.
Args:
path (Path or str): path to split into name and extension
Returns (name, extension_including_dot).
### *recipe_modules* / [platform](/recipe_modules/platform)
[DEPS](/recipe_modules/platform/__init__.py#7): [version](#recipe_modules-version)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Mockable system platform identity functions.
#### **class [PlatformApi](/recipe_modules/platform/api.py#24)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
Provides host-platform-detection properties.
Mocks:
* name (str): A value equivalent to something that might be returned by
sys.platform.
* bits (int): Either 32 or 64.
&emsp; **@property**<br>&mdash; **def [arch](/recipe_modules/platform/api.py#122)(self):**
Returns the current CPU architecture.
Can return "arm" or "intel".
&emsp; **@property**<br>&mdash; **def [bits](/recipe_modules/platform/api.py#112)(self):**
Returns the bitness of the userland for the current system (either 32 or
64 bit).
TODO: If anyone needs to query for the kernel bitness, another accessor
should be added.
&emsp; **@property**<br>&mdash; **def [cpu\_count](/recipe_modules/platform/api.py#139)(self):**
The number of logical CPU cores (i.e. including hyper-threaded cores),
according to `psutil.cpu_count(True)`.
&mdash; **def [initialize](/recipe_modules/platform/api.py#34)(self):**
&emsp; **@property**<br>&mdash; **def [is\_linux](/recipe_modules/platform/api.py#98)(self):**
Returns True iff the recipe is running on Linux.
&emsp; **@property**<br>&mdash; **def [is\_mac](/recipe_modules/platform/api.py#83)(self):**
Returns True iff the recipe is running on OS X.
&emsp; **@property**<br>&mdash; **def [is\_win](/recipe_modules/platform/api.py#78)(self):**
Returns True iff the recipe is running on Windows.
&emsp; **@property**<br>&mdash; **def [mac\_release](/recipe_modules/platform/api.py#88)(self):**
The current OS X release version number (like "10.13.5") as a
pkg_resources Version object, or None, if the current platform is not mac.
Use the "recipe_engine/version" module to parse symvers to compare to this
Version object.
&emsp; **@property**<br>&mdash; **def [name](/recipe_modules/platform/api.py#103)(self):**
Returns the current platform name which will be in:
* win
* mac
* linux
&emsp; **@staticmethod**<br>&mdash; **def [normalize\_platform\_name](/recipe_modules/platform/api.py#145)(plat):**
One of python's sys.platform values -> 'win', 'linux' or 'mac'.
&emsp; **@property**<br>&mdash; **def [total\_memory](/recipe_modules/platform/api.py#130)(self):**
The total physical memory in MiB.
Return type is int.
This is equivalent to `psutil.virtual_memory().total / (1024 ** 2)`.
### *recipe_modules* / [properties](/recipe_modules/properties)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Provides access to the recipes input properties.
Every recipe is run with a JSON object called "properties". These contain all
inputs to the recipe. Some common examples would be properties like "revision",
which the build scheduler sets to tell a recipe to build/test a certain
revision.
The properties that affect a particular recipe are defined by the recipe itself,
and this module provides access to them.
Recipe properties are read-only; the values obtained via this API reflect the
values provided to the recipe engine at the beginning of execution. There is
intentionally no API to write property values (lest they become a kind of
random-access global variable).
#### **class [PropertiesApi](/recipe_modules/properties/api.py#30)([RecipeApiPlain](/recipe_engine/recipe_api.py#738), collections.Mapping):**
PropertiesApi implements all the standard Mapping functions, so you
can use it like a read-only dict.
&mdash; **def [legacy](/recipe_modules/properties/api.py#56)(self):**
*** note
**DEPRECATED**: Returns a set of properties, possibly used by legacy
scripts.
***
This excludes any recipe module-specific properties (i.e. those beginning
with `$`).
Instead of passing all of the properties as a blob, please consider passing
specific arguments to scripts that need them. Doing this makes it much
easier to debug and diagnose which scripts use which properties.
&mdash; **def [thaw](/recipe_modules/properties/api.py#80)(self):**
Returns a read-write copy of all of the properties.
### *recipe_modules* / [proto](/recipe_modules/proto)
[DEPS](/recipe_modules/proto/__init__.py#7): [raw\_io](#recipe_modules-raw_io)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Methods for producing and consuming protobuf data to/from steps and the
filesystem.
#### **class [ProtoApi](/recipe_modules/proto/api.py#83)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@staticmethod**<br>&mdash; **def [decode](/recipe_modules/proto/api.py#161)(data, msg_class, codec, \*\*decoding_kwargs):**
Decodes a proto message from a string.
Args:
* msg_class (protobuf Message subclass) - The message type to decode.
* codec ('BINARY'|'JSONPB'|'TEXTPB') - The encoder to use.
* decoding_kwargs - Passed directly to the chosen decoder. See input
placeholder for details.
Returns the decoded proto object.
&emsp; **@staticmethod**<br>&mdash; **def [encode](/recipe_modules/proto/api.py#146)(proto_msg, codec, \*\*encoding_kwargs):**
Encodes a proto message to a string.
Args:
* codec ('BINARY'|'JSONPB'|'TEXTPB') - The encoder to use.
* encoding_kwargs - Passed directly to the chosen encoder. See output
placeholder for details.
Returns the encoded proto message.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&mdash; **def [input](/recipe_modules/proto/api.py#85)(self, proto_msg, codec, \*\*encoding_kwargs):**
A placeholder which will expand to a file path containing the encoded
`proto_msg`.
Example:
proto_msg = MyMessage(field=10)
api.step('step name', ['some_cmd', api.proto.input(proto_msg)])
# some_cmd sees "/path/to/random.pb"
Args:
* proto_msg (message.Message) - The message data to encode.
* codec ('BINARY'|'JSONPB'|'TEXTPB') - The encoder to use.
* encoding_kwargs - Passed directly to the chosen encoder. See:
- BINARY: google.protobuf.message.Message.SerializeToString
* 'deterministic' defaults to True.
- JSONPB: google.protobuf.json_format.MessageToJson
* 'preserving_proto_field_name' defaults to True.
* 'sort_keys' defaults to True.
* 'indent' defaults to 0.
- TEXTPB: google.protobuf.text_format.MessageToString
Returns an InputPlaceholder.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&mdash; **def [output](/recipe_modules/proto/api.py#116)(self, msg_class, codec, add_json_log=True, name=None, leak_to=None, \*\*decoding_kwargs):**
A placeholder which expands to a file path and then reads an encoded
proto back from that location when the step finishes.
Args:
* msg_class (protobuf Message subclass) - The message type to decode.
* codec ('BINARY'|'JSONPB'|'TEXTPB') - The encoder to use.
* add_json_log (True|False|'on_failure') - Log a copy of the parsed proto
in JSONPB form to a step link named `name`. If this is 'on_failure',
only create this log when the step has a non-SUCCESS status.
* leak_to (Optional[Path]) - This path will be used in place of a random
temporary file, and the file will not be deleted at the end of the step.
* decoding_kwargs - Passed directly to the chosen decoder. See:
- BINARY: google.protobuf.message.Message.Parse
- JSONPB: google.protobuf.json_format.Parse
* 'ignore_unknown_fields' defaults to True.
- TEXTPB: google.protobuf.text_format.Parse
### *recipe_modules* / [python](/recipe_modules/python)
[DEPS](/recipe_modules/python/__init__.py#11): [context](#recipe_modules-context), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step), [warning](#recipe_modules-warning)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Provides methods for running python scripts correctly.
This includes support for `vpython`, and knows how to specify parameters
correctly for bots (e.g. ensuring that python is working on Windows, passing the
unbuffered flag, etc.)
#### **class [PythonApi](/recipe_modules/python/api.py#20)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
*** note
**DEPRECATED**: Directly invoke python instead of using this module.
***
&mdash; **def [\_\_call\_\_](/recipe_modules/python/api.py#24)(self, name, script, args=None, unbuffered=True, venv=None, \*\*kwargs):**
Return a step to run a python script with arguments.
**TODO**: We should just use a single "args" list. Having "script"
separate but required/first leads to weird things like:
(... script='-m', args=['module'])
Args:
* name (str): The name of the step.
* script (str or Path): The Path of the script to run, or the first
command-line argument to pass to Python.
* args (list or None): If not None, additional arguments to pass to the
Python command.
* unbuffered (bool): If True, run Python in unbuffered mode.
* venv (None or False or True or Path): If True, run the script through
"vpython". This will, by default, probe the target script for a
configured VirtualEnv and, failing that, use an empty VirtualEnv. If a
Path, this is a path to an explicit "vpython" VirtualEnv spec file to
use. If False or None (default), the script will be run through the
standard Python interpreter.
* kwargs: Additional keyword arguments to forward to "step".
**Returns (`step_data.StepData`)** - The StepData object as returned by
api.step.
&emsp; **@recipe_api.ignore_warnings('recipe_engine/PYTHON_RESULT_STEP_DEPRECATED')**<br>&mdash; **def [failing\_step](/recipe_modules/python/api.py#141)(self, name, text, as_log=None):**
Runs a failing step (exits 1).
*** note
**DEPRECATED**: crbug.com/1276131
***
&emsp; **@recipe_api.ignore_warnings('recipe_engine/PYTHON_RESULT_STEP_DEPRECATED')**<br>&mdash; **def [infra\_failing\_step](/recipe_modules/python/api.py#151)(self, name, text, as_log=None):**
Runs an infra-failing step (exits 1).
*** note
**DEPRECATED**: crbug.com/1276131
***
&emsp; **@recipe_api.ignore_warnings('recipe_engine/PYTHON_CALL_DEPRECATED')**<br>&mdash; **def [inline](/recipe_modules/python/api.py#69)(self, name, program, add_python_log=True, \*\*kwargs):**
Run an inline python program as a step.
Program is output to a temp file and run when this step executes.
Args:
* name (str) - The name of the step
* program (str) - The literal python program text. This will be dumped to
a file and run like `python /path/to/file.py`
* add_python_log (bool) - Whether to add a 'python.inline' link on this
step on the build page. If true, the link will point to a log with
a copy of `program`.
**Returns (`step_data.StepData`)** - The StepData object as returned by
api.step.
&mdash; **def [result\_step](/recipe_modules/python/api.py#103)(self, name, text, retcode, as_log=None, \*\*kwargs):**
Runs a no-op step that exits with a specified return code.
*** note
**DEPRECATED**: crbug.com/1276131
***
The recipe engine will raise an exception when seeing a return code != 0.
The text is expected to be str. Passing a list of lines(str) works but is
discouraged and may be deprecated in the future. Please concatenate the
lines with newline character instead.
&emsp; **@recipe_api.ignore_warnings('recipe_engine/PYTHON_RESULT_STEP_DEPRECATED')**<br>&mdash; **def [succeeding\_step](/recipe_modules/python/api.py#131)(self, name, text, as_log=None):**
Runs a succeeding step (exits 0).
*** note
**DEPRECATED**: crbug.com/1276131
***
### *recipe_modules* / [random](/recipe_modules/random)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Allows randomness in recipes.
This module sets up an internal instance of 'random.Random'. In tests, this is
seeded with `1234`, or a seed of your choosing (using the test_api's `seed()`
method)
All members of `random.Random` are exposed via this API with getattr.
*** promo
NOTE: This is based on the python `random` module, and so all caveats which
apply there also apply to this (i.e. don't use it for anything resembling
crypto).
***
Example:
def RunSteps(api):
my_list = range(100)
api.random.shuffle(my_list)
# my_list is now random!
#### **class [RandomApi](/recipe_modules/random/api.py#56)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [\_\_getattr\_\_](/recipe_modules/random/api.py#63)(self, name):**
Access a member of `random.Random`.
### *recipe_modules* / [raw\_io](/recipe_modules/raw_io)
[DEPS](/recipe_modules/raw_io/__init__.py#7): [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Provides objects for reading and writing raw data to and from steps.
#### **class [RawIOApi](/recipe_modules/raw_io/api.py#330)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&emsp; **@staticmethod**<br>&mdash; **def [input](/recipe_modules/raw_io/api.py#331)(data, suffix='', name=None):**
Returns a Placeholder for use as a step argument.
This placeholder can be used to pass data to steps. The recipe engine will
dump the 'data' into a file, and pass the filename to the command line
argument.
data MUST be either of type 'bytes' (recommended) or type 'str' in Python 3.
Respectively, 'str' or 'unicode' in Python 2.
If the provided data is of type 'str', it is encoded to bytes assuming
utf-8 encoding. Please switch to `input_text(...)` instead in this case.
If 'suffix' is not '', it will be used when the engine calls
tempfile.mkstemp.
See examples/full.py for usage example.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&emsp; **@staticmethod**<br>&mdash; **def [input\_text](/recipe_modules/raw_io/api.py#358)(data, suffix='', name=None):**
Returns a Placeholder for use as a step argument.
Similar to input(), but ensures that 'data' is valid utf-8 text. Any
non-utf-8 characters will be replaced with �.
data MUST be either of type 'bytes' or type 'str' (recommended) in Python 3.
Respectively, 'str' or 'unicode' in Python 2.
If the provided data is of type 'bytes', it is expected to be valid utf-8
encoded data. Note that, the support of type 'bytes' is for backwards
compatibility to Python 2, we may drop this support in the future after
recipe becomes Python 3 only.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&emsp; **@staticmethod**<br>&mdash; **def [output](/recipe_modules/raw_io/api.py#381)(suffix='', leak_to=None, name=None, add_output_log=False):**
Returns a Placeholder for use as a step argument, or for std{out,err}.
If 'leak_to' is None, the placeholder is backed by a temporary file with
a suffix 'suffix'. The file is deleted when the step finishes.
If 'leak_to' is not None, then it should be a Path and placeholder
redirects IO to a file at that path. Once step finishes, the file is
NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case.
Args:
* add_output_log (True|False|'on_failure') - Log a copy of the output
to a step link named `name`. If this is 'on_failure', only create this
log when the step has a non-SUCCESS status.
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&mdash; **def [output\_dir](/recipe_modules/raw_io/api.py#419)(self, leak_to=None, name=None):**
Returns a directory Placeholder for use as a step argument.
If `leak_to` is None, the placeholder is backed by a temporary dir.
Otherwise `leak_to` must be a Path; if the path doesn't exist, it will be
created.
The placeholder value attached to the step will be a dictionary-like mapping
of relative paths to the contents of the file. The actual reading of the
file data is done lazily (i.e. on first access).
Relative paths are stored with the native slash delimitation (i.e. forward
slash on *nix, backslash on Windows).
Example:
```python
result = api.step('name', [..., api.raw_io.output_dir()])
# some time later; The read of 'some/file' happens now:
some_file = api.path.join('some', 'file')
assert result.raw_io.output_dir[some_file] == 'contents of some/file'
# data for 'some/file' is cached now; To free it from memory (and make
# all further reads of 'some/file' an error):
del result.raw_io.output_dir[some_file]
result.raw_io.output_dir[some_file] -> raises KeyError
```
&emsp; **@[returns\_placeholder](/recipe_engine/util.py#156)**<br>&emsp; **@staticmethod**<br>&mdash; **def [output\_text](/recipe_modules/raw_io/api.py#401)(suffix='', leak_to=None, name=None, add_output_log=False):**
Returns a Placeholder for use as a step argument, or for std{out,err}.
Similar to output(), but uses an OutputTextPlaceholder, which expects utf-8
encoded text.
Similar to input(), but tries to decode the resulting data as utf-8 text,
replacing any decoding errors with �.
Args:
* add_output_log (True|False|'on_failure') - Log a copy of the output
to a step link named `name`. If this is 'on_failure', only create this
log when the step has a non-SUCCESS status.
### *recipe_modules* / [resultdb](/recipe_modules/resultdb)
[DEPS](/recipe_modules/resultdb/__init__.py#7): [context](#recipe_modules-context), [futures](#recipe_modules-futures), [json](#recipe_modules-json), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step), [time](#recipe_modules-time), [uuid](#recipe_modules-uuid)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for interacting with the ResultDB service.
Requires `rdb` command in `$PATH`:
https://godoc.org/go.chromium.org/luci/resultdb/cmd/rdb
#### **class [ResultDBAPI](/recipe_modules/resultdb/api.py#29)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for interacting with ResultDB.
&mdash; **def [assert\_enabled](/recipe_modules/resultdb/api.py#51)(self):**
&mdash; **def [config\_test\_presentation](/recipe_modules/resultdb/api.py#563)(self, column_keys=(), grouping_keys=('status',)):**
Specifies how the test results should be rendered.
Args:
column_keys:
A list of keys that will be rendered as 'columns'. status is always the
first column and name is always the last column (you don't need to
specify them). A key must be one of the following:
1. 'v.{variant_key}': variant.def[variant_key] of the test variant (e.g.
v.gpu).
grouping_keys:
A list of keys that will be used for grouping tests. A key must be one
of the following:
1. 'status': status of the test variant.
2. 'name': name of the test variant.
3. 'v.{variant_key}': variant.def[variant_key] of the test variant (e.g.
v.gpu).
Caveat: test variants with only expected results are not affected by
this setting and are always in their own group.
&emsp; **@property**<br>&mdash; **def [current\_invocation](/recipe_modules/resultdb/api.py#43)(self):**
&emsp; **@property**<br>&mdash; **def [enabled](/recipe_modules/resultdb/api.py#47)(self):**
&mdash; **def [exclude\_invocations](/recipe_modules/resultdb/api.py#62)(self, invocations, step_name=None):**
Shortcut for resultdb.update_included_invocations().
&mdash; **def [exonerate](/recipe_modules/resultdb/api.py#128)(self, test_exonerations, step_name=None):**
Exonerates test variants in the current invocation.
Args:
test_exonerations (list): A list of test_result_pb2.TestExoneration.
step_name (str): name of the step.
&mdash; **def [get\_included\_invocations](/recipe_modules/resultdb/api.py#103)(self, inv_name=None, step_name=None):**
Returns names of included invocations of the input invocation.
Args:
inv_name (str): the name of the input invocation. If input is None, will
use current invocation.
step_name (str): name of the step.
Returns:
A list of invocation name strs.
&mdash; **def [get\_test\_result\_history](/recipe_modules/resultdb/api.py#254)(self, realm, test_id_regexp, variant_predicate=None, time_range=None, page_size=10, page_token=None, step_name=None):**
Receive test results for a given configuration.
Makes a call to GetTestResultHistory rpc. Allows for test retrieval for
all invocations with the specified configurations.
Args:
realm (str): the realm that the data being queried exists in.
Example: "chromium:ci".
test_id_regexp (str): the subset of test ids to request history for.
variant_predicate (resultdb.proto.v1.predicate.VariantPredicate):
the subset of test variants to request history for. Defaults to None,
but specifying will improve runtime.
time_range (common_v1.TimeRange): a range of timestamps in which to
request history from. If no time_range is specified, it defaults to the
past day.
page_size (int): the number of results per page in the response. If the
number of results satisfying the given configuration exceeds this
number, only the page_size results will be available in the response.
Defaults to 10 results.
page_token (str): for instances in which the results span multiple pages,
each response will contain a page token for the next page, which can be
passed in to the next request. Defaults to None, which returns the first
page.
step_name (str): name of the step.
Returns:
A GetTestResultHistoryResponse proto message with entries for each test
matching the configuration.
For value format, see [`GetTestResultHistoryResponse` message]
(https://bit.ly/3bSXxU1)
&mdash; **def [include\_invocations](/recipe_modules/resultdb/api.py#57)(self, invocations, step_name=None):**
Shortcut for resultdb.update_included_invocations().
&mdash; **def [invocation\_ids](/recipe_modules/resultdb/api.py#170)(self, inv_names):**
Returns invocation ids by parsing invocation names.
Args:
inv_names (list of str): resultdb invocation names.
Returns:
A list of invocation_ids.
&mdash; **def [query](/recipe_modules/resultdb/api.py#186)(self, inv_ids, variants_with_unexpected_results=False, merge=False, limit=None, step_name=None, tr_fields=None):**
Returns test results in the invocations.
Most users will be interested only in results of test variants that had
unexpected results. This can be achieved by passing
variants_with_unexpected_results=True. This significantly reduces output
size and latency.
Example:
results = api.resultdb.query(
[
# invocation id for a swarming task.
'task-chromium-swarm.appspot.com-deadbeef',
# invocation id for a buildbucket build.
'build-234298374982'
],
variants_with_unexpected_results=True,
)
Args:
inv_ids (list of str): ids of the invocations.
variants_with_unexpected_results (bool): if True, return only test
results from variants that have unexpected results.
merge (bool): if True, return test results as if all invocations
are one, otherwise, results will be ordered by invocation.
limit (int): maximum number of test results to return.
Unlimited if 0. Defaults to 1000.
step_name (str): name of the step.
tr_fields (list of str): test result fields in the response.
Test result name will always be included regardless of this param value.
Returns:
A dict {invocation_id: api.Invocation}.
&mdash; **def [query\_test\_result\_statistics](/recipe_modules/resultdb/api.py#324)(self, invocations=None, step_name=None):**
Retrieve stats of test results for the given invocations.
Makes a call to the QueryTestResultStatistics API. Returns stats for all
given invocations, including those included indirectly.
Args:
invocations (list): A list of the invocations to query statistics for.
If None, the current invocation will be used.
step_name (str): name of the step.
Returns:
A QueryTestResultStatisticsResponse proto message with statistics for the
queried invocations.
&mdash; **def [update\_included\_invocations](/recipe_modules/resultdb/api.py#67)(self, add_invocations=None, remove_invocations=None, step_name=None):**
Add and/or remove included invocations to/from the current invocation.
Args:
add_invocations (list of str): invocation id's to add to the current
invocation.
remove_invocations (list of str): invocation id's to remove from the
current invocation.
This updates the inclusions of the current invocation specified in the
LUCI_CONTEXT.
&mdash; **def [upload\_invocation\_artifacts](/recipe_modules/resultdb/api.py#357)(self, artifacts, parent_inv=None, step_name=None):**
Create artifacts with the given content type and contents or gcs_uri.
Makes a call to the BatchCreateArtifacts API. Returns the created
artifacts.
Args:
artifacts (dict): a collection of artifacts to create. Each key is an
artifact id, with the corresponding value being a dict containing:
'content_type' (optional)
one of 'contents' (binary string) or 'gcs_uri' (str)
parent_inv (str): the name of the invocation to create the artifacts
under. If None, the current invocation will be used.
step_name (str): name of the step.
Returns:
A BatchCreateArtifactsResponse proto message listing the artifacts that
were created.
&mdash; **def [wrap](/recipe_modules/resultdb/api.py#464)(self, cmd, test_id_prefix='', base_variant=None, test_location_base='', base_tags=None, coerce_negative_duration=False, include=False, realm='', location_tags_file='', require_build_inv=True, exonerate_unexpected_pass=False):**
Wraps the command with ResultSink.
Returns a command that, when executed, runs cmd in a go/result-sink
environment. For example:
api.step('test', api.resultdb.wrap(['./my_test']))
Args:
cmd (list of strings): the command line to run.
test_id_prefix (str): a prefix to prepend to test IDs of test results
reported by cmd.
base_variant (dict): variant key-value pairs to attach to all test results
reported by cmd. If both base_variant and a reported variant have a
value for the same key, the reported one wins.
Example:
base_variant={
'bucket': api.buildbucket.build.builder.bucket,
'builder': api.buildbucket.builder_name,
}
test_location_base (str): the base path to prepend to the test location
file name with a relative path. The value must start with "//".
base_tags (list of (string, string)): tags to attach to all test results
reported by cmd. Each element is a tuple of (key, value), and a key
may be repeated.
coerce_negative_duration (bool): If true, negative duration values will
be coerced to 0. If false, tests results with negative duration values
will be rejected with an error.
include (bool): If true, a new invocation will be created and included
in the parent invocation.
realm (str): realm used for the new invocation created if `include=True`.
Default is the current realm used in buildbucket.
location_tags_file (str): path to the file that contains test location
tags in JSON format.
require_build_inv(bool): flag to control if the build is required to have
an invocation.
exonerate_unexpected_pass(bool): flag to control if automatically
exonerate unexpected passes.
### *recipe_modules* / [runtime](/recipe_modules/runtime)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [RuntimeApi](/recipe_modules/runtime/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
This module assists in experimenting with production recipes.
For example, when migrating builders from Buildbot to pure LUCI stack.
&emsp; **@property**<br>&mdash; **def [in\_global\_shutdown](/recipe_modules/runtime/api.py#33)(self):**
True iff this recipe is currently in the 'grace_period' specified by
`LUCI_CONTEXT['deadline']`.
This can occur when:
* The LUCI_CONTEXT has hit the 'soft_deadline'; OR
* The LUCI_CONTEXT has been 'canceled' and the recipe_engine has recieved
a SIGTERM (on *nix) or Ctrl-Break (on Windows).
As of 2021Q2, while the recipe is in the grace_period, it can do anything
_except_ for starting new steps (but it can e.g. update presentation of open
steps, or return RawResult from RunSteps). Attempting to start a step while
in the grace_period will cause the step to skip execution. When a signal is
recieved or the soft_deadline is hit, all currently running steps will be
signaled in turn (according to the `LUCI_CONTEXT['deadline']` protocol).
It is good practice to ensure that recipes exit cleanly when canceled or
time out, and this could be used anywhere to skip 'cleanup' behavior in
'finally' clauses or context managers.
https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/client/LUCI_CONTEXT.md
&emsp; **@property**<br>&mdash; **def [is\_experimental](/recipe_modules/runtime/api.py#20)(self):**
True if this recipe is currently running in experimental mode.
Typical usage is to modify steps which produce external side-effects so that
non-production runs of the recipe do not affect production data.
Examples:
* Uploading to an alternate google storage file name when in non-prod mode
* Appending a 'non-production' tag to external RPCs
### *recipe_modules* / [scheduler](/recipe_modules/scheduler)
[DEPS](/recipe_modules/scheduler/__init__.py#12): [buildbucket](#recipe_modules-buildbucket), [json](#recipe_modules-json), [platform](#recipe_modules-platform), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step), [time](#recipe_modules-time)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for interacting with the LUCI Scheduler service.
Depends on 'prpc' binary available in $PATH:
https://godoc.org/go.chromium.org/luci/grpc/cmd/prpc
Documentation for scheduler API is in
https://chromium.googlesource.com/infra/luci/luci-go/+/main/scheduler/api/scheduler/v1/scheduler.proto
RPCExplorer available at
https://luci-scheduler.appspot.com/rpcexplorer/services/scheduler.Scheduler
#### **class [SchedulerApi](/recipe_modules/scheduler/api.py#28)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
A module for interacting with LUCI Scheduler service.
&mdash; **def [emit\_trigger](/recipe_modules/scheduler/api.py#221)(self, trigger, project, jobs, step_name=None):**
Emits trigger to one or more jobs of a given project.
Args:
trigger (Trigger): defines payload to trigger jobs with.
project (str): name of the project in LUCI Config service, which is used
by LUCI Scheduler instance. See https://luci-config.appspot.com/.
jobs (iterable of str): job names per LUCI Scheduler config for the given
project. These typically are the same as builder names.
&mdash; **def [emit\_triggers](/recipe_modules/scheduler/api.py#233)(self, trigger_project_jobs, timestamp_usec=None, step_name=None):**
Emits a batch of triggers spanning one or more projects.
Up to date documentation is at
https://chromium.googlesource.com/infra/luci/luci-go/+/main/scheduler/api/scheduler/v1/scheduler.proto
Args:
trigger_project_jobs (iterable of tuples(trigger, project, jobs)):
each tuple corresponds to parameters of `emit_trigger` API above.
timestamp_usec (int): unix timestamp in microseconds.
Useful for idempotency of calls if your recipe is doing its own retries.
https://chromium.googlesource.com/infra/luci/luci-go/+/main/scheduler/api/scheduler/v1/triggers.proto
&emsp; **@property**<br>&mdash; **def [host](/recipe_modules/scheduler/api.py#52)(self):**
Returns the backend hostname used by this module.
&emsp; **@property**<br>&mdash; **def [invocation\_id](/recipe_modules/scheduler/api.py#73)(self):**
Returns the invocation ID of the current build as an int64 integer.
Returns None if the current build was not triggered by the scheduler.
&emsp; **@property**<br>&mdash; **def [job\_id](/recipe_modules/scheduler/api.py#65)(self):**
Returns the job ID of the current build as "<project>/<job>".
Returns None if the current build was not triggered by the scheduler.
&mdash; **def [set\_host](/recipe_modules/scheduler/api.py#57)(self, host):**
Changes the backend hostname used by this module.
Args:
host (str): server host (e.g. 'luci-scheduler.appspot.com').
&emsp; **@property**<br>&mdash; **def [triggers](/recipe_modules/scheduler/api.py#44)(self):**
Returns a list of triggers that triggered the current build.
A trigger is an instance of triggers_pb2.Trigger.
### *recipe_modules* / [service\_account](/recipe_modules/service_account)
[DEPS](/recipe_modules/service_account/__init__.py#7): [path](#recipe_modules-path), [platform](#recipe_modules-platform), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for getting OAuth2 access tokens for LUCI tasks or private keys.
This is a thin wrapper over the luci-auth go executable (
https://godoc.org/go.chromium.org/luci/auth/client/cmd/luci-auth).
Depends on luci-auth to be in PATH.
#### **class [ServiceAccountApi](/recipe_modules/service_account/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [default](/recipe_modules/service_account/api.py#57)(self):**
Returns an account associated with the task.
On LUCI, this is default account exposed through LUCI_CONTEXT["local_auth"]
protocol. When running locally this is an account the user logged in via
"luci-auth login ..." command prior to running the recipe.
&mdash; **def [from\_credentials\_json](/recipe_modules/service_account/api.py#66)(self, key_path):**
Returns a service account based on a JSON credentials file.
This is the file generated by Cloud Console when creating a service account
key. It contains the private key inside.
Args:
key_path: (str|Path) object pointing to a service account JSON key.
### *recipe_modules* / [step](/recipe_modules/step)
[DEPS](/recipe_modules/step/__init__.py#10): [context](#recipe_modules-context), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [proto](#recipe_modules-proto)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Step is the primary API for running steps (external programs, etc.)
#### **class [StepApi](/recipe_modules/step/api.py#26)([RecipeApiPlain](/recipe_engine/recipe_api.py#738)):**
&emsp; **@property**<br>&mdash; **def [InfraFailure](/recipe_modules/step/api.py#145)(self):**
InfraFailure is a subclass of StepFailure, and will translate to a purple
build.
This exception is raised from steps which are marked as `infra_step`s when
they fail.
&emsp; **@property**<br>&mdash; **def [MAX\_CPU](/recipe_modules/step/api.py#117)(self):**
Returns the maximum number of millicores this system has.
&emsp; **@property**<br>&mdash; **def [MAX\_MEMORY](/recipe_modules/step/api.py#122)(self):**
Returns the maximum amount of memory on the system in MB.
&mdash; **def [ResourceCost](/recipe_modules/step/api.py#50)(self, cpu=500, memory=50, disk=0, net=0):**
A structure defining the resources that a given step may need.
The four resources are:
* cpu (measured in millicores): The amount of cpu the step is expected to
take. Defaults to 500.
* memory (measured in MB): The amount of memory the step is expected to
take. Defaults to 50.
* disk (as percentage of max disk bandwidth): The amount of "disk
bandwidth" the step is expected to take. This is a very simplified
percentage covering IOPS, read/write bandwidth, seek time, etc. At 100,
the step will run exclusively w.r.t. all other steps having a `disk`
cost. At 0, the step will run regardless of other steps with disk cost.
* net (as percentage of max net bandwidth): The amount of "net
bandwidth" the step is expected to take. This is a very simplified
percentage covering bandwidth, latency, etc. and is indescriminate of
the remote hosts, network conditions, etc. At 100, the step will run
exclusively w.r.t. all other steps having a `net` cost. At 0, the step
will run regardless of other steps with net cost.
A step will run when ALL of the resources are simultaneously available. The
Recipe Engine currently uses a greedy scheduling algorithm for picking the
next step to run. If multiple steps are waiting for resources, this will
pick the largest (cpu, memory, disk, net) step which fits the currently
available resources and run that. The theory is that, assuming:
* Recipes are finite tasks, which aim to run ALL of their steps, and want
to do so as quickly as possible. This is not a typical OS scheduling
scenario where there's some window of time over which the recipe needs
to be 'fair'. Additionally, recipes run with finite timeouts attached.
* The duration of a given step is the same regardless of when during the
build it runs (i.e. running a step now vs later should take roughly the
same amount of time).
It's therefore optimal to run steps as quickly as possible, to avoid wasting
the timeout attached to the build.
Note that `bool(ResourceCost(...))` is defined to be True if the
ResourceCost has at least one non-zero cost, and False otherwise.
Args:
* cpu (int): Millicores that this step will take to run. See `MAX_CPU`
helper. A value higher than the maximum number of millicores on the system
is equivalent to `MAX_CPU`.
* memory (int): Number of Mebibytes of memory this step will take to run.
See `MAX_MEMORY` as a helper. A value higher than the maximum amount of
memory on the system is equivalent to `MAX_MEMORY`.
* disk (int [0..100]): The disk IO resource this step will take as
a percentage of the maximum system disk IO.
* net (int [0..100]): The network IO resource this step will take as
a percentage of the maximum system network IO.
Returns:
a ResourceCost suitable for use with `api.step(...)`'s cost kwarg. Note
that passing `None` to api.step for the cost kwarg is equivalent to
`ResourceCost(0, 0, 0, 0)`.
&emsp; **@property**<br>&mdash; **def [StepFailure](/recipe_modules/step/api.py#127)(self):**
This is the base Exception class for all step failures.
It can be manually raised from recipe code to cause the build to turn red.
Usage:
* `raise api.StepFailure("some reason")`
* `except api.StepFailure:`
&emsp; **@property**<br>&mdash; **def [StepWarning](/recipe_modules/step/api.py#139)(self):**
StepWarning is a subclass of StepFailure, and will translate to a yellow
build.
&emsp; **@recipe_api.composite_step**<br>&mdash; **def [\_\_call\_\_](/recipe_modules/step/api.py#617)(self, name, cmd, ok_ret=(0,), infra_step=False, raise_on_failure=True, wrapper=(), timeout=None, stdout=None, stderr=None, stdin=None, step_test_data=None, cost=_ResourceCost()):**
Runs a step (subprocess).
Args:
* name (string): The name of this step.
* cmd (None|List[int|string|Placeholder|Path]): The program arguments to
run. If None or an empty list, then this step just shows up in the UI
but doesn't run anything.
* Numbers and strings are used as-is.
* Placeholders are 'rendered' to a string (using their render() method).
Placeholders are e.g. `api.json.input()` or `api.raw_io.output()`.
Typically rendering these turns into an absolute path to a file on
disk, which the program is expected to read from/write to.
* Paths are rendered to an OS-native absolute path.
* ok_ret (tuple or set of ints, 'any', 'all'): allowed return codes. Any
unexpected return codes will cause an exception to be thrown. If you
pass in the value 'any' or 'all', the engine will allow any return code
to be returned. Defaults to {0}.
* infra_step: Whether or not this is an infrastructure step.
Failing infrastructure steps will place the step in an EXCEPTION state
and if raise_on_failure is True an InfraFailure will be raised.
* raise_on_failure: Whether or not the step will raise on failure. If
True, a StepFailure will be raised if the step's status is FAILURE, an
InfraFailure will be raised if the step's status is EXCEPTION and a
StepWarning will be raised if the step's status is WARNING. Regardless
of the value of this argument, an InfraFailure will be raised if the
step is canceled.
* wrapper: If supplied, a command to prepend to the executed step as a
command wrapper.
* timeout: If supplied, the recipe engine will kill the step after the
specified number of seconds. Also accepts a datetime.timedelta.
* stdout: Placeholder to put step stdout into. If used, stdout won't
appear in annotator's stdout.
* stderr: Placeholder to put step stderr into. If used, stderr won't
appear in annotator's stderr.
* stdin: Placeholder to read step stdin from.
* step_test_data (func -> recipe_test_api.StepTestData): A factory which
returns a StepTestData object that will be used as the default test
data for this step. The recipe author can override/augment this object
in the GenTests function.
* cost (None|ResourceCost): The estimated system resource cost of this
step. See `ResourceCost()`. The recipe_engine will prevent more than the
machine's maximum resources worth of steps from running at once (i.e.
steps will wait until there's enough resource available before
starting). Waiting subprocesses are unblocked in capacity-available
order. This means it's possible for pending tasks with large
requirements to 'starve' temporarily while other smaller cost tasks
run in parallel. Equal-weight tasks will start in FIFO order. Steps
with a cost of None will NEVER wait (which is the equivalent of
`ResourceCost()`). Defaults to `ResourceCost(cpu=500, memory=50)`.
Returns a `step_data.StepData` for the running step.
&emsp; **@property**<br>&mdash; **def [active\_result](/recipe_modules/step/api.py#155)(self):**
The currently active (open) result from the last step that was run. This
is a `step_data.StepData` object.
Allows you to do things like:
```python
try:
api.step('run test', [..., api.json.output()])
finally:
result = api.step.active_result
if result.json.output:
new_step_text = result.json.output['step_text']
api.step.active_result.presentation.step_text = new_step_text
```
This will update the step_text of the test, even if the test fails. Without
this api, the above code would look like:
```python
try:
result = api.step('run test', [..., api.json.output()])
except api.StepFailure as f:
result = f.result
raise
finally:
if result.json.output:
new_step_text = result.json.output['step_text']
api.step.active_result.presentation.step_text = new_step_text
```
&mdash; **def [close\_non\_nest\_step](/recipe_modules/step/api.py#188)(self):**
Call this to explicitly terminate the currently open non-nest step.
After calling this, api.step.active_step will return the current nest step
context (if any).
No-op if there's no currently active non-nest step.
&emsp; **@property**<br>&mdash; **def [defer\_results](/recipe_modules/step/api.py#356)(self):**
See recipe_api.py for docs.
&mdash; **def [empty](/recipe_modules/step/api.py#316)(self, name, status='SUCCESS', step_text=None, log_text=None, log_name='stdout', raise_on_failure=True):**
Runs an "empty" step (one without any command).
This can be useful to insert a status step/message in the UI, or summarize
some computation which occurred inside the recipe logic.
Args:
name (str) - The name of the step.
status step.(INFRA_FAILURE|FAILURE|SUCCESS) - The initial status for this
step.
step_text (str) - Some text to set for the "step_text" on the presentation
of this step.
log_text (str|list(str)) - Some text to set for the log of this step. If
this is a list(str), will be treated as separate lines of the log.
Otherwise newlines will be respected.
log_name (str) - The name of the log to output `log_text` to.
raise_on_failure (bool) - If set, and `status` is not SUCCESS, raise
the appropriate exception.
Returns step_data.StepData.
&emsp; **@contextlib.contextmanager**<br>&mdash; **def [nest](/recipe_modules/step/api.py#222)(self, name, status='worst'):**
Nest allows you to nest steps hierarchically on the build UI.
This generates a dummy step with the provided name in the current namespace.
All other steps run within this `with` statement will be nested inside of
this dummy step. Nested steps can also nest within each other.
The presentation for the dummy step can be updated (e.g. to add step_text,
step_links, etc.) or set the step's status. If you do not set the status,
it will be calculated from the status' of all the steps run within this one
according to the `status` algorithm selected.
1. If there's an active exception when leaving the `with` statement, the
status will be one of FAILURE, WARNING or EXCEPTION (depending on the
type of exception).
2. Otherwise:
1. If the status algorithm is 'worst', it will assume the status of the
worst child step. This is useful for when your nest step runs e.g.
a bunch of test shards. If any shard fails, you want the nest step
to fail as well.
2. If the status algorithm is 'last', it will assume the status of the
last child step. This is useful for when you're using the nest step
to encapsulate a sequence operation where only the last step's
status really matters.
*** promo
NOTE: Because the nest step allows action on the result of all steps run
within it, a nest step will wait for ALL recipe code within it (including
greenlets spawned with api.future.spawn!).
***
Example:
# status='worst'
with api.step.nest('run test'):
with api.step.defer_results():
for shard in range(4):
run_shard('test', shard)
# status='last'
with api.step.nest('do upload'):
for attempt in range(4):
try:
do_upload() # first one fails, but second succeeds.
except api.step.StepFailure:
pass
else:
report_error()
# manually adjust status
with api.step.nest('custom thing') as presentation:
# stuff!
presentation.status = 'FAILURE' # or whatever
*** note
**DEPRECATED**: The object yielded also has a '.presentation' field to be
compatible with code that treats the yielded object as a StepData object. If
you see such code, please update it to treat the yielded object directly as
StepPresentation instead.
***
Args:
* name (str): The name of this step.
* status ('worst'|'last'): The algorithm to use to pick a
`presentation.status` if the recipe doesn't set one explicitly.
Yields a StepPresentation for this dummy step, which you may update as you
please.
&mdash; **def [raise\_on\_failure](/recipe_modules/step/api.py#464)(self, result, status_override=None):**
Raise an appropriate exception if a step is not successful.
Arguments:
* result - The step result.
* status_override - An optional status value to override the status
present on the result of the step. This allows for the exception to
include information about the result and be based off of the initial
status even if the step's status has subsequently been changed, which
aligns with the behavior that would occur if a step was executed with
raise_on_failure=True and a step's status was changed in a finally
block.
Returns:
If the step's status is SUCCESS, the step result will be returned.
Raises:
* StepFailure if the step's status is FAILURE
* StepWarning if the step's status is WARNING
* InfraFailure if the step's status is EXCEPTION or CANCELED
&emsp; **@recipe_api.composite_step**<br>&mdash; **def [sub\_build](/recipe_modules/step/api.py#495)(self, name, cmd, build, raise_on_failure=True, output_path=None, legacy_global_namespace=False, timeout=None, step_test_data=None, cost=_ResourceCost()):**
Launch a sub-build by invoking a LUCI executable. All steps in the
sub-build will appear as child steps of this step (Merge Step).
See protocol: https://go.chromium.org/luci/luciexe
Example:
```python
run_exe = api.cipd.ensure_tool(...) # Install LUCI executable `run_exe`
# Basic Example: launch `run_exe` with empty initial build and
# default options.
ret = api.sub_build("launch sub build", [run_exe], build_pb2.Build())
sub_build = ret.step.sub_build # access final build proto result
# Example: launch `run_exe` with input build to recipe and customized
# output path, cwd and cache directory.
with api.context(
# Change the cwd of the launched LUCI executable
cwd=api.path['start_dir'].join('subdir'),
# Change the cache_dir of the launched LUCI executable. Defaults to
# api.path['cache'] if unchanged.
luciexe=sections_pb2.LUCIExe(cache_dir=api.path['cache'].join('sub')),
):
# Command executed:
# `/path/to/run_exe --output [CLEANUP]/build.json --foo bar baz`
ret = api.sub_build("launch sub build",
[run_exe, '--foo', 'bar', 'baz'],
api.buildbucket.build,
output_path=api.path['cleanup'].join('build.json'))
sub_build = ret.step.sub_build # access final build proto result
```
Args:
* name (str): The name of this step.
* cmd (List[int|string|Placeholder|Path]): Same as the `cmd` parameter in
`__call__` method except that None is NOT allowed. cmd[0] MUST denote a
LUCI executable. The `--output` flag and its value should NOT be
provided in the list. It should be provided via keyword arg
`output_path` instead.
* build (build_pb2.Build): The initial build state that the launched
luciexe will start with. This method will clone the input build, modify
the clone's fields and pass the clone to luciexe (see 'Invocation'
section in http://go.chromium.org/luci/luciexe for what modification
will be done).
* raise_on_failure: Whether or not the step will raise on failure. If
True, a StepFailure will be raised if the step's status is FAILURE, an
InfraFailure will be raised if the step's status is EXCEPTION and a
StepWarning will be raised if the step's status is WARNING. Regardless
of the value of this argument, an InfraFailure will be raised if the
step is canceled.
* output_path (None|str|Path): The value of the `--output` flag. If
provided, it should be a path to a non-existent file (its directory
MUST exist). The extension of the path dictates the encoding format of
final build proto (See `EXT_TO_CODEC`). If not provided, the output
will be a temp file with binary encoding.
* legacy_global_namespace (bool): If set, activates legacy global
namespace merging. Only meant for legacy ChromeOS builders.
See crbug.com/1310155.
* timeout (None|int|float|datetime.timedelta): Same as the `timeout`
parameter in `__call__` method.
* step_test_data(Callable[[], recipe_test_api.StepTestData]): Same as the
`step_test_data` parameter in `__call__` method.
* cost (None|ResourceCost): Same as the `cost` parameter in `__call__`
method.
Returns a `step_data.StepData` for the finished step. The final build proto
object can be accessed via `ret.step.sub_build`. The build is guaranteed to
be present (i.e. not None) with a terminal build status.
Raises `StepFailure` if the sub-build reports FAILURE status.
Raises `InfraFailure` if the sub-build reports INFRA_FAILURE or CANCELED
status.
### *recipe_modules* / [swarming](/recipe_modules/swarming)
[DEPS](/recipe_modules/swarming/__init__.py#9): [buildbucket](#recipe_modules-buildbucket), [cas](#recipe_modules-cas), [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
#### **class [SwarmingApi](/recipe_modules/swarming/api.py#1118)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
API for interacting with swarming.
The tool's source lives at
http://go.chromium.org/luci/client/cmd/swarming.
This module will deploy the client to [CACHE]/swarming_client/; users should
add this path to the named cache for their builder.
&emsp; **@property**<br>&mdash; **def [bot\_id](/recipe_modules/swarming/api.py#1141)(self):**
Swarming bot ID executing this task.
&mdash; **def [collect](/recipe_modules/swarming/api.py#1270)(self, name, tasks, output_dir=None, task_output_stdout='json', timeout=None, eager=False, verbose=False):**
Waits on a set of Swarming tasks.
Args:
name (str): The name of the step.
tasks (Iterable(str|TaskRequestMetadata)): A list of ids or metadata
objects corresponding to tasks to wait
output_dir (Path|None): Where to download the tasks' isolated outputs. If
set to None, they will not be downloaded; else, a given task's outputs
will be downloaded to output_dir/<task id>/.
task_output_stdout (str): Where to output each task's output. Must be one
of 'none', 'json', 'console' or 'all'.
timeout (str|None): The duration for which to wait on the tasks to finish.
If set to None, there will be no timeout; else, timeout follows the
format described by https://golang.org/pkg/time/#ParseDuration.
eager (bool): Whether to return as soon as the first task finishes,
instead of waiting for all tasks to finish.
verbose (bool): Whether to use verbose logs.
Returns:
A list of TaskResult objects.
&emsp; **@property**<br>&mdash; **def [current\_server](/recipe_modules/swarming/api.py#1151)(self):**
Swarming server executing this task.
&mdash; **def [ensure\_client](/recipe_modules/swarming/api.py#1171)(self):**
&mdash; **def [initialize](/recipe_modules/swarming/api.py#1156)(self):**
&emsp; **@contextlib.contextmanager**<br>&mdash; **def [on\_path](/recipe_modules/swarming/api.py#1187)(self):**
This context manager ensures the go swarming client is available on
$PATH.
Example:
with api.swarming.on_path():
# do your steps which require the swarming binary on path
&mdash; **def [show\_request](/recipe_modules/swarming/api.py#1359)(self, name, task):**
Retrive the TaskRequest for a Swarming task.
Args:
name (str): The name of the step.
task (str|TaskRequestMetadata): Task Id or metadata objects of the
swarming task to be retrived.
Returns:
TaskRequest objects.
&emsp; **@property**<br>&mdash; **def [task\_id](/recipe_modules/swarming/api.py#1146)(self):**
This task's Swarming ID.
&mdash; **def [task\_request](/recipe_modules/swarming/api.py#1201)(self):**
Creates a new TaskRequest object.
See documentation for TaskRequest/TaskSlice to see how to build this up
into a full task.
Once your TaskRequest is complete, you can pass it to `trigger` in order to
have it start running on the swarming server.
&mdash; **def [task\_request\_from\_jsonish](/recipe_modules/swarming/api.py#1212)(self, json_d):**
Creates a new TaskRequest object from a JSON-serializable dict.
The input argument should match the schema as the output of
TaskRequest.to_jsonish().
&mdash; **def [trigger](/recipe_modules/swarming/api.py#1220)(self, step_name, requests, verbose=False):**
Triggers a set of Swarming tasks.
Args:
step_name (str): The name of the step.
requests (seq[TaskRequest]): A sequence of task request objects
representing the tasks we want to trigger.
verbose (bool): Whether to use verbose logs.
Returns:
A list of TaskRequestMetadata objects.
### *recipe_modules* / [time](/recipe_modules/time)
[DEPS](/recipe_modules/time/__init__.py#7): [random](#recipe_modules-random), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Allows mockable access to the current time.
#### **class [TimeApi](/recipe_modules/time/api.py#95)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [exponential\_retry](/recipe_modules/time/api.py#131)(self, retries, delay, condition=None):**
Adds exponential retry to a function.
See the 'exponential_retry' function in this module for more docs.
&mdash; **def [ms\_since\_epoch](/recipe_modules/time/api.py#146)(self):**
Returns current timestamp as an int number of milliseconds since epoch.
&mdash; **def [sleep](/recipe_modules/time/api.py#104)(self, secs, with_step=None, step_result=None):**
Suspend execution of |secs| (float) seconds, waiting for GLOBAL_SHUTDOWN.
Does nothing in testing.
Args:
* secs (number) - The number of seconds to sleep.
* with_step (bool|None) - If True (or None and secs>60), emits a step to
indicate to users that the recipe is sleeping (not just hanging). False
suppresses this.
* step_result (step_data.StepData|None) - Result of running a step. Should
be None if with_step is True or None.
&mdash; **def [time](/recipe_modules/time/api.py#138)(self):**
Returns current timestamp as a float number of seconds since epoch.
&mdash; **def [utcnow](/recipe_modules/time/api.py#151)(self):**
Returns current UTC time as a datetime.datetime.
### *recipe_modules* / [tricium](/recipe_modules/tricium)
[DEPS](/recipe_modules/tricium/__init__.py#7): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [proto](#recipe_modules-proto), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
API for Tricium analyzers to use.
This recipe module is intended to support different kinds of
analyzer recipes, including:
* Recipes that wrap one or more legacy analyzers.
* Recipes that accumulate comments one by one.
* Recipes that wrap other tools and parse their output.
#### **class [TriciumApi](/recipe_modules/tricium/api.py#26)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
TriciumApi provides basic support for Tricium.
&mdash; **def [\_\_init\_\_](/recipe_modules/tricium/api.py#38)(self, \*\*kwargs):**
Sets up the API.
Initializes an empty list of comments for use with
add_comment and write_comments.
&mdash; **def [add\_comment](/recipe_modules/tricium/api.py#47)(self, category, message, path, start_line=0, end_line=0, start_char=0, end_char=0, suggestions=()):**
Adds one comment to accumulate.
For semantics of start_line, start_char, end_line, end_char, see Gerrit doc
https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#comment-range
&mdash; **def [run\_legacy](/recipe_modules/tricium/api.py#138)(self, analyzers, input_base, affected_files, commit_message, emit=True):**
Runs legacy analyzers.
This function internally accumulates the comments from the analyzers it
runs to the same global storage used by `add_comment()`. By default it
emits comments from legacy analyzers to the tricium output property,
along with any comments previously created by calling `add_comment()`
directly, after running all the specified analyzers.
Args:
* analyzers (List(LegacyAnalyer)): Analyzers to run.
* input_base (Path): The Tricium input dir, generally a checkout base.
* affected_files (List(str)): Paths of files in the change, relative
to input_base.
* commit_message (str): Commit message from Gerrit.
* emit (bool): Whether to write results to the tricium output
property. If unset, the caller will be responsible for calling
`write_comments` to emit the comments added by the legacy analyzers.
This is useful for recipes that need to run a mixture of custom
analyzers (using `add_comment()` to store comments) and legacy
analyzers.
&emsp; **@staticmethod**<br>&mdash; **def [validate\_comment](/recipe_modules/tricium/api.py#74)(comment):**
Validates comment to comply with Tricium/Gerrit requirements.
Raise ValueError on the first detected problem.
&mdash; **def [write\_comments](/recipe_modules/tricium/api.py#115)(self):**
Emit the results accumulated by `add_comment` and `run_legacy`.
### *recipe_modules* / [url](/recipe_modules/url)
[DEPS](/recipe_modules/url/__init__.py#7): [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Methods for interacting with HTTP(s) URLs.
#### **class [UrlApi](/recipe_modules/url/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [get\_file](/recipe_modules/url/api.py#131)(self, url, path, step_name=None, headers=None, transient_retry=True, strip_prefix=None, timeout=None):**
GET data at given URL and writes it to file.
Args:
* url: URL to request.
* path (Path): the Path where the content will be written.
* step_name: optional step name, 'GET <url>' by default.
* headers: a {header_name: value} dictionary for HTTP headers.
* transient_retry (bool or int): Determines how transient HTTP errorts
(>500) will be retried. If True (default), errors will be retried up
to 10 times. If False, no transient retries will occur. If an integer
is supplied, this is the number of transient retries to perform. All
retries have exponential backoff applied.
* strip_prefix (str or None): If not None, this prefix must be present at
the beginning of the response, and will be stripped from the resulting
content (e.g., GERRIT_JSON_PREFIX).
* timeout: Timeout (see step.__call__).
Returns (UrlApi.Response):
Response with "path" as its "output" value.
Raises:
* HTTPError, InfraHTTPError: if the request failed.
* ValueError: If the request was invalid.
&mdash; **def [get\_json](/recipe_modules/url/api.py#187)(self, url, step_name=None, headers=None, transient_retry=True, strip_prefix=None, log=False, timeout=None, default_test_data=None):**
GET data at given URL and writes it to file.
Args:
* url: URL to request.
* step_name: optional step name, 'GET <url>' by default.
* headers: a {header_name: value} dictionary for HTTP headers.
* transient_retry (bool or int): Determines how transient HTTP errorts
(>500) will be retried. If True (default), errors will be retried up
to 10 times. If False, no transient retries will occur. If an integer
is supplied, this is the number of transient retries to perform. All
retries have exponential backoff applied.
* strip_prefix (str or None): If not None, this prefix must be present at
the beginning of the response, and will be stripped from the resulting
content (e.g., GERRIT_JSON_PREFIX).
* log (bool): If True, emit the JSON content as a log.
* timeout: Timeout (see step.__call__).
* default_test_data (jsonish): If provided, use this as the unmarshalled
JSON result when testing if no overriding data is available.
Returns (UrlApi.Response): Response with the JSON as its "output" value.
Raises:
* HTTPError, InfraHTTPError: if the request failed.
* ValueError: If the request was invalid.
&mdash; **def [get\_text](/recipe_modules/url/api.py#160)(self, url, step_name=None, headers=None, transient_retry=True, timeout=None, default_test_data=None):**
GET data at given URL and writes it to file.
Args:
* url: URL to request.
* step_name: optional step name, 'GET <url>' by default.
* headers: a {header_name: value} dictionary for HTTP headers.
* transient_retry (bool or int): Determines how transient HTTP errorts
(>500) will be retried. If True (default), errors will be retried up
to 10 times. If False, no transient retries will occur. If an integer
is supplied, this is the number of transient retries to perform. All
retries have exponential backoff applied.
* timeout: Timeout (see step.__call__).
* default_test_data (str): If provided, use this as the text output when
testing if no overriding data is available.
Returns (UrlApi.Response): Response with the content as its output value.
Raises:
* HTTPError, InfraHTTPError: if the request failed.
* ValueError: If the request was invalid.
&mdash; **def [join](/recipe_modules/url/api.py#93)(self, \*parts):**
Constructs a URL path from composite parts.
Args:
* parts (str...): Strings to concastenate. Any leading or trailing slashes
will be stripped from intermediate strings to ensure that they join
together. Trailing slashes will not be stripped from the last part.
&mdash; **def [validate\_url](/recipe_modules/url/api.py#109)(self, v):**
Validates that "v" is a valid URL.
A valid URL has a scheme and netloc, and must begin with HTTP or HTTPS.
Args:
* v (str): The URL to validate.
Returns (bool): True if the URL is considered secure, False if not.
Raises:
ValueError: if "v" is not valid.
### *recipe_modules* / [uuid](/recipe_modules/uuid)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Allows test-repeatable access to a random UUID.
#### **class [UuidApi](/recipe_modules/uuid/api.py#11)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&mdash; **def [random](/recipe_modules/uuid/api.py#20)(self):**
Returns a random UUID string.
### *recipe_modules* / [version](/recipe_modules/version)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Thin API for parsing semver strings into comparable object.
#### **class [VersionApi](/recipe_modules/version/api.py#12)([RecipeApi](/recipe_engine/recipe_api.py#886)):**
&emsp; **@staticmethod**<br>&mdash; **def [parse](/recipe_modules/version/api.py#14)(version):**
Parse implements PEP 440 parsing for semvers.
If `version` is strictly parseable as PEP 440, this returns a Version
object. Otherwise it does a 'loose' parse, just extracting numerals from
version.
You can read more about how this works at:
https://setuptools.readthedocs.io/en/latest/pkg_resources.html#parsing-utilities
### *recipe_modules* / [warning](/recipe_modules/warning)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Allows recipe modules to issue warnings in simulation test.
#### **class [WarningApi](/recipe_modules/warning/api.py#12)([RecipeApiPlain](/recipe_engine/recipe_api.py#738)):**
&emsp; **@recipe_api.escape_all_warnings**<br>&mdash; **def [issue](/recipe_modules/warning/api.py#15)(self, name):**
Issues an execution warning.
`name` MAY either be a fully qualified "repo_name/WARNING_NAME" or a short
"WARNING_NAME". If it's a short name, then the "repo_name" will be
determined from the location of the file issuing the warning (i.e. if the
issue() comes from a file in repo_X, then "WARNING_NAME" will be
transformed to "repo_X/WARNING_NAME").
It is recommended to use the short name if the warning is defined in the
same repo as the issue() call.
## Recipes
### *recipes* / [archive:examples/full](/recipe_modules/archive/examples/full.py)
[DEPS](/recipe_modules/archive/examples/full.py#9): [archive](#recipe_modules-archive), [context](#recipe_modules-context), [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/archive/examples/full.py#21)(api):**
### *recipes* / [assertions:tests/assert-raises](/recipe_modules/assertions/tests/assert-raises.py)
[DEPS](/recipe_modules/assertions/tests/assert-raises.py#9): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/assert-raises.py#18)(api):**
### *recipes* / [assertions:tests/assert\_count\_equal](/recipe_modules/assertions/tests/assert_count_equal.py)
[DEPS](/recipe_modules/assertions/tests/assert_count_equal.py#9): [assertions](#recipe_modules-assertions)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/assert_count_equal.py#14)(api):**
### *recipes* / [assertions:tests/assertions](/recipe_modules/assertions/tests/assertions.py)
[DEPS](/recipe_modules/assertions/tests/assertions.py#9): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/assertions.py#15)(api):**
### *recipes* / [assertions:tests/attribute\_error](/recipe_modules/assertions/tests/attribute_error.py)
[DEPS](/recipe_modules/assertions/tests/attribute_error.py#9): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/attribute_error.py#15)(api):**
### *recipes* / [assertions:tests/long\_message](/recipe_modules/assertions/tests/long_message.py)
[DEPS](/recipe_modules/assertions/tests/long_message.py#9): [assertions](#recipe_modules-assertions), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/long_message.py#14)(api):**
### *recipes* / [assertions:tests/max\_diff](/recipe_modules/assertions/tests/max_diff.py)
[DEPS](/recipe_modules/assertions/tests/max_diff.py#9): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/assertions/tests/max_diff.py#15)(api):**
### *recipes* / [bcid\_reporter:examples/usage](/recipe_modules/bcid_reporter/examples/usage.py)
[DEPS](/recipe_modules/bcid_reporter/examples/usage.py#7): [bcid\_reporter](#recipe_modules-bcid_reporter), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/bcid_reporter/examples/usage.py#12)(api):**
### *recipes* / [buildbucket:examples/full](/recipe_modules/buildbucket/examples/full.py)
[DEPS](/recipe_modules/buildbucket/examples/full.py#16): [buildbucket](#recipe_modules-buildbucket), [json](#recipe_modules-json), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This file is a recipe demonstrating the buildbucket recipe module.
&mdash; **def [RunSteps](/recipe_modules/buildbucket/examples/full.py#27)(api):**
### *recipes* / [buildbucket:run/multi](/recipe_modules/buildbucket/run/multi.py)
[DEPS](/recipe_modules/buildbucket/run/multi.py#13): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY3
Launches multiple builds at the same revision.
&mdash; **def [RunSteps](/recipe_modules/buildbucket/run/multi.py#31)(api, build_requests, collect_builds):**
### *recipes* / [buildbucket:tests/add\_tags](/recipe_modules/buildbucket/tests/add_tags.py)
[DEPS](/recipe_modules/buildbucket/tests/add_tags.py#7): [buildbucket](#recipe_modules-buildbucket), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/add_tags.py#13)(api):**
### *recipes* / [buildbucket:tests/build](/recipe_modules/buildbucket/tests/build.py)
[DEPS](/recipe_modules/buildbucket/tests/build.py#22): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/build.py#30)(api):**
### *recipes* / [buildbucket:tests/cancel](/recipe_modules/buildbucket/tests/cancel.py)
[DEPS](/recipe_modules/buildbucket/tests/cancel.py#12): [buildbucket](#recipe_modules-buildbucket)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/cancel.py#16)(api):**
### *recipes* / [buildbucket:tests/collect](/recipe_modules/buildbucket/tests/collect.py)
[DEPS](/recipe_modules/buildbucket/tests/collect.py#7): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/collect.py#13)(api):**
### *recipes* / [buildbucket:tests/output\_commit](/recipe_modules/buildbucket/tests/output_commit.py)
[DEPS](/recipe_modules/buildbucket/tests/output_commit.py#11): [buildbucket](#recipe_modules-buildbucket), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This recipe tests the buildbucket.set_output_gitiles_commit function.
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/output_commit.py#20)(api):**
### *recipes* / [buildbucket:tests/schedule](/recipe_modules/buildbucket/tests/schedule.py)
[DEPS](/recipe_modules/buildbucket/tests/schedule.py#14): [buildbucket](#recipe_modules-buildbucket), [json](#recipe_modules-json), [properties](#recipe_modules-properties), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/schedule.py#23)(api):**
### *recipes* / [buildbucket:tests/search](/recipe_modules/buildbucket/tests/search.py)
[DEPS](/recipe_modules/buildbucket/tests/search.py#14): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/search.py#23)(api):**
### *recipes* / [cas:examples/full](/recipe_modules/cas/examples/full.py)
[DEPS](/recipe_modules/cas/examples/full.py#7): [cas](#recipe_modules-cas), [file](#recipe_modules-file), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cas/examples/full.py#17)(api):**
### *recipes* / [cas\_input:examples/full](/recipe_modules/cas_input/examples/full.py)
[DEPS](/recipe_modules/cas_input/examples/full.py#7): [cas\_input](#recipe_modules-cas_input), [path](#recipe_modules-path), [properties](#recipe_modules-properties)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cas_input/examples/full.py#18)(api):**
### *recipes* / [change\_verifier:tests/search](/recipe_modules/change_verifier/tests/search.py)
[DEPS](/recipe_modules/change_verifier/tests/search.py#10): [change\_verifier](#recipe_modules-change_verifier), [proto](#recipe_modules-proto)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/change_verifier/tests/search.py#16)(api):**
&mdash; **def [make\_runs](/recipe_modules/change_verifier/tests/search.py#36)(count=1):**
Generates response Runs for a test.
### *recipes* / [cipd:examples/full](/recipe_modules/cipd/examples/full.py)
[DEPS](/recipe_modules/cipd/examples/full.py#11): [cipd](#recipe_modules-cipd), [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cipd/examples/full.py#38)(api, use_pkg, pkg_files, pkg_dirs, pkg_vars, ver_files, install_mode, refs, tags, metadata, max_threads):**
### *recipes* / [commit\_position:examples/full](/recipe_modules/commit_position/examples/full.py)
[DEPS](/recipe_modules/commit_position/examples/full.py#7): [commit\_position](#recipe_modules-commit_position), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/commit_position/examples/full.py#13)(api):**
### *recipes* / [context:examples/full](/recipe_modules/context/examples/full.py)
[DEPS](/recipe_modules/context/examples/full.py#11): [context](#recipe_modules-context), [path](#recipe_modules-path), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step), [time](#recipe_modules-time)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/examples/full.py#20)(api):**
### *recipes* / [context:tests/cwd](/recipe_modules/context/tests/cwd.py)
[DEPS](/recipe_modules/context/tests/cwd.py#7): [context](#recipe_modules-context), [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/tests/cwd.py#13)(api):**
### *recipes* / [context:tests/env](/recipe_modules/context/tests/env.py)
[DEPS](/recipe_modules/context/tests/env.py#7): [context](#recipe_modules-context), [path](#recipe_modules-path), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/tests/env.py#21)(api):**
### *recipes* / [context:tests/greenlet](/recipe_modules/context/tests/greenlet.py)
[DEPS](/recipe_modules/context/tests/greenlet.py#7): [context](#recipe_modules-context), [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/tests/greenlet.py#13)(api):**
### *recipes* / [context:tests/infra\_step](/recipe_modules/context/tests/infra_step.py)
[DEPS](/recipe_modules/context/tests/infra_step.py#7): [context](#recipe_modules-context), [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/tests/infra_step.py#13)(api):**
### *recipes* / [context:tests/luci\_context](/recipe_modules/context/tests/luci_context.py)
[DEPS](/recipe_modules/context/tests/luci_context.py#9): [assertions](#recipe_modules-assertions), [context](#recipe_modules-context), [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/context/tests/luci_context.py#16)(api):**
### *recipes* / [cq:examples/ordered\_cls](/recipe_modules/cq/examples/ordered_cls.py)
[DEPS](/recipe_modules/cq/examples/ordered_cls.py#12): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [cq](#recipe_modules-cq), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/examples/ordered_cls.py#21)(api):**
### *recipes* / [cq:examples/trigger\_child\_builds](/recipe_modules/cq/examples/trigger_child_builds.py)
[DEPS](/recipe_modules/cq/examples/trigger_child_builds.py#11): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [cq](#recipe_modules-cq), [json](#recipe_modules-json), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/examples/trigger_child_builds.py#21)(api):**
### *recipes* / [cq:tests/cl\_group\_key](/recipe_modules/cq/tests/cl_group_key.py)
[DEPS](/recipe_modules/cq/tests/cl_group_key.py#9): [buildbucket](#recipe_modules-buildbucket), [cq](#recipe_modules-cq)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/cl_group_key.py#19)(api):**
### *recipes* / [cq:tests/do\_not\_retry](/recipe_modules/cq/tests/do_not_retry.py)
[DEPS](/recipe_modules/cq/tests/do_not_retry.py#7): [buildbucket](#recipe_modules-buildbucket), [cq](#recipe_modules-cq), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/do_not_retry.py#17)(api):**
### *recipes* / [cq:tests/experimental](/recipe_modules/cq/tests/experimental.py)
[DEPS](/recipe_modules/cq/tests/experimental.py#9): [assertions](#recipe_modules-assertions), [cq](#recipe_modules-cq), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/experimental.py#17)(api):**
### *recipes* / [cq:tests/inactive](/recipe_modules/cq/tests/inactive.py)
[DEPS](/recipe_modules/cq/tests/inactive.py#9): [assertions](#recipe_modules-assertions), [cq](#recipe_modules-cq), [properties](#recipe_modules-properties)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/inactive.py#16)(api):**
### *recipes* / [cq:tests/mode\_of\_run](/recipe_modules/cq/tests/mode_of_run.py)
[DEPS](/recipe_modules/cq/tests/mode_of_run.py#7): [cq](#recipe_modules-cq), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/mode_of_run.py#14)(api):**
### *recipes* / [cq:tests/reuse](/recipe_modules/cq/tests/reuse.py)
[DEPS](/recipe_modules/cq/tests/reuse.py#7): [assertions](#recipe_modules-assertions), [cq](#recipe_modules-cq), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/reuse.py#14)(api):**
### *recipes* / [cq:tests/triggered\_build\_ids](/recipe_modules/cq/tests/triggered_build_ids.py)
[DEPS](/recipe_modules/cq/tests/triggered_build_ids.py#7): [buildbucket](#recipe_modules-buildbucket), [cq](#recipe_modules-cq), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/cq/tests/triggered_build_ids.py#17)(api):**
### *recipes* / [engine\_tests/bad\_subprocess](/recipes/engine_tests/bad_subprocess.py)
[DEPS](/recipes/engine_tests/bad_subprocess.py#9): [platform](#recipe_modules-platform), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that daemons that hang on to STDOUT can't cause the engine to hang.
&mdash; **def [RunSteps](/recipes/engine_tests/bad_subprocess.py#15)(api):**
### *recipes* / [engine\_tests/comprehensive\_ui](/recipes/engine_tests/comprehensive_ui.py)
[DEPS](/recipes/engine_tests/comprehensive_ui.py#13): [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
A fast-running recipe which comprehensively covers all StepPresentation
features available in the recipe engine.
&mdash; **def [RunSteps](/recipes/engine_tests/comprehensive_ui.py#25)(api):**
&mdash; **def [named\_step](/recipes/engine_tests/comprehensive_ui.py#21)(api, name):**
### *recipes* / [engine\_tests/config\_operations](/recipes/engine_tests/config_operations.py)
[DEPS](/recipes/engine_tests/config_operations.py#44): [json](#recipe_modules-json), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that recipes can modify configuration options in various ways.
&mdash; **def [BaseConfig](/recipes/engine_tests/config_operations.py#15)(\*\*_kwargs):**
&mdash; **def [DumpRecipeEngineTestConfig](/recipes/engine_tests/config_operations.py#49)(api, config):**
&mdash; **def [RunSteps](/recipes/engine_tests/config_operations.py#54)(api):**
&emsp; **@config_ctx()**<br>&mdash; **def [test1](/recipes/engine_tests/config_operations.py#30)(c):**
&emsp; **@config_ctx(includes=['test2a'])**<br>&mdash; **def [test2](/recipes/engine_tests/config_operations.py#38)(c):**
&emsp; **@config_ctx()**<br>&mdash; **def [test2a](/recipes/engine_tests/config_operations.py#34)(c):**
### *recipes* / [engine\_tests/early\_termination](/recipes/engine_tests/early_termination.py)
[DEPS](/recipes/engine_tests/early_termination.py#10): [file](#recipe_modules-file), [futures](#recipe_modules-futures), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Simple recipe which runs a bunch of subprocesses which react to early
termination in different ways.
&mdash; **def [RunSteps](/recipes/engine_tests/early_termination.py#23)(api, props):**
### *recipes* / [engine\_tests/expect\_exception](/recipes/engine_tests/expect_exception.py)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that step_data can accept multiple specs at once.
&mdash; **def [RunSteps](/recipes/engine_tests/expect_exception.py#22)(api):**
&emsp; **@composite_step**<br>&mdash; **def [my\_function](/recipes/engine_tests/expect_exception.py#17)():**
### *recipes* / [engine\_tests/failure\_results](/recipes/engine_tests/failure_results.py)
[DEPS](/recipes/engine_tests/failure_results.py#14): [json](#recipe_modules-json), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that run_steps is handling recipe failures correctly.
&mdash; **def [RunSteps](/recipes/engine_tests/failure_results.py#19)(api):**
### *recipes* / [engine\_tests/functools\_partial](/recipes/engine_tests/functools_partial.py)
[DEPS](/recipes/engine_tests/functools_partial.py#18): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Engine shouldn't explode when step_test_data gets functools.partial.
This is a regression test for a bug caused by this revision:
http://src.chromium.org/viewvc/chrome?revision=298072&view=revision
When this recipe is run (by run_test.py), the _print_step code is exercised.
&mdash; **def [RunSteps](/recipes/engine_tests/functools_partial.py#20)(api):**
### *recipes* / [engine\_tests/incorrect\_recipe\_result](/recipes/engine_tests/incorrect_recipe_result.py)
[DEPS](/recipes/engine_tests/incorrect_recipe_result.py#14): [json](#recipe_modules-json), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that engine.py can handle unknown recipe results.
&mdash; **def [RunSteps](/recipes/engine_tests/incorrect_recipe_result.py#22)(api, props):**
### *recipes* / [engine\_tests/long\_sleep](/recipes/engine_tests/long_sleep.py)
[DEPS](/recipes/engine_tests/long_sleep.py#8): [futures](#recipe_modules-futures), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Simple recipe which sleeps in a subprocess forever to facilitate early
termination tests.
&mdash; **def [RunSteps](/recipes/engine_tests/long_sleep.py#22)(api, props):**
### *recipes* / [engine\_tests/missing\_start\_dir](/recipes/engine_tests/missing_start_dir.py)
[DEPS](/recipes/engine_tests/missing_start_dir.py#9): [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that deleting the current working directory doesn't immediately fail
&mdash; **def [RunSteps](/recipes/engine_tests/missing_start_dir.py#15)(api):**
### *recipes* / [engine\_tests/module\_injection\_site](/recipes/engine_tests/module_injection_site.py)
[DEPS](/recipes/engine_tests/module_injection_site.py#15): [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This test serves to demonstrate that the ModuleInjectionSite object on
recipe modules (i.e. the `.m`) also contains a reference to the module which
owns it.
This was implemented to aid in refactoring some recipes (crbug.com/782142).
&mdash; **def [RunSteps](/recipes/engine_tests/module_injection_site.py#20)(api):**
### *recipes* / [engine\_tests/multi\_test\_data](/recipes/engine_tests/multi_test_data.py)
[DEPS](/recipes/engine_tests/multi_test_data.py#9): [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that step_data can accept multiple specs at once.
&mdash; **def [RunSteps](/recipes/engine_tests/multi_test_data.py#14)(api):**
### *recipes* / [engine\_tests/multiple\_placeholders](/recipes/engine_tests/multiple_placeholders.py)
[DEPS](/recipes/engine_tests/multiple_placeholders.py#11): [assertions](#recipe_modules-assertions), [json](#recipe_modules-json), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests error checking around multiple placeholders in a single step.
&mdash; **def [RunSteps](/recipes/engine_tests/multiple_placeholders.py#18)(api):**
### *recipes* / [engine\_tests/nonexistent\_command](/recipes/engine_tests/nonexistent_command.py)
[DEPS](/recipes/engine_tests/nonexistent_command.py#9): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipes/engine_tests/nonexistent_command.py#14)(api):**
### *recipes* / [engine\_tests/placeholder\_exception](/recipes/engine_tests/placeholder_exception.py)
[DEPS](/recipes/engine_tests/placeholder_exception.py#14): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that placeholders can't wreck the world by exhausting the step stack.
&mdash; **def [RunSteps](/recipes/engine_tests/placeholder_exception.py#26)(api):**
### *recipes* / [engine\_tests/proto\_output\_properties](/recipes/engine_tests/proto_output_properties.py)
[DEPS](/recipes/engine_tests/proto_output_properties.py#12): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that output properties can be a proto message.
&mdash; **def [RunSteps](/recipes/engine_tests/proto_output_properties.py#16)(api):**
### *recipes* / [engine\_tests/proto\_properties](/recipes/engine_tests/proto_properties.py)
[DEPS](/recipes/engine_tests/proto_properties.py#9): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipes/engine_tests/proto_properties.py#18)(api, properties, env_props):**
### *recipes* / [engine\_tests/recipe\_paths](/recipes/engine_tests/recipe_paths.py)
[DEPS](/recipes/engine_tests/recipe_paths.py#9): [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that recipes have access to names, resources and their repo.
&mdash; **def [RunSteps](/recipes/engine_tests/recipe_paths.py#14)(api):**
### *recipes* / [engine\_tests/sort\_properties](/recipes/engine_tests/sort_properties.py)
[DEPS](/recipes/engine_tests/sort_properties.py#9): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that step presentation properties can be ordered.
&mdash; **def [RunSteps](/recipes/engine_tests/sort_properties.py#13)(api):**
### *recipes* / [engine\_tests/undeclared\_method](/recipes/engine_tests/undeclared_method.py)
[DEPS](/recipes/engine_tests/undeclared_method.py#10): [cipd](#recipe_modules-cipd), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipes/engine_tests/undeclared_method.py#22)(api, from_recipe, attribute, module):**
### *recipes* / [engine\_tests/unicode](/recipes/engine_tests/unicode.py)
[DEPS](/recipes/engine_tests/unicode.py#8): [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipes/engine_tests/unicode.py#13)(api):**
### *recipes* / [engine\_tests/whitelist\_steps](/recipes/engine_tests/whitelist_steps.py)
[DEPS](/recipes/engine_tests/whitelist_steps.py#14): [context](#recipe_modules-context), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
Tests that step_data can accept multiple specs at once.
&mdash; **def [RunSteps](/recipes/engine_tests/whitelist_steps.py#24)(api, fakeit):**
### *recipes* / [file:examples/compute\_hash](/recipe_modules/file/examples/compute_hash.py)
[DEPS](/recipe_modules/file/examples/compute_hash.py#7): [assertions](#recipe_modules-assertions), [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/compute_hash.py#13)(api):**
### *recipes* / [file:examples/copy](/recipe_modules/file/examples/copy.py)
[DEPS](/recipe_modules/file/examples/copy.py#7): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/copy.py#14)(api):**
### *recipes* / [file:examples/copytree](/recipe_modules/file/examples/copytree.py)
[DEPS](/recipe_modules/file/examples/copytree.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/copytree.py#13)(api):**
### *recipes* / [file:examples/error](/recipe_modules/file/examples/error.py)
[DEPS](/recipe_modules/file/examples/error.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/error.py#13)(api):**
### *recipes* / [file:examples/file\_hash](/recipe_modules/file/examples/file_hash.py)
[DEPS](/recipe_modules/file/examples/file_hash.py#7): [assertions](#recipe_modules-assertions), [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/file_hash.py#13)(api):**
### *recipes* / [file:examples/flatten\_single\_directories](/recipe_modules/file/examples/flatten_single_directories.py)
[DEPS](/recipe_modules/file/examples/flatten_single_directories.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/flatten_single_directories.py#13)(api):**
### *recipes* / [file:examples/glob](/recipe_modules/file/examples/glob.py)
[DEPS](/recipe_modules/file/examples/glob.py#7): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/glob.py#13)(api):**
### *recipes* / [file:examples/handle\_json\_file](/recipe_modules/file/examples/handle_json_file.py)
[DEPS](/recipe_modules/file/examples/handle_json_file.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/handle_json_file.py#13)(api):**
### *recipes* / [file:examples/listdir](/recipe_modules/file/examples/listdir.py)
[DEPS](/recipe_modules/file/examples/listdir.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/listdir.py#13)(api):**
### *recipes* / [file:examples/raw\_copy](/recipe_modules/file/examples/raw_copy.py)
[DEPS](/recipe_modules/file/examples/raw_copy.py#7): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/raw_copy.py#14)(api):**
### *recipes* / [file:examples/read\_write\_proto](/recipe_modules/file/examples/read_write_proto.py)
[DEPS](/recipe_modules/file/examples/read_write_proto.py#9): [file](#recipe_modules-file), [path](#recipe_modules-path), [proto](#recipe_modules-proto)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/read_write_proto.py#16)(api):**
### *recipes* / [file:examples/symlink](/recipe_modules/file/examples/symlink.py)
[DEPS](/recipe_modules/file/examples/symlink.py#7): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/symlink.py#14)(api):**
### *recipes* / [file:examples/truncate](/recipe_modules/file/examples/truncate.py)
[DEPS](/recipe_modules/file/examples/truncate.py#7): [file](#recipe_modules-file), [path](#recipe_modules-path)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/file/examples/truncate.py#13)(api):**
### *recipes* / [futures:examples/background\_helper](/recipe_modules/futures/examples/background_helper.py)
[DEPS](/recipe_modules/futures/examples/background_helper.py#9): [futures](#recipe_modules-futures), [json](#recipe_modules-json), [path](#recipe_modules-path), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/background_helper.py#86)(api):**
&mdash; **def [manage\_helper](/recipe_modules/futures/examples/background_helper.py#21)(api, chn):**
&emsp; **@contextmanager**<br>&mdash; **def [run\_helper](/recipe_modules/futures/examples/background_helper.py#59)(api):**
Runs the background helper.
Yields control once helper is ready. Kills helper once leaving the context
manager.
This is an example of what your recipe module code would look like. Note that
we don't pass the channel to the 'user' code (i.e. RunSteps).
### *recipes* / [futures:examples/extreme\_namespaces](/recipe_modules/futures/examples/extreme_namespaces.py)
[DEPS](/recipe_modules/futures/examples/extreme_namespaces.py#7): [context](#recipe_modules-context), [futures](#recipe_modules-futures), [path](#recipe_modules-path), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [Level1](/recipe_modules/futures/examples/extreme_namespaces.py#27)(api, i):**
&mdash; **def [Level2](/recipe_modules/futures/examples/extreme_namespaces.py#15)(api, i):**
&mdash; **def [RunSteps](/recipe_modules/futures/examples/extreme_namespaces.py#33)(api):**
### *recipes* / [futures:examples/fan\_out\_in](/recipe_modules/futures/examples/fan_out_in.py)
[DEPS](/recipe_modules/futures/examples/fan_out_in.py#7): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/fan_out_in.py#13)(api):**
### *recipes* / [futures:examples/lazy\_fan\_out\_in](/recipe_modules/futures/examples/lazy_fan_out_in.py)
[DEPS](/recipe_modules/futures/examples/lazy_fan_out_in.py#7): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/lazy_fan_out_in.py#13)(api):**
### *recipes* / [futures:examples/lazy\_fan\_out\_in\_early\_abort](/recipe_modules/futures/examples/lazy_fan_out_in_early_abort.py)
[DEPS](/recipe_modules/futures/examples/lazy_fan_out_in_early_abort.py#7): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/lazy_fan_out_in_early_abort.py#13)(api):**
### *recipes* / [futures:examples/lottasteps](/recipe_modules/futures/examples/lottasteps.py)
[DEPS](/recipe_modules/futures/examples/lottasteps.py#20): [futures](#recipe_modules-futures), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This tests the engine's ability to handle many simultaneously-started steps.
Prior to this, logdog butler and the recipe engine would run out of file
handles, because every spawn_immediate would immediately generate all log
handles for the step, instead of waiting for the step's cost to be available.
&mdash; **def [RunSteps](/recipe_modules/futures/examples/lottasteps.py#29)(api, props):**
### *recipes* / [futures:examples/metadata](/recipe_modules/futures/examples/metadata.py)
[DEPS](/recipe_modules/futures/examples/metadata.py#9): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This tests metadata features of the Future object.
&mdash; **def [RunSteps](/recipe_modules/futures/examples/metadata.py#15)(api):**
### *recipes* / [futures:examples/result](/recipe_modules/futures/examples/result.py)
[DEPS](/recipe_modules/futures/examples/result.py#7): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/result.py#13)(api):**
### *recipes* / [futures:examples/semaphore](/recipe_modules/futures/examples/semaphore.py)
[DEPS](/recipe_modules/futures/examples/semaphore.py#7): [futures](#recipe_modules-futures), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/futures/examples/semaphore.py#20)(api):**
&mdash; **def [worker](/recipe_modules/futures/examples/semaphore.py#13)(api, sem, i, N):**
### *recipes* / [generator\_script:examples/full](/recipe_modules/generator_script/examples/full.py)
[DEPS](/recipe_modules/generator_script/examples/full.py#9): [generator\_script](#recipe_modules-generator_script), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/generator_script/examples/full.py#21)(api, script_name):**
### *recipes* / [golang:examples/full](/recipe_modules/golang/examples/full.py)
[DEPS](/recipe_modules/golang/examples/full.py#7): [golang](#recipe_modules-golang), [platform](#recipe_modules-platform), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/golang/examples/full.py#14)(api):**
### *recipes* / [json:examples/full](/recipe_modules/json/examples/full.py)
[DEPS](/recipe_modules/json/examples/full.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/json/examples/full.py#23)(api):**
### *recipes* / [json:tests/add\_json\_log](/recipe_modules/json/tests/add_json_log.py)
[DEPS](/recipe_modules/json/tests/add_json_log.py#7): [json](#recipe_modules-json), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/json/tests/add_json_log.py#12)(api):**
### *recipes* / [led:tests/full](/recipe_modules/led/tests/full.py)
[DEPS](/recipe_modules/led/tests/full.py#16): [buildbucket](#recipe_modules-buildbucket), [led](#recipe_modules-led), [properties](#recipe_modules-properties), [proto](#recipe_modules-proto), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/led/tests/full.py#32)(api, get_cmd, child_properties, sloppy_child_properties, do_bogus_edits):**
### *recipes* / [led:tests/no\_exist](/recipe_modules/led/tests/no_exist.py)
[DEPS](/recipe_modules/led/tests/no_exist.py#7): [led](#recipe_modules-led), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/led/tests/no_exist.py#12)(api):**
### *recipes* / [led:tests/trigger\_build](/recipe_modules/led/tests/trigger_build.py)
[DEPS](/recipe_modules/led/tests/trigger_build.py#9): [led](#recipe_modules-led), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/led/tests/trigger_build.py#15)(api):**
### *recipes* / [legacy\_annotation:examples/full](/recipe_modules/legacy_annotation/examples/full.py)
[DEPS](/recipe_modules/legacy_annotation/examples/full.py#7): [legacy\_annotation](#recipe_modules-legacy_annotation), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/legacy_annotation/examples/full.py#20)(api):**
### *recipes* / [milo:examples/full](/recipe_modules/milo/examples/full.py)
[DEPS](/recipe_modules/milo/examples/full.py#9): [milo](#recipe_modules-milo)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/milo/examples/full.py#13)(api):**
### *recipes* / [nodejs:examples/full](/recipe_modules/nodejs/examples/full.py)
[DEPS](/recipe_modules/nodejs/examples/full.py#7): [nodejs](#recipe_modules-nodejs), [platform](#recipe_modules-platform), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/nodejs/examples/full.py#14)(api):**
### *recipes* / [path:examples/full](/recipe_modules/path/examples/full.py)
[DEPS](/recipe_modules/path/examples/full.py#7): [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/path/examples/full.py#19)(api):**
### *recipes* / [placeholder](/recipes/placeholder.py)
[DEPS](/recipes/placeholder.py#7): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step), [swarming](#recipe_modules-swarming), [time](#recipe_modules-time)
PYTHON_VERSION_COMPATIBILITY: PY3
&mdash; **def [RunSteps](/recipes/placeholder.py#30)(api, properties):**
### *recipes* / [platform:examples/full](/recipe_modules/platform/examples/full.py)
[DEPS](/recipe_modules/platform/examples/full.py#7): [platform](#recipe_modules-platform), [step](#recipe_modules-step), [version](#recipe_modules-version)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/platform/examples/full.py#13)(api):**
### *recipes* / [properties:examples/full](/recipe_modules/properties/examples/full.py)
[DEPS](/recipe_modules/properties/examples/full.py#16): [json](#recipe_modules-json), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/properties/examples/full.py#25)(api, props, env_props):**
### *recipes* / [proto:tests/encode\_decode](/recipe_modules/proto/tests/encode_decode.py)
[DEPS](/recipe_modules/proto/tests/encode_decode.py#7): [assertions](#recipe_modules-assertions), [path](#recipe_modules-path), [proto](#recipe_modules-proto), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/proto/tests/encode_decode.py#17)(api):**
### *recipes* / [proto:tests/placeholders](/recipe_modules/proto/tests/placeholders.py)
[DEPS](/recipe_modules/proto/tests/placeholders.py#7): [path](#recipe_modules-path), [proto](#recipe_modules-proto), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/proto/tests/placeholders.py#16)(api):**
### *recipes* / [random:tests/full](/recipe_modules/random/tests/full.py)
[DEPS](/recipe_modules/random/tests/full.py#7): [random](#recipe_modules-random), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/random/tests/full.py#13)(api):**
### *recipes* / [raw\_io:examples/full](/recipe_modules/raw_io/examples/full.py)
[DEPS](/recipe_modules/raw_io/examples/full.py#8): [path](#recipe_modules-path), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/raw_io/examples/full.py#17)(api):**
### *recipes* / [raw\_io:tests/output\_mismatch](/recipe_modules/raw_io/tests/output_mismatch.py)
[DEPS](/recipe_modules/raw_io/tests/output_mismatch.py#10): [assertions](#recipe_modules-assertions), [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY3
&mdash; **def [RunSteps](/recipe_modules/raw_io/tests/output_mismatch.py#17)(api):**
### *recipes* / [resultdb:examples/exonerate](/recipe_modules/resultdb/examples/exonerate.py)
[DEPS](/recipe_modules/resultdb/examples/exonerate.py#13): [context](#recipe_modules-context), [json](#recipe_modules-json), [properties](#recipe_modules-properties), [resultdb](#recipe_modules-resultdb), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/exonerate.py#41)(api):**
### *recipes* / [resultdb:examples/get\_included\_invocations](/recipe_modules/resultdb/examples/get_included_invocations.py)
[DEPS](/recipe_modules/resultdb/examples/get_included_invocations.py#9): [assertions](#recipe_modules-assertions), [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/get_included_invocations.py#15)(api):**
### *recipes* / [resultdb:examples/include](/recipe_modules/resultdb/examples/include.py)
[DEPS](/recipe_modules/resultdb/examples/include.py#13): [context](#recipe_modules-context), [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/include.py#19)(api):**
### *recipes* / [resultdb:examples/query](/recipe_modules/resultdb/examples/query.py)
[DEPS](/recipe_modules/resultdb/examples/query.py#17): [buildbucket](#recipe_modules-buildbucket), [resultdb](#recipe_modules-resultdb), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/query.py#24)(api):**
### *recipes* / [resultdb:examples/query\_test\_result\_statistics](/recipe_modules/resultdb/examples/query_test_result_statistics.py)
[DEPS](/recipe_modules/resultdb/examples/query_test_result_statistics.py#12): [context](#recipe_modules-context), [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/query_test_result_statistics.py#18)(api):**
### *recipes* / [resultdb:examples/result\_history](/recipe_modules/resultdb/examples/result_history.py)
[DEPS](/recipe_modules/resultdb/examples/result_history.py#13): [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/result_history.py#18)(api):**
### *recipes* / [resultdb:examples/resultsink](/recipe_modules/resultdb/examples/resultsink.py)
[DEPS](/recipe_modules/resultdb/examples/resultsink.py#9): [context](#recipe_modules-context), [resultdb](#recipe_modules-resultdb), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/resultsink.py#16)(api):**
### *recipes* / [resultdb:examples/test\_presentation](/recipe_modules/resultdb/examples/test_presentation.py)
[DEPS](/recipe_modules/resultdb/examples/test_presentation.py#7): [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/test_presentation.py#11)(api):**
### *recipes* / [resultdb:examples/test\_presentation\_default](/recipe_modules/resultdb/examples/test_presentation_default.py)
[DEPS](/recipe_modules/resultdb/examples/test_presentation_default.py#7): [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/test_presentation_default.py#11)(api):**
### *recipes* / [resultdb:examples/upload\_invocation\_artifacts](/recipe_modules/resultdb/examples/upload_invocation_artifacts.py)
[DEPS](/recipe_modules/resultdb/examples/upload_invocation_artifacts.py#12): [resultdb](#recipe_modules-resultdb)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/resultdb/examples/upload_invocation_artifacts.py#17)(api):**
### *recipes* / [runtime:tests/full](/recipe_modules/runtime/tests/full.py)
[DEPS](/recipe_modules/runtime/tests/full.py#9): [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/runtime/tests/full.py#15)(api):**
### *recipes* / [scheduler:examples/emit\_triggers](/recipe_modules/scheduler/examples/emit_triggers.py)
[DEPS](/recipe_modules/scheduler/examples/emit_triggers.py#9): [buildbucket](#recipe_modules-buildbucket), [json](#recipe_modules-json), [runtime](#recipe_modules-runtime), [scheduler](#recipe_modules-scheduler), [time](#recipe_modules-time)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This file is a recipe demonstrating emitting triggers to LUCI Scheduler.
&mdash; **def [RunSteps](/recipe_modules/scheduler/examples/emit_triggers.py#18)(api):**
### *recipes* / [scheduler:examples/info](/recipe_modules/scheduler/examples/info.py)
[DEPS](/recipe_modules/scheduler/examples/info.py#9): [scheduler](#recipe_modules-scheduler), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This file is a recipe demonstrating reading/mocking scheduler host.
&mdash; **def [RunSteps](/recipe_modules/scheduler/examples/info.py#14)(api):**
### *recipes* / [scheduler:examples/triggers](/recipe_modules/scheduler/examples/triggers.py)
[DEPS](/recipe_modules/scheduler/examples/triggers.py#16): [json](#recipe_modules-json), [scheduler](#recipe_modules-scheduler), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This file is a recipe demonstrating reading triggers of the current build.
&mdash; **def [RunSteps](/recipe_modules/scheduler/examples/triggers.py#22)(api):**
### *recipes* / [service\_account:examples/full](/recipe_modules/service_account/examples/full.py)
[DEPS](/recipe_modules/service_account/examples/full.py#9): [path](#recipe_modules-path), [platform](#recipe_modules-platform), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [service\_account](#recipe_modules-service_account)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/service_account/examples/full.py#23)(api, key_path, scopes):**
### *recipes* / [step:examples/full](/recipe_modules/step/examples/full.py)
[DEPS](/recipe_modules/step/examples/full.py#9): [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/examples/full.py#27)(api, bad_return, access_invalid_data, access_deep_invalid_data, assign_extra_junk, timeout):**
### *recipes* / [step:tests/active\_result](/recipe_modules/step/tests/active_result.py)
[DEPS](/recipe_modules/step/tests/active_result.py#7): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/active_result.py#12)(api):**
### *recipes* / [step:tests/defer](/recipe_modules/step/tests/defer.py)
[DEPS](/recipe_modules/step/tests/defer.py#7): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/defer.py#11)(api):**
### *recipes* / [step:tests/empty](/recipe_modules/step/tests/empty.py)
[DEPS](/recipe_modules/step/tests/empty.py#11): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/empty.py#16)(api):**
### *recipes* / [step:tests/inject\_paths](/recipe_modules/step/tests/inject_paths.py)
[DEPS](/recipe_modules/step/tests/inject_paths.py#7): [context](#recipe_modules-context), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/inject_paths.py#15)(api):**
### *recipes* / [step:tests/nested](/recipe_modules/step/tests/nested.py)
[DEPS](/recipe_modules/step/tests/nested.py#11): [context](#recipe_modules-context), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/nested.py#17)(api):**
### *recipes* / [step:tests/raise\_on\_failure](/recipe_modules/step/tests/raise_on_failure.py)
[DEPS](/recipe_modules/step/tests/raise_on_failure.py#9): [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/raise_on_failure.py#19)(api, infra_step, set_status_to_exception):**
### *recipes* / [step:tests/stdio](/recipe_modules/step/tests/stdio.py)
[DEPS](/recipe_modules/step/tests/stdio.py#7): [raw\_io](#recipe_modules-raw_io), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/stdio.py#13)(api):**
### *recipes* / [step:tests/step\_call\_args](/recipe_modules/step/tests/step_call_args.py)
[DEPS](/recipe_modules/step/tests/step_call_args.py#9): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/step_call_args.py#14)(api):**
### *recipes* / [step:tests/step\_cost](/recipe_modules/step/tests/step_cost.py)
[DEPS](/recipe_modules/step/tests/step_cost.py#9): [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/step_cost.py#14)(api):**
### *recipes* / [step:tests/sub\_build](/recipe_modules/step/tests/sub_build.py)
[DEPS](/recipe_modules/step/tests/sub_build.py#20): [assertions](#recipe_modules-assertions), [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/sub_build.py#31)(api, props):**
### *recipes* / [step:tests/timeout](/recipe_modules/step/tests/timeout.py)
[DEPS](/recipe_modules/step/tests/timeout.py#9): [properties](#recipe_modules-properties), [step](#recipe_modules-step)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/step/tests/timeout.py#20)(api, timeout):**
### *recipes* / [swarming:examples/full](/recipe_modules/swarming/examples/full.py)
[DEPS](/recipe_modules/swarming/examples/full.py#16): [cipd](#recipe_modules-cipd), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/swarming/examples/full.py#28)(api):**
### *recipes* / [swarming:examples/this\_task](/recipe_modules/swarming/examples/this_task.py)
[DEPS](/recipe_modules/swarming/examples/this_task.py#7): [step](#recipe_modules-step), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/swarming/examples/this_task.py#13)(api):**
### *recipes* / [swarming:tests/copy](/recipe_modules/swarming/tests/copy.py)
[DEPS](/recipe_modules/swarming/tests/copy.py#9): [assertions](#recipe_modules-assertions), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/swarming/tests/copy.py#15)(api):**
### *recipes* / [swarming:tests/realms](/recipe_modules/swarming/tests/realms.py)
[DEPS](/recipe_modules/swarming/tests/realms.py#9): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [context](#recipe_modules-context), [step](#recipe_modules-step), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/swarming/tests/realms.py#18)(api):**
### *recipes* / [swarming:tests/task\_request\_from\_jsonish](/recipe_modules/swarming/tests/task_request_from_jsonish.py)
[DEPS](/recipe_modules/swarming/tests/task_request_from_jsonish.py#9): [assertions](#recipe_modules-assertions), [swarming](#recipe_modules-swarming)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/swarming/tests/task_request_from_jsonish.py#15)(api):**
### *recipes* / [time:examples/full](/recipe_modules/time/examples/full.py)
[DEPS](/recipe_modules/time/examples/full.py#11): [properties](#recipe_modules-properties), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step), [time](#recipe_modules-time)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/time/examples/full.py#19)(api):**
### *recipes* / [tricium:examples/add\_comment](/recipe_modules/tricium/examples/add_comment.py)
[DEPS](/recipe_modules/tricium/examples/add_comment.py#10): [properties](#recipe_modules-properties), [tricium](#recipe_modules-tricium)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/tricium/examples/add_comment.py#17)(api, trigger_type_error):**
### *recipes* / [tricium:examples/wrapper](/recipe_modules/tricium/examples/wrapper.py)
[DEPS](/recipe_modules/tricium/examples/wrapper.py#14): [file](#recipe_modules-file), [path](#recipe_modules-path), [tricium](#recipe_modules-tricium)
PYTHON_VERSION_COMPATIBILITY: PY2+3
An example of a recipe wrapping legacy analyzers.
&mdash; **def [RunSteps](/recipe_modules/tricium/examples/wrapper.py#21)(api):**
### *recipes* / [tricium:tests/add\_comment\_validation](/recipe_modules/tricium/tests/add_comment_validation.py)
[DEPS](/recipe_modules/tricium/tests/add_comment_validation.py#10): [properties](#recipe_modules-properties), [tricium](#recipe_modules-tricium)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/tricium/tests/add_comment_validation.py#43)(api, case):**
### *recipes* / [tricium:tests/enforce\_comments\_num\_limit](/recipe_modules/tricium/tests/enforce_comments_num_limit.py)
[DEPS](/recipe_modules/tricium/tests/enforce_comments_num_limit.py#12): [assertions](#recipe_modules-assertions), [properties](#recipe_modules-properties), [proto](#recipe_modules-proto), [tricium](#recipe_modules-tricium)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/tricium/tests/enforce_comments_num_limit.py#22)(api, props):**
### *recipes* / [url:examples/full](/recipe_modules/url/examples/full.py)
[DEPS](/recipe_modules/url/examples/full.py#7): [context](#recipe_modules-context), [path](#recipe_modules-path), [step](#recipe_modules-step), [url](#recipe_modules-url)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/url/examples/full.py#29)(api):**
### *recipes* / [url:tests/join](/recipe_modules/url/tests/join.py)
[DEPS](/recipe_modules/url/tests/join.py#7): [step](#recipe_modules-step), [url](#recipe_modules-url)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/url/tests/join.py#13)(api):**
### *recipes* / [url:tests/validate\_url](/recipe_modules/url/tests/validate_url.py)
[DEPS](/recipe_modules/url/tests/validate_url.py#9): [properties](#recipe_modules-properties), [step](#recipe_modules-step), [url](#recipe_modules-url)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/url/tests/validate_url.py#16)(api):**
### *recipes* / [uuid:examples/full](/recipe_modules/uuid/examples/full.py)
[DEPS](/recipe_modules/uuid/examples/full.py#7): [step](#recipe_modules-step), [uuid](#recipe_modules-uuid)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/uuid/examples/full.py#13)(api):**
### *recipes* / [version:examples/full](/recipe_modules/version/examples/full.py)
[DEPS](/recipe_modules/version/examples/full.py#7): [version](#recipe_modules-version)
PYTHON_VERSION_COMPATIBILITY: PY2+3
&mdash; **def [RunSteps](/recipe_modules/version/examples/full.py#12)(api):**
### *recipes* / [warning:tests/fakes](/recipe_modules/warning/tests/fakes.py)
PYTHON_VERSION_COMPATIBILITY: PY2+3
This is a fake recipe to trick the simulation and make it believes that
this module has tests. The actual test for this module is done via unit test
because the `issue` method can only be used from recipe_modules, not recipes.
&mdash; **def [RunSteps](/recipe_modules/warning/tests/fakes.py#14)(api):**