feat: Make dump for fns, classes more stable and helpful (#36188)
The `dump_settings` command currently prints out the raw `repr(...)`s for
defined functions, e.g.:
"WIKI_CAN_ASSIGN": "<function CAN_ASSIGN at 0x74ce5e9b2020>",
In addition to being uninformative, these `at 0x74ce...` hashes change every
run, so they appear in the diff as having "changed" every time. With this
commit, here's what `dump_settings` will print out for a function instead:
"WIKI_CAN_ASSIGN": {
"module": "lms.djangoapps.course_wiki.settings",
"qualname": "CAN_ASSIGN"
},
This commit is contained in:
@@ -33,13 +33,14 @@ class Command(BaseCommand):
|
||||
* _("hello") # <-- this will become just "hello"
|
||||
* "hello"
|
||||
|
||||
Furthermore, objects which are not easily JSON-ifiable will stringified using their `repr(...)`, e.g.:
|
||||
* "Path('my/path')" # a Path object
|
||||
* "<lms.myapp.MyClass object at 0x704599fa2fd0>" # some random class instance
|
||||
* "<_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>" # sys.stderr
|
||||
Furthermore, functions and classes are printed as JSON objects like:
|
||||
{
|
||||
"module": "path.to.module",
|
||||
"qualname": "MyClass.MyInnerClass.my_method", // Or, "<lambda>"
|
||||
"source_hint": "MY_SETTING = lambda: x + y", // For <lambda>s only
|
||||
}
|
||||
|
||||
and lambdas are printed by *roughly* printing out their source lines (it's impossible in Python to get the *exact*
|
||||
source code, as it's been compiled into bytecode).
|
||||
And everything else will be stringified as its `repr(...)`.
|
||||
"""
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
@@ -82,12 +83,18 @@ def _to_json_friendly_repr(value: object) -> object:
|
||||
# Print gettext_lazy as simply the wrapped string
|
||||
return proxy_args[0]
|
||||
try:
|
||||
module = value.__module__
|
||||
qualname = value.__qualname__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if qualname == "<lambda>":
|
||||
# Handle lambdas by printing the source lines
|
||||
return "lambda defined with line(s): " + inspect.getsource(value).strip()
|
||||
# Handle functions and classes by printing their location (plus approximate source, for lambdas)
|
||||
return {
|
||||
"module": module,
|
||||
"qualname": qualname,
|
||||
**({
|
||||
"source_hint": inspect.getsource(value).strip(),
|
||||
} if qualname == "<lambda>" else {}),
|
||||
}
|
||||
# For all other objects, print the repr
|
||||
return repr(value)
|
||||
|
||||
@@ -26,8 +26,8 @@ def test_for_lms_settings(capsys):
|
||||
# Check: tuples are converted to lists
|
||||
assert isinstance(dump['XBLOCK_MIXINS'], list)
|
||||
|
||||
# Check: objects (like classes) are repr'd
|
||||
assert "<class 'xmodule.x_module.XModuleMixin'>" in dump['XBLOCK_MIXINS']
|
||||
# Check: classes are converted to dicts of info on the class location
|
||||
assert {"module": "xmodule.x_module", "qualname": "XModuleMixin"} in dump['XBLOCK_MIXINS']
|
||||
|
||||
# Check: nested dictionaries come through OK, and int'l strings are just strings
|
||||
assert dump['COURSE_ENROLLMENT_MODES']['audit']['display_name'] == "Audit"
|
||||
@@ -46,8 +46,8 @@ def test_for_cms_settings(capsys):
|
||||
# Check: tuples are converted to lists
|
||||
assert isinstance(dump['XBLOCK_MIXINS'], list)
|
||||
|
||||
# Check: objects (like classes) are repr'd
|
||||
assert "<class 'xmodule.x_module.XModuleMixin'>" in dump['XBLOCK_MIXINS']
|
||||
# Check: classes are converted to dicts of info on the class location
|
||||
assert {"module": "xmodule.x_module", "qualname": "XModuleMixin"} in dump['XBLOCK_MIXINS']
|
||||
|
||||
# Check: nested dictionaries come through OK, and int'l strings are just strings
|
||||
assert dump['COURSE_ENROLLMENT_MODES']['audit']['display_name'] == "Audit"
|
||||
|
||||
Reference in New Issue
Block a user