1

I'm running Python 3.5 under Windows 10, and I'm using matplotlib.pyplot to generate PGF files, which are images I use for use in LaTeX.

I'm running a front-end GUI that gives the end-user configuration options, and then make calls into matplotlib.pyplot.savefig() which generates and saves the image.

The problem I have is that the matplotlib backend used (backend_pgf.py) makes a subprocess.Popen() call that forces a Windows console window (cmd) to pop up in order to do the required LaTeX processing. Visually it's distracting to the user and should be hidden.

Here's that code fragment:

   latex = subprocess.Popen([str(self.texcommand), "-halt-on-error"],
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            cwd=self.tmpdir)

What I want to do is prevent that console window from displaying. I know I can use subprocess.STARTUPINFO() to set dwFlags and prevent this console window from displaying (or pass in shell=True).

I could override the class in question, but that class is nested deep in other classes and modules, so you can imagine the complexity of managing the code base for a simple function change.

My question then is... how to make this change in a logically deep package like matplotlib?

Thanks much.

Rich

richbl
  • 145
  • 9

2 Answers2

0

If you are in full control of the app you are writing, and do not want at any moment the terminal window (I assume you don't), you can resort to monkey-patch the subprocess.Popen call itself to always set that flag.

It is relatively painless - just call a function like this in the initialization code for your app:

def patch():
    import subprocess
    original_popen = subprocess.Popen
    def Popen(*args, **kwargs):
        # code to create your STARTUPINFO object
        kwargs["startupinfo"] = subprocess # ...
        return original_open(*args, **kwargs)
    subprocess.Popen = Popen

It does not matter this is not changing the call where it is deep nested inside matplotlib - as long as this function is called before matplotlib itself is initialized, and even then, it would only fail if the module in matplotlib would do from subprocess import Popen (so it would have an independent reference to the original Popen). But if that is happening, so much the better: just patch the Popen name in the matplotlib submodule then.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • **Learning is fun.** Thanks for this. I was not aware of monkey-patching at all (the concept makes total sense for dynamic languagues). For those not familiar, here's more on [monkey-patching (SO link)](https://stackoverflow.com/questions/5626193/what-is-a-monkey-patch). Note that with any good concept, it must be treated with caution. – richbl Feb 09 '17 at 20:25
0

For problems like this where the changes would be inconsequential to the regular functioning of the library, I often just monkey-patch the offending function/method. In your case it would be something like this

from matplotlib.backends.backend_pgf import LatexManager    

def __init_patched__(self):
    # copy/paste the original source here and make your changes

LatexManager.__init__ = __init_patched__

Of course, you will need to update the patched code if the matplotlib source changes

user3419537
  • 4,740
  • 2
  • 24
  • 42
  • Thanks. I gave the accept to [jsbueno](https://stackoverflow.com/users/108205/jsbueno), as it appears that he answered before you, though you both came up with the same answer. I'll add that you took the extra effort to correctly identify the class required (`LatexManager`). If I could give you extra credit for this detail, I would! – richbl Feb 09 '17 at 20:28
  • In this case I think the accepted answer is better because it simply adds the required argument and is completely independent from the matplotlib source. My answer is more applicable to the general case where the patch is not as simple as adding an extra function argument. – user3419537 Feb 10 '17 at 07:28