Schindlers Lifte, das muss man einfach bloggen ;-)
Pages tagged as ‘antizen’
Python Wishlist
- new, non-flat stdlib that follows PEP 8
- absolute imports only
- coroutines
- assignments as expressions
- better scoping
- a with-statement that executes blocks instead of wrapping code
One Code Styleguide Per Programming Language Is Not Silly
I love Python but there are things where I look at Ruby or Java and want to switch right away. What do have Ruby and Java in common? Nearly all of their libraries follow the same function/method/class naming conventions. foo_bar_baz in Ruby and fooBarBaz in Java. In Python there we have that PEP8 thing, but not even the standard library is PEP8 compatible. Many people tell me that not having the same naming guidelines for all libraries is a problem. I would argue that different naming guidelines among different libraries is a bigger problem than different indentation etc.
The reason for that is that in Python we often subclass clases from other libraries. Just take the threading module as example. Now we are forced to use different names in your own libraries/code too. Now one has to start thinking about the names of methods. (Is is get_some_foo() or some_foo or getSomeFoo()). It becomes even worse if you use mixin classes with one styleguide in a subclass of a class with a different one. I’ve seen people using the `DictMixin` in classes with camel case method names. But even the lowercase names are not coherent. Is it iteritems or iter_items? This example might be answered easily because I never saw iter_items but I saw countless occurrences of both getcurrentuser and get_current_user.
The more different name styles in a code the more I have to use dir(), help(), external documentation or the ipython source introspection. And that’s not the fault of the library developers, it starts with the python language itself. The internal types are all lowercase although they are classes. It’s true that this is because of backwards compatibility (when dict, list and others were just functions) but a big language change like Python3 would have made changing some of those names possible.
</rant>
Patching Python Tracebacks — Part Two
After posting this I hacked up a “tbtools” module that contains various traceback helper functions to modify tracebacks. You can find it here: tbtools.
It’s far from stable and I want to ask some of the authors of other template engines (namely zzeeek) for their ideas. There is also an example of the werkzeug minitmpl engine with support for tbtools. Now what it does is the following:
It converts this useless traceback of a template engine with code generation:
Traceback (most recent call last):
File "templating.py", line 151, in <module>
print tmpl.render(broken=broken)
File "templating.py", line 120, in render
return u''.join(tuple(tmp['__generate']()))
File "<template>", line 10, in __generate
File "templating.py", line 149, in broken
foo()
File "templating.py", line 148, in foo
raise RuntimeError("nested broken")
RuntimeError: nested broken
Into this:
Traceback (most recent call last):
File "templating.py", line 151, in <module>
print tmpl.render(broken=broken)
File "test.html", line 3, in <module>
<%= broken(item) %>
File "templating.py", line 149, in broken
foo()
File "templating.py", line 148, in foo
raise RuntimeError("nested broken")
RuntimeError: nested broken
Because Python locks the traceback object a minimal C module is required. ctypes also doesn’t work because the python API doesn’t export the struct we need.
Patching Python Tracebacks
So what’s one of the greatest features Python has? Correct. Exceptions and Tracebacks. Especially the latter is a cool thing but it’s also limited. Say you have a template engine that generates code before executing the template. So if an runtime error occurs the message is correct but the line number points to the generated code and not the source line.
So how to fix? The plan basically is:
- Have a generated line <-> source line mapping somewhere
- catch exceptions that happen in the code execution and skip the first one or two frames until you reach the frame where the generated code is
- reraise the exception in a isolated namespace but in the new line (hackish)
- patch the traceback and inject your new traceback from the isolated frame (nearly impossible)
Now the mapping is the smallest problem, as well as the frame skipping (just access tb_next a couple of times). The real problems is reraising the exception in a different line and patching that new traceback into the traceback chain. There are solutions but it requires a C extension module and some code that you better hide. Still, it works :D
If you want to see how it’s implemented check those files:
So yes. You can patch tracebacks and customize them, but it’s deeper magic.
What about a PHP-like Python Application?
Actually there are so many people that want to have a PHP like Python, so why not give it a try? Using Mako and Werkzeug it’s easy to create such an application.
Basically all what I did is processing *.mako as Mako template that knows about the current request and response, and serving everything else as static file. Inside the templates you have access to GET, POST, FILES, COOKIES, REQUEST and RESPONSE and can use all of mako’s power.
Example template:
<%
data = GET.get('data', 'World')
%>
<title>Hello ${data|h}</title>
<link rel="stylesheet" href="style.css" type="text/css">
<h1>Hello ${data|h}</h1>
<p>
Welcome on the PHP like Python application.
</p>
<p>
Say hello <a href="?data=Daddy">daddy</a> or show
<a href="missing">a missing page</a>. There is also
<a href="special.mako">a special page</a>.
</p>
The full sourcecode is available here: phplikepy
It needs the SVN version of Werkzeug, Mako and Python2.4 or Python 2.3 with wsgiref.
Implicit Python Properties
Everybody knows that nice property function that uses the python descriptor protocol to call functions when getting or settings values. But the disadvantage: For read/write properties it requires two helper functions which causes code that looks like this:
class Foo(object):
def __init__(self, initial):
self.foo = initial
def _get_foo(self):
return self._foo
def _set_foo(self, value):
self._foo = value
foo = property(_get_foo, _set_foo, 'The docstring')
del _get_foo, _set_foo
I thought about introspecting the call frame and retrieving wrapped functions but because the python bytecode uses STORE_FAST/LOAD_FAST opcodes this doesn’t work. But with a little bit of bytecode hacking I got a decorator working that is both magical and implicit (and quite slow on application startup, but equally fast at execution because it just creates a normal property). It looks like this when in use:
class Foo(object):
def __init__(self, initial):
self.foo = initial
@autoproperty
def foo():
'''The docstring'''
def fget(self):
return self._foo
def fset(self, value):
self._foo = value
The module that implements this decorator is available here: autoproperty.py
It’s cool to see what’s possible with python, even if it makes no sense and nobody would ever use it :D