Posts about python (old posts, page 7)

2006-05-02 15:44

FaxWeb is done

FaxWeb, a web frontend for mgetty+sendfax is finished. It works. It's probably close to bugfree ;-)

The missing piece is a nicer reimplementation of respond (and this one will be cross-platform, too) using PyQt, which is 50% done.

I am only missing how to implement portable systray icons. On Mac they make no sense, on Linux I have it working, on Windows I have no idea.

Here's the simple interface for faxweb:

http://static.flickr.com/54/139230422_f453d38430_t.jpg http://static.flickr.com/50/139230433_0d87a2f0ae_t.jpg http://static.flickr.com/47/139230434_f91f8d0b01_t.jpg

It even has a little AJAXy "the page doesn't reload" niceties courtesy of MochiKit!

Also from MochiKit, the nicer, rounded look&feel. Compare to this older, uglier one:

http://static.flickr.com/48/137206420_07ca331974_t.jpg

I know the new one is not good, either, but I have decided that since I can't aim for awesome, I should aim for adequate, and settle for boring and harmless.

Of course, if any CSS/XHTML guru volunteers for a makeover, I'd be very happy, since I use the same CSS everywhere (even on parts of this blog ;-).

All in all, a pleasure to write this thing, thanks to CherryPy!

2006-04-02 01:56

BPython Lives!!!

In January, I suggested it would be trivial to write a preprocessor that would accept a version of python which delimited blocks with braces instead of indentation.

Ok, almost, I suggested #{ and #} as delimiters.

Well, I had a few minutes free today, and here it is, a functional BPython->Python compiler, so to speak.

Here's the kind of imput it takes (notice how it's not indented at all:

def factorial(n):
#{
if n==1:
#{
return 1
#}
else:
#{
return n*factorial(n-1)
#}
#}

for x in range(1,10):
#{
print x,"!=",factorial(x)
#}

And it produces this (I am not happy with the indenting of the comments, but who cares):

def factorial(n):
  #{
  if n==1:
    #{
    return 1
  #}
  else:
    #{
    return n*factorial(n-1)
  #}
#}

for x in range(1,10):
  #{
  print x,"!=",factorial(x)
#}

As you can see, this is both a legal Python and a legal BPython program ;-)

It has some problems, like not working when you use a line like this:

#{ x=1

But hey, it is python with braces.

Here's the code. I predicted 30 lines. It's 34. And it's 99% a ripoff of Ka Ping Yee's regurgitate.py which you can find all around the web.

#!/usr/bin/env python
import tokenize, sys
program = []
lastrow, lastcol = 1, 0
lastline = ''
indlevel=0
def rebuild(type, token, (startrow, startcol), (endrow, endcol), line):
    global lastrow, lastcol, lastline, program,indlevel
    if type==52 and token.startswith ("#{"):
            type=5
            indlevel+=1
    if type==52 and token.startswith ("#}"):
            type=6
            indlevel-=1
    line="  "*indlevel+line.lstrip()
    startcol+=indlevel*2
    endcol+=indlevel*2
    # Deal with the bits between tokens.
    if lastrow == startrow == endrow:            # ordinary token
        program.append(line[lastcol:startcol])
    elif lastrow != startrow:                    # backslash continuation
        program.append(lastline[lastcol:] + line[:startcol])
    elif startrow != endrow:                     # multi-line string
        program.append(lastline[lastcol:startcol])
    # Append the token itself.
    program.append(token)
    # Save some information for the next time around.
    if token and token[-1] == '\n':
        lastrow, lastcol = endrow+1, 0           # start on next line
    else:
        lastrow, lastcol = endrow, endcol        # remember last position
    lastline = line                              # remember last line
tokenize.tokenize(sys.stdin.readline, rebuild)
for piece in program: sys.stdout.write(piece)

So, all of you who dislike python because of the lack of braces and the significant whitespace:

BPython has no significant whitespace, and braces are mandatory.

Enjoy coding!

2006-01-27 22:04

Silly idea to make Python popular

I have an idea that can kill the most frequent complain about python.

BPython.

BPython is a simple wrapper around pyhton which processes a .bpy file, produces a .py file, then compiles it into a .pyc.

What does it do? It uses braces to control flow.

Since braces are actual python syntax, you will have to use #{ and #}

As an added bonus, if you are careful, a bpython program will also be a valid python program.

Of course it has issues (like module loading) but those can be worked around.

The implementation should not be more than 30 lines of python. Or bpython ;-)

2005-12-30 14:25

My first time

I just found here the announcement of the first free software I published (at least, that I recall), from may 13, 1996. So, It's going to be 10 years in 5 months!

Killer quote:

Requires:
Python 1.3 (Maybe 1.2 would work too)
XForms 0.80 (NOT 0.75)

2005-12-29 20:04

Ok, here is how you write the shortest one

About http://www.pycontest.net... here's how it's done.

Sadly, it's pretty much impossible to put the code on the web because it has some really non-ascii stuff in it ;-)

Here's the key tricks (thanks to Remi and the others):

One of the key tricks is writing a short version of a very long octal number (or three).

I tried using base36 and int('gobledygook',36).

You could use 0xhexacodehere.

But here's the most space-efficient one: use base 256!

For each byte you want, create a char using chr(byte). Then paste them all together. Then put it in your code as a string. If you are lucky you will have no quotes or newlines in it, and the python interpreter will eat it.

You can later get the byte number x using ord('h!alsdf'[x]), and each byte by some dividing and modulo operation.

Another important piece is encoding 7 3-character strings as compactly as possible, and splitting them by offsets (which are encoded in the previosuly mentioned big number in base 8 ;-)

One extra squeeze is finding the shortest way to concatenate strings.

It's ''.join(), but if you are using it twice (or more), you save space by doing j=''.join and using j later.

Last one: Instead of defining a function, do a lambda:

def seven_seg(x): return .....

seven_seg=lambda x:

6 chars less!

And here is the shortest code I got so far (121 characters, requires python 2.4. On 2.2, it has to be 124).

In the contest, some guys are at 120. I don't know how. I can't even guess how ;-)

Update: it turns out I did know how to do it check it out I think it's the first public 120 :-)

BTW: you can't write that using kwrite. Vim works, though.

2005-12-28 09:50

Python Contest

There is a python contest at http://www.pycontest.net/

The task is writing the shortest program to drive a seven-segment LCD thingy.

I have no hope of winning, but here's a helpful hint:

If your code is any longer than this (191 chars), it will not win ;-)

a=' _ '
b='|_|'
c='   '
d='  |'
e=' _|'
f='|_ '
g='| |'
v='agbcddaefaeecbdafeafbaddabbabe'
def seven_seg(x):
        return '\n'.join([eval('+'.join([v[int(l)*3+i]for l in x]))for i in 0,1,2])+'\n'

Note: I edited this item way too many times already ;-)

And yes, I can save two characters moving the return up.

A much uglier, yet much shorter (151) version:

def seven_seg(x):return''.join([''.join(['|    ||__  __ || |  |'[int('a302ho6nqyp9vxvpeow',36)/10**(int(l)*3+u)%10::7]for l in x])+'\n'for u in 0,1,2])

And yes, that pretty much proves you can write ugly python. And I am giving up. A shorter version probably involves a different algorithm, and I can't find any.

I am particularly proud of saving one character by writing 104004334054154302114514332064 as int('a302ho6nqyp9vxvpeow',36).

Also interesting is that the number I was using there originally started with 4 and was exactly the same length written both ways ;-)

Since that number is pretty arbitrary (it's an index table into the "graphics" array), I just shuffled the 1 and the 4. The 0 would have been better but then it didn't work, of course :-)

2005-12-20 12:25

Forgotten Language: Jorf could have been Python (or Ruby)

A long time ago, there was no Internet.

Ok, there was an internet, but I lived outside of it. It was 1992 or 1993, and I only saw my first webpage and send my first email in 1995. And I was perhaps the third person to have an account on a permanently internet-connected box in a 150 km radius.

But that didn't mean I had no access to internet stuff! What I did was buy CDs containing mirrors of repositories like Simtel.net (it was a single CD, too!) and in there you could find hundreds of programs.

Most of them shareware, most of them crap, but every once in a while, there was something cool, like DJGPP (a whole gcc suite for DOS! A real C compiler! For Free!)

At the time, I had a side job writing data entry software for statistics students. They were simple programs that showed a form, where data was loaded, then it did some simple manipulations of the data.

The natural language for that was something like Clipper or DBase, but I didn't have access to them (or a way to learn it. Remember, no Internet).

On one of those Simtel CDs I found Jorf. (Josephine's Recipe Filer). It was a OO language, with an interpreter for DOS or Windows, and it supported stuff that was really advanced for the time, and it made my coding a lot simpler.

Out of nostalgy, I downloaded a copy (yes, it is still there), and ran it in DosBOX (yes, it still works), to check if it was as good as I remembered.

You know what? It is.

In fact, if it had come out 2 or three years later, and as free software instead of shareware... I think it would have been big.

Here are some highlights og the language:

  • OOP
  • Has integrated windowing toolkit (for DOS and Windows)
  • It had an interactive hypertext/windowing tutorial written in itself. In 1993.
  • It looks like a cousin of Python. A freaky cousing, though.
    1. Comments start with |
    2. Strings limited with single or double quotes
    3. Automatic type conversions
    4. Intentation controls flow :-)
    5. No declared data types
    6. Integrated editor and debugger

Sample Hello World:

Demo:Start
  Msg:Add ("Quick Demonstration","Ok")
    Sure you can say "Hello World" in one line of
    C code. But how many punctuation characters
    are required to display a dialog box like this?
  Return (Ok)

That piece of code showed a window with the message in it, and a Ok button.

The funky thing is: in the tutorial, you saw the integrated editor open, and the text of the example start to appear, and then it ran.

That looked like magic at the time :-)

The toolkit supported radio buttons, checkboxes, text entries, all the basics, and it was a thousand times easier than what Turbo Pascal or Turbo C guys battled with at the time.

The author was Wayland Bruns. He lived, in 1993, in Colton, Oregon.

He later seems to have become CTO of a company that designs sync software for Goldmine, Lotus and other such things.

So, he became a suit ;-). However, he was once a guy that wrote, in his software's manual, things like:

JORF Company is just me, Wayland Bruns. I have been working on JORF for six years, and ran out of money three years ago.

Or:

JORF(R) is a new computer language. JORF was created by a Grunt-programmer frustrated by low level math based computer languages that are inappropiate for business data processing.

And you know what? It was the right idea. If he started Jorf in 1987, that means he started it around the same time Perl 1.0, (and the syntax is much nicer ;-). He started it around the same time Guido started Python.

Here's a toast to JORF, which could have been Perl, or Python, or Ruby. But was not.

2005-12-16 14:27

Bound by Smoke I

This is what I understood of Smoke so far. I may be way off, since it is C++ sorcery of a higher level than I'm used to, but I really think I am getting the hang of it (and a bunch of thanks to Richard Dale and Ashley Winters who are the ones that made me understand so far. Any mistakes a re my fault, any good thing is theirs ;-).

This piece is only half of the story, though. Maybe one third.

Concept

Since Smoke's goal is to help you write bindings for languages other than C++, it provides a way to access Qt's API. The original thing about Smoke is that it does so by providing you with a smaller, more dynamic API that maps onto Qt's.

You could write a Qt C++ program using Smoke as the API instead of using Qt's. In fact, you can see it here written by Ashley Winters.

I had to rename it hello.cpp to make it work because that looks like C but may not really be C ;-)

As you can see, the Smoke version is quite a bit more complex than the Qt one. But that's ok, remember that the goal is a binding, which means that what you need to make your life simpler is less API... which is what makes the program more verbose.

Let's examine the Smoke hello.cpp in detail.

One key point is callMethod:

// call obj->method(args)
void callMethod(Smoke *smoke, void *obj, Smoke::Index method, Smoke::Stack args) {
        Smoke::Method *m = smoke->methods + method;
        Smoke::ClassFn fn = smoke->classes[m->classId].classFn;
        fn(m->method, obj, args);
}

If you have programmed in Python or Ruby or a number of other dynamic languages you may guess what this does already.

It takes as arguments a number of things which still have to be explained but the main gist is there is an object, there is a method, there are args, and it ends calling obj->method(args).

The first argument is a Smoke pointer , which is the big object in the Smoke library, created in our program by the init_smoke function.

A Smoke object is a strange beast. It contains a description (in this case, because we got qt_smoke) for the whole Qt API.

You can find classes in it indexed by their names, and methods for each class indexed by their names and types of arguments.

That is why you can have a generic callMethod that will work for any kind of object and for any method in the class, because all of them are somewhere in the Smoke object.

The second argument is void *obj which is the object itself we are manipulating. So, if you are trying to call QLabel::setText, it will have to be a Qlabel* casted as void*.

In the hello.cpp example, we even create these objects using Smoke's API (see later).

The third argument is a Smoke::Index which is what Smoke uses to find the requested method in its method table. This Index we get using the getMethod function, which is the second key piece of code:

// given class-name and mangled function-name, return an unambiguous method ID
Smoke::Index getMethod(Smoke *smoke, const char* c, const char* m) {
        Smoke::Index method = smoke->findMethod(c, m);
        Smoke::Index i = smoke->methodMaps[method].method;
        if(i <= 0) {
                // ambiguous method have i < 0; it's possible to resolve them, see the other bindings
                fprintf(stderr, "%s method %s::%s\n",
                i ? "Ambiguous" : "Unknown", c, m);
                exit(-1);
        }
        return i;
}

Here is an example of a getMethod call, where we are getting QApplication::setMainWidget

Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");

As you can see, we search for the method using strings of the class name and method name. Excapt for that pesky # at the end of setMainWidget#.

That is a basic argument-type-mangling scheme, since there can be more than one QApplication::setMainWidget on the Qt side of the fence, we are saying we want the one that has an object as the first and only argument. Here is the key to the mangling taken from smoke.h:

* The munging works this way:
* $ is a plain scalar
* # is an object
* ? is a non-scalar (reference to array or hash, undef)
*
* e.g. QApplication(int &, char **) becomes QApplication$?

I am not yet completely clear on how this is enough to do all the work (for example, what happens if you have two methods that take different objects as only argument?) but it's what I saw :-)

The last argument, args is of Smoke::Stack type, and it's the tricky one, at least for me.

Here's how it's used in the previous example, QApplication::setMainWidget

// qapp->setMainWidget(l)
Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");
Smoke::StackItem args[2];
smokeCast(smoke, method, args, 1, l, "QLabel");
smokeCastThis(smoke, method, args, qapp, "QApplication");
callMethod(smoke, args[0].s_class, method, args);

A Smoke::Stack is a way to pass the arguments to be used with the method getMethod gave us.

We first create an array of 2 StackItems:

Smoke::StackItem args[2];

Then we assign a value to the second of them:

smokeCast(smoke, method, args, 1, l, "QLabel");

Here l is a pointer to a QLabel. ( Ok, it is really declared as a void* because, remember, we are not using the Qt API, so we have no clue what a QLabel is ;-) and what we are doing is storing in args[1] a casted version of l.

The exact details of why you have to pass smoke and method are not that important, and they seem pretty involved, so I won't try to go there, at least not yet. This has to be done for each argument for the method.

Then we have a similar, yet different line:

smokeCastThis(smoke, method, args, qapp, "QApplication");

This puts the qapp void * in args[0], casted to QApplication. There are tricky C++ reasons why this is done slightly different here than on smokeCast, which I am not 100% sure I get right, so I will keep quiet ;-)

This special case is only for the object to which the method belongs (the this object).

Here is the code for smokeCast and smokeCastThis

// cast argument pointer to the correct type for the specified method argument
// args[i].s_class = (void*)(typeof(args[i]))(className*)obj
void smokeCast(Smoke *smoke, Smoke::Index method, Smoke::Stack args, Smoke::Index i, void *obj, const char *className) {
        // cast obj from className to the desired type of args[i]
        Smoke::Index arg = smoke->argumentList[
                smoke->methods[method].args + i - 1
        ];
        // cast(obj, from_type, to_type)
        args[i].s_class = smoke->cast(obj, smoke->idClass(className), smoke->types[arg].classId);
}

// cast obj to the required type of this, which, dur to multiple-inheritance, could change the pointer-address
// from the one returned by new. Puts the pointer in args[0].s_class, even though smoke doesn't do it that way
void smokeCastThis(Smoke *smoke, Smoke::Index method, Smoke::Stack args, void *obj, const char *className) {
        args[0].s_class = smoke->cast(obj, smoke->idClass(className), smoke->methods[method].classId);
}

But where did we get l or qapp? You can use these same mechanisms to create an object:

void *qapp;
{
    // new QApplication(argc, argv)
    Smoke::Index method = getMethod(smoke, "QApplication", "QApplication$?");
    Smoke::StackItem args[3];
    args[1].s_voidp = (void*)&argc;
    args[2].s_voidp = (void*)argv;
    callMethod(smoke, 0, method, args);

    qapp = args[0].s_class;
}

You get QApplication::QApplication(scalar,undef) which should hopefully map to QApplication::QApplication(argc,argv). You create a Smoke::Stack of 3 items. The first is unset because this is a constructor, so it has no this yet, and the other two are argc and argv.

Then you call it through callMethod, and you get the resulting object via args[0].s_class.

Later you repeat this sort of operation for every method call you want, and you got yourself an application.

The binding side of things

So, how do you use this to bind your language to Qt?

Well, you will have to create an object in your language called, for example, QApplication, and map the "missing method" mechanism (your scripting language probably has one) which is used when a method is undefined so that somehow it finds the right Smoke method (mangling the name correctly should be the hard part), then create the Smoke::Stack with the passed arguments, call, get the result, wrap it in some language construct you can use of the side of your language, and pass it back.

It looks involved, and I am sure it's tricky, but at least it only has to be done once unlike on traditional bindings, where you had to do it for every class and for every method.

The traditional solution was to automatically generate the code for such wrapping (like SWIG does). I think Smoke is less error prone.

If I keep on understanding things, there may be a second part of this article, explaining the SmokeBinding class, and perhaps a short example of how to bind Qt into a language (Io is a strong candidate).

The regularity of Io's syntax is probably going to make the binding simpler than most.

2005-12-12 19:47

Ok, so, I am a lazy guy

I just realized I have not learned a whole new real language in almost 5 years.

I have learned a few dialects, some minor stuff to work around or through an obstacle in some project, but I have not written a whole program in anything but python since 2001.

So, since I don't want my brain to turn into muesli, I will now proceed to learn Io .

Nice OO language, prototype-based, which is something I haven't run into outside of JS, I think, a sort of LISPy flavor, with a dash of smalltalk for piquancy.

Maybe I will get really bored with it, maybe not :-)

And yes, the previous post was about having the idea of hacking a Qt binding for it, but it's probably not doable right now, since extending the language is pretty much undocumented, and extending it in C++ is unheard of.

To top that, it uses continuations, which probably cause all kinds of hell on Qt's memory management :-(

2005-10-18 00:46

A simple question

I was trying to do something weird: take one of my PyQt proggies, and compile it into a standalone binary using Jython, koala/qtjava and gjc.

Sadly, it doesn't work :-(

Has anyone ever gotten a rather recent gjc and jython to work together?

The best I can get is this:

System Message: ERROR/3 (<string>, line 9)

Cannot find pygments lexer for language "NULL"

.. code-block:: NULL

  [[email protected] jython]$ gcj -fjni /usr/lib/lib-org-*.so.* --classpath=/usr/java/j2sdk1.4.2_08/jre/lib/ --main=org.python.util.jython /usr/share/java/dom3-xerces-j2.jar /usr/share/java/ant.jar /usr/share/java/servlet.jar /usr/share/java/oro.jar /usr/share/java/jython.jar  -o jython
  org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class   org.w3c.dom.events.EventTarget
  org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.TypeInfo
  org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.html.HTMLElement
  org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.html.HTMLAnchorElement
  org/apache/html/dom/HTMLAnchorElementImpl.class:0: confused by earlier errors, bailing out

This is using gcc-java-3.4.3-22.1 and jython-2.2-0.a0.2jpp

I mean, this is completely unnecessary, but it would be a nice hack :-)

Contents © 2000-2018 Roberto Alsina