Personal tools
You are here: Home Weblog Archive 2008

Entries For: 2008

2008-12-03

Specific imports versus module imports in Python

Filed Under:

I've always been a fan of explicit, separate imports in Python, vs generic module imports. Maybe it's because I like things to be explicit, or I've been spoiled by the way Eclipse Pydev deals with auto-importing, but I have an aversion towards generic module imports. To keep the code style consistent, I even rewrite to this style any foreign code that ends up in my code.

Specifically, I'm talking about:

from foo import Bar
Bar()

vs

import foo
foo.Bar()

Needless to say, I like the first style better.

There are advantages and disadvantages for each of the above methods:

  • general module imports demand less from the editor (the programmer needs to type less "import" lines and the editor doesn't need to be very much aware of Python - emacs and vim users will probably favor this style)
  • but it makes it hard sometimes to figure out where a module comes from. Example: you're deep down reading a module and you encounter a line referencing the component module. Now, where does that component module comes from? Could be both of the following imports:
from zope.app import component
from zope import component

Of course, some people sensed this problem and write code like:

class MyView(object):
    template = zope.app.pagetemplate.ViewPageTemplateFile('template.pt')

But this code is hard to read, hard to write and is almost at the limit with the self-imposed line length of 80 characters, which means most of the times it needs to be broken in two lines.

Today I became aware of what I consider the biggest advantage of using specific import (from foo import Bar). Heavy refactoring, in the absence of a comprehensive test suite, is a lot easier! When starting the program, the imports will fail and you get an immediate pointer to where you need to make a fix. If I would have used a generic module import, the error will have appeared only when trying to use the piece of code that calls foo.Bar().

TL;DR: use specific imports! If your editor doesn't support it, take some time to look at Eclipse Pydev or Netbeans, two free IDEs with Python support. You'll get:

  • easier refactoring
  • better code legibility
  • I'll be happier when I need to reuse code snippets :-)

2008-11-23

Moving to Intrepid and KDE 4.1

Filed Under:

KDE 4.1 screenshotI've migrated my laptop (my main workstation, these days) and I've done a complete migration to the KDE 4.1 desktop. Things are not perfect, (on the old KDE 3.5 I'd say things were 99% according to my tastes), but I'm trying to accomodate and find replacements. I didn't like the KDE 4.1 launcher at first and I've even tested Launcelot for a while, but now I'm back to the default menu and I'm starting to like it (on 3.5 I was using Tasty Menu).

I'm using Scultptura style with the Scultura-Stone color scheme and Sculptura window decoration. I needed to add another plasmoid from kde-look, the panel spacer, to make the systray smaller and separate the buttons on the right side to those on the left.

Things that I'm missing:

  • no "window" list menu widget, which I've tried to replace with a plasmoid. I might as well remove it, because it doesn't list all the windows from all desktops, just the current desktop.
  • I don't like the double spacing of the clock & date widget, I wish that it was a single line. I saw that there's a plasmoid on kde-look.org, I might try that.
  • I don't like the black background in the systray (at the right top)
  • There's no working "app shortcuts buttons" plasmoid, although I may try to simulate that with the Quick Access plasmoid.

Things that I'm happy with:

  • Shiny new software!
  • KDE 4.1 is actually not that horrible, once I tweak it (although Kde 3.5 had the same problem, closest to my "visual" tastes comes Gnome, but I like Kde applications better).
  • NetworkManager seems to deal a bit better with my wireless connection (when my girlfriend opens her Macbook I get terrible disconnects from the access point)

2008-11-18

Subversion 1.5 + default instalation Plone 3.1 buildout: no problems here

Filed Under:

I've hit the setuptools + subversion 1.5 problem again with a freshly install Plone 3.1.7 buildout: I've added my egg develop folder in zinstance/src/, I've added the egg in the relevant sections in buildout.cfg, but it wouldn't work because of the incompatibility with the old setuptools version.

The solution is to upgrade the installed setuptools to the latest version. For example, for a standalone Plone installed at /home/tibi/Plone, we have this structure:

/home/tibi/Plone
   /Python-2.4
   /buildout-cache
   /zinstance

I've changed directory to the Python-2.4 from above and ran:

bin/easy_install -U setuptools

Then I could succesfully run the buildout in the zinstance folder.

2008-11-01

A wxPython based tagcloud renderer

Filed Under:

This is a small example app that will render a tag cloud with various font weights/height, based on their weight in the cloud. Not much to say here, hope it is useful to someone. It has actually been easier to design and create then expected, the only difficulty was in figuring out how to resize the buttons based on the size of their label. The algorithm could be improved to generate the cloud in a single pass, but I'm not gonna bother, it works fast enough right now.

import wx

TAGS = [
 ['animals', 0],
 ['architecture', 2],
 ['art', 5],
 ['august', 1],
 ['australia', 1],
 ['autumn', 3],
 ['baby', 5],
 ['band', 1],
 ['barcelona', 3],
 ['beach', 2],
 ['berlin', 5],
 ['bird', 1],
 ['birthday', 0],
 ['black', 1],
 ['blackandwhite', 5],
 ['blue', 2],
 ['boston', 3],
 ['bw', 5],
 ['california', 1],
 ['cameraphone', 1],
 ['camping', 1],
 ['canada', 4],
 ['canon', 0],
 ['car', 5],
 ['cat', 3],
 ['chicago', 4],
 ['china', 5],
 ['christmas', 0],
 ['church', 0],
 ['city', 1],
 ['clouds', 3],
 ['color', 5],
 ['concert', 5],
 ['cute', 3],
 ['dance', 0],
 ['day', 5],
 ['de', 4],
 ['dog', 0],
 ['england', 5],
 ['europe', 4],
 ['fall', 1],
 ['family', 4],
 ['festival', 1],
 ['film', 1],
 ['florida', 2],
 ['flower', 1]
]

WIDTH = 500

def getwidth(line):
    return sum([l.GetSize()[0] for l in line])

def getheight(line):
    return max([l.GetSize()[1] for l in line])


class TopFrame(wx.Frame):
    
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.SetSize((500,400))

        lines = []
        
        for tag in TAGS:
            l = wx.Button(self, -1, tag[0], style=wx.NO_BORDER|wx.BU_EXACTFIT)
            weight = ((tag[1] % 2) and wx.BOLD) or wx.NORMAL
            l.SetFont(wx.Font(10 + tag[1], wx.DEFAULT, wx.NORMAL, weight, 0, ""))
            l.SetSize(l.GetBestSize())
            w, _h = l.GetSize()
            if lines:
                line_width = getwidth(lines[-1]) 
                if (line_width + w) > WIDTH:   #make a new line
                    lines.append([l])
                else:
                    lines[-1].append(l)   
            else:   #lines is empty
                lines.append([l])
        
        row_pos = 0
        for line in lines:
            height = getheight(line)
            row_w = 0
            w = getwidth(line)
            spacer = (WIDTH - w) / len(line)
            
            for l in line:
                dh = row_pos + height - l.GetSize()[1]
                l.MoveXY(row_w, dh)
                row_w += l.GetSize()[0] + spacer
                
            row_pos += height


class MyApp(wx.App):

    def OnInit(self):
        wx.InitAllImageHandlers()
        frame = TopFrame(None, - 1, "")
        self.SetTopWindow(frame)
        
        frame.Show()
        return 1
    

def start():
    app = MyApp(0)
    app.MainLoop()
    
if __name__ == "__main__":
    start()

2008-10-08

RichText control with wxpython: saving, loading and converting from internal XML to HTML

Filed Under:

I tend to be an angry caveman these days (see my previous post) and this doesn't lead to too much clear thinking. I've been dealing with the wx.richtext.RichTextCtrl for the past couple of days and I think I'm now close to finishing the tasks that I set myself to do with it. I'm trying to run a bunch of richtext controls on the same page, that would increase their size and show a toolbar when focused. My first problems came from the fact that all 5 of them flashed a caret which I couldn't hide. Trying to do ctrl.GetCaret().Hide() would result in a crash. After struggling with various possible solutions - none of which worked, I've realized that I should try the latest wxpython distribution, I was even decided to compile it manually. Fortunately, the wxwidgets project offers a repository for Ubuntu and sure enough, after I've upgraded, things started to work (I still needed to hide the caret for all the richtext controls and show it when they were focused, but that's no problem).

Now, unto the next tasks: saving and loading the content of these fields. I wanted to display the content in an HtmlWindow control, so converting the content to html was one thing I had to discover how to do. First step that I've tried, saving the html and loading it through the RichTextCtrl and its closely related buffers and handlers didn't work, so I've settled for a solution that involves one RichTextHTMLHandler and one RichTextXmlHandler. To help me understand how this thing works, I've created a clean example class where I could play with this thing. I hope it helps someone, as I haven't found too much info on this on the web (the Load/SaveStream methods are not even documented anywhere).

The toolbar class is taken from my project, created with wxGlade, while the frame class is created by me, manually (actually, this is the first frame that I have created by hand). To demonstrate the loading and saving, a variable is used to keep the content (self.content). Change the text, add some formatting, hit Save and then Load. This will take the text that was saved from the richtext control (in xml format), load it in a buffer and convert it to html which is then displayed in the HtmlWindow. I've only tried this on Linux, hope it works on Windows too. Oh, and you need to supply your own icons if you want to try the code (hint: I've used /usr/share/icons/gnome/32x32/actions as source of my icons.

#!/usr/bin/env python
from StringIO import StringIO
import wx
import wx.html
import wx.richtext

class TextFormatToolBar(wx.ToolBar):
    def __init__(self, *args, **kwds):
        self.text_ctrl = kwds.pop('text_ctrl')
        kwds["style"] = wx.TB_FLAT|wx.TB_3DBUTTONS
        wx.ToolBar.__init__(self, *args, **kwds)
        self.AddLabelTool(wx.ID_CUT, "Cut", wx.Bitmap("edit-cut.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "Cut selection", "")
        self.AddLabelTool(wx.ID_COPY, "Copy", wx.Bitmap("edit-copy.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddLabelTool(wx.ID_PASTE, "Paste", wx.Bitmap("edit-paste.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddSeparator()
        self.AddLabelTool(wx.ID_UNDO, "Undo", wx.Bitmap("edit-undo.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddLabelTool(wx.ID_REDO, "Redo", wx.Bitmap("edit-redo.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddSeparator()
        self.AddLabelTool(wx.ID_BOLD, "Bold", wx.Bitmap("format-text-bold.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddLabelTool(wx.ID_ITALIC, "Italic", wx.Bitmap("format-text-italic.png", wx.BITMAP_TYPE_ANY), 
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        self.AddLabelTool(wx.ID_UNDERLINE, "Underline", wx.Bitmap("format-text-underline.png", 
wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "", "")

        self.Realize()

        self.Bind(wx.EVT_TOOL, self.handle_bold, id=wx.ID_BOLD)
        self.Bind(wx.EVT_TOOL, self.handle_italic, id=wx.ID_ITALIC)
        self.Bind(wx.EVT_TOOL, self.handle_underline, id=wx.ID_UNDERLINE)
        self.Bind(wx.EVT_TOOL, self.handle_paste, id=wx.ID_PASTE)
        self.Bind(wx.EVT_TOOL, self.handle_copy, id=wx.ID_COPY)
        self.Bind(wx.EVT_TOOL, self.handle_cut, id=wx.ID_CUT)
        self.Bind(wx.EVT_TOOL, self.handle_undo, id=wx.ID_UNDO)
        self.Bind(wx.EVT_TOOL, self.handle_redo, id=wx.ID_REDO)

    def handle_bold(self, event):
        self.text_ctrl.ApplyBoldToSelection()

    def handle_italic(self, event):
        self.text_ctrl.ApplyItalicToSelection()

    def handle_underline(self, event):
        self.text_ctrl.ApplyUnderlineToSelection()

    def handle_paste(self, event):
        self.text_ctrl.Paste()
        
    def handle_copy(self, event):
        self.text_ctrl.Copy()
        
    def handle_cut(self, event):
        self.text_ctrl.Cut()
        
    def handle_undo(self, event):
        self.text_ctrl.Undo()
        
    def handle_redo(self, event):
        self.text_ctrl.Redo()


class TopFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.Freeze()
        sizer = wx.BoxSizer(wx.VERTICAL)
        rt = wx.richtext.RichTextCtrl(self, -1)
        toolbar = TextFormatToolBar(self, text_ctrl=rt)
        rt.SetMinSize((300,200))
        htmlwindow = wx.html.HtmlWindow(self)
        htmlwindow.SetMinSize((300,200))
        save_button = wx.Button(self, label="Save")
        load_button = wx.Button(self, label="Load")
        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
        btn_sizer.Add(load_button, 0, wx.EXPAND|wx.ALL, 6)
        btn_sizer.Add(save_button, 0, wx.EXPAND|wx.ALL, 6)
        self.Bind(wx.EVT_BUTTON, self.handle_save, save_button)
        self.Bind(wx.EVT_BUTTON, self.handle_load, load_button)
        sizer.Add(toolbar, 0, wx.EXPAND|wx.ALL, 6)
        sizer.Add(rt, 1, wx.EXPAND|wx.ALL, 6)
        sizer.Add(htmlwindow, 1, wx.EXPAND|wx.ALL, 6)
        sizer.Add(btn_sizer)
        self.SetSizer(sizer)
        sizer.Fit(self)
        self.Thaw()
        self.rt = rt
        self.htmlwindow = htmlwindow
        
    def handle_save(self, event):
        out = StringIO()
        handler = wx.richtext.RichTextXMLHandler()
        buffer = self.rt.GetBuffer()
        handler.SaveStream(buffer, out)
        out.seek(0)
        self.content = out.read()
        
    def handle_load(self, event):
        out = StringIO()
        handler = wx.richtext.RichTextXMLHandler()
        buffer = self.rt.GetBuffer()
        buffer.AddHandler(handler)
        out.write(self.content)
        out.seek(0)
        handler.LoadStream(buffer, out)
        self.rt.Refresh()
        
        cio = StringIO()
        cio.write(self.content)
        cio.seek(0)
        cout = StringIO()
        
        xmlhandler = wx.richtext.RichTextXMLHandler()
        htmlhandler = wx.richtext.RichTextHTMLHandler()
        newbuff = wx.richtext.RichTextBuffer()
        newbuff.AddHandler(htmlhandler)
        
        xmlhandler.LoadStream(newbuff, cio) #load xml into buffer
        newbuff.SaveStream(cout, wx.richtext.RICHTEXT_TYPE_HTML)
        cout.seek(0)
        self.htmlwindow.SetPage(cout.read())


class MyApp(wx.App):

    def OnInit(self):
        wx.InitAllImageHandlers()
        frame = TopFrame(None, - 1, "")
        self.SetTopWindow(frame)
        
        frame.Show()
        return 1
    

def start():
    app = MyApp(0)
    app.MainLoop()
    
if __name__ == "__main__":
    start()

2008-09-15

Stupid Internet Explorer and stupid IE bugs

Filed Under:

I'm placing the finishing touches on a web application I've been writing on and off for the last year. At last, I've reached Internet Explorer bugs, which are usually the last to be fixed. Among several other stupid but documented bugs, I've found one which I couldn't find documented in a shallow search on Google: it seems IE has an algorithm for comparing strings that is different then the one that is used internally when sorting an array of strings.

Take this code, for example, where I have separated the issue:

<html>
<body>
<script>

var x = [
  "Zwischenwasse",
  "Gurtis",
  "Götzis",
  "Partenen",
  "Raggal",
  "Rietz",
  "Schnifis",
  "Vösendorf",
  "Bludenz",
  "Galtür"
];

var y = [
  [34241, "Zwischenwasse"],
  [11223, "Gurtis"],
  [12321, "Götzis"],
  [12345, "Partenen"],
  [32454, "Raggal"],
  [34355, "Rietz"],
  [43453, "Schnifis"],
  [42321, "Vösendorf"],
  [43435, "Bludenz"],
  [43222, "Galtür"]
];

x.sort();
document.write(x);

document.write("<br/>");

var sorted = y.sort(function(a,b){
	return a[1] > b[1];
});

for (var i=0;i<sorted.length;i++) {
	document.write(sorted[i][1]+ ",");
}
</script>
</html>

The value which will be written in Internet Explorer when running this html document is:

Bludenz,Galtür,Gurtis,Götzis,Partenen,Raggal,Rietz,Schnifis,Vösendorf,Zwischenwasse
Bludenz,Galtür,Zwischenwasse,Gurtis,Götzis,Partenen,Raggal,Rietz,Schnifis,Vösendorf, 

Needless to say, in Firefox both lines will be the same, displayed in the proper order. On a side note, while I'm bashing Internet Explorer, let me just say that Internet Explorer 8 is a broken piece of software, at least on the side that I'm concerned. The developer tools are really buggy and tend to block the browser for even trivial operations (that is, when it doesn't crash it completely).

Update: apparently my mind is clouded with too much Python (I admit that Javascript is not a language that I use often). The problem is that the inline function used as sorting discriminator should return numeric values of -1, 0, 1, like this:

var sorted = y.sort(function(a,b){
	if (a[1] > b[1]){
		return 1;
	}
	if (a[1] < b[1]){
		return -1
	}
	return 0;
});

2008-09-14

Workingenv, setuptools and svn 1.5 redux

Filed Under:

I'm back to dealing with the incompatibility between setuptools and svn 1.5 which I have installed. Last time I've solved the problem by doing a svn checkout of http://svn.python.org/projects/sandbox/branches/setuptools-0.6/ and running sudo python setup.py install. Now I'm trying to do setup a virtualenv to play with repoze.catalog and I'm hitting the same problem. Of course, virtualenv comes with a hardcoded setuptools package and so it will setup a "broken" setuptools in the virtual environment. The solution is of course the same, adapted to use the virtualenv python script:

virtualenv myenv
cd myenv
svn co http://svn.python.org/projects/sandbox/branches/setuptools-0.6/ setuptools
cd setuptools
../bin/python setup.py install

This will upgrade the easy_install script to use the new setuptools library

2008-08-22

Bug in PyPi

Filed Under:

The zope.app.form PyPi page looks awful, it should be fixed. Who's fault is that? Django, the framework that sits underneath (AFAIK), or the docutils libraries that probably parse the RST pages?

2008-07-11

Hosting Plone and Zope 3 applications using nginx

Filed Under:

I'm doing a setup on a new server, I've decided to replace the default Apache 2.2 with an nginx http server. The setup which is needed for Zope 3 and Plone applications is the following:

[buildout]
parts =
    nginx
    nginxctl

[nginx]
recipe = gocept.cmmi
url = http://sysoev.ru/nginx/nginx-0.7.6.tar.gz
md5sum = ae7ce6f66a2cf5a5970d9a9a0da0cf7d

[nginxctl]
recipe = gocept.nginx
hostname = localhost
port = 80
configuration =
    worker_processes 1;
    events {
        worker_connections 1024;
    }
    http {
            upstream z3 {
                server 127.0.0.1:8080;
            }
            upstream plone {
                server 127.0.0.1:9080;
            }
        server {
            listen      ${nginxctl:port};
            server_name z3.example.org;
            root html;
            include /etc/nginx/proxy.conf

            location / {
                proxy_pass http://z3/++lang++ro/++skin++myskin/mysite/++vh++http:z3.example.org:80/++/;
            }
        }
        server {
            server_name plone.example.org;
            include /etc/nginx/proxy.conf

            location / {
                proxy_pass http://plone/VirtualHostBase/http/plone.example.org:80/t1/VirtualHostRoot/;
            }
        }
        server {
            server_name plone.example.org;
            rewrite ^/(.*)  /VirtualHostBase/http/plone.example.org:80/t1/VirtualHostRoot/$1 last;
            location / {
                proxy_pass http://plone;
            }
        }
    }

Note: this is a buildout.cfg. Using it together with zc.buildout makes the nginx instalation a very simple process: install zc.buildout (easy_install zc.buildout), and then run buildout in the folder that contains the .cfg file.

The settings in proxy.conf are important. Without a valid proxy_temp_path, for some reason delivery of all content that came from a Plone 2.5 site that used CacheFu setup with no proxy cache was freezing at 16014 bytes. The paths in /var/nginx need to be created and set to be writable by the nginx process (user nobody in my case).

client_max_body_size            0;
client_body_buffer_size    128k;
client_body_temp_path      /var/nginx/client_body_temp;

proxy_connect_timeout      90;
proxy_send_timeout         90;
proxy_read_timeout         90;
proxy_buffer_size          4k;
proxy_buffers              4 32k;
proxy_busy_buffers_size    64k;
proxy_temp_file_write_size 64k;
proxy_temp_path            /var/nginx/proxy_temp;
proxy_redirect                  off;
proxy_set_header                Host $host;
proxy_set_header                X-Real-IP $remote_addr;
proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;

 

Resources

A more complete nginx sample configuration file (but that only covers how to configure Plone)

Grok guide on hosting Zope 3 with nginx (note, at this moment the document is wrong, the setup line is missing a slash at the end).

2008-06-01

Using views as information mixins in templates

Filed Under:

This may be basic trick for some, a non-obvious usage of views for others, who knows, I'm documenting it here anyway. I've been using this technique for quite some time without giving it much thought.

There are times when I have an object in a template. I want to display information associated with that object. This information is already coded in a @@detail view on this object. Suppose this example (in mostly pseudocode):

class PersonDetail(BrowserView):
    """Show detail about a person"""
    def name(self):
        return compute_somehow_name()

class CommentDetail(BrowserView):
    """Show details about a comment"""

Now we have the following template for the CommentDetail view:

<div tal:define='person comment/author; person_info nocall:person/@@detail'>
    <a tal:attributes="href person/@@absolute_url" tal:content="person_info/name">The author</a>
</div>

Notice the nocall: keyword placed in from of the person/@@detail call. This ensures that the @@detail view is instantiated, but not called (so it is not rendered). This way we have access to the view class attributes, properly associated to the Person context.

2008-04-15

In case you're having problems installing ssl-for-setuptools...

Filed Under:

I've stumbled on this error when trying to install a easyshop buildout:

Exception: No SSL support found
An error occured when trying to install ssl-for-setuptools 1.10.Look above this message for any errors thatwere output by easy_install.
While:
  Installing instance.
  Getting distribution for 'ssl-for-setuptools'.
Error: Couldn't install: ssl-for-setuptools 1.10

After scratching my head for a while, I've found the solution:

# apt-get install libssl-dev

2008-04-08

A tip on debugging Zope 2 with ZEO

Filed Under:

I'm having some troubles with CacheFu, and I've resorted to deleting some objects from a debug prompt, which is very easy to get at if one runs the Zope under ZEO. Still, I couldn't delete the objects because I was getting Unauthorized errors. Luckily, there is #plone, from which I got the following tip:

<naro> from AccessControl.SecurityManagement import newSecurityManager
<naro> user = app.acl_users.getUser('admin')
<naro> newSecurityManager(None, user)

Thanks, naro! 

Update: I realize now that I didn't write how to connect to ZEO:

#bin/instance debug

or

#bin/zopectl debug

This will connect the ZEO client and drop you in a python shell

2008-03-28

Ubuntu Hardy Heron: some things are bad

Filed Under:

Note to self: always create a root account on Ubuntu.

I've updated my laptop to Kubuntu Hardy Heron, and while some things worked fine, there are a couple of stupid bugs that chained to make my life hard.

First of all, why did the Network Configuration applet in Settings Manager in KDE saw fit to delete the hostname of my localhost, tibi-laptop, from /etc/hosts? Now I can't run anything with sudo, as it imediately aborts with an error "No hostname tibi-laptop". Why can't my laptop find any Access Point if there wasn't one accessible at boot time? Why, when I've started Ubuntu in single mode, was I greeted with a dialog that asked me to select an option (continue normally, drop in root shell, fix X) but which didn't allow me to select anything (the keyboard wasn't properly recognized, even though that I have a perfectly regular keyboard on my laptop). Why does the reboot never finishes and maxes out the CPU?

The only way I could fix the hosts problem, while avoiding to hunt for some Linux cd, was to boot in Windows and install Ext2fsd and after that I was able to mount and change the /etc/hosts file.

These things are basic, they should just work. I'm aware that Hardy is beta right now and I'm using the KDE part of Hardy, but with Gutsy, (even though that applet always screwed up my network settings), I never had these problems. Shouldn't things go forward instead of backwards?

2008-03-19

Why do I use Zope 3?

Filed Under:

I'm in the process of beginning a new project and I'm debating on what framework to use. Of course it will be Zope 3, but why do I use it. Well, it's sure something that has to do with these facts:

  • it's open source, with a strong, mature community around it
  • while it's still actively development, it has a stable API
  • it's written in Python, one of the easiest and most powerful languages
  • it's built around a component architecture, which means writing pluggable applications comes naturally
  • solves the problem of publishing objects through the web
  • everything is transaction based. You won't lose data with it, you won't get garbage data inside your database
  • it has fast, configurable storages (ZODB, Relstorage)
  • internationalization and localization is easy
  • it's a library as much as it is an application server. When it's a library, it's very slim and as a full application server has a lot going on, including XMLRPC, ftp, webdav, sql connectivity, its own http server, etc.
  • has a cool, extensible templating language (the Zope Page Templates)
  • makes it extremely easy to create and store objects through ZODB
  • has facilities to query for objects using indexed data, through zope.catalog and extensions
  • writing a new catalog index is not an extremely complex task
  • it has an event framework; no complex application should run without an event framework
  • has advanced, flexible form libraries that can generate forms introspected from the models (zope.formlib, z3c.form)
  • has advanced templating concepts and content placement, such as the pagelets and viewlets
  • it's "enterprise ready": it's possible to load balance zope clients using ZEO or relstorage
  • comes with extensible authentication and user sources
  • promotes an extensible build system for applications (zc.buildout)
  • follows python standards, for the most part: library packaged as independent eggs and while it's not built around WSGI, it has full support for it
  • it can do document workflows (hurry.workflow)
  • it has some very cool packages from the community:
    • zc.table
    • z3c.form
    • Storm and sqlalchemy wrappers
    • zc.resourcelibrary, z3c.resourceinclude
    • z3c.pagelet
    • lovely.remotetask
    • grok
    • gocept.registration
    • z3c.traverser
  • is very well documented
  • it fits comfortably in my brain


2008-03-18

A few ATReferenceBrowserWidget tips

Filed Under:

On a Plone 2.5 project I'm working I have a content type that has 3 reference association to another content type. ArchGenXML generated the fields with the same name, which means that in the interface there will be just one field, as they overwrite each other. To have them working I need to rename them, but how to do this from the model? Agx, at first glance, doesn't have support for this. While playing and even trying to change agx to support this use case, I've noticed that agx considers the "association end" as the one meaningful: all tagged values set to it are reflected in the generated case. So the solution to this problem is to set a "name" tagged value in the "end" of the direct associations relationships. Check the following screenshot from Poseidon to see what I mean:

 

Field name in association

 

Another issue, this time directly related to the ATReferenceBrowserIssue, is the startup directory support. The widget has support for defaults set in portal_properties/site_properties, a property called refwidget_startupdirectories. The wording of this property's description in the README left me, as a non-native English speaker, a bit clueless. After some digging and debugging, I found out that the format for the lines there is:

/pathA:/pathB

where pathA is a path relative to the Plone site root; when you call the reference browser widget for an object in this path, the second path, pathB is returned as a base for browsing for objects. Even after I have understood the thing about the two colon separated path, it didn't work for me because I was refering to paths with their absolute paths (based in the Zope root) instead of their paths relative to the Plone site.

 

2008-03-17

GHOP Plone skins overview

Filed Under:

Plone has very few skins available from the community, when compared to just about anything. The skin incompatibilities that appeared between Plone versions 2.0/2.1/3.0 further deepen this problem. As a result of the 2007 Plone - GHOP there are some skins placed in the collective, but they're not visible anywhere (they're not published in the PSC, only some of them are available as eggs in PyPI, to get a glimpse of them you need to install them). These skins are placed under the plonetheme namespace, along with other skins, unrelated to GHOP.

To make them a bit more visible (and to serve as a personal reminder for their design), I'm showcasing them here. In reviewing them I've used Plone 3.1 (the trunk of the ploneout package).  After each skin was installed and snapshotted, I've uninstalled it and installed the next one.

Click on each thumbnail to get to the full image.

Andreas 01

Andreas01 - anonymous view  Andreas01 - editor view

One of the nicer skin, it stands out through its center align design, different sized columns and its simplicity. I can see myself using this skin as a base for a skin customization.

BlueBlog


BlueBlog - anonymous  BlueBlog - editor

This skin, as its name implies, is better suited for a blog, through its tall, thin design. Not a programmers blog though, and that's too bad, as it's a good skin.

Essay


Essay - anonymous  Essay - editor

Nice skin, I especially like the portlet design. Somewhat simple, though. If I'd customize this skin, I'd make it wider (maybe make it whole page instead of center-centred)

Green Community


GreenCommunity - anonymous  GreenCommunity - editor

A non-conformist skin, but still good looking. From what I have seen watching the PyPI, it's still under active development. There are some problems with it, though: the login portlet at the top is not fully visible, the "editor" version is missing some bits of the full skin. Still, it could be used for blog-ish type of sites, after some customizations.

Grok

Grok - anonymous  Grok - editor

This skin is very similar (might be the same) to the one used for the Grok project website. Probably not a GHOP skin. Although it seems like a very simple customization of default Plone, there are many issues with it that would mean that it would require heavy work to make it publishable for a real website.

Napoli

This is an almost empty skin, no point in showing it here

Pizza


Pizza - anonymous  Pizza - editor

Not a GHOP skin, this skin was created in a sprint. I believe its aim is to be one of the official supported skin. It is somewhat similar to NuPlone. Still needs work, though.

Plone2Kiss

Another empty skin

Python

Python - anonymous  Python - editor

Very similar to the current python.org skin. It feels rough and unfinished, but otherwise a nice, clean skin.

Relic

Relic - anonymous  Relic - editor

Ugly, amateurish skin. Needs a lot of work to get it into something usable. Probably you're better looking for something else.

Sonia

Sonia - anonymous  Sonia - editor

Not a GHOP, it just sits in the Collective svn repository. Promising, but would need a lot of customizations to make it work.

Conclusion: the problem remains: there are very few community-produced plone skins, and the ones available are usually low quality. We, the community, have got to do something about this.

Note: I have no affiliation to any of the GHOP students, mentors or the Plone - GHOP project.

2008-03-13

Tutorial: run ArchGenXML 2.0 under virtualenv

Filed Under:

The "modern" (post 1.5) version of ArchGenXML is packaged as egg, available on pypi. While in theory you could run "sudo easy_install archgenxml" and have it running with minimal effort, because it depends on zope.component and zope.configuration, things tend to get muddy and complicated. If you'll "easy_install zope.component" you'll get a bunch of zope eggs installed in python's site-packages and this will probably cause problems. When I've started developing with Zope 3 I had some hard time tracking some problems that ultimately turned out to be caused by zope packages installed in the system python "conflicting" with my regular zope instances. Even the Plone installer prefers to create its own Python directory, to keep the its packages separated from other python packages installed in the system. To solve this problem, ArchGenXML has a mechanism to specify a path to the zope packages, of which I've already blogged about, but I now consider it an extra step which can be avoided by using virtualenv to install the archgenxml egg.

This following recipe is specific to Ubuntu, but probably adaptable to any Linux system, and even Windows. First, if you don't have easy_install in your system, install it with:

#sudo apt-get install python-setuptools
#easy_install virtualenv

In my ~/Software folder I've ran:

#virtualenv agx

This will create a ~/Software/agx virtual environment. We need to activate it:

#source agx/bin/activate

Now all python commands we will run (python, easy_install) will 'belong' to this virtual environment. We can safely install agx, zope.component and zope.configuration, as they will be installed there:

(agx)#easy_install archgenxml
(agx)#easy_install zope.component
(agx)#easy_install zope.configuration

The ArchGenXML egg has defined several console scripts, they're available as scripts "tied" to this virtual python environment, for example, this is the 'archgenxml' script that was installed in my ~/Software/agx/bin folder:

#!/home/tibi/tmp/arch/bin/python2.4
# EASY-INSTALL-ENTRY-SCRIPT: 'archgenxml==2.0-rc1','console_scripts','archgenxml'
__requires__ = 'archgenxml==2.0-rc1'
import sys
from pkg_resources import load_entry_point

sys.exit(
load_entry_point('archgenxml==2.0-rc1', 'console_scripts', 'archgenxml')()
)

What this tells us is that you can safely run this script from "outside" the virtual environment, without needing to first "source bin/activate" it.

2008-01-31

The 763 projects in the Collective SVN

Filed Under:

Today I was curious about the number of projects found in the collective svn. They always seemed a lot, but I never knew how many. Until today, when I ran a xpath query over the subversion page and I found that there are 763! This number shocked me, I was expecting at most 200. My number of projects there is very small though, I can only claim ownership over 2 of them.

2008-01-28

Monty Python sketches and download script

Filed Under:

A friend pointed me to a page with many many links to Monty Python sketches videos.

Being a Monty Python fan, I've countered with this script:

#!/usr/bin/python2.4
downloader = "/home/tibi/Software/youtube-dl.py '%s' -o '%s'"
url = "http://onemansblog.com/2006/12/01/a-compendium-of-150-monty-python-sketches/"

import lxml.html
import urllib
import os

content  = urllib.urlopen(url)
etree = lxml.html.parse(content)
links = etree.xpath('//ol/li/a')

for link in links:
    print "Downloading ", link.text
    cmd = downloader %  (link.get('href'), link.text + '.flv')
    print os.popen(cmd).read()

It needs the YouTube downloader script and lxml 2.0.

2008-01-27

Problem with jQuery version 1.2.2 and getJSON

Filed Under:

It took me a couple of hours to track this (I was just starting a new project using jQuery), but I finally nailed it: jQuery version 1.2.2 has a problem with loading JSON data from absolute urls. Use the trunk version and you'll be fine.

Weblog
Atom
RDF
RSS 2.0
Powered by Quills
Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.
 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: