<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web-In-Sight &#187; python</title>
	<atom:link href="http://web-in-sight.nl/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://web-in-sight.nl</link>
	<description>Inzicht in internet en werken</description>
	<lastBuildDate>Mon, 30 Jan 2012 09:00:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>nexec, distributed terminal commands</title>
		<link>http://web-in-sight.nl/2011/03/20/nexec-distributed-terminal-commands/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=nexec-distributed-terminal-commands</link>
		<comments>http://web-in-sight.nl/2011/03/20/nexec-distributed-terminal-commands/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 09:01:19 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[CLI]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[sysadmin]]></category>

		<guid isPermaLink="false">http://www.gp-net.nl/?p=45</guid>
		<description><![CDATA[Update: since there&#8217;s been a change in pylib&#8217;s netexec that breaks nexec.py I&#8217;ve updated the script. Click here to download the newest version! I Developed this commandline tool to make it easier to maintain stuff on a groing amount of &#8230; <a href="http://web-in-sight.nl/2011/03/20/nexec-distributed-terminal-commands/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!--TOC--><em><strong>Update</strong>: since there&#8217;s been a change in <a href="http://codespeak.net/execnet/">pylib&#8217;s netexec</a> that breaks nexec.py I&#8217;ve updated the script. <a href="http://www.gerardjp.com/wp-content/uploads/2008/06/nexec-1.1.zip">Click here to download the newest version</a>!</em></p>
<p>I Developed <a href="http://www.gerardjp.com/wp-content/uploads/2008/06/nexec-10.tgz">this</a> commandline tool to make it easier to maintain stuff on a groing amount of servers. <strong>nexec </strong>is a tool that can help you gather ad-hoc info when it&#8217;s needed quickly. <strong>nexec</strong> is written in python, it depends on <a title="Py lib from codespeak" href="http://codespeak.net/py/dist/" target="_blank">codespeak&#8217;s pylib</a>, and does all it&#8217;s communication over SSH. The only thing you need on &#8216;the other side&#8217; is a python interpreter. (that&#8217;s right, no further libs or modules). Oh, and you need your SSH key pairs in place, otherwise it&#8217;s &#8216;password galore&#8217;.</p>
<p>Of course, the bash-ers out there would say that the same functionality can be achieved with a for loop. True! But that&#8217;s exactly what <strong>nexec</strong> does for you so you can concentrate on what you&#8217;re doing remote. And ofcourse you can have nightly cron-ed scripts in place that do this every day. But <strong>nexec</strong> aids in all those things that are not &#8216;in place&#8217;.</p>
<h2>Examples</h2>
<p>Before getting into any details, first some tricks of the &#8216;nexec&#8217; trade to get you warmed up &#8230; <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Are my server systems running the proper time:</p>
<pre>$ ./nexec.py -n 'server1 server2  server3' -c 'date'
server2: Fri May 23 16:21:18 CEST 2008
server1: Fri May 23 16:21:17 CEST 2008
server3: Fri May 23 16:21:19 CEST 2008</pre>
<p>See if all your servers are running ssh protocol 2 only:</p>
<pre>$ ./nexec.py -n 'server1 server2  server3' -c 'cat /etc/ssh/sshd_config | grep -v ^# | grep Protocol' | sort
server1: Protocol 2
server2: Protocol 2
server3: Protocol 2</pre>
<p>(You could also do a &#8220;-n ALL&#8221;, then nexec reads your server list from a config file. More on that in a minute)</p>
<p>Get a list with all different kernel versions that are running on your systems:</p>
<pre>$ ./nexec.py -n ALL -q -c 'uname -r' | sort -u
2.6.18-5-686
2.6.18.8-xen</pre>
<p>(The &#8220;-q&#8221; parameter suppresses the &#8216;per line&#8217; hostname prefix).</p>
<p>See if &#8216;john&#8217; is a member of the sudo group on servers 1 and 2:</p>
<pre>./nexec.py -v -q -n 'server1 server2' -c 'cat /etc/group | grep sudo | grep -v john'
Host:  server1.example.com
sudo:x:27:jane,jerry
Host:  server2.example.com
sudo:x:27:jane</pre>
<p>(The&#8221;-v&#8221; adds the &#8220;Host: &#8230;.&#8221; header, per server output. Note that &#8220;-v&#8221; and &#8220;-q&#8221; are interchangeable)</p>
<p>As you can see, by combining local and remote greps and using the -v and/or -q parameters there are some interesting ways to obtain live data from your server.</p>
<p>Lets look at the configuration.</p>
<h2>Configuration</h2>
<p>Run without parms, <strong>nexec</strong> shows help output. Furthermore the only two files <strong>nexec</strong> depends on (and not even necessarily) are a &#8220;~/.nexec.conf&#8221; file and a possible &#8220;~/.ssh/config&#8221;.</p>
<p>The &#8220;~/.nexec.conf&#8221; does not contain much, just &#8220;key: value&#8221; pairs under a hosts section. The actual &#8216;value&#8217; is only obtained when using the &#8220;-v&#8221; option to display the header above each server output &#8220;<em>Host: server1.example.com</em>&#8220;.</p>
<pre>$ cat .nexec.conf
[hosts]
server1:   server1.example.com
server2:   server2.example.com
#server3:   server3.example.com</pre>
<p>Other then that, <strong>nexec</strong> simply expands &#8220;-c ALL&#8221; to all available keys under the &#8220;[hosts]&#8221; section, skipping the usual hashsign &#8216;#&#8217; being a comment. In this case the &#8220;ALL&#8221; parameter given to &#8220;-c&#8221; would be expanded to &#8220;server1 server2&#8243; in the previous example. After that the full name is taken from the &#8220;~.ssh/config&#8221; file.</p>
<pre>$ cat .ssh/config
# server1
Host server1
Hostname server1.example.com
User john
Port 4321
# server2
Host server2
Hostname server2.example.com
User john
<a href="http://www.gerardjp.com/wp-content/uploads/2008/06/nexec-1.0.tgz"></a>Port 4321</pre>
<p>I deliberately left all that in there so other usernames and port numbers can be specified. Besides, that&#8217;s all access related and shouldn&#8217;t be anywhere else anyway.</p>
<p>Then one more note on the &#8220;-q&#8221; and &#8220;-v&#8221; option. What they basically do is mark (or suppress mark&#8217;s) on the output that get&#8217;s returned. So you can mark output per server adding the &#8220;-v&#8221;, and output get&#8217;s marked per line (default) if not suppressed with &#8220;-q&#8221;.</p>
<h2>Download</h2>
<p>The script can be downloaded here: <a href="http://www.gerardjp.com/wp-content/uploads/2008/06/nexec-10.tgz">nexec-10.tgz</a></p>
<p>Set the &#8216;execute&#8217; bit with chmod and put it in your $PATH somewhere. And for the &#8220;ALL&#8221; parameter, set up the config file as the example shows in the <a title="TOC: Configuration" href="#toc-configuration">Configuration</a> section.</p>
<h3>Gotcha&#8217;s</h3>
<p>Escape your backticks with with a backslash if you want remote expansion.</p>
<p>This runs the uname part locally and then sends the command to the other side.</p>
<pre>$ nexec.py -n ALL -c "date | mailx -s `uname -n` root"</pre>
<p>This also executes the uname command on the other side.</p>
<pre>$ nexec.py -n ALL -c "date | mailx -s \`uname -n\` root"</pre>
<h2>Todo</h2>
<p>There&#8217;s two things still on my list.</p>
<ul>
<li>Catch a non-existing hostname when taken from the &#8220;.ssh/config&#8221; file. The script breaks if a host is not resolvable. (Being a sysadmin you should know what your doing .. <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </li>
<li>Make the &#8220;<em>Host: server1.example.com</em>&#8221; header not show when no output is returned from a host. That is when using the &#8220;-c ALL&#8221;.</li>
</ul>
<p>New tricks might be added in the future, so if there&#8217;s stuff that you think should be added drop me a reply.</p>
<p>That&#8217;s all folks &#8230;</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2011%2F03%2F20%2Fnexec-distributed-terminal-commands%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2011/03/20/nexec-distributed-terminal-commands/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Setting up your own custom python userlib</title>
		<link>http://web-in-sight.nl/2010/09/28/setting-up-your-own-custom-python-userlib/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=setting-up-your-own-custom-python-userlib</link>
		<comments>http://web-in-sight.nl/2010/09/28/setting-up-your-own-custom-python-userlib/#comments</comments>
		<pubDate>Tue, 28 Sep 2010 11:29:04 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[script]]></category>

		<guid isPermaLink="false">http://www.gerardjp.com/?p=1222</guid>
		<description><![CDATA[Never were able, when in need of a simple script, to do a quick finish. Always end up copying code from your other scripts for such a simple thing as sending an email? Then this might be something for you. <a href="http://web-in-sight.nl/2010/09/28/setting-up-your-own-custom-python-userlib/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I recently wrote a a script that monitors file/dir changes, acts on a change and notifies me via <a title="Notify OSD" href="https://wiki.ubuntu.com/NotifyOSD" target="_blank">notify-osd</a> or email. While writing this I came to the conclusion that a simple thing as sending an email easily kicks up to at least 10 lines of code and some settings. So it was time to find an easier way for this.</p>
<p>You can of course create distributable modules or packages but with simple local workstation like maintenance this seems overkill &#8230; yep, you are allowed to disagree <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>So, we need a place to put code that is easy to import in our used scripts and it should be easy to maintain. Dropping it in my homedir in a directory called &#8220;.python&#8221; seems well suited for this.</p>
<h2>The Setup</h2>
<p>Create the directory &#8220;.python&#8221; in your $HOMEDIR. Then add a line to your &#8220;.bashrc&#8221; to export $PYTHONPATH so when you do an import in python it is actually found.</p>
<pre>PYTHONPATH=~/.python
export PYTHONPATH</pre>
<p>Python picks up the paths from this variable and adds them to &#8220;sys.path&#8221; so there&#8217;s no need to add the path in every script you make. In individual cases you can of course do it like it is <a title="Appending to Your Python Path" href="http://www.johnny-lin.com/cdat_tips/tips_pylang/path.html" target="_blank">explained here</a>.</p>
<p>Then create a file in &#8220;~/.python/&#8221; for instance &#8220;userconfig.py&#8221;.</p>
<p>To make it available in your script simply import it and execute one of your functions.</p>
<pre>import userconfig as mystuff
mystuff.function(with, parms)</pre>
<p>I&#8217;ll give an example of a userconfig.py function to easily send out emails.</p>
<pre>import os
import sys
import pynotify
import smtplib
from email.mime.text import MIMEText

# Settings &lt;&lt; These are the "Sensible defaults" <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />
MAILSERVER = 'localhost'
MAILFROM = 'somebody'
MAILTO = 'you@yourdomain.com'

def send_mail(subject, body, fro=MAILFROM, to=MAILTO):
    """
    Stripped function to send mail from local code
    """
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = fro
    msg['To'] = to

    s = smtplib.SMTP(MAILSERVER)
    s.sendmail(msg['From'], msg['To'], msg.as_string())
    s.close()</pre>
<p>The code above composes and sends an email. Doing all this in the script you are writing results in quite a bunch of these lines ending up in every one of your scripts.</p>
<h2>Sensible defaults</h2>
<p>How about not having to enter those &#8216;always the same defaults&#8217;? Well &#8230; When using the setup described in this article most can be omitted and parameters only need to be added when you deviate from your sensible defaults.</p>
<p>For argument sake I&#8217;ll give an example script that just mails something and calls the &#8220;send_mail()&#8221; function.</p>
<pre>import userconfig as user

body = """Some multiline triple quoted body mass\n\nwith lot's of

funky stuff in it."""

fish = 'whale'
subject = 'something fishy: %s' % fish

user.send_mail(subject, body)</pre>
<p>So that&#8217;s only 1 line for the actual &#8216;send my mail&#8217; command. Should you want a different FROM address then you just change the function call to this.</p>
<pre>user.send_mail(subject, body, 'notme')</pre>
<p>Thus, add parms when you need them or rely on your &#8220;Sensible Defaults&#8221; from your userconfig.py.</p>
<p>You can build a world of functionality in your &#8220;~/.python&#8221;. Import and use system wide installed packages in there, test if they are present on your system, etc.</p>
<h2>Gotcha&#8217;s</h2>
<p>When using scripts under CRON you need to add the PYTHONPATH to the  definition like below because CRON does not read your  &#8220;.bashrc&#8221;.</p>
<pre>*/1 * * * *  (PYTHONPATH=~/.python; /path/to/your/script.py)</pre>
<p>NB: Mind the parenthesis and semicolon on the line above.</p>
<p>Besides the fact that you should watch for name clashes, I can&#8217;t think of any other gotcha&#8217;s. If you do, then please teach me something and drop me a line <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>GrtzG</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2010%2F09%2F28%2Fsetting-up-your-own-custom-python-userlib%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2010/09/28/setting-up-your-own-custom-python-userlib/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django: get distinct field selection</title>
		<link>http://web-in-sight.nl/2009/12/16/django-get-distinct-field-selection/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=django-get-distinct-field-selection</link>
		<comments>http://web-in-sight.nl/2009/12/16/django-get-distinct-field-selection/#comments</comments>
		<pubDate>Wed, 16 Dec 2009 13:11:55 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.gerardjp.com/?p=1131</guid>
		<description><![CDATA[A function for Django that is a simple and safe solution to get a distinct (group by) field selection from a model. <a href="http://web-in-sight.nl/2009/12/16/django-get-distinct-field-selection/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I recently have been dinging around in <a href="http://www.djangoproject.com/" target="_blank">Django</a>&#8216;s <a href="http://en.wikipedia.org/wiki/Object-relational_mapping" target="_blank">ORM</a> because I needed a certain &#8216;fields &#8230; group by&#8217; selection. I was very impressed by the logic that the Django developers put in there. Especially the joins and aggregations that have been released with version 1.1. However, after a hours of testing and googling, I was not able to solve my problem with it. I needed a distinct selection like this:</p>
<pre>SELECT id, name FROM myapp_model WHERE owner_id='1' GROUP BY name;</pre>
<p>This would have been (easily) possible except for the WHERE clause on ownership that screwed me. Since the SQL statement was that simple I decided to write a bypass function that get&#8217;s the data straight from the database. It&#8217;s readonly access and I dont do anything more with it then create a list of links. Even more so, from a <a href="http://en.wikipedia.org/wiki/KISS_principle" target="_blank">KISS</a> point of view this code is easier to read back after several months.</p>
<p>And for reusability I turned it in to a function. It takes the model and the distinct field, and for my purpose the needed ownership as parameter:</p>
<pre>from django.db import connection

def get_latest_objects(model_name=None, distinct_field=None, user_id=None):
    """ Get lastest distinct selection (as tuples) of a given model
    """
    model_name = model_name.lower()
    query = ("select id,%(distinct_field)s from myapp_%(model_name)s "
                "where owner_id='%(user_id)s' "
                "group by %(distinct_field)s;") % {
                    'model_name': model_name,
                    'distinct_field': distinct_field,
                    'user_id': user_id,
                    }
    cursor = connection.cursor()
    cursor.execute(query)</pre>
<p>There&#8217;s not much to it, I just hope it helps you to avoid &#8216;the hard way&#8217;. And it helps to keeps your view methods more readable because you dont need anything more then this:</p>
<pre>latest_objects = get_latest_objects(model_name='MyModel',
                        distinct_field='name',
                        user_id=request.user.id)</pre>
<p>What is returned (latest_objects) is a list of tuples that you can unpack in your templates straight away. Note that there&#8217;s no error checking in there, because I know what I&#8217;m doing <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Besides, worst case you get back an empty list and possible errors are caught when you write your tests. You do write those don&#8217;t you?</p>
<p>Grtz Gerard.</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2009%2F12%2F16%2Fdjango-get-distinct-field-selection%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2009/12/16/django-get-distinct-field-selection/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django: QueryScreener, a record level ownership development auditor</title>
		<link>http://web-in-sight.nl/2009/10/26/django-queryscreener-a-record-level-ownership-development-auditor/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=django-queryscreener-a-record-level-ownership-development-auditor</link>
		<comments>http://web-in-sight.nl/2009/10/26/django-queryscreener-a-record-level-ownership-development-auditor/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 16:41:12 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.gerardjp.com/?p=1049</guid>
		<description><![CDATA[QueryScreener is a middleware development tool that helps to avoid unwanted data disclosure once you go into production. It monitors queries to the models and warns you when queries are executed that lack a ownership clause.  <a href="http://web-in-sight.nl/2009/10/26/django-queryscreener-a-record-level-ownership-development-auditor/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>During the development stage of a Django app I&#8217;m working on I was exploring how to best implement rowlevel user ownerships. There are several ways to overwrite methods on <a title="Object Managers" href="http://docs.djangoproject.com/en/dev/topics/db/managers/" target="_blank">object managers</a> and even the Django admin interface is properly configurable to take a ownership from &#8220;request.user&#8221;.</p>
<p>But since wrongfull data disclosure is absolutely unacceptable I was still afraid that I would miss something somewhere. A nice example I ran into was populating a dropdown list in a form, where all records were visible instead of only those owned by the logged in user.</p>
<p>That got me thinking and eventually I wrote this small but sweet piece of <a title="Middleware" href="http://docs.djangoproject.com/en/dev/topics/http/middleware/" target="_blank">middleware</a>. Further elaboration below the code.</p>
<pre class="python" name="code">from django.db import connection
import re

"""
QueryScreener is a middleware development tool. This tool helps to avoid
unwanted data disclosure once you go into production.

It monitors queries to the models in your model_list and warns you when queries
are executed that do not contain a ownership where clause. And thus can be a
potential data disclosure hazard.

It requires a owner attribute in your model definition, e.g:

    owner = models.ForeignKey(User, editable=False)

Edit the 'model_list' below for what models should be monitored. And add
QueryScreener to MIDDLEWARE_CLASSES in you settings.py

Note: This can/should only be used while running Django's testserver command
with e.g: ./manage.py runserver 192.168.1.81:8000
"""

class QueryScreener(object):

    model_list = ['myapp_customer', 'myapp_order', 'myapp_product']

    def process_view(self, request, view_func, view_args, view_kwargs):
        if len(connection.queries) &gt; 0:
            query_parse(connection.queries, self.model_list, 'process_view')

    def process_response(self, request, response):
        if len(connection.queries) &gt; 0:
            query_parse(connection.queries, self.model_list, 'process_response')
        return response

def query_parse(self, model_list, caller_process):

    for query in connection.queries:
        for modelname in model_list:
            modelstring = 'FROM `'+modelname

            if re.search(modelstring, query['sql']) and not \
                re.search(r'^SELECT.\(1\).AS', query['sql']):

                reg = re.compile(r'^SELECT.*WHERE.*owner.*(ORDER BY.*)?$',
                                    re.DOTALL)

                if not reg.search(query['sql']):
                    print ('&lt;&lt;&lt; WARNING &gt;&gt;&gt; Query execution without ownership '
                            'clause, called from "' + caller_process + '"')
                    print query['sql']

            if re.search(r'^SELECT.\(1\).AS.`a`.FROM.*WHERE.*$', query['sql']):
                print ('&lt;&lt;&lt; Django Farted &gt;&gt;&gt;')
#                print query['sql']</pre>
<p><em>Update1: The &#8216;ORDER BY&#8217; in the regex needs to be optional.</em><br />
<em>Update2: Django does a &#8216;try update&#8217; in save_base() without owner (seperated the select statement)<br />
</em></p>
<p>The comment in the code above sums up how to get it working. What it does is print a warning and the query in question that does not respect ownership. If enabled while developing just keep track of your console output for:</p>
<pre>&lt;&lt;&lt; WARNING &gt;&gt;&gt; Query execution without ownership clause, called from "process_response"</pre>
<p>Should you  have suggestion, criticism, or words of admiration then please, do tell me <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>GrtzG</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2009%2F10%2F26%2Fdjango-queryscreener-a-record-level-ownership-development-auditor%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2009/10/26/django-queryscreener-a-record-level-ownership-development-auditor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8220;Walk-around&#8221; for pre lighttpd-1.4.23 feature &#8220;fix-root-scriptname&#8221;</title>
		<link>http://web-in-sight.nl/2009/08/15/walk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=walk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname</link>
		<comments>http://web-in-sight.nl/2009/08/15/walk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 09:41:24 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.gerardjp.com/?p=923</guid>
		<description><![CDATA[Walk-around should ofcourse be workaround but it feels more like a walk around &#8230; Today I &#8216;moved&#8217; the first version of my application from the Django development setup (run via &#8220;./manage runserver&#8221;) to a lighttpd/fcgi production setup. Been cleaning up &#8230; <a href="http://web-in-sight.nl/2009/08/15/walk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Walk-around should ofcourse be workaround but it feels more like a walk around &#8230; <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Today I &#8216;moved&#8217; the first version of my application from the Django development setup (run via &#8220;./manage runserver&#8221;) to a lighttpd/fcgi production setup. Been cleaning up code and concluded I wanted the app straight on the &#8216;/&#8217; or docroot with a simple config like this:</p>
<pre>HTTP["host"] == "example.int" {
    fastcgi.server = (
        "/" =&gt; (
            "main" =&gt; (
                "host" =&gt; "192.168.1.2",
                "port" =&gt; 8000,
                "check-local" =&gt; "disable",
                #"fix-root-scriptname" =&gt; "enable",
            )
        ),
    )
}</pre>
<p>After a lot of messing around and not getting the aformetioned setup to work I found <a href="http://redmine.lighttpd.net/issues/show/729" target="_blank">the &#8216;PATH_INFO&#8217; problem</a>. The easiest way is to have <a href="http://www.lighttpd.net/2009/6/19/1-4-23-leaving-the-nest" target="_blank">lighttpd version 1.4.23</a> running, because then you can enable:</p>
<pre>    "fix-root-scriptname" =&gt; "enable",</pre>
<p>And therewith actually acces your app on &#8220;/&#8221; instead of &#8220;/dd&#8221; as shown in the below example under the line with &#8220;fastcgi.server (&#8220;.</p>
<p>The problem however is that lighttpd version 1.4.23 is not apt-get-able yet and I like my systems clean. So here&#8217;s the somewhat cumbersome but functional example that works:</p>
<pre>HTTP["host"] == "example.int" {
    fastcgi.server = (
        "/dd" =&gt; (
            "main" =&gt; (
                "host" =&gt; "192.168.1.2",
                "port" =&gt; 8000,
                "check-local" =&gt; "disable",
            )
        ),
    )

    url.rewrite-once = (
        "^(/.*)$" =&gt; "/dd/$1",
    )
}</pre>
<p>What happens is that lighttpd passes an incoming request like &#8220;/url_to_something&#8221; to your fcgi proces, but fcgi is on &#8220;/dd/url_to_something&#8221;. So with the rewrite rule we make sure the &#8220;/dd&#8221; prefix is added, other wise it would not be passed to the fcgi handler.</p>
<p>The problem however is that you should modify all your in-app links so they would be prefixed with /dd otherwise you end up with a 404. This can be globally resolved by adding the line below to your Django settings.py</p>
<pre>FORCE_SCRIPT_NAME = ""</pre>
<p>Django then strips of the &#8220;/dd&#8221; prefix just before it gets parsed by your django app URL Dispatcher.</p>
<p>So when lighttpd version 1.4.23 is in the debian and/or Ubuntu repository, you apt get it and adjust your setup in 3 places.</p>
<p>Remove the rewrite rule and the &#8220;dd&#8221; part from your lighttpd host config and disable the FORCE_SCRIPT_NAME in your settings.py. Doing it like this keeps everything running with the least adjustments now and only a little work to be done in the near future.</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2009%2F08%2F15%2Fwalk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2009/08/15/walk-around-for-pre-lighttpd-1-4-23-feature-fix-root-scriptname/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solved python regex raising exception &#8220;unmatched group&#8221;</title>
		<link>http://web-in-sight.nl/2008/07/11/solved-python-regex-raising-exception-unmatched-group/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=solved-python-regex-raising-exception-unmatched-group</link>
		<comments>http://web-in-sight.nl/2008/07/11/solved-python-regex-raising-exception-unmatched-group/#comments</comments>
		<pubDate>Fri, 11 Jul 2008 08:07:40 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[backref]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[exception]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[regex]]></category>

		<guid isPermaLink="false">http://www.gp-net.nl/?p=51</guid>
		<description><![CDATA[If your a regex guru, and you know why you came here, you can go straight to the brief explanation. If not just keep reading. I found a workaround for python bug 1519638. It most definitely will not solve all &#8230; <a href="http://web-in-sight.nl/2008/07/11/solved-python-regex-raising-exception-unmatched-group/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!--TOC-->If your a regex guru, and you know why you came here, you can go straight to the <a href="#toc-brief-explanation">brief explanation</a>. If not just keep reading.</p>
<p>I found a workaround for python bug <a title="issue1519638" href="http://bugs.python.org/issue1519638" target="_blank">1519638</a>. It most definitely will not solve all of the puzzles out there but it stops breaking the sub method for replacing with the use of backrefs.</p>
<h3>The problem</h3>
<p>If you would like to replace this:</p>
<pre>&lt;label for="author"&gt;&lt;small&gt;Name</pre>
<p>With this:</p>
<pre>&lt;label for="author"&gt;&lt;small&gt;Naam</pre>
<p>And you&#8217;re not sure if the &lt;small&gt; tags is there, you would group the chars &#8220;&lt;small&gt;&#8221; and use a question mark for making them optional. BTW, running a replace on just &#8220;Name&#8221; is not allowed because they would mess up other parts of the file in question.</p>
<p><em>Example updated. Thanx dbr!</em></p>
<h3>The solution</h3>
<p>Using a compiled pattern and thus a regex to replace this, a solution might look like this:</p>
<pre>reg = re.compile(r'(&lt;label for="author"&gt;)(&lt;small&gt;)?(Name)', \
    re.VERBOSE | re.MULTILINE | re.DOTALL)
replace = r'\g&lt;1&gt;\g&lt;2&gt;\g&lt;3&gt;'
search = reg.sub(replace, data)</pre>
<p>In this case the replacement string uses backreferences to the groups being the sub expressions within the parenthesis in the search pattern.</p>
<h3>The oops</h3>
<p>However, if the &#8220;&lt;small&gt;&#8221; tag is not there the search command raises an exception.</p>
<pre>$ python regex.py
Traceback (most recent call last):
  File "regex.py", line 14, in &lt;module&gt;
    search = reg.sub(replace, data)
  File "/usr/lib/python2.5/re.py", line 274, in filter
    return sre_parse.expand_template(template, match)
  File "/usr/lib/python2.5/sre_parse.py", line 793, in expand_template
    raise error, "unmatched group"
sre_constants.error: unmatched group</pre>
<p>This happens because the second group represented with &#8220;\g&lt;2&gt;&#8221; in the replacement string returns a &#8220;None&#8221; instead of an empty string. That is (seems) the bug.</p>
<h3>Solving the oops</h3>
<p>This can be resolved by replacing the optional notation &#8220;(&lt;small&gt;)?&#8221; with an alternation &#8220;(|&lt;small&gt;)&#8221; because with the &#8220;&lt;small&gt;&#8221; tag being absent it matches on the empty subexpression. And then it actually returns an empty string so the search command won&#8217;t raise the exception.</p>
<p>In other words &#8230;</p>
<h3>Brief explanation</h3>
<p>When doing a search and replace with sub, replace the group represented as optional for a group represented as an alternation with one empty subexpression. So instead of this &#8220;(.+?)?&#8221; use this &#8220;(|.+?)&#8221; (without the double quotes).</p>
<p>If there&#8217;s nothing matched by this group the empty subexpression matches. Then an empty string is returned instead of a None and the sub method is executed normally instead of raising the &#8220;unmatched group&#8221; error.</p>
<p>That&#8217;s all folks &#8230;</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2008%2F07%2F11%2Fsolved-python-regex-raising-exception-unmatched-group%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2008/07/11/solved-python-regex-raising-exception-unmatched-group/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>A python function to capture an IP address or range</title>
		<link>http://web-in-sight.nl/2008/06/12/a-python-function-to-capture-an-ip-address-or-range/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=a-python-function-to-capture-an-ip-address-or-range</link>
		<comments>http://web-in-sight.nl/2008/06/12/a-python-function-to-capture-an-ip-address-or-range/#comments</comments>
		<pubDate>Thu, 12 Jun 2008 10:39:02 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[address]]></category>
		<category><![CDATA[ip]]></category>
		<category><![CDATA[netmask]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[regex]]></category>

		<guid isPermaLink="false">http://www.gp-net.nl/?p=49</guid>
		<description><![CDATA[I recently needed a function that validates an IP address or network range. Since my python application will pass it as a parameter to iptables it needs to be correct and not &#8216;close to&#8217;. So I dug in &#8230; Validating &#8230; <a href="http://web-in-sight.nl/2008/06/12/a-python-function-to-capture-an-ip-address-or-range/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I recently needed a function that validates an IP address or network range. Since my python application will pass it as a parameter to iptables it needs to be correct and not &#8216;close to&#8217;. So I dug in &#8230;</p>
<p>Validating an IP address or range with just a regex seems like self castigation. I looked at the source code of iptables and it check&#8217;s whether or not 1 octet fits in a byte. With your octet being only valid from 0 up to and 255 it must fit in 1 byte. That method seems ok but when you&#8217;re writing interpretable code the interpreter most likely does a better job then you in checking byte lenght&#8217;s. If it doesn&#8217;t already do it like that when checking int&#8217;s like this:</p>
<pre>if not (0 &lt;= int(octet) &lt;= 255):</pre>
<p>Digesting all that information I wrote the function below that takes an IP address or range and simply returns &#8216;True&#8217; or &#8216;False&#8217;.</p>
<pre>def check_address(address):

    if not (re.search('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(|\/\d{1,2})$', address)):
        return False

    if (address.count('/') == 1):
        (ip, mask) = address.split('/')
        if not (0 &lt;= int(mask) &lt;= 32):
            return False
    else:
        ip = address

    for octet in ip.split('.'):
        if not (0 &lt;= int(octet) &lt;= 255):
            return False

    return True</pre>
<p>IMHO, it&#8217;s very safe and very readable. You know &#8230; <a title="KISS" href="http://en.wikipedia.org/wiki/KISS_principle" target="_blank">KISS</a>.</p>
<p>If you have suggestions on how to do this more pythonesque I&#8217;m very curious to here them so please drop me a line.</p>
<p>GrtzG</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2008%2F06%2F12%2Fa-python-function-to-capture-an-ip-address-or-range%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2008/06/12/a-python-function-to-capture-an-ip-address-or-range/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Watch command does not support &#8216;utf-8&#8242; characters</title>
		<link>http://web-in-sight.nl/2008/05/22/watch-command-does-not-support-utf-8-characters/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=watch-command-does-not-support-utf-8-characters</link>
		<comments>http://web-in-sight.nl/2008/05/22/watch-command-does-not-support-utf-8-characters/#comments</comments>
		<pubDate>Thu, 22 May 2008 05:42:07 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[watch]]></category>

		<guid isPermaLink="false">http://www.gp-net.nl/?p=44</guid>
		<description><![CDATA[This issue was found on Ubuntu 8.04 (Hardy Heron) Desktop. If your coding in python and trying to write utf-8 encoded files, save yourself some time. The &#8216;watch&#8217; command on Ubuntu Hardy does not display utf-8 characters when watching the &#8230; <a href="http://web-in-sight.nl/2008/05/22/watch-command-does-not-support-utf-8-characters/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This issue was found on Ubuntu 8.04 (Hardy Heron) Desktop.</p>
<p>If your coding in python and trying to write utf-8 encoded files, save yourself some time. The &#8216;watch&#8217; command on Ubuntu Hardy does not display utf-8 characters when watching the contents of a file that contains them.</p>
<p>The file contents with the utf-8 character:</p>
<p><em>&#8220;This line shows a &#8216;»&#8217; (Guillemot right) and a &#8216;®&#8217; (Trademark sign).&#8221;</em></p>
<p>The command that <strong>doesn&#8217;t</strong> work:</p>
<pre>watch cat /path/to/some/file.txt</pre>
<p>Shows this:</p>
<pre>Every 5.0s: cat file.txt 						Thu May 22 07:36:22 2008

"This line shows a '' (Guillemot right) and a '' (Trademark sign)."</pre>
<p>The command that <strong>does</strong> work:</p>
<pre>while true ; do cat /path/to/some/file.txt ; sleep 2 ; clear ; done</pre>
<p>Shows this:</p>
<pre>"This line shows a '»' (Guillemot right) and a '®' (Trademark sign)."</pre>
<p>So if your learning proper encoding of utf-8 in python this does save you time when you&#8217;re troubeshooting your code .. <img src='http://web-in-sight.nl/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>GrtzG</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2008%2F05%2F22%2Fwatch-command-does-not-support-utf-8-characters%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2008/05/22/watch-command-does-not-support-utf-8-characters/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My First Python: chkdir.py</title>
		<link>http://web-in-sight.nl/2007/11/03/my-first-python-chkdirpy/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=my-first-python-chkdirpy</link>
		<comments>http://web-in-sight.nl/2007/11/03/my-first-python-chkdirpy/#comments</comments>
		<pubDate>Sat, 03 Nov 2007 12:07:02 +0000</pubDate>
		<dc:creator>Gerard</dc:creator>
				<category><![CDATA[All ENGLISH articles]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[chkdir]]></category>
		<category><![CDATA[directory]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.gp-net.nl/2007/11/03/my-first-python-chkdirpy/en/</guid>
		<description><![CDATA[First of all I would like to thank my -fun to work with- colleague JW for his elaborate teachings on how to properly code in python. Thanks a lot! The script: chkdir.py Description The script emails differences on a directory&#8217;s &#8230; <a href="http://web-in-sight.nl/2007/11/03/my-first-python-chkdirpy/">Lees verder <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>First of all I would like to thank my -fun to work with- colleague JW for his elaborate teachings on how to properly code in python. Thanks a lot!</p>
<p>The script: <a href="http://www.gerardjp.com/wp-content/uploads/2007/11/chkdir.py">chkdir.py</a></p>
<p><strong>Description</strong><br />
The script emails differences on a directory&#8217;s listing. So if you&#8217;re monitoring changes in a specific directory you can use this script. Run it under CRON every 5 minutes and on any changes you&#8217;ll get an email looking like this:</p>
<p><a href="http://www.gerardjp.com/wp-content/uploads/2007/11/screenshot-changes-in-home-gerard-files-python-chkdir-testdir-kontact1.png"><img class="alignnone size-full wp-image-535" title="screenshot-changes-in-home-gerard-files-python-chkdir-testdir-kontact1" src="http://www.gerardjp.com/wp-content/uploads/2007/11/screenshot-changes-in-home-gerard-files-python-chkdir-testdir-kontact1.png" alt="screenshot-changes-in-home-gerard-files-python-chkdir-testdir-kontact1" width="412" height="329" /></a></p>
<p><strong>Configuration</strong><br />
Not much. Change the email related variables in the config section in the header of the script. Example:</p>
<pre># Config section
MAILSERVER = 'localhost'
MAILFROM = 'somebody'
MAILTO = 'gerard'</pre>
<p><strong>Explanation</strong><br />
The script takes the absolute path to the directory that needs monitoring as the first argument. And an optional second argument called &#8216;verbose&#8217; if you want specifics. If all goes well during execution, and run without &#8216;verbose&#8217;, the script only outputs via email. The reason? This precludes your CRON daemon from doing unnecessary logging.</p>
<p>The previous listing of a directory, to which the new listing is compared, is stored in a file. The file is given the same name as the monitored directory. It has a suffix called &#8216;.dirlist&#8217; and the file is stored in the parent directory. See &#8216;Issues&#8217; at the end for more details.</p>
<p>The script only sends output to STDOUT during the initial stage when you start to monitor a directory.  The script cops out when: the parent directory is not writable, the given path does not exist, creating the first snapshot (dirlist) of a directory, etc. Since this is all done from the commandline at first, knowing what&#8217;s going at this stage is normal. After that the script turns quiet.</p>
<p><strong>Examples</strong><br />
Some startup failure messages:</p>
<pre>$ ./chkdir.py /tmp/
Sorry, I do not have permission to write in parent directory</pre>
<pre>$ ./chkdir.py /home/gerard/temp/testdir/
No previous listfile, created new one, will do diff on next run</pre>
<p>As said, after that it turns quiet, except when using &#8216;verbose&#8217;. Then your commandline output looks like this:</p>
<pre>./chkdir.py /home/gerard/temp/testdir verbose
New list
['Screenshot-GP-net \xe2\x80\xba Dashboard.png', 'GP-net \xe2\x80\xba Dashboard boe.png']

Old list
['Screenshot-GP-net \xe2\x80\xba Dashboard.png', 'GP-net \xe2\x80\xba Dashboard boe.png', 'Screenshot-Google.png']

Removed:
set(['Screenshot-Google.png'])</pre>
<p>The funky escaped values are unicode utf-8 characters. Like I said if you use verbose you get specifics. Not to worry though, any characters allowed in your file or directory names are correctly displayed when you get the output emailed (assuming your mail client supports them).</p>
<p><strong>Issues</strong><br />
Due to storing the file with the previous list in its parent directory it currently is not possible to monitor the &#8216;root&#8217; of you file system. A different approach would be to generate an md5 hash of the path and put the &#8216;.dirlist&#8217; file in &#8220;/tmp&#8221;. But that would result in people being able to obtain info on file system parts where they might not be allowed themselves.</p>
<p>Any suggestions on a better approach to store the previously made listing are welcome.</p>
<p>Regards,</p>
<p>Gerard.</p>
<div class="AWD_like_button "><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fweb-in-sight.nl%2F2007%2F11%2F03%2Fmy-first-python-chkdirpy%2F&amp;send=false&amp;layout=button_count&amp;width=&amp;show_faces=false&amp;action=recommend&amp;colorscheme=light&amp;font=arial&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:21px;" allowTransparency="true"></iframe></div>]]></content:encoded>
			<wfw:commentRss>http://web-in-sight.nl/2007/11/03/my-first-python-chkdirpy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

