Source code for metatools.entrypoints.build
import datetime
import os
import sys
from subprocess import call
import yaml
build_time = datetime.datetime.now()
absolute_self = os.path.abspath(__file__)
local_tools = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
[docs]def dedent(docstring):
if not docstring:
return ''
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = sys.maxint
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < sys.maxint:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Return a single string:
return '\n'.join(trimmed)
if __name__ == '__main__':
if len(sys.argv) < 3:
print 'usage: %s entrypoint.yml bin_dir [names]' % sys.argv[0]
exit(1)
entrypoints = yaml.load(open(sys.argv[1]).read())
bin_dir = sys.argv[2]
names = sys.argv[3:] or entrypoints.keys()
if not os.path.exists(bin_dir):
print '%r does not exist; aborting' % bin_dir
exit(2)
for name in sorted(names):
print name
spec = entrypoints.get(name)
if not spec:
print '%r is not specified; aborting' % name
exit(3)
# Strings are a shortcut for only providing an entrypoint.
if isinstance(spec, basestring):
spec = {'entrypoint': spec}
# From this point on only work with dicts.
if not isinstance(spec, dict):
print 'entrypoint specification must be a dict; got %r' % spec
continue
# Set some sensible defaults.
spec.setdefault('type', 'python')
spec.setdefault('interpreter', spec['type'])
spec.setdefault('environ', {})
spec.setdefault('development_preamble', True)
# Python specific defaults.
spec.setdefault('kwargs', {})
spec.setdefault('args', ())
spec.setdefault('module', None)
spec.setdefault('function', None)
# Bash specific defaults.
spec.setdefault('command', None)
# Parse a Python string entrypoint.
entrypoint = spec.pop('entrypoint', None)
if entrypoint:
parts = entrypoint.split(':')
if len(parts) > 2:
print 'entrypoint must be "package.module" or "package.module:function"; got %r; aborting %s' % (entrypoint, name)
continue
spec['module'] = parts[0]
spec['function'] = parts[1] if len(parts) > 1 else None
# Make sure it is a type that we understand.
if spec['type'] not in ('python', 'bash'):
print 'unknown entrypoint type %r; aborting %s' % (spec['type'], name)
continue
# Make sure Python entrypoints have a module.
if spec['type'] == 'python' and not spec['module']:
print 'python entrypoint must have a module; aborting %s' % (name, )
continue
# Make sure bash entrypoints have a command.
if spec['type'] == 'bash' and not spec['command']:
print 'bash entrypoint must have a command; aborting %s' % (name, )
# This will be concatenated into the final source which will be written.
source = []
# Shebang.
source.append('#!/usr/bin/env %(interpreter)s\n' % spec)
# Metadata.
source.append(dedent('''
# This file was automatically generated by
# %(absolute_self)s
# at %(build_time)s.
''' % globals()))
# Type specific initialization.
if spec['type'] == 'python':
source.append(dedent('''
import os
import sys
'''))
# Development pre-amble; hook into the environment from which the tool
# was called.
if spec['development_preamble']:
if spec['type'] == 'python':
source.append(dedent('''
# Bootstrap the dev environment.
local_tools = %(local_tools)r
if local_tools not in sys.path:
# Add to path to potentially include sitecustomize.
sys.path.append(local_tools)
# Add as site-package.
import sitecustomize
sitecustomize.add_site_dir(local_tools)
# Prepend to sites list for child processes.
os.environ['KS_PYTHON_SITES'] = local_tools + ':' + os.environ['KS_PYTHON_SITES']
''' % globals()))
if spec['environ']:
if spec['type'] == 'python':
source.append('os.environ.update({\n')
for x in sorted(spec['environ'].iteritems()):
source.append(' %r: %r,\n' % x)
source.append('})\n\n')
elif spec['type'] == 'bash':
for k, v in sorted(spec['environ'].iteritems()):
source.append('export %s=\'%s\'\n' % (k, v.replace('\'', '\'\\\'\'')))
# Call the python code.
if spec['type'] == 'python':
source.append('import %(module)s\n' % spec)
if spec['function']:
signature = []
signature.extend(repr(x) for x in spec['args'])
signature.extend('%s=%r' % x for x in sorted(spec['kwargs'].iteritems()))
spec.setdefault('signature', ', '.join(signature))
source.append('%(module)s.%(function)s(%(signature)s)\n' % spec)
elif spec['type'] == 'bash':
source.append('exec %(command)s $@')
# Write the file.
with open(os.path.join(bin_dir, name), 'w') as fh:
fh.write(''.join(source))
call(['chmod', '+x', os.path.join(bin_dir, name)])