-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1eb0913
Showing
13 changed files
with
2,095 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.zip | ||
.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License | ||
|
||
Copyright (c) <2009> Oliver Tonnhofer <olt@omniscale.de> | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from scriptine.path import path | ||
from scriptine.command import parse_and_run_commands, global_options | ||
|
||
__version__ = '0.0.6' | ||
|
||
__all__ = ['path', 'parse_and_run_commands'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
import sys | ||
import types | ||
import inspect | ||
import re | ||
import optparse | ||
from textwrap import wrap | ||
|
||
from scriptine import misc | ||
|
||
_global_options = None | ||
|
||
def global_options(): | ||
global _global_options | ||
return _global_options | ||
|
||
def parse_and_run_function(function, args=None, command_name=None, | ||
global_options=None, add_dry_run_option=True): | ||
#TODO refactor me, I'm too long | ||
if args is None: | ||
args = sys.argv | ||
|
||
required_args, optional_args = inspect_args(function) | ||
|
||
func_doc = function.__doc__ or '' | ||
params_doc = parse_rst_params(func_doc) | ||
|
||
usage = 'usage: %prog ' | ||
if command_name is not None: | ||
usage += command_name + ' ' | ||
usage += '[options] ' + ' '.join(required_args) | ||
|
||
if func_doc: | ||
usage += '\n\n' + '\n'.join(wrap(func_doc.strip().split('\n')[0], 60)) | ||
|
||
if set(required_args).intersection(params_doc.keys()): | ||
usage += '\n\nRequired arguments:' | ||
for arg in required_args: | ||
usage += '\n%s' % arg | ||
if arg in params_doc: | ||
usage += ': %s' % params_doc[arg] | ||
|
||
add_help_option = True | ||
if getattr(function, 'no_help', False): | ||
add_help_option = False | ||
|
||
fetch_all = None | ||
if hasattr(function, 'fetch_all'): | ||
fetch_all = function.fetch_all | ||
optional_args = [(arg, default) for arg, default in optional_args | ||
if arg != fetch_all] | ||
|
||
parser = optparse.OptionParser | ||
if getattr(function, 'non_strict', False): | ||
parser = NonStrictOptionParser | ||
|
||
parser = parser(usage, add_help_option=add_help_option) | ||
|
||
for arg_name, default in optional_args: | ||
options = {} | ||
if isinstance(default, bool): | ||
if default: | ||
options = {'action': 'store_false'} | ||
else: | ||
options = {'action': 'store_true'} | ||
elif isinstance(default, int): | ||
options = {'type': 'int'} | ||
elif isinstance(default, float): | ||
options = {'type': 'float'} | ||
parser.add_option('--' + arg_name.replace('_', '-'), | ||
help=params_doc.get(arg_name, None), | ||
dest=arg_name, default=default, metavar=default, **options) | ||
|
||
if add_dry_run_option: | ||
parser.add_option('--dry-run', '-n', dest='dry_run', default=False, | ||
action='store_true', help='don\'t actually do anything') | ||
|
||
if global_options: | ||
if '--help' in args or '-h' in args: | ||
group = optparse.OptionGroup(parser, 'Global options') | ||
group.add_options(global_options) | ||
parser.add_option_group(group) | ||
else: | ||
args = parse_global_options(args, global_options) | ||
|
||
(options, args) = parser.parse_args(args) | ||
|
||
if add_dry_run_option and options.dry_run: | ||
misc.options.dry = True | ||
|
||
args = args[1:] | ||
if len(args) < len(required_args): | ||
parser.error('number of arguments does not match') | ||
kw = {} | ||
for arg_name, _default in optional_args: | ||
kw[arg_name] = getattr(options, arg_name) | ||
|
||
if fetch_all: | ||
kw[fetch_all] = args[len(required_args):] | ||
return function(*args[:len(required_args)], **kw) | ||
|
||
def no_help(cmd): | ||
cmd.no_help = True | ||
return cmd | ||
|
||
def non_strict(cmd): | ||
cmd.non_strict = True | ||
return cmd | ||
|
||
def fetch_all(arg_name): | ||
def _fetch_all(cmd): | ||
cmd.fetch_all = arg_name | ||
return cmd | ||
return _fetch_all | ||
|
||
def parse_global_options(args, global_options): | ||
parser = NonStrictOptionParser(add_help_option=False) | ||
parser.add_options(global_options) | ||
(options, args) = parser.parse_args(args) | ||
global _global_options | ||
_global_options = options | ||
return args | ||
|
||
class NonStrictOptionParser(optparse.OptionParser): | ||
def _process_args(self, largs, rargs, values): | ||
while rargs: | ||
arg = rargs[0] | ||
# We handle bare "--" explicitly, and bare "-" is handled by the | ||
# standard arg handler since the short arg case ensures that the | ||
# len of the opt string is greater than 1. | ||
try: | ||
if arg == "--": | ||
del rargs[0] | ||
return | ||
elif arg[0:2] == "--": | ||
# process a single long option (possibly with value(s)) | ||
self._process_long_opt(rargs, values) | ||
elif arg[:1] == "-" and len(arg) > 1: | ||
# process a cluster of short options (possibly with | ||
# value(s) for the last one only) | ||
self._process_short_opts(rargs, values) | ||
elif self.allow_interspersed_args: | ||
largs.append(arg) | ||
del rargs[0] | ||
else: | ||
return | ||
except optparse.BadOptionError: | ||
largs.append(arg) | ||
|
||
def inspect_args(function): | ||
(args, _varargs, _varkw, defaults) = inspect.getargspec(function) | ||
|
||
optional_args = [] | ||
if defaults is not None: | ||
for default in defaults[::-1]: | ||
optional_args.append((args.pop(), default)) | ||
optional_args.reverse() | ||
return args, optional_args | ||
|
||
def parse_and_run_commands(namespace=None, args=None, global_options=None, | ||
add_dry_run_option=True, command_suffix='_command'): | ||
if namespace is None: | ||
namespace = sys._getframe(1).f_locals | ||
elif type(namespace) is types.ModuleType: | ||
namespace = namespace.__dict__ | ||
|
||
if args is None: | ||
args = sys.argv | ||
|
||
if len(args) < 2 or args[1] in ('-h', '--help'): | ||
print_help(namespace, command_suffix, global_options) | ||
return | ||
|
||
command_name = args.pop(1).replace('-', '_') | ||
function = namespace[command_name + command_suffix] | ||
parse_and_run_function(function, args, command_name, global_options, | ||
add_dry_run_option=add_dry_run_option) | ||
|
||
run = parse_and_run_commands | ||
|
||
def print_help(namespace, command_suffix, global_options): | ||
commands = [] | ||
for func_name, func in namespace.iteritems(): | ||
if func_name.endswith(command_suffix): | ||
command_name = func_name[:-len(command_suffix)].replace('_', '-') | ||
commands.append((command_name, func.__doc__)) | ||
|
||
if not commands: | ||
print 'no commands found in', sys.argv[0] | ||
return | ||
|
||
usage = 'usage: %prog command [options]' | ||
parser = optparse.OptionParser(usage) | ||
if global_options: | ||
parser.add_options(global_options) | ||
parser.print_help() | ||
|
||
print '\nCommands:' | ||
cmd_len = max(len(cmd) for cmd, _ in commands) | ||
for cmd, doc in commands: | ||
if doc is not None: | ||
doc = doc.strip().split('\n')[0] | ||
else: | ||
doc = '' | ||
print (' %-' + str(cmd_len) + 's %s') % (cmd, doc) | ||
|
||
def parse_rst_params(doc): | ||
""" | ||
Parse a reStructuredText docstring and return a dictionary | ||
with parameter names and descriptions. | ||
>>> doc = ''' | ||
... :param foo: foo parameter | ||
... foo parameter | ||
... | ||
... :param bar: bar parameter | ||
... :param baz: baz parameter | ||
... baz parameter | ||
... baz parameter | ||
... Some text. | ||
... ''' | ||
>>> params = parse_rst_params(doc) | ||
>>> params['foo'] | ||
'foo parameter foo parameter' | ||
>>> params['bar'] | ||
'bar parameter' | ||
>>> params['baz'] | ||
'baz parameter baz parameter baz parameter' | ||
""" | ||
param_re = re.compile(r"""^([ \t]*):param\ | ||
(?P<param>\w+):\ | ||
(?P<body>.*\n(\1[ \t]+\w.*\n)*)""", | ||
re.MULTILINE|re.VERBOSE) | ||
params = {} | ||
for match in param_re.finditer(doc): | ||
parts = match.groupdict() | ||
body_lines = parts['body'].strip().split('\n') | ||
params[parts['param']] = ' '.join(s.strip() for s in body_lines) | ||
|
||
return params |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from scriptine import path, log | ||
import tarfile | ||
|
||
class file_collection(object): | ||
def __init__(self): | ||
self.files = [] | ||
self.base = path('.') | ||
|
||
def include(self, patterns, recursive=False): | ||
if isinstance(patterns, basestring): patterns = (patterns,) | ||
for pattern in patterns: | ||
if recursive: | ||
for dirname in self.base.dirs(pattern): | ||
self.files.extend((f for f in dirname.walk())) | ||
else: | ||
self.files.extend((f for f in self.base.listdir(pattern))) | ||
|
||
def exclude(self, patterns): | ||
if isinstance(patterns, basestring): patterns = (patterns,) | ||
for pattern in patterns: | ||
self.files = [f for f in self.files if not f.fnmatch(pattern)] | ||
|
||
def __iter__(self): | ||
return iter(self.files) | ||
|
||
def tar(self, dest, archive_base=None): | ||
dest = path(dest) | ||
if archive_base is None: | ||
archive_base = path(self.dest.basename()).splitext()[0] | ||
tar = tarfile.open(dest, 'w:gz') | ||
|
||
for f in self: | ||
log.info('adding %s', f) | ||
tar.add(f, arcname=archive_base/f, recursive=False) | ||
tar.close() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import sys | ||
|
||
__all__ = ['warn', 'error', 'debug', 'info'] | ||
|
||
L_DEBUG = -1 | ||
L_INFO = 0 | ||
L_MARK = 1 | ||
L_WARN = 2 | ||
L_ERROR = 3 | ||
|
||
_level = 0 | ||
|
||
def inc_log_level(): | ||
global _level | ||
_level -= 1 | ||
|
||
def dec_log_level(): | ||
global _level | ||
_level += 1 | ||
|
||
def log(msg, *args): | ||
print msg % args | ||
sys.stdout.flush() | ||
|
||
def mark(msg, *args): | ||
if _level <= L_MARK: | ||
log('---> ' + msg, *args) | ||
|
||
def info(msg, *args): | ||
if _level <= L_INFO: | ||
log('INFO: ' + msg, *args) | ||
|
||
def warn(msg, *args): | ||
if _level <= L_WARN: | ||
log('WARN: ' + msg, *args) | ||
|
||
def error(msg, *args): | ||
if _level <= L_ERROR: | ||
log('ERROR: ' + msg, *args) | ||
|
||
def fatal(msg, *args): | ||
log('ERROR: ' + msg, *args) | ||
log('aborting script...') | ||
sys.exit(1) | ||
|
||
|
||
def debug(msg, *args): | ||
if _level <= L_DEBUG: | ||
log('DEBUG: ' + msg, *args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import scriptine | ||
from scriptine import path | ||
|
||
def version_command(): | ||
print 'scriptine ver.%s' % scriptine.__version__ | ||
|
||
def zipdist_command(): | ||
print 'creating scriptine.zip' | ||
if __file__ is None: | ||
print 'ERROR: creating zipdist from zipped scriptine is not supported.' | ||
return 1 | ||
from zipfile import ZipFile, ZIP_DEFLATED | ||
zipfile = ZipFile('scriptine.zip', 'w', compression=ZIP_DEFLATED) | ||
scripyt_src = path(__file__).dirname() | ||
for filename in path(scripyt_src).files('*.py'): | ||
arcname = path().joinpath(*filename.splitall()[-2:]) | ||
zipfile.write(filename, arcname) | ||
zipfile.close() | ||
|
||
if __name__ == '__main__': | ||
import sys | ||
if sys.argv[0] is None: | ||
sys.argv[0] = 'python -m scriptine.meta' | ||
scriptine.parse_and_run_commands(globals()) |
Oops, something went wrong.