Multiqc: Plots get identical id's if not manually supplied

Created on 3 Apr 2017  路  4Comments  路  Source: ewels/MultiQC

When toying with some graphs for the bbmap module I encountered an issue where all but the first plot included in the output report are gray with a loading... indication. @ewels and I had a look at this over Slack and found that the plots appear to be given the same id for some reason.

Attached is a report where the error is visible (rename to html).
multiqc_report.html.txt

Code to reproduce the error is in my repo here: https://github.com/boulund/MultiQC_bbmap/
Note that the bug is fixed in the code, in the submodules multiqc/modules/bbmap/{BaseComposition,Quality}.py, but can be restored by commenting out the config dictionary use to set the id value of the plot around line 70 in each of the files.

Most helpful comment

Yes, it's one of the python "special cases". When a function is created, objects for the functions default arguments are created as well. Once, just once. Then references to these objects are used with every call to the function. So it's the same dict in every call, and if you modify it that'll pass on to the next call. It's a way to "fake" static, function local variables.

All 4 comments

Reference for self: link

Ok, I found out what's going on. Apparently this is a _Common Gotcha_ for Python n00bs such as myself: http://python-guide-pt-br.readthedocs.io/en/latest/writing/gotchas/

The linegraph.plot() function uses default arguments if none are supplied. In this case, an empty dictionary:

def plot (data, pconfig={}):
    # do stuff

Apparently these default argument objects are mutable and reused every time the function is called. You can see this when I print the id() of the pconfig variable when running your code:

# pconfig id
4419621504
# pconfig
{}

# pconfig id (2nd call)
4419621504
# pconfig
{ "id": "mqc_hcplot_qbyhapzwgm" }

So, the first plot is given a randomly generated ID as one wasn't present. But the second time it runs, it _does_ have an ID set already, so it doesn't generate a new one. This results in the subsequent plots sharing IDs and not loading properly.

not even mad

I should be able to fix this fairly easily, as described in the above post (set default as None and create a new dict if None). I'll try to find other functions which could suffer from the same problem. At the same time I will also implement a check for unique plot IDs.

Phil

Yes, it's one of the python "special cases". When a function is created, objects for the functions default arguments are created as well. Once, just once. Then references to these objects are used with every call to the function. So it's the same dict in every call, and if you modify it that'll pass on to the next call. It's a way to "fake" static, function local variables.

I dread to think how many functions I've written are vulnerable to this.. 馃槅 Ah well!

Was this page helpful?
0 / 5 - 0 ratings