0

While writing some debugging python, I seem to have created a bit of ugly code that I would like to clean up.

Here is the function in its entirety:

def debug(message, variable=None):
    if variable and DEBUG:
        print("\n" + str(type(variable)))
        try:
            if isinstance(variable, (list, dict, 'OrderedDict')):
                variable = json.dumps(
                    variable,
                    indent=4,
                    sort_keys=True
                )
        except TypeError:
            if isinstance(variable, (list, dict)):
                variable = json.dumps(
                    variable,
                    indent=4,
                    sort_keys=True
                )

    if DEBUG:
        if variable:
            print(message + str(variable) + "\n")
        else:
            print("\n" + message)

I specifically despise my try-except statement, because not only am I repeating code, but if I run into another dictionary class (like CaseInsensitiveDict from requests' headers) that I would like to print nicely during debugging output I would have to nest try-except statements.

Is there a way that I could check if type(variable) is like *dict* or *list* then add it when creating the tuple for use in isinstance?

Robert J
  • 840
  • 10
  • 20
  • 1
    `'OrderedDict'` is not a valid type, that's a *string*. – Martijn Pieters Dec 09 '17 at 21:58
  • 1
    The only reason your code throws an exception is because you used a string as a type in your `isinstance()` call. There is no reason to nest any type errors here. – Martijn Pieters Dec 09 '17 at 22:04
  • 1
    It seems like you could be using the `logging` module to handle much of this logic. It's also not clear why you would check for an instance and still expect a `TypeError` as a possibility. – chepner Dec 09 '17 at 22:04
  • Thanks for reaching out. It is a set class when using `xmltodict`, and this code behaves as expected when parsing variables set from the use of `xmltodict.parse(xml_string)`. From what I have read the tuple has to be either types or classes. – Robert J Dec 09 '17 at 22:04
  • I will have to check out the `logging` module I have not used this before. – Robert J Dec 09 '17 at 22:06
  • The second easiest thing to do is make the caller responsible for passing something appropriate for `json.dumps` to handle. The easiest thing to do is make the caller responsible for passing the stringified data in the first place. All your function needs to do is assemble the output and print it if `DEBUG` is set. – chepner Dec 09 '17 at 22:08

1 Answers1

2

You want to look at the @functools.singledispatch() construct; this lets you delegate to specific functions to handle your debugging, keyed on types:

from functools import singledispatch

def debug(message, variable=None):
    if not DEBUG:
        return
    variable = debug_display(variable)
    print('{}{}\n'.format(message, variable))

@singledispatch
def debug_display(variable):
    return str(variable)

@debug_display.register(type(None))
def _none_to_empty_string(_ignored):
    return ''

@debug_display.register(dict)
@debug_display.register(list)
@debug_display.register(OrderedDict)
def _as_json(variable):
    return json.dumps(
        variable,
        indent=4,
        sort_keys=True
    )

@debug_display.register(SomeOtherType)
def _handle_specific_type(variable):
    # handle custom types any way you need to with separate registrations
    return "{} -> {}".format(variable.spam, variable.ham)

singledispatch knows how to delegate for subclasses that don't have specific handlers; so OrderedDict is handled by the _as_json handler because it is a subclass of dict.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343