Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed bugs.

More usable
Updated tests
  • Loading branch information...
commit 502e1e747c0919e342ca1820fb00a65c01fadd28 1 parent 26a6206
@Lispython authored
View
3  .gitignore
@@ -21,4 +21,5 @@ pip-log.txt
*.pid
*.egg-info*
dist/*
-_build/*
+_build/*
+virtualenv*
View
2  Makefile
@@ -25,7 +25,7 @@ clean-pyc:
find . -name '*~' -exec rm -f {} +
clean: clean-pyc
- find . -name '*.egg' -exec rm -f {} +
+ find . -name '*.egg' -exec rm -rf {} +
find . -name '*.egg-info' -exec rm -rf {} +
find-print:
View
25 buildenv.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+CURRENT_PATH=$(pwd)
+
+py(){
+
+ curl https://raw.github.com/pypa/virtualenv/master/virtualenv.py > ./virtualenv.py
+
+ if [ ! -d "./venv/" ]; then
+ echo "Virtualenv not exists, creating..."
+ python ./virtualenv.py --python=python2.7 --clear venv
+ else
+ echo "Virtualenv exists"
+ fi
+ . ./venv/bin/activate
+ ./venv/bin/easy_install pip
+ #./venv/bin/pip install -U -r ./req.txt
+ python ./virtualenv.py --relocatable venv
+ ./venv/bin/pip install -U ipdb
+}
+
+
+case $1 in
+ *) py;;
+esac
View
137 commandor/base.py
@@ -14,14 +14,29 @@
import sys
import os.path
-from optparse import Option, OptionParser
+from optparse import Option, OptionParser as BaseOptionParser
-from commandor.exceptions import InvalidCommand
+from commandor.exceptions import (InvalidCommand,
+ InvalidScriptOption, InvalidCommandOption)
from commandor.colors import blue, red
from commandor.utils import indent, parse_args
-__all__ = 'Command', 'Commandor'
+__all__ = 'Command', 'Commandor', 'OptionParser'
+
+class OptionParser(BaseOptionParser):
+ def exit(self, status=0, msg=None):
+ sys.exit(status=0)
+
+ def format_help(self, formatter=None):
+ if formatter is None:
+ formatter = self.formatter
+ result = []
+ if self.description:
+ result.append(self.format_description(formatter) + "\n")
+ result.append(self.format_option_help(formatter))
+ result.append(self.format_epilog(formatter))
+ return "".join(result)
class Mixin(object):
@@ -39,20 +54,23 @@ def lookup_command(cls, name):
return cls.commands[name]
@staticmethod
- def exit():
+ def exit(status=0):
"""Exit
"""
- print("Exit from loop")
- sys.exit(0)
+ sys.exit(status)
@staticmethod
- def display(s):
+ def display(s, c=None):
"""Display input string `s`
"""
- print(s)
+ if c:
+ # Display colorized
+ print(c(s))
+ else:
+ print(s)
def error(self, s):
- self.display(red(s))
+ self.display(s, red)
def abort(self, s):
"""Display and exit
@@ -78,6 +96,10 @@ def __new__(mcs, name, bases, params):
cls.name = name.lower()
cls.parent.add_command(cls)
cls.level = cls.parent.level + 1
+ if cls.parent.tree:
+ cls.tree = cls.parent.tree + [cls.parent]
+ else:
+ cls.tree = [cls.parent]
else:
cls.name = name.lower()
@@ -109,7 +131,11 @@ class Commandor(Mixin):
default_options = [Option('-L', '--list-commands',
action='store_true',
default=False,
- help='Show commands')]
+ help='Show commands'),
+ Option('-h', '--help',
+ action='store_true',
+ default=False,
+ help='Show help for script')]
def __init__(self, parser, args=sys.argv[1:], options=[]):
"""Initialize commandor
@@ -144,9 +170,12 @@ def parse_args(self, args=[]):
:param args: script args, exclude commands and commands args
:return: tuple of options and empty args
"""
- return self.parser.parse_args(args or self._args)
+ try:
+ return self.parser.parse_args(args)
+ except SystemExit, e:
+ raise InvalidScriptOption(e)
- def run(self, options, args):
+ def run(self, options, args, **kwargs):
"""Execute
:param options: commandor options
@@ -157,39 +186,45 @@ def run(self, options, args):
if options.list_commands:
self.parser.print_help()
self.show_commands(args)
+ # System exit
- if isinstance(args, (list, tuple)) and\
- not any([arg for arg in args if not arg.startswith('-')]):
- self.parser.print_help()
+ ## if isinstance(args, (list, tuple)) and \
+ ## not any([arg for arg in args if not arg.startswith('-')]):
+ ## self.parser.print_help()
- return False
+ return True
def process(self):
"""Process parsing
"""
- args, commands_args = parse_args(self._args)
+
+ # Configure self.parser options
+ self.add_parser_options()
+
+ commandor_args, commands = parse_args(self._args)
self._curdir = os.path.abspath(os.path.curdir)
- self.add_parser_options()
- self._parsed_options, _ = self.parse_args(args)
+ # Commandor parsed options and args (that empty list)
+ # Because all args separated into commands_args
+ self._parsed_options, _ = self.parse_args(commandor_args)
- res = self.run(self._parsed_options, commands_args)
+ res = self.run(self._parsed_options, commands)
if not res:
return res
- command, args = self.__class__.find_command(commands_args)
-
+ command, command_args = self.__class__.find_command(commands)
if issubclass(command, Commandor):
+ # if subcommands not specified, show help and exit
self.parser.print_help()
- self.show_commands(args)
+ self.show_commands(commands)
self.exit()
else:
command_instance = command(cur_dir=self._curdir,
- args=args, commandor_res=res)
+ args=command_args, commandor_res=res)
return command_instance.process()
@classmethod
@@ -217,7 +252,7 @@ def show_commands(self, args):
command = self
if args:
command, names = self.__class__.find_command(args)
- self.display("Subcommands list for {}".format('.'.join(names)))
+ self.display("Subcommands list for {0}".format('.'.join(names)))
self.exit()
self.display("\nCommands list:")
@@ -246,10 +281,16 @@ class Command(Mixin):
__metaclass__ = CommandMetaClass
+ tree = []
level = 0
parent = None
name = 'command'
options = []
+ default_options = [
+ Option(None, '--help',
+ action='store_true',
+ default=False,
+ help='Show help for command')]
help = None
@@ -278,30 +319,50 @@ def register_options(self):
"""Add specifed command options
to Options Group
"""
- for option in self.options:
+ for option in self.default_options + self.options:
self.register_option(option)
@classmethod
- def print_commands(cls, args):
+ def print_commands(cls, args, base_ident=None):
"""Display command commands
:param options: options dict
:param args: script arguments
"""
for name, command in cls.commands.items():
- cls.print_command(name, args)
+ cls.print_command(name, args, base_ident)
@classmethod
- def print_command(cls, name, args=[]):
+ def print_command(cls, name, args=[], base_ident=None):
"""Pretty print command
:param name: command name
"""
command = cls.lookup_command(name)
- command.show(args)
+ command.show(args, base_ident)
+
+
+ def print_command_help(self):
+ """Print command help and exit
+ """
+ self.display(self.__class__.usage())
+ self.display(self.parser.format_help())
+
+ # Display subcommands if exists
+ if self.commands:
+ self.display("Subcommands list for {0}".format(self.name))
+ self.__class__.print_commands([], base_ident=1)
@classmethod
- def show(cls, args=[]):
+ def usage(cls):
+ """Print command usage
+ """
+ return u"Usage: {0} [options] {1}\n".format(
+ sys.argv[0], ' '.join([x.name for x in cls.tree] + [cls.name]))
+
+
+ @classmethod
+ def show(cls, args=[], base_ident=None):
"""Show command repr
:param cls: cls object
@@ -311,10 +372,10 @@ def show(cls, args=[]):
# Self.Display cls doc
cls.display(indent("`{0}`: {1}".format(
blue(cls.name), cls.help or cls.__doc__.strip()),
- (cls.level + 1) * 4))
+ (cls.level + 1 if base_ident is None else base_ident) * 4))
if cls.commands:
- cls.print_commands(args)
+ cls.print_commands(args, base_ident if base_ident is None else base_ident + 1 )
@classmethod
def add_command(cls, command):
@@ -337,7 +398,10 @@ def parse_args(self):
"""Parse arguments
:return: tuple of options and args
"""
- return self.parser.parse_args(self._args)
+ try:
+ return self.parser.parse_args(self._args)
+ except SystemExit, e:
+ raise InvalidCommandOption(e)
def process(self):
"""Execute command
@@ -347,6 +411,11 @@ def process(self):
options, args = self.parse_args()
d = dict([(x.dest, getattr(options, x.dest, None))
for x in self.parser.option_list])
+ if options.help:
+ self.print_command_help()
+ self.exit()
+ del d["help"]
+
return self.run(**d)
def run(self, *args, **kwargs):
View
8 commandor/exceptions.py
@@ -14,3 +14,11 @@
class InvalidCommand(Exception):
"""Invalid console command
"""
+
+class InvalidScriptOption(Exception):
+ """Invalid script option
+ """
+
+class InvalidCommandOption(Exception):
+ """InvalidCommandOption
+ """
View
48 examples/main.py
@@ -11,7 +11,8 @@
:github: http://github.com/Lispython/commandor
"""
-from optparse import OptionParser
+from optparse import OptionParser, Option
+
from commandor import Command, Commandor
@@ -40,6 +41,26 @@ class ServerStart(Command):
"""
parent = Server
+ options = [
+ Option("-P", "--port",
+ metavar="int",
+ default=8891,
+ help="Port to run http server"),
+ Option("-r", "--reload",
+ action="store_true",
+ dest="reload",
+ default=False,
+ help="Auto realod source on changes"),
+ Option("-H", "--host",
+ metavar="str",
+ default="127.0.0.1",
+ help="Port for server"),
+ Option("-l", "--logging",
+ metavar="str",
+ default="none",
+ help="Log level")]
+
+
class ServerStop(Command):
"""Stop given server
@@ -73,12 +94,35 @@ class ClusterInfo(Command):
"""
parent = Cluster
+class RootLevel(Command):
+ """Root level command
+ """
+ commandor = CustomCommandor
+
+class Level1(Command):
+ """Level1 command
+ """
+ parent = RootLevel
+
+class Level2(Command):
+ """Level2 command
+ """
+ parent = Level1
+
+class Level3(Command):
+ """Level3
+ """
+ parent = Level2
+
+class Level4(Command):
+ """Level4
+ """
+ parent = Level3
def main():
"""Main execution loop
"""
- import ipdb; ipdb.set_trace()
parser = OptionParser(
usage="%prog [options] <commands>",
add_help_option=False)
View
14 tests/core.py
@@ -15,6 +15,7 @@
from base import BaseTestCase
from commandor.base import Commandor, Command
from commandor.utils import parse_args
+from commandor.exceptions import InvalidScriptOption
__all__ = 'CoreTestCase',
@@ -26,9 +27,14 @@ class CoreTestCase(BaseTestCase):
def test_parse_args(self):
args = "--config=./config.py server start --verbose --processes=8".split()
-
self.assertEquals(parse_args(args), (args[:1], args[1:]))
+ args = "server start --verbose --processes=8".split()
+ self.assertEquals(parse_args(args), ([], args))
+
+ args = "server --type=http start --verbose --processes=8".split()
+ self.assertEquals(parse_args(args), ([], args))
+
def test_command(self):
test_self = self
command_options = [Option("-v", "--verbose",
@@ -143,12 +149,16 @@ def run(self, verbose, processes):
help='Commandor configuration file')]
commandor_args = "--config=./config.py server start --verbose --processes=10".split()
-
commandor = Commandor1(parser, args=commandor_args, options=commandor_options)
+ self.assertEquals(parse_args(commandor._args), (commandor_args[:1], commandor_args[1:]))
+
commandor.add_parser_options()
parsed_options, parsed_args = commandor.parse_args(["--config=./config.py"])
+ self.assertRaises(InvalidScriptOption, commandor.parse_args, ["--dddd=true"])
+
+
self.assertEquals(parsed_options.config, "./config.py")
self.assertEquals(parsed_args, [])
Please sign in to comment.
Something went wrong with that request. Please try again.