Skip to content

Commit 7aebff7

Browse files
author
Alexander Smishlajev
committed
implement getopt().
support config file paths as well as directory paths for loading. UserConfig: defaults were added to all config sections. filter them out.
1 parent 43b59b5 commit 7aebff7

File tree

1 file changed

+110
-47
lines changed

1 file changed

+110
-47
lines changed

roundup/configuration.py

Lines changed: 110 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Roundup Issue Tracker configuration support
22
#
3-
# $Id: configuration.py,v 1.16 2004-07-28 09:46:58 a1s Exp $
3+
# $Id: configuration.py,v 1.17 2004-10-17 17:35:32 a1s Exp $
44
#
55
__docformat__ = "restructuredtext"
66

7+
import getopt
78
import imp
89
import os
910
import time
@@ -303,7 +304,7 @@ class FilePathOption(Option):
303304
"""
304305

305306
class_description = "The path may be either absolute or relative\n" \
306-
" to the directory containig this config file."
307+
"to the directory containig this config file."
307308

308309
def get(self):
309310
_val = Option.get(self)
@@ -588,15 +589,18 @@ class Config:
588589
section_options = None
589590
# mapping from option names and aliases to Option instances
590591
options = None
592+
# actual name of the config file. set on load.
593+
filepath = os.path.join(HOME, INI_FILE)
591594

592-
def __init__(self, home_dir=None, layout=None):
595+
def __init__(self, config_path=None, layout=None):
593596
"""Initialize confing instance
594597
595598
Parameters:
596-
home_dir:
597-
optional configuration directory.
598-
If passed, load the config from that directory
599-
after processing config layout (if any).
599+
config_path:
600+
optional directory or file name of the config file.
601+
If passed, load the config after processing layout (if any).
602+
If config_path is a directory name, use default base name
603+
of the config file.
600604
layout:
601605
optional configuration layout, a sequence of
602606
section definitions suitable for .add_section()
@@ -611,8 +615,8 @@ def __init__(self, home_dir=None, layout=None):
611615
if layout:
612616
for section in layout:
613617
self.add_section(*section)
614-
if home_dir is not None:
615-
self.load(home_dir)
618+
if config_path is not None:
619+
self.load(config_path)
616620

617621
def add_section(self, section, options, description=None):
618622
"""Define new config section
@@ -698,16 +702,30 @@ def reset(self):
698702
for _option in self.items():
699703
_option.reset()
700704

701-
# This is a placeholder. TBD.
702705
# Meant for commandline tools.
703706
# Allows automatic creation of configuration files like this:
704707
# roundup-server -p 8017 -u roundup --save-config
705-
def getopt(self, args, **options):
708+
def getopt(self, args, short_options="", long_options=(),
709+
config_load_options=("C", "config"), **options
710+
):
706711
"""Apply options specified in command line arguments.
707712
708713
Parameters:
709714
args:
710715
command line to parse (sys.argv[1:])
716+
short_options:
717+
optional string of letters for command line options
718+
that are not config options
719+
long_options:
720+
optional list of names for long options
721+
that are not config options
722+
config_load_options:
723+
two-element sequence (letter, long_option) defining
724+
the options for config file. If unset, don't load
725+
config file; otherwise config file is read prior
726+
to applying other options. Short option letter
727+
must not have a colon and long_option name must
728+
not have an equal sign or '--' prefix.
711729
options:
712730
mapping from option names to command line option specs.
713731
e.g. server_port="p:", server_user="u:"
@@ -720,6 +738,53 @@ def getopt(self, args, **options):
720738
processed options are removed from returned option list.
721739
722740
"""
741+
# take a copy of long_options
742+
long_options = list(long_options)
743+
# build option lists
744+
cfg_names = {}
745+
booleans = []
746+
for (name, letter) in options.items():
747+
cfg_name = name.upper()
748+
short_opt = "-" + letter[0]
749+
name = name.lower().replace("_", "-")
750+
cfg_names.update({short_opt: cfg_name, "--" + name: cfg_name})
751+
752+
short_options += letter
753+
if letter[-1] == ":":
754+
long_options.append(name + "=")
755+
else:
756+
booleans.append(short_opt)
757+
long_options.append(name)
758+
759+
if config_load_options:
760+
short_options += config_load_options[0] + ":"
761+
long_options.append(config_load_options[1] + "=")
762+
# compute names that will be searched in getopt return value
763+
config_load_options = (
764+
"-" + config_load_options[0],
765+
"--" + config_load_options[1],
766+
)
767+
# parse command line arguments
768+
optlist, args = getopt.getopt(args, short_options, long_options)
769+
# load config file if requested
770+
if config_load_options:
771+
for option in optlist:
772+
if option[0] in config_load_options:
773+
self.load_ini(option[1])
774+
optlist.remove(option)
775+
break
776+
# apply options
777+
extra_options = []
778+
for (opt, arg) in optlist:
779+
if (opt in booleans): # and not arg
780+
arg = "yes"
781+
try:
782+
name = cfg_names[opt]
783+
except KeyError:
784+
extra_options.append((opt, arg))
785+
else:
786+
self[name] = arg
787+
return (extra_options, args)
723788

724789
# option and section locators (used in option access methods)
725790

@@ -746,30 +811,48 @@ def _get_unset_options(self):
746811
need_set.setdefault(option.section, []).append(option.setting)
747812
return need_set
748813

814+
def _adjust_options(self, config):
815+
"""Load ad-hoc option definitions from ConfigParser instance."""
816+
pass
817+
818+
def _get_name(self):
819+
"""Return the service name for config file heading"""
820+
return ""
821+
749822
# file operations
750823

751-
def load_ini(self, home_dir, defaults=None):
824+
def load_ini(self, config_path, defaults=None):
752825
"""Set options from config.ini file in given home_dir
753826
754827
Parameters:
755-
home_dir:
756-
config home directory
828+
config_path:
829+
directory or file name of the config file.
830+
If config_path is a directory name, use default
831+
base name of the config file
757832
defaults:
758833
optional dictionary of defaults for ConfigParser
759834
760835
Note: if home_dir does not contain config.ini file,
761836
no error is raised. Config will be reset to defaults.
762837
763838
"""
839+
if os.path.isdir(config_path):
840+
home_dir = config_path
841+
config_path = os.path.join(config_path, self.INI_FILE)
842+
else:
843+
home_dir = os.path.dirname(config_path)
764844
# parse the file
765845
config_defaults = {"HOME": home_dir}
766846
if defaults:
767847
config_defaults.update(defaults)
768848
config = ConfigParser.ConfigParser(config_defaults)
769849
config.read([os.path.join(home_dir, self.INI_FILE)])
770-
# .ini file loaded ok. set the options, starting from HOME
771-
self.reset()
850+
# .ini file loaded ok.
772851
self.HOME = home_dir
852+
self.filepath = config_path
853+
self._adjust_options(config)
854+
# set the options, starting from HOME
855+
self.reset()
773856
for option in self.items():
774857
option.load_ini(config)
775858

@@ -789,12 +872,12 @@ def save(self, ini_file=None):
789872
790873
"""
791874
if ini_file is None:
792-
ini_file = os.path.join(self.HOME, self.INI_FILE)
875+
ini_file = self.filepath
793876
_tmp_file = os.path.splitext(ini_file)[0]
794877
_bak_file = _tmp_file + ".bak"
795878
_tmp_file = _tmp_file + ".tmp"
796879
_fp = file(_tmp_file, "wt")
797-
_fp.write("# %s configuration file\n" % self["TRACKER_NAME"])
880+
_fp.write("# %s configuration file\n" % self._get_name())
798881
_fp.write("# Autogenerated at %s\n" % time.asctime())
799882
need_set = self._get_unset_options()
800883
if need_set:
@@ -891,40 +974,17 @@ class UserConfig(Config):
891974
892975
"""
893976

894-
def load_ini(self, home_dir, defaults=None):
895-
"""Load options from config.ini file in given home_dir
896-
897-
Parameters:
898-
home_dir:
899-
config home directory
900-
defaults:
901-
optional dictionary of defaults for ConfigParser
902-
903-
Options are automatically created as they are read
904-
from the config file.
905-
906-
Note: if home_dir does not contain config.ini file,
907-
no error is raised. Config will be reset to defaults.
908-
909-
"""
910-
# parse the file
911-
config_defaults = {"HOME": home_dir}
912-
if defaults:
913-
config_defaults.update(defaults)
914-
config = ConfigParser.ConfigParser(defaults)
915-
config.read([os.path.join(home_dir, self.INI_FILE)])
916-
# .ini file loaded ok.
917-
self.HOME = home_dir
977+
def _adjust_options(self, config):
978+
# config defaults appear in all sections.
979+
# we'll need to filter them out.
980+
defaults = config.defaults().keys()
918981
# see what options are already defined and add missing ones
919982
preset = [(option.section, option.setting) for option in self.items()]
920983
for section in config.sections():
921984
for name in config.options(section):
922-
if (section, name) not in preset:
985+
if ((section, name) not in preset) \
986+
and (name not in defaults):
923987
self.add_option(Option(self, section, name))
924-
# set the options
925-
self.reset()
926-
for option in self.items():
927-
option.load_ini(config)
928988

929989
class CoreConfig(Config):
930990

@@ -968,6 +1028,9 @@ def _get_unset_options(self):
9681028
del need_set["mail"]
9691029
return need_set
9701030

1031+
def _get_name(self):
1032+
return self["TRACKER_NAME"]
1033+
9711034
def reset(self):
9721035
Config.reset(self)
9731036
if self.ext:

0 commit comments

Comments
 (0)