diff options
author | grabner <pjg.github@ubergrabner.net> | 2016-12-19 14:08:30 -0500 |
---|---|---|
committer | grabner <pjg.github@ubergrabner.net> | 2016-12-19 14:08:30 -0500 |
commit | e291946c3620411207d36f225847193ab26e4070 (patch) | |
tree | ddda275df869d203fedd096b61217d005a51f93d | |
parent | c0f625805cc88555a1c0ac8468731825a60f4058 (diff) | |
download | iniherit-e291946c3620411207d36f225847193ab26e4070.tar.gz |
more thorough implementation of PY3 compatibility (follow-up from issue #6)
-rw-r--r-- | iniherit/interpolation.py | 27 | ||||
-rw-r--r-- | iniherit/mixin.py | 24 | ||||
-rw-r--r-- | iniherit/parser.py | 3 | ||||
-rw-r--r-- | iniherit/test.py | 93 |
4 files changed, 110 insertions, 37 deletions
diff --git a/iniherit/interpolation.py b/iniherit/interpolation.py index 1c9e2f4..96b213d 100644 --- a/iniherit/interpolation.py +++ b/iniherit/interpolation.py @@ -9,16 +9,43 @@ import re import os +import six from six.moves import configparser as CP #------------------------------------------------------------------------------ + +if six.PY3: + _real_BasicInterpolation = CP.BasicInterpolation + _real_BasicInterpolation_before_get = CP.BasicInterpolation.before_get +else: + _real_BasicInterpolation = object + _real_BasicInterpolation_before_get = None + +#------------------------------------------------------------------------------ class InterpolationMissingEnvError(CP.InterpolationMissingOptionError): pass class InterpolationMissingSuperError(CP.InterpolationMissingOptionError): pass + +#------------------------------------------------------------------------------ +class BasicInterpolationMixin(object): + def before_get(self, parser, section, option, value, defaults): + def base_interpolate(*args, **kw): + return _real_BasicInterpolation_before_get(parser._interpolation, *args, **kw) + return interpolate(parser, base_interpolate, section, option, value, defaults) + + +#------------------------------------------------------------------------------ +class IniheritInterpolation(BasicInterpolationMixin, _real_BasicInterpolation): + # todo: rewrite this to use a more PY3-oriented approach... + pass + + #------------------------------------------------------------------------------ _env_cre = re.compile(r'%\(ENV:([^:)]+)(:-([^)]*))?\)s', flags=re.DOTALL) _super_cre = re.compile(r'%\(SUPER(:-([^)]*))?\)s', flags=re.DOTALL) def interpolate(parser, base_interpolate, section, option, rawval, vars): + # todo: ugh. this should be rewritten so that it uses + # `BasicInterpolationMixin` so as to be more "future-proof"... value = rawval depth = CP.MAX_INTERPOLATION_DEPTH erepl = lambda match: _env_replace( diff --git a/iniherit/mixin.py b/iniherit/mixin.py index 15b525d..7fc3982 100644 --- a/iniherit/mixin.py +++ b/iniherit/mixin.py @@ -10,6 +10,7 @@ import six from six.moves import configparser as CP from .parser import IniheritMixin +from .interpolation import BasicInterpolationMixin #------------------------------------------------------------------------------ @@ -17,6 +18,17 @@ from .parser import IniheritMixin raw_attrs = [attr for attr in dir(IniheritMixin) if not attr.startswith('__')] base_attrs = ['_interpolate'] +interpolation_attrs = [attr for attr in dir(BasicInterpolationMixin) if not attr.startswith('__')] + +_replacements = [ + (IniheritMixin, CP.RawConfigParser, raw_attrs), + (IniheritMixin, CP.ConfigParser, base_attrs), +] + +if six.PY3: + _replacements += [ + (BasicInterpolationMixin, CP.BasicInterpolation, interpolation_attrs), + ] #------------------------------------------------------------------------------ def install_globally(): @@ -28,15 +40,12 @@ def install_globally(): if hasattr(CP.RawConfigParser, '_iniherit_installed_'): return setattr(CP.RawConfigParser, '_iniherit_installed_', True) - for target, attrs in ( - (CP.RawConfigParser, raw_attrs), - (CP.ConfigParser, base_attrs), - ): + for source, target, attrs in _replacements: for attr in attrs: if hasattr(target, attr): setattr(target, '_iniherit_' + attr, getattr(target, attr)) - meth = getattr(IniheritMixin, attr) + meth = getattr(source, attr) if six.callable(meth): if six.PY2: import new @@ -53,10 +62,7 @@ def uninstall_globally(): if not hasattr(CP.RawConfigParser, '_iniherit_installed_'): return delattr(CP.RawConfigParser, '_iniherit_installed_') - for target, attrs in ( - (CP.RawConfigParser, raw_attrs), - (CP.ConfigParser, base_attrs), - ): + for source, target, attrs in _replacements: for attr in attrs: if hasattr(target, '_iniherit_' + attr): xattr = getattr(target, '_iniherit_' + attr) diff --git a/iniherit/parser.py b/iniherit/parser.py index ccb81e5..a97c4aa 100644 --- a/iniherit/parser.py +++ b/iniherit/parser.py @@ -192,7 +192,7 @@ class IniheritMixin(object): base_interpolate = getattr(_real_ConfigParser, '_interpolate', None) return interpolation.interpolate( self, base_interpolate, section, option, rawval, vars) - if not hasattr(_real_ConfigParser, '_interpolate'): + if not hasattr(_real_ConfigParser, '_interpolate') and not six.PY3: warnings.warn( 'ConfigParser did not have a "_interpolate" method' ' -- iniherit may be broken on this platform', @@ -202,6 +202,7 @@ class IniheritMixin(object): #------------------------------------------------------------------------------ # todo: i'm a little worried about the diamond inheritance here... class RawConfigParser(IniheritMixin, _real_RawConfigParser): + _DEFAULT_INTERPOLATION = interpolation.IniheritInterpolation() def __init__(self, *args, **kw): loader = kw.pop('loader', None) IniheritMixin.__init__(self, loader=loader) diff --git a/iniherit/test.py b/iniherit/test.py index 826d958..b2872ee 100644 --- a/iniherit/test.py +++ b/iniherit/test.py @@ -29,6 +29,8 @@ class ByteLoader(Loader): #------------------------------------------------------------------------------ class TestIniherit(unittest.TestCase): + maxDiff = None + #---------------------------------------------------------------------------- def test_iniherit(self): files = {k: textwrap.dedent(v) for k, v in { @@ -276,13 +278,22 @@ class TestIniherit(unittest.TestCase): self.assertEqual(parser.get('loggers', 'dkey'), 'more or less') with self.assertRaises(InterpolationMissingSuperError) as cm: parser.get('loggers', 'nkey') - self.assertMultiLineEqual(str(cm.exception), textwrap.dedent('''\ - Bad value substitution: - \tsection: [loggers] - \toption : nkey - \tkey : SUPER - \trawval : %(SUPER)s and boom! - ''')) + if six.PY2: + err = textwrap.dedent('''\ + Bad value substitution: + \tsection: [loggers] + \toption : nkey + \tkey : SUPER + \trawval : %(SUPER)s and boom! + ''') + else: + err = ( + "Bad value substitution:" + " option 'nkey' in section 'loggers'" + " contains an interpolation key 'SUPER' which is not a valid option name." + " Raw value: '%(SUPER)s and boom!'" + ) + self.assertMultiLineEqual(str(cm.exception), err) #---------------------------------------------------------------------------- def test_interpolation_super_invalid(self): @@ -303,13 +314,22 @@ class TestIniherit(unittest.TestCase): parser.read('extend.ini') with self.assertRaises(InterpolationMissingSuperError) as cm: parser.get('DEFAULT', 'key2') - self.assertMultiLineEqual(str(cm.exception), textwrap.dedent('''\ - Bad value substitution: - \tsection: [DEFAULT] - \toption : key2 - \tkey : SUPER - \trawval : %(SUPER)s and boom! - ''')) + if six.PY2: + err = textwrap.dedent('''\ + Bad value substitution: + \tsection: [DEFAULT] + \toption : key2 + \tkey : SUPER + \trawval : %(SUPER)s and boom! + ''') + else: + err = ( + "Bad value substitution:" + " option 'key2' in section 'DEFAULT'" + " contains an interpolation key 'SUPER' which is not a valid option name." + " Raw value: '%(SUPER)s and boom!'" + ) + self.assertMultiLineEqual(str(cm.exception), err) #---------------------------------------------------------------------------- def test_interpolation_env(self): @@ -339,21 +359,40 @@ class TestIniherit(unittest.TestCase): self.assertEqual(parser.get('section', 'key4'), 'default-value') with self.assertRaises(InterpolationMissingEnvError) as cm: parser.get('section', 'key3') - self.assertMultiLineEqual(str(cm.exception), textwrap.dedent('''\ - Bad value substitution: - \tsection: [section] - \toption : key3 - \tkey : INIHERIT_TEST_NOEXIST - \trawval : %(ENV:INIHERIT_TEST_NOEXIST)s - ''')) + if six.PY2: + err = textwrap.dedent('''\ + Bad value substitution: + \tsection: [section] + \toption : key3 + \tkey : INIHERIT_TEST_NOEXIST + \trawval : %(ENV:INIHERIT_TEST_NOEXIST)s + ''') + else: + err = ( + "Bad value substitution:" + " option 'key3' in section 'section'" + " contains an interpolation key 'INIHERIT_TEST_NOEXIST'" + " which is not a valid option name." + " Raw value: '%(ENV:INIHERIT_TEST_NOEXIST)s'" + ) + self.assertMultiLineEqual(str(cm.exception), err) with self.assertRaises(InterpolationDepthError) as cm: parser.get('section', 'key5') - self.assertMultiLineEqual(str(cm.exception), textwrap.dedent('''\ - Value interpolation too deeply recursive: - \tsection: [section] - \toption : key5 - \trawval : %(ENV:INIHERIT_TEST_INFLOOP)s - ''')) + if six.PY2: + err = textwrap.dedent('''\ + Value interpolation too deeply recursive: + \tsection: [section] + \toption : key5 + \trawval : %(ENV:INIHERIT_TEST_INFLOOP)s + ''') + else: + err = ( + "Recursion limit exceeded in value substitution:" + " option 'key5' in section 'section'" + " contains an interpolation key which cannot be substituted in 10 steps." + " Raw value: '%(ENV:INIHERIT_TEST_INFLOOP)s'" + ) + self.assertMultiLineEqual(str(cm.exception), err) #------------------------------------------------------------------------------ |