Skip to content

Commit 0209a0f

Browse files
committed
Improve env.Dump('json') a bit
When doing a Dump in json mode, custom SCons classes are not examined by default (unlike the standard pprint() Dump), and end up in the un-serialized bucket. Mostly, that's fine, but the BUILDERS dictionary contains valueable information (which builders actually got configured), as do the CLVar instances - these contain actual cmdline options that will be issued. Update the function called when an object looks unserializable to recognize subclasses of UserDict (which covers BuilderDict) and UserList (which covers CLVar) and return their .data field. Also tell the json encoder to sort the keys. The adjacent function in the source file got a small tweak (non-functional). Fixes SCons#4493 Signed-off-by: Mats Wichmann <[email protected]>
1 parent 04c86e1 commit 0209a0f

File tree

4 files changed

+45
-27
lines changed

4 files changed

+45
-27
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
1212
From Mats Wichmann:
1313

1414
- Updated Value Node docs and tests.
15+
- Dump() with json format selected now recognizes additional compound types
16+
(UserDict and UserList), which improves the detail of the display.
17+
json output is also sorted, to match the default display.
1518

1619

1720
RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700

RELEASE.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ DEPRECATED FUNCTIONALITY
2626
CHANGED/ENHANCED EXISTING FUNCTIONALITY
2727
---------------------------------------
2828

29-
- List modifications to existing features, where the previous behavior
30-
wouldn't actually be considered a bug
29+
- Dump() with json format selected now recognizes additional compound types
30+
(UserDict and UserList), which improves the detail of the display.
31+
json output is also sorted, to match the default display.
3132

3233
FIXES
3334
-----

SCons/Environment.py

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
import sys
3636
import re
3737
import shlex
38-
from collections import UserDict, deque
38+
from collections import UserDict, UserList, deque
3939
from subprocess import PIPE, DEVNULL
40-
from typing import Optional
40+
from typing import Optional, Sequence
4141

4242
import SCons.Action
4343
import SCons.Builder
@@ -1687,17 +1687,23 @@ def Dictionary(self, *args):
16871687
return dlist
16881688

16891689

1690-
def Dump(self, key=None, format: str='pretty'):
1691-
""" Return construction variables serialized to a string.
1690+
def Dump(self, key: Optional[str] = None, format: str = 'pretty') -> str:
1691+
""" Returns a dump of serialized construction variables.
1692+
1693+
The display formats are intended for humaan readers when
1694+
debugging - none of the supported formats produce a result that
1695+
SCons itself can directly make use of. Objects that cannot
1696+
directly be represented get a placeholder like
1697+
``<function foo at 0x123456>`` or ``<<non-serializable: function>>``.
16921698
16931699
Args:
1694-
key (optional): if None, format the whole dict of variables.
1695-
Else format the value of `key` (Default value = None)
1696-
format (str, optional): specify the format to serialize to.
1697-
`"pretty"` generates a pretty-printed string,
1698-
`"json"` a JSON-formatted string.
1699-
(Default value = `"pretty"`)
1700+
key: if ``None``, format the whole dict of variables,
1701+
else format just the value of *key*.
1702+
format: specify the format to serialize to. ``"pretty"`` generates
1703+
a pretty-printed string, ``"json"`` a JSON-formatted string.
17001704
1705+
Raises:
1706+
ValueError: *format* is not a recognized serialization format.
17011707
"""
17021708
if key:
17031709
cvars = self.Dictionary(key)
@@ -1707,9 +1713,9 @@ def Dump(self, key=None, format: str='pretty'):
17071713
fmt = format.lower()
17081714

17091715
if fmt == 'pretty':
1710-
import pprint
1711-
pp = pprint.PrettyPrinter(indent=2)
1716+
import pprint # pylint: disable=import-outside-toplevel
17121717

1718+
pp = pprint.PrettyPrinter(indent=2)
17131719
# TODO: pprint doesn't do a nice job on path-style values
17141720
# if the paths contain spaces (i.e. Windows), because the
17151721
# algorithm tries to break lines on spaces, while breaking
@@ -1718,26 +1724,33 @@ def Dump(self, key=None, format: str='pretty'):
17181724
return pp.pformat(cvars)
17191725

17201726
elif fmt == 'json':
1721-
import json
1722-
def non_serializable(obj):
1723-
return '<<non-serializable: %s>>' % type(obj).__qualname__
1724-
return json.dumps(cvars, indent=4, default=non_serializable)
1727+
import json # pylint: disable=import-outside-toplevel
1728+
1729+
class DumpEncoder(json.JSONEncoder):
1730+
"""SCons special json Dump formatter."""
1731+
def default(self, obj):
1732+
if isinstance(obj, (UserList, UserDict)):
1733+
return obj.data
1734+
return f'<<non-serializable: {type(obj).__qualname__}>>'
1735+
1736+
return json.dumps(cvars, indent=4, cls=DumpEncoder, sort_keys=True)
17251737
else:
17261738
raise ValueError("Unsupported serialization format: %s." % fmt)
17271739

17281740

1729-
def FindIxes(self, paths, prefix, suffix):
1730-
"""Search a list of paths for something that matches the prefix and suffix.
1741+
def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> Optional[str]:
1742+
"""Search *paths* for a path that has *prefix* and *suffix*.
17311743
1732-
Args:
1733-
paths: the list of paths or nodes.
1734-
prefix: construction variable for the prefix.
1735-
suffix: construction variable for the suffix.
1744+
Returns on first match.
17361745
1737-
Returns: the matched path or None
1746+
Arguments:
1747+
paths: the list of paths or nodes.
1748+
prefix: construction variable for the prefix.
1749+
suffix: construction variable for the suffix.
17381750
1751+
Returns:
1752+
The matched path or ``None``
17391753
"""
1740-
17411754
suffix = self.subst('$'+suffix)
17421755
prefix = self.subst('$'+prefix)
17431756

SCons/EnvironmentTests.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3182,7 +3182,8 @@ def test_Dump(self) -> None:
31823182
assert len(env.Dump()) > 200, env.Dump() # no args version
31833183

31843184
assert env.Dump('FOO', 'json') == '"foo"' # JSON key version
3185-
self.assertEqual(env.Dump('FOOFLAGS', 'json'), '"<<non-serializable: CLVar>>"')
3185+
expect = """[\n "--bar",\n "--baz"\n]"""
3186+
self.assertEqual(env.Dump('FOOFLAGS', 'json'), expect)
31863187
import json
31873188
env_dict = json.loads(env.Dump(format = 'json'))
31883189
assert env_dict['FOO'] == 'foo' # full JSON version

0 commit comments

Comments
 (0)