committed by
GitHub
5 changed files with 252 additions and 4 deletions
@ -0,0 +1,9 @@ |
|||||
|
# Runtime configuration file |
||||
|
# For all available options copy thefile located at |
||||
|
# "<Application-Home>/init/runtime-config.ini" to |
||||
|
# "<Application-Home>/config.ini" |
||||
|
# and edit it to fit your needs. |
||||
|
|
||||
|
|
||||
|
[default] |
||||
|
daemon: |
@ -0,0 +1,25 @@ |
|||||
|
# A template for the CouchPotatoServer config.ini file |
||||
|
|
||||
|
# Do not change the [default] section heading |
||||
|
[default] |
||||
|
|
||||
|
# Uncomment to run CouchPotato as a daemon |
||||
|
# daemon: |
||||
|
|
||||
|
# Uncomment to specify CouchPotato's data directory |
||||
|
# data_dir: /apps/configs/couchpotato |
||||
|
|
||||
|
# Uncomment to specify the location of CouchPotato's settings file |
||||
|
# config_file: /home/couchpotato/settings.conf |
||||
|
|
||||
|
# Uncomment to specifiy the location of the PID file |
||||
|
# pid_file: /home/couchpotato/cp.pid |
||||
|
|
||||
|
# Uncomment to run CP in debug mode |
||||
|
# debug: |
||||
|
|
||||
|
# Uncomment to log messages to the console instead of the log files |
||||
|
# console_log: |
||||
|
|
||||
|
# Uncomment to limit logging |
||||
|
# quiet: |
@ -0,0 +1,209 @@ |
|||||
|
""" |
||||
|
Author: tikitu |
||||
|
""" |
||||
|
import ConfigParser |
||||
|
from argparse import _SubParsersAction, _StoreAction, _StoreConstAction |
||||
|
import argparse |
||||
|
|
||||
|
__version__ = '0.5.1' |
||||
|
|
||||
|
|
||||
|
def get_config_parser(filename): |
||||
|
config_parser = ConfigParser.SafeConfigParser(allow_no_value=True) |
||||
|
config_parser.read([filename]) |
||||
|
return config_parser |
||||
|
|
||||
|
|
||||
|
def read_config_file(arg_parser, filename): |
||||
|
config_parser = get_config_parser(filename) |
||||
|
read_config_parser(arg_parser, config_parser) |
||||
|
|
||||
|
|
||||
|
def read_config_parser(arg_parser, config_parser): |
||||
|
ReadConfig(config_parser=config_parser).walk_parser(arg_parser) |
||||
|
|
||||
|
|
||||
|
def add_config_block_subcommand(arg_parser, subparsers, |
||||
|
config_parser=None, |
||||
|
only_non_defaults=False): |
||||
|
""" |
||||
|
Add a subcommand "config-block" to the arg_parser, to be used as follows: |
||||
|
|
||||
|
In myprog.py: |
||||
|
|
||||
|
subparsers = arg_parser.add_subparsers(..., dest='command') |
||||
|
add_config_block_subcommand(arg_parser, subparsers) |
||||
|
# ... |
||||
|
parsed_args = arg_parser.parse_args() |
||||
|
if parsed_args.command == 'config': |
||||
|
print parsed_args.func(parsed_args) |
||||
|
exit(0) |
||||
|
|
||||
|
On the commandline: |
||||
|
|
||||
|
$ myprog.py config default --username tikitu --secret xyzzy |
||||
|
[default] |
||||
|
username: tikitu |
||||
|
secret: xyzzy |
||||
|
|
||||
|
:param arg_parser: |
||||
|
:param config_parser: |
||||
|
:param dest: |
||||
|
:param only_non_defaults: |
||||
|
:return: None |
||||
|
""" |
||||
|
config_command_parser = subparsers.add_parser('config') |
||||
|
config_command_parser.add_argument('block') |
||||
|
config_command_parser.add_argument('commandline', nargs=argparse.REMAINDER) |
||||
|
|
||||
|
def handle_args(orig_parsed_args): |
||||
|
if config_parser is not None: |
||||
|
read_config_parser(arg_parser, config_parser) |
||||
|
args_for_commandline = list(orig_parsed_args.commandline) |
||||
|
if orig_parsed_args.block == 'default': |
||||
|
subparsers.add_parser('dummy-command') |
||||
|
args_for_commandline.append('dummy-command') |
||||
|
else: |
||||
|
args_for_commandline.insert(0, orig_parsed_args.block) |
||||
|
parsed_args = arg_parser.parse_args(args_for_commandline) |
||||
|
return generate_config(arg_parser, parsed_args, |
||||
|
section=orig_parsed_args.block, |
||||
|
only_non_defaults=only_non_defaults) |
||||
|
|
||||
|
config_command_parser.set_defaults(func=handle_args) |
||||
|
|
||||
|
|
||||
|
def generate_config(arg_parser, parsed_args, section='default', |
||||
|
only_non_defaults=False): |
||||
|
action = GenerateConfig(parsed_args, section, |
||||
|
only_non_defaults=only_non_defaults) |
||||
|
action.walk_parser(arg_parser) |
||||
|
return action.contents |
||||
|
|
||||
|
|
||||
|
class ArgParserWalker(object): |
||||
|
def start_section(self, section_name): |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
def end_section(self): |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
def process_parser_action(self, action, is_store_const=False): |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
def walk_parser(self, arg_parser): |
||||
|
try: |
||||
|
self.start_section('default') |
||||
|
for action in arg_parser._actions: |
||||
|
if isinstance(action, _StoreAction): |
||||
|
self.process_parser_action(action) |
||||
|
elif isinstance(action, _StoreConstAction): |
||||
|
self.process_parser_action(action, is_store_const=True) |
||||
|
elif isinstance(action, _SubParsersAction): |
||||
|
for command, sub_parser in action.choices.items(): |
||||
|
self.start_section(command) |
||||
|
for sub_action in sub_parser._actions: |
||||
|
self.process_parser_action(sub_action) |
||||
|
self.end_section() |
||||
|
self.end_section() |
||||
|
except DefaultError as e: |
||||
|
arg_parser.error( |
||||
|
u'[{section_name}] config option "{option_string}" ' |
||||
|
u'must be {type_transformer}() value, got: {value}'.format( |
||||
|
section_name=e.section_name, |
||||
|
option_string=e.option_string, |
||||
|
type_transformer=e.type_transformer.__name__, |
||||
|
value=e.value |
||||
|
)) |
||||
|
|
||||
|
|
||||
|
class GenerateConfig(ArgParserWalker): |
||||
|
def __init__(self, parsed_args, section, only_non_defaults=False): |
||||
|
self.parsed_args = parsed_args |
||||
|
self._contents = [] |
||||
|
self._only_non_defaults = only_non_defaults |
||||
|
self._section = section |
||||
|
self._in_sections = [] |
||||
|
|
||||
|
def start_section(self, section_name): |
||||
|
self._in_sections.append(section_name) |
||||
|
if section_name == self._section: |
||||
|
if self._contents: |
||||
|
self._contents.append(u'') |
||||
|
self._contents.append(u'[{0}]'.format(section_name)) |
||||
|
|
||||
|
def end_section(self): |
||||
|
self._in_sections.pop() |
||||
|
|
||||
|
@property |
||||
|
def contents(self): |
||||
|
return u'\n'.join(self._contents + [u'']) |
||||
|
|
||||
|
def process_parser_action(self, action, is_store_const=False): |
||||
|
if self._in_sections[-1] != self._section: |
||||
|
return |
||||
|
# take the longest string, likely the most informative |
||||
|
action_name = list(action.option_strings) |
||||
|
action_name.sort(key=lambda s: len(s), reverse=True) |
||||
|
action_name = _convert_option_string(action_name[0]) |
||||
|
|
||||
|
action_value = getattr(self.parsed_args, action.dest, None) |
||||
|
if self._only_non_defaults and action_value == action.default: |
||||
|
action_value = None |
||||
|
if action_value is not None: |
||||
|
if is_store_const: |
||||
|
self._contents.append(action_name) |
||||
|
else: |
||||
|
self._contents.append(u'{action_name}: {default_value}'.format( |
||||
|
action_name=action_name, |
||||
|
default_value=action_value, # hope it prints as wanted... |
||||
|
)) |
||||
|
|
||||
|
|
||||
|
class ReadConfig(ArgParserWalker): |
||||
|
def __init__(self, config_parser=None): |
||||
|
self.sections = [] |
||||
|
self.config_parser = config_parser |
||||
|
|
||||
|
def start_section(self, section_name): |
||||
|
self.sections.append(section_name) |
||||
|
|
||||
|
def end_section(self): |
||||
|
self.sections.pop() |
||||
|
|
||||
|
@property |
||||
|
def current_section(self): |
||||
|
return self.sections[-1] if self.sections else None |
||||
|
|
||||
|
def process_parser_action(self, action, is_store_const=False): |
||||
|
for option_string in action.option_strings: |
||||
|
option_string = _convert_option_string(option_string) |
||||
|
if self.config_parser.has_option(self.current_section, |
||||
|
option_string): |
||||
|
if is_store_const: |
||||
|
action.default = action.const |
||||
|
else: |
||||
|
value = self.config_parser.get(self.current_section, |
||||
|
option_string) |
||||
|
type_transformer = (action.type if action.type is not None |
||||
|
else lambda x: x) |
||||
|
try: |
||||
|
action.default = type_transformer(value) |
||||
|
except: |
||||
|
raise DefaultError(self.current_section, |
||||
|
option_string, |
||||
|
value, |
||||
|
type_transformer) |
||||
|
action.required = False |
||||
|
|
||||
|
|
||||
|
class DefaultError(Exception): |
||||
|
def __init__(self, section_name, option_string, value, type_transformer): |
||||
|
self.section_name = section_name |
||||
|
self.option_string = option_string |
||||
|
self.value = value |
||||
|
self.type_transformer = type_transformer |
||||
|
|
||||
|
|
||||
|
def _convert_option_string(op_s): |
||||
|
return op_s.lstrip('-').replace('-', '_') |
Loading…
Reference in new issue