codekingpro/portable-devtools
115k
1from distutils.util import convert_path2from distutils import log3from distutils.errors import DistutilsOptionError4import distutils5import os6import configparser7 8from setuptools import Command9 10__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']11 12 13def config_file(kind="local"):14 """Get the filename of the distutils, local, global, or per-user config15 16 `kind` must be one of "local", "global", or "user"17 """18 if kind == 'local':19 return 'setup.cfg'20 if kind == 'global':21 return os.path.join(os.path.dirname(distutils.__file__), 'distutils.cfg')22 if kind == 'user':23 dot = os.name == 'posix' and '.' or ''24 return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))25 raise ValueError("config_file() type must be 'local', 'global', or 'user'", kind)26 27 28def edit_config(filename, settings, dry_run=False):29 """Edit a configuration file to include `settings`30 31 `settings` is a dictionary of dictionaries or ``None`` values, keyed by32 command/section name. A ``None`` value means to delete the entire section,33 while a dictionary lists settings to be changed or deleted in that section.34 A setting of ``None`` means to delete that setting.35 """36 log.debug("Reading configuration from %s", filename)37 opts = configparser.RawConfigParser()38 opts.optionxform = lambda x: x39 opts.read([filename])40 for section, options in settings.items():41 if options is None:42 log.info("Deleting section [%s] from %s", section, filename)43 opts.remove_section(section)44 else:45 if not opts.has_section(section):46 log.debug("Adding new section [%s] to %s", section, filename)47 opts.add_section(section)48 for option, value in options.items():49 if value is None:50 log.debug("Deleting %s.%s from %s", section, option, filename)51 opts.remove_option(section, option)52 if not opts.options(section):53 log.info(54 "Deleting empty [%s] section from %s", section, filename55 )56 opts.remove_section(section)57 else:58 log.debug(59 "Setting %s.%s to %r in %s", section, option, value, filename60 )61 opts.set(section, option, value)62 63 log.info("Writing %s", filename)64 if not dry_run:65 with open(filename, 'w') as f:66 opts.write(f)67 68 69class option_base(Command):70 """Abstract base class for commands that mess with config files"""71 72 user_options = [73 ('global-config', 'g', "save options to the site-wide distutils.cfg file"),74 ('user-config', 'u', "save options to the current user's pydistutils.cfg file"),75 ('filename=', 'f', "configuration file to use (default=setup.cfg)"),76 ]77 78 boolean_options = [79 'global-config',80 'user-config',81 ]82 83 def initialize_options(self):84 self.global_config = None85 self.user_config = None86 self.filename = None87 88 def finalize_options(self):89 filenames = []90 if self.global_config:91 filenames.append(config_file('global'))92 if self.user_config:93 filenames.append(config_file('user'))94 if self.filename is not None:95 filenames.append(self.filename)96 if not filenames:97 filenames.append(config_file('local'))98 if len(filenames) > 1:99 raise DistutilsOptionError(100 "Must specify only one configuration file option", filenames101 )102 (self.filename,) = filenames103 104 105class setopt(option_base):106 """Save command-line options to a file"""107 108 description = "set an option in setup.cfg or another config file"109 110 user_options = [111 ('command=', 'c', 'command to set an option for'),112 ('option=', 'o', 'option to set'),113 ('set-value=', 's', 'value of the option'),114 ('remove', 'r', 'remove (unset) the value'),115 ] + option_base.user_options116 117 boolean_options = option_base.boolean_options + ['remove']118 119 def initialize_options(self):120 option_base.initialize_options(self)121 self.command = None122 self.option = None123 self.set_value = None124 self.remove = None125 126 def finalize_options(self):127 option_base.finalize_options(self)128 if self.command is None or self.option is None:129 raise DistutilsOptionError("Must specify --command *and* --option")130 if self.set_value is None and not self.remove:131 raise DistutilsOptionError("Must specify --set-value or --remove")132 133 def run(self):134 edit_config(135 self.filename,136 {self.command: {self.option.replace('-', '_'): self.set_value}},137 self.dry_run,138 )139 