পাইথনের কমান্ড লাইনে কনফিগারেশন বিকল্পগুলি ওভাররাইড করার অনুমতি দেওয়ার সর্বোত্তম উপায় কোনটি?


88

আমার কাছে পাইথন অ্যাপ্লিকেশন রয়েছে যার জন্য বেশ কয়েকটি (~ 30) কনফিগারেশন প্যারামিটার প্রয়োজন। এখন অবধি, আমি অ্যাপ্লিকেশনটি চাওয়ার সময় কমান্ড লাইনে পৃথক প্যারামিটারগুলি পরিবর্তনের সম্ভাবনা সহ অ্যাপ্লিকেশনটিতেই ডিফল্ট মানগুলি সংজ্ঞায়িত করতে অপশনপার্সার ক্লাসটি ব্যবহার করেছি।

এখন আমি 'যথাযথ' কনফিগারেশন ফাইলগুলি ব্যবহার করতে চাই, উদাহরণস্বরূপ কনফিগার পার্সার ক্লাস থেকে। একই সময়ে, ব্যবহারকারীদের এখনও কমান্ড লাইনে পৃথক পরামিতি পরিবর্তন করতে সক্ষম হওয়া উচিত।

আমি ভাবছিলাম দুটি পদক্ষেপের একত্রিত করার কোনও উপায় আছে কিনা, উদাহরণস্বরূপ কমান্ড লাইন বিকল্পগুলি পরিচালনা করতে অপ্ট পার্সি (বা নতুন আরগপার্স) ব্যবহার করুন, তবে কনফিগারেশন সিনট্যাক্সের কনফিগারেশন ফাইল থেকে ডিফল্ট মানগুলি পড়া।

কোনও ধারনা কীভাবে এটি সহজ উপায়ে করবেন? আমি কনফিগার পার্সে ম্যানুয়ালি আহ্বান জানাচ্ছি না, এবং তারপরে ম্যানুয়ালি সমস্ত বিকল্পের সমস্ত ডিফল্টকে যথাযথ মানগুলিতে সেট করে ...


6
আপডেট : কনফিগারআর্গ পার্স প্যাকেজটি আরগপার্সের জন্য একটি ড্রপ-ইন প্রতিস্থাপন যা বিকল্পগুলি কনফিগারেশন ফাইল এবং / অথবা পরিবেশের ভেরিয়েবলের মাধ্যমে সেট করার অনুমতি দেয়। নীচে উত্তরটি @ ব্যবহারকারী 55393965 দেখুন
নীলাম্যাকবি

উত্তর:


89

আমি সন্ধান পেয়েছি আপনি এটি দিয়ে এটি করতে পারেন argparse.ArgumentParser.parse_known_args()parse_known_args()কমান্ডলাইন থেকে একটি কনফিগারেশন ফাইল বিশ্লেষণ করে শুরু করুন , তারপরে এটি কনফিগার পার্সারের সাথে পড়ুন এবং ডিফল্ট সেট করুন এবং তারপরে বাকী বিকল্পগুলি বিশ্লেষণ করুন parse_args()। এটি আপনাকে একটি ডিফল্ট মান রাখতে দেয়, একটি কনফিগারেশন ফাইল দিয়ে ওভাররাইড করে এবং কমান্ডলাইন বিকল্পের সাহায্যে ওভাররাইড করে। যেমন:

কোনও ব্যবহারকারী ইনপুট ছাড়াই ডিফল্ট:

$ ./argparse-partial.py
Option is "default"

কনফিগারেশন ফাইল থেকে ডিফল্ট:

$ cat argparse-partial.config 
[Defaults]
option=Hello world!
$ ./argparse-partial.py -c argparse-partial.config 
Option is "Hello world!"

কনফিগারেশন ফাইল থেকে ডিফল্ট, কমান্ডলাইনের দ্বারা ওভাররাইড করা:

$ ./argparse-partial.py -c argparse-partial.config --option override
Option is "override"

argprase-partial.py অনুসরণ করে। -hসঠিকভাবে সাহায্যের জন্য পরিচালনা করা সামান্য জটিল ।

import argparse
import ConfigParser
import sys

def main(argv=None):
    # Do argv default this way, as doing it in the functional
    # declaration sets it at compile time.
    if argv is None:
        argv = sys.argv

    # Parse any conf_file specification
    # We make this parser with add_help=False so that
    # it doesn't parse -h and print help.
    conf_parser = argparse.ArgumentParser(
        description=__doc__, # printed with -h/--help
        # Don't mess with format of description
        formatter_class=argparse.RawDescriptionHelpFormatter,
        # Turn off help, so we print all options in response to -h
        add_help=False
        )
    conf_parser.add_argument("-c", "--conf_file",
                        help="Specify config file", metavar="FILE")
    args, remaining_argv = conf_parser.parse_known_args()

    defaults = { "option":"default" }

    if args.conf_file:
        config = ConfigParser.SafeConfigParser()
        config.read([args.conf_file])
        defaults.update(dict(config.items("Defaults")))

    # Parse rest of arguments
    # Don't suppress add_help here so it will handle -h
    parser = argparse.ArgumentParser(
        # Inherit options from config_parser
        parents=[conf_parser]
        )
    parser.set_defaults(**defaults)
    parser.add_argument("--option")
    args = parser.parse_args(remaining_argv)
    print "Option is \"{}\"".format(args.option)
    return(0)

if __name__ == "__main__":
    sys.exit(main())

20
আমাকে উপরের কোডটি পুনঃব্যবহার করে উপরের দিকে জিজ্ঞাসা করা হয়েছে এবং আমি এটিকে এটি পবিক ডোমেনে রেখেছি।
ভন

22
'পাবিক ডোমেন' আমাকে হাসায়। আমি কেবল বোকা ছেলে।
সিলভাইনড

4
আরগ! এটি সত্যিই দুর্দান্ত কোড, তবে কমান্ড লাইনের মাধ্যমে ওভারড্রিন করা বৈশিষ্ট্যের SafeConfigParser ইন্টারপোলটিং কাজ করে না । উদাহরণস্বরূপ, আপনি যদি অর্গপার্স-পার্টিশিয়াল কোডফিগটিতে নিম্নলিখিত লাইনটি যুক্ত করেন another=%(option)s you are cruelতবে কমান্ড লাইনের অন্য কোনও কিছুকে ওভাররাইড করা হলেও anotherসর্বদা সমাধান করা উচিত .. আরগহ-পার্সার! Hello world you are crueloption
ইহাদান্নি

দ্রষ্টব্য যে নামগুলির মধ্যে ড্যাশ বা আন্ডারস্কোর না থাকলে সেট_ডেফল্টগুলি কেবলমাত্র কাজ করে। সুতরাং কেউ --my-var (যা দুর্ভাগ্যক্রমে, বেশ কুৎসিত) এর পরিবর্তে --myVar বেছে নিতে পারে। কনফিগার ফাইলের ক্ষেত্রে কেস-সংবেদনশীলতা সক্ষম করতে, ফাইল পার্সিংয়ের আগে config.optionxform = str ব্যবহার করুন, তাই মাইভার মাইভারে রূপান্তরিত হয় না।
কেভিন Bader

4
মনে রাখবেন যে আপনি যদি --versionআপনার অ্যাপ্লিকেশনটিতে বিকল্প যুক্ত করতে চান তবে মুদ্রণ সাহায্যের পরে অ্যাপ্লিকেশনটি যুক্ত করে প্রস্থান করার conf_parserচেয়ে এটি যুক্ত করা ভাল parser। আপনি যদি যুক্ত --versionহন parserএবং আপনি --versionপতাকাটি দিয়ে অ্যাপ্লিকেশন শুরু করেন তবে আপনার অ্যাপ্লিকেশনটি অযথা args.conf_fileকনফিগারেশন ফাইলটি খোলার এবং পার্স করার চেষ্টা করবেন (যা ত্রুটিযুক্ত বা এমনকি অস্তিত্বহীন হতে পারে যা ব্যতিক্রমের দিকে পরিচালিত করে)।
patryk.beza

21

পরীক্ষা করে দেখুন ConfigArgParse তার একটি নতুন PyPI প্যাকেজ (- ওপেন সোর্স ) যে কনফিগ ফাইল ও বিভিন্ন পরিবেশের জন্য অতিরিক্ত সমর্থন argparse জন্য প্রতিস্থাপন মধ্যে একটি ড্রপ হিসেবে কাজ করে।


4
এটি চেষ্টা করে দেখুন এবং বুদ্ধি দুর্দান্ত কাজ করে :) এটি নির্দেশ করার জন্য ধন্যবাদ।
red_tiger

4
ধন্যবাদ - ভাল লাগছে! সেই ওয়েব পৃষ্ঠায় আরগপার্স, কনফার্গ পার্স, অ্যাপসেটিংস, আরগপারস_সিএনফিগ, ইকনফ, হাইয়ারপট এবং কনফিগারেশন সহ অন্যান্য বিকল্পগুলির সাথে কনফিগারআর্গ পার্সির তুলনা করা হয়েছে
নীলামকবি

9

আমি এই জাতীয় কাজগুলি পরিচালনা করতে কনফিগারেশন পার্সার এবং সাবকম্যান্ডগুলি সহ আরগপার্স ব্যবহার করছি। নীচের কোডে গুরুত্বপূর্ণ লাইনটি হ'ল:

subp.set_defaults(**dict(conffile.items(subn)))

এটি কনফিগার ফাইলের বিভাগে সাবকম্যান্ডের ডিফল্ট (আরগপার্স থেকে) মানগুলিতে সেট করবে।

আরও একটি সম্পূর্ণ উদাহরণ নীচে:

####### content of example.cfg:
# [sub1]
# verbosity=10
# gggg=3.5
# [sub2]
# host=localhost

import ConfigParser
import argparse

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser_sub1 = subparsers.add_parser('sub1')
parser_sub1.add_argument('-V','--verbosity', type=int, dest='verbosity')
parser_sub1.add_argument('-G', type=float, dest='gggg')

parser_sub2 = subparsers.add_parser('sub2')
parser_sub2.add_argument('-H','--host', dest='host')

conffile = ConfigParser.SafeConfigParser()
conffile.read('example.cfg')

for subp, subn in ((parser_sub1, "sub1"), (parser_sub2, "sub2")):
    subp.set_defaults(**dict(conffile.items(subn)))

print parser.parse_args(['sub1',])
# Namespace(gggg=3.5, verbosity=10)
print parser.parse_args(['sub1', '-V', '20'])
# Namespace(gggg=3.5, verbosity=20)
print parser.parse_args(['sub1', '-V', '20', '-G','42'])
# Namespace(gggg=42.0, verbosity=20)
print parser.parse_args(['sub2', '-H', 'www.example.com'])
# Namespace(host='www.example.com')
print parser.parse_args(['sub2',])
# Namespace(host='localhost')

আমার সমস্যাটি হ'ল আরগপারস কনফিগার ফাইলের ফাইলটি সেট করে এবং কনফিগারেশন ফাইলটি অর্গপার্স ডিফল্ট সেট করে ... বোকা মুরগির ডিমের সমস্যা
olivervbk

4

আমি এটি সেরা উপায় বলতে পারি না, তবে আমার কাছে একটি অপশনপার্সার ক্লাস রয়েছে যা আমি এটি করেছি - অপ্টপার্সের মতো কাজ করে a অপশনপার্সার একটি কনফিগার ফাইল বিভাগ থেকে আগত ডিফল্ট হিসাবে। আপনি এটি পেতে পারেন ...

class OptionParser(optparse.OptionParser):
    def __init__(self, **kwargs):
        import sys
        import os
        config_file = kwargs.pop('config_file',
                                 os.path.splitext(os.path.basename(sys.argv[0]))[0] + '.config')
        self.config_section = kwargs.pop('config_section', 'OPTIONS')

        self.configParser = ConfigParser()
        self.configParser.read(config_file)

        optparse.OptionParser.__init__(self, **kwargs)

    def add_option(self, *args, **kwargs):
        option = optparse.OptionParser.add_option(self, *args, **kwargs)
        name = option.get_opt_string()
        if name.startswith('--'):
            name = name[2:]
            if self.configParser.has_option(self.config_section, name):
                self.set_default(name, self.configParser.get(self.config_section, name))

উত্স ব্রাউজ নির্দ্বিধায় । টেস্টগুলি একটি সহোদর ডিরেক্টরিতে রয়েছে।


3

আপনি চ্যানম্যাপ ব্যবহার করতে পারেন

A ChainMap groups multiple dicts or other mappings together to create a single, updateable view. If no maps are specified, a single empty dictionary is provided so that a new chain always has at least one mapping.

আপনি কমান্ড লাইন, পরিবেশের ভেরিয়েবল, কনফিগারেশন ফাইল এবং মানটি যদি মান না থাকে তবে একটি ডিফল্ট মান নির্ধারণ করে can

import os
from collections import ChainMap, defaultdict

options = ChainMap(command_line_options, os.environ, config_file_options,
               defaultdict(lambda: 'default-value'))
value = options['optname']
value2 = options['other-option']


print(value, value2)
'optvalue', 'default-value'

চেইনম্যাপের ওপরে কী সুবিধা dictsহবে অগ্রাধিকার ক্রমে পছন্দসইভাবে আপডেট হওয়া একটি চেইন ? সঙ্গে defaultdictসেখানে সম্ভবত এর একটি সুবিধা উপন্যাস বা অসমর্থিত অপশন সেট করা যেতে পারে কিন্তু যে এর থেকে আলাদা হিসাবে ChainMap। আমি ধরে নিচ্ছি আমি কিছু মিস করছি।
ড্যান

2

আপডেট: এই উত্তরে এখনও সমস্যা আছে; উদাহরণস্বরূপ, এটি requiredআর্গুমেন্টগুলি পরিচালনা করতে পারে না এবং এর জন্য একটি বিশ্রী কনফিগার সিনট্যাক্স প্রয়োজন। পরিবর্তে, কনফিগারআর্গ পার্স মনে হচ্ছে ঠিক এই প্রশ্নটি যা জিজ্ঞাসা করেছে, এবং এটি একটি স্বচ্ছ, ড্রপ-ইন প্রতিস্থাপন।

বর্তমানের সাথে একটি সমস্যা হ'ল কনফিগার ফাইলে আর্গুমেন্ট অবৈধ থাকলে এটি ত্রুটি করবে না। এখানে একটি ভিন্ন নেতিবাচক সংস্করণ রয়েছে: আপনাকে কীগুলিতে --বা -উপসর্গটি অন্তর্ভুক্ত করতে হবে ।

অজগর কোডটি এখানে রয়েছে ( এমআইটি লাইসেন্সের সাথে সংক্ষেপে লিঙ্ক ):

# Filename: main.py
import argparse

import configparser

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--config_file', help='config file')
    args, left_argv = parser.parse_known_args()
    if args.config_file:
        with open(args.config_file, 'r') as f:
            config = configparser.SafeConfigParser()
            config.read([args.config_file])

    parser.add_argument('--arg1', help='argument 1')
    parser.add_argument('--arg2', type=int, help='argument 2')

    for k, v in config.items("Defaults"):
        parser.parse_args([str(k), str(v)], args)

    parser.parse_args(left_argv, args)
print(args)

এখানে একটি কনফিগার ফাইলের উদাহরণ রয়েছে:

# Filename: config_correct.conf
[Defaults]
--arg1=Hello!
--arg2=3

এখন, চলছে

> python main.py --config_file config_correct.conf --arg1 override
Namespace(arg1='override', arg2=3, config_file='test_argparse.conf')

তবে, আমাদের কনফিগারেশনের ফাইলটিতে যদি ত্রুটি থাকে:

# config_invalid.conf
--arg1=Hello!
--arg2='not an integer!'

স্ক্রিপ্টটি চালানো তত্ক্ষেত্র হিসাবে ত্রুটি তৈরি করবে:

> python main.py --config_file config_invalid.conf --arg1 override
usage: test_argparse_conf.py [-h] [--config_file CONFIG_FILE] [--arg1 ARG1]
                             [--arg2 ARG2]
main.py: error: argument --arg2: invalid int value: 'not an integer!'

মূল parser.parse_argsক্ষতিটি হ'ল এটি আর্গুমেন্ট পার্সার থেকে ত্রুটি পরীক্ষা করার জন্য কিছুটা হ্যাকলি ব্যবহার করে তবে আমি এর কোনও বিকল্প সম্পর্কে অবগত নই।


2

fromfile_prefix_chars

হতে পারে নিখুঁত এপিআই নয়, তবে এটি সম্পর্কে জানার পক্ষে মূল্যবান।

main.py

#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('-a', default=13)
parser.add_argument('-b', default=42)
print(parser.parse_args())

তারপরে:

$ printf -- '-a\n1\n-b\n2\n' > opts.txt
$ ./main.py
Namespace(a=13, b=42)
$ ./main.py @opts.txt
Namespace(a='1', b='2')
$ ./main.py @opts.txt -a 3 -b 4
Namespace(a='3', b='4')
$ ./main.py -a 3 -b 4 @opts.txt
Namespace(a='1', b='2')

ডকুমেন্টেশন: https://docs.python.org/3.6/library/argparse.html#fromfile-prefix-chars

পাইথন 3.6.5, উবুন্টু 18.04 এ পরীক্ষিত।


1

এইভাবে চেষ্টা করুন

# encoding: utf-8
import imp
import argparse


class LoadConfigAction(argparse._StoreAction):
    NIL = object()

    def __init__(self, option_strings, dest, **kwargs):
        super(self.__class__, self).__init__(option_strings, dest)
        self.help = "Load configuration from file"

    def __call__(self, parser, namespace, values, option_string=None):
        super(LoadConfigAction, self).__call__(parser, namespace, values, option_string)

        config = imp.load_source('config', values)

        for key in (set(map(lambda x: x.dest, parser._actions)) & set(dir(config))):
            setattr(namespace, key, getattr(config, key))

এটা ব্যবহার করো:

parser.add_argument("-C", "--config", action=LoadConfigAction)
parser.add_argument("-H", "--host", dest="host")

এবং উদাহরণ কনফিগারেশন তৈরি করুন:

# Example config: /etc/myservice.conf
import os
host = os.getenv("HOST_NAME", "localhost")
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.