(Python) PyProgram #7 - Time Object Diffs

December 14, 2007

I had two time objects and needed to find the difference of time between them. Upon Googling, querying in #python and checking the documentation, found that there is not built-in function available for this. Though there were some cookbook recipes, I wanted to write my own solution to find the difference between two datetime.time objects. Though datetime.timedelta was for time difference requirements it only has a days, seconds and microseconds property while I need hour, minute and seconds. Thus, I wrote my timediff method which accepts two time objects btime and stime where btime is a bigger time than stime (b for big and s for small ;) ), and it returns a datetime.time object for the difference time. (Why I need to have time objects? I use storm ORM ;) )

def timediff(btime, stime):

    """Difference between two datetime.time objects

    Accepts two datetime.time objects where btime > stime

    Returns a datetime.time object of the difference in time of

    the two datetime objects.

    """

    btdelta = timedelta(hours=btime.hour, minutes=btime.minute, seconds=btime.second)

    stdelta = timedelta(hours=stime.hour, minutes=stime.minute, seconds=stime.second)

    tdiff = btdelta - stdelta

    tdiffsec = tdiff.seconds

    if tdiffsec < 60 and tdiffsec > 0:

        return time(0, 0, int(tdiffsec))

    elif tdiffsec < 3600 and tdiffsec > 0:

        tdiffsplit = str(tdiffsec/60.0).split('.')

        tdiffmin = int(tdiffsplit[0])

        tdiffsec = float("0."+tdiffsplit[1])*60

        return time(0, int(tdiffmin), int(tdiffsec))

    elif tdiffsec > 0:

        tdiffhourmin = str(tdiffsec/3600.0).split('.')

        tdiffhour = int(tdiffhourmin[0])

        tdiffminsec = str(float("0."+tdiffhourmin[1])*60).split('.')

        tdiffmin = int(tdiffminsec[0])

        tdiffsec = float("0."+tdiffminsec[1])*60

        return time(tdiffhour, tdiffmin, int(tdiffsec))

    else:

        return time(0, 0, 0)

[Bash] Quick file renaming

November 28, 2007

I had a bunch of .deb packages from the apt archive. But the problem was that in the names of the packages, there was a “%3″ in the places where there should be a “_”. We found that the symbol was corresponding to the underscore character and it was somehow getting replaced in the name. Hence we needed to rename all filenames, changing the ‘%3′ to ‘_’ and I was requested to write a quick script. As my usual bash buddy was busy, I resolved to some Google’ing and found this.

I first started by trying to echo the file name, then replace the ‘%3′ with ‘_’, print it, and finally rename the file.

for FILE in `ls`; do echo $FILE; done;
for FILE in `ls`; do NEWFILE=`echo $FILE | sed 's/%3a/_3a/g'`; echo $NEWFILE; done;
for FILE in `ls`; do NEWFILE=`echo $FILE | sed 's/%3a/_3a/g'`; mv "$FILE"  $NEWFILE ; done;

But when I execute my final script, it informs that the oldfile and newfile to the `mv` command are the same. Think the `sed` command itself renamed the file. Anyways, it did the job I wanted it to :)

Updates from floyd_n_milan:

for file in *; do mv -v “$i” “${i/\%3a/_3a/}”; done;


(Python) Function calls for beginners

September 28, 2007

A function `call` is a callable object with a series of arguments which can be empty.

syntax : primary(arg_list['',''])
arg_list ::= positional_args [, keyword_ags][, *expression][, ** expression] |
keyword_args[, *expression][, ** expression] |
*expression [, **expression] |
** expression

positional_args ::= expression (, expression*)
keywords_args ::= keyword_item (, keyword_item*)
keyword_item ::= identifier = expression

`primary` is the callable object (function name, built-in callable method objs, class objs, methods of class instances and callable class instances)

How arguments are processed ?

1. a list of unfilled slots for formal parameters is created
2. N positional_args are placed in first N slots
3. keyword_args are placed in corrsponding slot using identifier

TypeError if a slot is already filled

4. Unfilled slots are filled with corresponding default values

TypeError if slots exist with no default values
TypeError if positional_args > formal param slots
TypeError if ketyword_args doesn’t match with formal param name unless **identifier is present

5. *expression evaluates to sequence like y1…yM and are placed as additional positional_args after x1…xN such that the call is made with x1….xN,y1..yM positional arguments
6. **expression is a dictionary contained excess keyword arguments

TypeError if **expression doesn’t evaluate to dictionary

7. *expression is processed before keyword arguments, hence unusual to use both keyword_args and *expression

A call return can be,

  • as per the return statement for a user-defined func
  • up to the interpreter for a build-in func or method
  • a new class instance for a class object
  • the user-defined function for a class instance method with the instance as the first argument
  • as per __call__() method for a class instance

Examples:

# define a function with two params, printing them as result
>>> def f(a,b): print a, b
...

# both are keyword_args
>>> f(a=1,b=2)
1 2

# both are positional_args
>>> f(1,2)
1 2

# a is keyword and b is considered positional. ERROR!
>>> f(a=1,2)
File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg

# a is positional and b is keyword_arg
>>> f(1,b=2)
1 2

# 1 is positional while 2 is *expr and takes as additional positional_arg for b
>>> f(1,*(2,))
1 2

# 1 is keyword for a, but 2 is also positional_arg for a. ERROR!
>>> f(a=1,*(2,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for keyword argument 'a'

# 1 is a keyword_arg for b, while 2 is positional_arg for a
>>> f(b=1,*(2,))
2 1

# 1 is keyword_arg for a, while 2 is specified thru **expr for b
>>> f(a=1,**{'b':2,})
1 2

# 1 is positional_arg using *expr for a, b is keyword_arg thru **expr for b
>>> f(*(1,),**{'b':2,})
1 2

# both a and b are specified as keyword_args thru **expression
>>> f(**{'a':1,'b':2,})
1 2

Original Reference (Python Reference Documentation)


Who Uses Python ?

June 5, 2007

I happen to see an interesting post in the Python mailing list. The question was to know who uses Python, other than sys-admins and web developers. And here is a consolidation of the replies till now.

  • For login scripts, locking down PCs, automating backups, GIS, and more.
  • Electrical engineering. It’s pretty handy for writing programs to talk to embedded systems using various protocols/interface (async-serial, ethernet, etc.). It’s also good for analyzing communications, analyzing performance tests for analog instruments, and so on.
  • The Technical Artists are using Python more and more for development needs.
  • For digital art (music visualization). It’s excellent for data analysis (I’ve done everything from stats on lines in an SQL database to mining flat text files of data for statistical projections of MLB baseball player performance). I know a couple of
    people who sell a double-entry accounting system written in Python, which is presumably “finance”. Web analytics is very common (I’ve seen several such projects)
  • we use Python for controlling fully automated logistics solutions (conveyors and stacker cranes), for generating PLC code etc.

There is also a very long reply on how Python has been used for Railway Computer-control Simulation which lists the use of Python 2.5 as,

  • The main database where all states are kept (signal aspects, given and actual turnout states, track occupation …). It’s a simple binary protocol using TCP; the server maintains string:int pairs in a Python dict. It’s made with Twisted and running under twistd.
  • The automatic drive controlling software of the model trains. It listens for changes of signals and track occupation and controls the model trains using a commercial digital model railroad controller attached to /dev/ttyS0. Also using Twisted/twistd, and
    pyserial.
  • Various helper scripts and little servers for small functional units (tramway reverser, level crossings)

This is the latest one I read,

  • To manage the database application, data integration, and reporting
  • to shuffle and sort important files around, to convert reports into formats necessary for various import and export functions, to reconcile data between database systems, and to maintain little text databases for various business processes.

Some other common replies includes text search, data mining and biotechnology. Nice to hear about some real time uses of Python, different from the usual administration and web stuffs. :)


PyProgram #5 - SMTP Mail Sender

May 3, 2007

Had some requirement to send mails in my project, so we were digging into the smtplib module. Thought of trying out a small script which will send mails. The example code was fine, but modified the functions and added “Subject: ” header to it. Tested it and voila it sent mails :P A rather trivial piece of code, but posting it to save it somewhere in my web space ;)

#!/usr/bin/env python

"""Command-line SMTP Mail Sender
This script can send mails provided you have an account
with an SMTP server.

The inputs have to be given at the
command line. The recepients address can be a space
separated list of emails.

The subject can be multiple lines, seperated by empty lines.
When done, end the subject with Ctrl+D to send the mail.
"""

import smtplib

# function to fetch the input from command line
def prompt(prompt):
return raw_input(prompt).strip()

fromaddr = prompt("From: ")
toaddrs = prompt("To: ").split()
subject = prompt("Subject: ")
print "Enter message, end with ^D (Unix):"

# Add the From: and To: headers at the start
msg = ("From: %snTo: %snSubject: %snn" %(fromaddr, ", ".join(toaddrs), subject))
while 1:
try:
line = raw_input()
except EOFError:
break
if not line:
break
msg = msg + line

print "Message length is " +repr(len(msg))

# connect and send the mail
server = smtplib.SMTP()
# connects to default port, if not specify it as additional argument
server.connect('mail.myhost.com')
# specify username and password
server.login('me.myhost.com','me1234me')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()
# done!

PyProgram #4 - Who is on duty ?

April 3, 2007

We had an interesting problem posed in python mailing list, though it is not a very tough one. But still, practical problems are a fun to solve. This guy wanted to know how he can find which group is on duty. There are 4 group of fire-fighters each cycled on duty for a day one after another. Poor guys, they do not get any off even for holidays. The 4 groups all joined on a single day, so the start day is common for all. Based on this starting day, we have to find which group is on duty if given a particular day. This is a simple and interesting problem to solve. So, we all jumped in and used the datetime module. Here is my code dutycycle.py,

#!/usr/bin/env python
"""Data Cycle Finder
Find which group takes charge on a corresponding date.
There are four groups, each working on a 24-hour cycle
one after another. The groups work one day after another
in order, without any break for holidays.

Given a date, it is needed to find which group is on
duty. All the groups have a start date and a group
number. Hence given a date, the output should be the
group number which is on duty.

The input date is of the form dd/mm/yyyy.
"""

from datetime import date
import sys

def dutyfinder(_date):
    start = date(2007,1,1) # the duty cycle started on 1st jan 2007
    return (date.toordinal(_date)-date.toordinal(start))%4

inputdate = sys.argv[1]
indate = sys.argv[1].split('/')
enddate = date(int(indate[2]),int(indate[1]),int(indate[0]))
onduty = int(dutyfinder(enddate)) + 1
print "The group to report for duty on %s is %s" %(str(inputdate),str(onduty))

Now, we need to call this file with the input date as the argument. The starting date is set to 1st January 2007 and the input date format is dd/mm/yyyy. To find which group should report to duty on 10.01.2007,

$ python datacycle.py 10/01/2007
The group to report for duty on 10/01/2007 is 2

I had mailed the OP the code and he has thanked me for it ;)