MacPython Logo from __future__ import *

2005-03-31

ANN: py2app 0.1.9

Filed under: PyObjC, py2app, python — bob @ 11:00 am

py2app is the bundlebuilder replacement we've all been waiting for. It is implemented as a distutils command, similar to py2exe, that builds Mac OS X applications from Python scripts, extensions, and related data files. It tries very hard to include all dependencies it can find so that your application can be distributed standalone, as Mac OS X applications should be.

py2app 0.1.9 is included in the installer for PyObjC 1.3. If you have installed PyObjC 1.3, then you already have py2app 0.1.9 installed.

Download and related links are here: http://undefined.org/python/#py2app

py2app 0.1.9 is a minor bug fix release:

Bugs fixed:

  • bdist_mpkg now builds zip files that are correctly unzipped by all known tools.
  • The behavior of the bootstrap has changed slightly such that __file__ should now point to your main script, rather than the bootstrap. The main script has also moved to Resources, from Resources/Python, so that __file__ relative resource paths should still work.

MacPython 2.4.1 Installer

Filed under: macosx, python — bob @ 12:20 am

I've put together an "official unofficial" framework build distribution of Python 2.4.1 for Mac OS X 10.3 (and later, but that is not tested yet). It is built using the same tools that Jack builds the official MacPython distribution with.

Unlike typical builds, this one has all the stock goodies:

  • readline 5.0.005 (static)
  • BerkeleyDB 4.3.27 (static)
  • waste (static)
  • Tcl/Tk Aqua 8.4.9 (dynamic - you'll need TclTkAqua to use Tkinter)

Note that this WILL NOT work with Mac OS X 10.2 (Jaguar) or earlier. It is ONLY for Mac OS X 10.3 (Panther) and later. That means, it should work on Mac OS X 10.4 (Tiger) . Also note that it expects you to install to the root drive only, despite the fact that the installer doesn't force you to do this.

If you are using Mac OS X 10.3, it is recommended that you also get the PantherPythonFix if you have not already, to prevent conflicts with the stock Python when you build new extensions. Packages for this Python will start popping up at pythonmac.org packages in the coming weeks.

If you are using Mac OS X 10.4, you should get the TigerPython24Fix package from pythonmac.org packages.

And now what you're all waiting for, the download link. It's here:
http://undefined.org/python/#python

2005-03-30

Five-minute Multimethods In Python (using dispatch)

Filed under: python — bob @ 9:37 pm

While PyProtocols dispatch more or less implements multimethods out of the box, I thought it would probably be useful to demonstrate that Guido's implementation of Five-minute Multimethods in Python can be cloned using it.

from dispatch.strategy import default, Argument, Signature
from dispatch.predicates import Getattr, Pointer
from dispatch.functions import GenericFunction

def multimethod_signature(args, kwargs):
    def isClass((arg, cls)):
        return Getattr(arg, '__class__'), Pointer(cls)
    def parse():
        for i, typ in enumerate(args):
            yield Argument(i), typ
        for name, typ in kwargs.iteritems():
            yield Argument(name=name), typ
    return Signature(map(isClass, parse()))

def multimethod(*args, **kwargs):
    def register(function):
        name = function.__name__
        oldfunc = function.func_globals.get(name, function)
        if not hasattr(oldfunc, 'addMethod'):
            oldfunc = GenericFunction(function).delegate
            def not_applicable(*a, **kw):
                raise TypeError("No Applicable Methods")
            oldfunc.addMethod(default, not_applicable)
        elif oldfunc is function:
            function = getattr(function, '_last_multimethod', function)
        sig = multimethod_signature(args, kwargs)
        oldfunc.addMethod(sig, function)
        oldfunc._last_multimethod = function
        return oldfunc
    return register

@multimethod(int, int)
def foo(a, b):
    print "code for two ints"

@multimethod(float, float)
def foo(a, b):
    print "code for two floats"

@multimethod(unicode, unicode)
@multimethod(str, str)
def foo(a, b):
    print (a, b)
    print "code for two strings"
    print "or unicodes..."

Note that it would be slightly less code if the type arguments were allowed to use isinstance(...), because that's what dispatch wants to do, but I wanted to clone Guido's implementation such that the class of the argument must be equal to the given type.

The other differences from Guido's implementation are:

  • No global registry, the registry is the func globals (could be extended to also look in locals).. so you could use this from multiple modules and not have to worry about having a flat namespace for multimethod names in your interpreter!
  • If you use default arguments, it's going to use the defaults specified on the first multimethod
  • You don't have to specify the type of every argument, you could multimethod dispatch on the first argument, or even named arguments

Note that would likely be more sensible and less code to use isinstance(...) style multimethod dispatch instead, but I just wanted to make sure that the (weird?) semantics were preserved. I'm guessing he used type identity because you could dispatch on a dict, so the implementation would be short :)

The following is a more "natural" implementation of how this would be implemented using PyProtocols dispatch. This version will accept subclasses, and can not be stacked. Its syntax is not as terse as @multimethod, but it's far more useful as you can dispatch on expressions, not just type.

import dispatch

@dispatch.generic()
def foo(a, b):
    """
    foo is a generic method that takes two 
    parameters and dispatches based on type
    """

@foo.when("isinstance(a, int) and isinstance(b, int)")
def foo(a, b):
    print "code for two ints"

@foo.when("isinstance(a, float) and isinstance(b, float)")
def foo(a, b):
    print "code for two floats"

@foo.when(
    # note that you can't use multi-line strings 
    # in here without backslash continuation.
    # dispatch's current parser hates them(?!)
    "(isinstance(a, str) and isinstance(b, str)) or "
    "(isinstance(a, unicode) and isinstance(b, unicode))")
def foo(a, b):
    # what makes this more interesting than dispatching on
    # basestring is that you can't mix str/unicode here.
    # also note that this is equivalent to stacking...
    print (a, b)
    print "code for two strings"
    print "or unicodes..."

2005: The Year of PyObjC

Filed under: PyObjC, python — bob @ 5:32 pm

This year is going to be huge for PyObjC:

  • Apple is paying attention.
  • We're going to have 3 major releases in 6 months (1.2 was at the end of December, 1.3 final will be out this week, and 1.4 will be out by WWDC 2005)
  • Tiger. More frameworks, better existing frameworks. Expect to see some Cool Stuff out of the box in PyObjC 1.4!

I could say a lot more here, but I'm under NDA and I have a lot of code to write anyhow :)

2005-03-24

.pyc’s, eggs, and zipimport

Filed under: python — bob @ 11:56 am

I've been working on the PythonEggs runtime during PyCon, and I ran across a bug in the zipimport built-in module. It's not a proper PEP 302 loader, so it can't be used on sys.meta_path. Unfortunately, due to some features we want to implement, we need this to work.

Because zipimport is written in C, is a built-in, and has no Python reference implementation, it's quite hard to fix. My current approach is to rewrite it in pure Python, determine the correct logic, and probably backport the correct behavior back to C.

I've finished rewriting zipimport in Python, though I haven't fully and correctly implemented the PEP, it passes test.test_zipimport and doesn't have the particular bug we encountered when importing a trivial package-using Egg with the importer on sys.meta_path.

One of the things I noticed when writing this is that the Python bytecode file format (.pyc and .pyo) isn't really well specified outside of the source code. Here are the constraints I came across by reading it.

Python Bytecode Files

Length:
A Python bytecode file must be more than 9 bytes long.
Magic Number [0:4]:
The first four bytes are a magic number. This magic number changes for every major Python release, and can be determined by PyImport_GetMagicNumber() from the C API, or imp.get_magic() from Python. For example, the magic number for Python 2.3 is ';\xf2\r\n' and Python 2.4 is 'm\xf2\r\n'. The reasoning and numbering scheme behind this magic number is documented in Python/import.c. One particularly clever feature of the magic number is that it incorporates a line ending, so if the file is read or written with an incorrect mode it will end up with a bad magic number, preventing it from being loaded.
Modification Time [4:8]:

The next four bytes are a 32 bit little endian signed long representing the modification time of the file. If the OS uses a time_t longer than 32 bits, and higher bits are set, Python breaks. So, Python (at least up to CVS) will not run if your clock is set past 2038. More generally, it is written by PyMarshal_WriteLongToFile(), but as that is not exposed to Python by the marshal module, I needed to know the precise layout. For whatever reason, the zipimport module doesn't use the marshal API for reading it back in anyway.

  • If the mtime is zero, the following checks are not performed, and the bytecode is used.
  • The mtime in the bytecode file must match the mtime of the bytecode file exactly. zipimport must relax this constraint to allow for there to be up to one second mismatch, due to the lack of precision in the zip format's TOC.
  • The mtime must be >= the mtime of the corresponding source file, if one is present.
Bytecode [8:]:
The rest of the bytecode file must be a marshaled code object.

2005-03-23

PyObjC at PyCon

Filed under: PyCon, PyObjC, macosx, py2app, python — bob @ 1:32 pm

I can finally take a breather now that my talks are over! For those of you that missed them, or want to take a closer look, here are the slides:

If you poke around in the svn repository that those link to, you can find the ReST source for the slides, the source imagery, the software that throws them together with docutils and Reportlab, etc.

2005-03-17

ANN: PyObjC 1.3b1

Filed under: PyObjC, py2app, python — bob @ 10:31 pm

NOTE:

This is an announcement for a BETA release of PyObjC. Though we know it to be quite stable, and have been using it on a daily basis for quite some time, use it at your own risk. 1.3.0 will be out in a matter of days, but it is essential that we get some eyes on this!
Installer package available from:
http://pythonmac.org/packages/ (note that the installer also contains py2app 0.1.8)
Source:
http://svn.red-bean.com/pyobjc/tags/pyobjc-1.3b1/

Version 1.3 (2005-03-??)

  • The bridge now maintains object identity across the bridge in both directions. Previous versions of the bridge only did this when bridging from Objective-C to Python.

    Exceptions: NSString and NSNumber do not have unique proxies. NSString never will have. Python numbers and strings are converted, not proxied and therefore also don't get unique proxies.

    And finally, any python object that is proxied using the __pyobjc_object__ interface will only get a unique proxy if the __pyobjc_object__ method implements that feature.

  • New objc.protocolsForClass function that returns a list of protocols that the class directly claims to conform to.

  • PyObjC classes can now declare that they implement formal protocols, for example:

    class MyLockingClass(NSObject, objc.protocolNamed('NSLocking')):
        # implementation
        pass
    

    It is also possible to define new protocols:

    MyProtocol = objc.formal_protocol("MyProtocol", None, [
       selector(None, selector='mymethod', signature='v@:'),
    ])
    

    All formal protocols are instances of objc.formal_protocol.

  • PyObjCTools.KeyValueCoding has a new kvc class that allows Pythonic Key-Value Coding.

    • __getitem__ is mapped to valueForKeyPath:
    • __setitem__ is mapped to setValue:forKeyPath:
    • __getattr__ is mapped to valueForKey:
    • __setattr__ is mapped to setValue:forKey:

    The kvc class uses __pyobjc_object__, so it may cross the bridge as the wrapped object.

  • NSNumber instances are bridged to a float, long, or int subclass that uses __pyobjc_object__. NSDecimal is converted to NSDecimalNumber when used as an object, NSDecimalNumber is not bridged to NSDecimal because the latter is a mutable type.

  • The Python to Objective-C bridge now looks for a __pyobjc_object__ attribute to get a PyObjC object from a Python object.

  • New IDNSnitch example in Inject that demonstrates how to write an monitor for the launch of another application, use objc.inject to load code into a target process, and override the implementation of an existing method but still call back into the original implementation (method swizzling).

  • objc.IMP should do the right thing now. This type is returned by +[NSObject methodForSelector:] and +[NSObject instanceMethodForSelector:]

  • New ToDos example in CocoaBindings that demonstrates how to use two array controllers for the same data, and how to use value transformers to alter the color of text. Originally from "Cocoa Bindings Examples and Hints", converted to PyObjC by u.fiedler.

  • New Bookmarks example in CocoaBindings that demonstrates how to subclass NSArrayController to implement the NSTableView delegate drag and drop protocol, including copying of objects between documents and accepting URL drops from other applications. Also demonstrates re-ordering of the content array. Originally from "Cocoa Bindings Examples and Hints", converted to PyObjC by u.fiedler.

  • New FilteringController example in CocoaBindings that demonstrates how to subclass NSArrayController to implement filtering of a NSTableView. Also demonstrates the use of indexed accessors. Originally from "Cocoa Bindings Examples and Hints", converted to PyObjC by u.fiedler.

  • New ControlledPreferences example in CocoaBindings that demonstrates how to use Cocoa Bindings to simplify storing and retrieving user preferences. Originally from "Cocoa Bindings Examples and Hints", converted to PyObjC by u.fiedler.

  • New TemperatureTransformer example in CocoaBindings that demonstrates how to use NSValueTransfomers with PyObjC. Based on Apple's "Cocoa: Value Transformers" documentation, converted to PyObjC by u.fiedler.

  • New CurrencyConvBindings example in CocoaBindings that demonstrates a Cocoa Bindings enabled version of the CurrencyConverter example. Converted to PyObjC by u.fiedler from the example in Apple's "Introduction to Developing Cocoa Applications Using Bindings".

  • New ManualBindings example in CocoaBindings that demonstrates how to develop programmatic bindings from a PyObjC application. Converted to PyObjC by u.fiedler from the "Cocoa Bindings and Hints" example of the same name.

  • New HotKeyPython example in AppKit that demonstrates how to use Carbon global hot keys from a PyObjC application. Also demonstrates how to use a NSApplication subclass.

  • Key-Value Observing support is now automatic in Python classes that descend from NSObject, unless they implement a custom willChangeValueForKey:, didChangeValueForKey:, or __useKVO__ is not True. This allows self.foo = 1 to automatically trigger notifications. This works in all cases, whether foo is a property, ivar, or just in the __dict__.

  • New Inject folder in Examples, with an InjectInterpreter example that will inject a GUI Python interpreter into any process.

  • New objc.inject() function for Mac OS X 10.3 and later, allows an arbitrary bundle to be loaded into another process using mach_inject.

  • objc.classAddMethods now recognizes and supports classmethods.

  • GC is now correctly implemented for struct wrappers.

  • The NSNumber bridge has been removed, now you will get NSNumber instances across the bridge instead of a Python representation.

  • PyObjCTools.AppHelper.runEventLoop() will now bring your application to the front at startup when using pdb mode for convenience.

  • objc.loadBundle() no longer filters the class list. This solves a few potential issues and shaves off about 1/3rd of the overhead of python -c "import AppKit".

  • PyObjCTools.AppHelper.runEventLoop() no longer breaks on pure Objective-C exceptions. Most exceptions of this variety are more like warnings, and there is nothing that can be done them anyway.

  • PyObjCTools.AppHelper.runEventLoop() now installs the interrupt handler and verbose exception logging when using pdb, either explicitly or by the USE_PDB environment variable.

  • There is now a fast path for the NSString/unicode bridge when Py_UNICODE_SIZE is 2. This is the default setting for Python.

  • The default selector signature will have a void return value unless a "return" statement with an argument is used in the bytecode. In that case, it will default to an object return value.

  • __bundle_hack__ is no longer necessary, py2app now sets a different environment variable to the current plugin during execution, and a hack is installed to NSBundle so that classes may respond to requests for their bundle with the +bundleForClass method. The class builder adds a default implementation of this to Python classes if this environment variable is set.

  • Added objc.currentBundle(), which is equivalent to NSBundle.mainBundle() except after loading a plug-in. Makes it easier to load nib files.

  • PyObjCTools.NibClassBuilder.extractClasses() now uses objc.currentBundle() instead of NSBundle.mainBundle(). This makes plugins less of a hassle to develop and allows identical code to be used for application or plugin development.

  • objc.registerPlugin() and objc.pluginBundle() are now deprecated as they are no longer useful.

  • It is now possible to subclass a class that implements copyWithZone: without setting __slots__ to ().

ANN: py2app 0.1.8

Filed under: PyObjC, py2app, python — bob @ 9:40 pm

py2app is the bundlebuilder replacement we've all been waiting for. It is implemented as a distutils command, similar to py2exe, that builds Mac OS X applications from Python scripts, extensions, and related data files. It tries very hard to include all dependencies it can find so that your application can be distributed standalone, as Mac OS X applications should be.

py2app 0.1.8 will be included in the installer for PyObjC 1.3. If you have installed PyObjC 1.3, then you already have py2app 0.1.8 installed.

Download and related links are here: http://undefined.org/python/#py2app

py2app 0.1.8 is a major enhancements release:

Bugs fixed:

  • Symlinks in included frameworks should be preserved correctly (fixes Tcl/Tk)
  • Fixes some minor issues with alias bundles
  • Removed implicit SpiderImagePlugin -> ImageTk reference in PIL recipe
  • The --optimize option should work now
  • weakref is now included by default
  • anydbm's dynamic dependencies are now in the standard implies list
  • Errors on app launch are brought to the front so the user does not miss them
  • bdist_mpkg now compatible with pychecker (data_files had issues)

Options changed:

  • deprecated --strip, it is now on by default
  • new --no-strip option to turn off stripping of executables

New features:

  • Looks for a hacked version of the PyOpenGL __init__.py so that it doesn't have to include the whole package in order to get at the stupid version file.
  • New loader_files key that a recipe can return in order to ensure that non-code ends up in the .zip (the pygame recipe uses this)
  • Now scans all files in the bundle and normalizes Mach-O load commands, not just extensions. This helps out when using the --package option, when including frameworks that have plugins, etc.
  • An embedded Python interpreter is now included in the executable bundle (sys.executable points to it), this currently only works for framework builds of Python
  • New macho_standalone tool
  • New macho_find tool
  • Major enhancements to the way plugins are built
  • bdist_mpkg now has a --zipdist option to build zip files from the built package
  • The bdist_mpkg "Installed to:" description is now based on the package install root, rather than the build root

2005-03-11

SWIG hate

Filed under: c, python — bob @ 4:28 pm

SWIG should definitely win the prize for worst implementation of a good idea, ever. Automatic wrapping of code sounds great, right? Well, it could be, but it's most certainly not. To the point where I can only recommend SWIG to people that I really don't like.

Why must they break things in EVERY SINGLE CONSECUTIVE RELEASE?! Do they hate us? Are they secretly fighting to keep C as the first and foremost language in open source development?

I'm thankful that I have always had the common sense to avoid using SWIG for my own projects, but I've had more than my own share of headaches maintaining software that depends on the beast, like PyOpenGL. As you can probably guess, that's what I'm dealing with right now. Although gouging my eyes out with a toothpick would be more pleasurable. I hope the devleopers of SWIG don't put it on their resume, cause it sure wouldn't get them hired anywhere I work.

pygame 1.7.0 for Mac OS X 10.3

Filed under: PyObjC, macosx, packman, pygame, python — bob @ 3:43 am

Pygame is a set of Python modules designed for writing games. It is written on top of the excellent SDL library. This allows you to create fully featured games and multimedia programs in the Python language. Pygame is highly portable and runs on nearly every platform and operating system.

Although the official sdist tarball isn't cut yet, I've packaged what should be pygame 1.7.0 for Mac OS X 10.3 (Panther) users. It is available from pythonmac.org packages.

This installer ships with:

pygame:
Installed to /Library/Python2.3/
pygame headers:
Installed to /System/Library/Frameworks/Python.framework/Versions/2.3/include/python2.3/
pygame examples and documentation:
Installed to /Developer/Python/pygame/
SDL with my AltiVec patches, SDL_mixer, SDL_ttf, SDL_image, smpeg:
Installed to /Library/Frameworks/

To use pygame, you will need PyObjC 1.2 or later, and you will probably want Numeric, PIL and PyOpenGL installed. All of these are also available from pythonmac.org packages.

The following improvements are especially relevant to Mac OS X:

  • Calls into nasty private Apple SPI (CPSEnableForegroundOperation) to grab GUI access if it doesn't already have it (read: no pythonw required)
  • Can now read the default font and icon out of any PEP 302 get_data compliant loader (read: py2app 0.1.8 can put pygame entirely in the site-packages.zip, and it works!).
  • Includes newer versions of all dependencies (it sure has been a long time since 1.6.0!)
  • Most surface operations should be significantly faster on G4 and G5 computers due to my AltiVec patches to SDL

As it has been quite a long time since the last release, there are pages full of bug fixes and feature enhancements. When pygame.org updates for the 1.7 release, check WhatsNew.

Note that I will not be making a Mac OS X 10.2 (Jaguar) compatible release for Python 2.3. When Python 2.4.1 is out (and probably not until there is an official distribution of it), I may build a pygame release for that.

UPDATE: I built PyOpenGL, with some ugly hacking and lots of compiling. If there was an award for having a crappy build process, I would nominate PyOpenGL, twice.

Next Page »

Powered by WordPress