Quiet mode for pyroot?

I would like to suppress the output from canvas.SaveAs, and I do not seem to be able to. I found this solution but it just complains about a local variable named gErrorIgnoreLevel not being set when I try to modify it. (Temporarily turn off warnings in PyROOT)

Are you using the context manager from that post you linked, or just trying to modify gErrorIgnoreLevel directly? Can you post a minimal example code & error message?

In a former life, I wrote this:

http://acode-browser2.usatlas.bnl.gov/lxr-rel17/source/atlas/Tools/PyUtils/python/Helpers.py

I tried using your code verbatim as well as with removal of the ROOT.<> by doing from ROOT import gErrorIgnoreLevel, since I don’t want to import the whole libarary:

class Quiet:
    """Context manager for silencing certain ROOT operations.  Usage:
    with Quiet(level = .kInfo+1):
       foo_that_makes_output

    You can set a higher or lower warning level to ignore different
    kinds of messages.  After the end of indentation, the level is set
    back to what it was previously.
    """
    def __init__(self, level=kInfo + 1):
        self.level = level

    def __enter__(self):
        self.oldlevel = gErrorIgnoreLevel
        gErrorIgnoreLevel = self.level

    def __exit__(self, type, value, traceback):
        gErrorIgnoreLevel = self.oldlevel

Then I ran:

with Quiet():
  canvas.SaveAs(self.runConfig["outputPath"] + "/plots/overlay_" + hist_name + ".pdf")

I don’t have the error output anymore but it was complaining that gErrorIgnoreLevel was being accessed before definition.

Overall the code is not so easy to minimally reproduce – I am using subprocess to parallelize. I do see that if I pipe STDERR with subprocess.Popen().communicate(), I can collect all of the root output into it. None of it goes to STDOUT, not even the stuff marked at “Info” level. So I suppose I can suppress it this way, but I thought there might be a another way in PyRoot.

Ah, I think if you remove the ROOT namespace, then python indeed thinks gErrorIgnoreLevel is just a regular local variable, not the global from the module. Try adding the line

global gErrorIgnoreLevel

at the beginning of the enter and exit methods. That should fix it.

This blog post seems to adequately explain what’s going on.

Ah yes – I havent “set” any of the other values I have imported from ROOT in this manner, so that makes sense. However when I try it, it does not change the output to stop showing “Info”'s. It doesn’t error, but it doesn’t seem to work right. Python probably thinks it is a normal variable and not a root variable

Using ‘global’ is not enough, you absolutely need ROOT.gErrorIgnoreLevel with ROOT being the ROOT module, not the ROOT namespace.

Global variables are implemented as Python properties. So a statement like this:

from ROOT import gErrorIgnoreLevel

translates to something like this (not really, but it illustrates the point well enough):

gErrorIgnoreLevel = ROOT.__class__.gErrorIgnoreLevel.__get__(ROOT)

i.e. it is the return result of a function call, and so even the “global” gErrorIgnoreLevel is a simply Python int, completely detached from anything ROOT. Changing that global variable then just means updating the entry in the globals() dictionary and never touches the C++ side.

So is there a solution to this that avoids importing all of ROOT? I currently only import what I need, so it is faster to boot up

Actually this is also relevant for another problem I have (PyROOT hijacks --help)

Measure it … If the criterium is startup time, then this:

from ROOT import gErrorIgnoreLevel

is significantly slower than this:

import ROOT

b/c everything in PyROOT is done lazily all the time and the first has to initialize more than the latter. Of course, that initialization still happens on first use of ROOT.gErrorIgnoreLevel.

Iow., there is no such thing as “importing all of ROOT” so by definition you’re avoiding it.

Note that in cppyy master, there is code that allows exploring “all of ROOT” for the express purpose of “importing all of ROOT” (apparently there is a use case), but doing so is actually a rather hard problem.

Aside, only in an inner loop, keeping access through the module can be measurably slower. Then again, for write access, the lookup is both slower and needed b/c it does more work (i.e. also setting the value on the C++ side). But for read-only access, it makes sense to reference the variable locally.

Really? I guess you learn stuff every day. What about from ROOT import *? Would this be the same as import ROOT (I would have to refactor a lot of code to add the ROOT namespace, but if it’s faster I would rather have it).

If you’re already using from ROOT import foo, you should still be able to add import ROOT without breaking anything. Even if you do have to refactor, at least all the ROOT types and classes start with T so it’s easy to search & replace!

That, too, does not do what you probably think it does. (I say “probably” b/c I can’t read your mind, but based on how the question was posed, I can guess.)

Python dictionaries contain a data member that is a pointer to a lookup function. “from ROOT import *” hijacks that for the global dictionary in the closure where the import happens. The upshot is that yes, “from ROOT import *” is still lazily implemented.

There are a couple of caveats, though: it doesn’t work with PyPy (where the dictionary implementation is different), it is disabled in PyROOT for p3 (no such problem with cppyy master, though), and it will give you a run-time slowdown. The reason for that last one is that for every new variable not only the closure, but all of ROOT is searched for that name, which isn’t cheap.

Up to about half-way 2005 or so, PyROOT actually did filter on ‘T’. Before that, PyLCGDict was used, which was based on a copy of the PyROOT code base. But then experiments moved to PyCintex, which was based on PyROOT proper, and so the filter had to go.

Okay – I think Ill refactor to improve the runtime then. Lets see how that goes! Thanks for the help, both of you

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.