Redirecting the print function

| categories: programming | tags:

Ordinarily a print statement prints to stdout, or your terminal/screen. You can redirect this so that printing is done to a file, for example. This might be helpful if you use print statements for debugging, and later want to save what is printed to a file. Here we make a simple function that prints some things.

def debug():
    print 'step 1'
    print 3 + 4
    print 'finished'

debug()
... ... ... >>> step 1
7
finished

Now, let us redirect the printed lines to a file. We create a file object, and set sys.stdout equal to that file object.

import sys
print >> sys.__stdout__, '__stdout__ before = ', sys.__stdout__
print >> sys.__stdout__, 'stdout before = ', sys.stdout

f = open('data/debug.txt', 'w')
sys.stdout = f

# note that sys.__stdout__ does not change, but stdout does.
print >> sys.__stdout__, '__stdout__ after = ', sys.__stdout__
print >> sys.__stdout__, 'stdout after = ', sys.stdout

debug()

# reset stdout back to console
sys.stdout = sys.__stdout__

print f
f.close() # try to make it a habit to close files
print f
__stdout__ before =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
stdout before =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
>>> >>> >>> >>> ... __stdout__ after =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
stdout after =  <open file 'data/debug.txt', mode 'w' at 0x2ae7dbcbbb70>
>>> >>> >>> ... >>> >>> >>> <open file 'data/debug.txt', mode 'w' at 0x2ae7dbcbbb70>
>>> <closed file 'data/debug.txt', mode 'w' at 0x2ae7dbcbbb70>

Note it can be important to close files. If you are looping through large numbers of files, you will eventually run out of file handles, causing an error. We can use a context manager to automatically close the file like this

import sys

# use the open context manager to automatically close the file
with open('data/debug.txt', 'w') as f:
    sys.stdout = f
    debug()
    print >> sys.__stdout__, f

# reset stdout
sys.stdout = sys.__stdout__
print f
>>> ... ... ... ... ... <open file 'data/debug.txt', mode 'w' at 0x0000000002071C00>
... >>> <closed file 'data/debug.txt', mode 'w' at 0x0000000002071C00>

See, the file is closed for us! We can see the contents of our file like this.

cat data/debug.txt
step 1
7
finished

The approaches above are not fault safe. Suppose our debug function raised an exception. Then, it could be possible the line to reset the stdout would not be executed. We can solve this with try/finally code.

import sys

print 'before: ', sys.stdout
try:
    with open('data/debug-2.txt', 'w') as f:
        sys.stdout = f
        # print to the original stdout
        print >> sys.__stdout__, 'during: ', sys.stdout
        debug()
        raise Exception('something bad happened')
finally:
    # reset stdout
    sys.stdout = sys.__stdout__

print 'after: ', sys.stdout
print f # verify it is closed
print sys.stdout # verify this is reset
>>> before:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
... ... ... ... ... ... ... ... ... ... during:  <open file 'data/debug-2.txt', mode 'w' at 0x2ae7dbcbbf60>
Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
Exception: something bad happened
after:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
<closed file 'data/debug-2.txt', mode 'w' at 0x2ae7dbcbbf60>
<open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
cat data/debug-2.txt
step 1
7
finished

This is the kind of situation where a context manager is handy. Context managers are typically a class that executes some code when you “enter” the context, and then execute some code when you “exit” the context. Here we want to change sys.stdout to a new value inside our context, and change it back when we exit the context. We will store the value of sys.stdout going in, and restore it on the way out.

import sys

class redirect:
    def __init__(self, f=sys.stdout):
        "redirect print statement to f. f must be a file-like object"
        self.f = f
        self.stdout = sys.stdout
        print >> sys.__stdout__, 'init stdout: ', sys.stdout        
    def __enter__(self): 
        sys.stdout = self.f
        print >> sys.__stdout__,  'stdout in context-manager: ',sys.stdout
    def __exit__(self, *args):
        sys.stdout = self.stdout
        print '__stdout__ at exit = ',sys.__stdout__        

# regular printing
with redirect():
    debug()

# write to a file
with open('data/debug-3.txt', 'w') as f:
    with redirect(f):
        debug()

# mixed regular and 
with open('data/debug-4.txt', 'w') as f:
    with redirect(f):
        print 'testing redirect'
        with redirect():
            print 'temporary console printing'
            debug()
        print 'Now outside the inner context. this should go to data/debug-4.txt'
        debug()
        raise Exception('something else bad happened')

print sys.stdout
>>> ... ... ... ... ... ... ... ... ... ... ... ... >>> ... ... ... init stdout:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
stdout in context-manager:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
step 1
7
finished
__stdout__ at exit =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
... ... ... ... init stdout:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
stdout in context-manager:  <open file 'data/debug-3.txt', mode 'w' at 0x2ae7dbcbbb70>
__stdout__ at exit =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
... ... ... ... ... ... ... ... ... ... init stdout:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
stdout in context-manager:  <open file 'data/debug-4.txt', mode 'w' at 0x2ae7dca4d030>
init stdout:  <open file 'data/debug-4.txt', mode 'w' at 0x2ae7dca4d030>
stdout in context-manager:  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
temporary console printing
step 1
7
finished
__stdout__ at exit =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
Traceback (most recent call last):
  File "<stdin>", line 10, in <module>
Exception: something else bad happened
<open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>

Here are the contents of the debug file.

cat data/debug-3.txt
step 1
7
finished

The contents of the other debug file have some additional lines, because we printed some things while in the redirect context.

cat data/debug-4.txt
testing redirect
__stdout__ at exit =  <open file '<stdout>', mode 'w' at 0x2ae7d70e01e0>
Now outside the inner context. this should go to data/debug-4.txt
step 1
7
finished

See http://www.python.org/dev/peps/pep-0343/ (number 5) for another example of redirecting using a function decorator. I think it is harder to understand, because it uses a generator.

There were a couple of points in this section:

  1. You can control where things are printed in your programs by modifying the value of sys.stdout
  2. You can use try/except/finally blocks to make sure code gets executed in the event an exception is raised
  3. You can use context managers to make sure files get closed, and code gets executed if exceptions are raised.

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

org-mode source

Discuss on Twitter

Professor Kitchin is attending the 2013 China-America Frontiers of Engineering meeting

| categories: news | tags:

This meeting will be held in Beijing, China from May 15 to 17. Here is a description of the meeting from the website:

“CAFOE 2013 China-America Frontiers of Engineering Symposium

From May 15 - 17, the 2013 China-America Frontiers of Engineering will take place in Beijing, China. Sixty of the most promising engineers under the age of 45 from China and the United States will meet for an intensive 2-1/2 day symposium on developments at the cutting edge of engineering technology. The event is intended to facilitate international and cross-disciplinary research collaboration, promote the transfer of new techniques and approaches across disparate engineering fields, and encourage the creation of a transpacific network of world-class engineers.”

Professor Kitchin will present his work in CO2 capture, water splitting, computational materials design and computing in engineering at a poster session in this meeting.

This meeting is hosted by the National Academy of Engineering and the Chinese Academy of Engineering. Learn more at http://www.naefrontiers.org/Symposia/CAFOE.aspx

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

org-mode source

Discuss on Twitter

Getting keyword options in org-files

| categories: org-mode | tags:

Continuing my work in using org-mode as a structured markup language to encode data in files, today we look at how to extract keywords and properties from org-files. This will enable me to store data in the org-files, and then retrieve it later from code. For example, you may want to store the email address, or a student-id in the file so you can connect some other property such as a grade to it.

First we consider the built-in org commands to access aspects of the file properties. The org-export-get-environment command returns a plist of properties of the file.

(org-export-get-environment)
(:author ("John Kitchin") :creator "Generated by Org mode 7.9.4 in Emacs 24.0.92.1." :date nil :description nil :email "johnrkitchin@gmail.com" :exclude-tags ("noexport") :headline-levels 3 :keywords nil :language "en" :preserve-breaks nil :section-numbers t :select-tags ("export") :time-stamp-file t :title "file-options" :with-archived-trees headline :with-author t :with-clocks nil :with-creator comment :with-drawers nil :with-email nil :with-emphasize t :with-entities t :with-fixed-width t :with-footnotes t :with-inlinetasks t :with-plannings nil :with-priority nil :with-special-strings t :with-sub-superscript t :with-toc t :with-tables t :with-tags not-in-toc :with-tasks t :with-timestamps t :with-todo-keywords t :input-file "c:/Users/jkitchin/Dropbox/blogofile-jkitchin.github.com/_blog/file-options.org" :footnote-definition-alist nil :id-alist nil :macro-modification-time "(eval (format-time-string \"$1\" '(20870 38242)))" :macro-input-file "file-options.org" :macro-date "(eval (format-time-string \"$1\"))" :macro-time "(eval (format-time-string \"$1\"))" :macro-property "(eval (org-entry-get nil \"$1\" 'selective))" :back-end nil :translate-alist nil)

We can access a particular property like this:

(plist-get (org-export-get-environment) ':email)
"johnrkitchin@gmail.com"

Most of those properties are set externally to this file. It may be useful to set your own options in a file, e.g. we may want to set a STUDENT-ID tag in our file.

#+STUDENT-ID: jkitchin

We want to be able to extract the value of that keyword in emacs-lisp code. Here is one org way to do that. This code parses the buffer to get a list of elements, then applies a lambda function that makes a (key . value) to all the keyword elements, finally returning an alist. The second function gets the value for a specific key.

; suggested by Nicolas Goaziou
(defun jk-org-kwds ()
  "parse the buffer and return a cons list of (property . value)
from lines like:
#+PROPERTY: value"
  (org-element-map (org-element-parse-buffer 'element) 'keyword
                   (lambda (keyword) (cons (org-element-property :key keyword)
                                           (org-element-property :value keyword)))))

(defun jk-org-kwd (KEYWORD)
  "get the value of a KEYWORD in the form of #+KEYWORD: value"
  (cdr (assoc KEYWORD (jk-org-kwds))))

(jk-org-kwd "STUDENT-ID")
jkitchin

That is serious org-fu there. Here is an alternative approach that uses searching and regular expressions. The idea is to use a case-insensitive regular expression search in the buffer to grab the value.

(defun jk-get-file-keyword (KEYWORD)
  "get the value from a line like this
#+KEYWORD: value
in a file."
  (interactive)
  (let ((case-fold-search t)
        (re (format "^#\\+%s:[ \t]+\\([^\t\n]+\\)" KEYWORD)))
    (if (not (save-excursion
               (or (re-search-forward re nil t)
                   (re-search-backward re nil t))))
        (error (format "No line containing #+%s: value found" KEYWORD)))
    (match-string 1)))

(jk-get-file-keyword "STUDENT-ID")
jkitchin

Both methods basically do the same thing, and enable you to store data in org-files that is easily retrievable.

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

org-mode source

Discuss on Twitter

Summarizing org-files in a report

| categories: org-mode, emacs-lisp | tags:

This is an example of using emacs-lisp to extract pieces of information about a bunch of org-files into a single report, as well as aggregating data from those files. The scenario where this would likely be useful is if you have a set of org-files that contain information, e.g. from a bunch of different calculations, or from documents turned in by different students, and you want to aggregate the results into a report.

In this example, I have a set of org-files in this directory that contain simulated homework assignments turned in. The files in this example all look something like this. Each heading corresponds to a problem, and there is a properties drawer for each heading that contains the grade.

We will create a navigation document that facilitates reviewing each of the files, as well as collecting the grades from the files. Here is what a typical file looks like:

#+PROPERTY: NAME Ellen Donnte
* 1a
  :PROPERTIES:
  :lettergrade: A
  :END:
* 1b
  :PROPERTIES:
  :lettergrade: R
  :END:
* 2
  :PROPERTIES:
  :lettergrade: A
  :END:
#+BEGIN_SRC emacs-lisp
(prin1 42)
#+END_SRC

#+RESULTS:
: 42

* 3
  :PROPERTIES:
  :lettergrade: C
  :END:

1 Creating a navigation document

In this section we write some code that creates text with a link to each file we need to review. This is something I imagine we would do after all the files have been turned in and collected. This buffer would facilitate navigating all the files, and checking them off. First we create checkboxes. All this does is create an easy to use navigation document that facilitates opening the files, grading them, and marking them as done.1

(require 'find-lisp)

(dolist (fname (find-lisp-find-files "." "\\HW1.org$") nil)
  (princ (format "- [ ] [[file:%s][%s]]\n" fname (file-name-nondirectory fname))))

In the results above I have marked one entry as completed.

It might be preferrable to have links to places in the file, e.g. to problem 2.

(require 'find-lisp)

(dolist (fname (find-lisp-find-files "." "\\HW1.org$") nil)
  (princ (format "- [ ] [[file:%s::*2][%s - problem 2]]\n" fname (file-name-nondirectory fname))))

2 Aggregating properties

Our goal here is to use emacs-lisp to aggregate the letter grades from all the assignments into a table. This would be done after all the files have been reviewed. First, we write a function that gets the data we want. The function should take a filename, and return the letter grade for a problem, e.g. (get-letter-grade filename problem) -> lettergrade. Then, we will map that function onto a list of files.

(require 'find-lisp)

(defun get-letter-grade (filename problem-name)
  "Open filename, get the grade associated with the heading of problem-name."
  (with-temp-buffer
    (insert-file-contents filename)
    (let ((studentname nil)
           (lettergrade nil))
      (org-ctrl-c-ctrl-c) ; this is needed to read the NAME property!
      (setq studentname (org-entry-get (point) "NAME" t))
      (goto-char (point-min))
      (search-forward problem-name)
      (setq lettergrade (org-entry-get (point) "lettergrade"))
      
      (princ (format "|%s|%s|%s|\n" studentname problem-name lettergrade)))))

(princ "#+ATTR_HTML: :border 2 :rules all :frame border\n")
(princ "#+tblname: GRADES\n")
(princ "| Name | Problem | Grade |\n|-\n")

(dolist (problem-name '("1a" "1b" "2" "3") nil)
  (mapcar (lambda (fname) (get-letter-grade fname problem-name)) 
      (find-lisp-find-files "." "\\HW1.org$")))
Name Problem Grade
Slim Shady 1a A
John Doe 1a B
Jim Vicious 1a B
Ellen Donnte 1a A
Slim Shady 1b B
John Doe 1b A
Jim Vicious 1b C
Ellen Donnte 1b R
Slim Shady 2 R
John Doe 2 B
Jim Vicious 2 R
Ellen Donnte 2 A
Slim Shady 3 R
John Doe 3 B
Jim Vicious 3 D
Ellen Donnte 3 C

You could imagine some other kind of aggregating or analysis here too. Now that we have that table, we can use it in other analysis. Let us count the number of A's.

(save-excursion
  (goto-char (point-min))
  (search-forward-regexp "^#\\+tblname: GRADES")
  (next-line)
  (let ((A-COUNT 0)
        (letter-grade nil)
        ;; cddr is used to remove the first two rows of the table
        (data (cddr (org-table-to-lisp))))
    (dolist (entry data nil)
      (setq letter-grade (nth 2 entry))
      (if (equal  letter-grade "A")
          (incf A-COUNT)))
    (princ (format "%s A's counted" A-COUNT))))
4 A's counted

Since we are in org-mode, we can use the table directly! Let us do that and count the number of R's.

(let ((COUNT 0)
      (letter-grade nil))
    (dolist (entry (cddr data) nil)
      (setq letter-grade (nth 2 entry))
      (if (equal  letter-grade "R")
          (incf COUNT)))
    (princ (format "%s R's counted" COUNT))))
4 R's counted

3 Aggregating sections of org-files into one file

Another scenario that may be interesting is to collect all of the responses in a single document. This might be useful to show examples in class, or to review all the problems to see if there are common errors. Here we collect Problem 2.

(require 'find-lisp)

(generate-new-buffer "Problem 2")
(set-buffer "Problem 2")
(insert "#+TITLE: Summary of problem 2\n")

(dolist (fname (find-lisp-find-files "." "\\HW1.org$") nil)
  (save-excursion
    (goto-char (point-max))
    (org-mode)
    (with-temp-buffer 
      (insert-file-contents fname)
      (org-mode)
      (goto-char (point-min))
      (setq studentname (org-entry-get nil "NAME" t))
      (search-forward "* 2")
      (org-narrow-to-subtree)
      (forward-line) ; skip heading
      (setq text (buffer-substring (point) (point-max))))
    (insert (format "* 2 - %s\n" studentname))
    (insert text "\n")
          
    (search-backward "* 2")
    (org-entry-put nil "NAME" studentname)
    (org-entry-put nil "source" (format "[[%s][link]]" fname))
))

(switch-to-buffer "Problem 2")
(org-mode) ; switch to org-mode in that buffer

;; print the lines to see what we got
(dolist (line (split-string (buffer-string) "\n") nil) (princ (format ": %s\n" line)))
: #+TITLE: Summary of problem 2
: * 2 - Slim Shady
:   :PROPERTIES:
:   :lettergrade: R
:   :NAME:     Slim Shady
:   :source:   [[c:/Users/jkitchin/Dropbox/blogofile-jkitchin.github.com/_blog/org-report/Slim-Shady-HW1.org][link]]
:   :END:
: #+BEGIN_SRC python
: print 3
: 
: #+END_SRC
: 
: #+RESULTS:
: : 3
: 
: * 2 - John Doe
:   :PROPERTIES:
:   :lettergrade: B
:   :NAME:     John Doe
:   :source:   [[c:/Users/jkitchin/Dropbox/blogofile-jkitchin.github.com/_blog/org-report/John-Doe-HW1.org][link]]
:   :END:
: Here is my solution
: #+BEGIN_SRC python
: print 4
: #+END_SRC
: 
: #+RESULTS:
: : 4
: 
: * 2 - Jim Vicious
:   :PROPERTIES:
:   :lettergrade: R
:   :NAME:     Jim Vicious
:   :source:   [[c:/Users/jkitchin/Dropbox/blogofile-jkitchin.github.com/_blog/org-report/Jim-Vicious-HW1.org][link]]
:   :END:
: I could not figure this out
: * 2 - Ellen Donnte
:   :PROPERTIES:
:   :lettergrade: A
:   :NAME:     Ellen Donnte
:   :source:   [[c:/Users/jkitchin/Dropbox/blogofile-jkitchin.github.com/_blog/org-report/Ellen-Donnte-HW1.org][link]]
:   :END:
: #+BEGIN_SRC emacs-lisp
: (prin1 42)
: #+END_SRC
: 
: #+RESULTS:
: : 42

I am not super thrilled with this approach. It feels too much like hand-crafting a result, but it does show some possibilities!

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

org-mode source

Discuss on Twitter

Successful PhD defense for Anita Lee

| categories: news | tags:

Congratulations to Anita Lee for successfully defending her dissertation entitled “A Multi-Scale Approach to Understanding CO2-Solvent Systems for the Development of CO2 Capture Technologies”!

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

org-mode source

Discuss on Twitter
« Previous Page -- Next Page »