The execution flow

At the beginning of this chapter, we briefly sketched the flow of data from user creation to its display in a user interface. Having toured matplotlib's architecture, which included taking a side trip to the namespaces and dependency graphs, there is enough context to appreciate the flow of data through the code.

As we trace through our simple line example, remember that we used the pyplot interface. There are several other ways by which one may use matplotlib. For each of these ways, the code execution flow will be slightly different.

An overview of the script

As a refresher, here's our code from simple-line.py:

#! /usr/bin/env python3.4
import matplotlib.pyplot as plt

def main () -> None:
  plt.plot([1,2,3,4])
  plt.ylabel('some numbers')
  plt.savefig('simple-line.png')

if __name__ == '__main__':
  main()

At the script level, here's what we've got:

  1. Operating system shell executes the script.
  2. Python 3.4 is invoked, which then runs the script.
  3. matplotlib is imported.
  4. A main() function is defined.
  5. The script then executes the main() function.

Having reviewed familiar territory, let's jump into what matplotlib does in the script. Here's a brief summary of the trace:

  • Using the import matplotlib.pyplot command line:
    1. Select the backend.
    2. Import and run pylab_setup from matplotlib.backends.
  • Using the pylab_setup function:
    1. Import the previously selected backend.
    2. Get the figure manager factory function.
    3. Select the show instance that you want to use, which can be integrated with the selected backend's mainloop function.
    4. Return all of these.
  • Plot the given data:
    1. Get the figure manager (or create one if it doesn't exist).
    2. Get its figure canvas.
    3. From this, get the figure object of the canvas.
    4. From the figure object, get the current axes object (or create it if it doesn't exist).
    5. Once the figure's axes object is available, call its plot function.
    6. The axes plot function clears the axes and creates some lines based on the provided data.
    7. Get the active figure manager.
    8. Call the figure manager's canvas.draw() function.
  • Set the y axis label. This updates the _text attribute of the label object on the y axis.
  • Save the plot as a .png image. This writes the file to filesystem by using the hardcopy backend, which correlates to the extension on the filename.

An interactive session

We can get a hands-on look at many of these via IPython either through an interactive shell in the terminal, or with this chapter's notebook in your browser. Note that if you run the following code in the notebook, you will get different results since a different backend is being used.

The following command in the terminal will ensure that you get an interactive IPython prompt, which has access to all the dependencies:

$ make repl

Let's examine some of the things that we covered in the execution flow outline in the preceding section. We'll start by importing pyplot and looking at the top-level setup that pyplot initiates after the import:

In [1]: import matplotlib.pyplot as plt
In [2]: plt.rcParams['backend']
Out[2]: 'MacOSX'

In some of the following calls, we will be able to access objects, methods, and so on that have been named according to the private Python naming convention. We will do this simply to explore some of the undocumented depths of matplotlib. The keyword here is undocumented. The private variables are subject to change without warning. So please do not use these in any projects.

In [3]: plt._backend_mod.__name__
Out[3]: 'matplotlib.backends.backend_macosx'

In [4]: plt._show
Out[4]: <matplotlib.backends.backend_macosx.Show at 0x1074bc940>

If we try to get a figure or its figure manager right now, nothing will be returned since one hasn't been created yet:

In [5]: plt._pylab_helpers.Gcf
Out[5]: matplotlib._pylab_helpers.Gcf

In [6]: plt._pylab_helpers.Gcf.get_active()

However, we can get the default figure manager in the following way:

In [7]: plt.get_current_fig_manager()
Out[7]: FigureManager object 0x106e1ea48 wrapping NSWindow 0x103e74e90

However, note that the figure manager too doesn't have a figure yet, this can be seen in the following way:

In [8]: plt.get_current_fig_manager().figure

-----------------------------------------------------------
AttributeError              Traceback (most recent call last)
<ipython-input-8-a80f1a99bf26> in <module>()
----> 1 plt.get_current_fig_manager().figure

AttributeError: 'FigureManagerMac' object has no attribute 'figure'

Now, let's call the plot function and see what's available:

In [9]: plt.plot([1,2,3,4])
Out[9]: [<matplotlib.lines.Line2D at 0x1088367b8>]

In [10]: plt._pylab_helpers.Gcf.get_active()
Out[10]: FigureManager object 0x1074c4a88 wrapping NSWindow 0x107026030

In [11]: plt._pylab_helpers.Gcf.get_active().canvas
Out[11]: FigureCanvas object 0x1074c45c8 wrapping NSView 0x10761cd60

In [12]: plt._pylab_helpers.Gcf.get_active().canvas.figure
Out[12]: <matplotlib.figure.Figure at 0x1074b5898>

Depending upon the operating system and backend that you are currently using, you may get results (or no results) that are different from the ones in the preceding section.

Better yet, by using the API function and its attributes:

In [13]: plt.get_current_fig_manager()
Out[13]: FigureManager object 0x1074c4a88 wrapping NSWindow 0x107026030

In [14]: plt.get_current_fig_manager().canvas
Out[14]: FigureCanvas object 0x1074c45c8 wrapping NSView 0x10761cd60

In [15]: plt.get_current_fig_manager().canvas.figure
Out[15]: <matplotlib.figure.Figure at 0x1074b5898>

In [16]: plt.get_current_fig_manager().canvas.figure.axes
Out[16]: [<matplotlib.axes._subplots.AxesSubplot at 0x108826160>]

In [17]: plt.get_current_fig_manager().canvas.figure.axes[0].lines
Out[17]: [<matplotlib.lines.Line2D at 0x1088367b8>]

However, the most consistent results will be obtained when we use the pyplot utility functions in the following way:

In [18]: plt.gcf()
Out[18]: <matplotlib.figure.Figure at 0x1074b5898>

In [19]: plt.gca()
Out[19]: <matplotlib.axes._subplots.AxesSubplot at 0x108826160>

In [20]: plt.gca().lines
Out[20]: [<matplotlib.lines.Line2D at 0x1088367b8>]

The next step is to add a label in the following way:

In [21]: plt.gca().get_ylabel()
Out[21]: ''

In [22]: plt.ylabel('some numbers')
Out[22]: <matplotlib.text.Text at 0x1088464a8>

In [23]: plt.gca().get_ylabel()
Out[23]: 'some numbers'

Finally, we will save the image in the following way:

In [24]: ls -al *.png
ls: *.png: No such file or directory

In [25]: plt.savefig('simple-line.png')

In [26]: ls -al *.png
-rw-r--r--  1 oubiwann  staff  22473 Nov  9 15:49 simple-line.png

A note on tracing. What we did in the previous section is a bit like sightseeing—a quick overview, some interesting moments, and then we move on to the next thing. When you really want to dive deep into the execution flow of a program, script, or a function, you perform the operation of tracing. As you might expect, the Python standard library has a module for this as well—the trace module.

It's beyond the scope of this chapter to trace this script, but this is an excellent exercise for the motivated reader. Here is an example that illustrates the trace module's usage:

In [46]: def plotit():
             plt.plot([1,2,3,4])
             plt.ylabel('some numbers')
             plt.show()

         tracer = trace.Trace(countfuncs=1, countcallers=1)
         _ = tracer.runfunc(plotit)

This will take some time to run. When runfunc() completes, the tracing results will be stored in tracer.results, an instance of trace.CoverageResults:

In [47]: results = tracer.results()
         _ = results.write_results(show_missing=True, summary=True,
                                   coverdir=".")

Note that by enabling countcallers, our results will have the call relationship tracking data. With this information, you should be able to build some highly detailed graphs using NetworkX and matplotlib that visually reveal which functions in matplotlib call where and which layers of the architecture call the other layers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset