Fun with Python

Posted February 12, 2011 under Python

I’ve used Python fairly actively for the past 4 years. Being entirely self taught on it, I’m rather certain I’ve been missing out on quite a lot of nice functionality. Most of my time has been spent writing my own Python code, and much less reading others. Which I’m sure has left me in a pretty poor state, as I think there are a lot of features that I could (and should) be using to write cleaner code.

Today I realized Python has class variables. It makes total sense, since many other languages have it, and otherwise you would have to use a global, but I never thought about using one:

Another sweet trick is using the map function to iterate over a list, and pass each item into a function. You could create the same two people like this:

map(Person, ["chris", "bob"])

Another thing I discovered recently, that I really really love, is chaining comparison operators.

if 10 <= x < 100

The question mark operator for if then statements has always irked me. Every time I come across it, it takes me twice as long to understand what the meaning of the statement is (maybe I’m just slow :P ). In python you can use full if then syntax in your assignements:

x = 3 if (y == 1) else 2

Debugging regex with re.DEBUG, object generators, and many more hidden features of Python are listed on this page: http://stackoverflow.com/questions/101268/hidden-features-of-python

Spend a few minutes reading through that and you’ll be seriously impressed. There is a lot of hidden functionality with Python that is quite awesome. After reading Metaprogramming Ruby recently, I was very intrigued, but now I’m not sure which language I like better. Python is beautiful in its own way, and so is Ruby.


Ubuntu Quickly, Python, and DistUtils

Posted December 20, 2010 under Keryx, Python

Preparing for the Keryx 1.0 release over the past couple of days, one of the things I’ve been learning is how to build Ubuntu and Debian packages. Sure sure, I’ve been “cheating” using Quickly. :)

Quickly is probably one of the coolest tools I have used. All the hassle of building an Ubuntu application is already taken care of, and it takes the Ruby on Rails approach to guiding you through development. It’s quite great.

As I was packaging Keryx 1.0 last night, I ran into an issue. DistUtils could not find my python modules. I was getting an ERROR: Python XXXXXXX module not found. when I would run quickly package.

Well that’s no good. Keryx runs just fine, all those modules are imported just fine. What the heck is going on??

I spent a few minutes trying to configure DistUtils py_modules to try to add them manually. No dice. It turns out that DistUtils really does not like relative path imports. Seriously?? Yeah. :( Sad story huh?

The most convenient solution I could come up with was to change ALL of my imports. My directory structure was pretty simple:

bin/keryx
keryx/
keryx/unwrapt

bin/keryx is the main application. keryx is a folder of extra GTK code. keryx/unwrapt is the entire backend for Keryx. The backend is a separate module and uses relative imports so that it can function independently.

I decided to move Unwrapt into Keryx’s source tree instead of making it a separate package. This would reduce dependencies and overall make things less complicated. The solution to DistUtils problem was to simply go through and change all of my imports from relative import XXXX to import keryx.unwrapt.XXXX. You reference modules from the top level which is where DistUtils will be run from. This lets Keryx still run, and everyone be happy.


Python and OpenGL Game Mode on Dual Monitors Tutorial

Posted December 5, 2010 under Python

One project I have been working on lately has to do with Python and OpenGL rendering on a cluster of computers. The pyopengl project has documentation on things, but it’s not much more than API docs. That may be fine for some, but I’m completely new to OpenGL and need some actual tutorials and example code to get myself started.

The first thing I needed to get working properly was rendering to dual monitors. That should be extremely easy right? Yeah, if you can find examples. Let’s step through this example:

#!/usr/bin/env python
 
import sys
 
from OpenGL.GL import *
from OpenGL.GLUT import *
 
 
def main():
 
    # Initialize OpenGL
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
 
    # Configure the display output
    glutGameModeString("2560x1024:24@60")
    glutEnterGameMode()
 
    # Setup callbacks
    glutKeyboardFunc(keyboard)
    glutDisplayFunc(display)
 
    # Begin!
    glutMainLoop()
 
 
def keyboard(key, x, y):
    if key == 'q':
        sys.exit(0)
 
 
def display():
 
    glClear(GL_COLOR_BUFFER_BIT)
 
    glBegin(GL_LINES)
    glVertex2f(1.0, 1.0)
    glVertex2f(-1.0, -1.0)
    glEnd()
 
    glutSwapBuffers()
 
if __name__ == "__main__":
    main()

Let’s start with the main() function. The first two glutInit calls setup glut. The first initializes glut with any command line arguments that might have been passed. The second sets up glut to render using a double buffer for smoothness and using RGB for colors. Pretty simple.

Next up we have glutGameModeString. You might recognize the format slightly. First you have your resolution, your color bits, and then your monitor refresh rate. Quite straight forward and easy.

Note that GameMode is the only way you can render to dual monitors with glut. Typically you see glutFullScreen but this will only fullscreen on a single monitor. You could code dual monitor support yourself, but GameMode takes care of it for you.

All we do is configure GameMode, and then enter it. Simple as that. There is one serious catch however. Since GameMode takes control of your screen…it captures your inputs too. There is no escape! Your mouse and keyboard get captured and then you’re stuck.

To combat this, we simply add a keyboard function. When the ‘q’ key is pressed, we exit the program. Using this with sys.exit may not be the greatest way to exit, but glut does not provide a method of leaving the MainLoop. You might try throwing an exception if need be.

The display function is pretty simple, and you can find lots of examples on how to draw. I won’t go into any detail with the display function since it is going to look vastly different between applications.

That’s the basics for running GameMode on dual monitors. In the future, I’ll likely cover some more topics regarding PyOpenGL and as always, I’m open to suggestions if you would like me to check out a specific topic. :D


Python binary executables with Pyinstaller!

Posted October 24, 2010 under Keryx, Python

We’ve had a hard time finding a decent Python binary builder. This is of course coupled with our requirement that it needs to build the wxWidgets binaries with it. I’m sure that it’s not a terribly easy task to build such a library that is capable of this. We’ve finally found a pretty decent solution I think.

Pyinstaller has hit version 1.4 and supports quite a few popular libraries out of the box. This turned out to be a godsend after we had fiddled for a long time with py2exe and then had to figure out yet another tool to build a Linux binary. Pyinstaller can build for Windows, Linux and OSX!

The only downside I have seen to using Pyinstaller so far is that we have to consistently build individual releases of Keryx along with each release of Ubuntu. As so many things regarding libraries between each release, it’s incredibly hard to have a binary that works across multiple releases.

That’s about all I can say about Pyinstaller, it works, and it works quite well. You can grab the latest release of Pyinstaller over at www.pyinstaller.org if you are looking to build a Python binary for your application.


Using Gravatar on Your Site!

Posted October 17, 2010 under General, Python

You might have noticed a lot of sites these days are offloading their avatar hosting elsewhere. Where might they be doing this? Probably Gravatar. The site is run by Automattic who runs WordPress.com. Sites can simply use the email address you registered with to fetch an appropriate sized avatar really easily. Take my Github avatar for example: right click on it and open up the image in a new tab. The URL points to Gravatar.com so it’s not actually being hosted on Github at all.

Sounds pretty cool right? Sure is, and it’s extremely easy to get running on your own application. I’m going to be pretty vague about implementations, as you could use this in desktop or mobile applications as well, you’re certainly not limited to using Gravatar with web apps.

The implementation is really quite simple. You generate a link to http://gravatar.com/avatar/WITHYOURMD5HASH?s=48. Your MD5 hash is just the user’s email address hashed using MD5. You can also grab other profile information as well, the “?s=48″ passes in the “s” parameter to Gravatar telling it we want a 48×48 size image. You can use any size from 1px to 512px. For more information on this, check out http://en.gravatar.com/site/implement/images/.

So now that we understand how to grab the proper URL, let’s implement some URL generation in a couple languages. After you’ve got the URL, you can embed it in Django or Rails however you like. If you’re developing a non-web app, you can download the image using the URL instead. Take not here, I’m not testing for invalid inputs, that’s up to you.

Using Python

import hashlib
def gravatar_url_for(email, size=48)
    hash = hashlib.md5(email).hexdigest()
    return "http://gravatar.com/avatar/%s?s=%i" % (hash, size)

Using Ruby

require 'digest/md5'
def gravatar_url_for(email, size=48)
  hash = Digest::MD5.hexdigest(email)
  "http://gravatar.com/avatar/#{hash}?s=#{size}"
end

Simple as that! Now your applications can have nice avatar graphics with hardly any work!


Python IRC Logbot Rewritten

Posted October 10, 2010 under Python

Lately I’ve been extremely busy with work, school, and life in general. It’s hard to find time to just sit down and work on a project for fun. I decide this weekend I would, and after a few comments of my IRC bot having some issues. Sounds like a good place to start.

Surprisingly it’s been several months since I have written anything in Python. I’ve been working primarily in Ruby and Rails lately and have started to become quite a fan. Sitting down trying to look at my messy, repetitive code from when I first wrote this bot was useless. Most of it didn’t make sense, and the logic behind most things was just plain messy. I sat down yesterday and looking at the old code, began to rewrite it entirely from scratch. Deadmau5 blaring in the background, caffeine running through my veins, I got a rewrite out quickly, and with more features than before. Configs are still missing, but everything is working in tip top shape, with a much more elegant but similar approach compared to before.

Above is a screenshot of some example logs. It uses the IRSSI style with custom coloring. Timestamps on the left are linkable, and it also creates indexes for channels and dates logged. It’s a single file, using the python-irclib library and nothing else. Hopefully it’ll be useful to someone as a logbot, or inspiration for improving their coding practices. You can download the bot over at http://github.com/excid3/logbot.


Getting Started With Quickly

Posted June 21, 2010 under Keryx, Python

Recently an open source project called Quickly has sprung up and looks to be an incredible tool for new developers looking to work on open source GUI projects. The description from their website reads as follows:

Quickly helps you create software programs (and other things) quickly. You can select from a set of application templates and use some simple quickly commands to create, edit code and GUI, and publish your software for others to use. Quickly’s templates are easy to write. So if you are a fan of language foo, you can create a foo-project template. Or if you want to help people making plugins for your killer app, you can make a killer-app-plugin template. You can even create a template for managing corporate documents, creating your awesome LaTeX helpers The sky is the limit!

Sounds like a cool project right? You bet. I’m going to be using Quickly to begin working on the official unstable branch for Keryx 1.0 and will outline the steps I took to get started here.

Installing Quickly

Super simple. Just fire up a terminal and “sudo apt-get install quickly” or install the quickly package via Synaptic or your favorite package management tool (maybe even through Keryx!). Once that is installed, you can fire up the documentation if you like by typing “quickly tutorial ubuntu-application” and it will open up the Quickly User Guide which is a handy reference.

Creating a Project

Nothing more than a “quickly create ubuntu-application keryx”. Just tells quickly you are building a new application with the Ubuntu template named Keryx. Simple as that. It sets up the directory structure for you and everything! So handy!

Getting to Work

Modifying the GUI is simple, just do “quickly design” and Glade is opened up with your ui file ready to go. The same goes for editing the code with a simple “quickly edit” which opens up all the files necessary for your application in your standard text editor.

Committing

“quickly save notes” is all you need to do to make a commit of your changes with the comment “notes” and submit this to launchpad using the bzr version control system.

There are a few other commands you might be interested in, mainly for packaging and releasing but this is a very simple tutorial on how we setup Keryx 1.0. If you’d like to contribute, just download the unstable code branch from launchpad and start using it with Quickly!

So I suppose I will leave it at that for now. Quickly is a way for new developers and contributors to easily be able to jump in and start working on projects. We will be using this for Keryx 1.0 and hope this will encourage contributions and make bugs easier to fix.


PyGTK and Threads Part 2

Posted June 14, 2010 under Python

If you’re just coming to this post, you may want to check out my introduction to threads in my previous post PyGTK and Threads Part 1.

Now that we have a better understanding of threads, let’s start learning with an example. You will need Python and PyGTK installed in order to run these.

The following code is the framework for the application we will be using. It’s very simple and consists of a window and a button. This code prints “Hello” into the terminal when the button is clicked so immediately after you click the button you can click it again and it works. This is because printing “Hello” is a very quick process.

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# By Chris Oliver
# Adapted from http://www.pygtk.org/pygtk2tutorial/examples/helloworld.py
 
import pygtk
pygtk.require("2.0")
 
import gtk
 
class HelloWorld:
    def __init__(self):
        """
            Initializes the GTK application, in our case, create the window
            and other widgets
        """
 
        # Create a window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_border_width(10)
 
        # Setup the application to exit GTK when the window is closed
        self.window.connect("destroy", self.destroy)
 
        # Create a button
        self.button = gtk.Button("Hello World")
 
        # Make the button call self.hello() when it is clicked
        self.button.connect("clicked", self.hello)
 
        # Add the button into the window
        self.window.add(self.button)
 
        # Display the button and the window
        self.button.show()
        self.window.show()
 
    def hello(self, widget, data=None):
        print "Hello"
 
    def main(self):
        """
            This function starts GTK drawing the GUI and responding to events
            like button clicks
        """
 
        gtk.main()
 
    def destroy(self, widget, data=None):
        """
            This function exits the application when the window is closed.
            Without this the GTK main thread would continue running while no
            interface would be displayed. We want the application to exit when
            the window is closed, so we tell the GTK loop to stop so we can
            quit.
        """
 
        gtk.main_quit()
 
if __name__ == "__main__":
    # Create an instance of our GTK application
    app = HelloWorld()
    app.main()

Go ahead and try it for yourself. You should get an application similar looking to this:
Clicking the button prints “Hello” in the terminal and the interface is still usable.

Now what if we wanted to do something more? Maybe we wanted to…wait 20 seconds and THEN print “Hello”? Now that might cause some problems. Well let’s try it first. Just change the code to look like the following:

    def hello(self, widget, data=None):
        import time
        time.sleep(20)
        print "Hello"

Now execute it. What happens? When you click the button, it goes down like it was pressed, but does not come backup. The GUI stopped drawing as soon as the button was clicked. Resizing the window was no longer smooth, and dragging around the window erased the button and make it look something like this:

Well, GTK was waiting to take control of your application again. You were busy forcing GTK to wait by sitting around fiddling your thumbs doing nothing for 20 seconds and then printing some text to the screen. Since you don’t take care of the drawing, you forced GTK to wait and made your application become unresponsive for a while. Users might think that your application has crashed, or at the very least they think its poorly written. And well…if you’re not using threads for long running tasks like this…then yes. It is poorly written.

So how do we do some simple threading? Doing two tasks at the same time sounds crazy complicated! Well, in reality, it certainly can be. However, as you begin to get the hang of things, it’s really not so bad.

First off, you need to initialize GTK Threads. I won’t go into detail on why this is necessary other than it’s required to setup GTK to use threads. You simply need to call “gtk.gdk.threads_init()” before the gtk.main() loop is called, so that’s pretty easy. There is a lot of things regarding threads that you need to be aware of, however this is just an introduction so I’m only going to mention what’s necessary. First off, when you have threads, they have access to all the variables in the program. This makes things easier, but unsafe. Think about this scenario: Two separate threads are running, but wish to modify the same variable, what happens? It’s impossible to tell. For this reason, you want to make sure that you NEVER update GTK from a secondary thread. This means you cannot make changes to ANYTHING related to the GUI in those separate threads.

Wait a minute, well then how am I going to do something like a progress bar? Won’t I have to update the GUI from that thread? Yes you will, but GTK provides you tools to do this in a safe manner. The gobject.idle_add() function does this for you. Woah woah woah, now what is gobject? Well, its the underlying architecture of GTK. The gtk.idle_add function has been deprecated in favor of using the direct gobject call. It’s best if you work with this directly but you don’t really need to know much about it for now other than it adds something to GTK’s todo list when it gets the chance. This lets you tell GTK to do things from external threads. GTK will be sitting around waiting to do something while your thread is hard at work. The thread can then tell it to update the progress bar occasionally and GTK will gladly do so safely.

So how do we apply these concepts to our existing application? Pretty simple as follows:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# By Chris Oliver 
# Adapted from http://www.pygtk.org/pygtk2tutorial/examples/helloworld.py
 
import pygtk
pygtk.require("2.0")
 
import gobject
import gtk
gtk.gdk.threads_init()
 
import threading
 
class HelloWorld:
    def __init__(self):
        """
            Initializes the GTK application, in our case, create the window
            and other widgets
        """
 
        # Create a window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_border_width(10)
 
        # Setup the application to exit GTK when the window is closed
        self.window.connect("destroy", self.destroy)
 
        # Create a button
        self.button = gtk.Button("Hello World")
 
        # Make the button call self.hello() when it is clicked
        self.button.connect("clicked", self.hello_helper)
 
        # Add the button into the window
        self.window.add(self.button)
 
        # Display the button and the window
        self.button.show()
        self.window.show()
 
    def hello(self, widget, data=None):
        import time
        time.sleep(5)
        print "Hello"
 
    def hello_helper(self, widget, data=None):
        print "starting new thread"
        threading.Thread(target=self.hello, args=(widget, data)).start()
 
    def main(self):
        """
            This function starts GTK drawing the GUI and responding to events
            like button clicks
        """
 
        gtk.main()
 
    def destroy(self, widget, data=None):
        """
            This function exits the application when the window is closed.
            Without this the GTK main thread would continue running while no
            interface would be displayed. We want the application to exit when
            the window is closed, so we tell the GTK loop to stop so we can
            quit.
        """
 
        gtk.main_quit()
 
if __name__ == "__main__":
    # Create an instance of our GTK application
    app = HelloWorld()
    gtk.gdk.threads_enter()
    app.main()
    gtk.gdk.threads_leave()

As you can see, there were only small modifications. In this case we created a new function called hello_helper which executes our old hello function inside of a thread. When you click the button now, the thread is spawned quickly and execution continues back to the GTK loop almost immediately. You can click the button several times and many threads will be created and will execute simultaneously. We initialized GTK Threads at the very beginning at import just to make sure that it wasn’t forgotten, but standard practice is to put it just before gtk.main().

That’s about it. It’s a pretty introductory example, but I think it sufficiently explains the basics of threading. There are many more things to it and so I suggest reading up on it some more. Let me know if you have any questions or comments!


PyGTK and Threads Part 1

Posted June 14, 2010 under Python

Last semester I took a course called Operating Systems. We dove a bit into memory management, scheduling, and resource management. This was great and all, however our professor was a pushover when it came to projects. Sure we learned the concepts, but you can’t fully understand them until implementation, or at least that’s how it works for me. This summer I’ve been working on a couple projects in which threading is imperative. Last summer I attempted some threading well before I understood any of the concepts so I thought I would put together a blog post or two to introduce the subject a little simpler than some of the other posts I had tried to learn the subject on in the past.

To start this off, we must introduce threads. What’s a thread? Well it’s a lightweight process. Well what exactly is a process? It’s an application that is in memory. On a single CPU system, the operating system can only be executing one process at a time. In order for the computer to be interactive, it needs to execute a small portion of one process, jump to another, work for a bit, and go to the next. This happens quick enough that it appears the computer is doing multiple things at once. It’s not. Each time a process is swapped out of memory, the process needs to save its state, this means all the variables and everything else regarding the process so that it can resume exactly where it left off. Each process also is completely separate of each other. They do not share memory or anything.

Okay, so what is a thread exactly? Well a thread is a lightweight process. It shares the same memory space of the parent process so it also has access to the other threads’ variables. This isn’t possible with separate processes and also makes switching between threads significantly faster than switching between processes. In general, it is easier and simpler to write multi-threaded applications over multi-process ones.

Now that we’ve got an understanding of threads and processes, why do we need them? Have you ever used an application that freezes up while it’s doing something? This is when the developer should have launched a new thread to do the intense calculation. When you want to do two things at the same time, threads are important. This is especially important in graphical (GUI) applications. Why? Well you want the GUI to continue responding while you do your work don’t you? You don’t want it to freeze up do you? The user might think it’s a crappy application or something. Ever have an application that freezes up and you drag it around to see if its still working and the interface becomes blank? That’s because the GUI is not being updated. The thread for drawing the GUI is busy doing some other stupid calculation and leaving the user to freak out. Certainly not what you want right? Didn’t think so. In fact, if you don’t use threads, you couldn’t even use a progress bar to tell the user something was happening in the background.

What we want to do is apply these methods to GTK specifically. This idea works for just about any GUI toolkit including Qt, and wxWidgets, but we are going to keep this in terms of GTK for our purposes. So first off, you need to understand how a GTK application works. GTK provides a loop, that continually redraws the windows and accepts inputs. By default, it does nothing. It’s not until you, the developer, come along and give GTK some things to do. Maybe it’s drawing a window and a button. GTK knows what to do when you tell it what you want. It knows that you want it drawn so it does that for you. The mundane details don’t need to be worried about, you get to do the creative part and use this framework. Maybe you also want that button to do something when it’s clicked. GTK also gets notified of this. You tell it, hey, when someone clicks this button, can you execute this code for me? Thanks. It does this, but here’s the problem. GTK needs to be in it’s loop constantly waiting for input or changes from the user. It is “event driven”. This means that every time something happens, it needs to be ready and waiting to decide what to do. If you take too long in your custom code, GTK may not be able to get back in time to receive those inputs. If that happens, your application appears unresponsive and thats no good. I’ve made a simple mockup of GTK and how a developer’s code simply extends GTK in order to do what the developer wants.

Head on over to Part 2 to start getting your hands dirty.


Resume downloads with PycURL RESUME_FROM

Posted June 12, 2010 under Keryx, Python

If you’ve ever played with urllib, you might have noticed its reasonably slow download speeds and wanted something a little bit more. That’s where cURL comes into play. It’s a very powerful library and is used in applications such as Gwibber. Seeing that this ships with Ubuntu, it is a good fit for Keryx’s new backend library Unwrapt.

I spent yesterday working on this new downloading code and wanted to share my code as I found the documentation (in reality, the entire library too) is primarily in C style and confusing examples. This is an easy to use class that helps to take care of some of the mundane details like following redirects (which is important! if you don’t enable this you won’t be able to reach a lot of files). I plan on extending this to support proxies soon as well as a GTK progress function.

#    Unwrapt - cross-platform package system emulator
#    Copyright (C) 2010 Chris Oliver <chris@excid3.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
import math
import pycurl
import os
import time
import sys
 
 
class EasyCurl:
 
 
    def __init__(self, proxy=None):
 
        self.pco = pycurl.Curl()
        self.pco.setopt(pycurl.FOLLOWLOCATION, 1)
        self.pco.setopt(pycurl.MAXREDIRS,      5)
        #self.pco.setopt(pycurl.TIMEOUT,       5*3600)
        self.pco.setopt(pycurl.CONNECTTIMEOUT, 30)
        self.pco.setopt(pycurl.AUTOREFERER,    1)
 
        #TODO: Proxy!
 
 
    def perform(self, url, filename=None, resume=True, progress=None):
 
        # Generate filename if not given
        if not filename:
            filename = url.strip("/").split("/")[-1].strip()        
        self.filename = filename
 
        # Get resume information 
        self.existing = self.start_existing = 0
        if resume and os.path.exists(filename):
            self.existing = self.start_existing = os.path.getsize(filename)
            self.pco.setopt(pycurl.RESUME_FROM, self.existing)
 
        # Configure progress hook            
        if progress:
            self.pco.setopt(pycurl.NOPROGRESS,       0)
            self.pco.setopt(pycurl.PROGRESSFUNCTION, progress)
 
        # Configure url and destination
        self.pco.setopt(pycurl.URL, url)
        self.pco.setopt(pycurl.WRITEDATA, open(filename, "ab"))
 
        # Start
        self.pco.perform()
 
        sys.stdout.write("n")
 
 
    def textprogress(self, download_t, download_d, upload_t, upload_d):            
 
        downloaded = download_d + self.existing
        total      = download_t + self.start_existing
        try:    frac = float(downloaded)/float(total)
        except: frac = 0
 
        bar = "=" * int(25*frac)
 
        sys.stdout.write("r%-25.25s %3i%% |%-25.25s| %5sB of %5sB" % 
            (self.filename,
             frac*100,
             bar,
             format_number(downloaded),
             format_number(total)))
 
 
# Borrowed from the urlgrabber source
def format_number(number, SI=0, space=' '):
    """Turn numbers into human-readable metric-like numbers"""
    symbols = ['',  # (none)
               'k', # kilo
               'M', # mega
               'G', # giga
               'T', # tera
               'P', # peta
               'E', # exa
               'Z', # zetta
               'Y'] # yotta
 
    if SI: step = 1000.0
    else: step = 1024.0
 
    thresh = 999
    depth = 0
    max_depth = len(symbols) - 1
 
    # we want numbers between 0 and thresh, but don't exceed the length
    # of our list.  In that event, the formatting will be screwed up,
    # but it'll still show the right number.
    while number > thresh and depth < max_depth:
        depth  = depth + 1
        number = number / step
 
    if type(number) == type(1) or type(number) == type(1L):
        # it's an int or a long, which means it didn't get divided,
        # which means it's already short enough
        format = '%i%s%s'
    elif number < 9.95:
        # must use 9.95 for proper sizing.  For example, 9.99 will be
        # rounded to 10.0 with the .1f format string (which is too long)
        format = '%.1f%s%s'
    else:
        format = '%.0f%s%s'
 
    return(format % (float(number or 0), space, symbols[depth]))
 
 
if __name__ == "__main__":
    url = "http://launchpad.net/keryx/stable/0.92/+download/keryx_0.92.4.zip"
    ec = EasyCurl()
    ec.perform(url, progress=ec.textprogress)

« Older |