summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJoshua Harlow <harlowja@yahoo-inc.com>2014-01-06 10:17:59 -0800
committerJoshua Harlow <harlowja@yahoo-inc.com>2014-01-09 11:09:08 -0800
commit3d437df7497827ed46624403bc19d72cd1935be9 (patch)
tree42d31c90bffc1fce889031ecfae689d87c547cf9 /tools
parentd617864898ec40d89ca040836fdcfaf4d6d583c1 (diff)
downloadtaskflow-3d437df7497827ed46624403bc19d72cd1935be9.tar.gz
Add envs for different sqlalchemy versions
Adjust tests to skip the sqlalchemy test if sqlalchemy is not installed. Adjust examples to fallback to a directory based backend if the sqlalchemy does not load or is not available. Include a updated tox.ini (generated from the toxgen.py script) that includes the new venv variations. Change-Id: I7686f09901a9b65d7c81b4e037b5bffc24aa7ef7
Diffstat (limited to 'tools')
-rwxr-xr-xtools/toxgen.py211
1 files changed, 211 insertions, 0 deletions
diff --git a/tools/toxgen.py b/tools/toxgen.py
new file mode 100755
index 0000000..111ed9b
--- /dev/null
+++ b/tools/toxgen.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+
+# From: https://bitbucket.org/cdevienne/toxgen (pypi soon hopefully) and
+# modified slightly to work in python 2.6 and set some values that are not
+# being set.
+#
+# TODO(harlowja): remove me when toxgen is a pypi package.
+
+"""
+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.
+"""
+
+import collections
+import itertools
+import optparse
+import os
+
+import six
+
+from six.moves import configparser
+
+try:
+ from collections import OrderedDict
+except ImportError:
+ from ordereddict import OrderedDict
+
+
+HEADER = '# DO NOT EDIT THIS FILE - it is machine generated from %(filename)s'
+SKIP_VENVS = frozenset(['venv'])
+
+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 = dict(config.items(sectionname))
+ else:
+ self.options = dict()
+
+ 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 = dict()
+ values = config.get('axes', self.name).split(',')
+ if config.has_section('axis:%s' % self.name):
+ self.defaults = dict(
+ config.items('axis:%s' % self.name)
+ )
+ else:
+ self.defaults = {}
+ for value in values:
+ self.items[value.strip('*')] = AxisItem(self, value, config)
+
+
+def format_list(contents, max_len=80, sep=","):
+ lines = []
+ for line in contents:
+ if not lines:
+ lines.append(line + ",")
+ else:
+ last_len = len(lines[-1])
+ if last_len + len(line) >= max_len:
+ lines.append(str(line) + sep)
+ else:
+ lines[-1] = lines[-1] + str(line) + sep
+ return "\n".join(lines).rstrip(",")
+
+
+def render(incfg, filename, adjust_envlist=True):
+ test_envs = set()
+ for s in incfg.sections():
+ if s.startswith("testenv:"):
+ env = s[len("testenv:"):].strip()
+ if env in SKIP_VENVS or not env:
+ continue
+ test_envs.add(env)
+ test_envs = [s for s in test_envs if s]
+
+ try:
+ envlist = incfg.get("tox", 'envlist')
+ envlist = [e.strip() for e in envlist.split(",")]
+ envlist = set([e for e in envlist if e])
+ except (configparser.NoOptionError, configparser.NoSectionError):
+ envlist = set()
+ for e in test_envs:
+ if e not in envlist:
+ envlist.add(e)
+
+ if not incfg.has_section("tox"):
+ incfg.add_section("tox")
+ incfg.set("tox", "envlist",
+ format_list(list(sorted(envlist)), max_len=-1))
+
+ text = six.StringIO()
+ incfg.write(text)
+ contents = [
+ HEADER % {'filename': os.path.basename(filename)},
+ '',
+ # Remove how configparser uses tabs instead of spaces, madness...
+ text.getvalue().replace("\t", " " * 4),
+ ]
+ return "\n".join(contents)
+
+
+def compile_template(incfg):
+ axes = dict()
+
+ if incfg.has_section('axes'):
+ for axis in incfg.options('axes'):
+ axes[axis] = Axis(axis, incfg)
+
+ out = configparser.ConfigParser(dict_type=OrderedDict)
+ 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)
+
+ items = [axis.items.keys() for axis in axes.values()]
+ for combination in itertools.product(*items):
+ options = {}
+
+ 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.ConfigParser()
+ with open(options.input, 'rb') as fh:
+ tmpl.readfp(fh, filename=options.input)
+ with open(options.output, 'wb') as outfile:
+ text = render(compile_template(tmpl), options.input)
+ outfile.write(text)
+
+
+if __name__ == '__main__':
+ main()
+