Skip to content

Commit 8d40520

Browse files
dhuadaarvsajipAlexWaygooderlend-aasland
authored
gh-103384: Generalize the regex pattern BaseConfigurator.INDEX_PATTERN to allow spaces and non-alphanumeric characters in keys. (GH-103391)
Co-authored-by: Vinay Sajip <[email protected]> Co-authored-by: Alex Waygood <[email protected]> Co-authored-by: Erlend E. Aasland <[email protected]>
1 parent 1350987 commit 8d40520

File tree

4 files changed

+51
-3
lines changed

4 files changed

+51
-3
lines changed

Doc/library/logging.config.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ resolve to ``'[email protected]'`` and the string
685685
``'[email protected]'``. The ``subject`` value could be accessed
686686
using either ``'cfg://handlers.email.subject'`` or, equivalently,
687687
``'cfg://handlers.email[subject]'``. The latter form only needs to be
688-
used if the key contains spaces or non-alphanumeric characters. If an
688+
used if the key contains spaces or non-alphanumeric characters. Please note
689+
that the characters ``[`` and ``]`` are not allowed in the keys. If an
689690
index value consists only of decimal digits, access will be attempted
690691
using the corresponding integer value, falling back to the string
691692
value if needed.

Lib/logging/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ class BaseConfigurator(object):
378378

379379
WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
380380
DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
381-
INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
381+
INDEX_PATTERN = re.compile(r'^\[([^\[\]]*)\]\s*')
382382
DIGIT_PATTERN = re.compile(r'^\d+$')
383383

384384
value_converters = {

Lib/test/test_logging.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3662,7 +3662,28 @@ def test_baseconfig(self):
36623662
d = {
36633663
'atuple': (1, 2, 3),
36643664
'alist': ['a', 'b', 'c'],
3665-
'adict': {'d': 'e', 'f': 3 },
3665+
'adict': {
3666+
'd': 'e', 'f': 3 ,
3667+
'alpha numeric 1 with spaces' : 5,
3668+
'aplha numeric 1 %( - © ©ß¯' : 9,
3669+
'alpha numeric ] 1 with spaces' : 15,
3670+
'aplha ]] numeric 1 %( - © ©ß¯]' : 19,
3671+
' aplha [ numeric 1 %( - © ©ß¯] ' : 11,
3672+
' aplha ' : 32,
3673+
'' : 10,
3674+
'nest4' : {
3675+
'd': 'e', 'f': 3 ,
3676+
'alpha numeric 1 with spaces' : 5,
3677+
'aplha numeric 1 %( - © ©ß¯' : 9,
3678+
'' : 10,
3679+
'somelist' : ('g', ('h', 'i'), 'j'),
3680+
'somedict' : {
3681+
'a' : 1,
3682+
'a with 1 and space' : 3,
3683+
'a with ( and space' : 4,
3684+
}
3685+
}
3686+
},
36663687
'nest1': ('g', ('h', 'i'), 'j'),
36673688
'nest2': ['k', ['l', 'm'], 'n'],
36683689
'nest3': ['o', 'cfg://alist', 'p'],
@@ -3674,11 +3695,36 @@ def test_baseconfig(self):
36743695
self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
36753696
self.assertEqual(bc.convert('cfg://adict.d'), 'e')
36763697
self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3698+
self.assertEqual(bc.convert('cfg://adict[alpha numeric 1 with spaces]'), 5)
3699+
self.assertEqual(bc.convert('cfg://adict[aplha numeric 1 %( - © ©ß¯]'), 9)
3700+
self.assertEqual(bc.convert('cfg://adict[]'), 10)
3701+
self.assertEqual(bc.convert('cfg://adict.nest4.d'), 'e')
3702+
self.assertEqual(bc.convert('cfg://adict.nest4[d]'), 'e')
3703+
self.assertEqual(bc.convert('cfg://adict[nest4].d'), 'e')
3704+
self.assertEqual(bc.convert('cfg://adict[nest4][f]'), 3)
3705+
self.assertEqual(bc.convert('cfg://adict[nest4][alpha numeric 1 with spaces]'), 5)
3706+
self.assertEqual(bc.convert('cfg://adict[nest4][aplha numeric 1 %( - © ©ß¯]'), 9)
3707+
self.assertEqual(bc.convert('cfg://adict[nest4][]'), 10)
3708+
self.assertEqual(bc.convert('cfg://adict[nest4][somelist][0]'), 'g')
3709+
self.assertEqual(bc.convert('cfg://adict[nest4][somelist][1][0]'), 'h')
3710+
self.assertEqual(bc.convert('cfg://adict[nest4][somelist][1][1]'), 'i')
3711+
self.assertEqual(bc.convert('cfg://adict[nest4][somelist][2]'), 'j')
3712+
self.assertEqual(bc.convert('cfg://adict[nest4].somedict.a'), 1)
3713+
self.assertEqual(bc.convert('cfg://adict[nest4].somedict[a]'), 1)
3714+
self.assertEqual(bc.convert('cfg://adict[nest4].somedict[a with 1 and space]'), 3)
3715+
self.assertEqual(bc.convert('cfg://adict[nest4].somedict[a with ( and space]'), 4)
3716+
self.assertEqual(bc.convert('cfg://adict.nest4.somelist[1][1]'), 'i')
3717+
self.assertEqual(bc.convert('cfg://adict.nest4.somelist[2]'), 'j')
3718+
self.assertEqual(bc.convert('cfg://adict.nest4.somedict.a'), 1)
3719+
self.assertEqual(bc.convert('cfg://adict.nest4.somedict[a]'), 1)
36773720
v = bc.convert('cfg://nest3')
36783721
self.assertEqual(v.pop(1), ['a', 'b', 'c'])
36793722
self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
36803723
self.assertRaises(ValueError, bc.convert, 'cfg://!')
36813724
self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
3725+
self.assertRaises(KeyError, bc.convert, 'cfg://adict[alpha numeric ] 1 with spaces]')
3726+
self.assertRaises(ValueError, bc.convert, 'cfg://adict[ aplha ]] numeric 1 %( - © ©ß¯] ]')
3727+
self.assertRaises(ValueError, bc.convert, 'cfg://adict[ aplha [ numeric 1 %( - © ©ß¯] ]')
36823728

36833729
def test_namedtuple(self):
36843730
# see bpo-39142
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Generalize the regex pattern ``BaseConfigurator.INDEX_PATTERN`` to allow spaces and non-alphanumeric characters in keys.

0 commit comments

Comments
 (0)