1OSRF_PYCOMMON(1)                 osrf_pycommon                OSRF_PYCOMMON(1)
2
3
4

NAME

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

THE CLI_UTILS MODULE

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

THE PROCESS_UTILS MODULE

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_sequences()
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

THE TERMINAL_COLOR MODULE

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.remove_ansi_escape_sequences(string)
1162              Removes any ansi escape sequences found in the given string  and
1163              returns it.
1164
1165       osrf_pycommon.terminal_color.sanitize(msg)
1166              Sanitizes  the  given string to prevent format_color() from sub‐
1167              stituting content.
1168
1169              For example, when the string 'Email: {user}@{org}' is passed  to
1170              format_color()  the  @{org}  will be incorrectly recognized as a
1171              colorization annotation and it will fail to  substitute  with  a
1172              KeyError: org.
1173
1174              In  order  to prevent this, you can first “sanitize” the string,
1175              add color  annotations,  and  then  pass  the  whole  string  to
1176              format_color().
1177
1178              If you give this function the string 'Email: {user}@{org}', then
1179              it will return 'Email: {{user}}@@{{org}}'. Then if you pass that
1180              to format_color() it will return 'Email: {user}@{org}'.  In this
1181              way format_color() is the reverse of this function and so it  is
1182              safe to call this function on any incoming data if it will even‐
1183              tually be passed to format_color().
1184
1185              In addition to expanding { => {{, } => }}, and  @  =>  @@,  this
1186              function  will  also replace any instances of @!, @/, @_, and @|
1187              with  @{atexclamation},  @{atfwdslash},   @{atunderscore},   and
1188              @{atbar} respectively.  And then there are corresponding keys in
1189              the ansi dict to convert them back.
1190
1191              For example, if you pass the string '|@ Notice @|' to this func‐
1192              tion   it   will   return  '|@@  Notice  @{atbar}'.   And  since
1193              ansi('atbar')     always     returns     @|,      even      when
1194              disable_ansi_color_substitution_globally()  has been called, the
1195              result of passing that string  to  format_color()  will  be  '|@
1196              Notice @|' again.
1197
1198              There are two main strategies for constructing strings which use
1199              both the Python str.format() function and the colorization anno‐
1200              tations.
1201
1202              One way is to just build each piece and concatenate the result:
1203
1204                 print_color("@{r}", "{error}".format(error=error_str))
1205                 # Or using print (remember to include an ansi reset)
1206                 print(format_color("@{r}" + "{error}".format(error=error_str) + "@|"))
1207
1208              Another  way  is to use this function on the format string, con‐
1209              catenate  to  the  annotations,  pass  the   whole   string   to
1210              format_color(), and then format the whole thing:
1211
1212                 print(format_color("@{r}" + sanitize("{error}") + "@|")
1213                       .format(error=error_str))
1214
1215              However,  the  most  common use for this function is to sanitize
1216              incoming strings which may have unknown content:
1217
1218                 def my_func(user_content):
1219                     print_color("@{y}" + sanitize(user_content))
1220
1221              This function is not intended to be used on strings  with  color
1222              annotations.
1223
1224              Parameters
1225                     msg (str) – string message to be sanitized
1226
1227              Returns
1228                     sanitized string
1229
1230              Return type
1231                     str
1232
1233       osrf_pycommon.terminal_color.split_by_ansi_escape_sequence(string,
1234       include_delimiters=False)
1235              Splits a string into a list using any ansi escape sequence as  a
1236              delimiter.
1237
1238              Parameters
1239
1240                     · string (str) – string to be split
1241
1242                     · include_delimiters  (bool)  –  If  True include matched
1243                       escape sequences in the list (default: False)
1244
1245              Returns
1246                     list of strings, split from  original  string  by  escape
1247                     sequences
1248
1249              Return type
1250                     list
1251
1252       osrf_pycommon.terminal_color.test_colors(file=None)
1253              Prints a color testing block using print_color()
1254

THE TERMINAL_UTILS MODULE

1256       This  module has a miscellaneous set of functions for working with ter‐
1257       minals.
1258
1259       You can use the get_terminal_dimensions() to get the width  and  height
1260       of the terminal as a tuple.
1261
1262       You  can  also use the is_tty() function to determine if a given object
1263       is a tty.
1264
1265       exception osrf_pycommon.terminal_utils.GetTerminalDimensionsError
1266              Raised when the terminal dimensions cannot be determined.
1267
1268       osrf_pycommon.terminal_utils.get_terminal_dimensions()
1269              Returns the width and height of the terminal.
1270
1271              Returns
1272                     width and height in that order as a tuple
1273
1274              Return type
1275                     tuple
1276
1277              Raises GetTerminalDimensionsError when the  terminal  dimensions
1278                     cannot be determined
1279
1280       osrf_pycommon.terminal_utils.is_tty(stream)
1281              Returns True if the given stream is a tty, else False
1282
1283              Parameters
1284                     stream – object to be checked for being a tty
1285
1286              Returns
1287                     True if the given object is a tty, otherwise False
1288
1289              Return type
1290                     bool
1291

INSTALLING FROM SOURCE

1293       Given  that  you  have  a  copy  of  the  source  code, you can install
1294       osrf_pycommon like this:
1295
1296          $ python setup.py install
1297
1298       NOTE:
1299          If you are installing to a system Python you may need to use sudo.
1300
1301       If you do not want to install osrf_pycommon into your system Python, or
1302       you don’t have access to sudo, then you can use a virtualenv.
1303

HACKING

1305       Because  osrf_pycommon  uses  setuptools  you  can (and should) use the
1306       develop feature:
1307
1308          $ python setup.py develop
1309
1310       NOTE:
1311          If you are developing against the system Python, you may need sudo.
1312
1313       This will “install” osrf_pycommon to your Python path, but rather  than
1314       copying  the  source  files, it will instead place a marker file in the
1315       PYTHONPATH redirecting Python to your source  directory.   This  allows
1316       you  to  use it as if it were installed but where changes to the source
1317       code take immediate affect.
1318
1319       When you are done with develop mode you can (and should) undo  it  like
1320       this:
1321
1322          $ python setup.py develop -u
1323
1324       NOTE:
1325          If you are developing against the system Python, you may need sudo.
1326
1327       That will “uninstall” the hooks into the PYTHONPATH which point to your
1328       source directory, but you should be wary that sometimes console scripts
1329       do not get removed from the bin folder.
1330

TESTING

1332       In  order  to run the tests you will need to install nosetests, flake8,
1333       and Mock.
1334
1335       Once you have installed those, then run nosetest in  the  root  of  the
1336       osrf_pycommon source directory:
1337
1338          $ nosetests
1339

BUILDING THE DOCUMENTATION

1341       In order to build the docs you will need to first install Sphinx.
1342
1343       You  can  build  the documentation by invoking the Sphinx provided make
1344       target in the docs folder:
1345
1346          $ # In the docs folder
1347          $ make html
1348          $ open _build/html/index.html
1349
1350       Sometimes Sphinx does not pickup on  changes  to  modules  in  packages
1351       which  utilize  the __all__ mechanism, so on repeat builds you may need
1352       to clean the docs first:
1353
1354          $ # In the docs folder
1355          $ make clean
1356          $ make html
1357          $ open _build/html/index.html
1358

AUTHOR

1360       William Woodall
1361
1363       2020, Open Source Robotics Foundation
1364
1365
1366
1367
13680.0                              Jan 30, 2020                 OSRF_PYCOMMON(1)
Impressum