run_py_script.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Hans-Martin von Gaudecker, 2012
  4. """
  5. Run a Python script in the directory specified by **ctx.bldnode**.
  6. Select a Python version by specifying the **version** keyword for
  7. the task generator instance as integer 2 or 3. Default is 3.
  8. If the build environment has an attribute "PROJECT_PATHS" with
  9. a key "PROJECT_ROOT", its value will be appended to the PYTHONPATH.
  10. Same a string passed to the optional **add_to_pythonpath**
  11. keyword (appended after the PROJECT_ROOT).
  12. Usage::
  13. ctx(features='run_py_script', version=3,
  14. source='some_script.py',
  15. target=['some_table.tex', 'some_figure.eps'],
  16. deps='some_data.csv',
  17. add_to_pythonpath='src/some/library')
  18. """
  19. import os, re
  20. from waflib import Task, TaskGen, Logs
  21. def configure(conf):
  22. """TODO: Might need to be updated for Windows once
  23. "PEP 397":http://www.python.org/dev/peps/pep-0397/ is settled.
  24. """
  25. conf.find_program('python', var='PY2CMD', mandatory=False)
  26. conf.find_program('python3', var='PY3CMD', mandatory=False)
  27. if not conf.env.PY2CMD and not conf.env.PY3CMD:
  28. conf.fatal("No Python interpreter found!")
  29. class run_py_2_script(Task.Task):
  30. """Run a Python 2 script."""
  31. run_str = '${PY2CMD} ${SRC[0].abspath()}'
  32. shell=True
  33. class run_py_3_script(Task.Task):
  34. """Run a Python 3 script."""
  35. run_str = '${PY3CMD} ${SRC[0].abspath()}'
  36. shell=True
  37. @TaskGen.feature('run_py_script')
  38. @TaskGen.before_method('process_source')
  39. def apply_run_py_script(tg):
  40. """Task generator for running either Python 2 or Python 3 on a single
  41. script.
  42. Attributes:
  43. * source -- A **single** source node or string. (required)
  44. * target -- A single target or list of targets (nodes or strings)
  45. * deps -- A single dependency or list of dependencies (nodes or strings)
  46. * add_to_pythonpath -- A string that will be appended to the PYTHONPATH environment variable
  47. If the build environment has an attribute "PROJECT_PATHS" with
  48. a key "PROJECT_ROOT", its value will be appended to the PYTHONPATH.
  49. """
  50. # Set the Python version to use, default to 3.
  51. v = getattr(tg, 'version', 3)
  52. if v not in (2, 3):
  53. raise ValueError("Specify the 'version' attribute for run_py_script task generator as integer 2 or 3.\n Got: %s" %v)
  54. # Convert sources and targets to nodes
  55. src_node = tg.path.find_resource(tg.source)
  56. tgt_nodes = [tg.path.find_or_declare(t) for t in tg.to_list(tg.target)]
  57. # Create the task.
  58. tsk = tg.create_task('run_py_%d_script' %v, src=src_node, tgt=tgt_nodes)
  59. # custom execution environment
  60. # TODO use a list and os.sep.join(lst) at the end instead of concatenating strings
  61. tsk.env.env = dict(os.environ)
  62. tsk.env.env['PYTHONPATH'] = tsk.env.env.get('PYTHONPATH', '')
  63. project_paths = getattr(tsk.env, 'PROJECT_PATHS', None)
  64. if project_paths and 'PROJECT_ROOT' in project_paths:
  65. tsk.env.env['PYTHONPATH'] += os.pathsep + project_paths['PROJECT_ROOT'].abspath()
  66. if getattr(tg, 'add_to_pythonpath', None):
  67. tsk.env.env['PYTHONPATH'] += os.pathsep + tg.add_to_pythonpath
  68. # Clean up the PYTHONPATH -- replace double occurrences of path separator
  69. tsk.env.env['PYTHONPATH'] = re.sub(os.pathsep + '+', os.pathsep, tsk.env.env['PYTHONPATH'])
  70. # Clean up the PYTHONPATH -- doesn't like starting with path separator
  71. if tsk.env.env['PYTHONPATH'].startswith(os.pathsep):
  72. tsk.env.env['PYTHONPATH'] = tsk.env.env['PYTHONPATH'][1:]
  73. # dependencies (if the attribute 'deps' changes, trigger a recompilation)
  74. for x in tg.to_list(getattr(tg, 'deps', [])):
  75. node = tg.path.find_resource(x)
  76. if not node:
  77. tg.bld.fatal('Could not find dependency %r for running %r' % (x, src_node.abspath()))
  78. tsk.dep_nodes.append(node)
  79. Logs.debug('deps: found dependencies %r for running %r', tsk.dep_nodes, src_node.abspath())
  80. # Bypass the execution of process_source by setting the source to an empty list
  81. tg.source = []