diff -r 50a94e1cabe0 Doc/library/configparser.rst --- a/Doc/library/configparser.rst Mon May 09 00:14:22 2016 +0300 +++ b/Doc/library/configparser.rst Tue May 10 19:00:03 2016 +0800 @@ -384,6 +384,10 @@ "a" in parser["section"] "A" in parser["section"] +* All section names including ``DEFAULTSECT`` are passed through + :meth:`sectionxform` [1]_. This does nothing but merely returns the section + name to preserve backward compatibility. + * All sections include ``DEFAULTSECT`` values as well which means that ``.clear()`` on a section may not leave the section visibly empty. This is because default values cannot be deleted from the section (because technically @@ -740,6 +744,39 @@ >>> list(custom['Section2'].keys()) ['AnotherKey'] +.. method:: sectionxform(section) + + This method transforms section names on every read, get or set operation. + The default merely returns *section*. You can customize section names by + overriding this method. + For example: + + .. doctest:: + + >>> config = """ + ... [Section] + ... key = value + ... """ + >>> typical = configparser.ConfigParser() + >>> typical.read_string(config) + >>> typical.has_section('Section') + True + >>> typical.has_section('section') + False + >>> typical['section'] + Traceback (most recent call last): + ... + KeyError: 'section' + >>> custom = configparser.ConfigParser() + >>> custom.sectionxform = lambda sect: sect.lower() + >>> custom.read_string(config) + >>> custom.has_section('Section') + True + >>> custom.has_section('section') + True + >>> custom['section'] + + .. attribute:: SECTCRE A compiled regular expression used to parse section headers. The default @@ -1150,6 +1187,15 @@ names is stripped before :meth:`optionxform` is called. + .. method:: sectionxform(section) + + Hook provided to transform the section names as :meth:`optionxform` does + for option names. The default implementation does nothing but merely + returns *section*. + + .. versionadded:: 3.6 + + .. method:: readfp(fp, filename=None) .. deprecated:: 3.2 diff -r 50a94e1cabe0 Doc/library/inspect.rst --- a/Doc/library/inspect.rst Mon May 09 00:14:22 2016 +0300 +++ b/Doc/library/inspect.rst Tue May 10 19:00:03 2016 +0800 @@ -33,185 +33,205 @@ They also help you determine when you can expect to find the following special attributes: -+-----------+-----------------+---------------------------+ -| Type | Attribute | Description | -+===========+=================+===========================+ -| module | __doc__ | documentation string | -+-----------+-----------------+---------------------------+ -| | __file__ | filename (missing for | -| | | built-in modules) | -+-----------+-----------------+---------------------------+ -| class | __doc__ | documentation string | -+-----------+-----------------+---------------------------+ -| | __name__ | name with which this | -| | | class was defined | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | __module__ | name of module in which | -| | | this class was defined | -+-----------+-----------------+---------------------------+ -| method | __doc__ | documentation string | -+-----------+-----------------+---------------------------+ -| | __name__ | name with which this | -| | | method was defined | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | __func__ | function object | -| | | containing implementation | -| | | of method | -+-----------+-----------------+---------------------------+ -| | __self__ | instance to which this | -| | | method is bound, or | -| | | ``None`` | -+-----------+-----------------+---------------------------+ -| function | __doc__ | documentation string | -+-----------+-----------------+---------------------------+ -| | __name__ | name with which this | -| | | function was defined | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | __code__ | code object containing | -| | | compiled function | -| | | :term:`bytecode` | -+-----------+-----------------+---------------------------+ -| | __defaults__ | tuple of any default | -| | | values for positional or | -| | | keyword parameters | -+-----------+-----------------+---------------------------+ -| | __kwdefaults__ | mapping of any default | -| | | values for keyword-only | -| | | parameters | -+-----------+-----------------+---------------------------+ -| | __globals__ | global namespace in which | -| | | this function was defined | -+-----------+-----------------+---------------------------+ -| | __annotations__ | mapping of parameters | -| | | names to annotations; | -| | | ``"return"`` key is | -| | | reserved for return | -| | | annotations. | -+-----------+-----------------+---------------------------+ -| traceback | tb_frame | frame object at this | -| | | level | -+-----------+-----------------+---------------------------+ -| | tb_lasti | index of last attempted | -| | | instruction in bytecode | -+-----------+-----------------+---------------------------+ -| | tb_lineno | current line number in | -| | | Python source code | -+-----------+-----------------+---------------------------+ -| | tb_next | next inner traceback | -| | | object (called by this | -| | | level) | -+-----------+-----------------+---------------------------+ -| frame | f_back | next outer frame object | -| | | (this frame's caller) | -+-----------+-----------------+---------------------------+ -| | f_builtins | builtins namespace seen | -| | | by this frame | -+-----------+-----------------+---------------------------+ -| | f_code | code object being | -| | | executed in this frame | -+-----------+-----------------+---------------------------+ -| | f_globals | global namespace seen by | -| | | this frame | -+-----------+-----------------+---------------------------+ -| | f_lasti | index of last attempted | -| | | instruction in bytecode | -+-----------+-----------------+---------------------------+ -| | f_lineno | current line number in | -| | | Python source code | -+-----------+-----------------+---------------------------+ -| | f_locals | local namespace seen by | -| | | this frame | -+-----------+-----------------+---------------------------+ -| | f_restricted | 0 or 1 if frame is in | -| | | restricted execution mode | -+-----------+-----------------+---------------------------+ -| | f_trace | tracing function for this | -| | | frame, or ``None`` | -+-----------+-----------------+---------------------------+ -| code | co_argcount | number of arguments (not | -| | | including \* or \*\* | -| | | args) | -+-----------+-----------------+---------------------------+ -| | co_code | string of raw compiled | -| | | bytecode | -+-----------+-----------------+---------------------------+ -| | co_consts | tuple of constants used | -| | | in the bytecode | -+-----------+-----------------+---------------------------+ -| | co_filename | name of file in which | -| | | this code object was | -| | | created | -+-----------+-----------------+---------------------------+ -| | co_firstlineno | number of first line in | -| | | Python source code | -+-----------+-----------------+---------------------------+ -| | co_flags | bitmap: 1=optimized ``|`` | -| | | 2=newlocals ``|`` 4=\*arg | -| | | ``|`` 8=\*\*arg | -+-----------+-----------------+---------------------------+ -| | co_lnotab | encoded mapping of line | -| | | numbers to bytecode | -| | | indices | -+-----------+-----------------+---------------------------+ -| | co_name | name with which this code | -| | | object was defined | -+-----------+-----------------+---------------------------+ -| | co_names | tuple of names of local | -| | | variables | -+-----------+-----------------+---------------------------+ -| | co_nlocals | number of local variables | -+-----------+-----------------+---------------------------+ -| | co_stacksize | virtual machine stack | -| | | space required | -+-----------+-----------------+---------------------------+ -| | co_varnames | tuple of names of | -| | | arguments and local | -| | | variables | -+-----------+-----------------+---------------------------+ -| generator | __name__ | name | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | gi_frame | frame | -+-----------+-----------------+---------------------------+ -| | gi_running | is the generator running? | -+-----------+-----------------+---------------------------+ -| | gi_code | code | -+-----------+-----------------+---------------------------+ -| | gi_yieldfrom | object being iterated by | -| | | ``yield from``, or | -| | | ``None`` | -+-----------+-----------------+---------------------------+ -| coroutine | __name__ | name | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | cr_await | object being awaited on, | -| | | or ``None`` | -+-----------+-----------------+---------------------------+ -| | cr_frame | frame | -+-----------+-----------------+---------------------------+ -| | cr_running | is the coroutine running? | -+-----------+-----------------+---------------------------+ -| | cr_code | code | -+-----------+-----------------+---------------------------+ -| builtin | __doc__ | documentation string | -+-----------+-----------------+---------------------------+ -| | __name__ | original name of this | -| | | function or method | -+-----------+-----------------+---------------------------+ -| | __qualname__ | qualified name | -+-----------+-----------------+---------------------------+ -| | __self__ | instance to which a | -| | | method is bound, or | -| | | ``None`` | -+-----------+-----------------+---------------------------+ ++-----------+-------------------+---------------------------+ +| Type | Attribute | Description | ++===========+===================+===========================+ +| module | __doc__ | documentation string | ++-----------+-------------------+---------------------------+ +| | __file__ | filename (missing for | +| | | built-in modules) | ++-----------+-------------------+---------------------------+ +| class | __doc__ | documentation string | ++-----------+-------------------+---------------------------+ +| | __name__ | name with which this | +| | | class was defined | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | __module__ | name of module in which | +| | | this class was defined | ++-----------+-------------------+---------------------------+ +| method | __doc__ | documentation string | ++-----------+-------------------+---------------------------+ +| | __name__ | name with which this | +| | | method was defined | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | __func__ | function object | +| | | containing implementation | +| | | of method | ++-----------+-------------------+---------------------------+ +| | __self__ | instance to which this | +| | | method is bound, or | +| | | ``None`` | ++-----------+-------------------+---------------------------+ +| function | __doc__ | documentation string | ++-----------+-------------------+---------------------------+ +| | __name__ | name with which this | +| | | function was defined | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | __code__ | code object containing | +| | | compiled function | +| | | :term:`bytecode` | ++-----------+-------------------+---------------------------+ +| | __defaults__ | tuple of any default | +| | | values for positional or | +| | | keyword parameters | ++-----------+-------------------+---------------------------+ +| | __kwdefaults__ | mapping of any default | +| | | values for keyword-only | +| | | parameters | ++-----------+-------------------+---------------------------+ +| | __globals__ | global namespace in which | +| | | this function was defined | ++-----------+-------------------+---------------------------+ +| | __annotations__ | mapping of parameters | +| | | names to annotations; | +| | | ``"return"`` key is | +| | | reserved for return | +| | | annotations. | ++-----------+-------------------+---------------------------+ +| traceback | tb_frame | frame object at this | +| | | level | ++-----------+-------------------+---------------------------+ +| | tb_lasti | index of last attempted | +| | | instruction in | +| | | :term:`bytecode` | ++-----------+-------------------+---------------------------+ +| | tb_lineno | current line number in | +| | | Python source code | ++-----------+-------------------+---------------------------+ +| | tb_next | next inner traceback | +| | | object (called by this | +| | | level) | ++-----------+-------------------+---------------------------+ +| frame | f_back | next outer frame object | +| | | (this frame's caller) | ++-----------+-------------------+---------------------------+ +| | f_builtins | builtins namespace seen | +| | | by this frame | ++-----------+-------------------+---------------------------+ +| | f_code | code object being | +| | | executed in this frame | ++-----------+-------------------+---------------------------+ +| | f_globals | global namespace seen by | +| | | this frame | ++-----------+-------------------+---------------------------+ +| | f_lasti | index of last attempted | +| | | instruction in | +| | | :term:`bytecode` | ++-----------+-------------------+---------------------------+ +| | f_lineno | current line number in | +| | | Python source code | ++-----------+-------------------+---------------------------+ +| | f_locals | local namespace seen by | +| | | this frame | ++-----------+-------------------+---------------------------+ +| | f_restricted | 0 or 1 if frame is in | +| | | restricted execution mode | ++-----------+-------------------+---------------------------+ +| | f_trace | tracing function for this | +| | | frame, or ``None`` | ++-----------+-------------------+---------------------------+ +| code | co_argcount | number of arguments (not | +| | | including \*, \*\* args | +| | | or keyword only | +| | | arguments) | ++-----------+-------------------+---------------------------+ +| | co_code | string of raw compiled | +| | | :term:`bytecode` | ++-----------+-------------------+---------------------------+ +| | co_cellvars | tuple of names of local | +| | | variables that are | +| | | referenced by nested | +| | | functions | ++-----------+-------------------+---------------------------+ +| | co_consts | tuple of constants used | +| | | in the :term:`bytecode` | ++-----------+-------------------+---------------------------+ +| | co_filename | name of file in which | +| | | this code object was | +| | | created | ++-----------+-------------------+---------------------------+ +| | co_firstlineno | number of first line in | +| | | Python source code | ++-----------+-------------------+---------------------------+ +| | co_flags | bitmap: 1=optimized ``|`` | +| | | 2=newlocals ``|`` 4=\*arg | +| | | ``|`` 8=\*\*arg ``|`` | +| | | 16=nested ``|`` | +| | | 32=generator ``|`` | +| | | 64=nofree ``|`` | +| | | 128=coroutine ``|`` | +| | | 256=iterable_coroutine | ++-----------+-------------------+---------------------------+ +| | co_freevars | tuple of names of free | +| | | variables | ++-----------+-------------------+---------------------------+ +| | co_kwonlyargcount | number of keyword only | +| | | arguments (not including | +| | | \*\*arg) | ++-----------+-------------------+---------------------------+ +| | co_lnotab | encoded mapping of line | +| | | numbers to | +| | | :term:`bytecode` indices | ++-----------+-------------------+---------------------------+ +| | co_name | name with which this code | +| | | object was defined | ++-----------+-------------------+---------------------------+ +| | co_names | tuple of names of local | +| | | variables | ++-----------+-------------------+---------------------------+ +| | co_nlocals | number of local variables | ++-----------+-------------------+---------------------------+ +| | co_stacksize | virtual machine stack | +| | | space required | ++-----------+-------------------+---------------------------+ +| | co_varnames | tuple of names of | +| | | arguments and local | +| | | variables | ++-----------+-------------------+---------------------------+ +| generator | __name__ | name | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | gi_frame | frame | ++-----------+-------------------+---------------------------+ +| | gi_running | is the generator running? | ++-----------+-------------------+---------------------------+ +| | gi_code | code | ++-----------+-------------------+---------------------------+ +| | gi_yieldfrom | object being iterated by | +| | | ``yield from``, or | +| | | ``None`` | ++-----------+-------------------+---------------------------+ +| coroutine | __name__ | name | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | cr_await | object being awaited on, | +| | | or ``None`` | ++-----------+-------------------+---------------------------+ +| | cr_frame | frame | ++-----------+-------------------+---------------------------+ +| | cr_running | is the coroutine running? | ++-----------+-------------------+---------------------------+ +| | cr_code | code | ++-----------+-------------------+---------------------------+ +| builtin | __doc__ | documentation string | ++-----------+-------------------+---------------------------+ +| | __name__ | original name of this | +| | | function or method | ++-----------+-------------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-------------------+---------------------------+ +| | __self__ | instance to which a | +| | | method is bound, or | +| | | ``None`` | ++-----------+-------------------+---------------------------+ .. versionchanged:: 3.5 diff -r 50a94e1cabe0 Lib/configparser.py --- a/Lib/configparser.py Mon May 09 00:14:22 2016 +0300 +++ b/Lib/configparser.py Tue May 10 19:00:03 2016 +0800 @@ -494,7 +494,7 @@ opt = parser.optionxform(path[0]) v = map[opt] elif len(path) == 2: - sect = path[0] + sect = parser.sectionxform(path[0]) opt = parser.optionxform(path[1]) v = parser.get(sect, opt, raw=True) else: @@ -608,6 +608,7 @@ self._defaults = self._dict() self._converters = ConverterMapping(self) self._proxies = self._dict() + default_section = self.sectionxform(default_section) self._proxies[default_section] = SectionProxy(self, default_section) if defaults: for key, value in defaults.items(): @@ -651,6 +652,7 @@ Raise DuplicateSectionError if a section by the specified name already exists. Raise ValueError if name is DEFAULT. """ + section = self.sectionxform(section) if section == self.default_section: raise ValueError('Invalid section name: %r' % section) @@ -664,11 +666,12 @@ The DEFAULT section is not acknowledged. """ - return section in self._sections + return self.sectionxform(section) in self._sections def options(self, section): """Return a list of option names for the given section name.""" try: + section = self.sectionxform(section) opts = self._sections[section].copy() except KeyError: raise NoSectionError(section) from None @@ -734,7 +737,7 @@ """ elements_added = set() for section, keys in dictionary.items(): - section = str(section) + section = self.sectionxform(str(section)) try: self.add_section(section) except (DuplicateSectionError, ValueError): @@ -774,6 +777,7 @@ The section DEFAULT is special. """ + section = self.sectionxform(section) try: d = self._unify_values(section, vars) except NoSectionError: @@ -838,6 +842,7 @@ """ if section is _UNSET: return super().items() + section = self.sectionxform(section) d = self._defaults.copy() try: d.update(self._sections[section]) @@ -870,10 +875,14 @@ def optionxform(self, optionstr): return optionstr.lower() + def sectionxform(self, sectionstr): + return sectionstr + def has_option(self, section, option): """Check for the existence of a given option in a given section. If the specified `section' is None or an empty string, DEFAULT is assumed. If the specified `section' does not exist, returns False.""" + section = self.sectionxform(section) if not section or section == self.default_section: option = self.optionxform(option) return option in self._defaults @@ -886,6 +895,8 @@ def set(self, section, option, value=None): """Set an option.""" + option = self.optionxform(option) + section = self.sectionxform(section) if value: value = self._interpolation.before_set(self, section, option, value) @@ -896,7 +907,7 @@ sectdict = self._sections[section] except KeyError: raise NoSectionError(section) from None - sectdict[self.optionxform(option)] = value + sectdict[option] = value def write(self, fp, space_around_delimiters=True): """Write an .ini-format representation of the configuration state. @@ -930,6 +941,7 @@ def remove_option(self, section, option): """Remove an option.""" + section = self.sectionxform(section) if not section or section == self.default_section: sectdict = self._defaults else: @@ -945,6 +957,7 @@ def remove_section(self, section): """Remove a file section.""" + section = self.sectionxform(section) existed = section in self._sections if existed: del self._sections[section] @@ -952,6 +965,7 @@ return existed def __getitem__(self, key): + key = self.sectionxform(key) if key != self.default_section and not self.has_section(key): raise KeyError(key) return self._proxies[key] @@ -962,6 +976,7 @@ # XXX this is not atomic if read_dict fails at any point. Then again, # no update method in configparser is atomic in this implementation. + key = self.sectionxform(key) if key == self.default_section: self._defaults.clear() elif key in self._sections: @@ -969,6 +984,7 @@ self.read_dict({key: value}) def __delitem__(self, key): + key = self.sectionxform(key) if key == self.default_section: raise ValueError("Cannot remove the default section.") if not self.has_section(key): @@ -976,6 +992,7 @@ self.remove_section(key) def __contains__(self, key): + key = self.sectionxform(key) return key == self.default_section or self.has_section(key) def __len__(self): @@ -1056,7 +1073,7 @@ # is it a section header? mo = self.SECTCRE.match(value) if mo: - sectname = mo.group('header') + sectname = self.sectionxform(mo.group('header')) if sectname in self._sections: if self._strict and sectname in elements_added: raise DuplicateSectionError(sectname, fpname, diff -r 50a94e1cabe0 Lib/inspect.py --- a/Lib/inspect.py Mon May 09 00:14:22 2016 +0300 +++ b/Lib/inspect.py Tue May 10 19:00:03 2016 +0800 @@ -242,18 +242,25 @@ """Return true if the object is a code object. Code objects provide these attributes: - co_argcount number of arguments (not including * or ** args) - co_code string of raw compiled bytecode - co_consts tuple of constants used in the bytecode - co_filename name of file in which this code object was created - co_firstlineno number of first line in Python source code - co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg - co_lnotab encoded mapping of line numbers to bytecode indices - co_name name with which this code object was defined - co_names tuple of names of local variables - co_nlocals number of local variables - co_stacksize virtual machine stack space required - co_varnames tuple of names of arguments and local variables""" + co_argcount number of arguments (not including *, ** args + or keyword only arguments) + co_code string of raw compiled bytecode + co_cellvars tuple of names of local variables that are + referenced by nested functions + co_consts tuple of constants used in the bytecode + co_filename name of file in which this code object was created + co_firstlineno number of first line in Python source code + co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg + | 16=nested | 32=generator | 64=nofree | 128=coroutine + | 256=iterable_coroutine + co_freevars tuple of names of free variables + co_kwonlyargcount number of keyword only arguments (not including **arg) + co_lnotab encoded mapping of line numbers to bytecode indices + co_name name with which this code object was defined + co_names tuple of names of local variables + co_nlocals number of local variables + co_stacksize virtual machine stack space required + co_varnames tuple of names of arguments and local variables""" return isinstance(object, types.CodeType) def isbuiltin(object): diff -r 50a94e1cabe0 Lib/test/test_configparser.py --- a/Lib/test/test_configparser.py Mon May 09 00:14:22 2016 +0300 +++ b/Lib/test/test_configparser.py Tue May 10 19:00:03 2016 +0800 @@ -1090,10 +1090,12 @@ default_section = 'common' strict = True - def fromstring(self, string, defaults=None, optionxform=None): + def fromstring(self, string, defaults=None, optionxform=None, sectionxform=None): cf = self.newconfig(defaults) if optionxform: cf.optionxform = optionxform + if sectionxform: + cf.sectionxform = sectionxform cf.read_string(string) return cf @@ -1231,6 +1233,29 @@ eq(cf['random']['foo'], 'value redefined') eq(cf['random']['Foo'], 'A Better Value Redefined') + def test_sectionxform(self): + ini = textwrap.dedent(""" + [Test] + option = value + """).strip() + cf = self.fromstring(ini, sectionxform=lambda sect: sect.lower()) + with self.assertRaises(ValueError): + cf.add_section('COMMON') + with self.assertRaises(configparser.DuplicateSectionError): + cf.add_section('test') + with self.assertRaises(configparser.DuplicateSectionError): + cf.read_dict({ + 'test': {'option1': 'value1'}, + 'TEST': {'option2': 'value2'} + }) + cf.set('TEST', 'key', 'value') + self.assertTrue(cf.has_section('TEST')) + self.assertTrue(cf.has_section('test')) + self.assertEqual(cf.get('TEST', 'option'), 'value') + self.assertEqual(cf.get('test', 'option'), 'value') + self.assertEqual(cf.get('Test', 'key'), 'value') + self.assertEqual(cf.get('test', 'key'), 'value') + def test_other_errors(self): cf = self.fromstring(""" [interpolation fail] diff -r 50a94e1cabe0 Python/ceval.c --- a/Python/ceval.c Mon May 09 00:14:22 2016 +0300 +++ b/Python/ceval.c Tue May 10 19:00:03 2016 +0800 @@ -3291,6 +3291,7 @@ PyObject *anns = PyDict_New(); if (anns == NULL) { Py_DECREF(func); + Py_DECREF(names); goto error; } name_ix = PyTuple_Size(names); @@ -3306,9 +3307,11 @@ if (err != 0) { Py_DECREF(anns); Py_DECREF(func); + Py_DECREF(names); goto error; } } + Py_DECREF(names); if (PyFunction_SetAnnotations(func, anns) != 0) { /* Can't happen unless @@ -3318,7 +3321,6 @@ goto error; } Py_DECREF(anns); - Py_DECREF(names); } /* XXX Maybe this should be a separate opcode? */