-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
10a5a06
72be2b1
8b7764f
7a05656
d7b6281
1e0ceb1
394b83f
fa3f10b
fdb7081
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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): | ||
|
||
Create the parser. When `defaults` is given, it is initialized into the | ||
dictionary or intrinsic defaults. The keys must be strings, the values | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. options without a section? |
||
``configparser.UNNAMED_SECTION``. In the file, they are placed before | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
||
|
@@ -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" | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did 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. | ||
|
@@ -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) | ||
|
@@ -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 | ||
|
||
pslacerda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
indent_level = cur_indent_level | ||
# is it a section header? | ||
mo = self.SECTCRE.match(value) | ||
|
@@ -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? | ||
pslacerda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -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: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
:class:`configparser.ConfigParser` now accepts unnamed sections before named | ||
ones, if configured to do so. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. “accepts an unnamed section before named ones” might be clearer |
||
|
||
Contributed by Pedro Sousa Lacerda and Christian Siefkes. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems ok.