1OSRF_PYCOMMON(1) osrf_pycommon OSRF_PYCOMMON(1)
2
3
4
6 osrf_pycommon - osrf_pycommon Documentation
7
8 osrf_pycommon is a python package which contains commonly used Python
9 boilerplate code and patterns. Things like ansi terminal coloring,
10 capturing colored output from programs using subprocess, or even a sim‐
11 ple logging system which provides some nice functionality over the
12 built-in Python logging system.
13
14 The functionality provided here should be generic enough to be reused
15 in arbitrary scenarios and should avoid bringing in dependencies which
16 are not part of the standard Python library. Where possible Windows
17 and Linux/OS X should be supported, and where it cannot it should be
18 gracefully degrading. Code should be pure Python as well as Python 2
19 and Python 3 bilingual.
20
21 Contents:
22
24 This module provides functions and patterns for creating Command Line
25 Interface (CLI) tools.
26
27 Common CLI Functions
28 Commonly used, CLI related functions.
29
30 osrf_pycommon.cli_utils.common.extract_argument_group(args, delimit‐
31 ing_option)
32 Extract a group of arguments from a list of arguments using a
33 delimiter.
34
35 Here is an example:
36
37 >>> extract_argument_group(['foo', '--args', 'bar', '--baz'], '--args')
38 (['foo'], ['bar', '--baz'])
39
40 The group can always be ended using the double hyphen --. In
41 order to pass a double hyphen as arguments, use three hyphens
42 ---. Any set of hyphens encountered after the delimiter, and up
43 to --, which have three or more hyphens and are isolated, will
44 be captured and reduced by one hyphen.
45
46 For example:
47
48 >> extract_argument_group(['foo',
49 '--args', 'bar', '--baz', '---', '--',
50 '--foo-option'], '--args')
51 (['foo', '--foo-option'], ['bar', '--baz', '--'])
52
53 In the result the -- comes from the --- in the input. The
54 --args and the corresponding -- are removed entirely.
55
56 The delimiter and -- terminator combination can also happen mul‐
57 tiple times, in which case the bodies of arguments are combined
58 and returned in the order they appeared.
59
60 For example:
61
62 >> extract_argument_group(['foo',
63 '--args', 'ping', '--',
64 'bar',
65 '--args', 'pong', '--',
66 'baz',
67 '--args', '--'], '--args')
68 (['foo', 'bar', 'baz'], ['ping', 'pong'])
69
70 Note: -- cannot be used as the delimiting_option.
71
72 Parameters
73
74 · args (list) – list of strings which are ordered argu‐
75 ments.
76
77 · delimiting_option (str) – option which denotes where to
78 split the args.
79
80 Returns
81 tuple of arguments before and after the delimiter.
82
83 Return type
84 tuple
85
86 Raises ValueError if the delimiting_option is --.
87
88 osrf_pycommon.cli_utils.common.extract_jobs_flags(arguments)
89 Extracts make job flags from a list of other make flags, i.e.
90 -j8 -l8
91
92 The input arguments are given as a string separated by white‐
93 space. Make job flags are matched and removed from the argu‐
94 ments, and the Make job flags and what is left over from the
95 input arguments are returned.
96
97 If no job flags are encountered, then an empty string is
98 returned as the first element of the returned tuple.
99
100 Examples:
101
102 >> extract_jobs_flags('-j8 -l8')
103 ('-j8 -l8', '')
104 >> extract_jobs_flags('-j8 ')
105 ('-j8', ' ')
106 >> extract_jobs_flags('target -j8 -l8 --some-option')
107 ('-j8 -l8', 'target --some-option')
108 >> extract_jobs_flags('target --some-option')
109 ('', 'target --some-option')
110
111 Parameters
112 arguments (str) – string of space separated arguments
113 which may or may not contain make job flags
114
115 Returns
116 tuple of make jobs flags as a space separated string and
117 leftover arguments as a space separated string
118
119 Return type
120 tuple
121
122 The Verb Pattern
123 The verb pattern is a pattern where a single command aggregates multi‐
124 ple related commands by taking a required positional argument which is
125 the “verb” for the action you want to perform. For example, catkin
126 build is an example of a command and verb pair, where catkin is the
127 command and build is the verb. In this example, the catkin command
128 groups “actions” which are related to catkin together using verbs like
129 build which will build a workspace of catkin packages.
130
131 Command Boilerplate
132 This is an example boilerplate of a command which will use verbs:
133
134 from __future__ import print_function
135
136 import argparse
137 import sys
138
139 from osrf_pycommon.cli_utils.verb_pattern import create_subparsers
140 from osrf_pycommon.cli_utils.verb_pattern import list_verbs
141 from osrf_pycommon.cli_utils.verb_pattern import split_arguments_by_verb
142
143 COMMAND_NAME = '<INSERT COMMAND NAME HERE>'
144
145 VERBS_ENTRY_POINT = '{0}.verbs'.format(COMMAND_NAME)
146
147
148 def main(sysargs=None):
149 # Assign sysargs if not set
150 sysargs = sys.argv[1:] if sysargs is None else sysargs
151
152 # Create a top level parser
153 parser = argparse.ArgumentParser(
154 description="{0} command".format(COMMAND_NAME)
155 )
156
157 # Generate a list of verbs available
158 verbs = list_verbs(VERBS_ENTRY_POINT)
159
160 # Create the subparsers for each verb and collect the arg preprocessors
161 argument_preprocessors, verb_subparsers = create_subparsers(
162 parser,
163 COMMAND_NAME,
164 verbs,
165 VERBS_ENTRY_POINT,
166 sysargs,
167 )
168
169 # Determine the verb, splitting arguments into pre and post verb
170 verb, pre_verb_args, post_verb_args = split_arguments_by_verb(sysargs)
171
172 # Short circuit -h and --help
173 if '-h' in pre_verb_args or '--help' in pre_verb_args:
174 parser.print_help()
175 sys.exit(0)
176
177 # Error on no verb provided
178 if verb is None:
179 print(parser.format_usage())
180 sys.exit("Error: No verb provided.")
181 # Error on unknown verb provided
182 if verb not in verbs:
183 print(parser.format_usage())
184 sys.exit("Error: Unknown verb '{0}' provided.".format(verb))
185
186 # Short circuit -h and --help for verbs
187 if '-h' in post_verb_args or '--help' in post_verb_args:
188 verb_subparsers[verb].print_help()
189 sys.exit(0)
190
191 # First allow the verb's argument preprocessor to strip any args
192 # and return any "extra" information it wants as a dict
193 processed_post_verb_args, extras = \
194 argument_preprocessors[verb](post_verb_args)
195 # Then allow argparse to process the left over post-verb arguments along
196 # with the pre-verb arguments and the verb itself
197 args = parser.parse_args(pre_verb_args + [verb] + processed_post_verb_args)
198 # Extend the argparse result with the extras from the preprocessor
199 for key, value in extras.items():
200 setattr(args, key, value)
201
202 # Finally call the subparser's main function with the processed args
203 # and the extras which the preprocessor may have returned
204 sys.exit(args.main(args) or 0)
205
206 This function is mostly boilerplate in that it will likely not change
207 much between commands of different types, but it would also be less
208 transparent to have this function created for you. If you are using
209 this boilerplate to implement your command, then you should be careful
210 to update COMMAND_NAME to reflect your command’s name.
211
212 This line defines the entry_point group for your command’s verbs:
213
214 VERBS_ENTRY_POINT = '{0}.verbs'.format(COMMAND_NAME)
215
216 In the case that your command is called foo then this would become
217 foo.verbs. This name is important because it is how verbs for this
218 command can be provided by your Python package or others. For example,
219 each verb for your command foo will need entry in the setup.py of its
220 containing package, like this:
221
222 setup(
223 ...
224 entry_points={
225 ...
226 'foo.verbs': [
227 'bar = foo.verbs.bar:entry_point_data',
228 ],
229 }
230 )
231
232 You can see here that you are defining bar to be a an entry_point of
233 type foo.verbs which in turn points to a module and reference
234 foo.verbs.bar and entry_point_data. At run time this verb pattern will
235 let your command lookup all things defined as foo.verbs and load up the
236 reference to which they point.
237
238 Adding Verbs
239 In order to add a verb to your command, a few things must happen.
240
241 First you must have an entry in the setup.py as described above. This
242 allows the command to find the entry_point for your verb at run time.
243 The entry_point for these verbs should point to a dictionary which
244 describes the verb being added.
245
246 This is an example of an entry_point_data dictionary for a verb:
247
248 entry_point_data = dict(
249 verb='build',
250 description='Builds a workspace of packages',
251 # Called for execution, given parsed arguments object
252 main=main,
253 # Called first to setup argparse, given argparse parser
254 prepare_arguments=prepare_arguments,
255 # Called after prepare_arguments, but before argparse.parse_args
256 argument_preprocessor=argument_preprocessor,
257 )
258
259 As you can see this dictionary describes the verb and gives references
260 to functions which allow the command to describe the verb, hook into
261 argparse parameter creation for the verb, and to execute the verb. The
262 verb, description, main, and prepare_arguments keys of the dictionary
263 are required, but the argument_preprocessor key is optional.
264
265 · verb: This is the name of the verb, and is how the command knows
266 which verb implementation to match to a verb on the command line.
267
268 · description: This is used by the argument parsing to describe the
269 verb in --help.
270
271 · prepare_arguments: This function gets called to allow the verb to
272 setup it’s own argparse options. This function should always take one
273 parameter which is the argparse.ArgumentParser for this verb, to
274 which arguments can be added. It can optionally take a second parame‐
275 ter which are the current command line arguments. This is not always
276 needed, but can be useful in some cases. This function should always
277 return the parser.
278
279 · argument_preprocessor: This function is optional, but allows your
280 verb an opportunity to process the raw arguments before they are
281 passed to argparse’s parse_args function. This can be useful when
282 argparse is not capable of processing the options correctly.
283
284 · main: This is the implementation of the verb, it gets called last and
285 is passed the parsed arguments. The return type of this function is
286 used for sys.exit, a return type of None is interpreted as 0.
287
288 Here is an invented example of main, prepare_arguments, and argu‐
289 ment_preprocessor:
290
291 def prepare_arguments(parser):
292 parser.add_argument('--some-argument', action='store_true', default=False)
293 return parser
294
295 def argument_preprocessor(args):
296 extras = {}
297
298 if '-strange-argument' in args:
299 args.remove('-strange-argument')
300 extras['strange_argument'] = True
301
302 return args, extras
303
304 def main(options):
305 print('--some-argument:', options.some_argument)
306 print('-strange-argument:', options.strange_argument)
307 if options.strange_argument:
308 return 1
309 return 0
310
311 The above example is simply to illustrate the signature of these func‐
312 tions and how they might be used.
313
314 Verb Pattern API
315 API for implementing commands and verbs which used the verb pattern.
316
317 osrf_pycommon.cli_utils.verb_pattern.call_prepare_arguments(func,
318 parser, sysargs=None)
319 Call a prepare_arguments function with the correct number of
320 parameters.
321
322 The prepare_arguments function of a verb can either take one
323 parameter, parser, or two parameters parser and args, where args
324 are the current arguments being processed.
325
326 Parameters
327
328 · func (Callable) – Callable prepare_arguments function.
329
330 · parser (argparse.ArgumentParser) – parser which is
331 always passed to the function
332
333 · sysargs (list) – arguments to optionally pass to the
334 function, if needed
335
336 Returns
337 return value of function or the parser if the function
338 returns None.
339
340 Return type
341 argparse.ArgumentParser
342
343 Raises ValueError if a function with the wrong number of parame‐
344 ters is given
345
346 osrf_pycommon.cli_utils.verb_pattern.create_subparsers(parser,
347 cmd_name, verbs, group, sysargs, title=None)
348 Creates argparse subparsers for each verb which can be discov‐
349 ered.
350
351 Using the verbs parameter, the available verbs are iterated
352 through. For each verb a subparser is created for it using the
353 parser parameter. The cmd_name is used to fill the title and
354 description of the add_subparsers function call. The group
355 parameter is used with each verb to load the verb’s description,
356 prepare_arguments function, and the verb’s argument_preproces‐
357 sors if available. Each verb’s prepare_arguments function is
358 called, allowing them to add arguments. Finally a list of argu‐
359 ment_preprocessors functions and verb subparsers are returned,
360 one for each verb.
361
362 Parameters
363
364 · parser (argparse.ArgumentParser) – parser for this com‐
365 mand
366
367 · cmd_name (str) – name of the command to which the verbs
368 are being added
369
370 · verbs (list) – list of verbs (by name as a string)
371
372 · group (str) – name of the entry_point group for the
373 verbs
374
375 · sysargs (list) – list of system arguments
376
377 · title (str) – optional custom title for the command
378
379 Returns
380 tuple of argument_preprocessors and verb subparsers
381
382 Return type
383 tuple
384
385 osrf_pycommon.cli_utils.verb_pattern.default_argument_preproces‐
386 sor(args)
387 Return unmodified args and an empty dict for extras
388
389 osrf_pycommon.cli_utils.verb_pattern.list_verbs(group)
390 List verbs available for a given entry_point group.
391
392 Parameters
393 group (str) – entry_point group name for the verbs to
394 list
395
396 Returns
397 list of verb names for the given entry_point group
398
399 Return type
400 list of str
401
402 osrf_pycommon.cli_utils.verb_pattern.load_verb_description(verb_name,
403 group)
404 Load description of a verb in a given group by name.
405
406 Parameters
407
408 · verb_name (str) – name of the verb to load, as a string
409
410 · group (str) – entry_point group name which the verb is
411 in
412
413 Returns
414 verb description
415
416 Return type
417 dict
418
419 osrf_pycommon.cli_utils.verb_pattern.split_arguments_by_verb(arguments)
420 Split arguments by verb.
421
422 Given a list of arguments (list of strings), the verb, the pre
423 verb arguments, and the post verb arguments are returned.
424
425 For example:
426
427 >>> args = ['--command-arg1', 'verb', '--verb-arg1', '--verb-arg2']
428 >>> split_arguments_by_verb(args)
429 ('verb', ['--command-arg1'], ['--verb-arg1', '--verb-arg2'])
430
431 Parameters
432 arguments (list) – list of system arguments
433
434 Returns
435 the verb (str), pre verb args (list), and post verb args
436 (list)
437
438 Return type
439 tuple
440
442 This module provides functions for doing process management.
443
444 These are the main sections of this module:
445
446 · Asynchronous Process Utilities
447
448 · Synchronous Process Utilities
449
450 · Utility Functions
451
452 Asynchronous Process Utilities
453 There is a function and class which can be used together with your cus‐
454 tom Tollius or asyncio run loop.
455
456 The osrf_pycommon.process_utils.async_execute_process() function is a
457 coroutine which allows you to run a process and get the output back bit
458 by bit in real-time, either with stdout and stderr separated or com‐
459 bined. This function also allows you to emulate the terminal using a
460 pty simply by toggling a flag in the parameters.
461
462 Along side this coroutine is a Protocol class,
463 osrf_pycommon.process_utils.AsyncSubprocessProtocol, from which you can
464 inherit in order to customize how the yielded output is handled.
465
466 Because this coroutine is built on the trollius/asyncio framework’s
467 subprocess functions, it is portable and should behave the same on all
468 major OS’s. (including on Windows where an IOCP implementation is used)
469
470 osrf_pycommon.process_utils.async_execute_process(protocol_class,
471 cmd=None, cwd=None, env=None, shell=False, emulate_tty=False,
472 stderr_to_stdout=True)
473 Coroutine to execute a subprocess and yield the output back
474 asynchronously.
475
476 This function is meant to be used with the Python asyncio mod‐
477 ule, which is available via pip with Python 3.3 and built-in to
478 Python 3.4. On Python >= 2.6 you can use the trollius module to
479 get the same functionality, but without using the new yield from
480 syntax.
481
482 Here is an example of how to use this function:
483
484 import asyncio
485 from osrf_pycommon.process_utils import async_execute_process
486 from osrf_pycommon.process_utils import AsyncSubprocessProtocol
487 from osrf_pycommon.process_utils import get_loop
488
489
490 @asyncio.coroutine
491 def setup():
492 transport, protocol = yield from async_execute_process(
493 AsyncSubprocessProtocol, ['ls', '/usr'])
494 returncode = yield from protocol.complete
495 return returncode
496
497 retcode = get_loop().run_until_complete(setup())
498 get_loop().close()
499
500 That same example using trollius would look like this:
501
502 import trollius as asyncio
503 from osrf_pycommon.process_utils import async_execute_process
504 from osrf_pycommon.process_utils import AsyncSubprocessProtocol
505 from osrf_pycommon.process_utils import get_loop
506
507
508 @asyncio.coroutine
509 def setup():
510 transport, protocol = yield asyncio.From(async_execute_process(
511 AsyncSubprocessProtocol, ['ls', '/usr']))
512 returncode = yield asyncio.From(protocol.complete)
513 raise asyncio.Return(returncode)
514
515 retcode = get_loop().run_until_complete(setup())
516 get_loop().close()
517
518 This difference is required because in Python < 3.3 the yield
519 from syntax is not valid.
520
521 In both examples, the first argument is the default
522 AsyncSubprocessProtocol protocol class, which simply prints out‐
523 put from stdout to stdout and output from stderr to stderr.
524
525 If you want to capture and do something with the output or write
526 to the stdin, then you need to subclass from the
527 AsyncSubprocessProtocol class, and override the on_std‐
528 out_received, on_stderr_received, and on_process_exited func‐
529 tions.
530
531 See the documentation for the AsyncSubprocessProtocol class for
532 more details, but here is an example which uses asyncio from
533 Python 3.4:
534
535 import asyncio
536 from osrf_pycommon.process_utils import async_execute_process
537 from osrf_pycommon.process_utils import AsyncSubprocessProtocol
538 from osrf_pycommon.process_utils import get_loop
539
540
541 class MyProtocol(AsyncSubprocessProtocol):
542 def __init__(self, file_name, **kwargs):
543 self.fh = open(file_name, 'w')
544 AsyncSubprocessProtocol.__init__(self, **kwargs)
545
546 def on_stdout_received(self, data):
547 # Data has line endings intact, but is bytes in Python 3
548 self.fh.write(data.decode('utf-8'))
549
550 def on_stderr_received(self, data):
551 self.fh.write(data.decode('utf-8'))
552
553 def on_process_exited(self, returncode):
554 self.fh.write("Exited with return code: {0}".format(returncode))
555 self.fh.close()
556
557
558 @asyncio.coroutine
559 def log_command_to_file(cmd, file_name):
560
561 def create_protocol(**kwargs):
562 return MyProtocol(file_name, **kwargs)
563
564 transport, protocol = yield from async_execute_process(
565 create_protocol, cmd)
566 returncode = yield from protocol.complete
567 return returncode
568
569 get_loop().run_until_complete(
570 log_command_to_file(['ls', '/'], '/tmp/out.txt'))
571 get_loop().close()
572
573 See the subprocess.Popen class for more details on some of the
574 parameters to this function like cwd, env, and shell.
575
576 See the osrf_pycommon.process_utils.execute_process() function
577 for more details on the emulate_tty parameter.
578
579 Parameters
580
581 · protocol_class (AsyncSubprocessProtocol or a subclass)
582 – Protocol class which handles subprocess callbacks
583
584 · cmd (list) – list of arguments where the executable is
585 the first item
586
587 · cwd (str) – directory in which to run the command
588
589 · env (dict) – a dictionary of environment variable names
590 to values
591
592 · shell (bool) – if True, the cmd variable is interpreted
593 by a the shell
594
595 · emulate_tty (bool) – if True, pty’s are passed to the
596 subprocess for stdout and stderr, see
597 osrf_pycommon.process_utils.execute_process().
598
599 · stderr_to_stdout (bool) – if True, stderr is directed
600 to stdout, so they are not captured separately.
601
602 class osrf_pycommon.process_utils.AsyncSubprocessProtocol(stdin=None,
603 stdout=None, stderr=None)
604 Protocol to subclass to get events from async_execute_process().
605
606 When subclassing this Protocol class, you should override these
607 functions:
608
609 def on_stdout_received(self, data):
610 # ...
611
612 def on_stderr_received(self, data):
613 # ...
614
615 def on_process_exited(self, returncode):
616 # ...
617
618 By default these functions just print the data received from
619 stdout and stderr and does nothing when the process exits.
620
621 Data received by the on_stdout_received and on_stderr_received
622 functions is always in bytes (str in Python2 and bytes in
623 Python3). Therefore, it may be necessary to call .decode() on
624 the data before printing to the screen.
625
626 Additionally, the data received will not be stripped of new
627 lines, so take that into consideration when printing the result.
628
629 You can also override these less commonly used functions:
630
631 def on_stdout_open(self):
632 # ...
633
634 def on_stdout_close(self, exc):
635 # ...
636
637 def on_stderr_open(self):
638 # ...
639
640 def on_stderr_close(self, exc):
641 # ...
642
643 These functions are called when stdout/stderr are opened and
644 closed, and can be useful when using pty’s for example. The exc
645 parameter of the *_close functions is None unless there was an
646 exception.
647
648 In addition to the overridable functions this class has a few
649 useful public attributes. The stdin attribute is a reference to
650 the PipeProto which follows the asyncio.WriteTransport inter‐
651 face. The stdout and stderr attributes also reference their
652 PipeProto. The complete attribute is a asyncio.Future which is
653 set to complete when the process exits and its result is the
654 return code.
655
656 The complete attribute can be used like this:
657
658 import asyncio
659 from osrf_pycommon.process_utils import async_execute_process
660 from osrf_pycommon.process_utils import AsyncSubprocessProtocol
661 from osrf_pycommon.process_utils import get_loop
662
663
664 @asyncio.coroutine
665 def setup():
666 transport, protocol = yield from async_execute_process(
667 AsyncSubprocessProtocol, ['ls', '-G', '/usr'])
668 retcode = yield from protocol.complete
669 print("Exited with", retcode)
670
671 # This will block until the protocol.complete Future is done.
672 get_loop().run_until_complete(setup())
673 get_loop().close()
674
675 connection_made(transport)
676 Called when a connection is made.
677
678 The argument is the transport representing the pipe con‐
679 nection. To receive data, wait for data_received()
680 calls. When the connection is closed, connection_lost()
681 is called.
682
683 pipe_data_received(fd, data)
684 Called when the subprocess writes data into stdout/stderr
685 pipe.
686
687 fd is int file descriptor. data is bytes object.
688
689 process_exited()
690 Called when subprocess has exited.
691
692 In addtion to these functions, there is a utility function for getting
693 the correct asyncio event loop:
694
695 osrf_pycommon.process_utils.get_loop()
696 This function will return the proper event loop for the subpro‐
697 cess async calls.
698
699 On Unix this just returns asyncio.get_event_loop(), but on Win‐
700 dows it will set and return a asyncio.ProactorEventLoop instead.
701
702 Treatment of File Descriptors
703 Unlike subprocess.Popen, all of the process_utils functions behave the
704 same way on Python versions 2.7 through 3.4, and they do not close
705 inheritable <https://docs.python.org/3.4/library/os.html#fd-inheri‐
706 tance>. file descriptors before starting subprocesses. This is equiva‐
707 lent to passing close_fds=False to subprocess.Popen on all Python ver‐
708 sions.
709
710 In Python 3.2, the subprocess.Popen default for the close_fds option
711 changed from False to True so that file descriptors opened by the par‐
712 ent process were closed before spawning the child process. In Python
713 3.4, PEP 0446 additionally made it so even when close_fds=False file
714 descriptors which are non-inheritable are still closed before spawning
715 the subprocess.
716
717 If you want to be able to pass file descriptors to subprocesses in
718 Python 3.4 or higher, you will need to make sure they are inheritable
719 <https://docs.python.org/3.4/library/os.html#fd-inheritance>.
720
721 Synchronous Process Utilities
722 For synchronous execution and output capture of subprocess, there are
723 two functions:
724
725 · osrf_pycommon.process_utils.execute_process()
726
727 · osrf_pycommon.process_utils.execute_process_split()
728
729 These functions are not yet using the trollius/asyncio framework as a
730 back-end and therefore on Windows will not stream the data from the
731 subprocess as it does on Unix machines. Instead data will not be
732 yielded until the subprocess is finished and all output is buffered
733 (the normal warnings about long running programs with lots of output
734 apply).
735
736 The streaming of output does not work on Windows because on Windows the
737 select.select() method only works on sockets and not file-like objects
738 which are used with subprocess pipes. asyncio implements Windows sub‐
739 process support by implementing a Proactor event loop based on Window’s
740 IOCP API. One future option will be to implement this synchronous
741 style method using IOCP in this module, but another option is to just
742 make synchronous the asynchronous calls, but there are issues with that
743 as well. In the mean time, if you need streaming of output in both
744 Windows and Unix, use the asynchronous calls.
745
746 osrf_pycommon.process_utils.execute_process(cmd, cwd=None, env=None,
747 shell=False, emulate_tty=False)
748 Executes a command with arguments and returns output line by
749 line.
750
751 All arguments, except emulate_tty, are passed directly to sub‐
752 process.Popen.
753
754 execute_process returns a generator which yields the output,
755 line by line, until the subprocess finishes at which point the
756 return code is yielded.
757
758 This is an example of how this function should be used:
759
760 from __future__ import print_function
761 from osrf_pycommon.process_utils import execute_process
762
763 cmd = ['ls', '-G']
764 for line in execute_process(cmd, cwd='/usr'):
765 if isinstance(line, int):
766 # This is a return code, the command has exited
767 print("'{0}' exited with: {1}".format(' '.join(cmd), line))
768 continue # break would also be appropriate here
769 # In Python 3, it will be a bytes array which needs to be decoded
770 if not isinstance(line, str):
771 line = line.decode('utf-8')
772 # Then print it to the screen
773 print(line, end='')
774
775 stdout and stderr are always captured together and returned line
776 by line through the returned generator. New line characters are
777 preserved in the output, so if re-printing the data take care to
778 use end='' or first rstrip the output lines.
779
780 When emulate_tty is used on Unix systems, commands will identify
781 that they are on a tty and should output color to the screen as
782 if you were running it on the terminal, and therefore there
783 should not be any need to pass arguments like -c color.ui=always
784 to commands like git. Additionally, programs might also behave
785 differently in when emulate_tty is being used, for example,
786 Python will default to unbuffered output when it detects a tty.
787
788 emulate_tty works by using psuedo-terminals on Unix machines,
789 and so if you are running this command many times in parallel
790 (like hundreds of times) then you may get one of a few different
791 OSError’s. For example, “OSError: [Errno 24] Too many open
792 files: ‘/dev/ttyp0’” or “OSError: out of pty devices”. You
793 should also be aware that you share pty devices with the rest of
794 the system, so even if you are not using a lot, it is possible
795 to get this error. You can catch this error before getting data
796 from the generator, so when using emulate_tty you might want to
797 do something like this:
798
799 from __future__ import print_function
800 from osrf_pycommon.process_utils import execute_process
801
802 cmd = ['ls', '-G', '/usr']
803 try:
804 output = execute_process(cmd, emulate_tty=True)
805 except OSError:
806 output = execute_process(cmd, emulate_tty=False)
807 for line in output:
808 if isinstance(line, int):
809 print("'{0}' exited with: {1}".format(' '.join(cmd), line))
810 continue
811 # In Python 3, it will be a bytes array which needs to be decoded
812 if not isinstance(line, str):
813 line = line.decode('utf-8')
814 print(line, end='')
815
816 This way if a pty cannot be opened in order to emulate the tty
817 then you can try again without emulation, and any other OSError
818 should raise again with emulate_tty set to False. Obviously,
819 you only want to do this if emulating the tty is non-critical to
820 your processing, like when you are using it to capture color.
821
822 Any color information that the command outputs as ANSI escape
823 sequences is captured by this command. That way you can print
824 the output to the screen and preserve the color formatting.
825
826 If you do not want color to be in the output, then try setting
827 emulate_tty to False, but that does not guarantee that there is
828 no color in the output, instead it only will cause called pro‐
829 cesses to identify that they are not being run in a terminal.
830 Most well behaved programs will not output color if they detect
831 that they are not being executed in a terminal, but you
832 shouldn’t rely on that.
833
834 If you want to ensure there is no color in the output from an
835 executed process, then use this function:
836
837 osrf_pycommon.terminal_color.remove_ansi_escape_senquences()
838
839 Exceptions can be raised by functions called by the implementa‐
840 tion, for example, subprocess.Popen can raise an OSError when
841 the given command is not found. If you want to check for the
842 existence of an executable on the path, see: which(). However,
843 this function itself does not raise any special exceptions.
844
845 Parameters
846
847 · cmd (list) – list of strings with the first item being
848 a command and subsequent items being any arguments to
849 that command; passed directly to subprocess.Popen.
850
851 · cwd (str) – path in which to run the command, defaults
852 to None which means os.getcwd() is used; passed
853 directly to subprocess.Popen.
854
855 · env (dict) – environment dictionary to use for execut‐
856 ing the command, default is None which uses the os.env‐
857 iron environment; passed directly to subprocess.Popen.
858
859 · shell (bool) – If True the system shell is used to
860 evaluate the command, default is False; passed directly
861 to subprocess.Popen.
862
863 · emulate_tty (bool) – If True attempts to use a pty to
864 convince subprocess’s that they are being run in a ter‐
865 minal. Typically this is useful for capturing colorized
866 output from commands. This does not work on Windows (no
867 pty’s), so it is considered False even when True.
868 Defaults to False.
869
870 Returns
871 a generator which yields output from the command line by
872 line
873
874 Return type
875 generator which yields strings
876
877 Availability: Unix (streaming), Windows (blocking)
878
879 osrf_pycommon.process_utils.execute_process_split(cmd, cwd=None,
880 env=None, shell=False, emulate_tty=False)
881 execute_process(), except stderr is returned separately.
882
883 Instead of yielding output line by line until yielding a return
884 code, this function always a triplet of stdout, stderr, and
885 return code. Each time only one of the three will not be None.
886 Once you receive a non-None return code (type will be int) there
887 will be no more stdout or stderr. Therefore you can use the
888 command like this:
889
890 from __future__ import print_function
891 import sys
892 from osrf_pycommon.process_utils import execute_process_split
893
894 cmd = ['time', 'ls', '-G']
895 for out, err, ret in execute_process_split(cmd, cwd='/usr'):
896 # In Python 3, it will be a bytes array which needs to be decoded
897 out = out.decode('utf-8') if out is not None else None
898 err = err.decode('utf-8') if err is not None else None
899 if ret is not None:
900 # This is a return code, the command has exited
901 print("'{0}' exited with: {1}".format(' '.join(cmd), ret))
902 break
903 if out is not None:
904 print(out, end='')
905 if err is not None:
906 print(err, end='', file=sys.stderr)
907
908 When using this, it is possible that the stdout and stderr data
909 can be returned in a different order than what would happen on
910 the terminal. This is due to the fact that the subprocess is
911 given different buffers for stdout and stderr and so there is a
912 race condition on the subprocess writing to the different buf‐
913 fers and this command reading the buffers. This can be avoided
914 in most scenarios by using emulate_tty, because of the use of
915 pty’s, though the ordering can still not be guaranteed and the
916 number of pty’s is finite as explained in the documentation for
917 execute_process(). For situations where output ordering between
918 stdout and stderr are critical, they should not be returned sep‐
919 arately and instead should share one buffer, and so
920 execute_process() should be used.
921
922 For all other parameters and documentation see:
923 execute_process()
924
925 Availability: Unix (streaming), Windows (blocking)
926
927 Utility Functions
928 Currently there is only one utility function, a Python implementation
929 of the which shell command.
930
931 osrf_pycommon.process_utils.which(cmd, mode=1, path=None, **kwargs)
932 Given a command, mode, and a PATH string, return the path which
933 conforms to the given mode on the PATH, or None if there is no
934 such file.
935
936 mode defaults to os.F_OK | os.X_OK. path defaults to the result
937 of os.environ.get("PATH"), or can be overridden with a custom
938 search path.
939
940 Backported from shutil.which() (‐
941 https://docs.python.org/3.3/library/shutil.html#shutil.which),
942 available in Python 3.3.
943
945 This module provides tools for colorizing terminal output.
946
947 This module defines the ansi escape sequences used for colorizing the
948 output from terminal programs in Linux. You can access the ansi escape
949 sequences using the ansi() function:
950
951 >>> from osrf_pycommon.terminal_color import ansi
952 >>> print(["This is ", ansi('red'), "red", ansi('reset'), "."])
953 ['This is ', '\x1b[31m', 'red', '\x1b[0m', '.']
954
955 You can also use format_color() to do in-line substitution of keys
956 wrapped in @{} markers for their ansi escape sequences:
957
958 >>> from osrf_pycommon.terminal_color import format_color
959 >>> print(format_color("This is @{bf}blue@{reset}.").split())
960 ['This', 'is', '\x1b[34mblue\x1b[0m.']
961
962 This is a list of all of the available substitutions:
963
964 ┌──────────────┬─────────┬──────────┐
965 │Long Form │ Shorter │ Value │
966 ├──────────────┼─────────┼──────────┤
967 │@{blackf} │ @{kf} │ \033[30m │
968 ├──────────────┼─────────┼──────────┤
969 │@{redf} │ @{rf} │ \033[31m │
970 ├──────────────┼─────────┼──────────┤
971 │@{greenf} │ @{gf} │ \033[32m │
972 ├──────────────┼─────────┼──────────┤
973 │@{yellowf} │ @{yf} │ \033[33m │
974 ├──────────────┼─────────┼──────────┤
975 │@{bluef} │ @{bf} │ \033[34m │
976 ├──────────────┼─────────┼──────────┤
977 │@{purplef} │ @{pf} │ \033[35m │
978 ├──────────────┼─────────┼──────────┤
979 │@{cyanf} │ @{cf} │ \033[36m │
980 ├──────────────┼─────────┼──────────┤
981 │@{whitef} │ @{wf} │ \033[37m │
982 ├──────────────┼─────────┼──────────┤
983 │@{blackb} │ @{kb} │ \033[40m │
984 ├──────────────┼─────────┼──────────┤
985 │@{redb} │ @{rb} │ \033[41m │
986 ├──────────────┼─────────┼──────────┤
987 │@{greenb} │ @{gb} │ \033[42m │
988 ├──────────────┼─────────┼──────────┤
989 │@{yellowb} │ @{yb} │ \033[43m │
990 ├──────────────┼─────────┼──────────┤
991 │@{blueb} │ @{bb} │ \033[44m │
992 ├──────────────┼─────────┼──────────┤
993 │@{purpleb} │ @{pb} │ \033[45m │
994 ├──────────────┼─────────┼──────────┤
995 │@{cyanb} │ @{cb} │ \033[46m │
996 ├──────────────┼─────────┼──────────┤
997 │@{whiteb} │ @{wb} │ \033[47m │
998 ├──────────────┼─────────┼──────────┤
999 │@{escape} │ │ \033 │
1000 ├──────────────┼─────────┼──────────┤
1001 │@{reset} │ @| │ \033[0m │
1002 ├──────────────┼─────────┼──────────┤
1003 │@{boldon} │ @! │ \033[1m │
1004 ├──────────────┼─────────┼──────────┤
1005 │@{italicson} │ @/ │ \033[3m │
1006 ├──────────────┼─────────┼──────────┤
1007 │@{ulon} │ @_ │ \033[4m │
1008 ├──────────────┼─────────┼──────────┤
1009 │@{invon} │ │ \033[7m │
1010 ├──────────────┼─────────┼──────────┤
1011 │@{boldoff} │ │ \033[22m │
1012 ├──────────────┼─────────┼──────────┤
1013 │@{italicsoff} │ │ \033[23m │
1014 ├──────────────┼─────────┼──────────┤
1015 │@{uloff} │ │ \033[24m │
1016 ├──────────────┼─────────┼──────────┤
1017 │@{invoff} │ │ \033[27m │
1018 └──────────────┴─────────┴──────────┘
1019
1020 These substitution's values come from the ANSI color escape sequences,
1021 see: http://en.wikipedia.org/wiki/ANSI_escape_code
1022
1023 Also for any of the keys which have a trailing f, you can safely drop
1024 the trailing f and get the same thing.
1025
1026 For example, format_color("@{redf}") and format_color("@{red}") are
1027 functionally equivalent.
1028
1029 Also, many of the substitutions have shorten forms for convenience,
1030 such that @{redf}, @{rf}, @{red}, and @{r} are all the same.
1031
1032 Note that a trailing b is always required when specifying a background.
1033
1034 Some of the most common non-color sequences have {}'less versions.
1035
1036 For example, @{boldon}'s shorter form is @!.
1037
1038 By default, the substitutions (and calls to ansi()) resolve to escape
1039 sequences, but if you call disable_ansi_color_substitution_globally()
1040 then they will resolve to empty strings.
1041
1042 This allows you to always use the substitution strings and disable them
1043 globally when desired.
1044
1045 On Windows the substitutions are always resolved to empty strings as
1046 the ansi escape sequences do not work on Windows. Instead strings
1047 annotated with @{} style substitutions or raw \x1b[xxm style ansi
1048 escape sequences must be passed to print_color() in order for colors to
1049 be displayed on windows. Also the print_ansi_color_win32() function
1050 can be used on strings which only contain ansi escape sequences.
1051
1052 NOTE:
1053 There are existing Python modules like colorama which provide ansi
1054 colorization on multiple platforms, so a valid question is: "why
1055 write this module?". The reason for writing this module is to pro‐
1056 vide the color annotation of strings and functions for removing or
1057 replacing ansi escape sequences which are not provided by modules
1058 like colorama. This module could have depended on colorama for col‐
1059 orization on Windows, but colorama works by replacing the built-in
1060 sys.stdout and sys.stderr, which we did not want and it has extra
1061 functionality that we do not need. So, instead of depending on col‐
1062 orama, the Windows color printing code was used as the inspiration
1063 for the Windows color printing in the windows.py module in this ter‐
1064 minal_color package. The colorama license was placed in the header
1065 of that file and the colorama license is compatible with this pack‐
1066 age's license.
1067
1068 osrf_pycommon.terminal_color.ansi(key)
1069 Returns the escape sequence for a given ansi color key.
1070
1071 osrf_pycommon.terminal_color.disable_ansi_color_substitution_globally()
1072 Causes format_color() to replace color annotations with empty
1073 strings.
1074
1075 It also affects ansi().
1076
1077 This is not the case by default, so if you want to make all sub‐
1078 stitutions given to either function mentioned above return empty
1079 strings then call this function.
1080
1081 The default behavior can be restored by calling
1082 enable_ansi_color_substitution_globally().
1083
1084 osrf_pycommon.terminal_color.enable_ansi_color_substitution_globally()
1085 Causes format_color() to replace color annotations with ansi
1086 esacpe sequences.
1087
1088 It also affects ansi().
1089
1090 This is the case by default, so there is no need to call this
1091 everytime.
1092
1093 If you have previously caused all substitutions to evaluate to
1094 an empty string by calling
1095 disable_ansi_color_substitution_globally(), then you can restore
1096 the escape sequences for substitutions by calling this function.
1097
1098 osrf_pycommon.terminal_color.format_color(msg)
1099 Replaces color annotations with ansi escape sequences.
1100
1101 See this module's documentation for the list of available sub‐
1102 stitutions.
1103
1104 If disable_ansi_color_substitution_globally() has been called
1105 then all color annotations will be replaced by empty strings.
1106
1107 Also, on Windows all color annotations will be replaced with
1108 empty strings. If you want colorization on Windows, you must
1109 pass annotated strings to print_color().
1110
1111 Parameters
1112 msg (str) -- string message to be colorized
1113
1114 Returns
1115 colorized string
1116
1117 Return type
1118 str
1119
1120 osrf_pycommon.terminal_color.get_ansi_dict()
1121 Returns a copy of the dictionary of keys and ansi escape
1122 sequences.
1123
1124 osrf_pycommon.terminal_color.print_ansi_color_win32(*args, **kwargs)
1125 Prints color string containing ansi escape sequences to console
1126 in Windows.
1127
1128 If called on a non-Windows system, a NotImplementedError occurs.
1129
1130 Does not respect disable_ansi_color_substitution_globally().
1131
1132 Does not substitute color annotations like @{r} or @!, the
1133 string must already contain the \033[1m style ansi escape
1134 sequences.
1135
1136 Works by splitting each argument up by ansi escape sequence,
1137 printing the text between the sequences, and doing the corre‐
1138 sponding win32 action for each ansi sequence encountered.
1139
1140 osrf_pycommon.terminal_color.print_color(*args, **kwargs)
1141 Colorizes and prints with an implicit ansi reset at the end
1142
1143 Calls format_color() on each positional argument and then sends
1144 all positional and keyword arguments to print.
1145
1146 If the end keyword argument is not present then the default end
1147 value ansi('reset') + '\n' is used and passed to print.
1148
1149 os.linesep is used to determine the actual value for \n.
1150
1151 Therefore, if you use the end keyword argument be sure to
1152 include an ansi reset escape sequence if necessary.
1153
1154 On Windows the substituted arguments and keyword arguments are
1155 passed to print_ansi_color_win32() instead of just print.
1156
1157 osrf_pycommon.terminal_color.remove_ansi_escape_senquences(string)
1158 Removes any ansi escape sequences found in the given string and
1159 returns it.
1160
1161 osrf_pycommon.terminal_color.sanitize(msg)
1162 Sanitizes the given string to prevent format_color() from sub‐
1163 stituting content.
1164
1165 For example, when the string 'Email: {user}@{org}' is passed to
1166 format_color() the @{org} will be incorrectly recognized as a
1167 colorization annotation and it will fail to substitute with a
1168 KeyError: org.
1169
1170 In order to prevent this, you can first "sanitize" the string,
1171 add color annotations, and then pass the whole string to
1172 format_color().
1173
1174 If you give this function the string 'Email: {user}@{org}', then
1175 it will return 'Email: {{user}}@@{{org}}'. Then if you pass that
1176 to format_color() it will return 'Email: {user}@{org}'. In this
1177 way format_color() is the reverse of this function and so it is
1178 safe to call this function on any incoming data if it will even‐
1179 tually be passed to format_color().
1180
1181 In addition to expanding { => {{, } => }}, and @ => @@, this
1182 function will also replace any instances of @!, @/, @_, and @|
1183 with @{atexclamation}, @{atfwdslash}, @{atunderscore}, and
1184 @{atbar} respectively. And then there are corresponding keys in
1185 the ansi dict to convert them back.
1186
1187 For example, if you pass the string '|@ Notice @|' to this func‐
1188 tion it will return '|@@ Notice @{atbar}'. And since
1189 ansi('atbar') always returns @|, even when
1190 disable_ansi_color_substitution_globally() has been called, the
1191 result of passing that string to format_color() will be '|@
1192 Notice @|' again.
1193
1194 There are two main strategies for constructing strings which use
1195 both the Python str.format() function and the colorization anno‐
1196 tations.
1197
1198 One way is to just build each piece and concatenate the result:
1199
1200 print_color("@{r}", "{error}".format(error=error_str))
1201 # Or using print (remember to include an ansi reset)
1202 print(format_color("@{r}" + "{error}".format(error=error_str) + "@|"))
1203
1204 Another way is to use this function on the format string, con‐
1205 catenate to the annotations, pass the whole string to
1206 format_color(), and then format the whole thing:
1207
1208 print(format_color("@{r}" + sanitize("{error}") + "@|")
1209 .format(error=error_str))
1210
1211 However, the most common use for this function is to sanitize
1212 incoming strings which may have unknown content:
1213
1214 def my_func(user_content):
1215 print_color("@{y}" + sanitize(user_content))
1216
1217 This function is not intended to be used on strings with color
1218 annotations.
1219
1220 Parameters
1221 msg (str) -- string message to be sanitized
1222
1223 Returns
1224 sanitized string
1225
1226 Return type
1227 str
1228
1229 osrf_pycommon.terminal_color.split_by_ansi_escape_sequence(string,
1230 include_delimiters=False)
1231 Splits a string into a list using any ansi escape sequence as a
1232 delimiter.
1233
1234 Parameters
1235
1236 · string (str) -- string to be split
1237
1238 · include_delimiters (bool) -- If True include matched
1239 escape sequences in the list (default: False)
1240
1241 Returns
1242 list of strings, split from original string by escape
1243 sequences
1244
1245 Return type
1246 list
1247
1248 osrf_pycommon.terminal_color.test_colors(file=None)
1249 Prints a color testing block using print_color()
1250
1252 This module has a miscellaneous set of functions for working with ter‐
1253 minals.
1254
1255 You can use the get_terminal_dimensions() to get the width and height
1256 of the terminal as a tuple.
1257
1258 You can also use the is_tty() function to determine if a given object
1259 is a tty.
1260
1261 exception osrf_pycommon.terminal_utils.GetTerminalDimensionsError
1262 Raised when the terminal dimensions cannot be determined.
1263
1264 osrf_pycommon.terminal_utils.get_terminal_dimensions()
1265 Returns the width and height of the terminal.
1266
1267 Returns
1268 width and height in that order as a tuple
1269
1270 Return type
1271 tuple
1272
1273 Raises GetTerminalDimensionsError when the terminal dimensions
1274 cannot be determined
1275
1276 osrf_pycommon.terminal_utils.is_tty(stream)
1277 Returns True if the given stream is a tty, else False
1278
1279 Parameters
1280 stream -- object to be checked for being a tty
1281
1282 Returns
1283 True if the given object is a tty, otherwise False
1284
1285 Return type
1286 bool
1287
1289 Given that you have a copy of the source code, you can install
1290 osrf_pycommon like this:
1291
1292 $ python setup.py install
1293
1294 NOTE:
1295 If you are installing to a system Python you may need to use sudo.
1296
1297 If you do not want to install osrf_pycommon into your system Python, or
1298 you don’t have access to sudo, then you can use a virtualenv.
1299
1301 Because osrf_pycommon uses setuptools you can (and should) use the
1302 develop feature:
1303
1304 $ python setup.py develop
1305
1306 NOTE:
1307 If you are developing against the system Python, you may need sudo.
1308
1309 This will “install” osrf_pycommon to your Python path, but rather than
1310 copying the source files, it will instead place a marker file in the
1311 PYTHONPATH redirecting Python to your source directory. This allows
1312 you to use it as if it were installed but where changes to the source
1313 code take immediate affect.
1314
1315 When you are done with develop mode you can (and should) undo it like
1316 this:
1317
1318 $ python setup.py develop -u
1319
1320 NOTE:
1321 If you are developing against the system Python, you may need sudo.
1322
1323 That will “uninstall” the hooks into the PYTHONPATH which point to your
1324 source directory, but you should be wary that sometimes console scripts
1325 do not get removed from the bin folder.
1326
1328 In order to run the tests you will need to install nosetests, flake8,
1329 and Mock.
1330
1331 Once you have installed those, then run nosetest in the root of the
1332 osrf_pycommon source directory:
1333
1334 $ nosetests
1335
1337 In order to build the docs you will need to first install Sphinx.
1338
1339 You can build the documentation by invoking the Sphinx provided make
1340 target in the docs folder:
1341
1342 $ # In the docs folder
1343 $ make html
1344 $ open _build/html/index.html
1345
1346 Sometimes Sphinx does not pickup on changes to modules in packages
1347 which utilize the __all__ mechanism, so on repeat builds you may need
1348 to clean the docs first:
1349
1350 $ # In the docs folder
1351 $ make clean
1352 $ make html
1353 $ open _build/html/index.html
1354
1356 William Woodall
1357
1359 2014, Open Source Robotics Foundation
1360
1361
1362
1363
13640.0 Apr 12, 2019 OSRF_PYCOMMON(1)