Skip to content

Using a GUI Python Debugger with Sage (PUDB)

Maxie D. Schmidt edited this page Dec 9, 2016 · 24 revisions

Introduction

The PUDB Python debugger provides a colorful ncurses-based "text-GUI" debugger that can be called by the local Sage python interpreter to debug Python scripts that interface with built-in Sage functions and types. In this tutorial, we provide a small sample script that interfaces with Sage and detail howto debug with PUDB in this case. The separate non-math-related tutorial for PUDB located at this link is a primary reference here, though we only reproduce the highlights of that full introduction to PUDB in this tutorial. We also cover installation of the PUDB module with the separate local Python that comes bundled with Sage in the next section, which is somewhat different than installing the module with a package-manager such as apt. Another video tutorial giving a concise overview of the PUDB debugger is found at this link.

Installation of PUDB in Sage

The actual installation of the PUDB module in Python is straightforward once you know a couple of tricks about how the Sage shell can be used to install new packages in its own local chroot-like environment. In particular, assuming you have the OpenSSL development libraries installed on your system (try apt-get install libssl-dev on a Debian variant Linux box), we can issue the following commands in sequence to get Sage's local Python to recognize the PUDB add-on:

 $ sage -i openssl
 $ sage -f python2
 $ sage -sh
 (sage-sh) pip install --upgrade pip
 (sage-sh) pip install pudb
 (sage-sh) exit

This method also works to install other Python add-on modules into Sage's Python, for example, such as the useful memory_profiler module that is not yet installed in Sage by default. To verify that the install of PUDB is working correctly, consider any fairly simple Python script test-script.py and then issue the following command:

 $ sage -python -m pudb test-script.py

This should bring up the colorful vim-like ncurses interface to our new GUI debugger for Sage. To exit this screen, type q and navigate to the < Quit > link to leave the application (more on these shortcuts in the next sections). As of the current moment a support request is active to have PUDB automatically installed for use with the fullscreen terminal application on the SMC.

Another convenience when using the long sage -python command is to add an alias to your local ~/.bashrc file using the next commands.

 $ echo "alias sage-debug='sage -python -m pudb'" >> ~/.bashrc
 $ source ~/.bashrc

Then PUDB can be run on our sample test script from above by typing sage-debug test-script.py from within your terminal. Additional information on customizing PUDB with themes, hacks, and including other add-ons to PUDB itself (such as a custom "stringifier" for displaying variables) are detailed in the following blog posts:

An Example Program

Source Code

PhiPoly.py:

 from sage.all import Integer, golden_ratio, var, fibonacci, binomial, sgn
 
 def AssertVType(obj, vtype, excpt = ValueError): 
      if not isinstance(obj, vtype): 
           raise excpt('Object should be of type ' + vtype)
           return False
      ##
      return True
 ##
 
 def Assert(cond, excpt, emsg = ""): 
      if not cond: 
           raise excpt(emsg)
           return False
      ##
      return True
 ##
 
 def IsInteger(iobj): 
      return isinstance(iobj, int) or iobj.is_integer()
 ## 
 
 class PhiPoly(object): 
 
      def __init__(self, a = Integer(0), b = Integer(0)): 
           self._a = a
           self._b = b
      ## 
 
      @property
      def a(self): 
           return self._a
      ##
 
      @a.setter
      def a(self, a): 
           self._a = a
      ## 
 
      @property
      def b(self): 
           return self._b
      ##
 
      @b.setter
      def b(self, b): 
           self._b = b
      ## 
 
      def __str__(self): 
           str_sign_func = lambda i, pos, neg, zero: \
                pos if sgn(i) == 1 else neg if sgn(i) == -1 else zero
           rstr = "%s * phi %s %s" % (self.a, str_sign_func(self.b, "+", "-", ""), \
                                      str_sign_func(self.b, self.b, abs(self.b), ""))
           return rstr
      ## 
 
      def __float__(self): 
           return n(self.a * golden_ratio + self.b)
 
      def __list__(self): 
           return [self.a, self.b]
      ## 
 
      def __tuple__(self): 
           return (self.a, self.b)
      ##
 
      def __repr__(self): 
           return str([self.a, self.b])
      ## 
 
      def __radd__(self, rhs): 
           return self.__add__(rhs)
      ##
 
      def __add__(self, rhs): 
           rop = PhiPoly(a = self.a, b = self.b)
           if IsInteger(rhs): 
                rop.b += rhs
           else: 
                rop.a += rhs.a
                rop.b += rhs.b
           return rop
      ## 
 
      def __pow__(self, p): 
           Assert(IsInteger(p), ValueError, "Exponent must be an integer")
           coeffs = [ [binomial(p, r) * (self.a ** r) * (self.b ** (p-r)), r] \
                      for r in range(0, p + 1)]
           return PhiPoly.fromcoeffs(coeffs)
      ## 
 
      def __xor__(self, p): 
           return self.__pow__(p)
      ##
 
      def is_integer(self): 
           return self.a == 0
      ##
 
      @classmethod
      def fromexpr(ppcls, expr, phi = golden_ratio): 
           x = var('x')
           coeffs = expr.subs(phi == x).coefficients()
           return ppcls.fromcoeffs(coeffs)
      ## 
 
      @classmethod
      def fromcoeffs(ppcls, coeffs): 
           from_pow_func = lambda (coeff, pexp): \
                ppcls(a = coeff * fibonacci(pexp), b = coeff * fibonacci(pexp - 1))
           pow2phipolylst = map(from_pow_func, coeffs)
           return sum(pow2phipolylst)
      ## 
 
 ## PhiPoly

pudb-main-runner.py:

 from PhiPoly import *

 if __name__ == "__main__": 
      mpowupper = 12
      constant_term = 2
      ppoly_pows_list = []
      for m in range(0, mpowupper + 1): 
           phipoly_expr = (golden_ratio + constant_term) ** m
           phipoly = PhiPoly.fromexpr(phipoly_expr)
           ppoly_pows_list += [(m, phipoly)]
           print "[m = %2d]: (phi + %s) ** %2d == %s" % \
                 (m, constant_term, m, phipoly)
      ## 
      print ppoly_pows_list
 ##

.python.py

Block quote 1 BQ 2

Core Features of PUDB

Shortcut Keys

Setting Breakpoints

Configuration of PUDB

Line Numbers

Configuring Printed Variable Representations

Other Sage Debugging Links

Clone this wiki locally