Realigned width of text meant for humans to 72 characters
This commit is contained in:
parent
ef05eb75d9
commit
c272fce312
10 changed files with 257 additions and 229 deletions
|
@ -14,7 +14,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The command system providing scalable, clean and convenient architecture in
|
The command system providing scalable, clean and convenient architecture
|
||||||
combination with declarative way of defining commands and a fair amount of
|
in combination with declarative way of defining commands and a fair
|
||||||
automatization for routine processes.
|
amount of automatization for routine processes.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The backbone of the command system. Provides automatic dispatching which does
|
The backbone of the command system. Provides automatic dispatching which
|
||||||
not require explicit registering commands or containers and remains active even
|
does not require explicit registering commands or containers and remains
|
||||||
after everything is done, so new commands can be added during the runtime.
|
active even after everything is done, so new commands can be added
|
||||||
|
during the runtime.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from types import NoneType
|
from types import NoneType
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
|
|
||||||
class BaseError(Exception):
|
class BaseError(Exception):
|
||||||
"""
|
"""
|
||||||
Common base for errors which relate to a specific command. Encapsulates
|
Common base for errors which relate to a specific command.
|
||||||
everything needed to identify a command, by either its object or name.
|
Encapsulates everything needed to identify a command, by either its
|
||||||
|
object or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, message, command=None, name=None):
|
def __init__(self, message, command=None, name=None):
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Provides a tiny framework with simple, yet powerful and extensible architecture
|
Provides a tiny framework with simple, yet powerful and extensible
|
||||||
to implement commands in a streight and flexible, declarative way.
|
architecture to implement commands in a streight and flexible,
|
||||||
|
declarative way.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -28,42 +29,43 @@ from errors import DefinitionError, CommandError
|
||||||
|
|
||||||
class CommandHost(object):
|
class CommandHost(object):
|
||||||
"""
|
"""
|
||||||
Command host is a hub between numerous command processors and command
|
Command host is a hub between numerous command processors and
|
||||||
containers. Aimed to participate in a dispatching process in order to
|
command containers. Aimed to participate in a dispatching process in
|
||||||
provide clean and transparent architecture.
|
order to provide clean and transparent architecture.
|
||||||
"""
|
"""
|
||||||
__metaclass__ = HostDispatcher
|
__metaclass__ = HostDispatcher
|
||||||
|
|
||||||
class CommandContainer(object):
|
class CommandContainer(object):
|
||||||
"""
|
"""
|
||||||
Command container is an entity which holds defined commands, allowing them
|
Command container is an entity which holds defined commands,
|
||||||
to be dispatched and proccessed correctly. Each command container may be
|
allowing them to be dispatched and proccessed correctly. Each
|
||||||
bound to a one or more command hosts.
|
command container may be bound to a one or more command hosts.
|
||||||
|
|
||||||
Bounding is controlled by the HOSTS variable, which must be defined in the
|
Bounding is controlled by the HOSTS variable, which must be defined
|
||||||
body of the command container. This variable should contain a list of hosts
|
in the body of the command container. This variable should contain a
|
||||||
to bound to, as a tuple or list.
|
list of hosts to bound to, as a tuple or list.
|
||||||
"""
|
"""
|
||||||
__metaclass__ = ContainerDispatcher
|
__metaclass__ = ContainerDispatcher
|
||||||
|
|
||||||
class CommandProcessor(object):
|
class CommandProcessor(object):
|
||||||
"""
|
"""
|
||||||
Command processor is an immediate command emitter. It does not participate
|
Command processor is an immediate command emitter. It does not
|
||||||
in the dispatching process directly, but must define a host to bound to.
|
participate in the dispatching process directly, but must define a
|
||||||
|
host to bound to.
|
||||||
|
|
||||||
Bounding is controlled by the COMMAND_HOST variable, which must be defined
|
Bounding is controlled by the COMMAND_HOST variable, which must be
|
||||||
in the body of the command processor. This variable should be set to a
|
defined in the body of the command processor. This variable should
|
||||||
specific command host.
|
be set to a specific command host.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This defines a command prefix (or an initializer), which should preceede a
|
# This defines a command prefix (or an initializer), which should
|
||||||
# a text in order it to be processed as a command.
|
# preceede a a text in order it to be processed as a command.
|
||||||
COMMAND_PREFIX = '/'
|
COMMAND_PREFIX = '/'
|
||||||
|
|
||||||
def process_as_command(self, text):
|
def process_as_command(self, text):
|
||||||
"""
|
"""
|
||||||
Try to process text as a command. Returns True if it has been processed
|
Try to process text as a command. Returns True if it has been
|
||||||
as a command and False otherwise.
|
processed as a command and False otherwise.
|
||||||
"""
|
"""
|
||||||
prefix = text.startswith(self.COMMAND_PREFIX)
|
prefix = text.startswith(self.COMMAND_PREFIX)
|
||||||
length = len(text) > len(self.COMMAND_PREFIX)
|
length = len(text) > len(self.COMMAND_PREFIX)
|
||||||
|
@ -97,28 +99,28 @@ class CommandProcessor(object):
|
||||||
|
|
||||||
def command_preprocessor(self, command, name, arguments, args, kwargs):
|
def command_preprocessor(self, command, name, arguments, args, kwargs):
|
||||||
"""
|
"""
|
||||||
Redefine this method in the subclass to execute custom code before
|
Redefine this method in the subclass to execute custom code
|
||||||
command gets executed.
|
before command gets executed.
|
||||||
|
|
||||||
If returns True then command execution will be interrupted and command
|
If returns True then command execution will be interrupted and
|
||||||
will not be executed.
|
command will not be executed.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def command_postprocessor(self, command, name, arguments, args, kwargs, value):
|
def command_postprocessor(self, command, name, arguments, args, kwargs, value):
|
||||||
"""
|
"""
|
||||||
Redefine this method in the subclass to execute custom code after
|
Redefine this method in the subclass to execute custom code
|
||||||
command gets executed.
|
after command gets executed.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def looks_like_command(self, text, body, name, arguments):
|
def looks_like_command(self, text, body, name, arguments):
|
||||||
"""
|
"""
|
||||||
This hook is being called before any processing, but after it was
|
This hook is being called before any processing, but after it
|
||||||
determined that text looks like a command.
|
was determined that text looks like a command.
|
||||||
|
|
||||||
If returns value other then None - then further processing will be
|
If returns value other then None - then further processing will
|
||||||
interrupted and that value will be used to return from
|
be interrupted and that value will be used to return from
|
||||||
process_as_command.
|
process_as_command.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
@ -136,8 +138,9 @@ class CommandProcessor(object):
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
|
|
||||||
# These two regular expression patterns control how command documentation
|
# These two regular expression patterns control how command
|
||||||
# will be formatted to be transformed to a normal, readable state.
|
# documentation will be formatted to be transformed to a normal,
|
||||||
|
# readable state.
|
||||||
DOC_STRIP_PATTERN = re.compile(r'(?:^[ \t]+|\A\n)', re.MULTILINE)
|
DOC_STRIP_PATTERN = re.compile(r'(?:^[ \t]+|\A\n)', re.MULTILINE)
|
||||||
DOC_FORMAT_PATTERN = re.compile(r'(?<!\n)\n(?!\n)', re.MULTILINE)
|
DOC_FORMAT_PATTERN = re.compile(r'(?<!\n)\n(?!\n)', re.MULTILINE)
|
||||||
|
|
||||||
|
@ -145,8 +148,8 @@ class Command(object):
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
self.names = names
|
self.names = names
|
||||||
|
|
||||||
# Automatically set all the properties passed to a constructor by the
|
# Automatically set all the properties passed to a constructor
|
||||||
# command decorator.
|
# by the command decorator.
|
||||||
for key, value in properties.iteritems():
|
for key, value in properties.iteritems():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
@ -154,18 +157,20 @@ class Command(object):
|
||||||
try:
|
try:
|
||||||
return self.handler(*args, **kwargs)
|
return self.handler(*args, **kwargs)
|
||||||
|
|
||||||
# This allows to use a shortcuted way of raising an exception inside a
|
# This allows to use a shortcuted way of raising an exception
|
||||||
# handler. That is to raise a CommandError without command or name
|
# inside a handler. That is to raise a CommandError without
|
||||||
# attributes set. They will be set to a corresponding values right here
|
# command or name attributes set. They will be set to a
|
||||||
# in case if they was not set by the one who raised an exception.
|
# corresponding values right here in case if they was not set by
|
||||||
|
# the one who raised an exception.
|
||||||
except CommandError, error:
|
except CommandError, error:
|
||||||
if not error.command and not error.name:
|
if not error.command and not error.name:
|
||||||
raise CommandError(error.message, self)
|
raise CommandError(error.message, self)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# This one is a little bit too wide, but as Python does not have
|
# This one is a little bit too wide, but as Python does not have
|
||||||
# anything more constrained - there is no other choice. Take a look here
|
# anything more constrained - there is no other choice. Take a
|
||||||
# if command complains about invalid arguments while they are ok.
|
# look here if command complains about invalid arguments while
|
||||||
|
# they are ok.
|
||||||
except TypeError:
|
except TypeError:
|
||||||
raise CommandError("Command received invalid arguments", self)
|
raise CommandError("Command received invalid arguments", self)
|
||||||
|
|
||||||
|
@ -185,8 +190,8 @@ class Command(object):
|
||||||
|
|
||||||
def extract_documentation(self):
|
def extract_documentation(self):
|
||||||
"""
|
"""
|
||||||
Extract handler's documentation which is a doc-string and transform it
|
Extract handler's documentation which is a doc-string and
|
||||||
to a usable format.
|
transform it to a usable format.
|
||||||
|
|
||||||
Transformation is done based on the DOC_STRIP_PATTERN and
|
Transformation is done based on the DOC_STRIP_PATTERN and
|
||||||
DOC_FORMAT_PATTERN regular expression patterns.
|
DOC_FORMAT_PATTERN regular expression patterns.
|
||||||
|
@ -211,19 +216,19 @@ class Command(object):
|
||||||
|
|
||||||
def extract_specification(self):
|
def extract_specification(self):
|
||||||
"""
|
"""
|
||||||
Extract handler's arguments specification, as it was defined preserving
|
Extract handler's arguments specification, as it was defined
|
||||||
their order.
|
preserving their order.
|
||||||
"""
|
"""
|
||||||
names, var_args, var_kwargs, defaults = getargspec(self.handler)
|
names, var_args, var_kwargs, defaults = getargspec(self.handler)
|
||||||
|
|
||||||
# Behavior of this code need to be checked. Might yield incorrect
|
# Behavior of this code need to be checked. Might yield
|
||||||
# results on some rare occasions.
|
# incorrect results on some rare occasions.
|
||||||
spec_args = names[:-len(defaults) if defaults else len(names)]
|
spec_args = names[:-len(defaults) if defaults else len(names)]
|
||||||
spec_kwargs = list(zip(names[-len(defaults):], defaults)) if defaults else {}
|
spec_kwargs = list(zip(names[-len(defaults):], defaults)) if defaults else {}
|
||||||
|
|
||||||
# Removing self from arguments specification. Command handler should
|
# Removing self from arguments specification. Command handler
|
||||||
# receive the processors as a first argument, which should be self by
|
# should receive the processors as a first argument, which
|
||||||
# the canonical means.
|
# should be self by the canonical means.
|
||||||
if spec_args.pop(0) != 'self':
|
if spec_args.pop(0) != 'self':
|
||||||
raise DefinitionError("First argument must be self", self)
|
raise DefinitionError("First argument must be self", self)
|
||||||
|
|
||||||
|
@ -231,44 +236,47 @@ class Command(object):
|
||||||
|
|
||||||
def command(*names, **properties):
|
def command(*names, **properties):
|
||||||
"""
|
"""
|
||||||
A decorator for defining commands in a declarative way. Provides facilities
|
A decorator for defining commands in a declarative way. Provides
|
||||||
for setting command's names and properties.
|
facilities for setting command's names and properties.
|
||||||
|
|
||||||
Names should contain a set of names (aliases) by which the command can be
|
Names should contain a set of names (aliases) by which the command
|
||||||
reached. If no names are given - the the native name (the one extracted from
|
can be reached. If no names are given - the the native name (the one
|
||||||
the command handler) will be used.
|
extracted from the command handler) will be used.
|
||||||
|
|
||||||
If include_native=True is given (default) and names is non-empty - then the
|
If include_native=True is given (default) and names is non-empty -
|
||||||
native name of the command will be prepended in addition to the given names.
|
then the native name of the command will be prepended in addition to
|
||||||
|
the given names.
|
||||||
|
|
||||||
If usage=True is given (default) - then command help will be appended with
|
If usage=True is given (default) - then command help will be
|
||||||
autogenerated usage info, based of the command handler arguments
|
appended with autogenerated usage info, based of the command handler
|
||||||
introspection.
|
arguments introspection.
|
||||||
|
|
||||||
If source=True is given - then the first argument of the command will
|
If source=True is given - then the first argument of the command
|
||||||
receive the source arguments, as a raw, unprocessed string. The further
|
will receive the source arguments, as a raw, unprocessed string. The
|
||||||
mapping of arguments and options will not be affected.
|
further mapping of arguments and options will not be affected.
|
||||||
|
|
||||||
If raw=True is given - then command considered to be raw and should define
|
If raw=True is given - then command considered to be raw and should
|
||||||
positional arguments only. If it defines only one positional argument - this
|
define positional arguments only. If it defines only one positional
|
||||||
argument will receive all the raw and unprocessed arguments. If the command
|
argument - this argument will receive all the raw and unprocessed
|
||||||
defines more then one positional argument - then all the arguments except
|
arguments. If the command defines more then one positional argument
|
||||||
the last one will be processed normally; the last argument will get what is
|
- then all the arguments except the last one will be processed
|
||||||
left after the processing as raw and unprocessed string.
|
normally; the last argument will get what is left after the
|
||||||
|
processing as raw and unprocessed string.
|
||||||
|
|
||||||
If empty=True is given - this will allow to call a raw command without
|
If empty=True is given - this will allow to call a raw command
|
||||||
arguments.
|
without arguments.
|
||||||
|
|
||||||
If extra=True is given - then all the extra arguments passed to a command
|
If extra=True is given - then all the extra arguments passed to a
|
||||||
will be collected into a sequence and given to the last positional argument.
|
command will be collected into a sequence and given to the last
|
||||||
|
positional argument.
|
||||||
|
|
||||||
If overlap=True is given - then all the extra arguments will be mapped as if
|
If overlap=True is given - then all the extra arguments will be
|
||||||
they were values for the keyword arguments.
|
mapped as if they were values for the keyword arguments.
|
||||||
|
|
||||||
If expand_short=True is given (default) - then short, one-letter options
|
If expand_short=True is given (default) - then short, one-letter
|
||||||
will be expanded to a verbose ones, based of the comparison of the first
|
options will be expanded to a verbose ones, based of the comparison
|
||||||
letter. If more then one option with the same first letter is given - then
|
of the first letter. If more then one option with the same first
|
||||||
only first one will be used in the expansion.
|
letter is given - then only first one will be used in the expansion.
|
||||||
"""
|
"""
|
||||||
names = list(names)
|
names = list(names)
|
||||||
|
|
||||||
|
@ -300,22 +308,23 @@ def command(*names, **properties):
|
||||||
|
|
||||||
def decorator(handler):
|
def decorator(handler):
|
||||||
"""
|
"""
|
||||||
Decorator which receives handler as a first argument and then wraps it
|
Decorator which receives handler as a first argument and then
|
||||||
in the command which then returns back.
|
wraps it in the command which then returns back.
|
||||||
"""
|
"""
|
||||||
command = Command(handler, *names, **properties)
|
command = Command(handler, *names, **properties)
|
||||||
|
|
||||||
# Extract and inject a native name if either no other names are
|
# Extract and inject a native name if either no other names are
|
||||||
# specified or include_native property is enabled, while making sure it
|
# specified or include_native property is enabled, while making
|
||||||
# is going to be the first one in the list.
|
# sure it is going to be the first one in the list.
|
||||||
if not names or include_native:
|
if not names or include_native:
|
||||||
names.insert(0, command.native_name)
|
names.insert(0, command.native_name)
|
||||||
command.names = tuple(names)
|
command.names = tuple(names)
|
||||||
|
|
||||||
return command
|
return command
|
||||||
|
|
||||||
# Workaround if we are getting called without parameters. Keep in mind that
|
# Workaround if we are getting called without parameters. Keep in
|
||||||
# in that case - first item in the names will be the handler.
|
# mind that in that case - first item in the names will be the
|
||||||
|
# handler.
|
||||||
if names and isinstance(names[0], FunctionType):
|
if names and isinstance(names[0], FunctionType):
|
||||||
return decorator(names.pop(0))
|
return decorator(names.pop(0))
|
||||||
|
|
||||||
|
@ -323,11 +332,8 @@ def command(*names, **properties):
|
||||||
|
|
||||||
def documentation(text):
|
def documentation(text):
|
||||||
"""
|
"""
|
||||||
This decorator is used to bind a documentation (a help) to a command.
|
This decorator is used to bind a documentation (a help) to a
|
||||||
|
command.
|
||||||
Though this can be done easily by using doc-strings in a declarative and
|
|
||||||
Pythonic way - some of Gajim's developers are against it because of the
|
|
||||||
scaffolding needed to support the tranlation of such documentation.
|
|
||||||
"""
|
"""
|
||||||
def decorator(target):
|
def decorator(target):
|
||||||
if isinstance(target, Command):
|
if isinstance(target, Command):
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The implementation and auxilary systems which implement the standard Gajim
|
The implementation and auxilary systems which implement the standard
|
||||||
commands and also provide an infrastructure for adding custom commands.
|
Gajim commands and also provide an infrastructure for adding custom
|
||||||
|
commands.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The module contains examples of how to create your own commands, by creating a
|
The module contains examples of how to create your own commands, by
|
||||||
new command container and definding a set of commands.
|
creating a new command container and definding a set of commands.
|
||||||
|
|
||||||
Keep in mind that this module is not being loaded, so the code will not be
|
Keep in mind that this module is not being loaded, so the code will not
|
||||||
executed and commands defined here will not be detected.
|
be executed and commands defined here will not be detected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..framework import CommandContainer, command, documentation
|
from ..framework import CommandContainer, command, documentation
|
||||||
|
@ -27,8 +27,9 @@ from hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||||
class CustomCommonCommands(CommandContainer):
|
class CustomCommonCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container bounds to all three available in the default
|
This command container bounds to all three available in the default
|
||||||
implementation command hosts. This means that commands defined in this
|
implementation command hosts. This means that commands defined in
|
||||||
container will be available to all - chat, private chat and a group chat.
|
this container will be available to all - chat, private chat and a
|
||||||
|
group chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
||||||
|
@ -39,12 +40,13 @@ class CustomCommonCommands(CommandContainer):
|
||||||
First line of the doc string is called a description and will be
|
First line of the doc string is called a description and will be
|
||||||
programmatically extracted and formatted.
|
programmatically extracted and formatted.
|
||||||
|
|
||||||
After that you can give more help, like explanation of the options. This
|
After that you can give more help, like explanation of the
|
||||||
one will be programatically extracted and formatted too.
|
options. This one will be programatically extracted and
|
||||||
|
formatted too.
|
||||||
|
|
||||||
After all the documentation - there will be autogenerated (based on the
|
After all the documentation - there will be autogenerated (based
|
||||||
method signature) usage information appended. You can turn it off
|
on the method signature) usage information appended. You can
|
||||||
though, if you want.
|
turn it off though, if you want.
|
||||||
"""
|
"""
|
||||||
return "I can't dance, you stupid fuck, I'm just a command system! A cool one, though..."
|
return "I can't dance, you stupid fuck, I'm just a command system! A cool one, though..."
|
||||||
|
|
||||||
|
@ -63,8 +65,9 @@ class CustomChatCommands(CommandContainer):
|
||||||
|
|
||||||
class CustomPrivateChatCommands(CommandContainer):
|
class CustomPrivateChatCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container bounds only to the PrivateChatCommands command host.
|
This command container bounds only to the PrivateChatCommands
|
||||||
Therefore command defined here will be available only to a private chat.
|
command host. Therefore command defined here will be available only
|
||||||
|
to a private chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (PrivateChatCommands,)
|
HOSTS = (PrivateChatCommands,)
|
||||||
|
@ -75,8 +78,9 @@ class CustomPrivateChatCommands(CommandContainer):
|
||||||
|
|
||||||
class CustomGroupChatCommands(CommandContainer):
|
class CustomGroupChatCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container bounds only to the GroupChatCommands command host.
|
This command container bounds only to the GroupChatCommands command
|
||||||
Therefore command defined here will be available only to a group chat.
|
host. Therefore command defined here will be available only to a
|
||||||
|
group chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (GroupChatCommands,)
|
HOSTS = (GroupChatCommands,)
|
||||||
|
|
|
@ -14,29 +14,29 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The module defines a set of command hosts, which are bound to a different
|
The module defines a set of command hosts, which are bound to a
|
||||||
command processors, which are the source of commands.
|
different command processors, which are the source of commands.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..framework import CommandHost
|
from ..framework import CommandHost
|
||||||
|
|
||||||
class ChatCommands(CommandHost):
|
class ChatCommands(CommandHost):
|
||||||
"""
|
"""
|
||||||
This command host is bound to the command processor which processes commands
|
This command host is bound to the command processor which processes
|
||||||
from a chat.
|
commands from a chat.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class PrivateChatCommands(CommandHost):
|
class PrivateChatCommands(CommandHost):
|
||||||
"""
|
"""
|
||||||
This command host is bound to the command processor which processes commands
|
This command host is bound to the command processor which processes
|
||||||
from a private chat.
|
commands from a private chat.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class GroupChatCommands(CommandHost):
|
class GroupChatCommands(CommandHost):
|
||||||
"""
|
"""
|
||||||
This command host is bound to the command processor which processes commands
|
This command host is bound to the command processor which processes
|
||||||
from a group chat.
|
commands from a group chat.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Provides a glue to tie command system framework and the actual code where it
|
Provides a glue to tie command system framework and the actual code
|
||||||
would be dropped in. Defines a little bit of scaffolding to support interaction
|
where it would be dropped in. Defines a little bit of scaffolding to
|
||||||
between the two and a few utility methods so you don't need to dig up the code
|
support interaction between the two and a few utility methods so you
|
||||||
itself code to write basic commands.
|
don't need to dig up the code itself code to write basic commands.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from types import StringTypes
|
from types import StringTypes
|
||||||
|
@ -30,8 +30,8 @@ from ..errors import CommandError
|
||||||
|
|
||||||
class ChatCommandProcessor(CommandProcessor):
|
class ChatCommandProcessor(CommandProcessor):
|
||||||
"""
|
"""
|
||||||
A basic scaffolding to provide convenient interaction between the command
|
A basic scaffolding to provide convenient interaction between the
|
||||||
system and chat controls.
|
command system and chat controls.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def process_as_command(self, text):
|
def process_as_command(self, text):
|
||||||
|
@ -76,8 +76,8 @@ class ChatCommandProcessor(CommandProcessor):
|
||||||
|
|
||||||
class CommandTools:
|
class CommandTools:
|
||||||
"""
|
"""
|
||||||
Contains a set of basic tools and shortcuts you can use in your commands to
|
Contains a set of basic tools and shortcuts you can use in your
|
||||||
performe some simple operations.
|
commands to performe some simple operations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def echo(self, text, kind='info'):
|
def echo(self, text, kind='info'):
|
||||||
|
@ -107,8 +107,8 @@ class CommandTools:
|
||||||
|
|
||||||
def add_history(self, text):
|
def add_history(self, text):
|
||||||
"""
|
"""
|
||||||
Add given text to the input history, so user can scroll through it using
|
Add given text to the input history, so user can scroll through
|
||||||
ctrl + up/down arrow keys.
|
it using ctrl + up/down arrow keys.
|
||||||
"""
|
"""
|
||||||
self.save_sent_message(text)
|
self.save_sent_message(text)
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ lc = Constants()
|
||||||
|
|
||||||
class StandardCommonCommands(CommandContainer):
|
class StandardCommonCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container contains standard commands which are common to all -
|
This command container contains standard commands which are common
|
||||||
chat, private chat, group chat.
|
to all - chat, private chat, group chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
||||||
|
@ -159,7 +159,8 @@ class StandardCommonCommands(CommandContainer):
|
||||||
|
|
||||||
class StandardChatCommands(CommandContainer):
|
class StandardChatCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container contains standard command which are unique to a chat.
|
This command container contains standard command which are unique to
|
||||||
|
a chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (ChatCommands,)
|
HOSTS = (ChatCommands,)
|
||||||
|
@ -211,16 +212,16 @@ class StandardChatCommands(CommandContainer):
|
||||||
|
|
||||||
class StandardPrivateChatCommands(CommandContainer):
|
class StandardPrivateChatCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container contains standard command which are unique to a
|
This command container contains standard command which are unique to
|
||||||
private chat.
|
a private chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (PrivateChatCommands,)
|
HOSTS = (PrivateChatCommands,)
|
||||||
|
|
||||||
class StandardGroupchatCommands(CommandContainer):
|
class StandardGroupchatCommands(CommandContainer):
|
||||||
"""
|
"""
|
||||||
This command container contains standard command which are unique to a group
|
This command container contains standard command which are unique to
|
||||||
chat.
|
a group chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HOSTS = (GroupChatCommands,)
|
HOSTS = (GroupChatCommands,)
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The module contains routines to parse command arguments and map them to the
|
The module contains routines to parse command arguments and map them to
|
||||||
command handler's positonal and keyword arguments.
|
the command handler's positonal and keyword arguments.
|
||||||
|
|
||||||
Mapping is done in two stages: 1) parse arguments into positional arguments and
|
Mapping is done in two stages: 1) parse arguments into positional
|
||||||
options; 2) adapt them to the specific command handler according to the command
|
arguments and options; 2) adapt them to the specific command handler
|
||||||
properties.
|
according to the command properties.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -34,28 +34,32 @@ from errors import DefinitionError, CommandError
|
||||||
ARG_PATTERN = re.compile(r'(\'|")?(?P<body>(?(1).+?|\S+))(?(1)\1)')
|
ARG_PATTERN = re.compile(r'(\'|")?(?P<body>(?(1).+?|\S+))(?(1)\1)')
|
||||||
OPT_PATTERN = re.compile(r'(?<!\w)--?(?P<key>[\w-]+)(?:(?:=|\s)(\'|")?(?P<value>(?(2)[^-]+?|[^-\s]+))(?(2)\2))?')
|
OPT_PATTERN = re.compile(r'(?<!\w)--?(?P<key>[\w-]+)(?:(?:=|\s)(\'|")?(?P<value>(?(2)[^-]+?|[^-\s]+))(?(2)\2))?')
|
||||||
|
|
||||||
# Option keys needs to be encoded to a specific encoding as Python does not
|
# Option keys needs to be encoded to a specific encoding as Python does
|
||||||
# allow to expand dictionary with raw unicode strings as keys from a **kwargs.
|
# not allow to expand dictionary with raw unicode strings as keys from a
|
||||||
|
# **kwargs.
|
||||||
KEY_ENCODING = 'UTF-8'
|
KEY_ENCODING = 'UTF-8'
|
||||||
|
|
||||||
# Defines how complete representation of command usage (generated based on
|
# Defines how complete representation of command usage (generated based
|
||||||
# command handler argument specification) will be rendered.
|
# on command handler argument specification) will be rendered.
|
||||||
USAGE_PATTERN = 'Usage: %s %s'
|
USAGE_PATTERN = 'Usage: %s %s'
|
||||||
|
|
||||||
def parse_arguments(arguments):
|
def parse_arguments(arguments):
|
||||||
"""
|
"""
|
||||||
Simple yet effective and sufficient in most cases parser which parses
|
Simple yet effective and sufficient in most cases parser which
|
||||||
command arguments and returns them as two lists.
|
parses command arguments and returns them as two lists.
|
||||||
|
|
||||||
First list represents positional arguments as (argument, position), and
|
First list represents positional arguments as (argument, position),
|
||||||
second representing options as (key, value, position) tuples, where position
|
and second representing options as (key, value, position) tuples,
|
||||||
is a (start, end) span tuple of where it was found in the string.
|
where position is a (start, end) span tuple of where it was found in
|
||||||
|
the string.
|
||||||
|
|
||||||
Options may be given in --long or -short format. As --option=value or
|
Options may be given in --long or -short format. As --option=value
|
||||||
--option value or -option value. Keys without values will get None as value.
|
or --option value or -option value. Keys without values will get
|
||||||
|
None as value.
|
||||||
|
|
||||||
Arguments and option values that contain spaces may be given as 'one two
|
Arguments and option values that contain spaces may be given as 'one
|
||||||
three' or "one two three"; that is between single or double quotes.
|
two three' or "one two three"; that is between single or double
|
||||||
|
quotes.
|
||||||
"""
|
"""
|
||||||
args, opts = [], []
|
args, opts = [], []
|
||||||
|
|
||||||
|
@ -90,14 +94,16 @@ def parse_arguments(arguments):
|
||||||
position = match.span()
|
position = match.span()
|
||||||
args.append((body, position))
|
args.append((body, position))
|
||||||
|
|
||||||
# Primitive but sufficiently effective way of disposing of conflicted
|
# Primitive but sufficiently effective way of disposing of
|
||||||
# sectors. Remove any arguments that intersect with options.
|
# conflicted sectors. Remove any arguments that intersect with
|
||||||
|
# options.
|
||||||
for arg, position in args[:]:
|
for arg, position in args[:]:
|
||||||
if intersects_opts(position):
|
if intersects_opts(position):
|
||||||
args.remove((arg, position))
|
args.remove((arg, position))
|
||||||
|
|
||||||
# Primitive but sufficiently effective way of disposing of conflicted
|
# Primitive but sufficiently effective way of disposing of
|
||||||
# sectors. Remove any options that intersect with arguments.
|
# conflicted sectors. Remove any options that intersect with
|
||||||
|
# arguments.
|
||||||
for key, value, position in opts[:]:
|
for key, value, position in opts[:]:
|
||||||
if intersects_args(position):
|
if intersects_args(position):
|
||||||
opts.remove((key, value, position))
|
opts.remove((key, value, position))
|
||||||
|
@ -106,41 +112,44 @@ def parse_arguments(arguments):
|
||||||
|
|
||||||
def adapt_arguments(command, arguments, args, opts):
|
def adapt_arguments(command, arguments, args, opts):
|
||||||
"""
|
"""
|
||||||
Adapt args and opts got from the parser to a specific handler by means of
|
Adapt args and opts got from the parser to a specific handler by
|
||||||
arguments specified on command definition. That is transform them to *args
|
means of arguments specified on command definition. That is
|
||||||
and **kwargs suitable for passing to a command handler.
|
transform them to *args and **kwargs suitable for passing to a
|
||||||
|
command handler.
|
||||||
|
|
||||||
Dashes (-) in the option names will be converted to underscores. So you can
|
Dashes (-) in the option names will be converted to underscores. So
|
||||||
map --one-more-option to a one_more_option=None.
|
you can map --one-more-option to a one_more_option=None.
|
||||||
|
|
||||||
If the initial value of a keyword argument is a boolean (False in most
|
If the initial value of a keyword argument is a boolean (False in
|
||||||
cases) - then this option will be treated as a switch, that is an option
|
most cases) - then this option will be treated as a switch, that is
|
||||||
which does not take an argument. If a switch is followed by an argument -
|
an option which does not take an argument. If a switch is followed
|
||||||
then this argument will be treated just like a normal positional argument.
|
by an argument - then this argument will be treated just like a
|
||||||
|
normal positional argument.
|
||||||
|
|
||||||
If the initial value of a keyword argument is a sequence, that is a tuple or
|
If the initial value of a keyword argument is a sequence, that is a
|
||||||
list - then a value of this option will be considered correct only if it is
|
tuple or list - then a value of this option will be considered
|
||||||
present in the sequence.
|
correct only if it is present in the sequence.
|
||||||
"""
|
"""
|
||||||
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
|
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
|
||||||
norm_kwargs = dict(spec_kwargs)
|
norm_kwargs = dict(spec_kwargs)
|
||||||
|
|
||||||
# Quite complex piece of neck-breaking logic to extract raw arguments if
|
# Quite complex piece of neck-breaking logic to extract raw
|
||||||
# there is more, then one positional argument specified by the command. In
|
# arguments if there is more, then one positional argument specified
|
||||||
# case if it's just one argument which is the collector - this is fairly
|
# by the command. In case if it's just one argument which is the
|
||||||
# easy. But when it's more then one argument - the neck-breaking logic of
|
# collector - this is fairly easy. But when it's more then one
|
||||||
# how to retrieve residual arguments as a raw, all in one piece string,
|
# argument - the neck-breaking logic of how to retrieve residual
|
||||||
# kicks in.
|
# arguments as a raw, all in one piece string, kicks in.
|
||||||
if command.raw:
|
if command.raw:
|
||||||
if arguments:
|
if arguments:
|
||||||
spec_fix = 1 if command.source else 0
|
spec_fix = 1 if command.source else 0
|
||||||
spec_len = len(spec_args) - spec_fix
|
spec_len = len(spec_args) - spec_fix
|
||||||
arguments_end = len(arguments) - 1
|
arguments_end = len(arguments) - 1
|
||||||
|
|
||||||
# If there are any optional arguments given they should be either an
|
# If there are any optional arguments given they should be
|
||||||
# unquoted postional argument or part of the raw argument. So we
|
# either an unquoted postional argument or part of the raw
|
||||||
# find all optional arguments that can possibly be unquoted argument
|
# argument. So we find all optional arguments that can
|
||||||
# and append them as is to the args.
|
# possibly be unquoted argument and append them as is to the
|
||||||
|
# args.
|
||||||
for key, value, (start, end) in opts[:spec_len]:
|
for key, value, (start, end) in opts[:spec_len]:
|
||||||
if value:
|
if value:
|
||||||
end -= len(value) + 1
|
end -= len(value) + 1
|
||||||
|
@ -149,9 +158,9 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
else:
|
else:
|
||||||
args.append((arguments[start:end], (start, end)))
|
args.append((arguments[start:end], (start, end)))
|
||||||
|
|
||||||
# We need in-place sort here because after manipulations with
|
# We need in-place sort here because after manipulations
|
||||||
# options order of arguments might be wrong and we just can't have
|
# with options order of arguments might be wrong and we just
|
||||||
# more complex logic to not let that happen.
|
# can't have more complex logic to not let that happen.
|
||||||
args.sort(key=itemgetter(1))
|
args.sort(key=itemgetter(1))
|
||||||
|
|
||||||
if spec_len > 1:
|
if spec_len > 1:
|
||||||
|
@ -160,27 +169,28 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise CommandError("Missing arguments", command)
|
raise CommandError("Missing arguments", command)
|
||||||
|
|
||||||
# The essential point of the whole play. After boundaries are
|
# The essential point of the whole play. After
|
||||||
# being determined (supposingly correct) we separate raw part
|
# boundaries are being determined (supposingly correct)
|
||||||
# from the rest of arguments, which should be normally
|
# we separate raw part from the rest of arguments, which
|
||||||
# processed.
|
# should be normally processed.
|
||||||
raw = arguments[end:]
|
raw = arguments[end:]
|
||||||
raw = raw.strip() or None
|
raw = raw.strip() or None
|
||||||
|
|
||||||
if not raw and not command.empty:
|
if not raw and not command.empty:
|
||||||
raise CommandError("Missing arguments", command)
|
raise CommandError("Missing arguments", command)
|
||||||
|
|
||||||
# Discard residual arguments and all of the options as raw
|
# Discard residual arguments and all of the options as
|
||||||
# command does not support options and if an option is given it
|
# raw command does not support options and if an option
|
||||||
# is rather a part of a raw argument.
|
# is given it is rather a part of a raw argument.
|
||||||
args = args[:spec_len - 1]
|
args = args[:spec_len - 1]
|
||||||
opts = []
|
opts = []
|
||||||
|
|
||||||
args.append((raw, (end, arguments_end)))
|
args.append((raw, (end, arguments_end)))
|
||||||
else:
|
else:
|
||||||
# Substitue all of the arguments with only one, which contain
|
# Substitue all of the arguments with only one, which
|
||||||
# raw and unprocessed arguments as a string. And discard all the
|
# contain raw and unprocessed arguments as a string. And
|
||||||
# options, as raw command does not support them.
|
# discard all the options, as raw command does not
|
||||||
|
# support them.
|
||||||
args = [(arguments, (0, arguments_end))]
|
args = [(arguments, (0, arguments_end))]
|
||||||
opts = []
|
opts = []
|
||||||
else:
|
else:
|
||||||
|
@ -189,16 +199,17 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
else:
|
else:
|
||||||
raise CommandError("Missing arguments", command)
|
raise CommandError("Missing arguments", command)
|
||||||
|
|
||||||
# The first stage of transforming options we have got to a format that can
|
# The first stage of transforming options we have got to a format
|
||||||
# be used to associate them with declared keyword arguments. Substituting
|
# that can be used to associate them with declared keyword
|
||||||
# dashes (-) in their names with underscores (_).
|
# arguments. Substituting dashes (-) in their names with
|
||||||
|
# underscores (_).
|
||||||
for index, (key, value, position) in enumerate(opts):
|
for index, (key, value, position) in enumerate(opts):
|
||||||
if '-' in key:
|
if '-' in key:
|
||||||
opts[index] = (key.replace('-', '_'), value, position)
|
opts[index] = (key.replace('-', '_'), value, position)
|
||||||
|
|
||||||
# The second stage of transforming options to an associatable state.
|
# The second stage of transforming options to an associatable state.
|
||||||
# Expanding short, one-letter options to a verbose ones, if corresponding
|
# Expanding short, one-letter options to a verbose ones, if
|
||||||
# optin has been given.
|
# corresponding optin has been given.
|
||||||
if command.expand_short:
|
if command.expand_short:
|
||||||
expanded = []
|
expanded = []
|
||||||
for spec_key, spec_value in norm_kwargs.iteritems():
|
for spec_key, spec_value in norm_kwargs.iteritems():
|
||||||
|
@ -210,26 +221,27 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
opts[index] = (spec_key, value, position)
|
opts[index] = (spec_key, value, position)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Detect switches and set their values accordingly. If any of them carries a
|
# Detect switches and set their values accordingly. If any of them
|
||||||
# value - append it to args.
|
# carries a value - append it to args.
|
||||||
for index, (key, value, position) in enumerate(opts):
|
for index, (key, value, position) in enumerate(opts):
|
||||||
if isinstance(norm_kwargs.get(key), BooleanType):
|
if isinstance(norm_kwargs.get(key), BooleanType):
|
||||||
opts[index] = (key, True, position)
|
opts[index] = (key, True, position)
|
||||||
if value:
|
if value:
|
||||||
args.append((value, position))
|
args.append((value, position))
|
||||||
|
|
||||||
# Sorting arguments and options (just to be sure) in regarding to their
|
# Sorting arguments and options (just to be sure) in regarding to
|
||||||
# positions in the string.
|
# their positions in the string.
|
||||||
args.sort(key=itemgetter(1))
|
args.sort(key=itemgetter(1))
|
||||||
opts.sort(key=itemgetter(2))
|
opts.sort(key=itemgetter(2))
|
||||||
|
|
||||||
# Stripping down position information supplied with arguments and options as
|
# Stripping down position information supplied with arguments and
|
||||||
# it won't be needed again.
|
# options as it won't be needed again.
|
||||||
args = map(lambda (arg, position): arg, args)
|
args = map(lambda (arg, position): arg, args)
|
||||||
opts = map(lambda (key, value, position): (key, value), opts)
|
opts = map(lambda (key, value, position): (key, value), opts)
|
||||||
|
|
||||||
# If command has extra option enabled - collect all extra arguments and pass
|
# If command has extra option enabled - collect all extra arguments
|
||||||
# them to a last positional argument command defines as a list.
|
# and pass them to a last positional argument command defines as a
|
||||||
|
# list.
|
||||||
if command.extra:
|
if command.extra:
|
||||||
if not var_args:
|
if not var_args:
|
||||||
spec_fix = 1 if not command.source else 2
|
spec_fix = 1 if not command.source else 2
|
||||||
|
@ -240,9 +252,9 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
else:
|
else:
|
||||||
raise DefinitionError("Can not have both, extra and *args")
|
raise DefinitionError("Can not have both, extra and *args")
|
||||||
|
|
||||||
# Detect if positional arguments overlap keyword arguments. If so and this
|
# Detect if positional arguments overlap keyword arguments. If so
|
||||||
# is allowed by command options - then map them directly to their options,
|
# and this is allowed by command options - then map them directly to
|
||||||
# so they can get propert further processings.
|
# their options, so they can get propert further processings.
|
||||||
spec_fix = 1 if command.source else 0
|
spec_fix = 1 if command.source else 0
|
||||||
spec_len = len(spec_args) - spec_fix
|
spec_len = len(spec_args) - spec_fix
|
||||||
if len(args) > spec_len:
|
if len(args) > spec_len:
|
||||||
|
@ -262,16 +274,17 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
if not isinstance(value, BooleanType):
|
if not isinstance(value, BooleanType):
|
||||||
raise CommandError("%s: Switch can not take an argument" % key, command)
|
raise CommandError("%s: Switch can not take an argument" % key, command)
|
||||||
|
|
||||||
# Detect every sequence constraint and ensure that if corresponding options
|
# Detect every sequence constraint and ensure that if corresponding
|
||||||
# are given - they contain proper values, within the constraint range.
|
# options are given - they contain proper values, within the
|
||||||
|
# constraint range.
|
||||||
for key, value in opts:
|
for key, value in opts:
|
||||||
initial = norm_kwargs.get(key)
|
initial = norm_kwargs.get(key)
|
||||||
if isinstance(initial, (TupleType, ListType)):
|
if isinstance(initial, (TupleType, ListType)):
|
||||||
if value not in initial:
|
if value not in initial:
|
||||||
raise CommandError("%s: Invalid argument" % key, command)
|
raise CommandError("%s: Invalid argument" % key, command)
|
||||||
|
|
||||||
# If argument to an option constrained by a sequence was not given - then
|
# If argument to an option constrained by a sequence was not given -
|
||||||
# it's value should be set to None.
|
# then it's value should be set to None.
|
||||||
for spec_key, spec_value in spec_kwargs:
|
for spec_key, spec_value in spec_kwargs:
|
||||||
if isinstance(spec_value, (TupleType, ListType)):
|
if isinstance(spec_value, (TupleType, ListType)):
|
||||||
for key, value in opts:
|
for key, value in opts:
|
||||||
|
@ -280,31 +293,32 @@ def adapt_arguments(command, arguments, args, opts):
|
||||||
else:
|
else:
|
||||||
opts.append((spec_key, None))
|
opts.append((spec_key, None))
|
||||||
|
|
||||||
# We need to encode every keyword argument to a simple string, not the
|
# We need to encode every keyword argument to a simple string, not
|
||||||
# unicode one, because ** expansion does not support it.
|
# the unicode one, because ** expansion does not support it.
|
||||||
for index, (key, value) in enumerate(opts):
|
for index, (key, value) in enumerate(opts):
|
||||||
if isinstance(key, UnicodeType):
|
if isinstance(key, UnicodeType):
|
||||||
opts[index] = (key.encode(KEY_ENCODING), value)
|
opts[index] = (key.encode(KEY_ENCODING), value)
|
||||||
|
|
||||||
# Inject the source arguments as a string as a first argument, if command
|
# Inject the source arguments as a string as a first argument, if
|
||||||
# has enabled the corresponding option.
|
# command has enabled the corresponding option.
|
||||||
if command.source:
|
if command.source:
|
||||||
args.insert(0, arguments)
|
args.insert(0, arguments)
|
||||||
|
|
||||||
# Return *args and **kwargs in the form suitable for passing to a command
|
# Return *args and **kwargs in the form suitable for passing to a
|
||||||
# handler and being expanded.
|
# command handler and being expanded.
|
||||||
return tuple(args), dict(opts)
|
return tuple(args), dict(opts)
|
||||||
|
|
||||||
def generate_usage(command, complete=True):
|
def generate_usage(command, complete=True):
|
||||||
"""
|
"""
|
||||||
Extract handler's arguments specification and wrap them in a human-readable
|
Extract handler's arguments specification and wrap them in a
|
||||||
format usage information. If complete is given - then USAGE_PATTERN will be
|
human-readable format usage information. If complete is given - then
|
||||||
used to render the specification completly.
|
USAGE_PATTERN will be used to render the specification completly.
|
||||||
"""
|
"""
|
||||||
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
|
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
|
||||||
|
|
||||||
# Remove some special positional arguments from the specifiaction, but store
|
# Remove some special positional arguments from the specifiaction,
|
||||||
# their names so they can be used for usage info generation.
|
# but store their names so they can be used for usage info
|
||||||
|
# generation.
|
||||||
sp_source = spec_args.pop(0) if command.source else None
|
sp_source = spec_args.pop(0) if command.source else None
|
||||||
sp_extra = spec_args.pop() if command.extra else None
|
sp_extra = spec_args.pop() if command.extra else None
|
||||||
|
|
||||||
|
@ -350,8 +364,8 @@ def generate_usage(command, complete=True):
|
||||||
if var_kwargs:
|
if var_kwargs:
|
||||||
usage += (' ' if args else str()) + '[[%s]]' % var_kwargs
|
usage += (' ' if args else str()) + '[[%s]]' % var_kwargs
|
||||||
|
|
||||||
# Native name will be the first one if it is included. Otherwise, names will
|
# Native name will be the first one if it is included. Otherwise,
|
||||||
# be in the order they were specified.
|
# names will be in the order they were specified.
|
||||||
if len(command.names) > 1:
|
if len(command.names) > 1:
|
||||||
names = '%s (%s)' % (command.first_name, ', '.join(command.names[1:]))
|
names = '%s (%s)' % (command.first_name, ', '.join(command.names[1:]))
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Reference in a new issue