145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
"""
|
|
Produce a tox.ini file from a template config file.
|
|
|
|
The template config file is a standard tox.ini file with additional sections.
|
|
Theses sections will be combined to create new testenv: sections if they do
|
|
not exists yet.
|
|
|
|
See REAME.rst for more detail.
|
|
"""
|
|
|
|
import itertools
|
|
import collections
|
|
import optparse
|
|
|
|
try:
|
|
from configparser import ConfigParser
|
|
except:
|
|
from ConfigParser import ConfigParser # noqa
|
|
|
|
|
|
parser = optparse.OptionParser(epilog=__doc__)
|
|
parser.add_option('-i', '--input', dest='input',
|
|
default='tox-tmpl.ini', metavar='FILE')
|
|
parser.add_option('-o', '--output', dest='output',
|
|
default='tox.ini', metavar='FILE')
|
|
|
|
|
|
class AxisItem(object):
|
|
def __init__(self, axis, name, config):
|
|
self.axis = axis
|
|
self.isdefault = name[-1] == '*'
|
|
self.name = name[:-1] if self.isdefault else name
|
|
self.load(config)
|
|
|
|
def load(self, config):
|
|
sectionname = 'axis:%s:%s' % (self.axis.name, self.name)
|
|
if config.has_section(sectionname):
|
|
self.options = collections.OrderedDict(config.items(sectionname))
|
|
else:
|
|
self.options = collections.OrderedDict()
|
|
|
|
for name, value in self.axis.defaults.items():
|
|
if name not in self.options:
|
|
self.options[name] = value
|
|
|
|
|
|
class Axis(object):
|
|
def __init__(self, name, config):
|
|
self.name = name
|
|
self.load(config)
|
|
|
|
def load(self, config):
|
|
self.items = collections.OrderedDict()
|
|
values = config.get('axes', self.name).split(',')
|
|
if config.has_section('axis:%s' % self.name):
|
|
self.defaults = collections.OrderedDict(
|
|
config.items('axis:%s' % self.name)
|
|
)
|
|
else:
|
|
self.defaults = {}
|
|
for value in values:
|
|
self.items[value.strip('*')] = AxisItem(self, value, config)
|
|
|
|
|
|
def render(incfg):
|
|
axes = collections.OrderedDict()
|
|
|
|
if incfg.has_section('axes'):
|
|
for axis in incfg.options('axes'):
|
|
axes[axis] = Axis(axis, incfg)
|
|
|
|
out = ConfigParser()
|
|
for section in incfg.sections():
|
|
if section == 'axes' or section.startswith('axis:'):
|
|
continue
|
|
out.add_section(section)
|
|
for name, value in incfg.items(section):
|
|
out.set(section, name, value)
|
|
|
|
for combination in itertools.product(
|
|
*[axis.items.keys() for axis in axes.values()]):
|
|
options = collections.OrderedDict()
|
|
|
|
section_name = (
|
|
'testenv:' + '-'.join([item for item in combination if item])
|
|
)
|
|
section_alt_name = (
|
|
'testenv:' + '-'.join([
|
|
itemname
|
|
for axis, itemname in zip(axes.values(), combination)
|
|
if itemname and not axis.items[itemname].isdefault
|
|
])
|
|
)
|
|
if section_alt_name == section_name:
|
|
section_alt_name = None
|
|
|
|
axes_items = [
|
|
'%s:%s' % (axis, itemname)
|
|
for axis, itemname in zip(axes, combination)
|
|
]
|
|
|
|
for axis, itemname in zip(axes.values(), combination):
|
|
axis_options = axis.items[itemname].options
|
|
if 'constraints' in axis_options:
|
|
constraints = axis_options['constraints'].split('\n')
|
|
for c in constraints:
|
|
if c.startswith('!') and c[1:] in axes_items:
|
|
continue
|
|
for name, value in axis_options.items():
|
|
if name in options:
|
|
options[name] += value
|
|
else:
|
|
options[name] = value
|
|
|
|
constraints = options.pop('constraints', '').split('\n')
|
|
neg_constraints = [c[1:] for c in constraints if c and c[0] == '!']
|
|
if not set(neg_constraints).isdisjoint(axes_items):
|
|
continue
|
|
|
|
if not out.has_section(section_name):
|
|
out.add_section(section_name)
|
|
|
|
if (section_alt_name and not out.has_section(section_alt_name)):
|
|
out.add_section(section_alt_name)
|
|
|
|
for name, value in reversed(options.items()):
|
|
if not out.has_option(section_name, name):
|
|
out.set(section_name, name, value)
|
|
if section_alt_name and not out.has_option(section_alt_name, name):
|
|
out.set(section_alt_name, name, value)
|
|
|
|
return out
|
|
|
|
|
|
def main():
|
|
options, args = parser.parse_args()
|
|
tmpl = ConfigParser()
|
|
tmpl.read(options.input)
|
|
with open(options.output, 'wb') as outfile:
|
|
render(tmpl).write(outfile)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|