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