Posted by Tim Freund
Thu, 19 Feb 2009 03:34:00 GMT
The TurboJson package used in TurboGears can automatically serialize
most SQLAlchemy model objects. Hints can be provided for those objects
that TurboJson (and simplejson) cannot serialize on their own. Provide
a method named __json__(self) in a class definition to override the
default serialization algorithm:
class Invoice(object):
def __json__(self):
return {'id': self.id, 'date': self.date, 'line_items': self.line_items}
This is an especially handy technique if object X has a reference to
object Y and object Y has a reference to object X. The default
serialization algorithm will produce the following message: ValueError:
Circular reference detected.
This is something that I learned, and then forgot, and then spent 15
minutes searching for last night. I finally found the answer by
looking in the TurboJson test suite. This post serves as a reminder
to myself, and hopefully others.
Posted in Python | no comments
Posted by Tim Freund
Mon, 02 Feb 2009 00:46:00 GMT
Last time we established that recording audio with Python and
GStreamer is a fairly straightforward process. The next task for my
project was to find the peak recording level, so I turned to the
vumeter.py example to see how to get that information from a live
stream. This program is provided in the examples directory of the
gst-python package. You can download it here.
But it didn't work. The program produced an error message, and that was
all:
So how do we debug problems like this? A very cool feature of the
GStreamer Python bindings is support for debug output configuration on
the command line. Provide an integer from 0 to 5 to the
--gst-debug-level option to get debug output in real time while
the application runs. But don't ask for level 5 if you know what's
good for your terminal -- you will be overwhelmed with debug output.
Level 3 was very helpful in sorting out my problem. There was no recording
channel on the default device. The default device was a USB audio device
that had since been removed, so there really wasn't a default device
at all. The following output pointed me in the right direction:
0:00:00.302811092 27033 0x1039f30 WARN alsa confmisc.c:768:parse_card: alsalib error: cannot find card '0'
0:00:00.302949192 27033 0x1039f30 WARN alsa conf.c:3513:_snd_config_evaluate: alsalib error: function snd_func_card_driver returned error: No such file or directory
0:00:00.303018433 27033 0x1039f30 WARN alsa confmisc.c:392:snd_func_concat: alsalib error: error evaluating strings
0:00:00.303088567 27033 0x1039f30 WARN alsa conf.c:3513:_snd_config_evaluate: alsalib error: function snd_func_concat returned error: No such file or directory
0:00:00.303156031 27033 0x1039f30 WARN alsa confmisc.c:1251:snd_func_refer: alsalib error: error evaluating name
0:00:00.303225321 27033 0x1039f30 WARN alsa conf.c:3513:_snd_config_evaluate: alsalib error: function snd_func_refer returned error: No such file or directory
0:00:00.303295320 27033 0x1039f30 WARN alsa conf.c:3985:snd_config_expand: alsalib error: Evaluate error: No such file or directory
0:00:00.303364659 27033 0x1039f30 WARN alsa pcm.c:2196:snd_pcm_open_noupdate: alsalib error: Unknown PCM default
0:00:00.316686427 27033 0x1039f30 WARN alsa gstalsasrc.c:635:gst_alsasrc_open:<alsasrc0> error: Could not open audio device for recording.
0:00:00.316703039 27033 0x1039f30 WARN alsa gstalsasrc.c:635:gst_alsasrc_open:<alsasrc0> error: Recording open error on device 'default': No such file or directory
0:00:00.316743785 27033 0x1039f30 INFO GST_ERROR_SYSTEM gstelement.c:1675:gst_element_message_full:<alsasrc0> posting message: Could not open audio device for recording.
0:00:00.316788221 27033 0x1039f30 INFO GST_ERROR_SYSTEM gstelement.c:1698:gst_element_message_full:<alsasrc0> posted error message: Could not open audio device for recording.
For the record, I was able to get this output (and a lot more) with the following
invocation:
python vumeter.py --gst-debug-level=3
The vumeter.py program started to work once the GStreamer pipeline
string was changed to specific an Alsa device:
s = 'alsasrc device=hw:1 ! level message=true ! fakesink'
We covered how to get an Alsa device ID in the last installment. Check it out
to remember how that was done.
Posted in Python | no comments
Posted by Tim Freund
Thu, 29 Jan 2009 03:45:00 GMT
I have a project idea that involves recording audio from USB powered
microphones, and I wanted to create a quick sketch of a program to
judge the difficulty of doing that with Python. Short answer? It's
really easy. If you're impatient, you can download this sample
script.
For those of you still reading, I'll explain the process in a little
more detail. The GStreamer library handles all sorts of media input and
output, and there are bindings written for Python. This seemed like an
excellent avenue for my project, but I had some trouble getting started.
I discovered Jokosher after a bit of searching. It is a multi-track
recording application written with Python and GStreamer. I was able
to use the debug output of Jokosher to figure out what an appropriate
GStreamer pipeline would look like to capture audio from my microphone,
and I was able to see how D-Bus can be used to determine the ALSA card
identifier for any given sound device.
This web cam tutorial was also helpful, as it demonstrated how to properly
close a GStreamer pipeline.
Of course, after my proof of concept was done I stumbled across this
documentation for GStreamer's Python bindings.
I will excerpt the interesting bits of the script here, but don't forget
that you can download the entire thing.
List Sound Cards and Get Their ALSA ID
def list_capture_devices():
bus = dbus.SystemBus()
hal_manager = bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")
hal_manager = dbus.Interface(hal_manager, "org.freedesktop.Hal.Manager")
devices = hal_manager.FindDeviceStringMatch("alsa.type", "capture")
identifiers = []
for dev in devices:
device = bus.get_object("org.freedesktop.Hal", dev)
card = device.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device")
if card["alsa.card"] not in identifiers:
print "%d. %s" % (card["alsa.card"], card["alsa.card_id"])
identifiers.append(card["alsa.card"])
return identifiers
Start (and Stop) a GStreamer Pipeline
def record(device_id, capture_path):
pipeline = gst.parse_launch("""alsasrc device=hw:%d ! audioconvert ! level name=recordlevel interval=10000000 ! audioconvert ! flacenc ! filesink location=%s""" % (device_id, capture_path))
pipeline.set_state(gst.STATE_PLAYING)
print "recording, press enter to stop"
sys.stdin.readline()
pipeline.set_state(gst.STATE_NULL)
time.sleep(5)
With as easy as this seems, maybe we really will import antigravity one day.
Posted in Open Source, Python | no comments
Posted by Tim Freund
Mon, 26 Jan 2009 05:07:00 GMT
This is a small detour from the planned discussion the different
approaches available for making HTTP requests. I needed an easy
topic since it has been so long since I last found time to work on
the application.
Those who have seen Rhymote in action know that it is pretty ugly, and
I want to change that. In addition to being ugly, the buttons are so
small that I can easily hit the wrong one. Right now the application
uses text on a standard Android button to crudely imitate the buttons
of a media player. Using legitimate media player icons would make the
app look much nicer. For those who haven't seen the app in its ugly
form, take a look:
Find or Design Icons
Android applications can use images as icons. The standard image
format is PNG, and images can be placed in the res/drawable
directory of your project.
Since I am a programmer and not a designer, I chose to look for
appropriate open source icons. The Tango Icon Library is a great
looking icon set, the icons are available in a scalable format, and
the set is available under a Creative Commons license. Awesome.
I tried to use the SVG icons directly, but that didn't work. Instead,
I used Inkscape to export the SVG icons as appropriately sized PNG
files. For reference, I made the icons approximately 100 pixels tall.
Update The Layout
The button definitions in the layout file now look something like this:
<ImageButton android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/media_skip_backward"></ImageButton>
Unfortunately the buttons still had the default white background.
When we finally get around to displaying album art, transparent button
backgrounds will allow that album art to be seen. To achieve this,
I made a small (10x10) transparent PNG file with no content. We can
then set the android:background attribute of the ImageButton to
achieve the desired effect.
<ImageButton android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/media_skip_backward"
android:background="@drawable/transparent"></ImageButton>
For this small amount of work, we get an application that looks much
better than before:
Posted in Android, Open Source | no comments
Posted by Tim Freund
Tue, 13 Jan 2009 05:47:00 GMT
There's nothing more relaxing than to sit down and write an
implementation of a commonly available network service in your
favorite scripting language. OK, so maybe writing an SFTP server in
Python wasn't the most relaxing thing I could have done this evening,
but the Paramiko library made it pretty darn easy, and I had a lot of
fun because the work moved so quickly.
Did I mention how quickly the development went? 284 lines, including
the license and a demo configuration file. And people wonder why
I enjoy Python programming so much.
But Why?
Most code is written to scratch an itch, and this code was no
different. We write a number of scheduled jobs at work that upload or
download files from SFTP servers. We needed a way to test the jobs,
but we didn't want to set up operating system accounts on our servers
to run the tests.
My minimal implementation of an SFTP service allows us to test many
job configurations with one SFTP service running as an unprivileged
user. The user accounts are optionally chroot'ed into their "home"
directory, so it appears as if they are connecting to a server that
has the exact same file hierarchy that is present on the production
file servers.
To play around with the code yourself, you can install Paramiko and
clone the Simple SFTP Server Mercurial repository. If you don't
have Mercurial installed, you can always download the script straight
out of the repository.
The code works, but it is minimally functional: it only does what we
needed it to do at work. No worries, though. It is licensed under
the MIT license, so you are free to do with it what you please. Patches
are accepted if you are feeling generous.
Posted in Open Source, Python | no comments
Posted by Tim Freund
Wed, 07 Jan 2009 04:32:00 GMT
Where we last left off, Rhymote worked, but it was painfully slow.
All of the network communication takes place in the UI thread, and
that's a big problem. An application has 5 seconds to respond to a UI
event, and if the click handler isn't done in that amount of time, the
user will be asked if they'd like to terminate the application. The
exact error message will look something like this:
Sorry! Activity Rhymote
(in application Rhymote)
is not responding.
Even though Rhymote is only making one HTTP request per button press,
that can put us over the 5 second limit. The HTTP requests return quickly
in the emulator, but the phone is a totally different world.
A partial solution to the problem is threading. We want to start
a new thread of execution separate from the UI to handle time
intensive work. These time intensive tasks can then update the UI if
necessary once they are done. If you are at all experienced with Java
threading, you will already know how to create a Runnable object
and pass that Runnable to a Thread. If you are new here,
follow this cheat sheet:
package com.example.androidapp;
public class WebRequestRunnable implements Runnable {
public void run(){
// do resource intensive stuff here...
// create an http request and read the results
}
}
and
package com.example.androidapp;
public class Application {
...
private OnClickListener mButtonListener = new OnClickListener() {
public void onClick(View v) {
WebRequestRunnable wrr = new WebRequestRunnable();
Thread t = new Thread(wrr);
t.start();
}
}
}
More information about this basic pattern can be found at the following
Android resource pages:
Although the application no longer hangs when responding to a button
press, there is still a latency problem. The HTTP request that was
taking 5+ seconds to execute in the UI thread is now taking 5+ seconds
to execute in a second thread. Two potential issues come to mind:
- The Apache Commons HttpClient code is really slow
- I'm doing something foolish in my code
I learned long ago to never blame another programmer or library
without some very solid evidence. When in doubt, it is probably my
own fault. Next time we will attempt to find the most efficient way
to deal with the required network requests.
Posted in Android, Open Source | no comments
Posted by Tim Freund
Tue, 06 Jan 2009 02:41:00 GMT
Is it possible to stay on top once you get there? That is the central
question in The Innovator's Dilemma. Although I'm far from the top of
anything, I read it anyway because so many other people have said it is an amazing book.
Oh, and it can be had for a song on Amazon.com.
I read The Innovator's Dilemma primarily because it is mentioned by so
many other entrepreneurial people as a great book. The fact that it can
be had used for a fraction of the cover price didn't hurt, either.
I was able to storm through the book in a single day, not because it
was captivating, but because it was a pretty good read and I was
enduring a full day's worth of air travel. The first part of the book
was more interesting than Sky Mall, but less interesting than much of
the reading material around my house. There were some great nuggets
of wisdom in the middle and back of the book that I may have never
read if it weren't for my situation.
Christensen uses the hard drive industry as his primary example
throughout the book. The industry has a large body of empirical
evidence available for research, and, as a bonus for those of us who
code, it is an industry that most software developers are already
familiar.
Despite the evidence available in the hard drive industry, I was
actually more interested in the other examples used in the book,
especially the story of the mini steel mill industry.
The example of mini mill steel plants eating away at big steel's market
from the bottom up is a great example of why it is OK to focus on small
problems. Small problems solved well create opportunities for us to
turn our attention to ever larger problems. If businesses can boom
and blossom with that approach in a high capital high risk business
like steel, then we software developers have no excuse not to
consider small problems.
The Innovator's Dilemma isn't a book that I will keep on my shelf to
read again and again, but it is one that I am very glad to have read
through once.
Posted in Reading List | no comments
Posted by Tim Freund
Fri, 19 Dec 2008 06:31:00 GMT
I'm still working on the Rhymote project.
I sat down with the intent of immediately putting the code on my
phone, but it was just so ugly that I had to spend about 30 minutes
cleaning things up. Let that be a lesson: late night programming
can be fun, but it isn't always productive.
Configuring the G1 for Deployment
First I opened the Settings program and choose Applications.
I checked the box next to Unknown Sources
While I was still on the Applications screen of the Settings
program, I selected Development and checked the box next to USB
Debugging.
Configuring my Computer for Deployment
Since I am developing the software on Ubuntu, I also need to configure
udev to recognize the phone as more than just a USB drive:
# echo "SUBSYSTEM=="usb", SYSFS{idVendor}=="0bb4", MODE="0666"" > /etc/udev/rules.d/50-android.rules
Developers who are using Windows or OS X do not need to worry about
similar changes for their operating system.
Deploying the Application
Then I plugged the phone in and ran:
$ adb -d install ~/workspace/Rhymote/bin/Rhymote.apk
The application works on the real device just like in the emulator,
except for one painful difference: it is terribly slow. Slow enough
that the system asks if I would like to terminate the application
while the OnClickListeners are firing. That's not so great.
Since this is kind of a hack-and-slash project, I just create a
BasicHttpRequest object right in the listener, fire it off,
and wait for the response. That worked great in the emulator,
but I guess I need to make the HTTP request in the background when
I am running on a real phone.
I could have learned all about that if took the time to read the docs
up front, but that's OK. I'm happy to have something that works, even
if it isn't working great, and I know what I can learn next.
Posted in Android, Open Source | 3 comments
Posted by Tim Freund
Wed, 17 Dec 2008 03:04:00 GMT
Programmers are supposed to be lazy right? Here is the scene for one
of my lazy moments: my home office contains a desk and a futon in
opposite corners. I often want to change music on my desktop PC while
reading on the futon. Most people would just get up to change the
music without much thought, but I'd much rather use my G1 as a remote
control for Rhythmbox. This will be my first Android project, and
it shouldn't be too much to handle for a newbie like myself.
I started the Rhymote project yesterday, and I am writing these
posts to keep track of all the hurdles I encounter along the
way. Although the project could end up doing all sorts of neat stuff,
all I really want to do is a handful of operations:
- Play/Pause
- Next track
- Previous track
- Volume adjustments
I have a tendency to over-engineer solutions, so I'm going to
explicitly avoid anything that smacks of over ambitious design. Quick
and dirty is the name of the game. The code will grow up if it
holds my interest for long enough.
Here's the gear I am working with:
- Ubuntu 8.10
- Rhythmbox 0.11.6
- Eclipse 3.4
- Android SDK + Eclipse Plug-in
- T-Mobile G1
All of the code that I write throughout the series can be found in
this repository.
Prerequisite: Put Rhythmbox on the Network
Rhythmbox isn't on the network by default, and I needed to change that if I
was going to have any luck with this project. In the spirit of quick and
dirty, I wrote a WSGI application that wraps rhythmbox-client. This solution
will evolve as the android application evolves, but it is tangential to the
process of Android application development. If I do anything particularly
interesting with Rhythmbox, I'll be sure to write it up separately.
Start an Android Project
I write Java in Eclipse most of the day, so I felt pretty comfortable
starting the Android portion of the Rhymote project. I followed along
with the Hello Android tutorial, and it wasn't long before the string
"Hello Android" was staring back at me from the emulator.
Create a User Interface
The first thing I needed was a UI, and building fat client user
interfaces is foreign territory for me. Oh, what's that? Android
uses XML to define screen layouts? Maybe this won't be so bad
after all. Remember, I did say I am a Java developer by day.
Make no mistakes, my UI is ugly. Did I mention that I don't
write much UI code at my day job?
Find the System Log
I spotted the commons-httpclient code in the API, and decided to
code up a client in an OnClickListener for each of my buttons.
After deploying the code and watching the application crash, I was
left scratching my head. Where can I see the logs? Fortunately the
command adb logcat is the Android equivalent of tail -f, and I was
able to pinpoint the problem with my HTTP client code.
Discover Permissions
The exception I found was a little strange:
E/AndroidRuntime( 6490): Caused by: java.net.SocketException: unknown error
E/AndroidRuntime( 6490): at org.apache.harmony.luni.platform.OSNetworkSystem.createSocketImpl(Native Method)
E/AndroidRuntime( 6490): at org.apache.harmony.luni.platform.OSNetworkSystem.createSocket(OSNetworkSystem.java:79)
E/AndroidRuntime( 6490): at org.apache.harmony.luni.net.PlainSocketImpl2.create(PlainSocketImpl2.java:59)
E/AndroidRuntime( 6490): at java.net.Socket.checkClosedAndCreate(Socket.java:763)
E/AndroidRuntime( 6490): at java.net.Socket.connect(Socket.java:910)
E/AndroidRuntime( 6490): at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:117)
E/AndroidRuntime( 6490): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129)
E/AndroidRuntime( 6490): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
E/AndroidRuntime( 6490): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
E/AndroidRuntime( 6490): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348)
E/AndroidRuntime( 6490): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
E/AndroidRuntime( 6490): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
E/AndroidRuntime( 6490): at us.achievewith.Rhymote.Rhymote$1.onClick(Rhymote.java:60)
E/AndroidRuntime( 6490): ... 23 more
I was expecting something along the lines of Connection Refused or
Unknown Host, but unknown error was a little vague to be of much
use. It was already late, and I foolishly struggled with the error
for a while before letting Google Search work its magic, and that's
when I learned about permissions.
Android applications don't just get free reign over the system without
first asking for permissions to do most anything that interacts with
the system at large or other systems. The following line cleared
up the exception once it was added to the AndroidManifest.xml file:
<uses-permission android:name="android.permission.INTERNET"/>
One more run through the emulator, and I was able to start and stop
my music. At this point it was 3:00 AM, and I decided that putting
the code on the phone could wait.
Posted in Android, Open Source | 1 comment
Posted by Tim Freund
Tue, 21 Oct 2008 03:35:00 GMT
Your TurboGears 2 application is not perfect. Just for a second,
let's pretend like it is perfect. Even with all of its perfection,
your application will need to deal with bad incoming links and
malformed data. Is it ready? It is only a matter of time before your
users receive a 404 or 500 response from your most wonderful
application. Whether the source of trouble is a bug in the code or a
bad incoming link, why leave users lost in the dark? Custom error
pages can put them back on track when something goes wrong, and they
are very easy to implement. We will create one in the following few
paragraphs.
We will use Turtle Goals to demonstrate the techniques in this
tutorial. It is open source and fairly simple. Please feel free to
follow along in the Turtle Goals source, or work along in your
own TurboGears 2 project.
Look in your controllers directory. See that file named error.py?
That's the key to a custom error page. The Routes package does a
bit of work behind the scenes to send any request that generates an
error through the ErrorController, so by customizing the
ErrorController, we can customize the resulting 404 and 500 pages.
The default document method produces a standard Pylons error page.
It looks nice, but it probably doesn't look right compared to the rest
of your project.
TurboGears projects default to the Genshi template engine, and
that is the engine used by Turtle Goals. Let's create a new template,
error.html, and save it in the templates directory.
So now all we need to do is add the expose decorator to the
document method, and return a dictionary of appropriate values.
Done, with enough time to check Reddit before your next meeting,
right? Well, almost. Look closely at the ErrorController
definition, and you will see that it is not a standard TurboGears
controller. It extends a Pylons controller class, WSGIController,
and that causes it to behave differently from our other controllers.
At least it should extend WSGIController according to a post on the
mailing list. Apparently there is a bug in the quickstart
template, and you you may need to chage the ErrorController definition
to extend WSGIController. I was easy to convince: as soon as I made
the suggested change, my error page started working. Back to the
point: the expose decorator will do you no good inside of the
WSGIController. It is up to us to render the template to a string and
return that string. Fortunately TurboGears provides a method to do
just that:
from tg import render
rendered_template = render.render_genshi("error.html", {})
Of course there are also methods like render.render_mako and
render.render_jinja if you prefer those other template engines.
Here's the full listing of our modified error.py:
There's one other small matter to deal with: configuration. There are
two relevant configuration values in your application: debug and
full_stack. To use your custom 404 error but still get the
interactive debug page for 500 errors, try the following:
debug = true
full_stack = true
The interactive debug page is inappropriate for production
environments. When your application is deployed into production, use
these settings instead:
debug = false
full_stack = true
If you have both set to false, you will get generic error pages that
end users will run from, screaming. All done, for real this time. And
you still have time to check Reddit, but you may want to check out
these links instead:
What is your strategy for designing and implementing custom 404 and 500
error pages?
Posted in Python, TurboGears, Tutorials | no comments