API documentation¶
The following documentation is based on the source code of version 23.2 of the executor package. The following modules are available:
- The
executormodule - The
executor.chrootmodule - The
executor.climodule - The
executor.concurrentmodule - The
executor.contextsmodule - The
executor.processmodule - The
executor.schrootmodule - The
executor.ssh.clientmodule - The
executor.ssh.servermodule - The
executor.tcpmodule
The executor module¶
Core functionality of the executor package.
If you’re looking for an easy way to run external commands from Python take a
look at the execute() function. When you need more flexibility consider
using the underlying ExternalCommand class directly instead.
execute() versus ExternalCommand¶
In executor 1.x the execute() function was the only interface
for external command execution. This had several drawbacks:
- The documentation for the
execute()function was getting way too complex given all of the supported options and combinations. - There was no way to execute asynchronous external commands (running in the
background) without sidestepping the complete
executormodule and going straight forsubprocess.Popen(with all of the verbosity that you get for free withsubprocess:-). - There was no way to prepare an external command without starting it immediately, making it impossible to prepare a batch of external commands before starting them (whether synchronously or asynchronously).
To solve these problems executor 2.x introduced the
ExternalCommand class. This explains why execute() is now a
trivial wrapper around ExternalCommand: It’s main purpose is to be an
easy to use shortcut that preserves compatibility with the old interface.
Classes and functions¶
-
executor.DEFAULT_ENCODING= 'UTF-8'¶ The default encoding of the standard input, output and error streams (a string).
-
executor.DEFAULT_WORKING_DIRECTORY= '.'¶ The default working directory for external commands (a string). Defaults to the working directory of the current process using
os.curdir.
-
executor.DEFAULT_SHELL= 'bash'¶ The default shell used to evaluate shell expressions (a string).
This variable isn’t based on the
$SHELLenvironment variable because:- Shells like
sh,dash,bashandzshall have their own subtly incompatible semantics. - People regularly use shells like
fishas their default login shell :-).
At an interactive prompt this is no problem (advanced users have obviously learned to context switch) but when you’re writing source code the last thing you want to worry about is which shell is going to evaluate your commands! The
executorpackage expects this shell to support the following features:- The
-coption to evaluate a shell command provided as a command line argument. - The
-argument to instruct the shell to read shell commands from its standard input stream and evaluate those.
Apart from these two things nothing else is expected from the default shell so you’re free to customize it if you really want to write your shell commands in
fishorzshsyntax :-).- Shells like
-
executor.COMMAND_NOT_FOUND_CODES= (2,)¶ Numeric error codes returned when a command isn’t available on the system (a tuple of integers).
-
executor.COMMAND_NOT_FOUND_STATUS= 127¶ The exit status used by shells when a command is not found (an integer).
-
executor.execute(*command, **options)[source]¶ Execute an external command and make sure it succeeded.
Parameters: - command – All positional arguments are passed on to the constructor
of
ExternalCommand. - options – All keyword arguments are passed on to the constructor of
ExternalCommand.
Returns: Refer to
execute_prepared().Raises: ExternalCommandFailedwhen the command exits with a nonzero exit code (andcheckisTrue).If
asynchronousisTruethenexecute()will automatically start the external command for you usingstart()(but it won’t wait for it to end). If you want to create anExternalCommandobject instance without immediately starting the external command then you can useExternalCommanddirectly.Some examples
By default the status code of the external command is returned as a boolean:
>>> from executor import execute >>> execute('true') True
However when an external command exits with a nonzero status code an exception is raised, this is intended to “make it easy to do the right thing” (never forget to check the status code of an external command without having to write a lot of repetitive code):
>>> execute('false') Traceback (most recent call last): File "executor/__init__.py", line 124, in execute cmd.start() File "executor/__init__.py", line 516, in start self.wait() File "executor/__init__.py", line 541, in wait self.check_errors() File "executor/__init__.py", line 568, in check_errors raise ExternalCommandFailed(self) executor.ExternalCommandFailed: External command failed with exit code 1! (command: false)
What’s also useful to know is that exceptions raised by
execute()exposecommandandreturncodeattributes. If you know a command is likely to exit with a nonzero status code and you wantexecute()to simply return a boolean you can do this instead:>>> execute('false', check=False) False
- command – All positional arguments are passed on to the constructor
of
-
executor.execute_prepared(command)[source]¶ The logic behind
execute()andremote().Parameters: command – An ExternalCommandobject (or an object created from a subclass with a compatible interface like for exampleRemoteCommand).Returns: The return value of this function depends on several options: - If
asynchronousisTruethe constructedExternalCommandobject is returned. - If
callbackis set the value ofresultis returned. - If
captureisTruethe value ofExternalCommand.outputis returned. - By default the value of
succeededis returned.
Raises: See execute()andremote().- If
-
class
executor.ExternalCommand(*command, **options)[source]¶ Programmer friendly
subprocess.Popenwrapper.The
ExternalCommandclass wrapssubprocess.Popento make it easier to do the right thing (the simplicity ofos.system()with the robustness ofsubprocess.Popen) and to provide additional features (e.g. asynchronous command execution that preserves the ability to provide input and capture output).- Process manipulation
ExternalCommandinherits fromControllableProcesswhich means that all of the process manipulation supported byControllableProcessis also supported byExternalCommandobjects.- Context manager
ExternalCommandobjects can be used as context managers by using thewithstatement:- When the scope of the
withstatement starts thestart()method is called (if the external command isn’t already running). - When the scope of the
withstatement endsterminate()is called if the command is still running. Theload_output()andcleanup()functions are used to cleanup after the external command. If an exception isn’t already being raisedcheck_errors()is called to make sure the external command succeeded.
- When the scope of the
- Event callbacks
The
start_event,retry_eventandfinish_eventproperties can be set to callbacks (callable values like functions) to subscribe to the corresponding events. The callback receives a single positional argument which is theExternalCommandobject.The
start_eventandfinish_eventproperties were originally created for use in command pools, for example to report to the operator when specific commands are started and when they finish. The event handling is performed inside theExternalCommandclass though, so you’re free to repurpose these events outside the context of command pools.
Here’s an overview of the
ExternalCommandclass:You can set the values of the
asynchronous,buffer_size,buffered,callback,capture,capture_stderr,check,command,dependencies,directory,encoding,environment,error_message,error_type,fakeroot,finish_event,group_by,input,ionice,merge_streams,really_silent,retry,retry_allowed,retry_count,retry_event,retry_limit,returncode,shell,silent,start_event,stderr_file,stdout_file,subprocess,sudo,tty,uid,user,virtual_environmentandwas_startedproperties by passing keyword arguments to the class initializer.-
__init__(*command, **options)[source]¶ Initialize an
ExternalCommandobject.Parameters: - command – Any positional arguments are converted to a list and
used to set
command. - options – Keyword arguments can be used to conveniently override
the default values of
asynchronous,callback,capture,capture_stderr,check,directory,encoding,environment,fakeroot,input,logger,merge_streams,really_silent,shell,silent,stdout_file,stderr_file,uid,user,sudoandvirtual_environment. Keyword argument that are not supported will raiseTypeErroras usual.
The external command is not started until you call
start()orwait().- command – Any positional arguments are converted to a list and
used to set
-
asynchronous[source]¶ Enable asynchronous command execution.
If this option is
True(not the default) preparations are made to execute the external command asynchronously (in the background). This has several consequences:Calling
start()will start the external command but will not block until the external command is finished, instead you are responsible for callingwait()at some later point in time.When
inputis set its value will be written to a temporary file and the standard input stream of the external command is connected to read from the temporary file.By using a temporary file the external command can consume its input as fast or slow as it pleases without needing a separate thread or process to “feed” the external command.
When
captureisTruethe standard output of the external command is redirected to a temporary file whose contents are read once the external command has finished.By using a temporary file the external command can produce output as fast or slow as it pleases without needing a thread or subprocess on our side to consume the output in real time.
See also
async(backwards compatible alias)Note
The
asynchronousproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
buffer_size[source]¶ Control the size of the stdin/stdout/stderr pipe buffers.
The value of
buffer_sizebecomes the bufsize argument that’s passed tosubprocess.Popenbystart().If
asynchronousisTrueandbufferedisFalsethe value ofbuffer_sizedefaults to 0 which means unbuffered, in all other cases its value defaults to -1 which means to use the system default buffer size.Note
The
buffer_sizeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
buffered[source]¶ Control whether command output is buffered to temporary files.
When
asynchronousisTrueand the standard output and/or error streams are being captured, temporary files will be used to collect the output. This enables the use of theoutput,stdoutandstderrproperties to easily get the full output of the command in a single string.You can set
bufferedtoFalseto disable the use of temporary files, in this casesubprocess.PIPEis passed tosubprocess.Popen. Onceis_runningisTrueyou can use thestdin,stdoutand/orstderrproperties to communicate with the command.This enables runtime processing of the standard input, output and error streams and makes it possible to run commands that never return but keep producing output (for example
xscreensaver-command -watch). Here’s an example that setsbufferedtoFalseand uses the magic method__iter__()to iterate over the lines of output in realtime:# Run external commands when xscreensaver changes state. import os from executor import execute known_states = set(['BLANK', 'LOCK', 'UNBLANK']) while True: options = dict(asynchronous=True, capture=True, buffered=False) with execute('xscreensaver-command', '-watch', **options) as command: for line in command: tokens = line.split() if tokens and tokens[0] in known_states: value = os.environ.get('XSCREENSAVER_%s_COMMAND' % tokens[0]) if value: execute(value)
Some sanity checks and error handling have been omitted from the example above, in order to keep it simple, but I did test it and it should actually work (at least it did for me):
$ export XSCREENSAVER_BLANK_COMMAND='echo $(date) - Screen is now blanked' $ export XSCREENSAVER_LOCK_COMMAND='echo $(date) - Screen is now locked' $ export XSCREENSAVER_UNBLANK_COMMAND='echo $(date) - Screen is now unblanked' $ python xscreensaver-monitor.py Sat Jan 20 16:03:07 CET 2018 - Screen is now blanked Sat Jan 20 16:03:15 CET 2018 - Screen is now unblanked Sat Jan 20 16:03:20 CET 2018 - Screen is now locked Sat Jan 20 16:03:34 CET 2018 - Screen is now unblanked
Note
The
bufferedproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
callback[source]¶ Optional callback used to generate the value of
result.The
callbackandresultproperties were created for use in command pools, where it can be useful to define how to process (parse) a command’s output when the command is constructed.Note
The
callbackproperty is awritable_property. You can change the value of this property using normal attribute assignment syntax.
-
capture[source]¶ Enable capturing of the standard output stream.
If this option is
True(not the default) the standard output of the external command is captured and made available to the caller viastdoutandoutput.The standard error stream will not be captured, use
capture_stderrfor that. You can also silence the standard error stream using thesilentoption.If
callbackis setcapturedefaults toTrue(but you can still setcapturetoFalseif that is what you want).Note
The
captureproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
capture_stderr[source]¶ Enable capturing of the standard error stream.
If this option is
True(not the default) the standard error stream of the external command is captured and made available to the caller viastderr.Note
The
capture_stderrproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
check[source]¶ Enable automatic status code checking.
If this option is
True(the default) and the external command exits with a nonzero status codeExternalCommandFailedwill be raised bystart()(whenasynchronousisn’t set) orwait()(whenasynchronousis set).Note
The
checkproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command[source]¶ A list of strings with the command to execute.
Note
In executor version 14.0 it became valid to set
inputandshellwithout providingcommand(in older versions it was required to setcommandregardless of the other options).Note
The
commandproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command_line¶ The command line of the external command.
The command line used to actually run the external command requested by the user (a list of strings). The command line is constructed based on
commandaccording to the following rules:- If
shellisTruethe external command is run usingbash -c '...'(assuming you haven’t changedDEFAULT_SHELL) which means constructs like semicolons, ampersands and pipes can be used (and all the usual caveats apply :-). - If
virtual_environmentis set the command is converted to a shell command line and prefixed by the applicablesource ...command. - If
uidoruseris set thesudo -ucommand will be prefixed to the command line generated here. - If
fakerootorsudois set the respective command name is prefixed to the command line generated here (sudois only prefixed when the current process doesn’t already have super user privileges). - If
ioniceis set the appropriate command is prefixed to the command line generated here.
- If
-
decoded_stdout¶ The value of
stdoutdecoded usingencoding.This is a
unicode()object (in Python 2) or astrobject (in Python 3).
-
decoded_stderr¶ The value of
stderrdecoded usingencoding.This is a
unicode()object (in Python 2) or astrobject (in Python 3).
-
dependencies[source]¶ The dependencies of the command (a list of
ExternalCommandobjects).The
dependenciesproperty enables low level concurrency control in command pools by imposing a specific order of execution:- Command pools will never start a command until the
is_finishedproperties of all of the command’sdependenciesareTrue. - If
dependenciesis empty it has no effect and concurrency is controlled bygroup_byandconcurrency.
Note
The
dependenciesproperty is acustom_property. You can change the value of this property using normal attribute assignment syntax. This property’s value is computed once (the first time it is accessed) and the result is cached.- Command pools will never start a command until the
-
directory[source]¶ The working directory for the external command.
A string, defaults to
DEFAULT_WORKING_DIRECTORY.Note
The
directoryproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
encoded_input¶ The value of
inputencoded usingencoding.This is a
strobject (in Python 2) or abytesobject (in Python 3).
-
encoding[source]¶ The character encoding of standard input and standard output.
A string, defaults to
DEFAULT_ENCODING. This option is used to encodeinputand to decodeoutput.Note
The
encodingproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
environment[source]¶ A dictionary of environment variables for the external command.
You only need to specify environment variables that differ from those of the current process (that is to say the environment variables of the current process are merged with the variables that you specify here).
Note
The
environmentproperty is acustom_property. You can change the value of this property using normal attribute assignment syntax. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
error_message[source]¶ A string describing how the external command failed or
None.Note
The
error_messageproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
error_type[source]¶ An appropriate exception class or
None(when no error occurred).CommandNotFoundif the external command exits with return codeCOMMAND_NOT_FOUND_STATUSorExternalCommandFailedif the external command exits with any other nonzero return code.Note
The
error_typeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
failed¶ Whether the external command has failed.
Trueifreturncodeis a nonzero number orerror_typeis set (e.g. because the external command doesn’t exist).Falseifreturncodeis zero.Nonewhen the external command hasn’t been started or is still running.
-
fakeroot[source]¶ Run the external command under
fakeroot.If this option is
True(not the default) and the current process doesn’t have superuser privileges the external command is run withfakeroot. If thefakerootprogram is not installed the external command will fail.Note
The
fakerootproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
finish_event[source]¶ Optional callback that’s called just after the command finishes (see event callbacks).
Note
The
finish_eventproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
group_by[source]¶ Identifier that’s used to group the external command (any hashable value).
The
group_byproperty enables high level concurrency control in command pools by making it easy to control which commands are allowed to run concurrently and which are required to run serially:- Command pools will never start more than one command within a group
of commands that share the same value of
group_by(for values that aren’tNone). - If
group_byisNoneit has no effect and concurrency is controlled bydependenciesandconcurrency.
Note
The
group_byproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().- Command pools will never start more than one command within a group
of commands that share the same value of
-
have_superuser_privileges¶ Whether the parent Python process is running under superuser privileges.
Trueif running with superuser privileges,Falseotherwise. Used bycommand_lineto decide whethersudoneeds to be used.
-
input[source]¶ The input to feed to the external command on the standard input stream.
When you provide a
unicode()object (in Python 2) or astrobject (in Python 3) as input it will be encoded usingencoding. To avoid the automatic conversion you can simply pass astrobject (in Python 2) or abytesobject (in Python 3). This conversion logic is implemented in theencoded_inputattribute.When
inputis set toTruea pipe will be created to communicate with the external command in real time. See also thebufferedandstdinproperties.Defaults to
None.Note
The
inputproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
ionice[source]¶ The I/O scheduling class for the external command (a string or
None).When this property is set then ionice will be used to set the I/O scheduling class for the external command. This can be useful to reduce the impact of heavy disk operations on the rest of the system.
Raises: Any exceptions raised by validate_ionice_class().Note
The
ioniceproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
is_finished¶ Whether the external command has finished execution (excluding retries).
Trueonce the external command has been started and has since finished (excluding retries),Falsewhen the external command hasn’t been started yet or is still running.
-
is_finished_with_retries¶ Whether the external command has finished execution (including retries).
Trueonce the external command has been started and has since finished (including retries),Falsewhen the external command hasn’t been started yet, is still running or can be retried.
-
is_terminated¶ Whether the external command has been terminated (a boolean).
Trueif the external command was terminated usingSIGTERM(e.g. byterminate()),Falseotherwise.
-
merge_streams[source]¶ Whether to merge the standard output and error streams.
A boolean, defaults to
False. If this option is enabledstdoutwill contain the external command’s output on both streams.Note
The
merge_streamsproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
output¶ The value of
stdoutdecoded usingencoding.This is a
unicode()object (in Python 2) or astrobject (in Python 3).This is only available when
captureisTrue. Ifcaptureis notTruethenoutputwill beNone.After decoding any leading and trailing whitespace is stripped and if the resulting string doesn’t contain any remaining newlines then the string with leading and trailing whitespace stripped will be returned, otherwise the decoded string is returned unchanged:
>>> from executor import ExternalCommand >>> cmd = ExternalCommand('echo naïve', capture=True) >>> cmd.start() >>> cmd.output u'na\xefve' >>> cmd.stdout 'na\xc3\xafve\n'
This is intended to make simple things easy (
outputmakes it easy to deal with external commands that output a single line) while providing an escape hatch when the default assumptions don’t hold (you can always usestdoutto get the raw output).See also the
__iter__()magic method which makes it very easy to iterate over the lines of output produced by the command.
-
really_silent[source]¶ Whether output is really silenced or actually captured (a boolean).
When the
silentoption was originally added to executor it was implemented by redirecting the output streams toos.devnull, similar to howcommand &> /dev/nullworks in Bash.Since I made that decision I’ve regretted it many times because I ran into situations where
checkandsilentwere both set andExternalCommandFailedwas raised but I had no way to determine what had gone wrong.This is why the
really_silentproperty was introduced in executor release 19.0:- When
silentisTrueandcheckisFalsethe value ofreally_silentwill beTrue, otherwise it isFalse. - When
really_silentisFalse(becausecheckisTrue) thesilentproperty effectively becomes an alias forcaptureandcapture_stderrwhich means the output on both streams is captured instead of discarded. - Because output is captured instead of discarded the output of
failing commands can be reported by
ExternalCommandFailed(which is raised becausecheckisTrue).
This change was made after much consideration because it is backwards incompatible and not only in a theoretical sense: Imagine a daemon process spewing megabytes of log output on its standard error stream.
As an escape hatch to restore backwards compatibility you can set
really_silenttoTrueto override the computed value.Note
The
really_silentproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().- When
-
result¶ The result of calling the value given by
callback.If the command hasn’t been started yet
start()is called. When the command hasn’t finished yet func:wait() is called. Ifcallbackisn’t setNoneis returned.
-
retry[source]¶ Whether the external command should be retried when it fails (a boolean, defaults to
False).Warning
Retrying of failing commands is an experimental feature that was introduced with the release of executor 20.0. Please refer to the 20.0 release notes for details.
Note
The
retryproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
retry_allowed[source]¶ Trueif the external command can be retried,Falseotherwise.The value of this property is computed by checking if the following conditions hold:
retryisTrue,failedisTrue,returncodeis notCOMMAND_NOT_FOUND_STATUS,retry_countis lower thanretry_limit(only ifretry_limitis not zero).
Note that when the
retry_eventcallback returnsFalseto cancel the retrying of a failed command, the computed value ofretry_allowedis overridden by assigningretry_allowedthe valueFalse.Note
The
retry_allowedproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
retry_count[source]¶ The number of times that the command was retried (an integer number, defaults to 0).
The value of
retry_countis automatically incremented bystart_once()when it notices thatwas_startedisTruebeforestart_once()has started the command.Note
The
retry_countproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
retry_event[source]¶ Optional callback that’s called when a command is retried (see event callbacks).
The callback can return
Falseto abort retrying.Note
The
retry_eventproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
retry_limit[source]¶ The maximum number of times to retry the command when it fails (an integer, defaults to 2).
Given the default value of two, when
retryisTruethe command will be run at most three times (the initial run and two retries). The value 0 means the command will be retried until it succeeds.Note
The
retry_limitproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
returncode[source]¶ The return code of the external command (an integer) or
None.This will be
Noneuntil the external command has finished.Note
The
returncodeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
shell[source]¶ Whether to evaluate the external command as a shell command.
A boolean, the default depends on the value of
command:- If
commandcontains a single stringshelldefaults toTrue. - If
commandcontains more than one stringshelldefaults toFalse.
When
shellisTruethe external command is evaluated by the shell given byDEFAULT_SHELL, otherwise the external command is run without shell evaluation.Note
The
shellproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().- If
-
silent[source]¶ Whether the external command’s output should be silenced (a boolean).
If this is
True(not the default) any output of the external command is silenced by redirecting the output streams toos.devnull(ifreally_silentisTrue) or by capturing the output (ifreally_silentisFalse).You can enable
captureandsilenttogether to capture the standard output stream while silencing the standard error stream.Note
The
silentproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
start_event[source]¶ Optional callback that’s called just before the command is started (see event callbacks).
Note
The
start_eventproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
stderr¶ The standard error stream of the external command.
This property is only available when
capture_stderrisTrue. In all other cases the value ofstderrwill beNone.When
bufferedisTrue(the default) this is astrobject (in Python 2) or abytesobject (in Python 3).If you set
bufferedtoFalsethenstderrwill be a pipe that’s connected to the standard error stream of the command (for as long asis_runningisTrue).
-
stderr_file[source]¶ Capture the standard error stream to the given file handle.
When this property is set to a writable file object the standard error stream of the external command is redirected to the given file. The default value of this property is
None.This can be useful to (semi) permanently store command output or to run commands whose output is hidden but can be followed using tail -f if the need arises. By setting
stdout_fileandstderr_fileto the same file object the output from both streams can be merged and redirected to the same file. This accomplishes roughly the same thing as settingmerge_streamsbut leaves the caller in control of the file.If this property isn’t set but
captureisTruethe external command’s output is captured to a temporary file that’s automatically cleaned up after the external command is finished and its output has been cached (read into memory).Note
The
stderr_fileproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
stdin¶ The standard input stream of the external command.
If you set
inputtoTrueandbufferedtoFalsethenstdinwill be a pipe that’s connected to the standard input stream of the command (for as long asis_runningisTrue).
-
stdout¶ The standard output stream of the external command.
This property is only available when
captureisTrue. In all other cases the value ofstdoutwill beNone.When
bufferedisTrue(the default) this is astrobject (in Python 2) or abytesobject (in Python 3).If you set
bufferedtoFalsethenstdoutwill be a pipe that’s connected to the standard output stream of the command (for as long asis_runningisTrue).
-
stdout_file[source]¶ Capture the standard output stream to the given file handle.
When this property is set to a writable file object the standard output stream of the external command is redirected to the given file. The default value of this property is
None.This can be useful to (semi) permanently store command output or to run commands whose output is hidden but can be followed using tail -f if the need arises. By setting
stdout_fileandstderr_fileto the same file object the output from both streams can be merged and redirected to the same file. This accomplishes roughly the same thing as settingmerge_streamsbut leaves the caller in control of the file.If this property isn’t set but
captureisTruethe external command’s output is captured to a temporary file that’s automatically cleaned up after the external command is finished and its output has been cached (read into memory).Note
The
stdout_fileproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
subprocess[source]¶ A
subprocess.Popenobject orNone.The value of this property is set by
start()and it’s cleared bywait()(throughcleanup()) as soon as the external command has finished. This enables garbage collection of the resources associated with thesubprocess.Popenobject which helps to avoid IOError: [Errno 24] Too many open files errors.Note
The
subprocessproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
succeeded¶ Whether the external command succeeded.
Trueifreturncodeis zero.Falseifreturncodeis a nonzero number orerror_typeis set (e.g. because the external command doesn’t exist).Nonewhen the external command hasn’t been started or is still running.
-
sudo[source]¶ Whether
sudoshould be used to gain superuser privileges.If this option is
True(not the default) and the current process doesn’t have superuser privileges the external command is run withsudoto ensure that the external command runs with superuser privileges.The use of this option assumes that the
sudocommand is available.Note
The
sudoproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
sudo_command¶ The
sudocommand used to change privileges (a list of strings).This option looks at the
sudo,uidanduserproperties to decide whethercommandshould be run usingsudoor not. If it should then a prefix forcommandis constructed fromsudo,uid,userand/orenvironmentand returned. Some examples:>>> from executor import ExternalCommand >>> ExternalCommand('true', sudo=True).sudo_command ['sudo'] >>> ExternalCommand('true', uid=1000).sudo_command ['sudo', '-u', '#1000'] >>> ExternalCommand('true', user='peter').sudo_command ['sudo', '-u', 'peter'] >>> ExternalCommand('true', user='peter', environment=dict(gotcha='this is tricky')).sudo_command ['sudo', '-u', 'peter', 'gotcha=this is tricky']
-
tty[source]¶ Whether the command will be attached to the controlling terminal (a boolean).
captureisFalsecapture_stderrisFalseinputisNonesilentisFalsestdout_fileandstderr_fileareNoneor files that are connected to a tty(-like) device
If any of these conditions don’t hold
ttydefaults toFalse. WhenttyisFalsethe standard input stream of the external command will be connected toos.devnull.Note
The
ttyproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
uid[source]¶ The user ID of the system user that’s used to run the command.
If this option is set to an integer number (it defaults to
None) the external command is prefixed withsudo -u #UIDto run the command as a different user than the current user.The use of this option assumes that the
sudocommand is available.Note
The
uidproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
user[source]¶ The name of the system user that’s used to run the command.
If this option is set to a string (it defaults to
None) the external command is prefixed withsudo -u USERto run the command as a different user than the current user.The use of this option assumes that the
sudocommand is available.Note
The
userproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
virtual_environment[source]¶ The Python virtual environment to activate before running the command.
If this option is set to the directory of a Python virtual environment (a string) then the external command will be prefixed by a source shell command that evaluates the
bin/activatescript in the Python virtual environment before executing the user defined external command.Note
The
virtual_environmentproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
was_started[source]¶ Whether the external command has already been started.
Trueoncestart()has been called to start executing the external command,Falsewhenstart()hasn’t been called yet.Note
The
was_startedproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
async_fdel()[source]¶ Delete the value of
asynchronous.
-
async_fget()[source]¶ Get the value of
asynchronous.
-
async_fset(value)[source]¶ Set the value of
asynchronous.
-
check_retry_allowed()[source]¶ Check if retrying is allowed by invoking the
retry_eventcallback.Returns: Trueifretry_allowedisTrueand theretry_eventcallback didn’t returnFalse, otherwiseFalse.
-
format_error_message(message, *args, **kw)[source]¶ Add the command’s captured standard output and/or error to an error message.
Refer to
compact()for details on argument handling. Theget_decoded_output()method is used to try to decode the output without raising exceptions.
-
get_decoded_output(name)[source]¶ Try to decode the output captured on standard output or error.
Parameters: name – One of the strings ‘stdout’ or ‘stderr’. Returns: A Unicode string, byte string or None.
-
prefix_shell_command(preamble, command)[source]¶ Prefix a shell command to a command line.
Parameters: - preamble – Any shell command (a string).
- command – The command line (a string, tuple or list).
Returns: The command line to run the combined commands through a shell (a list of strings).
This function uses
reduce_shell_command()to convert command into a string and then prefixes the preamble to the command, delimited by&&.
-
reduce_shell_command(command)[source]¶ Reduce a command line to a shell command.
Parameters: command – The command line (a string, tuple or list). Returns: The shell command (a string). If the given command is a
DEFAULT_SHELLinvocation that uses the-coption it is reduced to the argument of the-coption. All other command lines are simply quoted and returned.This method is used in various places where a command needs to be transformed into a shell command so that a command like
cdorsourcecan be prefixed to the command line.
-
start()[source]¶ Start execution of the external command (including
retrysupport).Raises: ExternalCommandFailedwhencheckisTrue,asynchronousisFalseand the external command exits with a nonzero status code.ValueErrorwhen the external command is still running (you need to explicitly terminate, kill or wait for the running process before you can re-use theExternalCommandobject).
This method instantiates a
subprocess.Popenobject based on the defaults defined byExternalCommandand the overrides configured by the caller. What happens then depends onasynchronous:- If
asynchronousis setstart()starts the external command but doesn’t wait for it to end (usewait()for that). - If
asynchronousisn’t set thecommunicate()method on thesubprocessobject is called to synchronously execute the external command.
-
start_once(check=None, **kw)[source]¶ Start execution of the external command (excluding
retrysupport).Parameters: - check – Override the value of
checkfor the duration of this call tostart_once(). Defaults toNone. - kw – The keyword arguments to the
subprocess.Popeninitializer (prepared bystart()).
The code in this internal method used to be the second half of
start()but was extracted into a separate method so that it can be called more than once, which made it possible to addretrysupport.- check – Override the value of
-
wait(check=None, **kw)[source]¶ Wait for the external command to finish.
Parameters: - check – Override the value of
checkfor the duration of this call towait(). Defaults toNonewhich meanscheckis not overridden. - kw – Any keyword arguments are passed on to
wait_for_process().
Raises: ExternalCommandFailedwhencheckisTrue,asynchronousisTrueand the external command exits with a nonzero status code.The
wait()function is only useful whenasynchronousisTrue, it performs the following steps:- If
was_startedisFalsethestart()method is called. - If
is_runningisTruethewait_for_process()method is called to wait for the child process to end. - If
subprocessisn’tNonethecleanup()method is called to wait for the external command to end, load its output into memory and release the resources associated with thesubprocessobject. - Finally
check_errors()is called (in case the caller didn’t disablecheck).
- check – Override the value of
-
terminate_helper()[source]¶ Gracefully terminate the process.
Raises: Any exceptions raised by the subprocessmodule.This method sets
checktoFalse, the idea being that if you consciously terminate a command you don’t need to be bothered with an exception telling you that you succeeded :-).
-
kill_helper()[source]¶ Forcefully kill the process.
Raises: Any exceptions raised by the subprocessmodule.This method sets
checktoFalse, the idea being that if you consciously kill a command you don’t need to be bothered with an exception telling you that you succeeded :-).
-
load_output()[source]¶ Load output captured from the standard output/error streams.
Reads the contents of the temporary file(s) created by
start()(whenasynchronousandcaptureare both set) into memory so that the output doesn’t get lost when the temporary file is cleaned up bycleanup().
-
cleanup()[source]¶ Clean up after the external command has ended.
This internal method is called by methods like
start()andwait()to clean up the following temporary resources:- The temporary file(s) used to to buffer the external command’s
input,stdoutandstderr(only whenasynchronousisTrue). - File handles to the previously mentioned temporary files and
os.devnull(used to implement thesilentoption). - The reference to the
subprocess.Popenobject stored insubprocess. By destroying this reference as soon as possible we enable the object to be garbage collected and its related resources to be released.
- The temporary file(s) used to to buffer the external command’s
-
check_errors(check=None)[source]¶ Raise an exception if the external command failed.
This raises
error_typewhencheckis set and the external command failed.Parameters: check – Override the value of checkfor the duration of this call. Defaults toNonewhich meanscheckis not overridden.Raises: error_typewhencheckis set anderror_typeis notNone.This internal method is used by
start()andwait()to make sure that failing external commands don’t go unnoticed.
-
invoke_event_callback(name)[source]¶ Invoke one of the event callbacks.
Parameters: name – The name of the callback (a string). Returns: The return value of the callback.
-
__enter__()[source]¶ Start the external command if it hasn’t already been started.
Returns: The ExternalCommandobject.When you use an
ExternalCommandas a context manager in thewithstatement, the command is automatically started when entering the context and terminated when leaving the context.If the proces hasn’t already been started yet
asynchronousis automatically set toTrue(if it’s not alreadyTrue), otherwise the command will have finished execution by the time the body of thewithstatement is executed (which isn’t really all that useful :-).
-
__exit__(exc_type=None, exc_value=None, traceback=None)[source]¶ Automatically terminate and clean up after the external command.
Terminates the external command if it is still running (using
terminate()), cleans up (usingcleanup()) and checks for errors (usingcheck_errors(), only if an exception is not already being handled).
-
__iter__()[source]¶ Iterate over the lines of text in the captured output.
Returns: An iterator of Unicode strings ( unicode()objects in Python 2 orstrobjects in Python 3).If
captureisTruethis will iterate over the lines instdout, alternatively ifcapture_stderrisTruethis will iterate over the lines instderrinstead. In both cases the output will be decoded usingencoding.If
bufferedisTruethe captured output will be split into a list of strings which is then iterated. If it isFalsethen the following idiom is used to create an iterator instead:iter(handle.readline, b'')
To understand why this is useful, consider the following:
- Iteration over file-like objects in Python uses a hidden read-ahead buffer for efficiency. Refer to the file.next() documentation for details.
- Pipes are file-like objects so if you iterate over them but the command doesn’t emit a lot of output, the iteration may not produce any lines until the hidden read-ahead buffer is full. This makes realtime processing of command output harder than it should be.
-
__str__()[source]¶ Render a human friendly string representation of the external command.
This special method calls
quote()oncommand_linewhich is used byexecutorto enable lazy formatting of log messages containing the quoted command line.
-
async¶ An alias for the
asynchronousproperty.In release 21.0 the
asyncproperty was renamed toasynchronousbecause Python 3.7 introduces theasynckeyword, invalidating its use as an identifier. This alias ensures backwards compatibility with callers that are still using the oldasyncname. If you’re wondering which of the two properties you should use:- If Python >= 3.7 compatibility is important to you then your only
choice is
asynchronous. - If you’re working in a Python < 3.7 code base you can pick either one of the properties, it really doesn’t matter.
- Of course most Python 2 code bases will eventually need to be
upgraded to Python 3 and the future proof choice is
asynchronous.
- If Python >= 3.7 compatibility is important to you then your only
choice is
-
class
executor.CachedStream(command, kind)[source]¶ Manages a temporary file with input for / output from an external command.
-
__init__(command, kind)[source]¶ Initialize a
CachedStreamobject.Parameters: - command – The
ExternalCommandobject that’s using theCachedStreamobject. - kind – A simple (alphanumeric) string with the name of the stream.
- command – The
-
prepare_input()[source]¶ Initialize the input stream.
Returns: A value that can be passed to the constructor of subprocess.Popenas thestdinargument.
-
prepare_output(file, capture)[source]¶ Initialize an (asynchronous) output stream.
Parameters: Returns: A value that can be passed to the constructor of
subprocess.Popenas thestdoutand/orstderrargument.
-
redirect(obj)[source]¶ Capture the stream in a file provided by the caller.
Parameters: obj – A file-like object that has an associated file descriptor.
-
load()[source]¶ Load the stream’s contents from the temporary file.
Returns: The output of the stream (a string) or Nonewhen the stream was never initialized.
-
-
executor.quote(*args)[source]¶ Quote a string or a sequence of strings to be used as command line argument(s).
This function is a simple wrapper around
pipes.quote()which adds support for quoting sequences of strings (lists and tuples). For example the following calls are all equivalent:>>> from executor import quote >>> quote('echo', 'argument with spaces') "echo 'argument with spaces'" >>> quote(['echo', 'argument with spaces']) "echo 'argument with spaces'" >>> quote(('echo', 'argument with spaces')) "echo 'argument with spaces'"
Parameters: args – One or more strings, tuples and/or lists of strings to be quoted. Returns: A string containing quoted command line arguments.
-
executor.which(program, mode=1, path=None)[source]¶ Find the pathname(s) of a program on the executable search path (
$PATH).Parameters: program – The name of the program (a string). Returns: A list of pathnames (strings) with found programs. Some examples:
>>> from executor import which >>> which('python') ['/home/peter/.virtualenvs/executor/bin/python', '/usr/bin/python'] >>> which('vim') ['/usr/bin/vim'] >>> which('non-existing-program') []
-
executor.get_search_path(path=None)[source]¶ Get the executable search path (
$PATH).Parameters: path – Override the value of $PATH(a string orNone).Returns: A list of strings with pathnames of directories. The executable search path is constructed as follows:
- The search path is taken from the environment variable
$PATH. - If
$PATHisn’t defined the value ofos.defpathis used. - The search path is split on
os.pathsepto get a list. - On Windows the current directory is prepended to the list.
- Duplicate directories are removed from the list.
- The search path is taken from the environment variable
-
executor.get_path_extensions(extensions=None)[source]¶ Get the executable search path extensions (
$PATHEXT).Returns: A list of strings with unique path extensions (on Windows) or a list containing an empty string (on other platforms).
-
executor.is_executable(filename, mode=1)[source]¶ Check whether the given file is executable.
Parameters: filename – A relative or absolute pathname (a string). Returns: Trueif the file is executable,Falseotherwise.
-
executor.validate_ionice_class(value)[source]¶ Ensure that the given value is a valid I/O scheduling class for ionice.
Parameters: value – The value to validate (a string). Returns: The validated value (one of the strings ‘1’, ‘2’, ‘3’, ‘idle’, ‘best-effort’, or ‘realtime’). Raises: ValueErrorwhen the given value isn’t one of the strings mentioned above.The strings ‘idle’, ‘best-effort’ and ‘realtime’ are preferred for readability but not supported in minimalistic environments like busybox which only support the values ‘1’, ‘2’ and ‘3’ (refer to #16). It’s up to the caller to choose the correct value, no translation is done.
-
exception
executor.ExternalCommandFailed(command, **options)[source]¶ Raised when an external command exits with a nonzero status code.
This exception is raised by
execute(),start()andwait()when an external command exits with a nonzero status code.Here’s an overview of the
ExternalCommandFailedclass:Superclasses: PropertyManagerandExceptionSpecial methods: __init__()Properties: command,error_message,poolandreturncodeWhen you initialize a
ExternalCommandFailedobject you are required to provide a value for thecommandproperty. You can set the values of thecommand,error_messageandpoolproperties by passing keyword arguments to the class initializer.-
__init__(command, **options)[source]¶ Initialize an
ExternalCommandFailedobject.Parameters: - command – The
ExternalCommandobject that triggered the exception. - options – Any keyword arguments are passed on to the initializer
of the base class
PropertyManagerto initialize the writable propertiespoolanderror_message.
- command – The
-
command[source]¶ The
ExternalCommandobject that triggered the exception.
-
pool[source]¶ The
CommandPoolobject that triggered the exception.This property will be
Nonewhen the exception wasn’t raised from a command pool.
-
returncode¶ Shortcut for the external command’s
returncode.
-
error_message[source]¶ An error message explaining what went wrong (a string).
Defaults to
error_messagebut can be overridden using the keyword argument of the same name to__init__().
-
-
exception
executor.CommandNotFound(command, **options)[source]¶ Raised when an external command is not available on the system.
This exception is raised by
execute(),start()andwait()when an external command can’t be started because the command isn’t available.It inherits from
ExternalCommandFailedto enable uniform error handling but it also inherits fromOSErrorfor backwards compatibility (seeerrnoandstrerror).Here’s an overview of the
CommandNotFoundclass:Superclasses: ExternalCommandFailedandOSErrorProperties: errnoandstrerror
The executor.chroot module¶
Simple command execution in chroot environments.
The executor.chroot module defines the ChangeRootCommand class
which makes it easy to run commands inside chroots. If this doesn’t mean
anything to you, here’s a primer:
- The word ‘chroot’ is an abbreviation of the phrase ‘change root’ which is
Unix functionality that changes the ‘root’ directory (
/) of a running process, so ‘change root’ describes an action. - Even though the phrase ‘change root’ describes an action the word ‘chroot’ is also used to refer to the directory which serves as the new root directory (English is flexible like that :-). This is why it makes sense to say that “you’re entering the chroot”.
Warning
This is low level functionality. This module performs absolutely
no chroot initialization, for example /etc/resolv.conf may be
incorrect and there won’t be any bind mounts available in the
chroot (unless you’ve prepared them yourself).
If you need your chroot to be initialized for you then consider using the
executor.schroot module instead. It takes a bit of time to set up
schroot but it provides a more high level experience than chroot.
-
class
executor.chroot.ChangeRootCommand(*args, **options)[source]¶ ChangeRootCommandobjects use the chroot program to execute commands inside chroots.Here’s an overview of the
ChangeRootCommandclass:Superclass: ExternalCommandSpecial methods: __init__()Properties: chroot,chroot_command,chroot_directory,chroot_group,chroot_user,command_line,directoryandhave_superuser_privilegesWhen you initialize a
ChangeRootCommandobject you are required to provide a value for thechrootproperty. You can set the values of thechroot,chroot_command,chroot_directory,chroot_groupandchroot_userproperties by passing keyword arguments to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
ChangeRootCommandobject.Parameters: - args – Positional arguments are passed on to the initializer of
the
ExternalCommandclass. - options – Any keyword arguments are passed on to the initializer
of the
ExternalCommandclass.
If the keyword argument chroot isn’t given but positional arguments are provided, the first positional argument is used to set the
chrootproperty.The command is not started until you call
start()orwait().- args – Positional arguments are passed on to the initializer of
the
-
chroot[source]¶ The pathname of the root directory of the chroot (a string).
Note
The
chrootproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named chroot (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
chroot_command[source]¶ The command used to run the
chrootprogram.This is a list of strings, by default the list contains just
CHROOT_PROGRAM_NAME. Thechroot,chroot_groupandchroot_userproperties also influence the chroot command line used.Note
The
chroot_commandproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
chroot_directory[source]¶ The working directory _inside the chroot (a string, defaults to
None).Note
The
chroot_directoryproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
chroot_group[source]¶ The name or ID of the system group that runs the command (a string or number, defaults to ‘root’).
Note
The
chroot_groupproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
chroot_user[source]¶ The name or ID of the system user that runs the command (a string or number, defaults to ‘root’).
Note
The
chroot_userproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command_line¶ The complete chroot command including the command to run inside the chroot.
This is a list of strings with the chroot command line to enter the requested chroot and execute
command.
-
directory¶ Set the working directory inside the chroot.
When you set this property you change
chroot_directory, however reading back the property you’ll just getDEFAULT_WORKING_DIRECTORY. This is because the superclassExternalCommandusesdirectoryas the working directory for the chroot command, and directories inside chroots aren’t guaranteed to exist on the host system.
-
have_superuser_privileges¶ Whether the command inside the chroot will be running under superuser privileges.
This is
Truewhenchroot_useris the number 0 or the string ‘root’,Falseotherwise.This overrides
ExternalCommand.have_superuser_privilegesto ensure thatsudoisn’t used inside the chroot unless it’s really necessary. This is important because not all chroot environments havesudoinstalled and failing due to a lack ofsudowhen we’re already running asrootis just stupid :-).
-
The executor.cli module¶
Usage: executor [OPTIONS] COMMAND …
Easy subprocess management on the command line based on the Python package with the same name. The “executor” program runs external commands with support for timeouts, dynamic startup delay (fudge factor) and exclusive locking.
You can think of “executor” as a combination of the “flock” and “timelimit” programs with some additional niceties (namely the dynamic startup delay and integrated system logging on UNIX platforms).
Supported options:
| Option | Description |
|---|---|
-t, --timeout=LIMIT |
Set the time after which the given command will be aborted. By default
LIMIT is counted in seconds. You can also use one of the suffixes “s”
(seconds), “m” (minutes), “h” (hours) or “d” (days). |
-f, --fudge-factor=LIMIT |
This option controls the dynamic startup delay (fudge factor) which is
useful when you want a periodic task to run once per given interval but the
exact time is not important. Refer to the --timeout option for acceptable
values of LIMIT, this number specifies the maximum amount of time to sleep
before running the command (the minimum is zero, otherwise you could just
include the command “sleep N && …” in your command line :-). |
-e, --exclusive |
Use an interprocess lock file to guarantee that executor will never run
the external command concurrently. Refer to the --lock-timeout option
to customize blocking / non-blocking behavior. To customize the name
of the lock file you can use the --lock-file option. |
-T, --lock-timeout=LIMIT |
By default executor tries to claim the lock and if it fails it will exit
with a nonzero exit code. This option can be used to enable blocking
behavior. Refer to the --timeout option for acceptable values of LIMIT. |
-l, --lock-file=NAME |
Customize the name of the lock file. By default this is the base name of the external command, so if you’re running something generic like “bash” or “python” you might want to change this :-). |
-v, --verbose |
Increase logging verbosity (can be repeated). |
-q, --quiet |
Decrease logging verbosity (can be repeated). |
-h, --help |
Show this message and exit. |
-
executor.cli.LOCKS_DIRECTORY= '/var/lock'¶ The pathname of the preferred directory for lock files (a string).
Refer to
get_lock_path()for more details.
-
executor.cli.INTERRUPT_FILE= 'executor-fudge-factor-interrupt'¶ The base name of the file used to interrupt the fudge factor (a string).
-
executor.cli.apply_fudge_factor(fudge_factor)[source]¶ Apply the requested scheduling fudge factor.
Parameters: fudge_factor – The maximum number of seconds to sleep (a number). Previous implementations of the fudge factor interrupt used UNIX signals (specifically
SIGUSR1) but the use of this signal turned out to be sensitive to awkward race conditions and it wasn’t very cross platform, so now the creation of a regular file is used to interrupt the fudge factor.
-
executor.cli.get_lock_path(lock_name)[source]¶ Get a pathname that can be used for an interprocess lock.
Parameters: lock_name – The base name for the lock file (a string). Returns: An absolute pathname (a string).
-
executor.cli.run_command(arguments, timeout=None)[source]¶ Run the specified command (with an optional timeout).
Parameters: - arguments – The command line for the external command (a list of strings).
- timeout – The optional command timeout (a number or
None).
Raises: CommandTimedOutif the command times out.
-
exception
executor.cli.CommandTimedOut(command, timeout)[source]¶ Raised when a command exceeds the given timeout.
Here’s an overview of the
CommandTimedOutclass:Superclass: ExternalCommandFailedSpecial methods: __init__()-
__init__(command, timeout)[source]¶ Initialize a
CommandTimedOutobject.Parameters: - command – The command that timed out (an
ExternalCommandobject). - timeout – The timeout that was exceeded (a number).
- command – The command that timed out (an
-
The executor.concurrent module¶
Support for concurrent external command execution.
The executor.concurrent module defines the CommandPool class
which makes it easy to prepare a large number of external commands, group them
together in a pool, start executing a configurable number of external commands
simultaneously and wait for all external commands to finish. For fine grained
concurrency control please refer to the dependencies
and group_by properties of the
ExternalCommand class.
-
class
executor.concurrent.CommandPool(concurrency=None, **options)[source]¶ Execute multiple external commands concurrently.
After constructing a
CommandPoolinstance you add commands to it usingadd()and when you’re ready to run the commands you callrun().Here’s an overview of the
CommandPoolclass:Superclass: PropertyManagerSpecial methods: __init__()Public methods: add(),collect(),get_spinner(),run(),spawn()andterminate()Properties: concurrency,delay_checks,is_finished,logger,logs_directory,num_commands,num_failed,num_finished,num_running,results,running_groups,spinnerandunexpected_failuresYou can set the values of the
concurrency,delay_checks,logger,logs_directoryandspinnerproperties by passing keyword arguments to the class initializer.-
__init__(concurrency=None, **options)[source]¶ Initialize a
CommandPoolobject.Parameters: - concurrency – Override the value of
concurrency. - logs_directory – Override the value of
logs_directory.
- concurrency – Override the value of
-
concurrency[source]¶ The number of external commands that the pool is allowed to run simultaneously.
This is a positive integer number. It defaults to the return value of
multiprocessing.cpu_count()(which may not make much sense if your commands are I/O bound instead of CPU bound).Setting
concurrencyto one is a supported use case intended to make it easier for users of theexecutor.concurrentmodule to reuse the code they’ve built on top of command pools (if only for debugging, but there are lots of use cases :-).Note
The
concurrencyproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
delay_checks[source]¶ Whether to postpone raising an exception until all commands have run (a boolean).
If this option is
True(not the default) and a command withcheckset toTruefails the command pool’s execution is not aborted, instead all commands will be allowed to run. After all commands have finished aCommandPoolFailedexception will be raised that tells you which command(s) failed.Note
The
delay_checksproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
logger[source]¶ The
logging.Loggerobject to use.If you are using Python’s
loggingmodule and you find it confusing that command pool execution is logged under theexecutor.concurrentname space instead of the name space of the application or library usingexecutoryou can set this attribute to inject a custom (and more appropriate) logger.Note
The
loggerproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
logs_directory[source]¶ The pathname of a directory where captured output is stored (a string).
If this property is set to the pathname of a directory (before any external commands have been started) the merged output of each external command is captured and stored in a log file in this directory. The directory will be created if it doesn’t exist yet.
Output will start appearing in the log files before the external commands are finished, this enables tail -f to inspect the progress of commands that are still running and emitting output.
Note
The
logs_directoryproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
num_commands¶ The number of commands in the pool (an integer).
-
num_finished¶ The number of commands in the pool that have already finished, including retries (an integer).
-
num_failed¶ The number of commands in the pool that failed (an integer).
-
num_running¶ The number of currently running commands in the pool (an integer).
-
running_groups¶ A set of running command groups.
The value of
running_groupsis asetwith thegroup_byvalues of all currently running commands (Noneis never included in the set).
-
results¶ A mapping of identifiers to external command objects.
This is a dictionary with external command identifiers as keys (refer to
add()) andExternalCommandobjects as values. TheExternalCommandobjects provide access to the return codes and/or output of the finished commands.
-
spinner[source]¶ Control if and how an animated spinner is shown when the command pool is active.
The following values are supported:
- The default value
Nonemeans “auto detect”, which means the spinner is shown only when the process is connected to a terminal. - The value
Trueunconditionally enables the spinner. - The value
Falseunconditionally disables the spinner. - A
Spinnerobject can be provided by the caller, giving them the chance to configure how the spinner behaves.
Note
The
spinnerproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().- The default value
-
unexpected_failures¶ A list of
ExternalCommandobjects that failed unexpectedly.The resulting list includes only commands where
checkandfailedare bothTrue.
-
add(command, identifier=None, log_file=None)[source]¶ Add an external command to the pool of commands.
Parameters: - command – The external command to add to the pool (an
ExternalCommandobject). - identifier – A unique identifier for the external command (any value). When this parameter is not provided the identifier is set to the number of commands in the pool plus one (i.e. the first command gets id 1).
- log_file – Override the default log file name for the command
(the identifier with
.logappended) in caselogs_directoryis set.
When a command is added to a command pool the following options are changed automatically:
- The
asynchronousproperty is set toTrue. If you want the commands to execute with a concurrency of one then you should setconcurrencyto one. - The
ttyproperty is set toFalsewhenconcurrencyis higher than one because interaction with multiple concurrent subprocesses in a single terminal is prone to serious miscommunication (when multiple subprocesses present an interactive prompt at the same time and the user tries to answer one of the prompts it will be impossible to tell which of the subprocesses will receive the user’s reply).
- command – The external command to add to the pool (an
-
run()[source]¶ Keep spawning commands and collecting results until all commands have run.
Returns: The value of results.Raises: Any exceptions raised by collect().This method calls
spawn()andcollect()in a loop until all commands registered usingadd()have run and finished. Ifcollect()raises an exception any running commands are terminated before the exception is propagated to the caller.If you’re writing code where you want to own the main loop then consider calling
spawn()andcollect()directly instead of usingrun().When
concurrencyis set to one, specific care is taken to make sure that the callbacks configured bystart_eventandfinish_eventare called in the expected (intuitive) order.
-
spawn()[source]¶ Spawn additional external commands up to the
concurrencylevel.Returns: The number of external commands that were spawned by this invocation of spawn()(an integer).The commands to start are picked according to three criteria:
- The command’s
was_startedproperty isFalse. - The command’s
group_byvalue is not present inrunning_groups. - The
is_finished_with_retriesproperties of all of the command’sdependenciesareTrue.
- The command’s
-
collect()[source]¶ Collect the exit codes and output of finished commands.
Returns: The number of external commands that were collected by this invocation of
collect()(an integer).Raises: - If
delay_checksisTrue: After all external commands have started and finished, if any commands that have
checkset toTruefailedCommandPoolFailedis raised.- If
delay_checksisFalse: The exceptions
ExternalCommandFailed,RemoteCommandFailedandRemoteConnectFailedcan be raised if a command in the pool that hascheckset toTruefails. Thepoolattribute of the exception will be set to the pool.
Warning
If an exception is raised, commands that are still running will not be terminated! If this concerns you then consider calling
terminate()from afinallyblock (this is whatrun()does).- If
-
terminate()[source]¶ Terminate any commands that are currently running.
Returns: The number of commands that were terminated (an integer). If
terminate()successfully terminates commands, you then callcollect()and thecheckproperty of a terminated command isTrueyou will get an exception because terminated commands (by definition) report a nonzeroreturncode.
-
-
exception
executor.concurrent.CommandPoolFailed(pool)[source]¶ Raised by
collect()when not all commands succeeded.This exception is only raised when
delay_checksisTrue.-
__init__(pool)[source]¶ Initialize a
CommandPoolFailedobject.Parameters: pool – The CommandPoolobject that triggered the exception.
-
commands¶ A shortcut for
unexpected_failures.
-
error_message¶ An error message that explains which commands failed unexpectedly (a string).
-
The executor.contexts module¶
Dependency injection for command execution contexts.
The contexts module defines the LocalContext,
RemoteContext and SecureChangeRootContext classes. All of
these classes support the same API for executing external commands, they are
simple wrappers for ExternalCommand, RemoteCommand and
SecureChangeRootCommand.
This allows you to script interaction with external commands in Python and perform that interaction on your local system, on remote systems over SSH or inside chroots using the exact same Python code. Dependency injection on steroids anyone? :-)
Here’s a simple example:
from executor.contexts import LocalContext, RemoteContext
from humanfriendly import format_timespan
def details_about_system(context):
return "\n".join([
"Information about %s:" % context,
" - Host name: %s" % context.capture('hostname', '--fqdn'),
" - Uptime: %s" % format_timespan(float(context.capture('cat', '/proc/uptime').split()[0])),
])
print(details_about_system(LocalContext()))
# Information about local system (peter-macbook):
# - Host name: peter-macbook
# - Uptime: 1 week, 3 days and 10 hours
print(details_about_system(RemoteContext('file-server')))
# Information about remote system (file-server):
# - Host name: file-server
# - Uptime: 18 weeks, 3 days and 4 hours
Whether this functionality looks exciting or horrible I’ll leave up to your judgment. I created it because I’m always building “tools that help me build tools” and this functionality enables me to very rapidly prototype system integration tools developed using Python:
- During development:
- I write code on my workstation which I prefer because of the “rich editing environment” but I run the code against a remote system over SSH (a backup server, database server, hypervisor, mail server, etc.).
- In production:
- I change one line of code to inject a
LocalContextobject instead of aRemoteContextobject, I install the executor package and the code I wrote on the remote system and I’m done!
-
executor.contexts.MIRROR_TO_DISTRIB_MAPPING= {u'http://archive.ubuntu.com/ubuntu': u'ubuntu', u'http://deb.debian.org/debian': u'debian'}¶ Mapping of canonical package mirror URLs to “distributor ID” strings.
Each key in this dictionary is the canonical package mirror URL of a Debian based Linux distribution and each value is the corresponding distributor ID. The following canonical mirror URLs are currently supported:
Mirror URL Value http://deb.debian.org/debian debianhttp://archive.ubuntu.com/ubuntu/ ubuntuFor more details refer to the
AbstractContext.apt_sources_infoproperty.
-
executor.contexts.create_context(**options)[source]¶ Create an execution context.
Parameters: options – Any keyword arguments are passed on to the context’s initializer. Returns: A LocalContext,SecureChangeRootContextorRemoteContextobject.This function provides an easy to use shortcut for constructing context objects:
- If the keyword argument
chroot_nameis given (and notNone) then aSecureChangeRootContextobject will be created. - If the keyword argument
ssh_aliasis given (and notNone) then aRemoteContextobject will be created. - Otherwise a
LocalContextobject is created.
- If the keyword argument
-
class
executor.contexts.AbstractContext(*args, **options)[source]¶ Abstract base class for shared logic of all context classes.
Here’s an overview of the
AbstractContextclass:Superclass: PropertyManagerSpecial methods: __enter__(),__exit__()and__init__()Public methods: atomic_write(),capture(),cleanup(),execute(),exists(),find_chroots(),find_program(),get_options(),glob(),is_directory(),is_executable(),is_file(),is_readable(),is_writable(),list_entries(),merge_options(),prepare(),prepare_command(),prepare_interactive_shell(),read_file(),start_interactive_shell(),test()andwrite_file()Properties: apt_sources_info,command_type,cpu_count,distribution_codename,distributor_id,have_ionice,have_superuser_privileges,lsb_release_variables,optionsandparentWhen you initialize a
AbstractContextobject you are required to provide a value for thecommand_typeproperty. You can set the values of thecommand_type,optionsandparentproperties by passing keyword arguments to the class initializer.-
__init__(*args, **options)[source]¶ Initialize an
AbstractContextobject.Parameters: - args – Any positional arguments are passed on to the initializer
of the
PropertyManagerclass (for future extensibility). - options –
The keyword arguments are handled as follows:
- Keyword arguments whose name matches a property of
the context object are used to set that property
(by passing them to the initializer of the
PropertyManagerclass). - Any other keyword arguments are collected into the
optionsdictionary.
- Keyword arguments whose name matches a property of
the context object are used to set that property
(by passing them to the initializer of the
- args – Any positional arguments are passed on to the initializer
of the
-
apt_sources_info[source]¶ A tuple with two strings (the distributor ID and distribution codename).
The values of the
distributor_idanddistribution_codenameproperties are determined by one of the following three methods (in decreasing order of preference):- If
lsb_release_variablesis available it’s used. - If the lsb_release program is available it’s used.
- Finally
/etc/apt/sources.listis parsed for hints.
The
apt_sources_infoproperty concerns the third step which works as follows:- The
debdirectives in/etc/apt/sources.listare parsed to determine the primary package mirror URL (it’s fine if this file doesn’t exist, no error will be reported). - The
MIRROR_TO_DISTRIB_MAPPINGdictionary is used to look up the distributor ID corresponding to the package mirror URL that was found in/etc/apt/sources.list. - If the mirror URL is successfully translated to a distributor ID, the
third token in the
debdirective is taken to be the distribution codename.
The
apt_sources_infoproperty was added in response to issue #17 where it was reported that official Debian Docker images don’t contain the/etc/lsb-releasefile nor the lsb_release program.This is only used as a last resort because of its specificness to Debian based Linux distributions and because I have concerns about how robust this new functionality will turn out to be.
Note
The
apt_sources_infoproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.- If
-
command_type[source]¶ The type of command objects created by this context (
ExternalCommandor a subclass).Note
The
command_typeproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named command_type (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
cpu_count¶ The number of CPUs in the system (an integer).
Note
This is an abstract property that must be implemented by subclasses.
-
distribution_codename[source]¶ The code name of the system’s distribution (a lowercased string like
preciseortrusty).How this property is computed depends on the execution context:
- When the file
/etc/lsb-releaseexists and defines the variableDISTRIB_CODENAMEthen this is the preferred source (for details seelsb_release_variables). - When lsb_release is installed the output of the command
lsb_release --short --codenameis used. - Finally
apt_sources_infois used if possible.
The returned string is guaranteed to be lowercased, in order to enable reliable string comparison.
Note
The
distribution_codenameproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.- When the file
-
distributor_id[source]¶ The distributor ID of the system (a lowercased string like
debianorubuntu).How this property is computed depends on the execution context:
- When the file
/etc/lsb-releaseexists and defines the variableDISTRIB_IDthen this is the preferred source (for details seelsb_release_variables). - When lsb_release is installed the output of the command
lsb_release --short --idis used. - Finally
apt_sources_infois used if possible.
The returned string is guaranteed to be lowercased, in order to enable reliable string comparison.
Note
The
distributor_idproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.- When the file
-
have_ionice[source]¶ Truewhen ionice is installed,Falseotherwise.Note
The
have_ioniceproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
lsb_release_variables[source]¶ The contents of
/etc/lsb-releaseas a dictionary.The values of
distributor_idanddistribution_codenameare based on the information provided bylsb_release_variables. If/etc/lsb-releasedoesn’t exist or can’t be parsed a debug message is logged and an empty dictionary is returned. Here’s an example:>>> from executor.contexts import LocalContext >>> context = LocalContext() >>> context.lsb_release_variables {'DISTRIB_CODENAME': 'bionic', 'DISTRIB_DESCRIPTION': 'Ubuntu 18.04.1 LTS', 'DISTRIB_ID': 'Ubuntu', 'DISTRIB_RELEASE': '18.04'}
The
lsb_release_variablesproperty was added in response to issue #10 where it was reported that the lsb_release program wasn’t available in vanilla Ubuntu 18.04 Docker images.Note
The
lsb_release_variablesproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
options[source]¶ The options that are passed to commands created by the context (a dictionary).
Note
The
optionsproperty is awritable_property. You can change the value of this property using normal attribute assignment syntax.
-
parent[source]¶ The parent context (a context object or
None).The
parentproperty (and the code inprepare_command()that uses theparentproperty) enables the use of “nested contexts”.For example
find_chroots()createsSecureChangeRootContextobjects whoseparentis set to the context that found the chroots. Because of this theSecureChangeRootContextobjects can be used to create commands without knowing or caring whether the chroots reside on the local system or on a remote system accessed via SSH.Warning
Support for parent contexts was introduced in executor version 15 and for now this feature is considered experimental and subject to change. While I’m definitely convinced of the usefulness of nested contexts I’m not happy with the current implementation at all. The most important reason for this is that it’s very surprising (and not in a good way) that a context with a
parentwill create commands with the parent’scommand_typeinstead of the expected type.Note
The
parentproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
__exit__(exc_type=None, exc_value=None, traceback=None)[source]¶ Execute any commands on the “undo stack” (refer to
cleanup()).
-
atomic_write(**kwds)[source]¶ Create or update the contents of a file atomically.
Parameters: filename – The pathname of the file to create/update (a string). Returns: A context manager (see the withkeyword) that returns a single string which is the pathname of the temporary file where the contents should be written to initially.If an exception is raised from the
withblock and the temporary file exists, an attempt will be made to remove it but failure to do so will be silenced instead of propagated (to avoid obscuring the original exception).The temporary file is created in the same directory as the real file, but a dot is prefixed to the name (making it a hidden file) and the suffix ‘.tmp-‘ followed by a random integer number is used.
-
capture(*command, **options)[source]¶ Execute an external command in the current context and capture its output.
Parameters: - command – All positional arguments are passed on to the
initializer of the
command_typeclass. - options – All keyword arguments are passed on to the
initializer of the
command_typeclass.
Returns: The value of
ExternalCommand.output.- command – All positional arguments are passed on to the
initializer of the
-
cleanup(*args, **kw)[source]¶ Register an action to be performed before the context ends.
Parameters: - args – The external command to execute or callable to invoke.
- kw – Options to the command or keyword arguments to the callable.
Raises: ValueErrorwhencleanup()is called outside awithstatement.This method registers the intent to perform an action just before the context ends. To actually perform the action(s) you need to use (the subclass of) the
AbstractContextobject as a context manager using thewithstatement.The last action that is registered is the first one to be performed. This gives the equivalent functionality of a deeply nested
try/finallystructure without actually having to write such ugly code :-).The handling of arguments in
cleanup()depends on the type of the first positional argument:- If the first positional argument is a string, the positional
arguments and keyword arguments are passed on to the initializer
of the
command_typeclass to execute an external command just before the context ends. - If the first positional argument is a callable, it is called with any remaining positional arguments and keyword arguments before the context ends.
-
execute(*command, **options)[source]¶ Execute an external command in the current context.
Parameters: - command – All positional arguments are passed on to the
initializer of the
command_typeclass. - options – All keyword arguments are passed on to the
initializer of the
command_typeclass.
Returns: The
command_typeobject.Note
After constructing a
command_typeobject this method callsstart()on the command before returning it to the caller, so by the time the caller gets the command object a synchronous command will have already ended. Asynchronous commands don’t have this limitation of course.- command – All positional arguments are passed on to the
initializer of the
-
exists(pathname)[source]¶ Check whether the given pathname exists.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname exists,Falseotherwise.This is a shortcut for the
test -e ...command.
-
find_chroots(namespace='chroot')[source]¶ Find the chroots available in the current context.
Parameters: namespace – The chroot namespace to look for (a string, defaults to DEFAULT_NAMESPACE). Refer to the schroot documentation for more information about chroot namespaces.Returns: A generator of SecureChangeRootContextobjects whoseparentis set to the context where the chroots were found.Raises: ExternalCommandFailed(or a subclass) when theschrootprogram isn’t installed or theschroot --listcommand fails.
-
find_program(program_name, *args)[source]¶ Find the absolute pathname(s) of one or more programs.
Parameters: program_name – Each of the positional arguments is expected to be a string containing the name of a program to search for in the $PATH. At least one is required.Returns: A list of strings with absolute pathnames. This method is a simple wrapper around
which.
-
get_options()[source]¶ Get the options that are passed to commands created by the context.
Returns: A dictionary of command options. By default this method simply returns the
optionsdictionary, however the purpose ofget_options()is to enable subclasses to customize the options passed to commands on the fly.
-
glob(pattern)[source]¶ Find matches for a given filename pattern.
Parameters: pattern – A filename pattern (a string). Returns: A list of strings with matches. Some implementation notes:
- This method emulates filename globbing as supported by system
shells like Bash and ZSH. It works by forking a Python interpreter
and using that to call the
glob.glob()function. This approach is of course rather heavyweight. - Initially this method used Bash for filename matching (similar to this StackOverflow answer) but I found it impossible to make this work well for patterns containing whitespace.
- I took the whitespace issue as a sign that I was heading down the wrong path (trying to add robustness to a fragile solution) and so the new implementation was born (which prioritizes robustness over performance).
- This method emulates filename globbing as supported by system
shells like Bash and ZSH. It works by forking a Python interpreter
and using that to call the
-
is_directory(pathname)[source]¶ Check whether the given pathname points to an existing directory.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname points to an existing directory,Falseotherwise.This is a shortcut for the
test -d ...command.
-
is_executable(pathname)[source]¶ Check whether the given pathname points to an executable file.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname points to an executable file,Falseotherwise.This is a shortcut for the
test -x ...command.
-
is_file(pathname)[source]¶ Check whether the given pathname points to an existing file.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname points to an existing file,Falseotherwise.This is a shortcut for the
test -f ...command.
-
is_readable(pathname)[source]¶ Check whether the given pathname exists and is readable.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname exists and is readable,Falseotherwise.This is a shortcut for the
test -r ...command.
-
is_writable(pathname)[source]¶ Check whether the given pathname exists and is writable.
Parameters: pathname – The pathname to check (a string). Returns: Trueif the pathname exists and is writable,Falseotherwise.This is a shortcut for the
test -w ...command.
-
list_entries(directory)[source]¶ List the entries in a directory.
Parameters: directory – The pathname of the directory (a string). Returns: A list of strings with the names of the directory entries. This method uses
find -mindepth 1 -maxdepth 1 -print0to list directory entries instead of going for the more obvious choicels -A1becausefindenables more reliable parsing of command output (with regards to whitespace).
-
merge_options(overrides)[source]¶ Merge default options and overrides into a single dictionary.
Parameters: overrides – A dictionary with any keyword arguments given to execute()orstart_interactive_shell().Returns: The dictionary with overrides, but any keyword arguments given to the initializer of AbstractContextthat are not set in the overrides are set to the value of the initializer argument.The
ioniceoption is automatically unset whenhave_ioniceisFalse, regardless of whether the option was set from defaults or overrides.
-
prepare(*command, **options)[source]¶ Prepare to execute an external command in the current context.
Parameters: - command – All positional arguments are passed on to the
initializer of the
command_typeclass. - options – All keyword arguments are passed on to the
initializer of the
command_typeclass.
Returns: The
command_typeobject.Note
After constructing a
command_typeobject this method doesn’t callstart()which means you control if and when the command is started. This can be useful to prepare a large batch of commands and execute them concurrently using aCommandPool.- command – All positional arguments are passed on to the
initializer of the
-
prepare_command(command, options)[source]¶ Create a
command_typeobject based onoptions.Parameters: - command – A tuple of strings (the positional arguments to the
initializer of the
command_typeclass). - options – A dictionary (the keyword arguments to the initializer
of the
command_typeclass).
Returns: A
command_typeobject that hasn’t been started yet.- command – A tuple of strings (the positional arguments to the
initializer of the
-
prepare_interactive_shell(options)[source]¶ Create a
command_typeobject that starts an interactive shell.Parameters: options – A dictionary (the keyword arguments to the initializer of the command_typeclass).Returns: A command_typeobject that hasn’t been started yet.
-
read_file(filename, **options)[source]¶ Read the contents of a file.
Parameters: - filename – The pathname of the file to read (a string).
- options – Optional keyword arguments to
execute().
Returns: The contents of the file (a byte string).
This method uses cat to read the contents of files so that options like
sudoare respected (regardless of whether we’re dealing with aLocalContextorRemoteContext).
-
start_interactive_shell(**options)[source]¶ Start an interactive shell in the current context.
Parameters: options – All keyword arguments are passed on to the initializer of the command_typeclass.Returns: The command_typeobject.Note
After constructing a
command_typeobject this method callsstart()on the command before returning it to the caller, so by the time the caller gets the command object a synchronous command will have already ended. Asynchronous commands don’t have this limitation of course.
-
test(*command, **options)[source]¶ Execute an external command in the current context and get its status.
Parameters: - command – All positional arguments are passed on to the
initializer of the
command_typeclass. - options – All keyword arguments are passed on to the
initializer of the
command_typeclass.
Returns: The value of
ExternalCommand.succeeded.This method automatically sets
checktoFalseandsilenttoTrue.- command – All positional arguments are passed on to the
initializer of the
-
write_file(filename, contents, **options)[source]¶ Change the contents of a file.
Parameters: - filename – The pathname of the file to write (a string).
- contents – The contents to write to the file (a byte string).
- options – Optional keyword arguments to
execute().
This method uses a combination of cat and output redirection to change the contents of files so that options like
sudoare respected (regardless of whether we’re dealing with aLocalContextorRemoteContext). Due to the use of cat this method will create files that don’t exist yet, assuming the directory containing the file already exists and the context provides permission to write to the directory.
-
-
class
executor.contexts.LocalContext(*args, **options)[source]¶ Context for executing commands on the local system.
Here’s an overview of the
LocalContextclass:Superclass: AbstractContextSpecial methods: __str__()Public methods: glob()Properties: command_typeandcpu_count-
command_type¶ The type of command objects created by this context (
ExternalCommand).
-
cpu_count[source]¶ The number of CPUs in the system (an integer).
This property’s value is computed using
multiprocessing.cpu_count().Note
The
cpu_countproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
glob(pattern)[source]¶ Find matches for a given filename pattern.
Parameters: pattern – A filename pattern (a string). Returns: A list of strings with matches. This method overrides
AbstractContext.glob()to callglob.glob()directly instead of forking a new Python interpreter.This optimization is skipped when
optionscontainssudo,uidoruserto avoid reporting wrong matches due to insufficient filesystem permissions.
-
-
class
executor.contexts.ChangeRootContext(*args, **options)[source]¶ Context for executing commands in change roots using chroot.
Here’s an overview of the
ChangeRootContextclass:Superclass: AbstractContextSpecial methods: __init__()and__str__()Public methods: get_options()Properties: chroot,command_typeandcpu_countWhen you initialize a
ChangeRootContextobject you are required to provide a value for thechrootproperty. You can set the value of thechrootproperty by passing a keyword argument to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
ChangeRootContextobject.Parameters: - args – Positional arguments are passed on to the initializer of
the
AbstractContextclass (for future extensibility). - options – Any keyword arguments are passed on to the initializer
of the
AbstractContextclass.
If the keyword argument chroot isn’t given but positional arguments are provided, the first positional argument is used to set the
chrootproperty.- args – Positional arguments are passed on to the initializer of
the
-
chroot[source]¶ The pathname of the root directory of the chroot (a string).
Note
The
chrootproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named chroot (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
command_type¶ The type of command objects created by this context (
ChangeRootCommand).
-
cpu_count[source]¶ The number of CPUs in the system (an integer).
This property’s value is computed using
multiprocessing.cpu_count().Note
The
cpu_countproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
-
class
executor.contexts.SecureChangeRootContext(*args, **options)[source]¶ Context for executing commands in change roots using schroot.
Here’s an overview of the
SecureChangeRootContextclass:Superclass: AbstractContextSpecial methods: __init__()and__str__()Public methods: get_options()Properties: chroot_name,command_typeandcpu_countWhen you initialize a
SecureChangeRootContextobject you are required to provide a value for thechroot_nameproperty. You can set the value of thechroot_nameproperty by passing a keyword argument to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
SecureChangeRootContextobject.Parameters: - args – Positional arguments are passed on to the initializer of
the
AbstractContextclass (for future extensibility). - options – Any keyword arguments are passed on to the initializer
of the
AbstractContextclass.
If the keyword argument chroot_name isn’t given but positional arguments are provided, the first positional argument is used to set the
chroot_nameproperty.- args – Positional arguments are passed on to the initializer of
the
-
chroot_name[source]¶ The name of a chroot managed by schroot (a string).
Note
The
chroot_nameproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named chroot_name (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
command_type¶ The type of command objects created by this context (
SecureChangeRootCommand).
-
cpu_count[source]¶ The number of CPUs in the system (an integer).
This property’s value is computed using
multiprocessing.cpu_count().Note
The
cpu_countproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
get_options()[source]¶ The
optionsincludingchroot_name.
-
-
class
executor.contexts.RemoteContext(*args, **options)[source]¶ Context for executing commands on a remote system over SSH.
Here’s an overview of the
RemoteContextclass:Superclasses: RemoteAccountandAbstractContextSpecial methods: __str__()Public methods: get_options()Properties: command_typeandcpu_count-
command_type¶ The type of command objects created by this context (
RemoteCommand).
-
cpu_count[source]¶ The number of CPUs in the system (an integer).
This property’s value is computed by executing the remote command nproc. If that command fails
cpu_countfalls back to the commandgrep -ci '^processor\s*:' /proc/cpuinfo.Note
The
cpu_countproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
The executor.process module¶
Portable process control functionality for the executor package.
The executor.process module defines the ControllableProcess
abstract base class which enables process control features like waiting for a
process to end, gracefully terminating it and forcefully killing it. The
process control functionality in ControllableProcess is separated from
the command execution functionality in ExternalCommand to
make it possible to re-use the process control functionality in other Python
packages, see for example the proc.core.Process class.
-
executor.process.DEFAULT_TIMEOUT= 10¶ The default timeout used to wait for process termination (number of seconds).
-
class
executor.process.ControllableProcess(**kw)[source]¶ Abstract, portable process control functionality.
By defining a subclass of
ControllableProcessand implementing thepid,command_lineandis_runningproperties and theterminate_helper()andkill_helper()methods you get thewait_for_process(),terminate()andkill()methods for free. This decoupling has enabled me to share a lot of code between two Python projects of mine with similar goals but very different requirements:- The executor package builds on top of the
subprocessmodule in the Python standard library and strives to be as cross platform as possible. This means things like UNIX signals are not an option (although signals exist on Windows they are hardly usable). The package mostly deals withsubprocess.Popenobjects internally (to hide platform specific details as much as possible). - The proc package exposes process information available in the Linux
process information pseudo-file system available at
/proc. The package mostly deals with process IDs internally. Because this is completely specialized to a UNIX environment the use of things like UNIX signals is not a problem at all.
Here’s an overview of the
ControllableProcessclass:Superclass: PropertyManagerSpecial methods: __str__()Public methods: kill(),kill_helper(),terminate(),terminate_helper()andwait_for_process()Properties: command_line,is_running,loggerandpidYou can set the values of the
command_line,loggerandpidproperties by passing keyword arguments to the class initializer.-
command_line[source]¶ A list of strings with the command line used to start the process.
This property may be set or implemented by subclasses to enable
__str__()to render a human friendly representation of aControllableProcessobject.Note
The
command_lineproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
is_running¶ Trueif the process is running,Falseotherwise.This property must be implemented by subclasses to enable
wait_for_process(),terminate()andkill()to work properly.
-
logger[source]¶ The
logging.Loggerobject to use (defaults to theexecutor.processlogger).If you are using Python’s
loggingmodule and you find it confusing that command manipulation is logged under theexecutor.processname space instead of the name space of the application or library usingexecutoryou can set this attribute to inject a custom (and more appropriate) logger.Note
The
loggerproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
pid[source]¶ The process ID (a number) or
None.This property must be set or implemented by subclasses:
- It provides
wait_for_process()with a short and unique representation of a process that most users will understand. - It enables
__str__()to render a human friendly representation of aControllableProcessobject.
Note
The
pidproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().- It provides
-
wait_for_process(timeout=0, use_spinner=None)[source]¶ Wait until the process ends or the timeout expires.
Parameters: - timeout – The number of seconds to wait for the process to terminate after we’ve asked it nicely (defaults to zero which means we wait indefinitely).
- use_spinner –
Whether or not to display an interactive spinner on the terminal (using
Spinner) to explain to the user what they are waiting for:
Returns: A
Timerobject telling you how long it took to wait for the process.
-
terminate(wait=True, timeout=10, use_spinner=None)[source]¶ Gracefully terminate the process.
Parameters: - wait – Whether to wait for the process to end (a boolean,
defaults to
True). - timeout – The number of seconds to wait for the process to
terminate after we’ve signaled it (defaults to
DEFAULT_TIMEOUT). Zero means to wait indefinitely. - use_spinner – See the
wait_for_process()documentation.
Returns: Raises: Any exceptions raised by
terminate_helper()implementations of subclasses orkill().This method works as follows:
- Signal the process to gracefully terminate itself. Processes can choose to intercept termination signals to allow for graceful termination (many UNIX daemons work like this) however the default action is to simply exit immediately.
- If wait is
Trueand we’ve signaled the process, we wait for it to terminate gracefully or timeout seconds have passed (whichever comes first). - If wait is
Trueand the process is still running after timeout seconds have passed, it will be forcefully terminated usingkill()(the value of timeout that was given toterminate()will be passed on tokill()).
This method does nothing when
is_runningisFalse.- wait – Whether to wait for the process to end (a boolean,
defaults to
-
terminate_helper()[source]¶ Request the process to gracefully terminate itself (needs to be implemented by subclasses).
-
kill(wait=True, timeout=10, use_spinner=None)[source]¶ Forcefully kill the process.
Parameters: - wait – Whether to wait for the process to end (a boolean,
defaults to
True). - timeout – The number of seconds to wait for the process to
terminate after we’ve signaled it (defaults to
DEFAULT_TIMEOUT). Zero means to wait indefinitely. - use_spinner – See the
wait_for_process()documentation.
Returns: Raises: - Any exceptions raised by
kill_helper()implementations of subclasses. ProcessTerminationFailedif the process is still running afterkill_helper()andwait_for_process()have been called.
This method does nothing when
is_runningisFalse.- wait – Whether to wait for the process to end (a boolean,
defaults to
-
__str__()[source]¶ Render a human friendly representation of a
ControllableProcessobject.Returns: A string describing the process. Includes the process ID and the command line (when available).
- The executor package builds on top of the
-
exception
executor.process.ProcessTerminationFailed(*args, **kw)[source]¶ Raised when process termination fails.
Here’s an overview of the
ProcessTerminationFailedclass:Superclasses: PropertyManagerandExceptionSpecial methods: __init__()Properties: messageandprocessWhen you initialize a
ProcessTerminationFailedobject you are required to provide values for themessageandprocessproperties. You can set the values of themessageandprocessproperties by passing keyword arguments to the class initializer.-
__init__(*args, **kw)[source]¶ Initialize a
ProcessTerminationFailedobject.This method’s signature is the same as the initializer of the
PropertyManagerclass.
-
process[source]¶ The
ControllableProcessobject that triggered the exception.
-
The executor.schroot module¶
Secure command execution in chroot environments.
The executor.schroot module defines the SecureChangeRootCommand
class which makes it easy to run commands inside chroots that are managed
using the schroot program.
-
executor.schroot.SCHROOT_PROGRAM_NAME= 'schroot'¶ The name of the
schrootprogram (a string).
-
executor.schroot.DEFAULT_NAMESPACE= 'chroot'¶ The default chroot namespace (a string).
Refer to the schroot documentation for more information about chroot namespaces.
-
class
executor.schroot.SecureChangeRootCommand(*args, **options)[source]¶ SecureChangeRootCommandobjects use the schroot program to execute commands inside chroots.Here’s an overview of the
SecureChangeRootCommandclass:Superclass: ExternalCommandSpecial methods: __init__()Properties: chroot_directory,chroot_name,chroot_user,command_line,directoryandschroot_commandWhen you initialize a
SecureChangeRootCommandobject you are required to provide a value for thechroot_nameproperty. You can set the values of thechroot_directory,chroot_name,chroot_userandschroot_commandproperties by passing keyword arguments to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
SecureChangeRootCommandobject.Parameters: - args – Positional arguments are passed on to the initializer of
the
ExternalCommandclass. - options – Any keyword arguments are passed on to the initializer
of the
ExternalCommandclass.
If the keyword argument chroot_name isn’t given but positional arguments are provided, the first positional argument is used to set the
chroot_nameproperty.The command is not started until you call
start()orwait().- args – Positional arguments are passed on to the initializer of
the
-
chroot_directory[source]¶ The working directory _inside the chroot (a string or
None, defaults to/).When
chroot_directoryisNonethe schroot program gets to pick the working directory inside the chroot (refer to the schroot documentation for the complete details).For non-interactive usage (which I anticipate to be the default usage of
SecureChangeRootCommand) the schroot program simply assumes that the working directory outside of the chroot also exists inside the chroot, then fails with an error message when this is not the case.Because this isn’t a very robust default,
chroot_directorydefaults to/instead.Note
The
chroot_directoryproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
chroot_name[source]¶ The name of a chroot managed by schroot (a string).
This is expected to match one of the names configured in the directory
/etc/schroot/chroot.d.Note
The
chroot_nameproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named chroot_name (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
chroot_user[source]¶ The name of the user inside the chroot to run the command as (a string or
None).This defaults to
Nonewhich means to run as the current user.Note
The
chroot_userproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command_line¶ The complete schroot command including the command to run inside the chroot.
This is a list of strings with the schroot command line to enter the requested chroot and execute
command.
-
directory¶ Set the working directory inside the chroot.
When you set this property you change
chroot_directory, however reading back the property you’ll just getDEFAULT_WORKING_DIRECTORY. This is because the superclassExternalCommandusesdirectoryas the working directory for the schroot command, and directories inside chroots aren’t guaranteed to exist on the host system.
-
schroot_command[source]¶ The command used to run the schroot program.
This is a list of strings, by default the list contains just
SCHROOT_PROGRAM_NAME. Thechroot_directory,chroot_nameandchroot_userproperties also influence the schroot command line used.Note
The
schroot_commandproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
The executor.ssh.client module¶
Remote command execution using SSH.
The executor.ssh.client module defines the RemoteCommand class
and the foreach() function which make it easy to run a remote command
in parallel on multiple remote hosts using SSH.
The foreach() function also serves as a simple example of how to use
CommandPool and RemoteCommand objects
(it’s just 16 lines of code if you squint in the right way and that includes
logging :-).
The SecureTunnel class makes it easy to use “tunnel only”
SSH connections by waiting until the tunnel becomes connected and
automatically selecting a free ephemeral port number if a
local port number isn’t provided by the caller.
-
executor.ssh.client.DEFAULT_CONCURRENCY= 10¶ The default
concurrencyvalue to use forCommandPoolobjects created byforeach().
-
executor.ssh.client.DEFAULT_CONNECT_TIMEOUT= 10¶ The default
connect_timeoutvalue to use forRemoteCommandobjects.
-
executor.ssh.client.SSH_PROGRAM_NAME= 'ssh'¶ The name of the SSH client executable (a string).
-
executor.ssh.client.SSH_ERROR_STATUS= 255¶ The exit status used by the
sshprogram if an error occurred (an integer).Used by
RemoteCommand.error_messageandRemoteCommand.error_typeto distinguish when thesshprogram itself fails and when a remote command fails.
-
executor.ssh.client.foreach(hosts, *command, **options)[source]¶ Execute a command simultaneously on a group of remote hosts using SSH.
Parameters: - hosts – An iterable of strings with SSH host aliases.
- command – Any positional arguments are converted to a list and used to set the
commandproperty of theRemoteCommandobjects constructed byforeach(). - concurrency – Set the command pool
concurrencyproperty (defaults toDEFAULT_CONCURRENCY). - delay_checks – Set the command pool
delay_checksproperty (defaults toTrue). - logs_directory – Set the command pool
logs_directoryproperty (not set by default). - spinner – Set the command pool
spinnerproperty (not set by default). - options – Additional keyword arguments can be used to conveniently override the
default values of the writable properties of the
RemoteCommandobjects constructed byforeach()(seeRemoteCommand.__init__()for details).
Returns: The list of
RemoteCommandobjects constructed byforeach().Raises: Any of the following exceptions can be raised:
CommandPoolFailedifdelay_checksis enabled (the default) and a command in the pool that hascheckenabled (the default) fails.RemoteCommandFailedifdelay_checksis disabled (not the default) and an SSH connection was successful but the remote command failed (the exit code of thesshcommand was neither zero nor 255). Use the keyword argumentcheck=Falseto disable raising of this exception.RemoteConnectFailedifdelay_checksis disabled (not the default) and an SSH connection failed (the exit code of thesshcommand is 255). Use the keyword argumentcheck=Falseto disable raising of this exception.
Note
The
foreach()function enables thecheckanddelay_checksoptions by default in an attempt to make it easy to do “the right thing”. My assumption here is that if you are running the same command on multiple remote hosts:- You definitely want to know when a remote command has failed,
ideally without manually checking the
succeededproperty of each command. - Regardless of whether some remote commands fail you want to know that the command was at least executed on all hosts, otherwise your cluster of hosts will end up in a very inconsistent state.
- If remote commands fail and an exception is raised the exception message should explain which remote commands failed.
If these assumptions are incorrect then you can use the keyword arguments
check=Falseand/ordelay_checks=Falseto opt out of “doing the right thing” ;-)
-
executor.ssh.client.remote(ssh_alias, *command, **options)[source]¶ Execute a remote command (similar to
execute()).Parameters: - ssh_alias – Used to set
RemoteAccount.ssh_alias. - command – All positional arguments are passed to
RemoteCommand.__init__(). - options – All keyword arguments are passed to
RemoteCommand.__init__().
Returns: Refer to
execute_prepared().Raises: RemoteCommandFailedwhen the command exits with a nonzero exit code (andcheckisTrue).- ssh_alias – Used to set
-
class
executor.ssh.client.RemoteAccount(*args, **options)[source]¶ Trivial SSH alias parser.
This class acts as a base class for
RemoteCommandandRemoteContextthat provides three simple features:- It defines the
ssh_aliasandssh_userproperties. - When
ssh_aliasis set to a string that contains an@token it parses the string and sets bothssh_aliasandssh_user[1]. - It allows for
ssh_aliasto be passed as the first positional argument [2] or as a keyword argument [3].
[1] This enables RemoteCommand.have_superuser_privilegesto know that superuser privileges are available when the caller setsssh_aliasto a value likeroot@host. Of course the SSH client configuration can also override the remote username without the executor package knowing about it, but at least executor will be able to use the information that it does have.[2] This calling convention enables backwards compatibility with executor versions 14 and below which required ssh_aliasto be set using the first positional argument to the initializer of theRemoteCommandclass.[3] This new calling convention provides a uniform calling convention for the initializers of the local/remote command/context classes. Here’s an overview of the
RemoteAccountclass:Superclass: PropertyManagerSpecial methods: __init__()Properties: ssh_aliasandssh_userWhen you initialize a
RemoteAccountobject you are required to provide a value for thessh_aliasproperty. You can set the values of thessh_aliasandssh_userproperties by passing keyword arguments to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
RemoteAccountobject.Parameters: - args – Positional arguments are passed on to the initializer of
the
PropertyManagerclass (for future extensibility). - options – Any keyword arguments are passed on to the initializer
of the
PropertyManagerclass.
If the keyword argument ssh_alias isn’t given but positional arguments are provided, the first positional argument is used to set the
ssh_aliasproperty.- args – Positional arguments are passed on to the initializer of
the
-
ssh_alias[source]¶ The SSH alias of the remote host (a string).
If you set this property to a string that contains two nonempty tokens delimited by an
@character, the first token is used to setssh_userand the second token is used to setssh_alias. Here’s an example:>>> from executor.ssh.client import RemoteAccount >>> RemoteAccount('root@server') RemoteAccount(ssh_alias='server', ssh_user='root') >>> RemoteAccount(ssh_alias='server', ssh_user='root') RemoteAccount(ssh_alias='server', ssh_user='root') >>> RemoteAccount('server') RemoteAccount(ssh_alias='server', ssh_user=None)
Note
The
ssh_aliasproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named ssh_alias (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
ssh_user[source]¶ The username on the remote system (a string or
None).If the value of
ssh_userisNonethe SSH client program gets to decide about the remote username.Note
The
ssh_userproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
- It defines the
-
class
executor.ssh.client.RemoteCommand(*args, **options)[source]¶ RemoteCommandobjects use the SSH client program to execute remote commands.Here’s an overview of the
RemoteCommandclass:Superclasses: RemoteAccountandExternalCommandSpecial methods: __init__()Properties: batch_mode,command,command_line,compression,connect_timeout,directory,error_message,error_type,have_superuser_privileges,identity_file,ignore_known_hosts,known_hosts_file,log_level,port,ssh_commandandstrict_host_key_checkingYou can set the values of the
batch_mode,command,compression,connect_timeout,error_message,error_type,identity_file,known_hosts_file,log_level,port,ssh_commandandstrict_host_key_checkingproperties by passing keyword arguments to the class initializer.-
__init__(*args, **options)[source]¶ Initialize a
RemoteCommandobject.Parameters: - args – Refer to the initializers of the
RemoteAccountandExternalCommandclasses. - options – Keyword arguments can be used to conveniently override
the values of
batch_mode,connect_timeout,identity_file,ignore_known_hosts,log_level,port,strict_host_key_checking,known_hosts_file,ssh_commandand the writable properties of the base classesRemoteAccountandExternalCommand. Any other keyword argument will raiseTypeErroras usual.
The remote command is not started until you call
start()orwait().- args – Refer to the initializers of the
-
batch_mode[source]¶ Control the SSH client option
BatchMode(a boolean, defaults toTrue).The following description is quoted from man ssh_config:
If set to “yes”, passphrase/password querying will be disabled. In addition, theServerAliveIntervaloption will be set to 300 seconds by default. This option is useful in scripts and other batch jobs where no user is present to supply the password, and where it is desirable to detect a broken network swiftly. The argument must be “yes” or “no”. The default is “no”.This property defaults to
Truebecause it can get really awkward when a batch of SSH clients query for a passphrase/password on standard input at the same time.Note
The
batch_modeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command[source]¶ A list of strings with the command to execute (optional).
The value of
commandis optional forRemoteCommandobjects (as opposed toExternalCommandobjects) because the use of SSH implies a remote (interactive) shell that usually also accepts (interactive) commands as input. This means it is valid to create a remote command object without an actual remote command to execute, but with input that provides commands to execute instead.This “feature” can be useful to control non-UNIX systems that do accept SSH connections but don’t support a conventional UNIX shell. For example, I added support for this “feature” so that I was able to send commands to Juniper routers and switches over SSH with the purpose of automating the failover of a connection between two datacenters (the resulting Python program works great and it’s much faster than I am, making all of the required changes in a couple of seconds :-).
Note
The
commandproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command_line¶ The complete SSH client command including the remote command.
This is a list of strings with the SSH client command to connect to the remote host and execute
command.
-
compression[source]¶ Whether compression is enabled (a boolean, defaults to
False).Note
The
compressionproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
connect_timeout[source]¶ Control the SSH client option
ConnectTimeout(an integer).The following description is quoted from man ssh_config:
Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout. This value is used only when the target is down or really unreachable, not when it refuses the connection.Defaults to
DEFAULT_CONNECT_TIMEOUTso that non-interactive SSH connections created byRemoteCommanddon’t hang indefinitely when the remote system doesn’t respond properly.Note
The
connect_timeoutproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
directory¶ Set the remote working directory.
When you set this property you change the remote working directory, however reading back the property you’ll just get
DEFAULT_WORKING_DIRECTORY. This is because the superclassExternalCommandusesdirectoryas the local working directory for thesshcommand, and a remote working directory isn’t guaranteed to also exist on the local system.
-
error_message[source]¶ A user friendly explanation of how the remote command failed (a string or
None).Note
The
error_messageproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
error_type[source]¶ An exception class applicable to the kind of failure detected or
None.RemoteConnectFailedwhenreturncodeis set and matchesSSH_ERROR_STATUS,RemoteCommandFailedwhenreturncodeis set and not zero,Noneotherwise.Note
The
error_typeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
have_superuser_privileges¶ Trueifssh_useris set to ‘root’,Falseotherwise.There’s no easy way for
RemoteCommandto determine whether any given SSH alias logs into a remote system with superuser privileges so unlessssh_useris set to ‘root’ this is alwaysFalse.
-
identity_file[source]¶ The pathname of the identity file used to connect to the remote host (a string or
None).Note
The
identity_fileproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
ignore_known_hosts¶ Whether host key checking is disabled.
This is
Trueif host key checking is completely disabled:known_hosts_fileis set toos.devnullstrict_host_key_checkingis set toFalse
If you set this to
Truehost key checking is disabled andlog_levelis set to ‘error’ to silence warnings about automatically accepting host keys.If you set this to
Falsethenknown_hosts_file,log_levelandstrict_host_key_checkingare reset to their default values.
-
log_level[source]¶ Control the SSH client option
LogLevel(a string, defaults to ‘info’).The following description is quoted from man ssh_config:
Gives the verbosity level that is used when logging messages fromssh. The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of verbose output.Note
The
log_levelproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
ssh_command[source]¶ The command used to run the SSH client program.
This is a list of strings, by default the list contains just
SSH_PROGRAM_NAME. Thebatch_mode,connect_timeout,log_level,ssh_aliasandstrict_host_key_checkingproperties also influence the SSH client command line used.Note
The
ssh_commandproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
port[source]¶ The port number of the SSH server (defaults to
Nonewhich means the SSH client program decides).Note
The
portproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
strict_host_key_checking[source]¶ Control the SSH client option
StrictHostKeyChecking.This property accepts the values
TrueandFalseand the strings ‘yes’, ‘no’ and ‘ask’. The following description is quoted from man ssh_config:If this flag is set to “yes”,sshwill never automatically add host keys to the~/.ssh/known_hostsfile, and refuses to connect to hosts whose host key has changed. This provides maximum protection against trojan horse attacks, though it can be annoying when the/etc/ssh/ssh_known_hostsfile is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. If this flag is set to “no”, ssh will automatically add new host keys to the user known hosts files. If this flag is set to “ask”, new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases. The argument must be “yes”, “no”, or “ask”. The default is “ask”.This property defaults to
Falseso that when you connect to a remote system over SSH for the first time the host key is automatically added to the user known hosts file (instead of requiring interaction). As mentioned in the quote above the host keys of known hosts are always verified (but seeignore_known_hosts).Note
The
strict_host_key_checkingproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
known_hosts_file[source]¶ Control the SSH client option
UserKnownHostsFile(a string).The following description is quoted from man ssh_config:
Specifies one or more files to use for the user host key database, separated by whitespace. The default is~/.ssh/known_hosts,~/.ssh/known_hosts2.Note
The
known_hosts_fileproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
-
class
executor.ssh.client.RemoteCommandPool(concurrency=10, **options)[source]¶ Execute multiple remote commands concurrently.
After constructing a
RemoteCommandPoolinstance you add commands to it usingadd()and when you’re ready to run the commands you callrun().Note
The only difference between
CommandPoolandRemoteCommandPoolis the default concurrency. This may of course change in the future.Here’s an overview of the
RemoteCommandPoolclass:Superclass: CommandPoolSpecial methods: __init__()-
__init__(concurrency=10, **options)[source]¶ Initialize a
RemoteCommandPoolobject.Parameters: - concurrency – Override the value of
concurrency(an integer, defaults toDEFAULT_CONCURRENCYfor remote command pools). - options – Any additional keyword arguments are passed on
to the
CommandPoolconstructor.
- concurrency – Override the value of
-
-
class
executor.ssh.client.SecureTunnel(*args, **options)[source]¶ Easy to use SSH tunnels.
The
SecureTunnelclass combinesRemoteCommandwithEphemeralPortAllocatorandWaitUntilConnectedto implement easy to use SSH tunnel support.The
-Loption of the SSH client program is used to open a tunnel between the local system and a remote system that persists until the command is terminated (tip: use awithstatement).Additionally the
-Noption is used to prevent the client from executing a remote command, this enables compatibility with “tunnel only” SSH accounts that have their shell set to something like/usr/sbin/nologin.The
start()method waits for the tunnel to become available before returning control to the caller.Here’s an overview of the
SecureTunnelclass:Superclass: RemoteCommandPublic methods: start()Properties: asynchronous,command_line,compression,local_port,remote_host,remote_portandttyWhen you initialize a
SecureTunnelobject you are required to provide a value for theremote_portproperty. You can set the values of theasynchronous,compression,local_port,remote_hostandremote_portproperties by passing keyword arguments to the class initializer.-
asynchronous[source]¶ Whether to enable asynchronous command execution (a boolean, defaults to
True).Note
The
asynchronousproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
command_line¶ The complete SSH client command to open the tunnel (a list of strings).
This property overrides
RemoteCommand.command_lineto inject the command line options-Land-N.
-
compression[source]¶ Whether to enable compression (a boolean, defaults to
True).Note
The
compressionproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
local_port[source]¶ The port number on the side of the SSH client (an integer).
When the value of
local_portisn’t specified a free ephemeral port number is automatically selected usingEphemeralPortAllocator.Note
The
local_portproperty is acustom_property. You can change the value of this property using normal attribute assignment syntax. This property’s value is computed once (the first time it is accessed) and the result is cached. To clear the cached value you can usedelordelattr().
-
remote_host[source]¶ The remote host name to connect to (a string, defaults to ‘localhost’).
Note
The
remote_hostproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
remote_port[source]¶ The remote port number to connect to (an integer).
Note
The
remote_portproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named remote_port (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
-
exception
executor.ssh.client.RemoteConnectFailed(command, **options)[source]¶ Raised by
RemoteCommandwhen an SSH connection itself fails (not the remote command).Here’s an overview of the
RemoteConnectFailedclass:Superclass: ExternalCommandFailed
-
exception
executor.ssh.client.RemoteCommandFailed(command, **options)[source]¶ Raised by
RemoteCommandwhen a remote command executed over SSH fails.Here’s an overview of the
RemoteCommandFailedclass:Superclass: ExternalCommandFailed
-
exception
executor.ssh.client.RemoteCommandNotFound(command, **options)[source]¶ Raised by
RemoteCommandwhen a remote command returnsCOMMAND_NOT_FOUND_STATUS.Here’s an overview of the
RemoteCommandNotFoundclass:Superclasses: RemoteCommandFailedandCommandNotFound
The executor.ssh.server module¶
OpenSSH server automation for testing.
The executor.ssh.server module defines the SSHServer class
which can be used to start temporary OpenSSH servers that are isolated enough
from the host system to make them usable in the executor test suite (to
test remote command execution).
-
executor.ssh.server.SSHD_PROGRAM_NAME= 'sshd'¶ The name of the SSH server executable (a string).
-
class
executor.ssh.server.SSHServer(**options)[source]¶ Subclass of
ExternalCommandthat manages a temporary SSH server.The OpenSSH server spawned by the
SSHServerclass doesn’t need superuser privileges and doesn’t require any changes to/etc/passwdor/etc/shadow.Here’s an overview of the
SSHServerclass:Superclass: EphemeralTCPServerSpecial methods: __init__()Public methods: cleanup(),generate_config(),generate_key_file()andstart()Properties: client_optionsandsshd_path-
__init__(**options)[source]¶ Initialize an
SSHServerobject.Parameters: options – All keyword arguments are passed on to executor.ExternalCommand.__init__().
-
temporary_directory= None¶ The pathname of the temporary directory used to store the files required to run the SSH server (a string).
-
client_key_file= None¶ The pathname of the generated OpenSSH client key file (a string).
-
config_file= None¶ The pathname of the generated OpenSSH server configuration file (a string).
-
host_key_file= None¶ The random port number on which the SSH server will listen (an integer).
-
sshd_path¶ The absolute pathname of
SSHD_PROGRAM_NAME(a string).
-
client_options¶ The options for the OpenSSH client (required to connect with the server).
This is a dictionary of keyword arguments for
RemoteCommandto make it connect with the OpenSSH server (assuming the remote command connects to an IP address in the 127.0.0.0/24 range).
-
start(**options)[source]¶ Start the SSH server and wait for it to start accepting connections.
Parameters: options – Any keyword arguments are passed to the start()method of the superclass.Raises: Any exceptions raised by the start()method of the superclass.The
start()method automatically calls thegenerate_key_file()andgenerate_config()methods.
-
generate_key_file(filename)[source]¶ Generate a temporary host or client key for the OpenSSH server.
The
start()method automatically callsgenerate_key_file()to generatehost_key_fileandclient_key_file. This method uses thessh-keygenprogram to generate the keys.
-
generate_config()[source]¶ Generate a configuration file for the OpenSSH server.
The
start()method automatically callsgenerate_config().
-
cleanup()[source]¶ Clean up
temporary_directoryafter the test server finishes.
-
-
class
executor.ssh.server.EphemeralTCPServer(*command, **options)[source]¶ Make it easy to launch ephemeral TCP servers.
The
EphemeralTCPServerclass makes it easy to allocate an ephemeral port number that is not (yet) in use.Here’s an overview of the
EphemeralTCPServerclass:Superclasses: ExternalCommandandEphemeralPortAllocatorPublic methods: start()Properties: asynchronous-
asynchronous¶ Ephemeral TCP servers always set
ExternalCommand.asynchronoustoTrue.
-
start(**options)[source]¶ Start the TCP server and wait for it to start accepting connections.
Parameters: options – Any keyword arguments are passed to the start()method of the superclass.Raises: Any exceptions raised by start()andwait_until_connected().If the TCP server doesn’t start accepting connections within the configured timeout (see
wait_timeout) the process will be terminated and the timeout exception is propagated.
-
-
exception
executor.ssh.server.TimeoutError[source]¶ Raised when a TCP server doesn’t start accepting connections quickly enough.
This exception is raised by
wait_until_connected()when the TCP server doesn’t start accepting connections within a reasonable time.
The executor.tcp module¶
Miscellaneous TCP networking functionality.
The functionality in this module originated in the executor.ssh.server
module with the purpose of facilitating a robust automated test suite for the
executor.ssh.client module. While working on SSH tunnel support I
needed similar logic again and I decided to extract this code from the
executor.ssh.server module.
-
class
executor.tcp.WaitUntilConnected(**kw)[source]¶ Wait for a TCP endpoint to start accepting connections.
Here’s an overview of the
WaitUntilConnectedclass:Superclass: PropertyManagerPublic methods: wait_until_connected()Properties: connect_timeout,endpoint,hostname,is_connected,port_number,schemeandwait_timeoutWhen you initialize a
WaitUntilConnectedobject you are required to provide a value for theport_numberproperty. You can set the values of theconnect_timeout,hostname,port_number,schemeandwait_timeoutproperties by passing keyword arguments to the class initializer.-
connect_timeout[source]¶ The timeout in seconds for individual connection attempts (a number, defaults to 2).
Note
The
connect_timeoutproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
endpoint¶ A human friendly representation of the TCP endpoint (a string containing a URL).
-
hostname[source]¶ The host name or IP address to connect to (a string, defaults to
localhost).Note
The
hostnameproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
port_number[source]¶ The port number to connect to (an integer).
Note
The
port_numberproperty is arequired_property. You are required to provide a value for this property by calling the constructor of the class that defines the property with a keyword argument named port_number (unless a custom constructor is defined, in this case please refer to the documentation of that constructor). You can change the value of this property using normal attribute assignment syntax.
-
scheme[source]¶ A URL scheme that indicates the purpose of the ephemeral port (a string, defaults to ‘tcp’).
Note
The
schemeproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
wait_timeout[source]¶ The timeout in seconds for
wait_until_connected()(a number, defaults to 30).Note
The
wait_timeoutproperty is amutable_property. You can change the value of this property using normal attribute assignment syntax. To reset it to its default (computed) value you can usedelordelattr().
-
wait_until_connected()[source]¶ Wait until connections are being accepted.
Raises: TimeoutErrorwhen the SSH server isn’t fast enough to initialize.
-
-
class
executor.tcp.EphemeralPortAllocator(**kw)[source]¶ Allocate a free ephemeral port number.
Here’s an overview of the
EphemeralPortAllocatorclass:Superclass: WaitUntilConnectedProperties: ephemeral_port_numberandport_number-
port_number[source]¶ A dynamically selected free ephemeral port number (an integer between 49152 and 65535).
Note
The
port_numberproperty is alazy_property. This property’s value is computed once (the first time it is accessed) and the result is cached.
-
ephemeral_port_number¶ A random ephemeral port number (an integer between 49152 and 65535).
-
-
class
executor.tcp.EphemeralTCPServer(*command, **options)[source]¶ Make it easy to launch ephemeral TCP servers.
The
EphemeralTCPServerclass makes it easy to allocate an ephemeral port number that is not (yet) in use.Here’s an overview of the
EphemeralTCPServerclass:Superclasses: ExternalCommandandEphemeralPortAllocatorPublic methods: start()Properties: asynchronous-
asynchronous¶ Ephemeral TCP servers always set
ExternalCommand.asynchronoustoTrue.
-
start(**options)[source]¶ Start the TCP server and wait for it to start accepting connections.
Parameters: options – Any keyword arguments are passed to the start()method of the superclass.Raises: Any exceptions raised by start()andwait_until_connected().If the TCP server doesn’t start accepting connections within the configured timeout (see
wait_timeout) the process will be terminated and the timeout exception is propagated.
-
-
exception
executor.tcp.TimeoutError[source]¶ Raised when a TCP server doesn’t start accepting connections quickly enough.
This exception is raised by
wait_until_connected()when the TCP server doesn’t start accepting connections within a reasonable time.