added a flask venv
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2010—2014 Andrey Mikhaylenko and contributors
|
||||
#
|
||||
# This file is part of Argh.
|
||||
#
|
||||
# Argh is free software under terms of the GNU Lesser
|
||||
# General Public License version 3 (LGPLv3) as published by the Free
|
||||
# Software Foundation. See the file README.rst for copying conditions.
|
||||
#
|
||||
"""
|
||||
Assembling
|
||||
~~~~~~~~~~
|
||||
|
||||
Functions and classes to properly assemble your commands in a parser.
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from argh.completion import COMPLETION_ENABLED
|
||||
from argh.compat import OrderedDict
|
||||
from argh.constants import (
|
||||
ATTR_ALIASES,
|
||||
ATTR_ARGS,
|
||||
ATTR_NAME,
|
||||
ATTR_EXPECTS_NAMESPACE_OBJECT,
|
||||
PARSER_FORMATTER,
|
||||
DEFAULT_ARGUMENT_TEMPLATE,
|
||||
DEST_FUNCTION,
|
||||
)
|
||||
from argh.utils import get_subparsers, get_arg_spec
|
||||
from argh.exceptions import AssemblingError
|
||||
|
||||
|
||||
__all__ = [
|
||||
'SUPPORTS_ALIASES',
|
||||
'set_default_command',
|
||||
'add_commands',
|
||||
'add_subcommands',
|
||||
]
|
||||
|
||||
|
||||
def _check_support_aliases():
|
||||
p = argparse.ArgumentParser()
|
||||
s = p.add_subparsers()
|
||||
try:
|
||||
s.add_parser('x', aliases=[])
|
||||
except TypeError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
SUPPORTS_ALIASES = _check_support_aliases()
|
||||
"""
|
||||
Calculated on load. If `True`, current version of argparse supports
|
||||
alternative command names (can be set via :func:`~argh.decorators.aliases`).
|
||||
"""
|
||||
|
||||
|
||||
def _get_args_from_signature(function):
|
||||
if getattr(function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
|
||||
return
|
||||
|
||||
spec = get_arg_spec(function)
|
||||
|
||||
defaults = dict(zip(*[reversed(x) for x in (spec.args,
|
||||
spec.defaults or [])]))
|
||||
defaults.update(getattr(spec, 'kwonlydefaults', None) or {})
|
||||
|
||||
kwonly = getattr(spec, 'kwonlyargs', [])
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
annotations = {}
|
||||
else:
|
||||
annotations = dict((k,v) for k,v in function.__annotations__.items()
|
||||
if isinstance(v, str))
|
||||
|
||||
# define the list of conflicting option strings
|
||||
# (short forms, i.e. single-character ones)
|
||||
chars = [a[0] for a in spec.args + kwonly]
|
||||
char_counts = dict((char, chars.count(char)) for char in set(chars))
|
||||
conflicting_opts = tuple(char for char in char_counts
|
||||
if 1 < char_counts[char])
|
||||
|
||||
for name in spec.args + kwonly:
|
||||
flags = [] # name_or_flags
|
||||
akwargs = {} # keyword arguments for add_argument()
|
||||
|
||||
if name in annotations:
|
||||
# help message: func(a : "b") -> add_argument("a", help="b")
|
||||
akwargs.update(help=annotations.get(name))
|
||||
|
||||
if name in defaults or name in kwonly:
|
||||
if name in defaults:
|
||||
akwargs.update(default=defaults.get(name))
|
||||
else:
|
||||
akwargs.update(required=True)
|
||||
flags = ('-{0}'.format(name[0]), '--{0}'.format(name))
|
||||
if name.startswith(conflicting_opts):
|
||||
# remove short name
|
||||
flags = flags[1:]
|
||||
|
||||
else:
|
||||
# positional argument
|
||||
flags = (name,)
|
||||
|
||||
# cmd(foo_bar) -> add_argument('foo-bar')
|
||||
flags = tuple(x.replace('_', '-') for x in flags)
|
||||
|
||||
yield dict(option_strings=flags, **akwargs)
|
||||
|
||||
if spec.varargs:
|
||||
# *args
|
||||
yield dict(option_strings=[spec.varargs], nargs='*')
|
||||
|
||||
|
||||
def _guess(kwargs):
|
||||
"""
|
||||
Adds types, actions, etc. to given argument specification.
|
||||
For example, ``default=3`` implies ``type=int``.
|
||||
|
||||
:param arg: a :class:`argh.utils.Arg` instance
|
||||
"""
|
||||
guessed = {}
|
||||
|
||||
# Parser actions that accept argument 'type'
|
||||
TYPE_AWARE_ACTIONS = 'store', 'append'
|
||||
|
||||
# guess type/action from default value
|
||||
value = kwargs.get('default')
|
||||
if value is not None:
|
||||
if isinstance(value, bool):
|
||||
if kwargs.get('action') is None:
|
||||
# infer action from default value
|
||||
guessed['action'] = 'store_false' if value else 'store_true'
|
||||
elif kwargs.get('type') is None:
|
||||
# infer type from default value
|
||||
# (make sure that action handler supports this keyword)
|
||||
if kwargs.get('action', 'store') in TYPE_AWARE_ACTIONS:
|
||||
guessed['type'] = type(value)
|
||||
|
||||
# guess type from choices (first item)
|
||||
if kwargs.get('choices') and 'type' not in list(guessed) + list(kwargs):
|
||||
guessed['type'] = type(kwargs['choices'][0])
|
||||
|
||||
return dict(kwargs, **guessed)
|
||||
|
||||
|
||||
def _is_positional(args, prefix_chars='-'):
|
||||
assert args
|
||||
if 1 < len(args) or args[0][0].startswith(tuple(prefix_chars)):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def _get_parser_param_kwargs(parser, argspec):
|
||||
argspec = argspec.copy() # parser methods modify source data
|
||||
args = argspec['option_strings']
|
||||
|
||||
if _is_positional(args, prefix_chars=parser.prefix_chars):
|
||||
get_kwargs = parser._get_positional_kwargs
|
||||
else:
|
||||
get_kwargs = parser._get_optional_kwargs
|
||||
|
||||
kwargs = get_kwargs(*args, **argspec)
|
||||
|
||||
kwargs['dest'] = kwargs['dest'].replace('-', '_')
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
def _get_dest(parser, argspec):
|
||||
kwargs = _get_parser_param_kwargs(parser, argspec)
|
||||
return kwargs['dest']
|
||||
|
||||
|
||||
def _require_support_for_default_command_with_subparsers():
|
||||
if sys.version_info < (3,4):
|
||||
raise AssemblingError(
|
||||
'Argparse library bundled with this version of Python '
|
||||
'does not support combining a default command with nested ones.')
|
||||
|
||||
|
||||
def set_default_command(parser, function):
|
||||
"""
|
||||
Sets default command (i.e. a function) for given parser.
|
||||
|
||||
If `parser.description` is empty and the function has a docstring,
|
||||
it is used as the description.
|
||||
|
||||
.. note::
|
||||
|
||||
An attempt to set default command to a parser which already has
|
||||
subparsers (e.g. added with :func:`~argh.assembling.add_commands`)
|
||||
results in a `AssemblingError`.
|
||||
|
||||
.. note::
|
||||
|
||||
If there are both explicitly declared arguments (e.g. via
|
||||
:func:`~argh.decorators.arg`) and ones inferred from the function
|
||||
signature (e.g. via :func:`~argh.decorators.command`), declared ones
|
||||
will be merged into inferred ones. If an argument does not conform
|
||||
function signature, `AssemblingError` is raised.
|
||||
|
||||
.. note::
|
||||
|
||||
If the parser was created with ``add_help=True`` (which is by default),
|
||||
option name ``-h`` is silently removed from any argument.
|
||||
|
||||
"""
|
||||
if parser._subparsers:
|
||||
_require_support_for_default_command_with_subparsers()
|
||||
|
||||
spec = get_arg_spec(function)
|
||||
|
||||
declared_args = getattr(function, ATTR_ARGS, [])
|
||||
inferred_args = list(_get_args_from_signature(function))
|
||||
|
||||
if inferred_args and declared_args:
|
||||
# We've got a mixture of declared and inferred arguments
|
||||
|
||||
# a mapping of "dest" strings to argument declarations.
|
||||
#
|
||||
# * a "dest" string is a normalized form of argument name, i.e.:
|
||||
#
|
||||
# '-f', '--foo' → 'foo'
|
||||
# 'foo-bar' → 'foo_bar'
|
||||
#
|
||||
# * argument declaration is a dictionary representing an argument;
|
||||
# it is obtained either from _get_args_from_signature() or from
|
||||
# an @arg decorator (as is).
|
||||
#
|
||||
dests = OrderedDict()
|
||||
|
||||
for argspec in inferred_args:
|
||||
dest = _get_parser_param_kwargs(parser, argspec)['dest']
|
||||
dests[dest] = argspec
|
||||
|
||||
for declared_kw in declared_args:
|
||||
# an argument is declared via decorator
|
||||
dest = _get_dest(parser, declared_kw)
|
||||
if dest in dests:
|
||||
# the argument is already known from function signature
|
||||
#
|
||||
# now make sure that this declared arg conforms to the function
|
||||
# signature and therefore only refines an inferred arg:
|
||||
#
|
||||
# @arg('my-foo') maps to func(my_foo)
|
||||
# @arg('--my-bar') maps to func(my_bar=...)
|
||||
|
||||
# either both arguments are positional or both are optional
|
||||
decl_positional = _is_positional(declared_kw['option_strings'])
|
||||
infr_positional = _is_positional(dests[dest]['option_strings'])
|
||||
if decl_positional != infr_positional:
|
||||
kinds = {True: 'positional', False: 'optional'}
|
||||
raise AssemblingError(
|
||||
'{func}: argument "{dest}" declared as {kind_i} '
|
||||
'(in function signature) and {kind_d} (via decorator)'
|
||||
.format(
|
||||
func=function.__name__,
|
||||
dest=dest,
|
||||
kind_i=kinds[infr_positional],
|
||||
kind_d=kinds[decl_positional],
|
||||
))
|
||||
|
||||
# merge explicit argument declaration into the inferred one
|
||||
# (e.g. `help=...`)
|
||||
dests[dest].update(**declared_kw)
|
||||
else:
|
||||
# the argument is not in function signature
|
||||
varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
|
||||
if varkw:
|
||||
# function accepts **kwargs; the argument goes into it
|
||||
dests[dest] = declared_kw
|
||||
else:
|
||||
# there's no way we can map the argument declaration
|
||||
# to function signature
|
||||
xs = (dests[x]['option_strings'] for x in dests)
|
||||
raise AssemblingError(
|
||||
'{func}: argument {flags} does not fit '
|
||||
'function signature: {sig}'.format(
|
||||
flags=', '.join(declared_kw['option_strings']),
|
||||
func=function.__name__,
|
||||
sig=', '.join('/'.join(x) for x in xs)))
|
||||
|
||||
# pack the modified data back into a list
|
||||
inferred_args = dests.values()
|
||||
|
||||
command_args = inferred_args or declared_args
|
||||
|
||||
# add types, actions, etc. (e.g. default=3 implies type=int)
|
||||
command_args = [_guess(x) for x in command_args]
|
||||
|
||||
for draft in command_args:
|
||||
draft = draft.copy()
|
||||
if 'help' not in draft:
|
||||
draft.update(help=DEFAULT_ARGUMENT_TEMPLATE)
|
||||
dest_or_opt_strings = draft.pop('option_strings')
|
||||
if parser.add_help and '-h' in dest_or_opt_strings:
|
||||
dest_or_opt_strings = [x for x in dest_or_opt_strings if x != '-h']
|
||||
completer = draft.pop('completer', None)
|
||||
try:
|
||||
action = parser.add_argument(*dest_or_opt_strings, **draft)
|
||||
if COMPLETION_ENABLED and completer:
|
||||
action.completer = completer
|
||||
except Exception as e:
|
||||
raise type(e)('{func}: cannot add arg {args}: {msg}'.format(
|
||||
args='/'.join(dest_or_opt_strings), func=function.__name__, msg=e))
|
||||
|
||||
if function.__doc__ and not parser.description:
|
||||
parser.description = function.__doc__
|
||||
parser.set_defaults(**{
|
||||
DEST_FUNCTION: function,
|
||||
})
|
||||
|
||||
|
||||
def add_commands(parser, functions, namespace=None, namespace_kwargs=None,
|
||||
func_kwargs=None,
|
||||
# deprecated args:
|
||||
title=None, description=None, help=None):
|
||||
"""
|
||||
Adds given functions as commands to given parser.
|
||||
|
||||
:param parser:
|
||||
|
||||
an :class:`argparse.ArgumentParser` instance.
|
||||
|
||||
:param functions:
|
||||
|
||||
a list of functions. A subparser is created for each of them.
|
||||
If the function is decorated with :func:`~argh.decorators.arg`, the
|
||||
arguments are passed to :class:`argparse.ArgumentParser.add_argument`.
|
||||
See also :func:`~argh.dispatching.dispatch` for requirements
|
||||
concerning function signatures. The command name is inferred from the
|
||||
function name. Note that the underscores in the name are replaced with
|
||||
hyphens, i.e. function name "foo_bar" becomes command name "foo-bar".
|
||||
|
||||
:param namespace:
|
||||
|
||||
an optional string representing the group of commands. For example, if
|
||||
a command named "hello" is added without the namespace, it will be
|
||||
available as "prog.py hello"; if the namespace if specified as "greet",
|
||||
then the command will be accessible as "prog.py greet hello". The
|
||||
namespace itself is not callable, so "prog.py greet" will fail and only
|
||||
display a help message.
|
||||
|
||||
:param func_kwargs:
|
||||
|
||||
a `dict` of keyword arguments to be passed to each nested ArgumentParser
|
||||
instance created per command (i.e. per function). Members of this
|
||||
dictionary have the highest priority, so a function's docstring is
|
||||
overridden by a `help` in `func_kwargs` (if present).
|
||||
|
||||
:param namespace_kwargs:
|
||||
|
||||
a `dict` of keyword arguments to be passed to the nested ArgumentParser
|
||||
instance under given `namespace`.
|
||||
|
||||
Deprecated params that should be moved into `namespace_kwargs`:
|
||||
|
||||
:param title:
|
||||
|
||||
passed to :meth:`argparse.ArgumentParser.add_subparsers` as `title`.
|
||||
|
||||
.. deprecated:: 0.26.0
|
||||
|
||||
Please use `namespace_kwargs` instead.
|
||||
|
||||
:param description:
|
||||
|
||||
passed to :meth:`argparse.ArgumentParser.add_subparsers` as
|
||||
`description`.
|
||||
|
||||
.. deprecated:: 0.26.0
|
||||
|
||||
Please use `namespace_kwargs` instead.
|
||||
|
||||
:param help:
|
||||
|
||||
passed to :meth:`argparse.ArgumentParser.add_subparsers` as `help`.
|
||||
|
||||
.. deprecated:: 0.26.0
|
||||
|
||||
Please use `namespace_kwargs` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This function modifies the parser object. Generally side effects are
|
||||
bad practice but we don't seem to have any choice as ArgumentParser is
|
||||
pretty opaque.
|
||||
You may prefer :class:`~argh.helpers.ArghParser.add_commands` for a bit
|
||||
more predictable API.
|
||||
|
||||
.. note::
|
||||
|
||||
An attempt to add commands to a parser which already has a default
|
||||
function (e.g. added with :func:`~argh.assembling.set_default_command`)
|
||||
results in `AssemblingError`.
|
||||
|
||||
"""
|
||||
# FIXME "namespace" is a correct name but it clashes with the "namespace"
|
||||
# that represents arguments (argparse.Namespace and our ArghNamespace).
|
||||
# We should rename the argument here.
|
||||
|
||||
if DEST_FUNCTION in parser._defaults:
|
||||
_require_support_for_default_command_with_subparsers()
|
||||
|
||||
namespace_kwargs = namespace_kwargs or {}
|
||||
|
||||
# FIXME remove this by 1.0
|
||||
#
|
||||
if title:
|
||||
warnings.warn('argument `title` is deprecated in add_commands(),'
|
||||
' use `parser_kwargs` instead', DeprecationWarning)
|
||||
namespace_kwargs['description'] = title
|
||||
if help:
|
||||
warnings.warn('argument `help` is deprecated in add_commands(),'
|
||||
' use `parser_kwargs` instead', DeprecationWarning)
|
||||
namespace_kwargs['help'] = help
|
||||
if description:
|
||||
warnings.warn('argument `description` is deprecated in add_commands(),'
|
||||
' use `parser_kwargs` instead', DeprecationWarning)
|
||||
namespace_kwargs['description'] = description
|
||||
#
|
||||
# /
|
||||
|
||||
subparsers_action = get_subparsers(parser, create=True)
|
||||
|
||||
if namespace:
|
||||
# Make a nested parser and init a deeper _SubParsersAction under it.
|
||||
|
||||
# Create a named group of commands. It will be listed along with
|
||||
# root-level commands in ``app.py --help``; in that context its `title`
|
||||
# can be used as a short description on the right side of its name.
|
||||
# Normally `title` is shown above the list of commands
|
||||
# in ``app.py my-namespace --help``.
|
||||
subsubparser_kw = {
|
||||
'help': namespace_kwargs.get('title'),
|
||||
}
|
||||
subsubparser = subparsers_action.add_parser(namespace, **subsubparser_kw)
|
||||
subparsers_action = subsubparser.add_subparsers(**namespace_kwargs)
|
||||
else:
|
||||
assert not namespace_kwargs, ('`parser_kwargs` only makes sense '
|
||||
'with `namespace`.')
|
||||
|
||||
for func in functions:
|
||||
cmd_name, func_parser_kwargs = _extract_command_meta_from_func(func)
|
||||
|
||||
# override any computed kwargs by manually supplied ones
|
||||
if func_kwargs:
|
||||
func_parser_kwargs.update(func_kwargs)
|
||||
|
||||
# create and set up the parser for this command
|
||||
command_parser = subparsers_action.add_parser(cmd_name, **func_parser_kwargs)
|
||||
set_default_command(command_parser, func)
|
||||
|
||||
|
||||
def _extract_command_meta_from_func(func):
|
||||
# use explicitly defined name; if none, use function name (a_b → a-b)
|
||||
cmd_name = getattr(func, ATTR_NAME,
|
||||
func.__name__.replace('_','-'))
|
||||
|
||||
func_parser_kwargs = {
|
||||
|
||||
# add command help from function's docstring
|
||||
'help': func.__doc__,
|
||||
|
||||
# set default formatter
|
||||
'formatter_class': PARSER_FORMATTER,
|
||||
|
||||
}
|
||||
|
||||
# try adding aliases for command name
|
||||
if SUPPORTS_ALIASES:
|
||||
func_parser_kwargs['aliases'] = getattr(func, ATTR_ALIASES, [])
|
||||
|
||||
return cmd_name, func_parser_kwargs
|
||||
|
||||
|
||||
def add_subcommands(parser, namespace, functions, **namespace_kwargs):
|
||||
"""
|
||||
A wrapper for :func:`add_commands`.
|
||||
|
||||
These examples are equivalent::
|
||||
|
||||
add_commands(parser, [get, put], namespace='db',
|
||||
namespace_kwargs={
|
||||
'title': 'database commands',
|
||||
'help': 'CRUD for our silly database'
|
||||
})
|
||||
|
||||
add_subcommands(parser, 'db', [get, put],
|
||||
title='database commands',
|
||||
help='CRUD for our silly database')
|
||||
|
||||
"""
|
||||
add_commands(parser, functions, namespace=namespace,
|
||||
namespace_kwargs=namespace_kwargs)
|
||||
Reference in New Issue
Block a user