| 1 | #vim: fileencoding=utf8 |
|---|
| 2 | """ tasktools |
|---|
| 3 | |
|---|
| 4 | ``tasktools`` is a collection of enhancement to Python ``distutils(setuptools)`` |
|---|
| 5 | that allow you to more easily create extra commands. |
|---|
| 6 | |
|---|
| 7 | """ |
|---|
| 8 | from __future__ import with_statement |
|---|
| 9 | import re, sys, imp, os |
|---|
| 10 | from os.path import join, abspath, dirname |
|---|
| 11 | from contextlib import contextmanager |
|---|
| 12 | try: |
|---|
| 13 | from setuptools import setup |
|---|
| 14 | def _clear_commands(): |
|---|
| 15 | from pkg_resources import working_set as ws |
|---|
| 16 | setattr(ws.by_key["setuptools"], "_ep_map", {}) |
|---|
| 17 | except ImportError,e: |
|---|
| 18 | from distutils.core import setup |
|---|
| 19 | def _clear_commands(): pass |
|---|
| 20 | from distutils.core import Command |
|---|
| 21 | import distutils.dist |
|---|
| 22 | from distutils.dist import Distribution |
|---|
| 23 | import distutils.command as scommands |
|---|
| 24 | |
|---|
| 25 | __version__ = "1.0.0" |
|---|
| 26 | __author__ = "Yusuke Inuzuka" |
|---|
| 27 | |
|---|
| 28 | class _Odict(dict): keys = lambda self: sorted(dict.keys(self)) |
|---|
| 29 | _va = dict(global_description=None, cmd_args={}, nssep="_", load_path=[]) |
|---|
| 30 | _cmds = _Odict() |
|---|
| 31 | _namespace = [] |
|---|
| 32 | _old_scommands = scommands.__all__ |
|---|
| 33 | |
|---|
| 34 | _get_ns = lambda:_namespace and _va["nssep"].join(_namespace)+_va["nssep"] or "" |
|---|
| 35 | def global_description(desc): _va["global_description"] = desc |
|---|
| 36 | def cmd_args(**v): _va["cmd_args"] = v |
|---|
| 37 | def load_path(*path): _va["load_path"] = _va["load_path"] + list(path) |
|---|
| 38 | def use_without_standard(): |
|---|
| 39 | if not use_without_standard.initialized: |
|---|
| 40 | _va["nssep"] = ":" |
|---|
| 41 | _clear_commands() |
|---|
| 42 | scommands.__all__ = [] |
|---|
| 43 | distutils.dist.command_re = re.compile("[a-zA-Z0-9_:]+") |
|---|
| 44 | Distribution.common_usage = "" |
|---|
| 45 | def new_print_command_list (self, commands, header, max_length): |
|---|
| 46 | if header == "Standard commands" and not scommands.__all__ : return |
|---|
| 47 | if "extra" in header.lower() : header = "Commands" |
|---|
| 48 | return self.old_print_command_list(commands, header, max_length) |
|---|
| 49 | setattr(Distribution, "old_print_command_list", getattr(Distribution, "print_command_list")) |
|---|
| 50 | setattr(Distribution, "print_command_list", new_print_command_list) |
|---|
| 51 | use_without_standard.initialized = True |
|---|
| 52 | use_without_standard.initialized = False |
|---|
| 53 | |
|---|
| 54 | |
|---|
| 55 | def run(): |
|---|
| 56 | if _va["load_path"]: |
|---|
| 57 | def tasks(path): |
|---|
| 58 | result = [] |
|---|
| 59 | for root, dirs, files in os.walk(path): |
|---|
| 60 | map(result.append, (join(root, i) for i in files if i == "tasks.py")) |
|---|
| 61 | return result |
|---|
| 62 | |
|---|
| 63 | idf = "tasktools_dynamic_task%d" |
|---|
| 64 | for pa in _va["load_path"]: |
|---|
| 65 | for i, path in enumerate((abspath(i) for i in tasks(pa))): |
|---|
| 66 | imp.load_source(idf%i, path) |
|---|
| 67 | |
|---|
| 68 | if use_without_standard.initialized and _va["global_description"]: |
|---|
| 69 | _arg = sys.argv[1] if len(sys.argv) > 1 else "" |
|---|
| 70 | if _arg in ["--help-commands", "--help", "-h"] and _va["global_description"]: |
|---|
| 71 | print "\n".join([u"#"*60, _va["global_description"], u"#"*60]) |
|---|
| 72 | _va["cmd_args"].update({"cmdclass":_cmds}) |
|---|
| 73 | setup(**_va["cmd_args"]) |
|---|
| 74 | |
|---|
| 75 | class _CommandType(type): |
|---|
| 76 | def __new__(cls, class_name, class_bases, classdict): |
|---|
| 77 | d = dict(user_options=[], finalize_options=lambda s:None) |
|---|
| 78 | d.update(classdict) |
|---|
| 79 | def _(self): |
|---|
| 80 | [setattr(self,i[0].rstrip("="),None) for i in d["user_options"]] |
|---|
| 81 | d["initialize_options"] = _ |
|---|
| 82 | d["boolean_options"] = [i for i,j,k in d["user_options"] if not i.endswith("=")] |
|---|
| 83 | def _(self): |
|---|
| 84 | map(self.run_command, self.get_sub_commands()) |
|---|
| 85 | return classdict["run"](self) |
|---|
| 86 | d["run"] = _ |
|---|
| 87 | name = _get_ns()+class_name.lower() |
|---|
| 88 | cls = type.__new__(cls, name, class_bases + (object,), d) |
|---|
| 89 | cls.description = cls.__doc__ and cls.__doc__.strip() or "" |
|---|
| 90 | if "sub_commands" in d: |
|---|
| 91 | cls.description += "\n\tsub commands:\n"+"\n".join("\t\t"+i for i,j in d["sub_commands"]) |
|---|
| 92 | if name in _old_scommands and name not in scommands.__all__: scommands.__all__.append(name) |
|---|
| 93 | if class_name != "Task" : _cmds[name] = cls |
|---|
| 94 | return cls |
|---|
| 95 | class Task(Command): __metaclass__ = _CommandType |
|---|
| 96 | |
|---|
| 97 | @contextmanager |
|---|
| 98 | def namespace(name): |
|---|
| 99 | _namespace.append(name) |
|---|
| 100 | yield _get_ns() |
|---|
| 101 | _namespace.pop() |
|---|
| 102 | |
|---|
| 103 | __all__ = filter(lambda i: not i.startswith("_"), locals()) |
|---|