Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-66449: Add support to unnamed sections in ConfigParser#2735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
pslacerda wants to merge9 commits intopython:mainfrompslacerda:fix-issue-issue22253
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletionsLib/configparser.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,8 @@
delimiters=('=', ':'), comment_prefixes=('#', ';'),
inline_comment_prefixes=None, strict=True,
empty_lines_in_values=True, default_section='DEFAULT',
interpolation=<unset>, converters=<unset>):
interpolation=<unset>, converters=<unset>,
allow_unnamed_section=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

allow_unnamed_section not implemented

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This seems ok.


Create the parser. When `defaults` is given, it is initialized into the
dictionary or intrinsic defaults. The keys must be strings, the values
Expand DownExpand Up@@ -68,6 +69,12 @@
converter gets its corresponding get*() method on the parser object and
section proxies.

When `allow_unnamed_section` is True (default: False), options
without section are accepted: the section for this is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

options withouta section?

``configparser.UNNAMED_SECTION``. In the file, they are placed before
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

configparser.TOP_LEVEL?

the first explicit section header. This also allows reading and writing
files that don't contain any explicit section headers.

sections()
Return all the configuration section names, sans DEFAULT.

Expand DownExpand Up@@ -156,7 +163,7 @@
"ConfigParser", "RawConfigParser",
"Interpolation", "BasicInterpolation", "ExtendedInterpolation",
"LegacyInterpolation", "SectionProxy", "ConverterMapping",
"DEFAULTSECT", "MAX_INTERPOLATION_DEPTH")
"DEFAULTSECT", "MAX_INTERPOLATION_DEPTH", "TOP_LEVEL")

_default_dict = dict
DEFAULTSECT = "DEFAULT"
Expand DownExpand Up@@ -323,6 +330,20 @@ def __init__(self, filename, lineno, line):
self.args = (filename, lineno, line)


class _UnnamedSection:

def __repr__(self):
return "<TOP_LEVEL>"

def __eq__(self, other):
return repr(self) == repr(other)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Why did__eq__ get implemented? It makes more sense to me to keep the default inherited implementation. Doesn’t the class only get instantiated once anyway?

Won’t this make a valid section headed [<TOP_LEVEL>] match the headerless section in some cases?


def __hash__(self):
return hash(repr(self))

TOP_LEVEL = _UnnamedSection()


# Used in parser getters to indicate the default behaviour when a specific
# option is not found it to raise an exception. Created to enable `None` as
# a valid fallback value.
Expand DownExpand Up@@ -902,7 +923,8 @@ def write(self, fp, space_around_delimiters=True):

def _write_section(self, fp, section_name, section_items, delimiter):
"""Write a single section to the specified `fp`."""
fp.write("[{}]\n".format(section_name))
if section_name is not TOP_LEVEL:
fp.write(f"[{section_name}]\n")
for key, value in section_items:
value = self._interpolation.before_write(self, section_name, key,
value)
Expand DownExpand Up@@ -1038,6 +1060,10 @@ def _read(self, fp, fpname):
cursect[optname].append(value)
# a section header or option header?
else:
if self.default_section is TOP_LEVEL and cursect is None:
sectname = TOP_LEVEL
cursect = self._defaults

indent_level = cur_indent_level
# is it a section header?
mo = self.SECTCRE.match(value)
Expand All@@ -1058,7 +1084,7 @@ def _read(self, fp, fpname):
elements_added.add(sectname)
# So sections can't start with a continuation line
optname = None
# no section header in the file?
# no section header?
elif cursect is None:
raise MissingSectionHeaderError(fpname, lineno, line)
# an option line?
Expand DownExpand Up@@ -1088,6 +1114,7 @@ def _read(self, fp, fpname):
# raised at the end of the file and will contain a
# list of all bogus lines
e = self._handle_error(e, fpname, lineno, line)

self._join_multiline_values()
# if any parsing errors occurred, raise an exception
if e:
Expand Down
72 changes: 72 additions & 0 deletionsLib/test/test_configparser.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2119,6 +2119,78 @@ def test_instance_assignment(self):
self.assertEqual(cfg['two'].getlen('one'), 5)


class SectionlessTestCase(unittest.TestCase):

def fromstring(self, default_section, string):
cfg = configparser.ConfigParser(default_section=default_section)
cfg.read_string(string)
return cfg

def test_only_top_level(self):
cfg1 = self.fromstring(configparser.TOP_LEVEL, """
a = 1
b = 2
""")

self.assertEqual([], cfg1.sections())
self.assertEqual('1', cfg1[configparser.TOP_LEVEL]['a'])
self.assertEqual('2', cfg1[configparser.TOP_LEVEL]['b'])

output = io.StringIO()
cfg1.write(output)
cfg2 = self.fromstring(configparser.TOP_LEVEL, output.getvalue())

self.assertEqual([], cfg1.sections())
self.assertEqual('1', cfg2[configparser.TOP_LEVEL]['a'])
self.assertEqual('2', cfg2[configparser.TOP_LEVEL]['b'])

def test_only_regular(self):
cfg1 = self.fromstring(configparser.TOP_LEVEL, """
[sect1]
a = 1
b = 2
""")

self.assertEqual(['sect1'], cfg1.sections())
self.assertEqual('1', cfg1['sect1']['a'])
self.assertEqual('2', cfg1['sect1']['b'])

output = io.StringIO()
cfg1.write(output)
cfg2 = self.fromstring(configparser.TOP_LEVEL, output.getvalue())

self.assertEqual(['sect1'], cfg2.sections())
self.assertEqual('1', cfg2['sect1']['a'])
self.assertEqual('2', cfg2['sect1']['b'])

def test_top_level_and_regular(self):
cfg1 = self.fromstring(configparser.TOP_LEVEL, """
a = 1
b = 2
[sect1]
c = 3
d = %(a)s%(b)s%(c)s
""")

self.assertEqual(['sect1'], cfg1.sections())
self.assertEqual('1', cfg1[configparser.TOP_LEVEL]['a'])
self.assertEqual('2', cfg1[configparser.TOP_LEVEL]['b'])
self.assertEqual('3', cfg1['sect1']['c'])
self.assertEqual('1', cfg1['sect1']['a'])
self.assertEqual('123', cfg1['sect1']['d'])

output = io.StringIO()
cfg1.write(output)
cfg2 = self.fromstring(configparser.TOP_LEVEL, output.getvalue())

self.assertEqual(['sect1'], cfg1.sections())
self.assertEqual('1', cfg2[configparser.TOP_LEVEL]['a'])
self.assertEqual('2', cfg2[configparser.TOP_LEVEL]['b'])
self.assertEqual('3', cfg2['sect1']['c'])
self.assertEqual('1', cfg2['sect1']['a'])
self.assertEqual('123', cfg2['sect1']['d'])


class MiscTestCase(unittest.TestCase):
def test__all__(self):
support.check__all__(self, configparser, not_exported={"Error"})
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
:class:`configparser.ConfigParser` now accepts unnamed sections before named
ones, if configured to do so.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

“acceptsan unnamedsection before named ones” might be clearer


Contributed by Pedro Sousa Lacerda and Christian Siefkes.

[8]ページ先頭

©2009-2025 Movatter.jp