Book Review: Programming Clojure

If I were to write my What I Did This Summer essay this Fall — it would be how I struggled to learn the Clojure language. Since May of this year, using book review recommendations, I’ve purchased Practical Clojure (Apress) and The Joy of Clojure (Manning).

In addition, Chas Emerick is writing Clojure Programming (O’Reilly) which, along with Clojure in Action, is about to be released in early Fall.

Stuart Halloway’s Programming Clojure (Manning) was reviewed as an excellent book, but one comment said it was older and only covered Clojure 1.0. Although I value the books I’ve purchased, unfortunately I fell for the comment that Programming Clojure was an older book. As a result I initially missed out on a very well written book that helps guide those of us who are new to functional programming (FP) into the Clojure language. In other words, this is a very good book for those of us not used to Lisp or functional programming.

As it is with many books, especially some of the advanced Python books I’ve purchased, I’ll come back to the other books I’ve purchased as references or to take advantage of another viewpoint, so I am not disappointed to have purchased them.

I’m on a mission to learn well, appreciate, and eventually use Clojure in our production environment. For me this is serious enough that I’ve enrolled in a to-be-announced, instructor-led Clojure course at http:/codelesson.com .

Having programmed in many different languages, I heartily recommend Programming Clojure as a good book overall and certainly a good first book for Clojure.

Enjoy!


Clojure: Storing Data for Later Use

One of the biggest hurdles I’ve encountered learning Clojure is when to traverse a sequence or merely to apply a function to it, like zipmap. The other hurdle is how to populate local variables properly in a let statement.

The program I am designing to learn Clojure reads a .csv file filled with water meter data. My goal is to extract a premise id and reading, and do something with that data. The premise id is in column 12, zero-indexed at all, and reading is at column 2, zero-indexed by 1.

zipmap takes the values derived from the two map statements, and creates a sequence of values, which is what I wanted. The read-map value can be passed on to other functions.

This solution came from clojure Google group. Enroute to this solution, I tried various ways to populate the map variable, and these did not work or generated a compile-time error. It would be good for someone to write a cookbook that discussed pulling data out of files and storing it in different ways in local variables.


(ns test-csv

(:gen-class)
  (:use clojure.contrib.command-line)
  (:use clojure-csv.core))

  (defn process-file
    "Process csv file and prints a column in every row"
    [file-name]
    (let [data (slurp file-name)
          rows (parse-csv data)
          read-map (zipmap (map #(nth % 11 nil) rows) (map #(nth % 1 nil) rows))]
    (println read-map)))

(defn -main [& args]
    (with-command-line args
     "Get csv file name"
     [[file-name ".csv file name" "resultset.csv"]]
     [[file-name ".csv file name" 1]]
     (println "file-name:", file-name)
     (process-file file-name)))

Clojure: Command Line Processing

If I have any complaints about Clojure documentation it’s lack of I/O basics in the core documentation, like reading and writing files. This is a small matter, though, because there are lots of folks out there talking about those issues, like http://tech.puredanger.com/2010/02/25/clojure-command-line-options/.

I’ve modified my own .csv reading application to take a file name from the command line:

(ns test-csv
  (:gen-class)
  (:use clojure.contrib.command-line)
  (:use clojure-csv.core))</code>

(defn process-file
  "Process csv file and prints first item in every row"
  [file-name]
  (let [data (slurp file-name)
  rows (parse-csv data)]
  (dorun (map #(println (first %)) rows))))


(defn -main [&amp;amp; args]
  (with-command-line args
  "Get csv file name"
  [[file-name ".csv file name" 1]]
  (println "file-name:", file-name)
  (if file-name
  (process-file "resultset.csv")
  (process-file file-name))))

As the Google Clojure Users Group pointed out, there is a better way to get the default file on the command line, in case none is specified:

(defn -main [&amp; args]
  (with-command-line args
    "Get csv file name"
    [[file-name ".csv file name" "resultset.csv"]]
    (println "filename:" file-name)
    (process-file file-name)))

Tolerant Technical Communities

I don’t know what it is about the Clojure community, but it is very accepting of beginners and questions that sometimes ask  for feedback stating the obvious.  I have been programming over thirty years and paid to do so for over twenty-five years. I have used a lot of languages, but Lisp syntax has always thrown me for a loop.

The Clojure users group on Google http://groups.google.com/group/clojure?hl=en, Getting Clojure http://www.gettingclojure.com/, and resources on this blog’s Clojure Resource Page https://drknucklehead.wordpress.com/clojure-resources/ are good places to ask questions and get help.

It would be unfair of me not to include the Python community in this tribute as well. The core Python community is always encouraging and willing to answer questions. They are also a great group of people.


Programming Languages Used and Using

These are the computer programming languages I have used over the past few years.

1985 – 1987

PL/I Subset G

1987 – 1990

C, Bliss, Assember (DEC VAX)

1990 – 2000

C, C++

2000 – 2002

VBScript/VBA

2002-2003

Java, C, C++, Perl

2004 – current

Informix 4GL, C, VB, Perl, Python, JavaScript, Clojure


Book Review: Pro Python Marty Alchin

Pro Python is one of those books whose detail and clear writing took some time to appreciate. I purchased it while still learning Python, and other than taking a cursor look at the chapters, I did not have time to read it. I purchased the book during the implementation of an Automated Meter Reading (AMR) application that handles meter configuration and reads.

Eventually, I had time to read the book in greater detail. First, it contains the first, full, good description of Python decorators I have ever read. I had searched the web pretty thoroughly for an explanation before finding in Pro Python.  The book breaks down advanced topics into basics, functions, classes, meta programming, and includes distribution, not always covered by other texts.

This book does more to explain the Python mindset needed for clear, concise code, than some of the explanations I have read that seem to favor dictating a “Python Way” to the reader. Therefore, once you have learned Python basics, this book is a must for your library.

Bon Apetit!


Why Clojure?

These days, I program in several environments using several well-known traditional languages. These are to maintain old Informix “forms” code written in Informix 4GL and a few specialized I/O routines written in C; web programming using Javascript, Perl, PHP, Django/Python, and Python server applications and applets.

While browsing SoftPro’s web site — my favorite on-line and walk-in book seller in Centennial, CO — http://softpro.com/   — I came across a book called The Joy of Clojure. I wondered what Clojure was and why it was created.

Then, I read a very good review on two Clojure books — http://programmingzen.com/2010/07/16/in-depth-book-review-practical-clojure/ — and wound up purchasing both books mentioned in the review, The Joy of Clojure and Practical Clojure. Each book targets something different, and appears to be very well written.

Given the wide-spread interest in Python and that web sites web sites are written using html, JavaScript, Perl, PHP or a framework like Rails or Django are mainstream, the likelihood our town will ever see Clojure production programs is unlikely.

I am instead learning Clojure, because it is unlike other languages I’ve used. However, given its relationship with Java, learning Clojure might not turn out to be a long shot, at least learning functional programming might turn out not to be a long shot.


Parsing An Industrial XML File

Attached is code to parse an XML files that we will soon receive from our Automated Meter Reading (AMR) vendor. We bill our water accounts from daily reads, which we receive seven days a week. Hourly reads will assist our water department with helping customers figure out water usage. In other words, hourly reads serve as diagnostic information to see when water was used during a twenty-four hour period.

Each of the search tags in the Python program, which I believe are XML leaves, are the actual parts of the XML file. Adding these to the dictionary this way ties the XML file to the dictionary, which in this case I believe is a good thing, if someone looks at the XML file all on its own.

This example uses lxml, which from what I can interpret out in the Python community is the XML parsing library of choice.

"""
Pulls a name space away from an element. This is for easier searching.
"""

def parseHrNs(qname):
 try:
 namespace, element_name = re.search('^{(.+)}(.+)$', qname).groups()

 except:
 namespace = None
 element_name = qname

 return namespace, element_name

def parseHrXmlDoc(fnam):

 rc = 0
 se_read = []

 current_ch = 0
 current_endpoint = 0

 ert_ch_text_key = ' '

 hrDict = {}

 try:
 context = etree.iterparse(fnam)

 except:
 context = None
 rc = -1

 if 0 == rc:
 nspace = None

 """
 These searches are roughly in order of appearance in the XML document.

 Channel number found after ID.

 ID is the place to write the last data before assigning new ID.

 Check on se_read[] provides initialization step, so we don't assign rubbish at the
 beginning.
 """

 for action, elem in context:
 nspace, search_tag = parseHrNs(elem.tag)

 #print("%s: %s" % (search_tag, elem.text))

 if 'exportGuid' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'exportDateTime' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'collectionSystemType' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'collectionSystemID' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'startTime' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'endTime' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'intervalLengthInSeconds' == search_tag:
 hrDict[search_tag] = elem.text

 elif 'ID' == search_tag:

 """
 There really are null elements, so perform a continue.
 """

 if not elem.text:
 continue
 else:
 """
 This is an initialization step.
 se_read is an empty list on initialization, so
 just assign the current endpoint if se_read is an empty list. That
 should only happen once.

 After initialization, basically, this is already visted logic.
 We've already seen and cached the ID in current_endpoint and
 the channel in current_ch. Before assigning the new ID,
 assign what we have in the dictionary. The fact we're here
 means we've seen a new ID, so the prior data must be put in dictionary.
 """

 if se_read:

 """
 Endpoint ids are not unique for the dictionary if they are dual-port, so
 we need to create a unique dual key based on the channel number.

 current_ch was already saved, and look below. We have not taken
 elem.text's value yet. We're still going on already cached data, which
 is our intent. See explanation above.
 """

 ert_ch_text_key = str(current_endpoint) + '-' + str(current_ch)

 hrDict[ert_ch_text_key] = se_read

 se_read = []

 current_endpoint = elem.text

 elif 'channelID' == search_tag:
 """
 Channel must be preserved, even though we've already seen ID.
 current_ch will be used before next ID assigned.
 """

 current_ch = elem.text

 elif 'value' == search_tag:
 se_read.append(elem.text)

 return hrDict


mod_wsgi 64-bit

When building mod_wsgi there are two things to remember if you are building on a 64-bit Linux system. My examples came from Red Hat Enterprise Linux 5 WS.

Here is the link to these and other problems:

http://code.google.com/p/modwsgi/wiki/InstallationIssues

First, When running configure make sure the –enable-shared switch is used. This will create a shared object library similar to this /usr/local/lib:


-r-xr-xr-x  1 root root  5624411 Feb  7 10:11 libpython2.6.so.1.0

You will need to add a link, so this library can be found by Apache or your web server:


lrwxrwxrwx  1 root root       19 Feb  7 10:11 libpython2.6.so -> libpython2.6.so
 .1.0

And finally, don’t forget to compile the library’s location into make (mod_wsgi’s build directory).

 LD_RUN_PATH=/usr/local/lib make 

before running make install.

Building the shared library information is important, so you do not get an error like this:

Syntax error lin line 200 of /etc/httpd/conf/httpd.con Cannot load /etc/httpd/modules/mod_wsgi.so into server: libpython2.6.so.1.0: cannot open shared object file: No such file or directory

I had mistakenly thought you Apache would use environment variables to locate the shared library.


Choosing Django: mod_wsgi Configuration

The focus of using mod_python is to load and configure in Apache’s configuration file.

Here is a typical configuration extract for mod_python:


SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE settings
SetEnv PYTHON_EGG_CACHE /tmp/.python_eggs
PythonOption django.root /home/amr/django/amr
PythonPath "['/home/amr/django/amr/bin', '/home/amr/django', '/home/amr/django/amr', '/home/amr/django/amr/media', '/home/amr/django/amr/media/admin'] + sys.path"
PythonDebug On

On the other  hand, mod_wsgi is mentioned in the Apache configuration, but a lot more of the configuration is contained within the mod_wsgi handler, which you write:


WSGIScriptAlias / /usr/local/www/wsgi-scripts/wsgi_handler.py
<Directory /usr/local/www/wsgi-scripts>
 Order allow,deny
 Allow from all
</Directory>

Here is a working mod_wsgi handler:

import os
import sys

PROJECT_ROOT = os.path.realpath(os.path.dirname(__file__))
sys.path.append(PROJECT_ROOT)
sys.path.append(os.path.join('/home/amr/django'))
sys.path.append(os.path.join('/home/amr/django/amr'))
sys.path.append(os.path.join('/home/amr/bin'))
sys.path.append(os.path.join('/usr/local/www/documents/media'))
sys.path.append(os.path.join('/usr/local/www/documents/media/media'))

os.environ['DJANGO_SETTINGS_MODULE'] = 'amr.settings'
os.environ['PYTHON_EGG_CACHE'] = '/tmp/python_egg_cache'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

And finally, here is the full Apache configuration, which will allow loading css and other static documents along with the Django admin site.


Listen 8002
<VirtualHost _default_:8002>

AliasMatch ^/([^/]*\.css) /usr/local/www/documents/media/$1

Alias /media/ /usr/local/www/documents/media/media/

Alias /static/ /usr/local/www/documents/media/
<Directory /usr/local/www/documents/media/>
Order deny,allow
Allow from all
</Directory>

WSGIScriptAlias / /usr/local/www/wsgi-scripts/wsgi_handler.py
<Directory /usr/local/www/wsgi-scripts>
 Order allow,deny
 Allow from all
</Directory>

ServerAdmin someone@someone.com
</VirtualHost>

Finally, the pertinent environment variables from settings.py

# Main URL for the project
BASE_URL = 'http://myserver:8002'

# Absolute path to the directory that holds media
MEDIA_ROOT = '/usr/local/www/documents/media/'

# URL that handles the media served from MEDIA_ROOT
MEDIA_URL = 'http://myserver:8002/'

# URL prefix for admin media -- CSS, JavaScript and images.
ADMIN_MEDIA_PREFIX = BASE_URL + '/media/'