From c8782f7f5213b312eab1485b5ff287b85b0e9950 Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Tue, 18 Sep 2012 20:50:56 -0400 Subject: [PATCH 01/10] Bump to version 0.0.2 and back to build tag -1. --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 11c34b3..10210bd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [egg_info] -tag_build = -2 +tag_build = -1 diff --git a/setup.py b/setup.py index 33ed18d..5b4e8c1 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.0.1", + version = "0.0.2", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"), From a0e9c02c86ff0fca4fd220d1b7c5a99600fcdc28 Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Mon, 1 Oct 2012 09:30:29 -0400 Subject: [PATCH 02/10] Change build tag to "b" --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 10210bd..bbcef9b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [egg_info] -tag_build = -1 +tag_build = b From d5badfebd70391b5a9d9b86308a7120373d7034d Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Tue, 15 Apr 2014 12:39:36 -0400 Subject: [PATCH 03/10] Bump to 0.1.0 Removed alpha status listing in metadata and the "b" build tag. --- changelog.md | 5 +++++ setup.cfg | 3 --- setup.py | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 setup.cfg diff --git a/changelog.md b/changelog.md index eca8305..0a30fda 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +0.1.0 + +* Added log naming helper functions to mandrel.config.helpers +* Eliminated alpha status and "b" build tag. + 0.0.4b * Fixed bug with option handling in mandrel-runner (thanks to Adam Tebbe) diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index bbcef9b..0000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[egg_info] -tag_build = b - diff --git a/setup.py b/setup.py index a679396..aadd344 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.0.4", + version = "0.1.0", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"), @@ -45,7 +45,6 @@ """, test_suite='mandrel.test', classifiers=[ - "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", From e56d6f9251ae8144029f990eedac4d65bd8ad424 Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Mon, 17 Nov 2014 10:38:05 -0500 Subject: [PATCH 04/10] .travis.yml for CI Basic testing on 2.6, 2.7, using the setuptools behavior for getting test dependencies --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d81b6d1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: python +python: + - "2.6" + - "2.7" +script: python setup.py test From be6d053c57bed851ee036a58fa592266cb9ffcd8 Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Mon, 17 Nov 2014 10:50:39 -0500 Subject: [PATCH 05/10] To 0.1.1 to reflect travis CI support --- changelog.md | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0a30fda..4bf5dfa 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +0.1.1 + +* Adding travis CI build support for 2.6/2.7 testing + 0.1.0 * Added log naming helper functions to mandrel.config.helpers diff --git a/setup.py b/setup.py index aadd344..09ea337 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.1.0", + version = "0.1.1", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"), From 2d1d0d4646145b7af1bc5658902d247fcf85cf6a Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Mon, 17 Nov 2014 18:03:27 -0500 Subject: [PATCH 06/10] version 0.2.0 mandrel-runner using return value as the exit code is a change to the interface, though probably a minor one for most uses. --- changelog.md | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4bf5dfa..7a665bc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +2.0.0 + +* mandrel-runner uses return value of callable as the exit code. + 0.1.1 * Adding travis CI build support for 2.6/2.7 testing diff --git a/setup.py b/setup.py index 09ea337..791c3b5 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.1.1", + version = "0.2.0", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"), From 500f15576da49fcea679dd994f67c3d2c918f175 Mon Sep 17 00:00:00 2001 From: ajenkinski Date: Fri, 14 Jul 2017 17:14:04 -0400 Subject: [PATCH 07/10] Added python3 support. Added a tox.ini which tests python 2 and 3 support. Tests all pass with python 2.7 and 3.6. --- .travis.yml | 5 +++- changelog.md | 6 ++++- mandrel/bootstrap.py | 4 +-- mandrel/config/core.py | 6 ++--- mandrel/runner.py | 7 +++++- .../test/config/configuration_class_test.py | 11 ++++---- .../test/config/loader_functionality_test.py | 25 ++++++++++++------- mandrel/test/runner_test.py | 20 +++++++++------ mandrel/test/util/file_finder_test.py | 8 +++--- mandrel/test/util/loaders_test.py | 2 +- mandrel/test/util/transforming_list_test.py | 12 ++++----- mandrel/test/utils.py | 6 +++++ mandrel/util.py | 19 +++++++++++--- setup.py | 2 +- tox.ini | 12 +++++++++ 15 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index d81b6d1..edf9bd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,7 @@ language: python python: - "2.6" - "2.7" -script: python setup.py test + - "3.6" +script: +- python setup.py test + diff --git a/changelog.md b/changelog.md index 7a665bc..4a85dcd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,8 @@ -2.0.0 +0.3.0 + +* Python 3 support + +0.2.0 * mandrel-runner uses return value of callable as the exit code. diff --git a/mandrel/bootstrap.py b/mandrel/bootstrap.py index f405605..45a2cfa 100644 --- a/mandrel/bootstrap.py +++ b/mandrel/bootstrap.py @@ -47,7 +47,7 @@ def find_logging_configuration(): """ for path in util.find_files(LOGGING_CONFIG_BASENAME, SEARCH_PATHS, matches=1): return path - raise exception.UnknownConfigurationException, "Cannot find logging configuration file(s) '%s'" % LOGGING_CONFIG_BASENAME + raise exception.UnknownConfigurationException("Cannot find logging configuration file(s) '%s'" % LOGGING_CONFIG_BASENAME) DEFAULT_LOGGING_CALLBACK = initialize_simple_logging DISABLE_EXISTING_LOGGERS = True @@ -109,7 +109,7 @@ def _find_bootstrap_base(): while not os.path.isfile(os.path.join(current, __BOOTSTRAP_BASENAME)): parent = os.path.dirname(current) if parent == current: - raise exception.MissingBootstrapException, 'Cannot find %s file in directory hierarchy' % __BOOTSTRAP_BASENAME + raise exception.MissingBootstrapException('Cannot find %s file in directory hierarchy' % __BOOTSTRAP_BASENAME) current = parent return current, os.path.join(current, __BOOTSTRAP_BASENAME) diff --git a/mandrel/config/core.py b/mandrel/config/core.py index c75ca36..d2bb7ed 100644 --- a/mandrel/config/core.py +++ b/mandrel/config/core.py @@ -81,7 +81,7 @@ def find_configuration_file(name): """ for path in find_configuration_files(name): return path - raise exception.UnknownConfigurationException, "No configuration file found for name '%s'" % name + raise exception.UnknownConfigurationException("No configuration file found for name '%s'" % name) def get_loader(path): """Gets the configuration loader for path according to file extension. @@ -98,7 +98,7 @@ def get_loader(path): fullext = '.' + ext if path[-len(fullext):] == fullext: return loader - raise exception.UnknownConfigurationException, "No configuration loader found for path '%s'" % path + raise exception.UnknownConfigurationException("No configuration loader found for path '%s'" % path) def load_configuration_file(path): """Loads the configuration at path and returns it. @@ -267,7 +267,7 @@ def chained_get(self, attribute): except AttributeError: pass - raise AttributeError, 'No such attribute: %s' % attribute + raise AttributeError('No such attribute: %s' % attribute) def __getattr__(self, attr): return self.chained_get(attr) diff --git a/mandrel/runner.py b/mandrel/runner.py index 8a01ccf..5349ebd 100644 --- a/mandrel/runner.py +++ b/mandrel/runner.py @@ -109,7 +109,12 @@ def prepare_environment(self, args): def execute_script(self, script): glb = globals() glb.update(__name__='__main__', __file__=script) - return execfile(script, glb) + if sys.version_info[0] > 2: + import builtins + execf = getattr(builtins, 'exec') + return execf(compile(open(script).read(), script, 'exec'), glb) + else: + return execfile(script, glb) def execute(self, target, args): self.prepare_environment(args) diff --git a/mandrel/test/config/configuration_class_test.py b/mandrel/test/config/configuration_class_test.py index 7294f72..519ecc1 100644 --- a/mandrel/test/config/configuration_class_test.py +++ b/mandrel/test/config/configuration_class_test.py @@ -1,8 +1,9 @@ -import unittest import mock + import mandrel.config -from mandrel.test import utils from mandrel import exception +from mandrel.test import utils + class TestConfigurationClass(utils.TestCase): @mock.patch('mandrel.config.core.get_configuration') @@ -21,7 +22,7 @@ def testGetLoggerName(self): result = mandrel.config.core.Configuration.get_logger_name('some.nested.name') self.assertEqual(mock_name + '.some.nested.name', result) - @mock.patch('mandrel.bootstrap') + @mock.patch('mandrel.bootstrap', create=True) @mock.patch('mandrel.config.core.Configuration.get_logger_name') def testGetLogger(self, get_logger_name, bootstrap): mock_name = str(mock.Mock(name='AnotherMockConfigurationName')) @@ -45,7 +46,7 @@ def testBasicAttributes(self): self.assertEqual(config, c.configuration) self.assertEqual((), c.chain) - chain = tuple(mock.Mock(name='Chain%d' % x) for x in xrange(3)) + chain = tuple(mock.Mock(name='Chain%d' % x) for x in range(3)) c = mandrel.config.core.Configuration(config, *chain) self.assertEqual(config, c.configuration) self.assertEqual(chain, c.chain) @@ -136,7 +137,7 @@ def testGetConfiguration(self, loader): self.assertEqual(loader.return_value, c.configuration) self.assertEqual((), c.chain) - chain = tuple(mock.Mock() for x in xrange(5)) + chain = tuple(mock.Mock() for x in range(5)) c = mandrel.config.core.Configuration.get_configuration(*chain) self.assertEqual(loader.return_value, c.configuration) self.assertEqual(chain, c.chain) diff --git a/mandrel/test/config/loader_functionality_test.py b/mandrel/test/config/loader_functionality_test.py index e2ab975..b88ceae 100644 --- a/mandrel/test/config/loader_functionality_test.py +++ b/mandrel/test/config/loader_functionality_test.py @@ -1,13 +1,20 @@ -import contextlib import unittest + import mock + import mandrel from mandrel import exception -from mandrel.test import utils + +try: + # python 3 compatibility + from importlib import reload +except ImportError: + pass + def scenario(func): def wrapper(*a, **kw): - with mock.patch('mandrel.bootstrap') as bootstrap: + with mock.patch('mandrel.bootstrap', create=True) as bootstrap: if hasattr(mandrel, 'config'): reload(mandrel.config.core) reload(mandrel.config) @@ -48,7 +55,7 @@ def testGetPossibleBasenames(self): def testFindConfigurationFiles(self): with mock.patch('mandrel.util.find_files') as find_files: with mock.patch('mandrel.config.core.get_possible_basenames') as get_possible_basenames: - exts = [mock.Mock(name='Extension%d' % x) for x in xrange(3)] + exts = [mock.Mock(name='Extension%d' % x) for x in range(3)] mandrel.config.core.LOADERS = [(ext, mock.Mock(name='Reader')) for ext in exts] mandrel.bootstrap.SEARCH_PATHS = mock.Mock() name = mock.Mock(name='FileBase') @@ -60,7 +67,7 @@ def testFindConfigurationFiles(self): @scenario def testFindConfigurationFileWithMatch(self): with mock.patch('mandrel.config.core.find_configuration_files') as find_configuration_files: - paths = [mock.Mock(name='Path%d' % x) for x in xrange(10)] + paths = [mock.Mock(name='Path%d' % x) for x in range(10)] find_configuration_files.side_effect = lambda x: iter(paths) name = mock.Mock(name='SomeBase') result = mandrel.config.core.find_configuration_file(name) @@ -76,11 +83,11 @@ def testFindConfigurationFilesWithoutMatch(self): @scenario def testGetLoader(self): - exts = [str(mock.Mock(name='Extension%d' % x)) for x in xrange(3)] - loaders = [mock.Mock(name='Loader%d' % x) for x in xrange(len(exts))] - mandrel.config.core.LOADERS = zip(exts, loaders) + exts = [str(mock.Mock(name='Extension%d' % x)) for x in range(3)] + loaders = [mock.Mock(name='Loader%d' % x) for x in range(len(exts))] + mandrel.config.core.LOADERS = list(zip(exts, loaders)) - for i in xrange(len(exts)): + for i in range(len(exts)): ext, loader = mandrel.config.core.LOADERS[i] path = '%s.%s' % (mock.Mock(name='someFile'), ext) result = mandrel.config.core.get_loader(path) diff --git a/mandrel/test/runner_test.py b/mandrel/test/runner_test.py index 9a809ec..a84b305 100644 --- a/mandrel/test/runner_test.py +++ b/mandrel/test/runner_test.py @@ -31,6 +31,12 @@ def func(*a, **kw): KNOWN_PATH = os.path.realpath('') +if sys.version_info[0] > 2: + builtin_name = 'builtins' +else: + builtin_name = '__builtin__' + + class TestRunner(unittest.TestCase): @scenario('-s', 'foo:bar:bah:', 'gloof', 'glof', 'floo', ensure_target=False) def testSearchPathSet(self, path): @@ -74,7 +80,7 @@ def testLogConfigBasenamePath(self, path): @mock.patch.object(runner.AbstractRunner, 'process_options') @mock.patch.object(runner.AbstractRunner, 'execute') def testOrderOfOperations(self, path, execute, process_options): - mocks = [mock.Mock('positional%d' % x) for x in xrange(4)] + mocks = [mock.Mock('positional%d' % x) for x in range(4)] target = mocks.pop(0) process_options.return_value = (target, mocks) runner.AbstractRunner().run() @@ -90,7 +96,7 @@ def testNotImplemented(self, path, process_options): @scenario() def testImporting(self, _): obj = runner.AbstractRunner() - with mock.patch('__builtin__.__import__') as importer: + with mock.patch(builtin_name + '.__import__') as importer: self.assertEqual(importer.return_value.bootstrap, obj.bootstrapper) importer.assert_called_once_with('mandrel.bootstrap') @@ -102,18 +108,18 @@ def testLaunch(self, run): @mock.patch('mandrel.util.get_by_fqn') def testCallableRunner(self, get_by_fqn): target = str(mock.Mock(name='MockTarget')) - opts = [str(mock.Mock(name='Arg%d' % x)) for x in xrange(3)] + opts = [str(mock.Mock(name='Arg%d' % x)) for x in range(3)] result = runner.CallableRunner().execute(target, opts) get_by_fqn.assert_called_once_with(target) get_by_fqn.return_value.assert_called_once_with(opts) self.assertEqual(get_by_fqn.return_value.return_value, result) - @mock.patch('__builtin__.globals') + @mock.patch(builtin_name + '.globals') @mock.patch('sys.argv', new=['foo', 'bar', 'bah']) - @mock.patch('__builtin__.execfile') + @mock.patch('mandrel.runner.ScriptRunner.execute_script') def testScriptRunner(self, mock_exec, mock_globals): target = str(mock.Mock(name='MockTarget')) - opts = [str(mock.Mock(name='Arg%d' % x)) for x in xrange(3)] + opts = [str(mock.Mock(name='Arg%d' % x)) for x in range(3)] glb = {'foo': mock.Mock(), 'bar': mock.Mock(), '__file__': mock.Mock(), '__name__': mock.Mock()} mock_globals.side_effect = lambda: dict(glb) exp = dict(glb) @@ -121,7 +127,7 @@ def testScriptRunner(self, mock_exec, mock_globals): exp['__name__'] = '__main__' result = runner.ScriptRunner().execute(target, opts) - mock_exec.assert_called_once_with(target, exp) + mock_exec.assert_called_once_with(target) self.assertEqual(mock_exec.return_value, result) # Should add args at sys.argv[1:] self.assertEqual(['foo'] + opts, sys.argv) diff --git a/mandrel/test/util/file_finder_test.py b/mandrel/test/util/file_finder_test.py index 47f03de..5bb6065 100644 --- a/mandrel/test/util/file_finder_test.py +++ b/mandrel/test/util/file_finder_test.py @@ -14,7 +14,7 @@ def scenario(**files_to_levels): levels.append(b) with utils.tempdir() as c: levels.append(c) - for name, dirs in files_to_levels.items(): + for name, dirs in list(files_to_levels.items()): for level in dirs: with open(os.path.join(levels[level], name), 'w') as f: f.write(str(level)) @@ -32,21 +32,21 @@ def get_level(path): class TestFileFinder(unittest.TestCase): def testSingleFindOneMatch(self): with scenario(**{'a.txt': (0, 1, 2), 'b.foo': (1, 2), 'c.bar': (2,)}) as dirs: - for name, level in {'a.txt': 0, 'b.foo': 1, 'c.bar': 2}.items(): + for name, level in list({'a.txt': 0, 'b.foo': 1, 'c.bar': 2}.items()): result = tuple(util.find_files(name, dirs, matches=1)) self.assertEqual(1, len(result)) self.assertEqual(level, get_level(result[0])) def testSingleFindTwoMatch(self): with scenario(**{'0.x': (0,), 'a.txt': (0, 1, 2), 'b.foo': (1, 2), 'c.bar': (2,)}) as dirs: - for name, levels in {'0.x': (0,), 'a.txt': (0, 1), 'b.foo': (1, 2), 'c.bar': (2,)}.items(): + for name, levels in list({'0.x': (0,), 'a.txt': (0, 1), 'b.foo': (1, 2), 'c.bar': (2,)}.items()): got = tuple(get_level(r) for r in util.find_files(name, dirs, matches=2)) self.assertEqual(levels, got) def testSingleFindMultiMatch(self): mapping = {'0.x': (0,), 'a.txt': (0, 1), 'b.blah': (0, 1, 2), 'c.pork': (1, 2), 'd.plonk': (1,), 'e.sporks': (2,)} with scenario(**mapping) as dirs: - for name, levels in mapping.items(): + for name, levels in list(mapping.items()): got = tuple(get_level(r) for r in util.find_files(name, dirs)) self.assertEqual(levels, got) diff --git a/mandrel/test/util/loaders_test.py b/mandrel/test/util/loaders_test.py index 698478d..e5b412f 100644 --- a/mandrel/test/util/loaders_test.py +++ b/mandrel/test/util/loaders_test.py @@ -30,7 +30,7 @@ def test_harness_loader_straight(self): callback = mock.Mock(name='Callback') harness = util.harness_loader(loader)(callback) name = mock.Mock(name='Plugin') - args = [mock.Mock(name='Arg%d' % x) for x in xrange(3)] + args = [mock.Mock(name='Arg%d' % x) for x in range(3)] result = harness(name) loader.assert_called_once_with(name) diff --git a/mandrel/test/util/transforming_list_test.py b/mandrel/test/util/transforming_list_test.py index 2f6e92c..abe9e38 100644 --- a/mandrel/test/util/transforming_list_test.py +++ b/mandrel/test/util/transforming_list_test.py @@ -6,14 +6,14 @@ _trans_count = itertools.count(0) def mock_transform(): - t = mock.Mock(name='MockTransform%d' % _trans_count.next()) + t = mock.Mock(name='MockTransform%d' % next(_trans_count)) t.side_effect = lambda v: v.transformed return t _vals_count = itertools.count(0) def mock_value(): - return mock.Mock(name='MockValue%d' % _vals_count.next()) + return mock.Mock(name='MockValue%d' % next(_vals_count)) class TestTransformingList(unittest.TestCase): def testAppendBasics(self): @@ -33,7 +33,7 @@ def testAppendBasics(self): def testAccessBasics(self): t = mock_transform() l = util.TransformingList(t) - vals = [mock_value() for i in xrange(5)] + vals = [mock_value() for i in range(5)] l[0:3] = vals[0:3] self.assertEqual(3, len(l)) self.assertEqual(tuple(v.transformed for v in vals[0:3]), tuple(l)) @@ -51,7 +51,7 @@ def testAccessBasics(self): def testExtend(self): t = mock_transform() - vals = [mock_value() for i in xrange(5)] + vals = [mock_value() for i in range(5)] exp = tuple(v.transformed for v in vals) l = util.TransformingList(t) l.extend(vals) @@ -64,7 +64,7 @@ def testExtend(self): def testInsertPop(self): t = mock_transform() - a, b, c = (mock_value() for i in xrange(3)) + a, b, c = (mock_value() for i in range(3)) l = util.TransformingList(t) l.append(a) l.append(b) @@ -77,7 +77,7 @@ def testInsertPop(self): def testContainment(self): t = mock_transform() - vals = [mock_value() for i in xrange(5)] + vals = [mock_value() for i in range(5)] l = util.TransformingList(t) member = lambda v: v in l self.assertFalse(member(vals[0])) diff --git a/mandrel/test/utils.py b/mandrel/test/utils.py index df7dbc8..3688ce5 100644 --- a/mandrel/test/utils.py +++ b/mandrel/test/utils.py @@ -5,6 +5,12 @@ import mandrel import unittest +try: + # python 3 compatibility + from importlib import reload +except ImportError: + pass + class TestCase(unittest.TestCase): def assertIs(self, a, b): # python 2.6/2.7 compatibility diff --git a/mandrel/util.py b/mandrel/util.py index fbc17e0..4fe9bbc 100644 --- a/mandrel/util.py +++ b/mandrel/util.py @@ -1,5 +1,13 @@ import os import re +import sys + + +if sys.version_info[0] == 2: + string_type = basestring +else: + string_type = str + class TransformingList(object): __slots__ = ('_list', '_transformer') @@ -9,7 +17,10 @@ def __init__(self, transformer): self._transformer = transformer def __setitem__(self, i, y): - self._list[i] = self._transformer(y) + if isinstance(i, slice): + self._list[i] = (self._transformer(v) for v in y) + else: + self._list[i] = self._transformer(y) def __setslice__(self, i, j, y): self._list[i:j] = (self._transformer(v) for v in y) @@ -72,7 +83,7 @@ def find_files(name_or_names, paths, matches=None): yielded value is the full path to a matching file. """ - if isinstance(name_or_names, basestring): + if isinstance(name_or_names, string_type): name_or_names = [name_or_names] if matches is None: @@ -138,9 +149,9 @@ def convention_loader(format_string): not contain one and only one '%s' within it. """ if not format_string: - raise TypeError, 'format_string cannot be blank' + raise TypeError('format_string cannot be blank') if re.findall('%.', format_string) != ['%s']: - raise TypeError, 'format_string must contain one and only one "%s" token' + raise TypeError('format_string must contain one and only one "%s" token') def func(name): return get_by_fqn(format_string % name) diff --git a/setup.py b/setup.py index 791c3b5..4cea793 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.2.0", + version = "0.3.0", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"), diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..fbbf9e5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,12 @@ +# Tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py26,py27,py36 + +[testenv] +commands = {envpython} setup.py test +deps = + From 94df4387178e5384c041360fe086e420680a6b29 Mon Sep 17 00:00:00 2001 From: Patrick Rusk Date: Tue, 5 Jun 2018 12:07:42 -0400 Subject: [PATCH 08/10] Allow code expecting 0.1.0 behavior to work with 0.3.0+ --- changelog.md | 4 ++++ mandrel/runner.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 4a85dcd..99d6506 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +CURRENT + +* Compatibility enhancements to allow code expecting 0.1.0 behavior to work with 0.3.0+ + 0.3.0 * Python 3 support diff --git a/mandrel/runner.py b/mandrel/runner.py index 5349ebd..44b3291 100644 --- a/mandrel/runner.py +++ b/mandrel/runner.py @@ -121,8 +121,35 @@ def execute(self, target, args): return self.execute_script(target) +def _convert_status(status): + """ + Converts the return value from a callable or script into a status that allows code + expecting mandrel-0.1.0 behavior (which ignored returns) to behave properly + with the latest code the interprets the return as a status code. + + :param status: anything that might have been returned by code + :return: 0 if the code should be thought of as successful, 1 if failed, or the status itself if already an integer + """ + if status is True: + # Exactly True is successful + return 0 + elif status is False: + # Exactly False is failed + return 1 + elif status is None: + # A function completing with no return is considered successful + return 0 + elif isinstance(status, int): + # Any other integer return is deemed a status + return status + else: + # Anything else is treated as successful + return 0 + + def launch_callable(): - return CallableRunner.launch() + return _convert_status(CallableRunner.launch()) + def launch_script(): - return ScriptRunner.launch() + return _convert_status(ScriptRunner.launch()) From 4f3849a1238e748af95e660620f73c0f08f517ce Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Tue, 5 Jun 2018 12:27:54 -0400 Subject: [PATCH 09/10] Add warnings against using mandrel for new work. For the love of god, people, this is obviously not a good way to approach the problem. --- README.md | 9 +++++++++ setup.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 79c1e57..919f9ff 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,15 @@ Mandrel provides bootstrapping and configuration tools for consistent, straightforward project config management. +**Mandrel is a highly idiosyncratic way to organize stuff and is ultimately +counterproductive. The author and maintainer recommends against its continued +use. This project is in maintenance mode only and the author cannot stress +enough the degree to which alternate approaches to bootstrapping should be +found. Don't use mandrel for new work. Deprecate its use where you can. +(FWIW, the author prefers to drive configuration via environment variables, +generally speaking, and only use specialized file-based config with JSON or +YAML if the configuration needs are especially nuanced.)** + Use Mandrel to: * bootstrap your python project configuration diff --git a/setup.py b/setup.py index 4cea793..949fd8b 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,15 @@ Mandrel provides bootstrapping and configuration tools for consistent, straightforward project config management. +**Mandrel is a highly idiosyncratic way to organize stuff and is ultimately +counterproductive. The author and maintainer recommends against its continued +use. This project is in maintenance mode only and the author cannot stress +enough the degree to which alternate approaches to bootstrapping should be +found. Don't use mandrel for new work. Deprecate its use where you can. +(FWIW, the author prefers to drive configuration via environment variables, +generally speaking, and only use specialized file-based config with JSON or +YAML if the configuration needs are especially nuanced.)** + Use Mandrel to: * bootstrap your python project configuration From 911e3843e5c141e1c9a5477b4c817d7b96d01654 Mon Sep 17 00:00:00 2001 From: Ethan Rowe Date: Tue, 5 Jun 2018 12:29:59 -0400 Subject: [PATCH 10/10] Bump to version 1.0.0! Just in time to tell people to please please please not use this project. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 949fd8b..0c6b3cc 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name = "mandrel", - version = "0.3.0", + version = "1.0.0", author = "Ethan Rowe", author_email = "ethan@the-rowes.com", description = ("Provides bootstrapping for sane configuration management"),