Enough with the hyperbole - hy does things that are not as easy in Python

| categories: python, hylang | tags:

We run a lot of molecular simulations using Python. Here is a typical script we would use. It creates an instance of a calculator inside a context manager.

from ase import Atoms, Atom
from jasp import *

co = Atoms([Atom('C',[0,   0, 0]),
            Atom('O',[1.2, 0, 0])],
            cell=(6., 6., 6.))

with jasp('molecules/simple-co', #output dir
          xc='PBE',  # the exchange-correlation functional
          nbands=6,  # number of bands
          encut=350, # planewave cutoff
          ismear=1,  # Methfessel-Paxton smearing
          sigma=0.01,# very small smearing factor for a molecule
          atoms=co) as calc:
    print 'energy = {0} eV'.format(co.get_potential_energy())
    print co.get_forces()

This basic approach has served us for more than a decade! Still, there are things about it that bug me. Most significantly is the arbitrary keyword args. We keep a list of legitimate kwargs in the module, but there is no documentation or validation to go with them that is accessible to users (short of reading the code). There are well over 100 kwargs that are possible, so documenting them in the init docstring is not that useful (we did it once, see https://gitlab.com/ase/ase/blob/master/ase/calculators/jacapo/jacapo.py#L143 , and it made a really long docstring). Providing validation for these (some can only be integers, floats, specific strings, lists, or dictionaries) is not easy. I did this for another simulation code by providing validation functions that could be looked up dynamically by name. I never did come up with a way to provide kwarg specific documentation though.

The access to documentation while writing code is becoming increasingly important to me; I don't remember all the kwargs and what values are valid. More importantly, as I teach people how to use these tools, it is not practical to tell them to "read the code". I don't even want to do that while running simulations, I just want to setup the simulation and run it.

Today, I had an idea that a macro in hy would allow me to get documentation and validation of these kwargs.

The pseudocode would look like this. Each "kwarg" will actually be a function that has a docstring, performs validation, and evaluates to its argument. "vaspm" is a macro that will expand to the calculator with the desired kwargs. We will have to be careful that these function names don't conflict with other function names, but that could be addressed in a variety of ways with namespaces and function names.

;; pseudocode of the macro
(setv calc (vaspm "molecules/simple-co"
                  (xc "PBE")
                  (nbands 6)
                  (encut 350)
                  (ismear 1)
                  (sigma 0.01)
                  (atoms co)))

This would expand to the following block, which is equivalent to what we already do today. In the process of expansion though, we gain docstrings and validation!

(setv calc (Vasp "molecules/simple-co"
                 :xc "PBE"
                 :nbands 6
                 :encut 6
                 :ismear 1
                 :sigma 0.01
                 :atoms co))

Here is a toy implementation that illustrates what the functions are, and how we build up the code from the macro.

(defn encut [cutoff]
  "The planewave cutoff energy in eV."
  (assert (integer? cutoff))
  (assert (> cutoff 0))
  (print "encut validated")
  cutoff)

(defn xc [exc]
  "The exchange correlation functional. Should be a string of PBE or LDA."
  (assert (string? exc))
  (assert (in exc ["PBE" "LDA"]))
  (print "exc validated")
  exc)


(defclass Calculator []
  "Toy class representing a calculator."
  (defn __init__ [self wd &kwargs kwargs]
    (setattr self "wd" wd)
    (for [key kwargs]
      (setattr self key (get kwargs key)))))

We tangle that block to calculator.hy so we can reuse it. First we show the traditional syntax.

(import [calculator [*]])

(setv calc (Calculator "some-dir" :encut 400 :xc "PBE"))

(print calc.wd)
(print calc.encut)
(print calc.xc)
some-dir
400
PBE

Note, we can also do this, and get the validation too. It is verbose for my taste, but shows what we need the final code to look like, and incidentally how this would be done in Python too. We just need a macro that expands to this code.

(import [calculator [*]])

(setv calc (Calculator "some-dir" :encut (encut 400) :xc (xc "PBE")))

(print calc.wd)
(print calc.encut)
(print calc.xc)
encut validated
exc validated
some-dir
400
PBE

That is what this macro below does. We build up that code by making a keyword of the function name, and setting it to the value of the form the function is in.

(defmacro vaspm [wd &rest body]
  "Macro to build a Calculator with validation of arguments in BODY"
  (let [code `(Calculator ~wd)]
    (for [form body]
      (.append code (keyword (name (car form))))
      (.append code form))
    code))

Now, lets consider the macro version.

(import [calculator [*]])
(require calculator)

(setv calc (vaspm "some-dir" (encut 400) (xc "PBE")))
(print calc.wd)
(print calc.encut)
(print calc.xc)

;; proof we can get to the encut docstring!
(help encut)
encut validated
exc validated
some-dir
400
PBE
Help on function encut in module calculator:

encut(cutoff)
    The planewave cutoff energy in eV.

Sweet. The macro allows us to simplify our notation to be approximately the same as the original function, but with validation and docstring availability. Here is a variation of the macro that even uses keywords and builds the validation in from the keyword. It is not clear we can access the docstrings so easily here (ok, we can build an eldoc function that works either way, but the function method above is "more native").

(import [calculator [*]])
(require calculator)


(defmacro vasp2 [wd &rest kwargs]
  (let [code `(Calculator ~wd)]
    (for [x (range   0 (len kwargs) 2)]
      (let [kw (nth kwargs x)
            val (nth kwargs (+ 1 x))]
        (.append code kw)
        (.append code `(~(HySymbol (name kw)) ~val))))
    code))

(print (macroexpand '(vasp2 "/tmp" :encut 1 :xc "PBE")))

(setv calc (vasp2 "some-dir"
                  :encut 400
                  :xc "PBE"))
(print calc.wd)
(print calc.encut)
(print calc.xc)
(u'Calculator' u'/tmp' u'\ufdd0:encut' (u'encut' 1L) u'\ufdd0:xc' (u'xc' u'PBE'))
encut validated
exc validated
some-dir
400
PBE

To summarize here, we have looked at some ways to incorporate validation and documentation into kwargs. There are certainly ways to do this in Python, using these auxiliary functions. In fact we use them in hy too. We could build the validation into a Python init function too, using dynamic lookup of the function names, and evaluation of the functions. The macro features of hy give different opportunities for this, and different syntactical sugars to work with. The hy approach leads to less duplication (e.g. only a keyword, not a keyword and a function name that are the same), which will lead to fewer mistakes of the type xc=xd(something). Overall, interesting differences to contemplate.

From a developer point of view there is the burden of writing all the validation functions, but the payoff is access to documentation and optionally, validation. Also, no kwargs that are not allowed will work. Right now, with **kwargs, they might silently fail.

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Managing contexts - Python vs hy

| categories: python, hylang | tags:

A common pattern we have in running molecular simulations is to temporarily change to a new directory, do some stuff, and then change back to the directory, even if something goes wrong and an exception is raised. Here we examine several approaches to handling this in Python.

1 a try/except/finally approach

A way to handle this is with a try/except/finally block in Python. Here we illustrate the idea. Nothing fancy happens for the exception here, other than we do get back to the original directory before the program ends. There is nothing wrong with this, but it is not that reusable, and has a lot of places to make sure the right thing happens.

import os
print(os.getcwd())
try:
    cwd = os.getcwd()
    os.chdir('/tmp')
    f = open('some-file', 'w')
    f.write('5')
    print(os.getcwd())
    1 / 0
except:
    pass
finally:
    f.close()
    os.chdir(cwd)

print(os.getcwd())

print(open('/tmp/some-file').read())
/Users/jkitchin/Dropbox/python/hyve
/private/tmp
/Users/jkitchin/Dropbox/python/hyve
5

2 A Python context manager

A more sophisticated way to handle this in Python is a context manager. We create a context manager here called cd that does the same thing. The context manager is longer, but we would presumably put this in module and import it. This allows us to to the same thing in a lot less code afterwards, and to reuse this pattern. We also use the built in context manager for opening a file. This is for the most part a syntactical sugar for the code above.

import contextlib

@contextlib.contextmanager
def cd(wd):
    import os
    cwd = os.getcwd()
    print('Started in {}'.format(os.getcwd()))
    os.chdir(wd)
    print('Entered {}'.format(os.getcwd()))
    try:
        yield
    except:
        pass
    finally:
        os.chdir(cwd)
        print('Entered {}'.format(os.getcwd()))

##################################################################
with cd('/tmp'):
    with open('some-other-file', 'w') as f:
        f.write('5')
    1 / 0

print(open('/tmp/some-other-file').read())
Started in /Users/jkitchin/Dropbox/python/hyve
Entered /private/tmp
Entered /Users/jkitchin/Dropbox/python/hyve
5

3 A python decorator

Here is an example of doing something like this with a decorator. I don't do this too often, but this does more or less the same thing. It does eliminate a with statement and provide some context to do work in. The overall indentation is identical to the context manager we looked at previously because we have to wrap our code in a function to delay its execution, which we have to ask for with f(). A downside of this is f is always decorated now. I am not sure you can undecorate it.

import os

def cd(wd):
    def outer(func):
        def inner(*args):
            cwd = os.getcwd()
            print('Started in {}'.format(os.getcwd()))
            os.chdir(wd)
            print('entered {}'.format(os.getcwd()))
            try:
                return func(*args)
            except:
                pass
            finally:
                os.chdir(cwd)
                print('entered {}'.format(os.getcwd()))
        return inner
    return outer
##################################################################

@cd('/tmp')
def f():
    with open('decorated-file', 'w') as f:
        f.write("5")
    1 / 0

f()
print(open("/tmp/decorated-file").read())
Started in /Users/jkitchin/Dropbox/python/hyve
entered /private/tmp
entered /Users/jkitchin/Dropbox/python/hyve
5

4 A hy macro approach

hy gives us yet another option: a macro. We can use a macro to construct the context for us by building up the try/except/finally code we used above. The indentation used here is just for readability.

(defmacro cd [wd &rest body]
  `(do
    (import os)
    (print "started in " (os.getcwd))
    (let [cwd (os.getcwd)]
      (try
       (do (os.chdir ~wd)
           (print "entered " (os.getcwd))
           ~@body)
       (except [e Exception] nil)
       (finally
        (os.chdir cwd)
        (print "entered " (os.getcwd)))))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(cd "/tmp"
    (with [f (open "some-hy-file" "w")]
          (.write f "5")
          (/ 1 0)))

(print (.read (open "/tmp/some-hy-file")))
started in  /Users/jkitchin/Dropbox/python/hyve
entered  /private/tmp
entered  /Users/jkitchin/Dropbox/python/hyve
5

The results are the same, even down to the reduced number of lines! But the mechanism that achieves that is different. In this example, we subtly changed the syntax that was possible, eliminating the need for one of the "with" statements. This is only possible with this kind of macro construction as far as I know. It still is not a game changer of programming, but does illustrate some new ways to think about writing these programs. It is not necessary to wrap the code into a function just to delay it from being executed.

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

More on Hy and why I think it is a big deal

| categories: emacs, hylang, python | tags:

Yesterday I talked about hylang , a Lisp that basically compiles to and runs Python code. Today, I want to show a few reasons why this is a great idea, and an important one. Below I give a few examples of why the hylang approach is better (in my opinion of course) than Python with a few examples of things I have always wanted in Python but couldn't get.

1 Editing with hy-mode and lispy

There is a major mode for Hy: https://github.com/hylang/hy-mode also on MELPA. It gives us some syntax highlighting and better access to a REPL.

Let's load lispy (https://github.com/abo-abo/lispy ) for it so we also get amazing editing. I always wanted to use lispy style navigation and editing in Python, but the whitespace and indentation did not make it that easy. Problem solved with these. @abo-abo already added basic eval support for Hy to lispy since the post yesterday (https://github.com/abo-abo/lispy/commit/f7f71e38e241d92b6add05be6628ac987067b11c ); Thanks!

(add-hook 'hy-mode-hook
          (lambda ()
            (lispy-mode 1)))

2 Python with no whitespace, or commas in lists

You can still use indentation (it is good style), but this works!

(for [x [0 1 2 3 4 5]]
(if (> x 3) (print "too big")
(print x)))
0
1
2
3
too big
too big

This looks nicer.

(for [x [0 1 2 3 4 5]]
  (if (> x 3)
    (print "too big")
    (print x)))
0
1
2
3
too big
too big

This is a big deal too. Using Python in sessions in org-mode has always been a little complicated by the indentation and whitespace, especially with nested loops and functions. That problem is probably gone.

3 No confusion in expressions in statements

In Python you can do this:

a = 5
print(a)
print(a + 5)
5
10

But not this:

print(a=5)
print(a + 5)
  File "<stdin>", line 1
   print(a=5)
          ^
SyntaxError: invalid syntax

You can't put assignment statements and expression statements anywhere you want, they are only legal syntax in some places. For example, a=5 above actually looks like the print function has an argument of a that set to 5. Not true in Lisp; there are only expressions! So this works fine.

(print (setv a 5))
(print (+ a 5))
5
10

I just like this style of simple syntax.

4 Proper multiline lambda functions

Python syntax fundamentally limits you to one line lambdas. Not so for Hy. Let's use one in a filter to print even numbers. Here is an example with a two-liner but you could make them more complicated. In Python, you have to make a separate function for this. That isn't terrible, but if it is never used for anything else, it could be avoided.

(setv a [0 1 2 3 4 5 6 7 8])

(defn display [list filter]
  (for [x list] (if (filter x) (print x))))

(display a (lambda [x]
             (= (% x 2) 0)))
0
2
4
6
8

5 Macros and Extensible syntax

It is not easy to get real macro (code expansion) behavior in Python. Yes, there are decorators, and closures, and related things that get close to it. But there are not lisp-like macros.

Here is a (too) simple macro to allow for infix notation. It only works for two arguments, but could be extended for multiple arguments.

(defmacro infix [code]
  (quasiquote ((unquote (get code 1))
               (unquote (get code 0))
               (unquote (get code 2)))))

(print (infix (1 + 1)))
2

If we want new syntax we can get it!

(defreader $ [code]
  (quasiquote
   ((unquote (get code 1))
    (unquote (get code 0))
    (unquote (get code 2)))))

(print #$(1 + 1))
2

Why is this nice? Here is a math example that shows why you might want to change syntax.

5.1 Some math

See http://kitchingroup.cheme.cmu.edu/blog/2013/02/07/Solving-Bessel-s-Equation-numerically/ for the Python version of solving the Bessel equation numerically. Here we do it with hylang.

Why would we want infix notation? Here is a good reason. The prefix notation is not easy to read. Compare:

dzdx = 1.0 / x**2 * (-x * z - (x**2 - nu**2) * y)

to

(setv dzdx (* (/ 1.0 (** x 2)) (- (* (* -1 x) z) (* (- (** x 2) (** nu 2)) y))))

The infix notation is simpler to read. Still, the code below is not that hard to figure out, especially if there was a generalized infix notation that allowed (with parens for explicit operation precedence):

(setv dzdx (nfx (1.0 / x**2) * ((-x * z) - ((x**2 - nu**2) * y))))

So, here is the hylang equivalent to my previous Python version.

(import [numpy :as np])
(import [scipy.integrate [odeint]])
(import [scipy.special [jn]])
(import [matplotlib.pyplot :as plt])

(defn fbessel [Y x]
  "System of 1st order ODEs for the Bessel equation."
  (setv nu 0.0
        y (get Y 0)
        z (get Y 1))

  ;; define the derivatives
  (setv dydx z
        dzdx (* (/ 1.0 (** x 2)) (- (* (* -1 x) z) (* (- (** x 2) (** nu 2)) y))))
  ;; return derivatives
  [dydx dzdx])

(setv x0 1e-15
      y0 1.0
      z0 0.0
      Y0 [y0 z0])

(setv xspan (np.linspace 1e-15 10)
      sol (odeint fbessel Y0 xspan))

(plt.plot xspan (. sol [[Ellipsis 0]]) :label "Numerical solution")
(plt.plot xspan (jn 0 xspan) "r--" :label "Analytical solution")
(plt.legend :loc "best")

(plt.savefig "hy-ode.png")
2016-04-01 13:48:17.499 Python[12151:d13] CoreText performance note: Client called CTFontCreateWithName() using name "Lucida Grande" and got font with PostScript name "LucidaGrande". For best performance, only use PostScript names when calling this API.
2016-04-01 13:48:17.499 Python[12151:d13] CoreText performance note: Set a breakpoint on CTFontLogSuboptimalRequest to debug.
None

This looks really good to me, except for that prefix math. The array slice syntax is interesting. Not that obvious yet.

6 Interoperability with Python

http://docs.hylang.org/en/latest/tutorial.html#hy-python-interop

Write Hy code and use it in Python. Use Python code in Hy. Repeat. Sweet.

7 Integration of emacs and Hy

This isn't so beautiful but it illustrates a pretty awesome integration of Hy(python) into Emacs!

(defmacro hy (body)
  `(let* ((temporary-file-directory ".")
          (tempfile (make-temp-file "hy-")))
     (message (format "code: %S" ,body))
     (with-temp-file tempfile
       (mapc (lambda (form) (insert (format "%s" form))) ,body))
     (read (unwind-protect
               (shell-command-to-string
                (format "hy %s" tempfile))
             (delete-file tempfile)))))

(aref (hy '((import numpy)
            (setv a (numpy.array [1 2 3]))
            (setv b (numpy.array [1 2 3]))
            (print (* a b))))
      1)
4

This isn't perfect, and there are many ways it could break down. But if you are careful to make the output "read"able, you can literally embed Hy code in Emacs lisp and use the results, a total win for Science! I feel like it might need something like progn, but that would not change what this does dramatically.

8 Hypster and Hy Society.

http://notes.pault.ag/hy-survival-guide/ ROTFL. ironically of course ;)

And the @hylang Twitter account is run by Hy Society. Nice.

9 What do we still need?

  1. Experience. Hy seems relatively young compared to other Lisps. It isn't clear yet if this could work like Python does at scale in research. I sure look forward to finding out though!
  2. Proper infix notation for engineering math. I could live with no operator precedence if it led to a quicker solution for now. As long as something like (1.0 / x**2 * (-x * z - (x**2 - nu**2) * y)) is legal!
  3. A proper integration with org-mode and the REPL.
  4. Toolchains like emacs-lisp has. I just love those. Killer debugging, access to hyperlinked documentation, code navigation, … Maybe integration with something like SLIME or CIDER? Hyder?
  5. Use it in a proper big project to find out where the limitations are, maybe Hycse as a companion to Pycse (http://kitchingroup.cheme.cmu.edu/pycse/ )? or a rewrite of http://kitchingroup.cheme.cmu.edu/dft-book/ in Hy?

Overall, I am pretty excited about this project. The syntax is a bit reminiscent of Clojure, and Racket, the former by design. Lots of new ideas still seem to be percolating in, so there is likely good stuff to see in the future!

I haven't used it enough to see the warts yet, but already the top issues I had with Python are largely addressed, so I see this as a way to continue progress with all the benefits of Python.

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

OMG A Lisp that runs python

| categories: lisp, python | tags:

For a year now I have struggled with abandoning Python for Lisp. It's complicated, I have used Python for 15 years now, and have a lot of skill and knowledge in it. I have used emacs-lisp for about 5 years now, and have a far bit of skill with it too. They solve really different problems. Between the two, I find I like writing and editing elisp lots better than writing Python, except it lacks the scipy+numpy+matplotlib stack. I looked into Racket and Common Lisp, but they also don't really have that as nicely as Python does at the moment. It hit me earlier today that a Lisp that compiled to Python might be the right middle ground. I had seen this project Hy (http://docs.hylang.org/en/latest/quickstart.html ) earlier, but didn't connect the dots to this.

Let me do that here. First, an obligatory execute function to run org-mode code blocks.

(defun org-babel-execute:hy (body params)
  (let* ((temporary-file-directory ".")
         (tempfile (make-temp-file "hy-")))
    (with-temp-file tempfile
      (insert body))
    (unwind-protect
        (shell-command-to-string
         (format "hy %s" tempfile))
      (delete-file tempfile))))
org-babel-execute:hy

Now the basic Hello world example. It looks like lisp.

(print "Hy world")
Hy world

Now for a use that looks like Python:

(import numpy)
(setv a (numpy.array [1 2 3]))
(setv b (numpy.array [1 2 3]))
(print (numpy.dot a b))
14

WHAT!!!!

A simple plot? Surely it can't be so easy…

(import [matplotlib.pyplot :as plt])
(plt.plot [1 2 4 8])
(plt.xlabel "x")
(plt.ylabel "y")
(plt.savefig "hy-test.png")
2016-03-30 17:09:40.826 Python[94292:d13] CoreText performance note: Client called CTFontCreateWithName() using name "Lucida Grande" and got font with PostScript name "LucidaGrande". For best performance, only use PostScript names when calling this API.
2016-03-30 17:09:40.826 Python[94292:d13] CoreText performance note: Set a breakpoint on CTFontLogSuboptimalRequest to debug.

Wow. I am not sure what the warnings are, I seem to get them on my Mac for some reason. How about solving an equation?

(import [scipy.optimize [fsolve]])
(defn objective [x] (- 2 x))
(print (fsolve objective -1))
[ 2.]
     _.-^^---....,,--
 _--                  --_
<                        >)
|                         |
 \._                   _./
    ```--. . , ; .--'''
          | |   |
       .-=||  | |=-.
       `-=#$%&%$#=-'
          | ;  :|
 _____.,-#%&$@%#&#~,._____
       _---~~(~~-_.
     _{        )   )
   ,   ) -~~- ( ,-' )_
  (  `-,_..`., )-- '_,)
 ( ` _)  (  -~( -_ `,  }
 (_-  _  ~_-~~~~`,  ,' )  <---- My brain right now...
   `~ -^(    __;-,((()))
         ~~~~ {_ -_(())
                `\  }
                  { }

I may not be able to sleep tonight…

Ascii art courtesy of http://chris.com/ascii/index.php?art=people/body%20parts/brains and http://www.ascii-code.com/ascii-art/weapons/explosives.php .

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Pycse is now using Python3

| categories: python | tags:

I spent the last couple of days going through the notes for pycse and updating it for Python 3! If you aren't familiar with pycse, it is an acronym for Python3 Calculations in Science and Engineering, and it is about 400 pages of examples spanning scientific and engineering computations, and a python module that extends numpy/scipy with some functions for adding events to differential equation solvers, and regression with confidence intervals.

It is mostly done, and was pretty easy. The Python module for pycse (http://github.com/jkitchin/pycse ) should also be Python 3 compliant. Yep, on my Mac I have switched over to the Anaconda Python 3 from Continuum IO (thanks for that!).

import sys
print(sys.version)
3.5.1 |Anaconda 2.5.0 (x86_64)| (default, Dec  7 2015, 11:24:55)
[GCC 4.2.1 (Apple Inc. build 5577)]

Now we can succinctly do matrix multiplication like this:

import numpy as np
a = np.array([1, 2, 3])
a = np.array([1, 2, 3])
print(a @ a)  # the matrix multiplication operator

# instead of
print(np.dot(a, a))
14
14

Here is a summary of what the changes to the Python2 version of Pycse entailed.

  1. Change all print statements to print functions. There were so many…
  2. Eliminate use of tabs in some code blocks, Python3 is not flexible on that.

I wrote this function to fix both of these for me (I use Emacs as my editor), which worked nicely:

(defun fp ()
  "convert print to print() allowing for line end comments. does not do multiline.
and untabify"
  (interactive)
  (beginning-of-line)
  (re-search-forward "print " (line-end-position) t)
  (delete-char -1)
  (insert "(")
  ;; rm spaces
  (delete-forward-char
   (save-excursion
     (skip-chars-forward " ")))
  (when
      (re-search-forward "#" (line-end-position) 'move)
    (goto-char (match-beginning 0)))
  (skip-chars-backward " ")
  (insert ")")
  ;; untabify
  (let ((src (org-element-context)))
    (untabify (org-element-property :begin src)
              (org-element-property :end src))))
  1. reduce no longer exists, you have to use functools.reduce. Probably will not affect me much…
  2. Strings are sometimes bytes, and need to be encoded/decoded depending on what you are doing.

Otherwise, most things seemed to work fine. In a few places I had articles on Windows specific code, which I couldn't test on the Mac I work on now. Only one package was apparently not ready for Python3, and that was scimath from Enthought, which had units capability. Quantities worked fine though.

For some reason I could not do interactive key-presses in matplotlib. I am not sure if that is a Python3, or my Mac setup for now.

When I first wrote the Pycse book (in org-mode naturally), I must have had a default session setup for org-mode, because there was no :session information in a few of the src-block headers. I had to add these back in a few places.

Overall the transition was pretty seamless. It looks like I will be transitioning to Python3 in teaching this fall! Big thanks to the Anaconda team for packaging Python 3.5 and making it so easy to install!

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter
« Previous Page -- Next Page »