<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;DUYDQHs-eCp7ImA9WhBUFEw.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008</id><updated>2013-05-01T07:06:11.550-07:00</updated><category term="pure" /><category term="twisted" /><category term="web" /><category term="registry" /><category term="visibility" /><category term="ttc" /><category term="api" /><category term="open source" /><category term="pyside" /><category term="audio" /><category term="module" /><category term="hint" /><category term="shrooms" /><category term="css" /><category term="extension" /><category term="spam" /><category term="software engineering" /><category term="rss" /><category term="nosql" /><category term="vim" /><category term="eclipse" /><category term="hg" /><category term="wsh" /><category term="backup" /><category term="patch" /><category term="reversing" /><category term="virtualbox" /><category term="genetics" /><category term="unified" /><category term="java" /><category term="anti-pattern" /><category term="engineering" /><category term="security" /><category term="bzr" /><category term="swig" /><category term="wacko" /><category term="msxml" /><category term="format" /><category term="ux" /><category term="diff" /><category term="django" /><category term="gui" /><category term="pdf" /><category term="batch" /><category term="c" /><category term="output" /><category term="trac" /><category term="build" /><category term="software" /><category term="ssl" /><category term="atom" /><category term="design" /><category term="network" /><category term="framework" /><category term="project" /><category term="automation" /><category term="architecture" /><category term="zend" /><category term="butterflies" /><category term="vcs" /><category term="google" /><category term="svn" /><category term="whitespace" /><category term="wiki" /><category term="proxy" /><category term="timeline" /><category term="magic" /><category term="sphinx" /><category term="perl" /><category term="subprocess" /><category term="local network" /><category term="geeks" /><category term="dump" /><category term="recover" /><category term="evolution" /><category term="console" /><category term="mingw" /><category term="flow" /><category term="evaluation" /><category term="plugin" /><category term="python" /><category term="parsers" /><category term="script" /><category term="debian" /><category term="windows" /><category term="windows 2000" /><category term="port" /><category term="scons" /><category term="colorer" /><category term="update" /><category term="apache" /><category term="linux" /><category term="hack" /><category term="idea" /><category term="cvs" /><category term="py2exe" /><category term="php" /><category term="programming" /><category term="sqlite" /><category term="pysqlite" /><category term="experience" /><category term="interoperability" /><category term="xslt" /><category term="optparse" /><category term="argparse" /><category term="vnc" /><category term="spyder" /><category term="tip" /><category term="dna" /><category term="trash" /><category term="jquery" /><category term="sus" /><category term="cgi" /><category term="far manager" /><category term="appengine" /><category term="python4" /><category term="unix" /><category term="getopt" /><category term="languages" /><category term="chromium" /><category term="https" /><category term="gcc" /><category term="bootstrap" /><category term="mercurial" /><category term="management" /><category term="industrial" /><title>another day another vice another roll of the dice</title><subtitle type="html">hacks, diffs and problems</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>77</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/techtonik" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="techtonik" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CEYFQn49eCp7ImA9WhBVFEg.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-6934294466521227879</id><published>2013-04-20T03:01:00.001-07:00</published><updated>2013-04-20T03:01:53.060-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-20T03:01:53.060-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="shrooms" /><category scheme="http://www.blogger.com/atom/ns#" term="engineering" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="design" /><category scheme="http://www.blogger.com/atom/ns#" term="ux" /><category scheme="http://www.blogger.com/atom/ns#" term="evolution" /><category scheme="http://www.blogger.com/atom/ns#" term="geeks" /><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="api" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="architecture" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="dna" /><category scheme="http://www.blogger.com/atom/ns#" term="languages" /><title>Program config as a DNA strand</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
This is a technical followup to the post about&amp;nbsp;&lt;a href="http://techtonik.rainforce.org/2013/03/programming-languages-that-alter-your.html"&gt;mind-altering programming languages&lt;/a&gt;, which concentrates, iterates and extends on the abstract DNA part.&lt;br /&gt;
&lt;br /&gt;
Do you know how DNA looks like from the point of view of Python programmer?&lt;br /&gt;
DNA is a list. Of genes, and fillers in between.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style="background-color: #666666; color: #eeeeee;"&gt;    [ GENE, GENE, GENE, None, GENE, STUFF, GENE, .... ]
&lt;/pre&gt;
&lt;br /&gt;
By the analogy with software, one GENE is one option. Here &lt;span style="font-family: Courier New, Courier, monospace;"&gt;None&lt;/span&gt; or &lt;span style="font-family: Courier New, Courier, monospace;"&gt;STUFF&lt;/span&gt; is something that is not recognized and not used in the process right now, but may be used later even in a different place for a different purpose. This leads to very interesting properties of configuration described as a DNA strand.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;
Property One: Everything is Optional and Configurable&lt;/b&gt;&lt;/div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
Usually it is a tough choice what to include into your program config, because an option a day keeps users away. But with DNA config it doesn't matter - you can encode everything - every bit of flexibility you wanted, every feature-creeper, forward-thinking matter. Don't bother yourself with restrictions - include everything.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Property Two: Indefinite Logical Groups and Namespaces&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
To make sense out of tons of information in a DNA strand, you need to concentrate only on the parts that are relevant to you. This is done by "masking" (or marking) parts of the DNA code according to your current task. When you apply a mask, all GENEs of DNA that are not used are cleared. You can have multiple masks for different programs. An awesome feature-creeper would be to figure out mask automatically during the program run (program explicitly marks sequences what it reads and what it skips).&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;
Property Three: Non-linear Option Identification&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
Because DNA is a list, you may think that you need to know option position to access it. This is not necessary. Index in a DNA is the most straightforward way to lookup an option value, and mask is a tool to help explore and identify its position. But the true power comes from the fact that a option can be identified by just analyzing content. Option can be a single GENE in specific format, or it can be identified as a sequence of GENEs that pass some validation logic. This makes is possible to write generic configuration analyzers, which can read these sequences to detect configuration problems, patterns and meaningful values to produce nice visualization out of them.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;
Property Four: Application Types&lt;/b&gt;&lt;/div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
In real world&lt;span style="background-color: white; font-family: sans-serif; font-size: 16px; line-height: 19.1875px;"&gt;™&lt;/span&gt;&amp;nbsp;DNA is packed in chromosomes. Humans have 46. Potatoes possess 48. Zombies are somewhere in between. In a virtual world of coding it makes sense to use one chromosome per application type. For example, every Python web framework has a lot of common parts, common features and minor details in these features, so a variety of such frameworks can be encoded with a single DNA strand.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;
Exercise&lt;/b&gt;&lt;/div&gt;
&lt;div style="text-align: left;"&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
It may sound too abstract, but try to think how to build a shared DNA strand for two or more mini web application frameworks in Python. Just remember that DNA code includes both hardcoded application features and modifiable configuration options. And as usual look at&amp;nbsp;&lt;a href="http://boxcar2d.com/about.html"&gt;BoxCar 2D&lt;/a&gt;&amp;nbsp;for an inspiration.&lt;/div&gt;
&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/6934294466521227879/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2013/04/program-config-as-dna-strand.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6934294466521227879?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6934294466521227879?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2013/04/program-config-as-dna-strand.html" title="Program config as a DNA strand" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;AkEHQHk8cCp7ImA9WhBQE0o.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-4297062283319702615</id><published>2013-03-15T13:17:00.000-07:00</published><updated>2013-03-15T13:17:11.778-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-15T13:17:11.778-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="genetics" /><category scheme="http://www.blogger.com/atom/ns#" term="shrooms" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="perl" /><category scheme="http://www.blogger.com/atom/ns#" term="geeks" /><category scheme="http://www.blogger.com/atom/ns#" term="languages" /><category scheme="http://www.blogger.com/atom/ns#" term="butterflies" /><title>Programming languages that alter your mind</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
Today I went to&amp;nbsp;&lt;a href="http://perl6.org/"&gt;Perl 6&lt;/a&gt;&amp;nbsp;site.&lt;br /&gt;
I left.&lt;br /&gt;
I can remember in details what brought me there.&lt;br /&gt;
I still shocked by the new world that opened before me.&lt;br /&gt;
Butterflies..&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NOTE: This is a not very positive post `about life`. Don't waste you time if you don't like such stuff.&lt;br /&gt;
&lt;br /&gt;
The text below contains depictions of deaths by coding, too much letters, biased speculations, insight into&amp;nbsp;psychedelic culture and a long way to the conclusions you're may not be ready to face with. You've been warned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Let's face it - programming languages alter your mind. They affect the way people think and solve problems. It creates problems in life and relationships. Languages are addictive, they provide a lot of fun to entertain one's brain well beyond solving simple crosswords and moving sprites of monsters on the screen through the means of other sprites guided by the mouse-type manipulator. And while programming languages are fun, they are also &lt;a href="http://blog.expensify.com/2011/03/25/ceo-friday-why-we-dont-hire-net-programmers/"&gt;harmful and even&amp;nbsp;toxic&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
A language is toxic when it doesn't match your expectations, such as were mine that resulted in filling Python bug #17426 (which I called the subzero wart, but that doesn't really matter). A language is harmful when it requires you to remember yet another thing from its "funny" behavior, and for that purpose you need to sacrifice a mem cell that was dedicated to store the name of&amp;nbsp;your girlfriend's favorite flowers. It is harmful if it affects your mood, and you get butthurt when you read bad things about it.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Programming can be fatal for a girlfriend, relatives and the person in general. Much like eating mushrooms. I was only at two funerals of people below the age of 25. Both of them died of pneumonia, because&amp;nbsp;both were not attentive enough to the&amp;nbsp;symptoms&amp;nbsp;of the cold they got, and that's because their attention was completely absorbed by tasks they needed to complete for their jobs. Their body failed, because their brain was unable to dedicated resources to timely response to the alerts of life sustaining system.&lt;br /&gt;
&lt;br /&gt;
That's why people quit programming. That's why salaries are so high. But that doesn't mean other jobs are not harmful either. Low salary can be much more harmful in many ways leading to low self esteem and to even more complicated problems with relatives, friends and society. Did I say mushrooms can do this too?&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Programming can be fatal even for the people with positive strong attitudes to pursue their own goals in life. If taken without prescription or without negative sides of the daily jobs in bad companies, programming becomes addictive - fun for one who practices and a loss for the human society. Ignoring everything around, being busy with developing your own _____ (framework, game, startup, social network, ...). A loss that happens when a person is leaves the job to pursue its virtual dreams about programming. Dreams disconnected from reality. And reality is something that you've completely ignored and skipped while being buried under the pile of daily tasks at your big corporation. Did I say that mushrooms can cause this too?&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Real life is tough. It requires interaction with people, responding to signals and basically you start to forget your ideas, dreams and become more far away from your goals. New ideas are less likely to visit you, because maintaining a business requires a mindset of a library with shallow functions, not a scientific library with modelling algorithms. Maintaining a business is reinventing the bicycle. Over and over, day after day. Pursuing your dreams is reinventing the bicycle. Over and over, day after day. That keeps people busy, and the progress slow.&lt;br /&gt;
&lt;br /&gt;
Programming languages can make the world less harmful and more close to the reality. Programming languages that are accessible, that save time and don't have a tough legacy. Enabling people to adapt them, discuss them and invent their own. If people can not invent ideal programming languages by themselves, by calculating, by using maths and inductive reasoning - they should try genetic algorithms. Did I mention that mushrooms can mutate?&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The more experience I gain, the less I want to learn the peculiarities and differences between the languages. I just want them to work and I'd gladly outsource the task of inventing such working language to genetic algorithm. Algorithm where I involved as a social actor that makes selection based on shared experience with other actors. &lt;a href="http://www.boxcar2d.com/"&gt;Genetic algorithm&lt;/a&gt; is where you have a set of parameters written into one long line. Mutation is a change of some or all of these parameters - sometimes completely random, sometimes guided. If your programming language has parameters - syntax, functions, modules, features - you can describe it using this long line called DNA.&lt;br /&gt;
&lt;br /&gt;
To apply genetic algorithm you need a selection method - this is a shared user experience. You need an identification for the language a DNA sequence, but also a registry of names for and versions for human consumption. You need to abandon backward compatibility excuses and start building reusable components that will become new parameters in the language DNA sequence.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
---&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
It starts to get late. Tomorrow there will be a new day, new karma and new ideas, so I need to finish this today. If programming languages can alter you mind, you can use this mind to alter the language. It is well known that when you look into the abyss, the abyss also gazes into you, but it is less known that it is an iterative process. Did I mention that&amp;nbsp;&lt;a href="https://plus.google.com/110592708597698994191/posts/8hf9KeoqqL9"&gt;shrooms can give you powers&lt;/a&gt;?..&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Let me say goodbye and wish you all the best, dear reader. I hope you had a good trip, and now enjoy the expanded state of your mind and consciousness. Did I mention mushrooms? Right.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/4297062283319702615/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2013/03/programming-languages-that-alter-your.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/4297062283319702615?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/4297062283319702615?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2013/03/programming-languages-that-alter-your.html" title="Programming languages that alter your mind" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DEAFQns6eSp7ImA9WhBSGUs.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-5363403486284037654</id><published>2013-02-27T04:05:00.001-08:00</published><updated>2013-02-27T04:05:13.511-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-27T04:05:13.511-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="anti-pattern" /><category scheme="http://www.blogger.com/atom/ns#" term="visibility" /><category scheme="http://www.blogger.com/atom/ns#" term="api" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="experience" /><title>Formatting API Anti-Pattern</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
I was meditating over subset of core Python API that's dedicated to self-inspection in a running script. This API consists of&amp;nbsp;&lt;a href="http://docs.python.org/2/library/inspect.html"&gt;inspect&lt;/a&gt; +&amp;nbsp;&lt;a href="http://docs.python.org/2/library/traceback.html"&gt;traceback&lt;/a&gt;&amp;nbsp;+ &lt;a href="http://docs.python.org/2/library/sys.html#sys.exc_info"&gt;sys.exc_*&lt;/a&gt;&amp;nbsp; + magical&amp;nbsp;&lt;a href="http://docs.python.org/2/library/functions.html#locals"&gt;locals()&lt;/a&gt;&amp;nbsp;and ideally should give you a&amp;nbsp;total&amp;nbsp;understanding of Python &lt;b&gt;state at a given point in your program&lt;/b&gt;. But that doesn't happen, because things are complicated, and here is why..&lt;br /&gt;
&lt;br /&gt;
I wanted to get a chain of callers for the function I was debugging. I don't like pdb at all - it is so outdated for 2013 that deserves a separate GSoC project. &amp;nbsp;I prefer to just insert debug statements into the code and after several weeks and attempts I came up with a helper to get &lt;a href="https://gist.github.com/techtonik/2151727"&gt;the name of the caller&lt;/a&gt;&amp;nbsp;that I used to &lt;a href="https://gist.github.com/techtonik/1291125"&gt;log and&lt;/a&gt; debug such complex projects as SCons and Spyder. I am not as smart as everybody else, but even I didn't expect to spend weeks researching this basic stuff.&lt;br /&gt;
&lt;br /&gt;
I think the reason historically is that people tried to produce the meaningful data using functional approach without taking due attention on the &lt;b&gt;data quality&lt;/b&gt; itself. Is the data &lt;b&gt;complete&lt;/b&gt;,&amp;nbsp;&lt;b&gt;sufficient&lt;/b&gt;&amp;nbsp;and&amp;nbsp;&lt;b&gt;accessible&lt;/b&gt;? You can not say that from the manual. The manual (see &lt;a href="http://docs.python.org/2/library/inspect.html"&gt;inspect&lt;/a&gt;)&amp;nbsp;encourages you to hack your way in the debris of various bits of data that elusively fail to create a complete picture - the puzzle just doesn't solve, but you can't see this, because you don't know what is the structure of the Python &lt;b&gt;state at a given point in your program&lt;/b&gt;&amp;nbsp;and what to look for. The API encourages you to find functions that join and analyse the bits, you fail and try again and in the end you come up with a function filled with uncertainties, that just does some formatting magic.&lt;br /&gt;
&lt;br /&gt;
"Formatting API Anti-Pattern" may not be the best term for that, but it is the API (data structures, functions and other stuff) so complex and incomplete that you can not see this, and instead of that forced to write formatting functions just to make sense of this incomplete data. We are so used to the fact that "API provides abilities" that completely miss the point that it also "provides limitation", and the data in API can be incomplete, insufficient (incomplete for certain tasks) and not accessible (code becomes very complicated to handle).&lt;br /&gt;
&lt;br /&gt;
How to fight this? Think about data first. What data should be available for inspection at a given point in your program when this program is paused? Can you see this data in the manual? If not, then you're dealing with "Formatting API".&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/5363403486284037654/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2013/02/formatting-api-anti-pattern.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/5363403486284037654?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/5363403486284037654?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2013/02/formatting-api-anti-pattern.html" title="Formatting API Anti-Pattern" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CEYFR3ozeCp7ImA9WhBSEU8.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-344970546309567054</id><published>2013-02-17T09:28:00.001-08:00</published><updated>2013-02-17T09:28:36.480-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-17T09:28:36.480-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="linux" /><category scheme="http://www.blogger.com/atom/ns#" term="unix" /><title>Ghosts in the shell</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;span style="background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;At the beginning of the era human controllers were dominating in the shell. Now only their ghosts are controlling processes, launching programs and executing tasks server side. Quite often without any shells at all.&lt;/span&gt;&lt;br /&gt;
&lt;span style="background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style="background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;Updated the &lt;a href="http://code.google.com/p/rainforce/wiki/CHAOS"&gt;CHAOS&lt;/a&gt; speccy.&lt;/span&gt;&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/344970546309567054/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2013/02/ghosts-in-shell.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/344970546309567054?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/344970546309567054?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2013/02/ghosts-in-shell.html" title="Ghosts in the shell" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;Ck8BRX4-cSp7ImA9WhBSEU8.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-321309815301453462</id><published>2012-12-14T05:58:00.000-08:00</published><updated>2013-02-17T09:07:34.059-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-17T09:07:34.059-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="scons" /><category scheme="http://www.blogger.com/atom/ns#" term="getopt" /><category scheme="http://www.blogger.com/atom/ns#" term="optparse" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="argparse" /><title>Using getopt with optparse (or how to move from getopt gradually)</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
TL;DR:&amp;nbsp;&lt;a href="https://bitbucket.org/techtonik/scons/commits/bcb60b"&gt;https://bitbucket.org/techtonik/scons/commits/bcb60b&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://en.wikipedia.org/wiki/SCons"&gt;SCons&lt;/a&gt; has a very old and interesting codebase with a lots of outdated and unusual stuff that makes it more difficult to extend. One such thing is &lt;a href="http://docs.python.org/2/library/getopt.html"&gt;getopt&lt;/a&gt; library, which is a predessor for Optik library (written by&amp;nbsp;Greg Ward) now&amp;nbsp;better known as &lt;a href="http://docs.python.org/2/library/optparse.html"&gt;optparse&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
So I wanted to replace getopt with optparse, but didn't want to change everything in one step, because I didn't have time to check every option. Instead I decided to parse options I needed with optparse and leave everything else to the old getopt engine.&lt;br /&gt;
&lt;br /&gt;
getopt only needs a list of arguments to work. sys.argv[1:] to be exact. This is also the second half of result returned by&amp;nbsp;&lt;a href="http://docs.python.org/2/library/optparse#parsing-arguments"&gt;OptionParser.parse_args()&lt;/a&gt; function. The only problem was to teach OptionParser to ignore unknown options and leave them in arguments. Strange thing, but Optik examples included this user story, completely ignored in optparse documentation. To make this long user story short, you need to subclass OptionParser  to use getopt with optparse:&lt;br /&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;# "Pass-through" option parsing -- an OptionParser that ignores
# unknown options and lets them pile up in the leftover argument
# list.  Useful to gradually port getopt to optparse.

from optparse import OptionParser, BadOptionError

class PassThroughOptionParser(OptionParser):
    def _process_long_opt(self, rargs, values):
        try:
            OptionParser._process_long_opt(self, rargs, values)
        except BadOptionError, err:
            self.largs.append(err.opt_str)
    def _process_short_opts(self, rargs, values):
        try:
            OptionParser._process_short_opts(self, rargs, values)
        except BadOptionError, err:
            self.largs.append(err.opt_str)

parser = PassThroughOptionParser(add_help_option=False)
parser.add_option('-a', '--all', action='store_true',
                      help="Run all tests.")
(options, args) = parser.parse_args()

#print "options:", options
#print "args:", args
&lt;/code&gt;&lt;/pre&gt;
Now pass args down to the getopt call and you're all set.&lt;br /&gt;
&lt;br /&gt;
P.S. In &lt;a href="http://docs.python.org/dev/library/argparse.html"&gt;argparse&lt;/a&gt;&amp;nbsp;you can use &lt;a href="http://docs.python.org/dev/library/argparse.html#partial-parsing"&gt;ArgumentParser.parse_known_args()&lt;/a&gt; function.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update 2013-02&lt;/b&gt;: For humane option parsing you should definitely see&amp;nbsp;&lt;a href="http://docopt.org/"&gt;docopt&lt;/a&gt;&amp;nbsp;library.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/321309815301453462/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/12/using-getopt-with-optparse-or-how-to.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/321309815301453462?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/321309815301453462?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/12/using-getopt-with-optparse-or-how-to.html" title="Using getopt with optparse (or how to move from getopt gradually)" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;A0IMQ3s7fyp7ImA9WhNXF0k.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-3863379404373000856</id><published>2012-12-05T15:19:00.002-08:00</published><updated>2012-12-05T15:19:42.507-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-12-05T15:19:42.507-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="magic" /><title>Good reference on Python magic methods</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
I've just stumbled upon this manual about Python magic methods and it's really amazing. Definitely a good refresher and highly recommended.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.rafekettler.com/magicmethods.html"&gt;http://www.rafekettler.com/magicmethods.html&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/me wonders if the same engineering technique can be applied to official documentation corpus..&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/3863379404373000856/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/12/good-reference-on-python-magic-methods.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/3863379404373000856?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/3863379404373000856?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/12/good-reference-on-python-magic-methods.html" title="Good reference on Python magic methods" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CUAFRXk-eCp7ImA9WhNQFEw.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-5322365416203273687</id><published>2012-11-20T04:15:00.000-08:00</published><updated>2012-11-20T04:15:14.750-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-20T04:15:14.750-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python4" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>Cinematic journey approach for Python development</title><content type="html">&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;a href="http://www.python.org/about/quotes/"&gt;Quotes page&lt;/a&gt;&amp;nbsp;(fixed in stone) is silent about the one&amp;nbsp;who said that Python, compared to other languages, allows to directly put thoughts into the code. I couldn't disagree with this, but taking idealistic approach, this was more true with Python 2 when coding on a system level, but not so true with the great&amp;nbsp;coming of the&amp;nbsp;web and i18n. So, what's wrong now? I don't have a clean and up to the point answer, because many people still think that there is nothing wrong with the Python. Probably the right question is: &lt;b&gt;why Python is not better than it is now?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This one of the complicated questions nobody is able to answer fully. 42 is the answer, but does the question clear enough? The question is probably too complex for a good technical answer and should undergo decomposition. The decomposition can be achieved by clarifying.&amp;nbsp;What means "better"? More easy to code in.&amp;nbsp;Why is it hard to code? Here goes a list of problems...&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
Well, there is no&amp;nbsp;list. Therefore there is no &lt;b&gt;visibility&lt;/b&gt;, and without visibility no answer is possible. Gain visibility into the list of problems that make&amp;nbsp;Python not-as-good as we want it to be is the primary step to take to make all subsequent steps reasonably grounded for a good party quest (and sane development roadmap for community to focus on).&lt;br /&gt;
&lt;br /&gt;
Historically there were several driving forces behind Python development - mailing lists, bug reports and PEPs. PEPs more than the bugs. Mailing lists somewhere in between (YMMV).&lt;br /&gt;
&lt;br /&gt;
ML were good until people had a lot of time to follow up. Bugs are good at tracking status of things, but they are tuned for fixing things and scratching issues, so language research naturally falls out of context in bug tracker interface. PEPs.&lt;br /&gt;
&lt;br /&gt;
PEP is a good thing that helped to free Python core from&amp;nbsp;featurecreep damage, provided a basis for discussions over a long period of time and insight into decisions over the language development. But PEPs start to fail, and the reason why they do this is the lack of time and energy to iterate over them. Most people can't say if technology is good or bad before testing it (version control as an example), and PEPs with&amp;nbsp;lengthy&amp;nbsp;pieces of design detail assume prior experience with the problem, require thorough imagination to see if the solution will play well.&lt;br /&gt;
&lt;br /&gt;
PEPs require a lot of concentration - the resource of a big shortage nowadays, especially of professional grade. Which is not a surprise if you look at how good HR and management technologies are developed in modern world to keep people busy and involved. We can only hope that collective minds of big corp.s are somehow bugged with the problem and look for solutions to divert their resource flow to improve the grounds they are standing on. Let's hope that community can back up their support, and also somehow bugged with the problem about how to lower barriers of requirements, responsibility, experience and technical expertise for occasional community member, a student or&amp;nbsp;elderly&amp;nbsp;accountant, to be useful in Python development process. Lets's hope that both parties are interested enough to constantly improve ways to use the resource flow to the fullest extent possible.&lt;br /&gt;
&lt;br /&gt;
There are two things that can be help here (and make Python better that it is now) - first one is to improve visibility. It takes its roots in cinematic industry and it's called &lt;b&gt;scenario&lt;/b&gt;. Second one is to improve the process and it is a best practice developed over the time by user experience professionals. This one named &lt;b&gt;customer journey map&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;What keeps me away from putting my thoughts into code when I write Python?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
"""Python forces me to maintain a lowest level structure of my writing - the indented layout,&amp;nbsp;a good thing. Although this also comes with a&amp;nbsp;pain while&amp;nbsp;debugging, because Gangam style multiline comments require me to remember to indent them as well.""" &amp;nbsp;- this is a scenario. You can add various metrics to it, such as:&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; """I have only 7 operational attention slots&amp;nbsp;in my mind, and one constantly falls out, because I have to pay attention to complicated commenting requirement.""" - the metrics here directly influences how deep one person can operate at any given moment. It is basically that multiline comments with strings are stealing concentration.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; """Those indentation errors are driving me mad every time I forget to indent multiline comment for debugging.""" - this says that a person uses iterative approach to debug problems, often commenting a lot, and probably in production environment using non-tuned editor. That's another scenario where Python comment hack doesn't play well.&lt;br /&gt;
&lt;br /&gt;
Scenarios have two good qualities - they are short and can be conflicting between each other. PEP is on the other side - it is self-sufficient. To notice that PEP is contradictory - you need to attentively and thoroughly read it or write it yourself. It takes a lot of time. Scenarios are somewhat emotional, they are easy to remember and refer to. This makes it possible to concentrate on conflicting scenarios, outline conflicting points and concentrate all work around them rather than around vague opinions, which makes the whole process of looking for compromises (or good solutions) more fun and involving.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To summarize, the scenario is a good title to remember and a short story to tell. What is the difference between scenario and a StackOverflow question? Question may not have a story, scenario may not contain questions. What's the difference between scenario, use case and user story? "Use case" is an enterprise slang, "user story" is an agile term. Both may have some definitions. Scenario is just scenario, like in movie. You should replay it to see how it works. Scenario is for humans, it is less formalized and comes with emotions included (YMMV).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let's skip to another example of problem with Python usability on a higher level - packaging - and present another tool from&amp;nbsp;usability&amp;nbsp;domain&amp;nbsp;that can help with&amp;nbsp;analyzing&amp;nbsp;processes in general.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;What's wrong with Python packaging that everybody constantly rewrites it?&lt;/b&gt;&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
I didn't intend to include it here first, but a half an hour ago I spotted this article - http://lucumr.pocoo.org/2012/6/22/hate-hate-hate-everywhere/ If distutils/setuptools had a scenario database for packaging, it could be possible to analyze limitations of Python in regard to each scenario. This analysis is similar to PEP, but not necessary a proposal and not necessary so extensive. Scenario may contain a history of the problem, a short description, summary and link to other conflicting scenarios. The role of scenario database is to aid decision making process and an easy reference for new people facing the same problems.&lt;br /&gt;
&lt;br /&gt;
Scenarios can be universal and it is a good analysis tool. You can substitute Ruby for Python and look how good this specific workflow looks for the different system.&lt;br /&gt;
&lt;br /&gt;
"""I can't list installed Python packages, why?""" - does anybody have a link? """I can't find the answer""", and that's another scenario about usefulness of scenarios.&lt;br /&gt;
&lt;br /&gt;
So, to fix packaging there should be a way to operate with scenarios. There should be at least a list of scenarios (or better indented tree), so that (y-)hackers&amp;nbsp;of a&amp;nbsp;new packaging tool could go over it, think about their approach, tick checkboxes and hopefully, spot and bring to the surface this "Essential Packaging Restraint" that eats a whole generations of people. The point is to spot the problem before starting to code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The scenario DB will help, but there is another usability tool that can make packaging, bug tracking and other development&amp;nbsp;processes&amp;nbsp;more streamlined (less time consuming, more fun and engaging). This tool is called &lt;b&gt;Customer Journey Map&lt;/b&gt;, and it shows to people, who are not experiencing any problems with the process, where those problems are for somebody else. This map is also a good starting point in web site redesigns, conference organizations, all kinds of activities that involve people, or more specific, a single person named "Customer", barriers this guy is facing and steps to remove these barriers.&lt;br /&gt;
&lt;br /&gt;
I can't extend to a great detail about CJM in this post due to time constraints. I was impressed by a presentation of awesome&amp;nbsp;UXpresso&amp;nbsp;team, there might be a video available, but it is likely Russian only, and I've heard of at least one major Python company (wargaming.net) that uses it extensively, so I can only give you a pointer for now.&amp;nbsp;It will be interesting to make presentation of&amp;nbsp;this technology&amp;nbsp;for Python contribution process and talk about CJM at PyCon, but I am unlikely to afford the&amp;nbsp;participation&amp;nbsp;costs, so somebody else should do this.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/5322365416203273687/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/11/cinematic-journey-approach-for-python.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/5322365416203273687?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/5322365416203273687?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/11/cinematic-journey-approach-for-python.html" title="Cinematic journey approach for Python development" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>5</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DEUCQn47fCp7ImA9WhJSGEg.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-6823428720874172003</id><published>2012-07-09T10:51:00.000-07:00</published><updated>2012-07-09T10:51:03.004-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-07-09T10:51:03.004-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="scons" /><category scheme="http://www.blogger.com/atom/ns#" term="trac" /><category scheme="http://www.blogger.com/atom/ns#" term="software engineering" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>About Environment</title><content type="html">&lt;br /&gt;
In Python applications &lt;b&gt;environment&lt;/b&gt;&amp;nbsp;is often an ambiguous term that needs clarification. In general sense `&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;the environment&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;`&lt;/span&gt;&amp;nbsp;is &lt;b&gt;system environment&lt;/b&gt;&amp;nbsp;with PATH and friends accessible from &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;os.environ&lt;/span&gt;&amp;nbsp;within Python. But in Python applications it can mean different things.&lt;br /&gt;
&lt;br /&gt;
In &lt;b&gt;Trac&amp;nbsp;&lt;/b&gt;`&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;the environment&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;`&lt;/span&gt;&amp;nbsp;is a directory with settings, database and other files related to one Trac site.&lt;br /&gt;
&lt;br /&gt;
In &lt;b&gt;SCons&amp;nbsp;&lt;/b&gt;`&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;the environment&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;`&amp;nbsp;&lt;/span&gt;is a structure in memory that holds dependency trees, helper functions, builders and other stuff. It is written to disk only for caching.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quite many other applications have some kind of environment for their own purpose with meanings close to either Trac or SCons, which often confuses newbies or strangers who are not aware about the context. Software development clearly needs more specific terms in English to make people write and read in the same language without those excessive contexts.&lt;br /&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/6823428720874172003/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/07/about-environment.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6823428720874172003?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6823428720874172003?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/07/about-environment.html" title="About Environment" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;A04FQXk4cCp7ImA9WhVbEUQ.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-2954733978013825964</id><published>2012-05-26T01:56:00.001-07:00</published><updated>2012-05-28T03:11:50.738-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-28T03:11:50.738-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="pyside" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="spyder" /><title>Spyder IDE Internals: Highlighting in 2.2.0dev</title><content type="html">&lt;b&gt;UPD:&lt;/b&gt;&amp;nbsp;Highligher support in code editor have been improved, so its instance can now be traced easily.&lt;br /&gt;
&lt;br /&gt;
The entrypoint to syntax highlighting in Spyder IDE is located in CodeEditor widget at&amp;nbsp;&lt;a href="http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/sourcecode/codeeditor.py?r=b48946b15f99a4845aa44a8ff85204b3c60b8a93#452"&gt;spyderlib/widgets/sourcecode/codeeditor.py&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
CodeEditor widget is basically file content in one of main tabs. The whole stack of tabs is called EditorStack. So editors are grouped in editor stacks, each editor renders one file, and each editor has its own highligher created from assigned self.highlighter_class&lt;br /&gt;
&lt;br /&gt;
Highlighers are implemented with Qt's &lt;a href="http://www.pyside.org/docs/pyside/PySide/QtGui/QSyntaxHighlighter.html"&gt;QSyntaxHighlighter&lt;/a&gt;. No pygments, nothing like this, so can't say if Qt is faster, but it should be.&amp;nbsp;Default self.highligher_class for every code editor is&amp;nbsp;&lt;a href="http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/sourcecode/syntaxhighlighters.py?r=c037ef100bde3774939efd18192205a345345b76#227"&gt;TextSH&lt;/a&gt;. The actual instance is created by set_language() method called from setup_editor(). If setup_editor() is not called, the highlighter can be unset, but frankly I don't know what's the purpose of using such editor.&lt;br /&gt;
&lt;br /&gt;
if set, syntax highlighter (self.highlighter) &lt;b&gt;is responsible&lt;/b&gt; for:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;coloring raw text data inside editor on load&lt;/li&gt;
&lt;li&gt;coloring text data when editor is cloned&lt;/li&gt;
&lt;li&gt;updating document highlight on line edits&lt;/li&gt;
&lt;li&gt;providing color palette (scheme) for the editor&lt;/li&gt;
&lt;li&gt;providing data for Outliner&lt;/li&gt;
&lt;/ul&gt;
self.highlighter is &lt;b&gt;not&lt;/b&gt; responsible for:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;background highlight for current line&lt;/li&gt;
&lt;li&gt;background highlight for search / current line occurrences&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;div&gt;
Enjoy hacking.&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/2954733978013825964/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/05/spyder-ide-internals-highlighting-in.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2954733978013825964?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2954733978013825964?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/05/spyder-ide-internals-highlighting-in.html" title="Spyder IDE Internals: Highlighting in 2.2.0dev" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DEcMRHs9fCp7ImA9WhVWFkw.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-2762721575165561302</id><published>2012-04-28T05:28:00.000-07:00</published><updated>2012-04-28T05:28:05.564-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-28T05:28:05.564-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="engineering" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>Multidimensional programming</title><content type="html">From time to time I reverse some piece of open source code just to understand how it works. The biggest problem with that is the amount of things I need to juggle in my head until they find a place on the canvas of reversed blueprint. If there are too many - I give up or put the project on the back burner. This was with Stackless and Twisted, but I really glad to finally get to them.&lt;br /&gt;
&lt;br /&gt;
Quite often is starts with some bug that seems easy to fix and I go for it. In ideal world it shouldn't take more than 15 minutes for studying the code to gain understanding what should be changed, but it is also important to have a confidence that the change won't break anything.&lt;br /&gt;
&lt;br /&gt;
I won't tell you how to deal with that complexity, but I'd like to share an idea that I've got from Large Hadron Collider. =) Let me tell you that story...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I always had troubles trying to squeeze more than 3 dimensions into my head. I could imagine a dot moving along X or Y axes easily, could imagine it moving along Z axis, but everything more than that caused a confusion.&lt;br /&gt;
&lt;br /&gt;
I could have my brain exploded when some time ago a friend of mine tried to explain what's going on in LHC. He said that physics and mathematicians are trying to figure out how many dimensions our universe has. They assume that our universe has&amp;nbsp;more than 4 dimensions (3 coordinates and 1 time value). In fact they argue that the truth is somewhere between 9 and 26!&lt;br /&gt;
&lt;br /&gt;
I am not a fan of scientific theories - my neurons are pretty calm to that matter, but when you just need to understand, because the person in front of you is knowledgeable and tries all the best to explain &amp;nbsp;- here is when you start to feel colliding brain cells under your skull. To the honor of my friend, he didn't try to build-up suspense (as I would probably do before explaining properly) and managed to draw a clear picture in my mind. A&amp;nbsp;standard plot with two axes - X and Y, and a dot.&lt;br /&gt;
&lt;br /&gt;
"Look at this dot on X/Y plane. This dot has coordinates in two dimensions - X and Y", - he explained. "These are only two you can see here. But this dot is from the real world, so it also has Z coordinate", - he added putting a label "Z=0.1" next to the dot. "This dot also have speed", - he drew "V=0.1 m/s". "Now we can see values for all 4&amp;nbsp;traditional&amp;nbsp;dimensions of our dot, but we can add more. There are many things that we can classify our dot. It probably has color.", - he drew a color box and a hex value. "It has temperature", - a new label "t = 25С" appeared in the column. "There are 6 already, and you can add your own.". I immediately imagined a dot travelling through canvas of "Stars!" game with all those labels nearby that constantly change their values as the dot moved. "Wow! Now I see", - that was a nice feeling -&amp;nbsp;I must admit I can be pretty dumb sometimes. =) It didn't make a science fan out of me, but did make an important short-circuit in the depths of my head, which popped up a few months later..&lt;br /&gt;
&lt;br /&gt;
A few months later.&lt;br /&gt;
&lt;br /&gt;
As it usually happens in software development you need at least some basic design before setting down to code, and with time design phase completely faded from our process into "who made it first - wins" motto. It is hard to argue with that, so I didn't. But as a result after some time, the stability of our releases dropped. People were not communicating, and started to forget about some aspects of our system that could fire at any time in completely unexpected places. Even though all our code undergoes reviews, the reviewers tend to forget about those aspect as well. The process went out of control - we couldn't keep all the aspects in our heads when planning for the next feature to be released, and I found myself in same state I was when trying to understand the complexity of multidimensional string theories. That lead me to the idea that we need to control the amount of aspects that we need to keep in mind when coding and restructure our architecture to keep those aspects at minimum. This will help us to regain sense of confidence into what we are doing, and save some money from the bills from the nearest bar on the name of our release manager.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, the `multidimensional programming` concept means that at any point of your code there are multiple things that you should be aware of. These things are the 'dimensions', and the more you have - the more complex your application is. Basically, these are the things that can be broken by any change at this place. Good application architecture is orthogonal - you can work at only one dimension at a time without thinking too much about all others. But you need to know all of them anyway to gain a sense of confidence.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, a recent change in Spyder IDE requires me to rename some file. This breaks the code, which I grep and fix - that's one dimension. But it will also likely to break a translation for the strings in that file, because the string is now at a different place. I imagine that nobody will be interested to check and translate the same stuff over and over again, so that's one more thing I'd like to avoid, so I should keep that in mind.&lt;br /&gt;
&lt;br /&gt;
Another example are web applications. You need to keep in mind 'user privileges', type of HTTP request ('ajax', ...) and response ('json', ...) required. You need to make sure `critical errors` are handled and reported, and `static files` are correctly served by web server. You need to save incomplete `data between requests`, and cleanup it where possible. Make sure there is sufficient `XSRF protection` and `browser compatibility`. There are a lot more to it, and so far these have nothing to do with the logic of your web application. Frameworks help to deal with that, but inside they are still multidimensional. If framework is not flexible for you - that probably means it tries to keep some dimensions orthogonal, and there could be a good reason for that.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maybe that's not much, but at least now you can argument that Large Hadron Collider experiments have much in common with software engineering, and when somebody asks about your job - you can proudly state that you're on par with scientists with their string theory, but in your own big enterprise application universe. =)&lt;br /&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/2762721575165561302/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/04/multidimensional-programming.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2762721575165561302?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2762721575165561302?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/04/multidimensional-programming.html" title="Multidimensional programming" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DU8AQX4zfCp7ImA9WhRaFEo.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-2215084567096284900</id><published>2012-02-17T03:24:00.000-08:00</published><updated>2012-02-17T03:24:00.084-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-17T03:24:00.084-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="architecture" /><category scheme="http://www.blogger.com/atom/ns#" term="reversing" /><category scheme="http://www.blogger.com/atom/ns#" term="appengine" /><title>Rietveld architecture: AppEngine/Django request processing</title><content type="html">Just a quick note/reminder of the request handling flow in AppEngine environment for mixed AE/Django application such a Rietveld&amp;nbsp;. Hopefully it provides a good &lt;b&gt;entrypoint&lt;/b&gt; to understand how AppEngine works.&amp;nbsp;I use &lt;a href="http://code.google.com/p/rietveld/"&gt;Rietveld&lt;/a&gt; as an example, because this project is basically born to show how to run Django on AE.&lt;br /&gt;
&lt;br /&gt;
Rietveld is a Django application that &lt;b&gt;is run by AppEngine&lt;/b&gt;. Let's leave all Django stuff aside and learn&amp;nbsp;how AppEngine loads and initializes Python applications first.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Import and execution in Python web apps&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In PHP when your code is executed, it is read and interpreted (executed) from start to finish every time a new request arrives. In Python the code is read only once (imported), executed and on subsequent requests only the part that handles request is invoked over and over. The catch is that &lt;b&gt;first request is always different&lt;/b&gt;&amp;nbsp;in Python.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;What happens when application is uploaded to AppEngine?&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Step 1&lt;/b&gt;. Standard AppEngine application loading and initialization sequence&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;AppEngine reads app.yaml to understand how to load application (which version of Python it requires and which URLs are handled by which Python scripts)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;AppEngine initializes application by creating an instance for it&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Then it looks at URL and executes script that shoud process this URL according to app.yaml&lt;/li&gt;
&lt;/ul&gt;
This stuff is actual for every AppEngine application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Example: ---&amp;nbsp;&lt;/b&gt;&lt;a href="http://code.google.com/p/rietveld/source/browse/app.yaml"&gt;app.yaml&lt;/a&gt;&amp;nbsp;from Rietveld project ---&amp;gt;&lt;br /&gt;
&lt;blockquote class="tr_bq"&gt;
It is the entrypoint to understand any AppEngine app. If you want to know what is called when you request an URL - first thing to do is to look there.&lt;/blockquote&gt;
&lt;b&gt;Step 2&lt;/b&gt;. Python code to fine-tune AppEngine params and configure request handler&lt;br /&gt;
&lt;br /&gt;
All requests in Rietveld (except static files) are handled by &lt;a href="http://main.py/"&gt;main.py&lt;/a&gt;. It does the following:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Imports &lt;a href="http://code.google.com/p/rietveld/source/browse/appengine_config.py"&gt;appengine_config.py&lt;/a&gt;&amp;nbsp;that in turn:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Initializes and tunes&amp;nbsp;&lt;a href="http://code.google.com/appengine/docs/python/tools/appstats.html"&gt;Appstats&lt;/a&gt; tool&lt;/li&gt;
&lt;li&gt;Chooses version of Django to use (1.2 currently)&lt;/li&gt;
&lt;li&gt;Configures Django to read&amp;nbsp;&lt;a href="http://code.google.com/p/rietveld/source/browse/settings.py"&gt;settings.py&lt;/a&gt;&amp;nbsp;with Rietveld specific parameters&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Adds logger for all exceptions&lt;/li&gt;
&lt;li&gt;Removes Django's DB rollback event handler (because Rietveld doesn't use DB layer of Django)&lt;/li&gt;
&lt;li&gt;Creates request handler using Django&lt;/li&gt;
&lt;li&gt;Passes handler to AE's&amp;nbsp;run_wsgi_app() util to give&amp;nbsp;Django control to process request&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Django didn't fire at this point, and nothing magical happened.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Step 3&lt;/b&gt;. Request handling magic&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Request handling starts with&amp;nbsp;&lt;span class="pln" style="background-color: white; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;run_wsgi_app&lt;/span&gt;&lt;span class="pun" style="background-color: white; color: #666600; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;()&lt;/span&gt;&amp;nbsp;- this magical function&amp;nbsp;&lt;a href="http://code.google.com/appengine/docs/python/tools/appstats.html#EventRecorders"&gt;implicitly imports appengine_config.py&lt;/a&gt;&amp;nbsp;to read its own settings behind the scenes and then gives control to Django handler created earlier.&lt;/div&gt;
&lt;br /&gt;
Django reads its &lt;a href="http://code.google.com/p/rietveld/source/browse/settings.py"&gt;settings.py&lt;/a&gt;&amp;nbsp;mentioned earlier and processes options before executing anything application/request specific:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Configures middlewares - that's important, because they provide such things as user object in request:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="background-color: white; color: #008800; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;django.middleware.common.CommonMiddleware&lt;/span&gt;&amp;nbsp; - doesn't seem to be used (&lt;a href="https://docs.djangoproject.com/en/dev/ref/middleware/#module-django.middleware.common"&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;span style="background-color: white; color: #008800; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;django.middleware.http.ConditionalGetMiddleware&lt;/span&gt; - not sure why it is needed&lt;/li&gt;
&lt;li&gt;&lt;span style="background-color: white; color: #008800; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;codereview.middleware.AddUserToRequestMiddleware&lt;/span&gt; - &lt;a href="http://code.google.com/p/rietveld/source/browse/codereview/middleware.py"&gt;this one&lt;/a&gt; also fetches user-specific parameters from Account record&lt;/li&gt;
&lt;li&gt;&lt;span style="background-color: white; color: #008800; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;codereview.middleware.PropagateExceptionMiddleware&lt;/span&gt; - logs and rewrites exceptions to be more user-friendly&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Sets &lt;a href="http://code.google.com/p/rietveld/source/browse/urls.py"&gt;urls.py&lt;/a&gt;&amp;nbsp;to be the ROOT_URLCONF - mapping between URLs and handler functions in &lt;a href="http://code.google.com/p/rietveld/source/browse/codereview/views.py"&gt;views.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Enables&amp;nbsp;&lt;span style="background-color: white; color: #008800; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; white-space: pre-wrap;"&gt;django.core.context_processors.request&lt;/span&gt; which adds `request` object to templates&lt;/li&gt;
&lt;li&gt;Configures template loaders&lt;/li&gt;
&lt;li&gt;Configures file uploads&lt;/li&gt;
&lt;li&gt;Configures URL to generate path to static files as `/static/`&lt;/li&gt;
&lt;li&gt;Rietveld own constants like incoming email address are also defined here&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
After all above is done, Django handler starts processing the request:&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;It looks into&amp;nbsp;&lt;a href="http://code.google.com/p/rietveld/source/browse/urls.py"&gt;urls.py&lt;/a&gt;&amp;nbsp;to find what function should process requested URL&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/rietveld/source/browse/urls.py"&gt;urls.py&lt;/a&gt;&amp;nbsp;is a redirect to&amp;nbsp;&lt;a href="http://code.google.com/p/rietveld/source/browse/codereview/urls.py"&gt;codereview/urls.py&lt;/a&gt;&amp;nbsp;with actual mapping, so it reads the latter as well&lt;/li&gt;
&lt;li&gt;Finds associated function name and calls this function from&amp;nbsp;&lt;a href="http://code.google.com/p/rietveld/source/browse/codereview/views.py"&gt;views.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
And that's basically the entrypoint that you need to start hacking Rietveld/Django and AppEngine.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/2215084567096284900/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2012/02/rietveld-architecture-appenginedjango.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2215084567096284900?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/2215084567096284900?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2012/02/rietveld-architecture-appenginedjango.html" title="Rietveld architecture: AppEngine/Django request processing" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;A08DRXo9eCp7ImA9WhRSEU4.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-8092677205295578082</id><published>2011-11-12T14:09:00.001-08:00</published><updated>2011-11-12T15:17:54.460-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-12T15:17:54.460-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="vnc" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>Python based VNC client in browser</title><content type="html">What I really like in open source is when people reuse existing stuff to bring up something new. Today Arkaitz Jimenez brought up &lt;a href="http://arkaitzj.wordpress.com/2011/11/12/vnc-in-your-browser-through-websockets-handled-by-gevent/"&gt;a way to connect to VNC server with browser&lt;/a&gt; using&amp;nbsp;hybrid&amp;nbsp;solution from Python, JavaScript, gevent and WebSockets.&lt;br /&gt;
&lt;br /&gt;
The exciting thing that this project uses patched version of&amp;nbsp;&lt;a href="http://code.google.com/p/python-vnc-viewer/"&gt;python-vnc-viewer&lt;/a&gt;&amp;nbsp;written by Chris Liechti that I've&amp;nbsp;uploaded to Google Code, because it had to be updated&amp;nbsp;to work with modern versions of required libraries. It is nice to see this effort brought up something cool today.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/8092677205295578082/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/11/python-based-vnc-client-in-browser.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/8092677205295578082?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/8092677205295578082?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/11/python-based-vnc-client-in-browser.html" title="Python based VNC client in browser" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DUABR3k-cSp7ImA9WhdXFUQ.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-7248412585026021307</id><published>2011-08-28T23:02:00.000-07:00</published><updated>2011-08-28T23:02:36.759-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-28T23:02:36.759-07:00</app:edited><title>How to make disk in memory in Linux</title><content type="html">&lt;br /&gt;
Disks in memory are useful for one major reason - speed. It is invaluable for test execution. While the idea is not new, there was no incentive to explore it until I've run upon &lt;a href="http://en.wikipedia.org/wiki/Tmpfs"&gt;tmpfs&lt;/a&gt;&amp;nbsp;reference in Ubuntu Wiki.&lt;br /&gt;
&lt;br /&gt;
For example, to get 2Gb of space for files in RAM, edit &lt;b&gt;/etc/fstab&lt;/b&gt;&amp;nbsp;to add the following line:&lt;br /&gt;
&lt;code class="prettyprint"&gt;&lt;pre&gt;
tmpfs &amp;nbsp; &amp;nbsp; /var/ramspace &amp;nbsp; &amp;nbsp; &amp;nbsp; tmpfs &amp;nbsp; &amp;nbsp; defaults,size=2048M &amp;nbsp; &amp;nbsp; 0 &amp;nbsp; &amp;nbsp; 0&lt;/pre&gt;&lt;/code&gt;
&lt;b&gt;/var/ramspace&lt;/b&gt; is now the place to store your files in memory.&lt;br /&gt;
</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/7248412585026021307/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/08/how-to-make-disk-in-memory-in-linux.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7248412585026021307?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7248412585026021307?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/08/how-to-make-disk-in-memory-in-linux.html" title="How to make disk in memory in Linux" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CEcMRH4-eip7ImA9WhZaF00.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-701608418101240666</id><published>2011-07-03T07:08:00.000-07:00</published><updated>2011-07-03T07:08:05.052-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-03T07:08:05.052-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="output" /><category scheme="http://www.blogger.com/atom/ns#" term="pure" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="audio" /><title>Audio output on Windows with pure Python + ctypes</title><content type="html">I want to make a simple walkie-talkie for a local LAN to have fun with my friends. I've created&amp;nbsp;&lt;a href="http://code.google.com/p/rainforce/wiki/RawAudioSocket"&gt;Raw Audio Socket&lt;/a&gt;&amp;nbsp;spec to keep it really simple. We have a lot of different operating systems, so it would be nice to have a single implementation that can be run cross-platform. I chose Python and started with getting the sound playing on Windows.&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Python is known for its easy integration with C libraries and, indeed, there is a lot of Python bindings calling audio libraries written in C from Python. However, it requires the library to be compiled for your specific platform. I chose to avoid dependencies on any C code and instead call Windows WinMM Multimedia API &amp;nbsp;directly with &lt;a href="http://docs.python.org/2.7/library/ctypes.html"&gt;ctypes&lt;/a&gt;&amp;nbsp;module.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Thanks to an excellent tutorial by David Overton, here is the pure Python proof of concept that plays standard CD Audio PCM 44.1kHz&amp;nbsp;16bit Stereo sample from external file to the default sound device.&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;
"""
Implementation of Raw Audio Socket server spec in pure Python
http://code.google.com/p/rainforce/wiki/RawAudioSocket

"""

import sys

#-- CHAPTER 1: CONTINUOUS SOUND PLAYBACK WITH WINDOWS WINMM LIBRARY --
#
# Based on tutorial "Playing Audio in Windows using waveOut Interface"
# by David Overton

import ctypes
from ctypes import wintypes


# 1. Open Sound Device

# --- define necessary data structures from mmsystem.h
HWAVEOUT = wintypes.HANDLE
WAVE_FORMAT_PCM = 0x1
WAVE_MAPPER = -1
CALLBACK_NULL = 0
MMSYSERR_NOERROR = 0

class WAVEFORMATEX(ctypes.Structure):
  _fields_ = [
    ('wFormatTag',  wintypes.WORD),
      # 0x0001 WAVE_FORMAT_PCM. PCM audio
      # 0xFFFE The format is specified in the WAVEFORMATEXTENSIBLE.SubFormat
      # Other values are in mmreg.h 
    ('nChannels',   wintypes.WORD),
    ('SamplesPerSec',  wintypes.DWORD),
    ('AvgBytesPerSec', wintypes.DWORD),
      # for WAVE_FORMAT_PCM is the product of nSamplesPerSec and nBlockAlign
    ('nBlockAlign', wintypes.WORD),
      # for WAVE_FORMAT_PCM is the product of nChannels and wBitsPerSample
      # divided by 8 (bits per byte)
    ('wBitsPerSample', wintypes.WORD),
      # for WAVE_FORMAT_PCM should be equal to 8 or 16
    ('cbSize',      wintypes.WORD)]
      # extra format information size, should be 0
# --- /define

# Data must be processes in pieces that are multiple of
# nBlockAlign bytes of data at a time. Written and read
# data from a device must always start at the beginning
# of a block. Playback of PCM data can not be started in
# the middle of a sample on a non-block-aligned boundary.

hwaveout = HWAVEOUT()
wavefx = WAVEFORMATEX(
  WAVE_FORMAT_PCM,
  2,     # nChannels
  44100, # SamplesPerSec
  705600,# AvgBytesPerSec = 44100 SamplesPerSec * 16 wBitsPerSample
  4,     # nBlockAlign = 2 nChannels * 16 wBitsPerSample / 8 bits per byte
  16,    # wBitsPerSample
  0
)

# Open default wave device
ret = ctypes.windll.winmm.waveOutOpen(
  ctypes.byref(hwaveout), # buffer to receive a handle identifying
                          # the open waveform-audio output device
  WAVE_MAPPER,            # constant to point to default wave device
  ctypes.byref(wavefx),   # identifier for data format sent for device
  0, # DWORD_PTR dwCallback - callback mechanizm
  0, # DWORD_PTR dwCallbackInstance - user instance data for callback
  CALLBACK_NULL # DWORD fdwOpen - flag for opening the device
)

if ret != MMSYSERR_NOERROR:
  sys.exit('Error opening default waveform audio device (WAVE_MAPPER)')

print "Default Wave Audio output device is opened successfully"


# 2. Write Audio Blocks to Device

# --- define necessary data structures
PVOID = wintypes.HANDLE
WAVERR_BASE = 32
WAVERR_STILLPLAYING = WAVERR_BASE + 1
class WAVEHDR(ctypes.Structure):
  _fields_ = [
    ('lpData', wintypes.LPSTR), # pointer to waveform buffer
    ('dwBufferLength', wintypes.DWORD),  # in bytes
    ('dwBytesRecorded', wintypes.DWORD), # when used in input
    ('dwUser', wintypes.DWORD),          # user data
    ('dwFlags', wintypes.DWORD),
    ('dwLoops', wintypes.DWORD),  # times to loop, for output buffers only
    ('lpNext', PVOID),            # reserved, struct wavehdr_tag *lpNext
    ('reserved', wintypes.DWORD)] # reserved
# The lpData, dwBufferLength, and dwFlags members must be set before calling
# the waveInPrepareHeader or waveOutPrepareHeader function. (For either
# function, the dwFlags member must be set to zero.)
# --- /define

class AudioWriter(object):
  def __init__(self, hwaveout):
    self.hwaveout = hwaveout
    self.wavehdr = WAVEHDR()

  def write(self, data):
    self.wavehdr.dwBufferLength = len(data)
    self.wavehdr.lpData = data
    
    # Prepare block for playback
    if ctypes.windll.winmm.waveOutPrepareHeader(
         self.hwaveout, ctypes.byref(self.wavehdr), ctypes.sizeof(self.wavehdr)
       ) != MMSYSERR_NOERROR:
      sys.exit('Error: waveOutPrepareHeader failed')

    # Write block, returns immediately unless a synchronous driver is
    # used (not often)
    if ctypes.windll.winmm.waveOutWrite(
         self.hwaveout, ctypes.byref(self.wavehdr), ctypes.sizeof(self.wavehdr)
       ) != MMSYSERR_NOERROR:
      sys.exit('Error: waveOutWrite failed')

    # [ ] calculate sleep delay based on sample length
    # iii [ ] Measure CPU usage spike during wait without delay
    import time
    time.sleep(1)

    # Wait until playback is finished
    while True:
      # unpreparing the header fails until the block is played
      ret = ctypes.windll.winmm.waveOutUnprepareHeader(
              self.hwaveout,
              ctypes.byref(self.wavehdr),
              ctypes.sizeof(self.wavehdr)
            )
      if ret == WAVERR_STILLPLAYING:
        import time
        time.sleep(1)
        continue
      if ret != MMSYSERR_NOERROR:
        sys.exit('Error: waveOutUnprepareHeader failed with code 0x%x' % ret)
      break


# [ ] it's no good to read all the PCM data into memory at once
data = open('95672__Corsica_S__frequency_change_approved.raw', 'rb').read()

aw = AudioWriter(hwaveout)
aw.write(data)


# x. Close Sound Device

ctypes.windll.winmm.waveOutClose(hwaveout)
print "Default Wave Audio output device is closed"

#-- /CHAPTER 1 --
&lt;/code&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;Windows also provides DirectSound API, but it looks too complicated for me ATM. The code above is also available from &lt;a href="https://bitbucket.org/techtonik/audiosocket"&gt;https://bitbucket.org/techtonik/audiosocket&lt;/a&gt; and marked with 0.1 tag. You may expect to find further modifications there.&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/701608418101240666/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/07/audio-output-on-windows-with-pure.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/701608418101240666?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/701608418101240666?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/07/audio-output-on-windows-with-pure.html" title="Audio output on Windows with pure Python + ctypes" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CE4GRHw4eSp7ImA9WhZWGE8.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-1897553707382843613</id><published>2011-05-19T09:25:00.000-07:00</published><updated>2011-05-19T09:35:25.231-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-19T09:35:25.231-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="vcs" /><category scheme="http://www.blogger.com/atom/ns#" term="svn" /><title>Why Subversion conflict resolution suxx?</title><content type="html">Because conflict resolution choice is confusing (for non-English natives for sure):&lt;br /&gt;
&lt;pre&gt;&lt;code class="prettyprint"
&gt;$ svn up
U &amp;nbsp; &amp;nbsp;bitten\slave.py
G &amp;nbsp; &amp;nbsp;bitten\tests\queue.py
Conflict discovered in 'bitten/queue.py'.
Select: (p) postpone, (df) diff-full, (e) edit,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (mc) mine-conflict, (tc) theirs-conflict,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (s) show all options:&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;
Should you&amp;nbsp;select&amp;nbsp;"theirs conflict" or "mine confict" to fix your code? In situation when you feel that incoming change is right you may want to select "mine conflict" as the change that conflicts and should be removed as a result. But the correct answer it to select "theirs conflict" as the change to be incorporated in your code. Subversion developers think that you're are not removing conflicts, but incorporating them into your code. If they could think of user action - it is &lt;b&gt;removing &lt;/b&gt;or &lt;b&gt;resolving conflict&lt;/b&gt;&amp;nbsp;in high-level terminology, or &lt;b&gt;selecting correct change&lt;/b&gt;&amp;nbsp;in&amp;nbsp;low-level description.&lt;br /&gt;
&lt;br /&gt;
Let's see the help:&lt;br /&gt;
&lt;pre&gt;&lt;code class="prettyprint"
&gt;Conflict discovered in 'bitten/slave.py'.
Select: (p) postpone, (df) diff-full, (e) edit,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (mc) mine-conflict, (tc) theirs-conflict,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (s) show all options: s
&amp;nbsp; (e) &amp;nbsp;edit &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - change merged file in an editor
&amp;nbsp; (df) diff-full &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;- show all changes made to merged file
&amp;nbsp; (r) &amp;nbsp;resolved &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - accept merged version of file
&amp;nbsp; (dc) display-conflict - show all conflicts (ignoring merged version)
&amp;nbsp; (mc) mine-conflict &amp;nbsp; &amp;nbsp;- accept my version for all conflicts (same)
&amp;nbsp; (tc) theirs-conflict &amp;nbsp;- accept their version for all conflicts (same)
&amp;nbsp; (mf) mine-full &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;- accept my version of entire file (even non-conflicts)
&amp;nbsp; (tf) theirs-full &amp;nbsp; &amp;nbsp; &amp;nbsp;- accept their version of entire file (same)
&amp;nbsp; (p) &amp;nbsp;postpone &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - mark the conflict to be resolved later
&amp;nbsp; (l) &amp;nbsp;launch &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - launch external tool to resolve conflict
&amp;nbsp; (s) &amp;nbsp;show all &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - show this list&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Frankly, I can't understand how &lt;b&gt;(mc)&lt;/b&gt; is different from &lt;b&gt;(mf)&lt;/b&gt; if both versions accept all changes. &lt;b&gt;(same)&lt;/b&gt; marker doesn't add much sense either. But even if help doesn't describe anything - these options were still added for a reason, and if we meditate a bit on them starting from the bottom comment to &lt;b&gt;(mf)&lt;/b&gt; help, it becomes clear that:&lt;br /&gt;
&lt;br /&gt;
1. &lt;b&gt;(mf)&lt;/b&gt; throws away any incoming changes for the given file even if some of them were merged successfully&lt;br /&gt;
2. that means &lt;b&gt;(mc)&lt;/b&gt; probably keeps the changes that were merged successfully&lt;br /&gt;
3. that means &lt;b&gt;(df)&lt;/b&gt; shows more changes than &lt;b&gt;(dc)&lt;/b&gt; output&lt;br /&gt;
&lt;br /&gt;
Recommendations? Introduce `&lt;b&gt;change&lt;/b&gt;` and `&lt;b&gt;conflict&lt;/b&gt;` terminology earlier, i.e. show info like `3 changes, 2 conflicts`. It will also allow incremental conflict resolution, like if you &lt;b&gt;(e)&lt;/b&gt;dited the counter is changed to `4 changes, 1 conflict` etc. Final recommendation if to replace `(ignoring merged version)` with `(ignoring merged changes)` string. There are surely more ways to improve this.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/1897553707382843613/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/05/why-subversion-conflict-resolution-suxx.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1897553707382843613?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1897553707382843613?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/05/why-subversion-conflict-resolution-suxx.html" title="Why Subversion conflict resolution suxx?" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DEIMQHw8eip7ImA9WhZXEEQ.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-7330819542248134898</id><published>2011-04-29T10:03:00.000-07:00</published><updated>2011-04-29T10:03:01.272-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-29T10:03:01.272-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="chromium" /><category scheme="http://www.blogger.com/atom/ns#" term="google" /><title>Chrome logo was awesome</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;This is a Chrome 11 logo.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-I7lP6bMbbsI/TbruzHgVtTI/AAAAAAAAAZY/iNfNuNuvqMk/s1600/new_chrome_logo_11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="181" src="http://2.bp.blogspot.com/-I7lP6bMbbsI/TbruzHgVtTI/AAAAAAAAAZY/iNfNuNuvqMk/s320/new_chrome_logo_11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="text-align: center;"&gt;This one is the old variant.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-LDaHoRmXnwg/TbrvAYebu3I/AAAAAAAAAZc/Gj211UFgShc/s1600/old_chrome_logo_is_awesome.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="178" src="http://2.bp.blogspot.com/-LDaHoRmXnwg/TbrvAYebu3I/AAAAAAAAAZc/Gj211UFgShc/s320/old_chrome_logo_is_awesome.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/7330819542248134898/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/04/chrome-logo-was-awesome.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7330819542248134898?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7330819542248134898?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/04/chrome-logo-was-awesome.html" title="Chrome logo was awesome" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-I7lP6bMbbsI/TbruzHgVtTI/AAAAAAAAAZY/iNfNuNuvqMk/s72-c/new_chrome_logo_11.png" height="72" width="72" /><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;C0cCQnkzeCp7ImA9WhZREUg.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-9161931139797217770</id><published>2011-04-06T21:37:00.000-07:00</published><updated>2011-04-06T21:37:43.780-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-06T21:37:43.780-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="scons" /><category scheme="http://www.blogger.com/atom/ns#" term="build" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><title>Finding unused files in a SCons project with Process Monitor</title><content type="html">New technologies are born and die, but one things remains in your project - their files. Quite often you have no idea about where are they used, and attempt to remove them may lead to serious consequences.&lt;br /&gt;
&lt;br /&gt;
Fortunately, if your project is managed by fine grained build system such as &lt;a href="http://www.scons.org/"&gt;SCons&lt;/a&gt;, if your build scripts are not &lt;a href="http://en.wikipedia.org/wiki/Glob_(programming)"&gt;globbing&lt;/a&gt; too much, there are chances you can find files that are not participating in the builds.&lt;br /&gt;
&lt;br /&gt;
Here is how to do this on Windows using &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896653"&gt;Process Monitor&lt;/a&gt; tool that intercepts all system calls including file access.&lt;br /&gt;
&lt;br /&gt;
While build systems are usually common for C/C++ and Java projects, it is possible to add fine-grained file usage control for any project. For example, SCons itself is written entirely in Python, it could run directly from the source checkout or build distributives from checkout. But instead, it uses build procedure to copy all necessary files from checkout into separate directory and do stuff from there.&lt;br /&gt;
&lt;br /&gt;
Thanks to that it is possible to see which files are no more actual. While it is possible to compare checkout source tree and copied directory trees, I'll go through the hells of monitoring system file access in a source tree during the build process using Process Monitor (FileMon in the past). Linux should have similar tools too - let me know how are they called.&lt;br /&gt;
&lt;br /&gt;
The process is the following:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Start Process Monitor&lt;/li&gt;
&lt;li&gt;Stop incoming event flood by (un)clicking Capture (Ctrl-E) button&lt;/li&gt;
&lt;li&gt;Open Filter (Ctrl-L) dialog to add some filters&lt;/li&gt;

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-h9OvBkQwtSs/TZ029cop9OI/AAAAAAAAAZM/H1fRSJ-C0ik/s1600/process_monitor_filter.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="195" width="400" src="http://2.bp.blogspot.com/-h9OvBkQwtSs/TZ029cop9OI/AAAAAAAAAZM/H1fRSJ-C0ik/s400/process_monitor_filter.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;SCons build is started by &lt;code&gt;bootstrap.py&lt;/code&gt; script from a root of SCons source checkout. The script is executed by python executable, so I add &lt;code&gt;python.exe&lt;/code&gt; process name to the filter. I know that &lt;code&gt;bootstrap.py&lt;/code&gt; copies files from &lt;code&gt;src/&lt;/code&gt; subdirectory, so it is the directory I need to monitor, so I add this dir to filters too.&lt;/p&gt;

&lt;li&gt;Go Tools -&gt; File Summary...&lt;/li&gt;

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-8x-iINT66iw/TZ010TFqkFI/AAAAAAAAAZE/JgnwuUuR_h0/s1600/process_monitor_file_summary.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="382" width="400" src="http://3.bp.blogspot.com/-8x-iINT66iw/TZ010TFqkFI/AAAAAAAAAZE/JgnwuUuR_h0/s400/process_monitor_file_summary.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;There is a list of paths catched by Process Monitor when listening to system calls. They are already filtered, but additional filters can be applied using bottom left button to make information even more useful.&lt;/p&gt;

&lt;li&gt;Export to CSV using Save...&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
Exported CSV is not very useful without some postprocessing. I used the following a script to compare the list of paths in CSV to actual &lt;code&gt;src/&lt;/code&gt; directory contents. This gives me names of files that were not touched during build at all.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;SRCDIR = "C:\\p\\python\\scons\\src"
CSVLIST = 'accessed_bootstrap_files.CSV'

import csv
import os

reader = csv.reader(open(CSVLIST))
header = reader.next()
pathidx = header.index("Path")
pathset = set([row[pathidx] for row in reader])

#for row in pathset:
#  print row

fileset = set()
for root, dirs, files in os.walk(SRCDIR):
  fileset.update( [os.path.join(root, f) for f in files] )
  if '.svn' in dirs:
    dirs.remove('.svn')  # don't visit .svn directories

if len(pathset &amp;amp; fileset) == 0:
  print 'Error: File sets do not intersect at all'

print "Files not found in source directory tree:"
for f in (pathset - fileset):
  if not os.path.isdir(f):
    print f

print
print "Untouched files in source directory tree:"
for f in sorted(fileset - pathset):
  if not os.path.isdir(f):
    print f
&lt;/code&gt;&lt;/pre&gt;I've found a few interesting things about SCons. Core tests are mixed with source files in repository checkout. They are not copied during bootstrap build. There are also few &lt;code&gt;setup.py&lt;/code&gt; files, post-install script and announcement that don't participate in the build.&lt;br /&gt;
&lt;br /&gt;
Here is the output of the above script:&lt;br /&gt;
&lt;code class="prettyprint"&gt;&lt;br /&gt;
Files not found in source directory tree:&lt;br /&gt;
&amp;lt;Total&gt;&lt;br /&gt;
&lt;br /&gt;
Untouched files in source directory tree:&lt;br /&gt;
C:\p\python\scons\src\.aeignore&lt;br /&gt;
C:\p\python\scons\src\Announce.txt&lt;br /&gt;
C:\p\python\scons\src\engine\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\ActionTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\BuilderTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\CacheDirTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\DefaultsTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\EnvironmentTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\ErrorsTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\ExecutorTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\JobTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\MemoizeTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Node\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Node\AliasTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Node\FSTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Node\NodeTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Node\PythonTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Optik\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\PathListTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Platform\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Platform\PlatformTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\SConfTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\SConsignTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\CTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\DirTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\FortranTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\IDLTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\LaTeXTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\ProgTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\RCTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Scanner\ScannerTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Script\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Script\MainTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Script\SConscriptTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\SubstTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\TaskmasterTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\.aeignore&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\JavaCommonTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\PharLapCommonTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\ToolTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\f03.xml&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Tool\msvsTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\UtilTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\BoolVariableTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\EnumVariableTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\ListVariableTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\PackageVariableTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\PathVariableTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\Variables\VariablesTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\WarningsTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\SCons\cppTests.py&lt;br /&gt;
C:\p\python\scons\src\engine\setup.cfg&lt;br /&gt;
C:\p\python\scons\src\engine\setup.py&lt;br /&gt;
C:\p\python\scons\src\script\.aeignore&lt;br /&gt;
C:\p\python\scons\src\script\scons-post-install.py&lt;br /&gt;
C:\p\python\scons\src\script\setup.cfg&lt;br /&gt;
C:\p\python\scons\src\script\setup.py&lt;br /&gt;
C:\p\python\scons\src\test_aegistests.py&lt;br /&gt;
C:\p\python\scons\src\test_files.py&lt;br /&gt;
C:\p\python\scons\src\test_interrupts.py&lt;br /&gt;
C:\p\python\scons\src\test_pychecker.py&lt;br /&gt;
C:\p\python\scons\src\test_setup.py&lt;br /&gt;
C:\p\python\scons\src\test_strings.py&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Hope this helps clean up your projects too.&lt;br /&gt;
&lt;br /&gt;
P.S. I wish there was a Python script replacement for Process Monitor, or at least that it could be controlled from command line.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/9161931139797217770/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/04/finding-unused-files-in-scons-project.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/9161931139797217770?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/9161931139797217770?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/04/finding-unused-files-in-scons-project.html" title="Finding unused files in a SCons project with Process Monitor" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-h9OvBkQwtSs/TZ029cop9OI/AAAAAAAAAZM/H1fRSJ-C0ik/s72-c/process_monitor_filter.png" height="72" width="72" /><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CUEMQHY-eCp7ImA9WhZSF0s.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-1399030920720425777</id><published>2011-03-29T06:03:00.000-07:00</published><updated>2011-04-02T10:01:21.850-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-02T10:01:21.850-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="twisted" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><title>Asynchronous input from Windows console</title><content type="html">&lt;b&gt;too long didn't read&lt;/b&gt; - &lt;a href="https://bitbucket.org/techtonik/async-console-input"&gt;https://bitbucket.org/techtonik/async-console-input&lt;/a&gt; - public domain/MIT example for Windows implemented in pure Python (ctypes)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;What is asynchronous input?&lt;/h3&gt;&lt;br /&gt;&lt;b&gt;Given:&lt;/b&gt; The program need to terminate immediately when a subprocess exits or user hits 'q'. It should not eat 100% CPU time.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Blocking synchronous input:&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;import sys, subprocess&lt;br /&gt;from msvcrt import getch, kbhit&lt;br /&gt;&lt;br /&gt;p = subprocess.Popen([r"notepad"], shell=True)&lt;br /&gt;&lt;br /&gt;while True:&lt;br /&gt;  char = getch()&lt;br /&gt;  if char == 'q':&lt;br /&gt;    sys.exit('terminated by user')&lt;br /&gt;  if p.poll() != None:&lt;br /&gt;    sys.exit('terminated by child exit')&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This one doesn't work (doesn't exits immediately when child process terminates), because getch() blocks execution key is pressed, and even if child process exits, it won't be detected until some key is pressed.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Non-blocking synchronous input (polling):&lt;/b&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;import sys, subprocess&lt;br /&gt;from msvcrt import getch, kbhit&lt;br /&gt;&lt;br /&gt;p = subprocess.Popen([r"notepad"], shell=True)&lt;br /&gt;&lt;br /&gt;while True:&lt;br /&gt;  while kbhit():&lt;br /&gt;    if getch() == 'q':&lt;br /&gt;      sys.exit('terminated by user')&lt;br /&gt;  if p.poll() != None:&lt;br /&gt;    sys.exit('terminated by child exit')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This works ok, but constant polling uses 100% of CPU resources.  It is possible to insert &lt;code&gt;time.sleep()&lt;/code&gt; instructions to reduce the carbon footprint, but these crutches will greatly slow down the console when you add &lt;a href="http://www.phoboslab.org/ztype/"&gt;z-type&lt;/a&gt; to console to spend your time until the background process is finished.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Asynchronous console input on Windows with Python&lt;/h3&gt;&lt;br /&gt;Asynchronous input allows your program to receive notification from operating system when an input is available. This means that you define events your program needs to &lt;b&gt;react&lt;/b&gt; (not necessary console events), send this list to operating system and put your program into &lt;code&gt;wait&lt;/code&gt; mode. When system sees this event, your wait function returns and it's possible to inspect/filter event to a greater detail.&lt;br /&gt;&lt;br /&gt;Windows provides &lt;b&gt;WaitForMultipleObjects&lt;/b&gt; wait function, and in Linux I believe it is &lt;b&gt;select&lt;/b&gt; call.  If you've tried to understand how &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt; works, but couldn't - will it make more clear if I say that &lt;b&gt;twisted reactor&lt;/b&gt; is a &lt;b&gt;wait function&lt;/b&gt;? What you do when you code with twisted is just configuring events to react, making chains of them so that one event reacts to another event. This allow to build very interesting, complex and de-coupled asynchronous applications in a couple of days.&lt;br /&gt; &lt;br /&gt;O.k. Getting back from Twisted to the asynchronous console input on Windows. Below is the full source code that uses &lt;b&gt;WaitForMultipleObjects&lt;/b&gt;. I am afraid that's minimal complete example  possible to build with ctypes using Windows API. Tested with Python 2.5&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Non-blocking asynchronous input from console:&lt;/b&gt;&lt;br /&gt;&lt;a href="https://bitbucket.org/techtonik/async-console-input"&gt;https://bitbucket.org/techtonik/async-console-input&lt;/a&gt;&lt;pre class="prettyprint"&gt;"""&lt;br /&gt;Example of non-blocking asynchronous console input using&lt;br /&gt;Windows API calls in Python.  This can become handy for&lt;br /&gt;async console tools such as IRC client.&lt;br /&gt;&lt;br /&gt;Public domain or MIT license&lt;br /&gt;by anatoly techtonik &lt;techtonik@gmail.com&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Notes:&lt;br /&gt;  1. WaitForMultipleObjects is used to listen for the&lt;br /&gt;     signals from process and stdin handles&lt;br /&gt;  2. When handle is signalled it remains in this state&lt;br /&gt;     until reset&lt;br /&gt;  3. msvcrt.* keyboard functions don't clear signalled&lt;br /&gt;     state from stdin handle, that's why console API&lt;br /&gt;     functions are used to clear the input buffer&lt;br /&gt;     instead of kbhit()/getch() loop&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;import ctypes&lt;br /&gt;import ctypes.wintypes&lt;br /&gt;import subprocess&lt;br /&gt;&lt;br /&gt;# open notepad in separate process and monitor its execution&lt;br /&gt;# at the same time asynchronously processing events from&lt;br /&gt;# standard input without wasting 100% CPU on looping&lt;br /&gt;&lt;br /&gt;# OpenProcess desired access flag&lt;br /&gt;#   "the right to use the object for synchronization. This&lt;br /&gt;#    enables a thread to wait until the object is in the&lt;br /&gt;#    signaled state"&lt;br /&gt;SYNCHRONIZE=0x00100000L&lt;br /&gt;# Constant to get stdin handle with GetStdHandle() call&lt;br /&gt;STD_INPUT_HANDLE = -10&lt;br /&gt;# Constant for infinite timeout in WaitForMultipleObjects()&lt;br /&gt;INFINITE = -1&lt;br /&gt;&lt;br /&gt;# --- processing input structures -------------------------&lt;br /&gt;# INPUT_RECORD structure&lt;br /&gt;#  events:&lt;br /&gt;EVENTIDS = dict(&lt;br /&gt;  FOCUS_EVENT = 0x0010,&lt;br /&gt;  KEY_EVENT = 0x0001,      # only key event is handled&lt;br /&gt;  MENU_EVENT = 0x0008,&lt;br /&gt;  MOUSE_EVENT = 0x0002,&lt;br /&gt;  WINDOW_BUFFER_SIZE_EVENT = 0x0004)&lt;br /&gt;EVENTS = dict(zip(EVENTIDS.values(), EVENTIDS.keys()))&lt;br /&gt;#  records:&lt;br /&gt;class _uChar(ctypes.Union):&lt;br /&gt;  _fields_ = [('UnicodeChar', ctypes.wintypes.WCHAR),&lt;br /&gt;              ('AsciiChar', ctypes.wintypes.c_char)]&lt;br /&gt;class KEY_EVENT_RECORD(ctypes.Structure):&lt;br /&gt;  _fields_ = [&lt;br /&gt;    ('keyDown', ctypes.wintypes.BOOL),&lt;br /&gt;    ('repeatCount', ctypes.wintypes.WORD),&lt;br /&gt;    ('virtualKeyCode', ctypes.wintypes.WORD),&lt;br /&gt;    ('virtualScanCode', ctypes.wintypes.WORD),&lt;br /&gt;    ('char', _uChar),&lt;br /&gt;    ('controlKeyState', ctypes.wintypes.DWORD)]&lt;br /&gt;class _Event(ctypes.Union):&lt;br /&gt;  _fields_ = [('keyEvent', KEY_EVENT_RECORD)]&lt;br /&gt;  #  MOUSE_EVENT_RECORD        MouseEvent;&lt;br /&gt;  #  WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;&lt;br /&gt;  #  MENU_EVENT_RECORD         MenuEvent;&lt;br /&gt;  #  FOCUS_EVENT_RECORD        FocusEvent;&lt;br /&gt;class INPUT_RECORD(ctypes.Structure):&lt;br /&gt;  _fields_ = [&lt;br /&gt;    ('eventType', ctypes.wintypes.WORD),&lt;br /&gt;    ('event', _Event)]&lt;br /&gt;# --- /processing input structures ------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;np = subprocess.Popen([r"notepad"],&lt;br /&gt;       stdin=subprocess.PIPE,&lt;br /&gt;       stdout=subprocess.PIPE,&lt;br /&gt;       stderr=subprocess.PIPE,&lt;br /&gt;       shell=True)&lt;br /&gt;# OpenProcess returns handle that can be used in wait functions&lt;br /&gt;# params: desiredAccess, inheritHandle, processId&lt;br /&gt;nph = ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, np.pid)&lt;br /&gt;print("Started Notepad with pid=%s, handle=%s" % (np.pid, nph))&lt;br /&gt;&lt;br /&gt;ch = ctypes.windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)&lt;br /&gt;&lt;br /&gt;handles = [ch, nph]&lt;br /&gt;&lt;br /&gt;ctypes.windll.kernel32.FlushConsoleInputBuffer(ch)&lt;br /&gt;eventnum = ctypes.wintypes.DWORD()&lt;br /&gt;eventread = ctypes.wintypes.DWORD()&lt;br /&gt;inbuf = (INPUT_RECORD * 1)()&lt;br /&gt;&lt;br /&gt;print "[q]uit, [s]top console processing, launch bro[w]ser"&lt;br /&gt;stopflag = False&lt;br /&gt;while not stopflag and nph in handles:&lt;br /&gt;    print "Waiting for handles %s.." % handles&lt;br /&gt;&lt;br /&gt;    HandleArrayType = ctypes.wintypes.HANDLE * len(handles)&lt;br /&gt;    handle_array = HandleArrayType(*handles)&lt;br /&gt;    # params: count, handles, waitAll, milliseconds&lt;br /&gt;    ret = ctypes.windll.kernel32.WaitForMultipleObjects(&lt;br /&gt;            len(handle_array), handle_array, False, INFINITE)&lt;br /&gt;&lt;br /&gt;    if handles[ret] == ch:&lt;br /&gt;        """&lt;br /&gt;        # msvcrt won't work, because it doesn't reset&lt;br /&gt;        # signalled state of stdin handle&lt;br /&gt;        import msvcrt&lt;br /&gt;        while msvcrt.kbhit():&lt;br /&gt;            print "key!"&lt;br /&gt;            print msvcrt.getch()&lt;br /&gt;        continue&lt;br /&gt;        """&lt;br /&gt;        # --- processing input ---------------------------&lt;br /&gt;        ctypes.windll.kernel32.GetNumberOfConsoleInputEvents(&lt;br /&gt;          ch, ctypes.byref(eventnum))&lt;br /&gt;        for i in range(eventnum.value):&lt;br /&gt;          # params: handler, buffer, length, eventsnum&lt;br /&gt;          ctypes.windll.kernel32.ReadConsoleInputW(&lt;br /&gt;            ch, ctypes.byref(inbuf), 2, ctypes.byref(eventread))&lt;br /&gt;          if EVENTS[inbuf[0].eventType] != 'KEY_EVENT':&lt;br /&gt;            print EVENTS[inbuf[0].eventType]&lt;br /&gt;            pass&lt;br /&gt;          else:&lt;br /&gt;            keyEvent = inbuf[0].event.keyEvent&lt;br /&gt;            if not keyEvent.keyDown:&lt;br /&gt;              continue&lt;br /&gt;            char = keyEvent.char.UnicodeChar.lower()&lt;br /&gt;            #print char, keyEvent&lt;br /&gt;            if char == 'q':&lt;br /&gt;              print('[q] key pressed. Exiting..')&lt;br /&gt;              stopflag = True&lt;br /&gt;            elif char == 's':&lt;br /&gt;              handles.remove(ch)&lt;br /&gt;            elif char == 'w':&lt;br /&gt;              import webbrowser&lt;br /&gt;              webbrowser.open('http://techtonik.rainforce.org')&lt;br /&gt;            #print char&lt;br /&gt;        # --- /processing input --------------------------&lt;br /&gt;&lt;br /&gt;    elif handles[ret] == nph:&lt;br /&gt;        print("Notepad is closed. Exiting..")&lt;br /&gt;        handles.remove(nph)&lt;br /&gt;    else:&lt;br /&gt;        print("Warning: Unknown return value '%s'" % ret)&lt;br /&gt;&lt;br /&gt;ctypes.windll.kernel32.FlushConsoleInputBuffer(ch)&lt;br /&gt;ctypes.windll.kernel32.CloseHandle(nph)&lt;br /&gt;print "Done."&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Where can it be useful?&lt;/h3&gt;&lt;br /&gt;Writing first Windows console IRC client in Python and make it cross-platform? Network log viewers with keyboard shortcuts? Real-time roguelikes? Matrix sniffer screensaver? I don't know - its your time. Hope I saved you some though.&lt;br /&gt;&lt;br /&gt;Enjoy! If you want to enhance this stuff - feel free to join ever empty &lt;a href="https://groups.google.com/forum/#!forum/rainforce"&gt;https://groups.google.com/forum/#!forum/rainforce&lt;/a&gt; for public discussion.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/1399030920720425777/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/03/asynchronous-input-from-windows-console.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1399030920720425777?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1399030920720425777?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/03/asynchronous-input-from-windows-console.html" title="Asynchronous input from Windows console" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DkQCQHw4eCp7ImA9WhZTGE0.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-6417076885534119284</id><published>2011-03-22T07:24:00.000-07:00</published><updated>2011-03-22T07:32:41.230-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-22T07:32:41.230-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="evaluation" /><category scheme="http://www.blogger.com/atom/ns#" term="api" /><category scheme="http://www.blogger.com/atom/ns#" term="idea" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="framework" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="nosql" /><category scheme="http://www.blogger.com/atom/ns#" term="design" /><category scheme="http://www.blogger.com/atom/ns#" term="flow" /><category scheme="http://www.blogger.com/atom/ns#" term="appengine" /><title>User Pattern: Button Status</title><content type="html">&lt;h4&gt;Introduction into User Patterns&lt;/h4&gt;&lt;br /&gt;
Many of you know about &lt;b&gt;&lt;a href="http://en.wikipedia.org/wiki/Design_pattern_(computer_science)"&gt;Design Patterns&lt;/a&gt;&lt;/b&gt; of software development, invented to make complicated solutions look less complicated. The problem with Design Patterns is that they describe solutions to coding problems - not for user problem. People abusing Design Patterns tend to align the functionality of application to patterns, sacrificing features users wanted. The application that looks well-designed in the eyes of developers, in fact makes even simple features hard to implement or just takes too much time. As time passes, users become dissatisfied and eventually lose all interest in the product.&lt;br /&gt;
&lt;br /&gt;
People tried to approach the problem from the other end with &lt;b&gt;User Interface Design&lt;/b&gt; guidelines. This often made developers unhappy, because their Design Patterns don't explain how to work with web and Ajax application where "business logic" becomes shared between client and server. Developers wanted to see how different frameworks approach this stuff and to make the development process more intuitive. For this reason there should be a way to see how some typical features/functionality are implemented in different frameworks and this should be more detailed than a wiki or a blog in 15 minutes.&lt;br /&gt;
&lt;br /&gt;
So, let me introduce &lt;b&gt;User Patterns&lt;/b&gt;. User Pattern is a very abstract story, missing details about design decisions, but detailed enough to provide implementation and see &lt;b&gt;how _simple_, _extensible_ and _maintainable_&lt;/b&gt; this implementation is.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Button Status User Pattern&lt;/h4&gt;&lt;br /&gt;
&lt;b&gt;User story: &lt;/b&gt;I want a page with a button and a status field displayed. When I click the button, the status should be updated.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Description: &lt;/b&gt;There are five levels in the quest for the implementation of this pattern.&lt;br /&gt;
Level 1. The status is blank - it is fetched/calculated only when the button is pressed.&lt;br /&gt;
Level 2. The status is initially available - and it is refreshed/calculated when the button is pressed.&lt;br /&gt;
Level 3. When refresh/recalculation fails, the error value is displayed in status field.&lt;br /&gt;
Level 4. When refresh/recalculation fails, the error value is displayed in status field along with user friendly message in separate field&lt;br /&gt;
Level 5. The status value is stored in application database - when the button is pressed, it is recalculated, saved and only when displayed to the user&lt;br /&gt;
Level 6. When recalculation/save or fetch operation fails, user should be presented with friendly message explaining the error and further action (i.e. report problem link)&lt;br /&gt;
&lt;br /&gt;
I'm looking forward to create samples for AppEngine webapp, classic Django and Django-nonrel. Feel free to share your snippets.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/6417076885534119284/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/03/user-pattern-button-status.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6417076885534119284?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6417076885534119284?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/03/user-pattern-button-status.html" title="User Pattern: Button Status" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DUYFRn85fip7ImA9Wx9WFkk.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-588728865863622527</id><published>2011-01-21T13:57:00.000-08:00</published><updated>2011-01-21T13:58:37.126-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-21T13:58:37.126-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="pyside" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>[PySide] Python for Qt version 1.0.0~beta4 "I have altered the deal" released</title><content type="html">I can't see this post on &lt;a href="http://planet.python.org"&gt;planet.python.org&lt;/a&gt; and can't resist to share the good news about new &lt;a href="http://www.pyside.org/"&gt;PySide&lt;/a&gt; release.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;The PySide team is happy to announce the fourth beta release of PySide:
Python for Qt. New versions of some of the PySide toolchain components
apiextractor, generatorrunner, shiboken, libpyside, pyside-tools have been
released as well.

Like the others, this is a source code release only; we hope our community
packagers will be providing provide binary packages shortly. To acquire the
source code packages, refer to our download wiki page [1] or pull the relevant
tagged versions from our git repositories [2].

Major changes since 1.0.0~beta3
===============================

This is a bug fix release. Since beta3, a total of 47 high-priority
bugs have been fixed. See the list of fixed bugs at the end of this message.

Path towards 1.0 release
========================

There are still some outstanding bugs in our Bugzilla [3]. To have these
fixed, we plan to do other beta in two weeks. The beta cycle will continue
until we have all P2 bugs fixed.


About PySide
============

PySide is the Nokia-sponsored Python Qt bindings project, providing
access to not only the complete Qt 4.7 framework but also Qt Mobility,
as well as to generator tools for rapidly generating bindings for any
Qt-based libraries.

The PySide project is developed in the open, with all facilities you'd
expect from any modern OSS project such as all code in a git repository
[2], an open Bugzilla [5] for reporting bugs, and an open design
process [4]. We welcome any contribution without requiring a transfer of
copyright.

List of bugs fixed
==================

624     button click emit doesn't work
484     Error compiling QtContacts 1.1 (problems with const QList&lt;qvariant&gt;)
498     powerStateChanged-SIGNAL not emitted!
509     Can't use Shiboken when both Debug and Released are installed.
528     Connecting to SIGNAL positionUpdated fails
552     Segmentation fault when using QUiLoader and QTabWidget
553     A warning against using QUILoader is needed in the documentation
560     Lack of QtCore.Signal documentation
582     Python slots don't get called when they have a custom decorator
589     Crash related to QGraphicsProxyWidget and QVariant
592     shiboken.dll produces a segmentation fault when reloading a PySide module
608     Photoviewer example missing license boilerplates and shebang lines
609     Python site-packages path cannot be customized
610     QWidgetItemV2 not exposed to Python
626     Problem building PySide on OS X (qabstractfileengine_wrapper.cpp:
No such file or directory)
406     Unable to send instant messages using QMessageService
458     Doesn't build with QtMobility 1.1.0~beta2
487     Support QContactDetailFieldDefinition.setAllowableTypes
497     Miising __lt__ operators in QtMobility::QGeoMapObject
499     QFeedbackHapticsInterface and QFeedbackFileInterface are broken
511     QPainter doesn't respect Qt.NoPen
522     example/threads/mandelbrot.py crashes on exit
523     QWidget.winId() returns PyCObject (expected unsigned long)
530     Importing division from future breaks QPoint division
531     sessionProperty "ConnectInBackground" does not work
539     MCC and MNC interchanged
541     QTableWidget.itemAt(row, col) always returns item at 0, 0.
550     Can't call PySide slot from QtScript when the args are a list of anything.
556     QGraphicsScene.addItem performs very poorly when the scene has &gt;10000 items
562     pyside-uic does not generate some layers properties
568     List insertion time grows with list size
574     In docs of QUuid there's documentation for a function called
"operator QString"
575     Strange behaviour of QTextEdit.ExtraSelection().cursor
584     python pickle module can't treat QByteArray object of PySide
591     QtCore.QRect has no attribute "getRect()" in Windows binary
611     enum values lack a tp_print implementation
614     FAil to register 2 objects in the same address
619     never automatically delete a QWidget that has no parent widget and
is visible
620     QAbstractItemModel.createIndex(int,int,PyObject*) does not
increment refcount
621     QGLWidget.bindTexture(QString) does not bind the texture correctly
622     PPA pyside is broken on Ubuntu 10.10
623     QGLWidget.bindTexture(QPixmap, GLenum, GLenum) is missing
625     QFileDialog return a tuple instead of a unicode
628     pyside-uic can't effect "headerVisible" attribute for QTreeView
and QTreeWidget
232     [FTBFS] Fails to build on hurd-i386 (Test "lock" hangs for more
than 191 minutes)
255     Test qtscripttools_debugger segfaults on ia64
298     Contact subtype not correctly set



References
==========

[1] &lt;a href="http://developer.qt.nokia.com/wiki/PySideDownloads"&gt;http://developer.qt.nokia.com/wiki/PySideDownloads&lt;/a&gt;
[2] &lt;a href="http://qt.gitorious.org/pyside"&gt;http://qt.gitorious.org/pyside&lt;/a&gt;
[3] &lt;a href="http://bugs.openbossa.org/"&gt;http://bugs.openbossa.org/&lt;/a&gt;
[4] &lt;a href="http://www.pyside.org/docs/pseps/psep-0001.html"&gt;http://www.pyside.org/docs/pseps/psep-0001.html&lt;/a&gt;

Thanks
PySide team.
&lt;/pre&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/588728865863622527/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/01/pyside-python-for-qt-version-100beta4-i.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/588728865863622527?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/588728865863622527?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/01/pyside-python-for-qt-version-100beta4-i.html" title="[PySide] Python for Qt version 1.0.0~beta4 &quot;I have altered the deal&quot; released" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;Ak4MSXY8fyp7ImA9Wx9WE0U.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-4054234440240055413</id><published>2011-01-18T14:16:00.000-08:00</published><updated>2011-01-18T14:16:28.877-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-18T14:16:28.877-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="pyside" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>Python GUIniverse</title><content type="html">I am on the &lt;a href="http://pyside.org"&gt;PySide&lt;/a&gt; of the Force.</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/4054234440240055413/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/01/python-guiniverse.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/4054234440240055413?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/4054234440240055413?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/01/python-guiniverse.html" title="Python GUIniverse" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DkAMQ3k-fip7ImA9Wx9XFEQ.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-6753018075190080454</id><published>2011-01-08T05:52:00.000-08:00</published><updated>2011-01-08T05:53:02.756-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-08T05:53:02.756-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mercurial" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="svn" /><category scheme="http://www.blogger.com/atom/ns#" term="py2exe" /><title>Injecting SVN bindings into Mercurial on Windows</title><content type="html">If you need to add &lt;a href="http://alagazam.net/"&gt;Subversion bindings&lt;/a&gt; for your Mercurial, which was installed using ordinary installer (and not &lt;a href="http://tortoisehg.bitbucket.org/"&gt;TortoiseHg&lt;/a&gt;), then the following script can help to put all files in place.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://bitbucket.org/techtonik/py2exe-relibzip/src/f15f8d032b5b/reinject.py"&gt;https://bitbucket.org/techtonik/py2exe-relibzip/src/f15f8d032b5b/reinject.py&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Unpack svn bindings, add all *.py files from svn/ and libsvn/ directories into the root of library.zip archive *with* these directories. Then call script with path to unpacked libsvn/. The script will generate some *.dll and *.py files in current directory. Put *.py into library.zip:/libsvn/ and *.dll into Mercurial installation directory. You may need to also copy libsvn_swig_py-1.dll into Mercurial dir.&lt;br /&gt;
&lt;br /&gt;
The script can be tuned to insert C extensions to any py2exe distribution compiled with bundle_files=3</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/6753018075190080454/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2011/01/injecting-svn-bindings-into-mercurial.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6753018075190080454?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6753018075190080454?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2011/01/injecting-svn-bindings-into-mercurial.html" title="Injecting SVN bindings into Mercurial on Windows" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CUcFSHw-eSp7ImA9Wx9SGUU.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-7065137041774403613</id><published>2010-12-10T04:10:00.000-08:00</published><updated>2010-12-10T04:10:19.251-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-10T04:10:19.251-08:00</app:edited><title>Got mail? Part II</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_-WP64rPqC8g/TQIYOD1QsYI/AAAAAAAAAXQ/YW4m975PbEU/s1600/ohno.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="80" width="93" src="http://1.bp.blogspot.com/_-WP64rPqC8g/TQIYOD1QsYI/AAAAAAAAAXQ/YW4m975PbEU/s320/ohno.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br clear="both"/&gt;&lt;br /&gt;
See &lt;a href="http://techtonik.rainforce.org/2007/01/got-mail.html"&gt;Part I&lt;/a&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/7065137041774403613/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2010/12/got-mail-part-ii.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7065137041774403613?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/7065137041774403613?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2010/12/got-mail-part-ii.html" title="Got mail? Part II" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_-WP64rPqC8g/TQIYOD1QsYI/AAAAAAAAAXQ/YW4m975PbEU/s72-c/ohno.png" height="72" width="72" /><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;DkUARn4-eyp7ImA9Wx5aFE0.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-6158535512976843323</id><published>2010-11-10T07:50:00.000-08:00</published><updated>2010-11-10T07:50:47.053-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-10T07:50:47.053-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ssl" /><category scheme="http://www.blogger.com/atom/ns#" term="https" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="security" /><title>Validating SSL server certificate with Python 2.x</title><content type="html">&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;SSL stands for&amp;nbsp;&lt;strong&gt;Secure Sockets Layer&lt;/strong&gt;&amp;nbsp;and is designed to create secure connection between client and server. Secure means that connection is encrypted and therefore protected from eavesdropping. It also allows to validate site identity when connecting with HTTPS protocol.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;However, there is a &lt;a href="http://bugs.python.org/issue1589"&gt;bug&lt;/a&gt; in&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;&lt;strong&gt;ssl&lt;/strong&gt;&amp;nbsp;module from standard library of Python 2.x,&amp;nbsp;that allows successful &lt;a href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack"&gt;MITM&lt;/a&gt; attack using valid certificate from other site.&amp;nbsp;Basically, module checks&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;when connecting&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;&amp;nbsp;that server certificate is valid and correctly signed by root certificate, but it does not check that certificate actually belongs to the site, i.e. that site name matches the name specified in certificate.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;span class="Apple-style-span" style="font-size: 15px; line-height: 17px;"&gt;It is still possible to validate server identity in Python 2.6 manually. Let's start with illustration of the vulnerability.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif; font-size: 15px; line-height: 17px;"&gt;The following snippet should fail - it replaces HOST "www.google.com" to connect to with its IP address. If you try to use this IP in Chrome like&amp;nbsp;&lt;a class="https" href="https://74.125.232.50/" style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dashed; border-bottom-width: 1px; color: #0000aa; text-decoration: none;"&gt;https://74.125.232.50&lt;/a&gt;&amp;nbsp;- it will show an error, but&amp;nbsp;&lt;strong&gt;ssl&lt;/strong&gt;&amp;nbsp;library will not throw exception.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code class="prettyprint"&gt;import socket&lt;br /&gt;
import ssl&lt;br /&gt;
&lt;br /&gt;
HOST = "www.google.com"&lt;br /&gt;
PORT = 443&lt;br /&gt;
&lt;br /&gt;
# replace HOST name with IP, this should fail connection attempt,&lt;br /&gt;
# but it doesn't in Python 2.x&lt;br /&gt;
HOST = socket.getaddrinfo(HOST, PORT)[0][4][0]&lt;br /&gt;
print(HOST)&lt;br /&gt;
&lt;br /&gt;
# create socket and connect to server&lt;br /&gt;
# server address is specified later in connect() method&lt;br /&gt;
sock = socket.socket()&lt;br /&gt;
sock.connect((HOST, PORT))&lt;br /&gt;
&lt;br /&gt;
# wrap socket to add SSL support&lt;br /&gt;
sock = ssl.wrap_socket(sock,&lt;br /&gt;
  # flag that certificate from the other side of connection is&lt;br /&gt;
  # required and should be validated when wrapping &lt;br /&gt;
  cert_reqs=ssl.CERT_REQUIRED,&lt;br /&gt;
  # file with root certificates&lt;br /&gt;
  ca_certs="cacert.pem"&lt;br /&gt;
)&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;You will need cacert.pem file with root certificates. Just&amp;nbsp;grab the latest version from&amp;nbsp;&lt;a class="http" href="http://curl.haxx.se/ca/cacert.pem" style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dashed; border-bottom-width: 1px; color: #0000aa; text-decoration: none;"&gt;http://curl.haxx.se/ca/cacert.pem&lt;/a&gt;&amp;nbsp;This code above won't give you any error. Replace HOST value with&amp;nbsp;&lt;a class="https" href="https://www.debian-administration.org/" style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dashed; border-bottom-width: 1px; color: #0000aa; text-decoration: none;"&gt;https://www.debian-administration.org/&lt;/a&gt;&amp;nbsp;to check that certificate validation actually works.&amp;nbsp;This site's certificate is not signed by any root certificates from "cacerts.txt", so you get an error.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;To validate that a certificate matches requested site, you need to check&amp;nbsp;&lt;em&gt;commonName&lt;/em&gt;&amp;nbsp;field in the&amp;nbsp;&lt;em&gt;subject&lt;/em&gt;&amp;nbsp;of the certificate. This information can be accessed with&amp;nbsp;&lt;em&gt;&lt;a href="http://docs.python.org/release/2.6.6/library/ssl.html#ssl.SSLSocket.getpeercert"&gt;getpeercert&lt;/a&gt;()&lt;/em&gt;&amp;nbsp;method of wrapped socket.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-size: 15px; line-height: 17px;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;
&lt;code class="prettyprint"&gt;import socket&lt;br /&gt;
import ssl&lt;br /&gt;
&lt;br /&gt;
HOST = "www.google.com"&lt;br /&gt;
PORT = 443&lt;br /&gt;
&lt;br /&gt;
# replace HOST name with IP, this should fail connection attempt&lt;br /&gt;
HOST = socket.getaddrinfo(HOST, PORT)[0][4][0]&lt;br /&gt;
print(HOST)&lt;br /&gt;
&lt;br /&gt;
# create socket and connect to server&lt;br /&gt;
# server address is specified later in connect() method&lt;br /&gt;
sock = socket.socket()&lt;br /&gt;
sock.connect((HOST, PORT))&lt;br /&gt;
&lt;br /&gt;
# wrap socket to add SSL support&lt;br /&gt;
sock = ssl.wrap_socket(sock,&lt;br /&gt;
# flag that certificate from the other side of connection is&lt;br /&gt;
# required and should be validated when wrapping &lt;br /&gt;
cert_reqs=ssl.CERT_REQUIRED,&lt;br /&gt;
# file with root certificates&lt;br /&gt;
ca_certs="cacerts.txt"&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
# manual check of hostname&lt;br /&gt;
cert = sock.getpeercert()&lt;br /&gt;
for field in cert['subject']:&lt;br /&gt;
if field[0][0] == 'commonName':&lt;br /&gt;
certhost = field[0][1]&lt;br /&gt;
if certhost != HOST:&lt;br /&gt;
raise ssl.SSLError(&lt;br /&gt;
    "Host name '%s' doesn't match certificate host '%s'"&lt;br /&gt;
    % (HOST, certhost))&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;span class="Apple-style-span" style="font-size: 15px; line-height: 17px;"&gt;That's it. I put my findings to &lt;a href="http://wiki.python.org/moin/SSL"&gt;http://wiki.python.org/moin/SSL&lt;/a&gt; - you may want check it for updates.&lt;/span&gt;&lt;/span&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/6158535512976843323/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2010/11/validating-ssl-server-certificate-with.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6158535512976843323?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/6158535512976843323?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2010/11/validating-ssl-server-certificate-with.html" title="Validating SSL server certificate with Python 2.x" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry><entry gd:etag="W/&quot;CUcBQn45fCp7ImA9WhZQF08.&quot;"><id>tag:blogger.com,1999:blog-1623059707500976008.post-1385713340840784947</id><published>2010-09-22T13:05:00.000-07:00</published><updated>2011-04-25T02:17:33.024-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-25T02:17:33.024-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="hg" /><category scheme="http://www.blogger.com/atom/ns#" term="vcs" /><category scheme="http://www.blogger.com/atom/ns#" term="svn" /><category scheme="http://www.blogger.com/atom/ns#" term="py2exe" /><category scheme="http://www.blogger.com/atom/ns#" term="hack" /><title>hgsubversion: Installing on Windows</title><content type="html">&lt;ul style="font-family: arial, sans-serif; font-size: small; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span" style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px; white-space: normal;"&gt;&lt;ul style="font-family: arial, sans-serif; font-size: small; line-height: 1.4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span" style="color: black; line-height: normal;"&gt;&lt;ul style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;I use Windows primarily, and it's not easy to install Mercurial extensions there if they are not bundled with Mercurial installer itself. I am using plain .msi installer, which doesn't include &lt;span style="font-weight: bold;" x="y"&gt;hgsubversion&lt;/span&gt;, so I'll show you how to get this extension in place manually.

Suppose you have Mercurial installed into C:\Mercurial Go there and notice &lt;span style="font-weight: bold;" x="y"&gt;library.zip&lt;/span&gt; archive. This is where you should add &lt;span style="font-weight: bold;" x="y"&gt;hgsubversion&lt;/span&gt;. &lt;span style="font-weight: bold;" x="y"&gt;library.zip&lt;/span&gt; has the following structure:

/
+-- email
+-- encodings
+-- hgext
+-- logging
...
__future__.pyc
_abcoll.pyc
_elementtree.pyd
...
zipextimporter.pyc
zipfile.pyc

Contents of this archive is the whole Mercurial code. Code is written in Python, and is compiled into .pyc and .pyd files for speed and packed into &lt;span style="font-weight: bold;" x="y"&gt;library.zip&lt;/span&gt; for convenience. For interpreted languages like Python it is not necessary to compile source files for execution, but they did it.

Get latest &lt;span style="font-weight: bold;" x="y"&gt;hgsubversion&lt;/span&gt; sources from &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Fbitbucket.org%2Fdurin42%2Fhgsubversion%2Fsrc" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;http://bitbucket.org/durin42/hgsubversion/src&lt;/a&gt; by pressing link "get source" in upper right corner. Or download released version from &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Fpypi.python.org%2Fpypi%2Fhgsubversion%2F1.1.2" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;http://pypi.python.org/pypi/hgsubversion/1.1.2&lt;/a&gt;

Unpack archive. You should have directory structure like:

/
+-- hgsubversion
+-- notes
+-- tests
+-- tools
.hgignore
...
setup.py

Now put hgsubversion/ dir from this structure into &lt;span style="font-weight: bold;" x="y"&gt;library.zip&lt;/span&gt; archive, inside hgext/ directory, where Mercurial expects to find its extensions.

Enable extension by uncommenting line ";hgsubversion =" in &lt;span style="font-weight: bold;" x="y"&gt;Mercurial.ini&lt;/span&gt; that is usually located in your profile directory, i.e. C:\Users\yourname If it is not there - copy one from your &lt;span style="font-weight: bold;" x="y"&gt;C:\Mercurial\hgrc.d&lt;/span&gt; directory. Try to checkout some subversion repository. You will get an error like this:

C:\p&amp;gt;hg clone &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Fgoogle-twitter.googlecode.com%2Fsvn%2Ftrunk" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;http://google-twitter.googlecode.com/svn/trunk&lt;/a&gt;
(falling back to static-http)
(falling back to Subversion support)
destination directory: trunk
abort: subvertpy 0.7.3 or later required, but not found!

You need Subversion bindings for Python. They advertise subvertpy, but it is also possible to use official bindings. Those bindings is just a set of libraries that allows Python scripts to use Subversion binary code directly. Unfortunately, after migration to &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Fapache.org" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;apache.org&lt;/a&gt;, Subversion bindings are not compiled anymore, so there is no official place where you can download them. But thanks to &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;http://&lt;/a&gt;&lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;alagazam&lt;/a&gt;&lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;.net/&lt;/a&gt; it is possible to get unofficial .

Download &lt;span style="font-weight: bold;" x="y"&gt;svn-win32-1.6.12_py.zip&lt;/span&gt; or more up-to-date distribution and extract it. You will get a directory structure like:

/
+-- python
+-- libsvn
+-- svn
README.txt

In libsvn/ rename _client.dll to _client.pyd. Do the same with _core.dll, _delta.dll, _ra.dll
Maybe you'll have to rename other .dll files, but these were enough for me. Now put svn/ and libsvn/ into the library.zip directly in the root. I also had to copy intl3_svn.dll from an old 1.6.6 installation of SVN into library.zip root (you may find this file in Windows binaries archive from &lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;http://&lt;/a&gt;&lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;alagazam&lt;/a&gt;&lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http%3A%2F%2Falagazam.net%2F" style="color: #003ea8; font-family: arial, sans-serif;" target="_blank" x="y"&gt;.net/&lt;/a&gt;) and place libsvn_swig_py-1.dll from libsvn/ dir also in library.zip root.

It should work now.&lt;span class="__wave_paste" data-wave-annotations="242,254,style%2FfontWeight,bold:396,407,style%2FfontWeight,bold:446,458,style%2FfontWeight,bold:460,471,style%2FfontWeight,bold:797,808,style%2FfontWeight,bold:954,966,style%2FfontWeight,bold:980,1025,link%2Fmanual,http%3A%2F%2Fbitbucket.org%2Fdurin42%2Fhgsubversion%2Fsrc:1113,1159,link%2Fmanual,http%3A%2F%2Fpypi.python.org%2Fpypi%2Fhgsubversion%2F1.1.2:1357,1368,style%2FfontWeight,bold:1513,1526,style%2FfontWeight,bold:1641,1660,style%2FfontWeight,bold:2364,2384,link%2Fmanual,http%3A%2F%2Falagazam.net%2F:2432,2455,style%2FfontWeight,bold:2995,3015,link%2Fmanual,http%3A%2F%2Falagazam.net%2F:" data-wave-xml="hgsubversion: Installing on Windows&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;I am using Windows primarily, and it's not very easy to install Mercurial extensions there if they are not bundled with Mercurial installer itself. I am using plain .msi installer, which doesn't include hgsubversion, so I'll show you how to get this extension in place manually.&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Suppose you have Mercurial installed into C:\Mercurial Go there and notice library.zip archive. This is where you should add hgsubversion. library.zip has the following structure:&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;/&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- email&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- encodings&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- hgext&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- logging&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;...&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;__future__.pyc&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;_abcoll.pyc&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;_elementtree.pyd&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;...&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;zipextimporter.pyc&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;zipfile.pyc&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Contents of this archive is the whole Mercurial code. Code is written in Python, and is compiled into .pyc and .pyd files for speed and packed into library.zip for convenience. For interpreted languages like Python it is not necessary to compile source files for execution, but they did it.&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Get latest hgsubversion sources from http://bitbucket.org/durin42/hgsubversion/src by pressing link &amp;quot;get source&amp;quot; in upper right corner. Or download released version from http://pypi.python.org/pypi/hgsubversion/1.1.2&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Unpack archive. You should have directory structure like:&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;/&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- hgsubversion&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- notes&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- tests&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- tools&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;.hgignore&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;...&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;setup.py&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Now put hgsubversion/ dir from this structure into library.zip archive, inside hgext/ directory, where Mercurial expects to find its extensions.&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Enable extension by uncommenting line &amp;quot;;hgsubversion =&amp;quot; in Mercurial.ini that is usually located in your profile directory, i.e. C:\Users\yourname If it is not there - copy one from your C:\Mercurial\hgrc.d directory. Try to checkout some subversion repository. You will get an error like this:&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;C:\p&amp;amp;gt;hg clone http://google-twitter.googlecode.com/svn/trunk&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;(falling back to static-http)&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;(falling back to Subversion support)&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;destination directory: trunk&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;abort: subvertpy 0.7.3 or later required, but not found!&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;You need Subversion bindings for Python. They advertise subvertpy, but it is also possible to use official bindings. Those bindings is just a set of libraries that allows Python scripts to use Subversion binary code directly. Unfortunately, after migration to apache.org, Subversion bindings are not compiled anymore, so there is no official place where you can download them. But thanks to http://alagazam.net/ it is possible to get unofficial .&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Download svn-win32-1.6.12_py.zip or more up-to-date distribution and extract it. You will get a directory structure like:&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;/&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;+-- python&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;    +-- libsvn&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;    +-- svn&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;README.txt&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;In libsvn/ rename _client.dll to _client.pyd. Do the same with _core.dll, _delta.dll, _ra.dll&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;Maybe you'll have to rename other .dll files, but these were enough for me. Now put svn/ and libsvn/ into the library.zip directly in the root. I also had to copy intl3_svn.dll from an old 1.6.6 installation of SVN into library.zip root (you may find this file in Windows binaries archive from http://alagazam.net/) and place libsvn_swig_py-1.dll from libsvn/ dir also in library.zip root.&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line&amp;gt;&amp;lt;/line&amp;gt;It should work now."&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/ul&gt;&lt;/span&gt;&lt;/ul&gt;&lt;/span&gt;&lt;/ul&gt;</content><link rel="replies" type="application/atom+xml" href="http://techtonik.rainforce.org/feeds/1385713340840784947/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://techtonik.rainforce.org/2010/09/hgsubversion-installing-on-windows.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1385713340840784947?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1623059707500976008/posts/default/1385713340840784947?v=2" /><link rel="alternate" type="text/html" href="http://techtonik.rainforce.org/2010/09/hgsubversion-installing-on-windows.html" title="hgsubversion: Installing on Windows" /><author><name>anatoly techtonik</name><uri>https://plus.google.com/110592708597698994191</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-7aZLGcpl-zQ/AAAAAAAAAAI/AAAAAAAAAAA/2DiBG26LgA0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /></entry></feed>
