Python 2.3 unter Mac OS X 10.5 (Leopard) bauen

Python 2.3 lässt sich unter Leopard nicht ohne Eingriffe bauen. Ich erläutere hier, was zu tun ist, um ein Python ohne Framework-Unterstützung zu bauen, so wie man es zum Beispiel für ältere Zope-Versionen benötigt.

by Michael Howitz posted at 2007-11-06 13:29 last modified 2007-11-06 13:29

Nach dem Herunterladen von Python 2.3.6 und dem Entpacken muss man erst mal das configure-Script bearbeiten, da ein Workaround benutzt wird, der in Leopard nicht mehr nötig ist und nicht mehr funktioniert.

sed "s/-u __dummy //" configure > configure.leo
chmod 755 configure.leo
./configure.leo --prefix=<Installationsverzeichnis>

Das Configure-Script setzt einige Präprozessordirektiven falsch, die sind folgendermaßen zu korrigieren:

echo '#undef _POSIX_C_SOURCE' >> pyconfig.h
echo '#undef _XOPEN_SOURCE' >> pyconfig.h
echo '#define HAVE_BROKEN_POSIX_SEMAPHORES' >> pyconfig.h

Jetzt ist es möglich, Python zu bauen und zu installieren:

make
make install

Nun fehlt noch readline-Unterstützung. Dazu gibt es verschiedene Varianten. Die folgende von bbum@mac.com war die einzige, die bei mir funktioniert hat:

svn co http://svn.red-bean.com/bbum/trunk/pyreadline/
cd pyreadline
tar -xzf readline-5.1.tar.gz
cd readline-5.1
./configure --disable-shared --enable-static
sudo make install
cd ../readline-0.0.0
<Pfad zu installiertem python> setup.py install

Diese Anleitung basiert auf der Anleitung für Python2.3 unter Tiger und Readline für Python.

Category(s)
Python

Zope3: Testen von felderübergreifenden Bedingungen in Interfaces mit zope.formlib -- aktualisiert

Um felderübergreifende Bedinungen (interface invariants) mit zope.formlib testen zu können sind in Zope 3.4 keine Klimmzüge mehr nötig.

by Michael Howitz posted at 2007-08-03 09:03 last modified 2007-08-03 09:03

Dieser Eintrag ist eine Aktualisierung des Eintrags Testen von felderübergreifenden Bedingungen in Interfaces mit zope.formlib, der sich auf Zope 3.2 bezieht. Mit Zope 3.4 ist alles viel einfacher. (Zope 3.3 habe ich nicht getestet, da die Ideen aber aus Philipp von Weitershausens Buch stammen, sollte es auch da funktionieren.)

Definition

Feldübergreiffende Bedingungen werden in Interfaces als invariants definiert. Als Beispiel soll ein Passwortfeld und eins für die Wiederholung des Passworts dienen. Beide Werte müssen also gleich sein.

Wenn die Bedingung nicht erfüllt ist, soll eine Exception geworfen werden. Da es sich um einen Validierungsfehler handelt, nutze ich zope.interface.Invalid.

Das Interface hat dann im Minimum folgende Gestalt:

import zope.interface

class IUser(zope.interface.Interface):
password = zope.schema.Password(title=u"Passwort")
password2 = zope.schema.Password(title=u"Wiederholung des Passworts")

@zope.interface.invariant
def arePasswordsEqual(user):
if user.password != user.password2:
raise zope.interface.Invalid(
u"""Das Passwort und die Wiederholung sind nicht gleich.""")
Es werden die beiden Felder definiert und zum Schluss wird die Funktion arePasswordsEqual als über den Dekorator  (@zope.interface.invariant) als Invariante des Interfaces definiert.

Benutzung mit zope.formlib

zope.formlib testet die Invarianten automatisch und zeigt Fehler oberhalb des Fomulars an. Es wird der Text der Exception angezeigt, der sich problemlos internationalisieren lassen sollte.

Category(s)
Zope 3

Programmatisch einen Zope3-Ordner in eine Site umwandeln

Eine Site enthält einen "SiteManager" und kann damit lokale Utilities beinhalten. Aus einem Ordner im ZMI eine Site zu machen, sind nur ein paar Klicks, programmatisch ist es aber auch nicht viel schwerer.

by Michael Howitz posted at 2007-05-15 16:10 last modified 2007-05-15 16:10

Angenommen, immer wenn eine Instanz der Klasse MySite angelegt wird, soll daraus eine Site gemacht werden. Es wird weiterhin angenommen, dass MySite das Interface IMySite implementiert.
Man abonniert zuerst per ZCML den ObjectAddedEvent:

<subscriber
for="zope.app.container.interfaces.IObjectAddedEvent"
handler=".eventhandler.onObjectAdded"
/>

Wir legen nun eine Datei eventhandler.py an und definieren dort die Funktion onObjectAdded, mit folgendem Inhalt:

from mypackage import IMySite
import zope.app.appsetup.bootstrap
import zope.app.component.interfaces
import zope.app.component.site
import zope.app.component.hooks
import zope.app.intids
import zope.app.intids.interfaces


def onObjectAdded(event):
if IMySite.providedBy(event.object):
# Macht aus object eine Site, wenn es noch keine ist
if not zope.app.component.interfaces.ISite.providedBy(event.object):
site_manager = zope.app.component.site.LocalSiteManager(event.object)
event.object.setSiteManager(site_manager)

# Wenn in der Site lokale Utilities installiert werden sollen, die
# sich auf den thread-globalen Context verlassen, dann ist dieser
# temporär zu setzen, damit in der richtigen, nämlich der neuen
# Site gesucht wird.
try:
old_site = zope.app.component.hooks.getSite()
zope.app.component.hooks.setSite(event.object)

 # Hier kann man jetzt die Utilities installieren.
# z.B. ein IntId-Util
zope.app.appsetup.bootstrap.ensureUtility(event.object,
zope.app.intid.interfaces.IIntIds, '', zope.app.intid.IntIds)

finally:
# Zum Schluss wird der alte Zustand wieder hergestellt.
zope.app.component.hooks.setSite(old_site)

Wenn der ObjectAddedEvent ausgelöst wird, ist das Objekt schon in seinem Container eingebettet und hat selbst Kontext. Nur in der thread-globalen Variablen steht noch der alte Wert für die Site, so dass neu installierte Utilities nicht gefunden werden.

Category(s)
Zope 3

Grundlagen von Webdesign

by Christian Theune posted at 2007-04-14 09:21 last modified 2007-04-14 09:21

Überblick

Das Medium Web unterscheidet sich vom Medium Druck. Dies erfordert entsprechend angepasste Arbeitsweisen bei der Gestaltung von Webseiten. Die folgenden Artikel geben einen Überblick über die Besonderheiten und weisen insbesondere auf Unterschiede zwischen Web und Druck hin:

Medium Internet

Das Medium Internet fordert den Designer, sich mit weitergehenden Themen zu beschäftigen, die im Druck nicht oder in anderer Form relevant sind:

Technische Detailinformationen


Umfassende Publikationen und Archive

Da es bereits umfassende Webseiten gibt, die Informationen für gute Gestaltung im Web zusammengetragen haben und pflegen, haben wir hier einige verlinkt:

Gallerien

Diese Seiten sammeln Beispiele guter Gestaltung, die sich mit guter technischer Implementierung verträgt:

Category(s)
Webdesign

Zope3-Formulare mit zope.formlib erzeugen

Benutzung von zope.formlib

by Michael Howitz posted at 2006-05-23 10:53 last modified 2006-05-23 10:53

Ab Zope 3.2 ist der empfohlene Weg, um Formulare zu erzeugen nicht mehr zope.app.form, sondern zope.formlib. Da es zu zope.formlib noch recht wenig Literatur gibt (als die zwei Zope3-Bücher geschrieben wurden, gab es sie noch nicht!) hier ein wenig Dokumentation.

Ausgangssituation (Beispiel)

Als Beispiel soll eine rudimentäre Adressverwaltung dienen. Die Interfaces sind dafür folgendermaßen definiert.

adressen/interfaces.py

from zope.interface import Interface
from zope.app.container.interfaces import IContainer
from zope.app.container.constraints import containers, contains
from zope.schema import TextLine, Text

class IAdresse(Interface):
u"Adresse"
containers('.IAdressenContainer')

name = TextLine(title=u"Name")
anschrift = Text(title=u"Anschrift")


class IAdressenContainer(IContainer):
u"Container für Adressen"
contains(IAddresse)

Es werden also zwei Interfaces erstellt, eins für Adressen und eins für einen Container, der die Adressen beinhaltet.

containers legt fest, dass IAdresse nur in IAdressenContainer erlaubt ist. Da dieses Interface erst später in der Datei definiert wird, muss der Pfad zu dem Interface als String angegeben werden.

containers fügt im Schema des Interfaces ein Feld __parent__ hinzu, welches ein Constraint hat, das sicherstellt, dass die Bedingung erfüllt wird.

adressen/adressen.py

from zope.interface import implements
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
from persistent import Persistent

from adressen.interfaces import IAdressenContainer, IAdresse

class AdressenContainer(BTreeContainer):
u"Container für Adressen."
implements(IAdressenContainer)

class Adresse(Persistent, Contained):
u"Eine Adresse"
name = u""
anschrift = u""

AdressenContainer ist ein BTreeContainer, Adressen ist eine Klassen, deren Objekte persitent gespeichert werden.

adressen/configure.zcml

<configure xmlns="http://namespaces.zope.org/zope">
<content class=".adressen.AdressenContainer">
<factory />
<allow interface=".interfaces.IAdressenContainer" />
</content>

<content class=".person.WaitingListItem">
<factory />
<allow interface=".interfaces.IAdresse"
set_schema=".interfaces.IAdresse" />
</content>

<include package=".browser" />
</configure>

Die Konfiguration berücksichtigt keine Sicherheitsaspekte, es der Einfacheit des Beispiels halber wird alles erlaubt.

Das include-Statement verweist schon auf die im nächsten Abschnitt beschriebenen Browser-Komponenten.

Die Browser-Komponenten für den AdressenContainer spare ich hier ein, weil daran nichts zope.formlib-spezifisches ist.

Hinzufügen-Formular (AddForm) mit zope.formlib

Für das Hinzufügen-Formular ist nur eine Klasse folgenden Inhalts zu definieren und dann per ZCML einzubinden.

browser/add.py

from zope.formlib import form
from adressen.interfaces import IAdresse

class AddForm(form.AddForm):
form_fields = form.FormFields(IAdresse).omit('__parent__')

Das ist die gesamte View-Klasse. Auf dem Attribut form_fields wird eine Instanz von form.FormFields gespeichert, die alle anzuzeigenden Schemaelemente beinhaltet. Es werden also die Schema-Felder aus IAdresse ermittelt. Zu diesen gehört auch das durch das containers-Statement erzeugte __parent__-Feld. Für dieses Feld existiert aber kein Widget und außerdem soll es sowieso nicht angezeigt werden. Dazu dient der Aufruf von omit, welches von einer bestehenden FormFields.-Instanz eine neue ohne die angegebenen Felder zurückliefert.

browser/configure.zcml

<configure xmlns="http://namespaces.zope.org/browser">
<page
class=".add.AddForm"
name="AddAdresse.html"
permission="zope.Public"
for="zope.app.container.interfaces.IAdding"
/>

<addMenuItem
title="Adresse"
description="Ein Adresse."
class="adressen.adressen.Adresse"
permission="zope.Public"
view="AddAdresse.html"
/>
</configure>

Die page-Direktive deklariert eine Seite, für die View-Klasse add.AddForm mit dem Namen AddAdresse.html, die jeder aufrufen darf (zope.Public). Damit diese Seite für ein Hinzufügen-Formular benutzt werden kann, muss sie für das Interface zope.app.container.interfaces.IAdding definiert sein.

addMenuItem erstellt einen Eintrag im Hinzufügen-Menü für die Adresse.

Bearbeiten-Formular mit zope.formlib

Das Bearbeiten-Formular ist nicht aufwendiger als das Hinzufügen-Fomular: eine minimale View-Klasse und ein bisschen ZCML.

browser/edit.py

from zope.formlib import form
from adressen.interfaces import IAdresse

class EditView(form.EditForm):
form_fields = form.FormFields(IAdresse).omit('__parent__')

Auch für Bearbeiten-Formulare stellt zope.formlib alles zur Verfügung, es ist nur noch anzugeben, welche Schema-Felder erscheinen sollen. Auch hier soll __parent__ wiederum nicht angezeigt werden.

browser/configure.zcml

Die folgende Direktive ist in browser/configure.zcml (siehe oben im Abschnitt Hinzufügen-Menü) hinzuzufügen:

<page
name="edit.html"
menu="zmi_views"
title="Bearbeiten"
for="adressen.interfaces.IAdresse"
permission="zope.Public"
class=".edit.EditView"
/>

Es wird für Objekte, die adressen.interfaces.IAdresse implementieren eine Seite deklariert, die edit.html heißt, zu der ein Link in den Tabs (Reitern) erscheint und die für jeden zugreifbar ist.

Benutzung von form.FormFields

Wenn Zope3 mittels zope.formlib ein Fomular erstellt, wird auf der View-Klasse das Attribut form_fields ausgewertet. Als Wert dieses Attributs wird eine Instanz vom zope.formlib.form.FormFields erwartet. Anmerkung: Die Klasse Fields ist als ein Alias für FormFields definiert.

FormFields bietet verschiedene Möglichkeiten, die Schema-Felder eines Interfaces zu filtern. Hier folgt nur eine Auswahl.

Parameter im Konstruktor von FormFields

ParameterBedeutung
*argsEs kann eine Liste von Interfaces angegeben werden, deren Schema-Felder in der angegebenen Reihenfolge im Formular erscheinen. Achtung: Es werden immer alle Schema-Felder angezeigt,. Wenn der Benutzer keinen Zugriff auf ein Feld hat, weil er zu wenige Rechte hat, kann es zu einem Fehler beim Erzeugen des Formulars kommen. Wenn der Benutzer keinen Schreibzugriff hat oder das Feld als readonly markiert ist, kommt es zu einem beim Speichern einer Änderung in dem entsprechenden Feld.
omit_readonly=True
Schema-Felder, die auf readonly (nur lesen) gesetzt sind,. werden im Formular nicht mit angezeigt.

Nachfiltern der FormFields

Nachdem eine Instanz von FormFields erzeugt wurde kann man diese Nachfiltern, um unerwünschte Felder nicht anzuzeigen.

MethodeBedeutung
omit(*args)Liste von Feldnamen (Strings), die in der zurückgegebenen neuen FormFields-Instanz nicht enthalten sein sollen.
select(*args)Liste von Feldnamen (Strings), die in der zurückgegebenen neuen FormFields-Instanz enthalten sein sollen.

Category(s)
Zope 3

Zope3: PAU programmatisch anlegen

Programmatisches anlegen eines Pluggable Authentication Utilities (Austauschbares Authentifizierungs-Utility) in Zope3.

by Michael Howitz posted at 2006-04-28 11:20 last modified 2006-04-28 11:20

In Zope3 kann man ohne weiteres über das Web-Interface ein Pluggable Authentication Utility, kurz PAU bzw. in der holprig klingenden deutschen Übersetzung "Austauschbares Authentifizierungs-Utility", hinzufügen. Will man das ganze programmatisch machen ist das auch nicht viel schwerer.

Voraussetzung für die hier folgende Anleitung ist folgende Anleitung: Programmatisch einen Zope3-Ordner in eine Site umwandeln.
Die im Folgenden beschriebene Methode createAuthenticationUtils wird innerhalb des try-finally-Blocks aufgerufen und erhält als Parameter event.object.

from zope.app.security.interfaces import IAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin

from zope.app.appsetup.bootstrap import ensureUtility
from zope.app.authentication.authentication import \
    PluggableAuthentication

from zope.app.authentication.principalfolder import PrincipalFolder
from zope.app.authentication.groupfolder import \
    GroupFolder, GroupInformation
from zope.app.component import site
from zope.app import zapi

from myproject import config

def addUtilityToPAU(pau, interface, utility_factory, id, name):
u"""Fügt ein Utility zu einem existierenden PAU hinzu.

pau ... PAU, zu dem das Utility hinzugefügt werden soll
interface ... Interface der neuen Komponente
utility_factory ... Klasse der neuen Komponente
id ... Id der Komponente im PAU.
name ... Name unter dem die Komponente (die ja ein Utility ist,
gefunden werden soll.)
"""
    utility = utility_factory()
    pau[id] = utility
    reg_man = pau.registrationManager
    reg = site.UtilityRegistration(name, interface, utility)
    reg_man.addRegistration(reg)
    reg.status = u'Active'

def _addGroupInformation(group_folder, id, title, description):
u"""Fügt einem Gruppenordner eine Gruppe hinzu.

group_folder ... Objekt des Gruppenordners
id ... Id der Gruppe
title ... title für die Gruppe
description ... Beschreibung für die Gruppe.
"""
    group = GroupInformation()
    group.title = title
    group.description = description
    group_folder[id] = group


def createAuthenticationUtils(obj):
u"""Erstellung des PAU und der darin liegenden Objekte.

obj ... Objekt, welches ISite implementiert, in dem PAU
installiert werden soll
"""
 
# PAU erstellen
    ensureUtility(obj, IAuthentication, '', PluggableAuthentication,
                  copy_to_zlog=False)
    auth = zapi.getUtility(IAuthentication)

# Anmeldung geschieht über Sitzungsdaten, weitere Anmeldearten sind
# in Zope3 schon implementiert, dazu in zope/app/authentication in den
# *.zcml Dateien nach 'provides=".interfaces.ICredentialsPlugin"' suchen
# der Eintrag unter name= des zugehörigen Utilities ist auch als
# Anmeldeart möglich.
    auth.credentialsPlugins = (u'Session Credentials',)

# Hinzufügen eines Ordners, der die Daten der Nutzungsberechtigenen
# (Principals) enthalten wird (deutsch: "Ordner über
# Nutzungsberechtigten")
    addUtilityToPAU(auth, IAuthenticatorPlugin, PrincipalFolder,
                    'principal_folder', u'Benutzerverwaltung')

# Hinzufügen eines Gruppenordners, der die Zuordnung der
# Nutzungsberechtigten zu Gruppen speichert
    addUtilityToPAU(auth, IAuthenticatorPlugin, GroupFolder,
                    'group_folder', u'Gruppenverwaltung')

# Festlegen, welche der angelegten Ordner für die Authentifizierung
# verwendet werden
    auth.authenticatorPlugins = (u'Benutzerverwaltung',
                                 u'Gruppenverwaltung',)

# Anlegen der Gruppen
    group_folder = auth['group_folder']
    for group in config.groups:
        _addGroupInformation(group_folder, group['id'],
                             group['title'], group['description'])

In myproject/config.py stehen die anzulegenden Gruppen:

groups = [{'id': 'management',
'title': 'Management',
'description': 'Management der Firma XY'},
]
Category(s)
Zope 3