python


2
Mar 06

Fancy TurboGears “Flash” Messages

Lee has a great article on making the TurboGears “flash” functionality more useful. “tg_flash” or “turbogears.flash” in 0.9 is a method used to set a status message on the next page to be displayed. In its default form this allows you to send some plain text or HTML to the page.

I was looking for a way to pass more information using this mechanism to indicate success or failure of an operation so that the message can be styled properly and Lee presents a great solution to this problem:

Fancy status messages using tg_flash


2
Mar 06

Forms with values in TurboGears

So 0.9a1 supports forms, that’s great. However, I found getting hold of suitable examples for use in the real world difficult. The examples all show how to create a form and display it, and also how to validate the information in the response. What they didn’t show, is how to get information into the form when it’s displayed.

So here is a quick sample showing how it’s done:

In your python code, you build up a dictionary of the field values:

class SomeFields(widgets.WidgetsDeclaration):
    name = widgets.TextField(validator=validators.NotEmpty)
    email = widgets.TextField(validator=validators.Email())

the_form = widgets.TableForm(fields=SomeFields(), submit_text="Post")

class Root(RootController):
    @expose(template=".templates.index")
    def index(self):
        values = {"name":"simon", "email":"me@there.com"}
        return dict(form=the_form, values=values)

Then in your kid template, you pass the values in to the form:

${form(action="submitForm", value=values)}


2
Mar 06

TurboGears 0.9a1 Released

TurboGears is a “mega-framework” collecting a number of well-known web architecture components and glueing them together to form a productive environment for web development.

I’m using TurboGears at work to develop a service management and configuration application, and had held off implementing things like users, authentication and configuration forms in order to wait for 0.9 which brings built in support for all of these things.

Thankfully, the first test release of 0.9 has now been packaged up and is ready to go, I’ve upgraded my project to work with it and it seems to be pretty good: Get TurboGears 0.9a1 here.

Note, the upgrade instructions didn’t work for me, I had to change this line:

easy_install -f
http://www.turbogears.org/preview/download/index.html TurboGears

to this:

easy_install -U -f
http://www.turbogears.org/preview/download/index.html TurboGears


4
Feb 06

Boost Python Release Builds – Ouch!

I just did a release build of PyPN just to see how fast it ran when everything was built release. It took 14 minutes to build! To put that into perspective, the entirety of the main PN executable takes less than 3 minutes to build. Most of the 14 minutes is spent in the Generating Code stage.

Of course, the first time I ran PN it crashed straight away, due to a compiler settings mismatch – doh! Another 14 minutes down the drain.

The delay is all due to compiling and optimizing Boost Python. All those templates are eventually going to slow a compiler down, and my poor 2Ghz Athlon is not impressed! Once I have this first developer build with PyPN out the door it’s time to get around to trying out SWIG as an alternative (as I promised ages ago). If I have to write a couple of hours more code to get SWIG working as well, then it may be worth it for the compile-time savings.

Update: On my laptop (1.8Ghz Pentium-M) it takes only 4 minutes to compile in release, so I guess that’s more usable. Also, I think my desktop may have something wrong with it, even PN runs like a dog – time for a re-pave.


2
Feb 06

PyPN

I’m making progress with the Python scripting system, and am getting close to releasing a new testing build that has this functionality enabled. This won’t be for the weak of heart because I suspect there will be all sorts of problems to iron out.

Adding a new python script to be run is easy, there is a scripts directory under your Programmer’s Notepad install, and any python file placed in there will be automatically loaded at run time. PN does not see every bit of code in that file as a script, instead you must register your function as a script when desired.

To do this, you use the “script” decorator:

from pypn.decorators import *
import debug

@script()
def myFirstScript():
    debug.OutputDebugString("hello world!")

This will automatically register a script called “myFirstScript” under a group of scripts called “Python”. You can also re-name your script and/or specify the name of a group of scripts to include it in:

@script("My First Script", "Tutorial")
def myFirstScript():
    debug.OutputDebugString("hello more advanced world!")

Scripts appear in a new Scripts docking window in PN with a tree. Currently the only way to run a script is to double-click on it but you will eventually be able to assign shortcuts to them.

Of course, having started with scripts, you now want to do something visible:

import pn
@script("Say Hello!", "Tutorial")
def sayHello():
    d = pn.CurrentDoc()
    Scintilla(d).AppendText(12, "Hello World!")
 

This uses the Scintilla API method AppendText to add the string “Hello World!” to your document (12 is the number of characters in that string). There are a number of functions in the Scintilla API that can be made much easier to use from Python, and that will happen over time.

I’ll post some more documentation over the next few days, hopefully leading up to a release!


14
Nov 05

Python Indent Script

So, Python scripting for PN is shaping up nicely. I fixed some leaks, I exposed some more classes, and look what I can do now:

def findPrevLineLastChar(p, sci):
	while p > 0:
		p = p - 1
		c = chr( sci.GetCharAt(p) )

		# Look for a non-whitespace character ending the previous line.
		if not c in ['\n','\r','\t',' ']:
			return c
	return None

def python_indent(c, doc):
	sci = scintilla.Scintilla(doc)
	if c == '\n' or c == '\r':
		pos = sci.CurrentPos
		line = sci.LineFromPosition( pos )

		lc = findPrevLineLastChar( pos, sci )

		# If the previous line ended with a colon, then indent
		if lc == ':':
			indent = sci.GetLineIndentation( line )

			# Modify DumbIndent(tm) Indenting...
			previndent = sci.GetLineIndentation( line - 1 )
			if indent == previndent or indent == 0:
				indent += 4
				sci.IndentLine( line, indent )

# Hook up the python indenter.
s = getSchemeConfig("python")
s.indenter = python_indent

For those of you that can’t be bothered to read the above code to find out what it does, it provides a simplistic smart-indent algorithm for Python. If you add a new line after a “:” character, it indents the line.

Sure the algorithm could be more clever (it won’t work if there’s a comment after the ‘:’ for example) but the code basically works, and I can now write Python code with smart indenting. PN with Python rocks.


8
Nov 05

It’s got python in it…

In my PN directory I have a file called “init.py”, the contents look like this:


import pn

def onCharAdded(c, doc):
	s = "Got character: " + c
	doc.SendMessage(2282,  len(s),  s)

In my editor window right now, I have:

aGot character: a

It’s not a lot, it leaks, but it is a python interpreter embedded in PN2 responding to keypresses in an editor window. I finally got this going about 10 minutes ago after hours of head-scratching with boost python, boost shared_ptrs and other fun things.


17
Oct 05

Exposing Functions to Embedded Python

So far, instead of exposing functions to Python, we have (the reverse I guess) embedded the Python interpreter in our process using Boost::Python. So, running Python from within our process is all good and well but we want the Python code to interact with our C++ code as well.

We’ll add to the previous example:

const char* greet()
{
	return "Hello World from C++";
}

BOOST_PYTHON_MODULE(embed_test)
{
	def("greet", greet);
}

Here we have defined a python module embed_test which we will now expose to our embedded Python interpreter. The module has one function, “greet” in the Python module calls the C++ greet function.

Now we add to the main function:

if(PyImport_AppendInittab("embed_test", initembed_test) == -1)
		throw std::runtime_error("Failed to add test module to builtins");

The initembed_test reference is the function name generated by the BOOST_PYTHON_MODULE macro. Once we have made Python aware of this built-in module, we can modify the python code we run in the example to print out the result of the greet function:

handle<> ignored((PyRun_String(
	"import embed_test\n"
	"print embed_test.greet()"

	, Py_file_input
	, main_namespace.ptr()
	, main_namespace.ptr())
));

The techniques used here are all based around the basic functionality available in the Python C API. However, the Boost::Python library does make it easier to expose your own code to the Python interpreter. Next we’ll look at exposing classes and then move on to the use of SWIG.


12
Oct 05

Hello World from Boost::Python

I just created a “Hello World” application in Boost::Python. I’m comparing the Boost and SWIG options for embedding Python and making PN code available to it so this is the first step for the Boost implementation.

Here’s the code:

#include <boost /python.hpp>

int main(int argc, char* argv[])
{
  Py_Initialize();

  object main_module((
    handle<>(borrowed(PyImport_AddModule("__main__")))
  ));

  object main_namespace = main_module.attr("__dict__");

  // Run some python!

  handle<> ignored((PyRun_String(

    "print \"Hello World from Python!\""

    , Py_file_input
    , main_namespace.ptr()
    , main_namespace.ptr())
  ));

  Py_Finalize();

  return 0;
}

Not very much code at all to get going! boost_python.dll (required to use Boost::Python) comes in at 180kb. The initial test app (as coded above) in Release configuration takes 40kb.

The differences between this and the SWIG embedding version will be minimal, boost doesn’t hide much of the embedding code for you. The real differences will be in creating wrappers for functions and classes. With boost I have to write wrappers, SWIG will automatically generate them. The interesting bit will be the size of the code created.