Solución:
Considere definir una clase de extensión para argparse.Namespace
que proporciona las sugerencias de tipo que desea:
class MyProgramArgs(argparse.Namespace):
def __init__():
self.somearg = 'defaultval' # type: str
Entonces usa namespace=
pasar eso a parse_args
:
def process_argv():
parser = argparse.ArgumentParser()
parser.add_argument('--somearg')
nsp = MyProgramArgs()
parsed = parser.parse_args(['--somearg','someval'], namespace=nsp) # type: MyProgramArgs
the_arg = parsed.somearg # <- Pycharm should not complain
El analizador de argumentos mecanografiados se hizo exactamente para este propósito. Envuelve argparse
. Su ejemplo se implementa como:
from tap import Tap
class ArgumentParser(Tap):
somearg: str
parsed = ArgumentParser().parse_args(['--somearg', 'someval'])
the_arg = parsed.somearg
Aquí hay una foto de él en acción.
Está en PyPI y se puede instalar con: pip install typed-argument-parser
Divulgación completa: soy uno de los creadores de esta biblioteca.
No sé nada sobre cómo PyCharm maneja estas sugerencias tipográficas, pero entiendo el Namespace
código.
argparse.Namespace
es una clase simple; esencialmente un objeto con algunos métodos que facilitan la visualización de los atributos. Y para facilitar las pruebas unitarias, tiene un __eq__
método. Puede leer la definición en el argparse.py
expediente.
los parser
interactúa con el espacio de nombres de la forma más general posible, con getattr
, setattr
, hasattr
. Entonces puedes usar casi cualquier dest
cadena, incluso aquellos a los que no puede acceder con el .dest
sintaxis.
Asegúrese de no confundir el add_argument
type=
parámetro; eso es una función.
Usando el tuyo namespace
class (desde cero o en subclases) como se sugiere en la otra respuesta puede ser la mejor opción. Esto se describe brevemente en la documentación. Objeto de espacio de nombres. No he visto que esto se haga mucho, aunque lo he sugerido varias veces para manejar necesidades especiales de almacenamiento. Entonces tendrás que experimentar.
Si usa subanálisis, el uso de una clase de espacio de nombres personalizada puede fallar, http://bugs.python.org/issue27859
Preste atención al manejo de incumplimientos. El valor predeterminado predeterminado para la mayoría argparse
acciones es None
. Es útil usar esto después de analizar para hacer algo especial si el usuario no proporcionó esta opción.
if args.foo is None:
# user did not use this optional
args.foo = 'some post parsing default'
else:
# user provided value
pass
Eso podría interferir con las sugerencias de tipo. Cualquiera que sea la solución que intente, preste atención a los valores predeterminados.
A namedtuple
no funcionará como Namespace
.
Primero, el uso adecuado de una clase de espacio de nombres personalizada es:
nm = MyClass(<default values>)
args = parser.parse_args(namespace=nm)
Es decir, inicializa una instancia de esa clase y la pasa como parámetro. El retorno args
será la misma instancia, con nuevos atributos establecidos por análisis.
En segundo lugar, solo se puede crear una tupla con nombre, no se puede cambiar.
In [72]: MagicSpace=namedtuple('MagicSpace',['foo','bar'])
In [73]: nm = MagicSpace(1,2)
In [74]: nm
Out[74]: MagicSpace(foo=1, bar=2)
In [75]: nm.foo='one'
...
AttributeError: can't set attribute
In [76]: getattr(nm, 'foo')
Out[76]: 1
In [77]: setattr(nm, 'foo', 'one') # not even with setattr
...
AttributeError: can't set attribute
Un espacio de nombres tiene que funcionar getattr
y setattr
.
Otro problema con namedtuple
es que no establece ningún tipo de type
información. Simplemente define los nombres de los campos / atributos. Por tanto, no hay nada que comprobar la escritura estática.
Si bien es fácil obtener los nombres de atributos esperados del parser
, no puede obtener los tipos esperados.
Para un analizador simple:
In [82]: parser.print_usage()
usage: ipython3 [-h] [-foo FOO] bar
In [83]: [a.dest for a in parser._actions[1:]]
Out[83]: ['foo', 'bar']
In [84]: [a.type for a in parser._actions[1:]]
Out[84]: [None, None]
Las acciones dest
es el nombre de atributo normal. Pero type
no es el tipo estático esperado de ese atributo. Es una función que puede convertir o no la cadena de entrada. Aquí None
significa que la cadena de entrada se guarda tal cual.
Porque la escritura estática y argparse
requieren información diferente, no hay una manera fácil de generar uno a partir del otro.
Creo que lo mejor que puede hacer es crear su propia base de datos de parámetros, probablemente en un diccionario, y crear tanto la clase de espacio de nombres como el analizador a partir de eso, con sus propias funciones de utilidad.
Digamos dd
es diccionario con las claves necesarias. Entonces podemos crear un argumento con:
parser.add_argument(dd['short'],dd['long'], dest=dd['dest'], type=dd['typefun'], default=dd['default'], help=dd['help'])
Usted u otra persona tendrá que crear una definición de clase de espacio de nombres que establezca el default
(fácil) y de tipo estático (¿difícil?) de dicho diccionario.