Analizza i file di configurazione, l'ambiente e gli argomenti della riga di comando per ottenere una singola raccolta di opzioni


110

La libreria standard di Python ha moduli per l' analisi dei file di configurazione ( configparser ), la lettura delle variabili d'ambiente ( os.environ ) e l' analisi degli argomenti della riga di comando ( argparse ). Voglio scrivere un programma che faccia tutto questo e anche:

  • Ha una cascata di valori di opzione :

    • valori delle opzioni predefinite, sovrascritti da
    • config opzioni del file, sovrascritte da
    • variabili d'ambiente, sovrascritte da
    • opzioni della riga di comando.
  • Consente uno o più percorsi dei file di configurazione specificati sulla riga di comando con es. --config-file foo.conf, E li legge (al posto o in aggiunta al solito file di configurazione). Questo deve comunque obbedire alla cascata di cui sopra.

  • Consente le definizioni delle opzioni in un unico punto per determinare il comportamento di analisi per i file di configurazione e la riga di comando.

  • Unifica le opzioni analizzate in una singola raccolta di valori di opzione a cui il resto del programma può accedere senza preoccuparsi della loro provenienza.

Tutto ciò di cui ho bisogno è apparentemente nella libreria standard di Python, ma non funzionano insieme senza problemi.

Come posso ottenere questo risultato con una deviazione minima dalla libreria standard Python?


6
Mi piace molto questa domanda. Ho pensato di fare qualcosa di simile per molto tempo ... Sono contento di aver jterracedato una taglia qui per spingermi oltre il limite abbastanza da provare la mia mano a fare qualcosa del genere :)
mgilson

4
Ottima domanda! È sorprendente che questo non sia stato risolto da un pacchetto popolare (o dalla stessa libreria standard) molto tempo fa.
Zearin

Risposte:


33

Il modulo argparse rende questo non folle, a patto che tu sia soddisfatto di un file di configurazione che assomiglia alla riga di comando. (Penso che questo sia un vantaggio, perché gli utenti dovranno imparare solo una sintassi.) L'impostazione di fromfile_prefix_chars su, ad esempio @, fa in modo che,

my_prog --foo=bar

è equivalente a

my_prog @baz.conf

se @baz.confè,

--foo
bar

Puoi persino fare in modo che il tuo codice cerchi foo.confautomaticamente modificandoloargv

if os.path.exists('foo.conf'):
    argv = ['@foo.conf'] + argv
args = argparser.parse_args(argv)

Il formato di questi file di configurazione è modificabile creando una sottoclasse di ArgumentParser e aggiungendo un metodo convert_arg_line_to_args .


Fino a quando qualcuno non fornisce un'alternativa migliore, questa è la risposta giusta. Ho usato argparse e non ho nemmeno guardato questa funzione. Bello!
Lemure

ma questo non ha una risposta per le variabili d'ambiente?
jterrace

1
@jterrace: Questa risposta così può funzionare per voi: stackoverflow.com/a/10551190/400793
Alex Szatmary

27

AGGIORNAMENTO: finalmente sono riuscito a metterlo su pypi. Installa l'ultima versione tramite:

   pip install configargparser

La guida completa e le istruzioni sono qui .

Post originale

Ecco una piccola cosa che ho hackerato insieme. Sentiti libero di suggerire miglioramenti / segnalazioni di bug nei commenti:

import argparse
import ConfigParser
import os

def _identity(x):
    return x

_SENTINEL = object()


class AddConfigFile(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        # I can never remember if `values` is a list all the time or if it
        # can be a scalar string; this takes care of both.
        if isinstance(values,basestring):
            parser.config_files.append(values)
        else:
            parser.config_files.extend(values)


class ArgumentConfigEnvParser(argparse.ArgumentParser):
    def __init__(self,*args,**kwargs):
        """
        Added 2 new keyword arguments to the ArgumentParser constructor:

           config --> List of filenames to parse for config goodness
           default_section --> name of the default section in the config file
        """
        self.config_files = kwargs.pop('config',[])  #Must be a list
        self.default_section = kwargs.pop('default_section','MAIN')
        self._action_defaults = {}
        argparse.ArgumentParser.__init__(self,*args,**kwargs)


    def add_argument(self,*args,**kwargs):
        """
        Works like `ArgumentParser.add_argument`, except that we've added an action:

           config: add a config file to the parser

        This also adds the ability to specify which section of the config file to pull the 
        data from, via the `section` keyword.  This relies on the (undocumented) fact that
        `ArgumentParser.add_argument` actually returns the `Action` object that it creates.
        We need this to reliably get `dest` (although we could probably write a simple
        function to do this for us).
        """

        if 'action' in kwargs and kwargs['action'] == 'config':
            kwargs['action'] = AddConfigFile
            kwargs['default'] = argparse.SUPPRESS

        # argparse won't know what to do with the section, so 
        # we'll pop it out and add it back in later.
        #
        # We also have to prevent argparse from doing any type conversion,
        # which is done explicitly in parse_known_args.  
        #
        # This way, we can reliably check whether argparse has replaced the default.
        #
        section = kwargs.pop('section', self.default_section)
        type = kwargs.pop('type', _identity)
        default = kwargs.pop('default', _SENTINEL)

        if default is not argparse.SUPPRESS:
            kwargs.update(default=_SENTINEL)
        else:  
            kwargs.update(default=argparse.SUPPRESS)

        action = argparse.ArgumentParser.add_argument(self,*args,**kwargs)
        kwargs.update(section=section, type=type, default=default)
        self._action_defaults[action.dest] = (args,kwargs)
        return action

    def parse_known_args(self,args=None, namespace=None):
        # `parse_args` calls `parse_known_args`, so we should be okay with this...
        ns, argv = argparse.ArgumentParser.parse_known_args(self, args=args, namespace=namespace)
        config_parser = ConfigParser.SafeConfigParser()
        config_files = [os.path.expanduser(os.path.expandvars(x)) for x in self.config_files]
        config_parser.read(config_files)

        for dest,(args,init_dict) in self._action_defaults.items():
            type_converter = init_dict['type']
            default = init_dict['default']
            obj = default

            if getattr(ns,dest,_SENTINEL) is not _SENTINEL: # found on command line
                obj = getattr(ns,dest)
            else: # not found on commandline
                try:  # get from config file
                    obj = config_parser.get(init_dict['section'],dest)
                except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): # Nope, not in config file
                    try: # get from environment
                        obj = os.environ[dest.upper()]
                    except KeyError:
                        pass

            if obj is _SENTINEL:
                setattr(ns,dest,None)
            elif obj is argparse.SUPPRESS:
                pass
            else:
                setattr(ns,dest,type_converter(obj))

        return ns, argv


if __name__ == '__main__':
    fake_config = """
[MAIN]
foo:bar
bar:1
"""
    with open('_config.file','w') as fout:
        fout.write(fake_config)

    parser = ArgumentConfigEnvParser()
    parser.add_argument('--config-file', action='config', help="location of config file")
    parser.add_argument('--foo', type=str, action='store', default="grape", help="don't know what foo does ...")
    parser.add_argument('--bar', type=int, default=7, action='store', help="This is an integer (I hope)")
    parser.add_argument('--baz', type=float, action='store', help="This is an float(I hope)")
    parser.add_argument('--qux', type=int, default='6', action='store', help="this is another int")
    ns = parser.parse_args([])

    parser_defaults = {'foo':"grape",'bar':7,'baz':None,'qux':6}
    config_defaults = {'foo':'bar','bar':1}
    env_defaults = {"baz":3.14159}

    # This should be the defaults we gave the parser
    print ns
    assert ns.__dict__ == parser_defaults

    # This should be the defaults we gave the parser + config defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    os.environ['BAZ'] = "3.14159"

    # This should be the parser defaults + config defaults + env_defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    # This should be the parser defaults + config defaults + env_defaults + commandline
    commandline = {'foo':'3','qux':4} 
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    d.update(commandline)
    ns = parser.parse_args(['--config-file','_config.file','--foo=3','--qux=4'])
    print ns
    assert ns.__dict__ == d

    os.remove('_config.file')

FARE

Questa implementazione è ancora incompleta. Ecco un elenco di TODO parziale:

Conforme al comportamento documentato

  • (facile) Scrivi una funzione che capisca destda argsdentro add_argument, invece di fare affidamento Actionsull'oggetto
  • (banale) Scrivi una parse_argsfunzione che usi parse_known_args. (es. copia parse_argsdall'implementazione cpythonper garantire che richiami parse_known_args.)

Cose meno facili ...

Non ho ancora provato niente di tutto questo. È improbabile - ma ancora possibile! - che potrebbe funzionare ...

  • (difficile?) Mutua esclusione
  • (difficile?) Gruppi di argomenti (se implementati, questi gruppi dovrebbero ottenere un sectionnel file di configurazione.)
  • (difficile?) Sottocomandi (Anche i sottocomandi dovrebbero avere un sectionnel file di configurazione.)

ti dispiace metterlo in un repository GitHub in modo che tutti possano migliorarlo?
brent.payne

1
@ brent.payne - github.com/mgilson/configargparser - Se voglio rilasciarlo come codice reale, ho deciso di prendermi un po 'di tempo stasera per ripulirlo un po'. :-)
mgilson

3
FWIW, ho finalmente ottenuto intorno a mettere questo su PyPI - Si dovrebbe essere in grado di installarlo tramitepip install configargparser
mgilson

@mgilson - Ho aggiornato il tuo post. Questo pacchetto merita di più!
ErichBSchulz

12

C'è una libreria che fa esattamente questo chiamato configglue .

configglue è una libreria che unisce optparse.OptionParser e ConfigParser.ConfigParser di python, così da non doverti ripetere quando vuoi esportare le stesse opzioni in un file di configurazione e un'interfaccia a riga di comando.

Supporta anche le variabili d'ambiente.

C'è anche un'altra libreria chiamata ConfigArgParse che è

Un sostituto drop-in per argparse che consente di impostare le opzioni anche tramite file di configurazione e / o variabili di ambiente.

Potresti essere interessato a PyCon talk about configuration di Łukasz Langa - Let Them Configure!


Ho chiesto se ci sono piani per supportare il modulo argparse.
Piotr Dobrogost

10

Anche se non l'ho provato da solo, c'è la libreria ConfigArgParse che afferma che fa la maggior parte delle cose che desideri:

Un sostituto drop-in per argparse che consente di impostare le opzioni anche tramite file di configurazione e / o variabili di ambiente.


1
L'ho provato, ConfigArgParse è molto conveniente e in effetti è un sostituto immediato.
maxschlepzig

7

Sembra che la libreria standard non affronta questo, lasciando ogni programmatore di ciottoli configparsere argparsee os.environtutti insieme in modo goffo.


5

La libreria standard di Python non lo fornisce, per quanto ne so. L'ho risolto da solo scrivendo codice da utilizzare optparsee ConfigParserper analizzare la riga di comando e i file di configurazione e fornire un livello di astrazione sopra di essi. Tuttavia, avresti bisogno di questo come dipendenza separata, che dal tuo commento precedente sembra essere sgradevole.

Se vuoi guardare il codice che ho scritto, è su http://liw.fi/cliapp/ . È integrato nella mia libreria "framework dell'applicazione a riga di comando", poiché è una parte importante di ciò che il framework deve fare.


4

Di recente ho provato qualcosa di simile, usando "optparse".

L'ho impostato come una sottoclasse di OptonParser, con un comando "--Store" e "--Check".

Il codice seguente dovrebbe praticamente coprirti. Hai solo bisogno di definire i tuoi metodi 'load' e 'store' che accettano / restituiscono dizionari e sei una preda molto impostata.


class SmartParse(optparse.OptionParser):
    def __init__(self,defaults,*args,**kwargs):
        self.smartDefaults=defaults
        optparse.OptionParser.__init__(self,*args,**kwargs)
        fileGroup = optparse.OptionGroup(self,'handle stored defaults')
        fileGroup.add_option(
            '-S','--Store',
            dest='Action',
            action='store_const',const='Store',
            help='store command line settings'
        )
        fileGroup.add_option(
            '-C','--Check',
            dest='Action',
            action='store_const',const='Check',
            help ='check stored settings'
        )
        self.add_option_group(fileGroup)
    def parse_args(self,*args,**kwargs):
        (options,arguments) = optparse.OptionParser.parse_args(self,*args,**kwargs)
        action = options.__dict__.pop('Action')
        if action == 'Check':
            assert all(
                value is None 
                for (key,value) in options.__dict__.iteritems() 
            )
            print 'defaults:',self.smartDefaults
            print 'config:',self.load()
            sys.exit()
        elif action == 'Store':
            self.store(options.__dict__)
            sys.exit()
        else:
            config=self.load()
            commandline=dict(
                [key,val] 
                for (key,val) in options.__dict__.iteritems() 
                if val is not None
            )
            result = {}
            result.update(self.defaults)
            result.update(config)
            result.update(commandline)
            return result,arguments
    def load(self):
        return {}
    def store(self,optionDict):
        print 'Storing:',optionDict

ma ancora utile se vuoi rimanere compatibile con le versioni precedenti di Python
MarioVilas

3

Per soddisfare tutti questi requisiti, consiglierei di scrivere la tua libreria che utilizzi sia [opt | arg] parse che configparser per la funzionalità sottostante.

Dati i primi due e l'ultimo requisito, direi che vuoi:

Fase uno: eseguire un passaggio del parser della riga di comando che cerchi solo l'opzione --config-file.

Fase due: analizzare il file di configurazione.

Passaggio tre: impostare un secondo passaggio del parser della riga di comando utilizzando l'output del passaggio del file di configurazione come predefinito.

Il terzo requisito probabilmente significa che devi progettare il tuo sistema di definizione delle opzioni per esporre tutte le funzionalità di optparse e configparser che ti interessano e scrivere un po 'di idraulica per fare conversioni nel mezzo.


Questo è un po 'più lontano dalla "deviazione minima dalla libreria standard Python" di quanto speravo.
bignose

2

Ecco un modulo che ho hackerato insieme che legge gli argomenti della riga di comando, le impostazioni dell'ambiente, i file ini e anche i valori del portachiavi. È anche disponibile in breve .

"""
Configuration Parser

Configurable parser that will parse config files, environment variables,
keyring, and command-line arguments.



Example test.ini file:

    [defaults]
    gini=10

    [app]
    xini = 50

Example test.arg file:

    --xfarg=30

Example test.py file:

    import os
    import sys

    import config


    def main(argv):
        '''Test.'''
        options = [
            config.Option("xpos",
                          help="positional argument",
                          nargs='?',
                          default="all",
                          env="APP_XPOS"),
            config.Option("--xarg",
                          help="optional argument",
                          default=1,
                          type=int,
                          env="APP_XARG"),
            config.Option("--xenv",
                          help="environment argument",
                          default=1,
                          type=int,
                          env="APP_XENV"),
            config.Option("--xfarg",
                          help="@file argument",
                          default=1,
                          type=int,
                          env="APP_XFARG"),
            config.Option("--xini",
                          help="ini argument",
                          default=1,
                          type=int,
                          ini_section="app",
                          env="APP_XINI"),
            config.Option("--gini",
                          help="global ini argument",
                          default=1,
                          type=int,
                          env="APP_GINI"),
            config.Option("--karg",
                          help="secret keyring arg",
                          default=-1,
                          type=int),
        ]
        ini_file_paths = [
            '/etc/default/app.ini',
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'test.ini')
        ]

        # default usage
        conf = config.Config(prog='app', options=options,
                             ini_paths=ini_file_paths)
        conf.parse()
        print conf

        # advanced usage
        cli_args = conf.parse_cli(argv=argv)
        env = conf.parse_env()
        secrets = conf.parse_keyring(namespace="app")
        ini = conf.parse_ini(ini_file_paths)
        sources = {}
        if ini:
            for key, value in ini.iteritems():
                conf[key] = value
                sources[key] = "ini-file"
        if secrets:
            for key, value in secrets.iteritems():
                conf[key] = value
                sources[key] = "keyring"
        if env:
            for key, value in env.iteritems():
                conf[key] = value
                sources[key] = "environment"
        if cli_args:
            for key, value in cli_args.iteritems():
                conf[key] = value
                sources[key] = "command-line"
        print '\n'.join(['%s:\t%s' % (k, v) for k, v in sources.items()])


    if __name__ == "__main__":
        if config.keyring:
            config.keyring.set_password("app", "karg", "13")
        main(sys.argv)

Example results:

    $APP_XENV=10 python test.py api --xarg=2 @test.arg
    <Config xpos=api, gini=1, xenv=10, xini=50, karg=13, xarg=2, xfarg=30>
    xpos:   command-line
    xenv:   environment
    xini:   ini-file
    karg:   keyring
    xarg:   command-line
    xfarg:  command-line


"""
import argparse
import ConfigParser
import copy
import os
import sys

try:
    import keyring
except ImportError:
    keyring = None


class Option(object):
    """Holds a configuration option and the names and locations for it.

    Instantiate options using the same arguments as you would for an
    add_arguments call in argparse. However, you have two additional kwargs
    available:

        env: the name of the environment variable to use for this option
        ini_section: the ini file section to look this value up from
    """

    def __init__(self, *args, **kwargs):
        self.args = args or []
        self.kwargs = kwargs or {}

    def add_argument(self, parser, **override_kwargs):
        """Add an option to a an argparse parser."""
        kwargs = {}
        if self.kwargs:
            kwargs = copy.copy(self.kwargs)
            try:
                del kwargs['env']
            except KeyError:
                pass
            try:
                del kwargs['ini_section']
            except KeyError:
                pass
        kwargs.update(override_kwargs)
        parser.add_argument(*self.args, **kwargs)

    @property
    def type(self):
        """The type of the option.

        Should be a callable to parse options.
        """
        return self.kwargs.get("type", str)

    @property
    def name(self):
        """The name of the option as determined from the args."""
        for arg in self.args:
            if arg.startswith("--"):
                return arg[2:].replace("-", "_")
            elif arg.startswith("-"):
                continue
            else:
                return arg.replace("-", "_")

    @property
    def default(self):
        """The default for the option."""
        return self.kwargs.get("default")


class Config(object):
    """Parses configuration sources."""

    def __init__(self, options=None, ini_paths=None, **parser_kwargs):
        """Initialize with list of options.

        :param ini_paths: optional paths to ini files to look up values from
        :param parser_kwargs: kwargs used to init argparse parsers.
        """
        self._parser_kwargs = parser_kwargs or {}
        self._ini_paths = ini_paths or []
        self._options = copy.copy(options) or []
        self._values = {option.name: option.default
                        for option in self._options}
        self._parser = argparse.ArgumentParser(**parser_kwargs)
        self.pass_thru_args = []

    @property
    def prog(self):
        """Program name."""
        return self._parser.prog

    def __getitem__(self, key):
        return self._values[key]

    def __setitem__(self, key, value):
        self._values[key] = value

    def __delitem__(self, key):
        del self._values[key]

    def __contains__(self, key):
        return key in self._values

    def __iter__(self):
        return iter(self._values)

    def __len__(self):
        return len(self._values)

    def get(self, key, *args):
        """
        Return the value for key if it exists otherwise the default.
        """
        return self._values.get(key, *args)

    def __getattr__(self, attr):
        if attr in self._values:
            return self._values[attr]
        else:
            raise AttributeError("'config' object has no attribute '%s'"
                                 % attr)

    def build_parser(self, options, **override_kwargs):
        """."""
        kwargs = copy.copy(self._parser_kwargs)
        kwargs.update(override_kwargs)
        if 'fromfile_prefix_chars' not in kwargs:
            kwargs['fromfile_prefix_chars'] = '@'
        parser = argparse.ArgumentParser(**kwargs)
        if options:
            for option in options:
                option.add_argument(parser)
        return parser

    def parse_cli(self, argv=None):
        """Parse command-line arguments into values."""
        if not argv:
            argv = sys.argv
        options = []
        for option in self._options:
            temp = Option(*option.args, **option.kwargs)
            temp.kwargs['default'] = argparse.SUPPRESS
            options.append(temp)
        parser = self.build_parser(options=options)
        parsed, extras = parser.parse_known_args(argv[1:])
        if extras:
            valid, pass_thru = self.parse_passthru_args(argv[1:])
            parsed, extras = parser.parse_known_args(valid)
            if extras:
                raise AttributeError("Unrecognized arguments: %s" %
                                     ' ,'.join(extras))
            self.pass_thru_args = pass_thru + extras
        return vars(parsed)

    def parse_env(self):
        results = {}
        for option in self._options:
            env_var = option.kwargs.get('env')
            if env_var and env_var in os.environ:
                value = os.environ[env_var]
                results[option.name] = option.type(value)
        return results

    def get_defaults(self):
        """Use argparse to determine and return dict of defaults."""
        parser = self.build_parser(options=self._options)
        parsed, _ = parser.parse_known_args([])
        return vars(parsed)

    def parse_ini(self, paths=None):
        """Parse config files and return configuration options.

        Expects array of files that are in ini format.
        :param paths: list of paths to files to parse (uses ConfigParse logic).
                      If not supplied, uses the ini_paths value supplied on
                      initialization.
        """
        results = {}
        config = ConfigParser.SafeConfigParser()
        config.read(paths or self._ini_paths)
        for option in self._options:
            ini_section = option.kwargs.get('ini_section')
            if ini_section:
                try:
                    value = config.get(ini_section, option.name)
                    results[option.name] = option.type(value)
                except ConfigParser.NoSectionError:
                    pass
        return results

    def parse_keyring(self, namespace=None):
        """."""
        results = {}
        if not keyring:
            return results
        if not namespace:
            namespace = self.prog
        for option in self._options:
            secret = keyring.get_password(namespace, option.name)
            if secret:
                results[option.name] = option.type(secret)
        return results

    def parse(self, argv=None):
        """."""
        defaults = self.get_defaults()
        args = self.parse_cli(argv=argv)
        env = self.parse_env()
        secrets = self.parse_keyring()
        ini = self.parse_ini()

        results = defaults
        results.update(ini)
        results.update(secrets)
        results.update(env)
        results.update(args)

        self._values = results
        return self

    @staticmethod
    def parse_passthru_args(argv):
        """Handles arguments to be passed thru to a subprocess using '--'.

        :returns: tuple of two lists; args and pass-thru-args
        """
        if '--' in argv:
            dashdash = argv.index("--")
            if dashdash == 0:
                return argv[1:], []
            elif dashdash > 0:
                return argv[0:dashdash], argv[dashdash + 1:]
        return argv, []

    def __repr__(self):
        return "<Config %s>" % ', '.join([
            '%s=%s' % (k, v) for k, v in self._values.iteritems()])


def comma_separated_strings(value):
    """Handles comma-separated arguments passed in command-line."""
    return map(str, value.split(","))


def comma_separated_pairs(value):
    """Handles comma-separated key/values passed in command-line."""
    pairs = value.split(",")
    results = {}
    for pair in pairs:
        key, pair_value = pair.split('=')
        results[key] = pair_value
    return results


-1

La libreria che ho costruito è proprio per soddisfare la maggior parte delle tue esigenze.

  • Può caricare il file di configurazione più volte attraverso i percorsi file o il nome del modulo specificati.
  • Carica le configurazioni dalle variabili d'ambiente con un dato prefisso.
  • Può allegare opzioni della riga di comando ad alcuni comandi di clic

    (scusate, non è argparse, ma il clic è migliore e molto più avanzato. confectpotrebbe supportare argparse nella versione futura).

  • Soprattutto, confectcarica i file di configurazione Python non JSON / YMAL / TOML / INI. Proprio come il file del profilo IPython o il file delle impostazioni DJANGO, il file di configurazione Python è flessibile e più facile da mantenere.

Per maggiori informazioni, controlla il README.rst nel repository del progetto . Tieni presente che supporta solo Python3.6.

Esempi

Collegamento delle opzioni della riga di comando

import click
from proj_X.core import conf

@click.command()
@conf.click_options
def cli():
    click.echo(f'cache_expire = {conf.api.cache_expire}')

if __name__ == '__main__':
    cli()

Crea automaticamente un messaggio di aiuto completo con tutte le proprietà e i valori predefiniti dichiarati.

$ python -m proj_X.cli --help
Usage: cli.py [OPTIONS]

Options:
  --api-cache_expire INTEGER  [default: 86400]
  --api-cache_prefix TEXT     [default: proj_X_cache]
  --api-url_base_path TEXT    [default: api/v2/]
  --db-db_name TEXT           [default: proj_x]
  --db-username TEXT          [default: proj_x_admin]
  --db-password TEXT          [default: your_password]
  --db-host TEXT              [default: 127.0.0.1]
  --help                      Show this message and exit.

Caricamento delle variabili d'ambiente

È necessaria solo una riga per caricare le variabili di ambiente

conf.load_envvars('proj_X')

> scusa, non è argparse, ma il clic è migliore e molto più avanzato […] Indipendentemente dai meriti di una libreria di terze parti, questo non è una risposta alla domanda.
bignose
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.