Skip to content

Commit 1a03c72

Browse files
committed
Track all config source for repr
1 parent 155b8ef commit 1a03c72

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

Lib/configparser.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ def __init__(self, defaults=None, dict_type=_default_dict,
689689
if defaults:
690690
self._read_defaults(defaults)
691691
self._allow_unnamed_section = allow_unnamed_section
692-
self._loaded_files = []
692+
693693

694694
def defaults(self):
695695
return self._defaults
@@ -753,12 +753,13 @@ def read(self, filenames, encoding=None):
753753
try:
754754
with open(filename, encoding=encoding) as fp:
755755
self._read(fp, filename)
756+
self._loaded_sources.append(filename)
756757
except OSError:
757758
continue
758759
if isinstance(filename, os.PathLike):
759760
filename = os.fspath(filename)
760761
read_ok.append(filename)
761-
self._loaded_files.extend(read_ok)
762+
762763
return read_ok
763764

764765
def read_file(self, f, source=None):
@@ -775,11 +776,13 @@ def read_file(self, f, source=None):
775776
except AttributeError:
776777
source = '<???>'
777778
self._read(f, source)
779+
self._loaded_sources.append(source)
778780

779781
def read_string(self, string, source='<string>'):
780782
"""Read configuration from a given string."""
781783
sfile = io.StringIO(string)
782784
self.read_file(sfile, source)
785+
self._loaded_sources.append(source)
783786

784787
def read_dict(self, dictionary, source='<dict>'):
785788
"""Read configuration from a dictionary.
@@ -811,6 +814,8 @@ def read_dict(self, dictionary, source='<dict>'):
811814
raise DuplicateOptionError(section, key, source)
812815
elements_added.add((section, key))
813816
self.set(section, key, value)
817+
self._loaded_sources.append(source)
818+
814819

815820
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
816821
"""Get an option value for a given section.
@@ -1069,12 +1074,15 @@ def __repr__(self):
10691074
"interpolation": type(self._interpolation).__name__,
10701075
}
10711076
init_params = {k: v for k, v in init_params.items() if v is not None}
1072-
10731077
state_summary = {
1074-
"loaded_files": self._loaded_files if hasattr(self, '_loaded_files') else "(no files loaded)",
1075-
"sections": len(self._sections),
1078+
"loaded_sources": self._loaded_sources,
1079+
"sections_count": len(self._sections),
1080+
"sections": list(self._sections.keys())[:5], # Limit to 5 section names for readability
10761081
}
10771082

1083+
if len(self._sections) > 5:
1084+
state_summary["sections_truncated"] = f"...and {len(self._sections) - 5} more"
1085+
10781086
return (f"<{self.__class__.__name__}("
10791087
f"params={init_params}, "
10801088
f"state={state_summary})>")

Lib/test/test_configparser.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -983,12 +983,19 @@ def test_set_nonstring_types(self):
983983
def test_str_and_repr(self):
984984
self.maxDiff = None
985985
cf = self.config_class(allow_no_value=True, delimiters=('=',), strict=True)
986-
987-
cf.add_section("sect")
988-
cf.set("sect", "option1", "foo")
989-
cf.set("sect", "option2", "bar")
990-
991-
expected_str = "{'sect': {'option1': 'foo', 'option2': 'bar'}}"
986+
cf.add_section("sect1")
987+
cf.add_section("sect2")
988+
cf.add_section("sect3")
989+
cf.add_section("sect4")
990+
cf.add_section("sect5")
991+
cf.add_section("sect6") # Added more than 5 sections to trigger truncation
992+
cf.set("sect1", "option1", "foo")
993+
cf.set("sect2", "option2", "bar")
994+
995+
expected_str = (
996+
"{'sect1': {'option1': 'foo'}, 'sect2': {'option2': 'bar'}, 'sect3': {}, "
997+
"'sect4': {}, 'sect5': {}, 'sect6': {}}"
998+
)
992999
self.assertEqual(str(cf), expected_str)
9931000

9941001
dict_type = type(cf._dict).__name__
@@ -998,10 +1005,13 @@ def test_str_and_repr(self):
9981005
f"params={{'dict_type': '{dict_type}', 'allow_no_value': True, "
9991006
"'delimiters': ('=',), 'strict': True, 'default_section': 'DEFAULT', "
10001007
"'interpolation': 'BasicInterpolation'}, "
1001-
"state={'loaded_files': [], 'sections': 1})>"
1008+
"state={'loaded_sources': [], 'sections_count': 6, "
1009+
"'sections': ['sect1', 'sect2', 'sect3', 'sect4', 'sect5'], "
1010+
"'sections_truncated': '...and 1 more'})>"
10021011
)
10031012
self.assertEqual(repr(cf), expected_repr)
10041013

1014+
10051015
def test_add_section_default(self):
10061016
cf = self.newconfig()
10071017
self.assertRaises(ValueError, cf.add_section, self.default_section)

0 commit comments

Comments
 (0)