summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrabner <pjg.github@ubergrabner.net>2016-12-19 14:08:30 -0500
committergrabner <pjg.github@ubergrabner.net>2016-12-19 14:08:30 -0500
commite291946c3620411207d36f225847193ab26e4070 (patch)
treeddda275df869d203fedd096b61217d005a51f93d
parentc0f625805cc88555a1c0ac8468731825a60f4058 (diff)
downloadiniherit-e291946c3620411207d36f225847193ab26e4070.tar.gz
more thorough implementation of PY3 compatibility (follow-up from issue #6)
-rw-r--r--iniherit/interpolation.py27
-rw-r--r--iniherit/mixin.py24
-rw-r--r--iniherit/parser.py3
-rw-r--r--iniherit/test.py93
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)
#------------------------------------------------------------------------------