MacPyBeg
Admin
Introduction
This document is mainly a compilation of information from the Usenet NewsGroup comp.lang.python
For any remark or info… fgranger@mail.dotcom.fr
I try to keep the name of the original poster at end of each answer. I thank them here for the help they provide on the NewsGroup.
If you don't want your name appearing in this document, just let me know.
New
In the RTF version of this document, they appear with a vertical bar in the left margin. You can download the RTF version </MacPyBeg.rtf>
History
Latest version : 1.84
Diplaying binary numbers
Sorting
Version 1.70
-	Added a script to change type & creator of Mac files
-	Added link to Mr Strout patches for IDE.
Version 1.65
-	added Bitwise operation
-	added some error catching
-	added some file handling
-	added dictionnary
-	added some string
-	added some TKinter
Version 1.20 : first public version
Looking for items
Feel free to contribute some tricks, code snippets…. 

Starting
MacPython links
<http://www.cwi.nl/~jack/macpython.html>
<ftp://ftp.cwi.nl/pub/jack/python/mac/>
This is an editor for Python code with advanced features like double-click selection of groups, command-click copying of chunks of text, function look-up, etc.
For more info, see:  <http://www.strout.net/python/mac/>

Q : What is Python and where can I find out more about it?
R :
See <http://www.python.org/>
Python is an object-oriented scripting language. If you know anything about Perl, Python can do roughly anything that Perl can do. Many people think that Python is easier to read than Perl, and therefore better suited for large programming projects.
- Aahz Maruch
R :
There are other arguments for Python's comparative superiority for large programming projects:
1. Python has a better OO model, and OO is a benefit in big work.
2. A slight variation on what you wrote: Perl's syntax is undeniably complex, and this adversely affects teamwork. It seems unavoidable that some of the Perl code in a large team project will be opaque to some of the team members. Python, in contrast, has a sufficiently coherent syntax that experienced coders can apprehend anything they run across, even if they didn't write it themselves.
3. Python's module system is allegedly cleaner.
4. Big projects inevitably pick up mixed-language objects. Only magicians interface Perl to foreign languages (like C). Python, in contrast, is easy to extend.

I summarize: Perl is a fine language for implementing many large-scale projects. Aahz correctly reported that Python is generally regarded as superior to Perl in this regard, and gave one example of its advantage. That's not Python's only advantage, though.
- Cameron Laird
And a lot of people say that we treat people nicer here compared to the perl newsgroups. ;)
- Grant Edwards
Sources of information
<http://python.faqts.com>
Code snippets
<http://members.tripod.com/~edcjones/pycode.html>
<http://www.faqts.com/knowledge-base/index.phtml/fid/199/>
Debugger
Q : How do I activate the python debugger on pythonmac ide?
R : To right of the windows, small black arrow to the right, click there is a menu.

Array
Initialisation
See <http://members.tripod.com/~edcjones/utility04.html>
Bitwise operation
Summary
Python has a full set of bitwise operators, which can be applied (only) to integers and long integers:
x << y	# left shift x by y bits
x >> y	# right shift
x & y	# bitwise and
x | y	# bitwise or
x ^ y	# bitwise xor
~x	# bitwise negation
- Greg Jorgensen
how can I catch the bit that falls off a rshift ?
Catch it before you do the shift:
bit_that_fell_off = number_to_shift & 1
number_to_shift = number_to_shift >> 1
- Rainer Deyke
invert number ?
To get the full (32 bit or more) inverse, use the ~ operator. To get the byte size inverse, use ^ 0xff.
full_inverse = ~byte_to_invert
byte_size_inverse = byte_to_invert ^ 0xff
Rainer Deyke
Utility for diplaying binaries
I got inspired by an algorithm by Tim Peters.
l = [1, 2, 3, 4, 8, 16, 32, 64, 128, 255, 512, 1024, 2048, 4096, 8192, 16384, 32768]

def toBin(x):
	a = ''
	y = x
	while x:
		x, y = x / 2, y % 2
		if y:
			a = '1' + a
		else:
			a = '0' + a
		y = x
	return
for x in l:
	print toBin(x)

Error catching
You need to know the type of an object
You can test the type of an object (e.g., a list entry) with the type operator (to use symbolic names for builtin types, you may also import module types, but it's often just as clear to test against, e.g., type(0) for integers, type(0L) for long integers, type([]) for lists, and so on). Thus, one way to do it could be:

def printType(t):
	if t==type([]): return "list"
	elif t==type(''): return "string"
	elif t==type(0): return "integer"
	elif t==type(()): return "tuple"
	else: return "something else"

for i in range(len(list)):
	print "element %d has type %s" % (i, printType(type(list[i])))

However, this kind of approach has some huge flaws – the number of built-in types (plus types that could be added by the user importing C-written modules...) is enormous, and all the _classes_ get lumped into one 'type', which you may not want to keep as one lump...
- Alex Martelli

try:
	try:
		print "foo"
	except:
		print "errors"
finally
	print "clean up"
I do agree that an optionally finally-clause after 1 or more except clauses (and possibly an else clause too) on a try would make for a nice alternative to this rather cumbersome nested syntax where except (& maybe else) and finally are mutually exclusive.
"Why you'd want to do this" seems rather obvious -- catch errors AND also guarantee some finalization.
- Alex Martelli
Error messages
Understanding them
Message:	Syntax error (at an apparently correct place)
Translation	1 The previous line has no colon (:) at the end
		2 You wrote = and it should be ==

Message	Name error <x> 
Translation	1 An import line is missing.
		2 There is a spelling error - the capitalization differs?
		3 You forgot to write 'self.' when accessing instance variables from within a method.

- Ian Hobson
Files
A script to change creator and type of Mac files
# python
"""
Change type & creator of the dropped files.
Prompt ask for the new values for each file.
If no new value, keep the old.

ToDO:
- if dble-click prompt for file
- if d&d do the same for all files.

001118	0.0.1	first version
"""

import sys, macfs
from mac import xstat

def ask_ok(prompt):
	ok = raw_input(prompt)
	return ok

files = sys.argv[1:]	# all but first wich is script file name
for file in files:
	creator = xstat(file)[-2]
	type = xstat(file)[-1]
	print file, creator, type
	c = ask_ok('Creator : ')
	t = ask_ok('Type : ')
	if c:
		creator = c
	if t:
		type = t
	fs = macfs.FSSpec(file) 
	fs.SetCreatorType(creator, type)

Changing a text file in place
Q : There obviously is a way to change a small piece of a string on a line and then write it back out to a file but I am having difficulty, new to python.
R :
It seems to me that what you're trying to do is something like:
lines = open("somefile.txt").readlines()
for i in range(len(lines)):
	if re.search(myre, lines[i]):
		lines[i] = lines[i].replace('foo','bar')
open("otherfile.txt","w").writelines(lines)

or, in old Python 1.5.2 or earlier, basically the same thing but with the 4th line changed to
		lines[i] = string.replace(lines[i],'foo','bar')

(this also works in Python 2.0, for compatibility).

I suspect that the loop you're using instead is more like the following:

for line in lines:
	if re.search(myre, line):
		line = string.replace(line,'foo','bar')

i.e., you're assigning the result of the replacement to the loop-variable, leaving the lines list intact and unchanged.  This seems the likeliest explanation.
Note that, if you want to do the replacement "in place" (write out to the same file you've read), the fileinput module is very handy for that purpose:

for line in fileinput.input("thefile.txt", inplace=1):
	if re.search(myre, line):
		line = string.replace(line,'foo','bar')
	print line

the 'print line' is redirected by fileinput to the filename being read (the original file having been temporarily 'shunted aside' under a fake name – it will be removed at the end of the loop, unless the further argument backup='.bak' [or whatever] is also passed to the .input function).  This saves having to keep the whole file in memory at once, which is handy for huge files, but is no help if you want to write the results to a *different* file (in that case, your approach based on readlines/modify the list/writelines is just fine for most files -- for truly huge files, you'll no doubt want to operate line by line rather than all-at-once, though -- but, you do have to make your modifications to *the list*, with some statement such as 'lines[i] = ...whatever...").
-	Alex Martelli
-	
You see, the last line should actually read:
	print line,    #note the trailing ","

the lines you get from "fileinput.input('thefile.txt', inplace=1)" have a trailing end-of-line symbol embedded if it was there in the input file. The print statement adds a trailing end-of-line symbol regardless of whether the string to write ended with one, *unless* you end the print statement with a comma, then this adding is omitted.

- Carel Fellinger
You want to get all lines from a file and do something to each
Option 1
while 1:
	line = fp.readline()
	if not line:
		break
	do something with 'line'
-	Steve Lamb
Option 2
line = fp.readline()
while line:
	 # do something with line
	 line = fp.readline()
- Alan Gauld
Option 3
while 1:

	line = fetchline()
	if end of data:
		break

	if empty:
		continue
	if comment:
		continue

	process line
- Fredrik Lundh

You want only the non empty lines of a file
A = []
for Line in H.readlines():
	if len(Line) > 1: # Don't keep the empty lines containing only '\n')
	A.append(Line)
Doing a copy of a file without blank lines
out = open(r'C:\bar.txt', 'w')
for line in open(r'c:\foo.txt').readlines():
	if len(line)>1:
		out.write(line)
out.close()

Handling files at startup
Import sys macfs
if len(sys.argv) > 1:
	# -- Drag and Drop
	filelist = sys.argv[1:]
else:
	# -- double click on applet, no args
	# Macfs
	temp, res = macfs.StandardGetFile()
	if res:
		toto = temp.as_pathname()
		filelist.append(toto)
	else: # user canceled
		sys.exit()
You don't know from which platform the file come…
# -- line ending
f = open(file, 'rb')
temp = f.read()
f.seek(0)
crNum = string.count(temp, '\r')
lfNum = string.count(temp, '\n')
if lfNum == crNum:	# dos
	f.close()
	os.rename(file, join(doneFolder, split(file)[1][:27]) + '.dos')
	temp = string.replace(temp, '\n\r', '\n')
	f = open(file, 'w')
	f.write(temp)
	f.close()
	f = open(file)
elif crNum:
	f.close()
	os.rename(file, join(doneFolder, split(file)[1][:27]) + '.uix')
	temp = string.replace(temp, '\r', '\n')
	f = open(file, 'w')
	f.write(temp)
	f.close()
	f = open(file)
Walking a directory tree
Use some Python 2 things, be careful
def crawl(root):
	from os.path import join, exists, isdir
	from os import listdir, chdir
	dirs = [root]
	while dirs:
		dir = dirs.pop()
		# This seems a little-known trick in Python: changing to the
		# current directory allows the subsequent isdir-in-a-loop to
		# work on just the file name, instead of making the OS reparse
		# the whole darn path from the root again each time.
		# Depending on the OS, can save gobs of time.
		chdir(dir)
		for f in filter(isdir, listdir(dir)):
			path = join(dir, f)
			if f == "CVS":
				entries = join(path, "Entries")
				if exists(entries):
					process(entries)
				else:
					raise TypeError("CVS dir w/o Entries!! " + path)
			else:
				dirs.append(path)
It does assume the argument given to it is a full path from the root. So change this line:
	dirs = [root]
to
	dirs = [os.path.abspath(root)]
and fiddle the imports however you like to make that pretty. In my 2.0 code, I use
import os.path.abspath as grant
    ...
	dirs = [grant(root)]
Whatever, it's a one-time cost at function startup.
For a *really* funky time, replace
	while dirs:
		dir = dirs.pop()
with
	for dir in dirs:
and you get a dirt-simple breadth-first search. This relies on (a) that "dirs" grows *inside* the loop; and (b) that Python's iteration protocol doesn't "know" the loop length in advance. The "for" keeps marching over the ever-growing "dirs" until dirs stops growing and the hidden iteration index oversteps its end. Sometimes-nice bonus: at the end, dirs contains a list of all the directories you've visited!
- Tim Peters
Dictionnary
Safe access
Simple method
Which means that safely accessing entries requires something like:
if a.has_key(k):
	do something with a[k]
else:
	do whatever is needed if there is no a[k]
- Christopher Browne
Simpler methode
dosomething(a.get(k,some_default))
is often handier. - Alex Martelli
Looping throught
Loops on dictionaries are often better expressed otherwise,
if you don't care about the order the keys-and-values are emitted:
for key, value in os.environ.items():
    print "%s = %s" % (key, value)
If you DO care about the order, then looping through the keys is going to be a problem, since the order is quite random-appearing.  The common idiom then becomes:
keys = os.environ.keys()
keys.sort()
for key in keys:
    print "%s = %s" % (key, os.environ[key])
- Alex Martelli
List handling
You need a list of the unique elements of the list
def unique(thelist):
	uniquedict = {}
	for i in thelist:
		uniquedict[i] = 0
	return uniquedict.keys()
Sorting
Q : I'm trying to sort a List of List, on the third col, but I don't find how to do this.
You can specify an alternative comparison function to the sort method. The easiest way to do this is with a lambda...
l.sort (lambda a, b : a[2] < b[2])

Faster for sorting a large list is a "Schwartzian Transform" approach:
auxlist = [ (x[2], x) for x in thelist ]
    auxlist.sort()
    thelist = [ x[1] for x in auxlist ]
This does roughly the same thing as:
thelist.sort(lambda a, b: cmp(a[2], b[2]))
but more rapidly.
"Roughly", because: the Schwartzian Transform approach is not in-place, which may matter if you're very tight in memory or there are other outstanding references to thelist; also, if two items have identical x[2] sub-items, the [random-ish] ways in which the two approaches sort them will probably differ).

Example:
perform a stable sort, case-independent, over the first 20 characters of each line of the 838-lines README.txt file that comes with Python 2.0.
Naive approach...:
def compare(a,b):
keya = a[:20].lower()
keyb = b[:20].lower()
return cmp(keya, keyb)

import time

ldata = open("D:/Python20/README.txt").readlines()

lines = ldata[:]

start1 = time.clock()
lines.sort(compare)
stend1 = time.clock()

# oops, this ISN'T stable, is it...?  Pretty
# hard to make Python's sort stable _without_
# a Transform... "here I come to save the day":

tlins = ldata[:]
start2 = time.clock()
aulin = [ ( x[:20].lower(), i)
for x, i in zip(tlins, range(len(tlins))) ]
aulin.sort()
tlins = [ x[-1] for x in aulin]
stend2 = time.clock()

print stend1-start1
print stend2-start2

D:\PySym>python tesor.py
0.220513718779
0.0353952707969
Note how, even on this small dataset, the Transform version is over 6 times faster (AND satisfies the specs about the sort being stable, a nice plus:-).
An idiom WELL worth keeping in mind...

- Alex Martelli
Regular expression
'Some people, when confronted with a problem, think, "I know, I'll use regular expressions". Now they have two problems.' --Jamie Zawinski, comp.lang.emacs
- Aahz Maruch
Strings
Prefix matching
(it may not be obvious, but "not string.find(str, prefix)" is only true if str *starts* with prefix)
- Joshua Muskovitz and Fredrik Lundh
Time
Readable current time
import time
eTime = time.ctime(time.time())
Gives a default presentation.

User interaction
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):

	while 1:
		ok = raw_input(prompt)
		if ok in ('y', 'ye', 'yes'): return 1
		if ok in ('n', 'no', 'nop', 'nope'): return 0
		retries = retries - 1
		if retries < 0: raise IOError, 'refusenik user'
		print complaint

ask_ok('toto')
Tkinter
Q : I'm looking for some hints as to how to get Tkinter on Macs to work.
You will find everything you need here: <http://DerSchockwellenreiter.editthispage.com/python/tkinterenglish.html>
Jörg Kantel

Yeah, that's basically it. Here's what worked for me using 1.5.2:
1. Download MacTk 8.3.1, unstuff.
2. "Tool Command Language (PPC)" into the "Tool Command Language" folder, and copy that into the Extensions folder.
3. Add the path to the Tkinter.py module, $Python:Lib:lib-tk
4. Put the MacTk 8.3.1 folder in the Python folder
- Louis M. Pecora

Editors
Enhancements to the IDE
<a href="http://www.strout.net/python/mac/">http://www.strout.net/python/mac/</a> work for Python 2.0, tested
Alpha
Bbedit
<http://www.barebones.com/>
Pepper
<http://www.hekkelman.com/pepper.html>
This is the one I currently use. It is great
Advanced
Create constants
If you're willing to use something like const.foo, it's not hard to ensure its value won't be changeable:

-- in const.py:

class Const:
    foo = 'Foobar!'
    bar = 23
    pi = 3.1415926
    baz = "Whacka-doo"
    def __setattr__(self,name,value):
        if Const.__dict__.has_key(name):
            raise AttributeError, "cannot change const.%s" % name
        Const.__dict__[name] = value

const = Const()

-- anywhere else:

from const import const

# may now use const.foo, const.bar, ..., and also add new
# const.whatever values by assignment, but NOT change
# any existing one
- Alex Martelli