2011-01-31

PSF core grant, day 19: more fun with imp.reload()

[From now on I will try to include a tl;dr line in the title of posts so people get a better idea of what the post is about, but I am not dropping the day count since it helps me make sure I don't fall behind in work]

Side-effects upon import are such a pain to deal with. Now this is not me speaking with my "import expert" cap on (although from that perspective I still think they suck, especially when people are crazy enough to spawn threads as a side-effect of importation, but that's another blog post), but as someone who has to deal with stdlib code.

There are various modules which like to cache stuff at the global level of a module. Good examples are copyreg and warnings. Others like to execute code, like abc. And then you have tests which like to do direct class comparisons to verify the proper class is returned by some API call. All of this breaks if you do module reloading as assumptions about the state of the module quickly go out the door. This sucks because there are no clear delineation as to which modules will collapse under reloading and which will be fine without manually looking at code to verify whether there is global code executed as part of an import (beyond obviously function and class definitions).

Now this could be solved in a couple of ways. Probably the most obvious is to have some specific module that exists to specifically to hold global data. Modules that have global data that they need to resist reloads would go there. A simple hasattr() check would then be able to determine if the module had already been loaded and thus didn't need to have the initialization of its global data. This also has the nice side-effect of centralizing global data for Python.

But an even easier solution is to simply test if the global holding the data is set:

try: state
except NameError: state = {}
else: state += 1

That will actually increment the state variable on every reload. This is because reloading a module is actually simply re-executing a module's globals over the module's original __dict__. So you could protect all of that global stuff in the 'except' clause so that it isn't re-initialized on reloads.

But doing any this will break backwards-compatibility because you know someone, somewhere is relying on reloading some module to reset it. Joys of working on the stdlib. But maybe people will want to be able to reload modules safely more?

The other option is for coverage.py to whitelist what modules can be reloaded when run with --pylib. All other modules already imported could then be flagged as unknown/unmeasured (which itself would require adding a new type to coverage.py), especially if only global-level code could be flagged as unmeasured and all local statements stayed flagged as not covered (and thus not tested).

At this point I consider my approach of blacklisting modules not to reload a failure. There are just too many modules that just do not care if their global state is completely reset upon reload (which perhaps may be a good thing since if you are reloading to pick up new code you would not want a cache of objects using an old class definition lying around).

PSF core grant, day 18

Spent the weekend do a little bit of writing along with a bunch of coverage debugging. A "fun" problem of working on Python is you end up in "interesting" situations that most people never have to deal with. For instance, the coverage results for some modules, (.e.g, stats) was really odd as all global statements were flagged as unexecuted.

Turns out that the stats module and others like it were simply being imported before coverage.py started running, so the normal execution side-effect of importing was not happening. But if you reload a module, that does trigger the coverage. Simple, right?

Wrong. Since reloading is not a common thing, not all modules have been tested to make sure they are robust in the face of a reload. For instance, issue 11074 has a fix for the tokenize module as it was trying to capture the built-in open() by simply assigning it to a global variable before the module defined its own open(). That fails under a reload, however, as the pre-existing module's open() gets picked up on the reload. The proper way to handle this is to use builtins.open() instead of doing the global assignment.

There are several other tests failing because of this. I'm hoping that I can narrow it down such that coverage.py, when run with its --pylib flag, can reload almost every module to get more accurate coverage information for all modules.

2011-01-29

PSF core grant, day 17

Yesterday was a writing/coding day. The coding surrounded getting Python 3.2's entire test suite to complete without issues under coverage.py. That required getting issue 10990 and 10992 (tests reseting the trace function and tests which simply failed under tracing, respectively). Along the way I created a fork of coverage.py on bitbucket which contains the patches I have sent upstream applied so that if people get antsy they don't have to download the patches separately (but I consider this a temporary issue). I also learned how to use Mercurial's mq extension on top of svn which worked out very nicely.

In terms of writing a firmed up the coverage instructions when using coverage.py, mostly so that generating HTML results is faster by skipping coverage of test files. I also did more writing on my Python 2/3 porting guide which I will make sure is up somewhere once the 2to3 portion of the doc is finished (hopefully mid next week).

2011-01-27

PSF core grant, day 16

Not an eventful day, just more work on the Python 2/3 porting guide. I am thinking I might actually toss the document up on Google Code under my personal repository. That way people can submit changes through a code review.

2011-01-26

Building Python with LLVM 2.8 (and why I still love the project)

With Python 3.2 due out in less than three weeks and LLVM 2.8 being the latest release of LLVM, I figure I will not be the only person in the world building Python with LLVM in the near future (let alone building other releases like Python 3.1.3 or Python 2.7.1 with LLVM 2.8). So, to simplify the lives of those that do, I just wanted to point out issue 10238 which notes that to build ctypes you should use the -no-integrated-as compiler flag (I have it set in my CFLAGS environment variable). Do note that LLVM 2.9 has a fix already and this issue only affects the ctypes module. I will see if I can't find some autoconf expert to write up a patch for Python 3.2 to auto-detect LLVM 2.8 and add the flag (if you are such an expert please submit a patch!).

But honestly I don't feel that put out by this issue. When I reported the bug to LLVM they responded within an hour that it had been fixed for LLVM 2.9 and what the fix was. This just continues my wonderful experience interacting with the LLVM project.

But beyond that I appreciate all the tools they provide. For instance, I just ran the Clang static analyzer over Python 3.2rc1 (see issue 8914). The HTML output is really slick! And it honestly was not difficult to do thanks to the command-line tool they provide to work with configure/make files. And even during normal compilation, the warnings that are outputted by clang are so much nicer than those from gcc that I honestly could not imagine going back to it for C/C++ compilation. I (continue) to highly recommend LLVM and clang for any C-related development.

PSF core grant, day 15

Today was another writing day (starting off with my last blog post no less). I wrote up a little guide about how to write a commit message for core developers to keep us all on the same page. I also introduced a doc that explained the development cycle for Python as a reminder that while something is in RC (like Python 3.2 is) that all commits need a peer review from another core developer.

I also started a new Python 3 porting HOWTO. It isn't up online yet as I need to figure out where it is going to live and if that place happens to be in py3k whether I should simply hold off on doing anything until Python 3.2 is out the door.

PSF core grant, day 14

Yesterday was all coding work on issue 10990. Basically there is a patch attached to that issue which (a) adds a test to regrtest to make sure no tests unset the trace function, and (b) fixes those tests that were unsetting. Coverage.py still complains about being unset, but I believe that is all thread-related as from regrtest's perspective no tests blank out the trace function with the patch.

I don't plan to apply the patch until Python 3.3 development opens up. While I technically could argue that this is all bug fixes, because we are in RC I would have to get another core developer to review the patch and I don't want to take anyone's time up for this patch as it will not impact the general Python community as much as other higher-priority bugs that need fixing and commit reviews.

2011-01-24

PyCon 2011 early bird registration ends tomorrow!

If you have been putting off registering for PyCon, your time is running out to get the early bird rate as it ends January 25th! As replies on this Buzz from Jesse Noller point out, registration will break the 1000 mark very soon, meaning that 2/3 of capacity for the conference is basically already gone. As for tutorials, one is full, two more have less than five seats, and three are quickly approaching capacity. So it is not just the conference that is filling up quickly.

PSF core grant, day 13

Today was part coding, part reading/thinking. The coding part was continuing to try to get it so that various tests in the test suite didn't reset the trace function set by coverage tools (this is being tracked in issue #10990). Turns out that there are typically two reasons why a test resets the trace function: it does it on purpose or forces a recursion depth RuntimeError. The former issues simply need to play nice and not wipe out the trace function blindly (as well as not run on VMs other than CPython since it's an implementation detail). The latter occurs because running up against the recursion limit usually ends up triggering the RuntimeError in the trace function, which causes the function to be removed under the assumption its buggy. Unless some fancy redesign of these semantics is developed the tests will simply need to have some guard that either unsets the trace functions temporarily or simply skips the test.

In terms of reading/thinking, I am trying to decide how I want to structure my Python 2->3 porting guide. My current thinking breaks down into three possible options for people. One, if you are creating a new project and your dependencies have already ported to Python 3, then write your project in Python 3 as well and use 3to2. If you have pre-existing Python 2 code and want to maintain a separate Python 3 codebase, use 2to3 to create a Python 3 branch of your code. Otherwise make your code source-compatible between Python 2 and 3 with the help of six.

2011-01-23

PSF core grant, day 12

Today was focused on diagnosing various issues related to running Python's test suite under coverage. I fixed a test in test_sys which triggered a fatal error under coverage.py. While doing that I discovered that a bunch of tests are changing what trace function is running, which is a no-no. This also led to discovering that a bunch of tests simply fail under coverage (a cursory look suggests that refcount tests are the culprit).

And this doesn't cover the specific issues of the two ways of generating coverage. For the trace module, the biggest issue is that it throws an exception if it can't find a file it traced. This is annoying for temporary files that created, imported, and then deleted; test_importlib and test_runpy does this along with others.

For coverage.py, I discovered that it is not opening source files based on the declared encoding. While typically this is not an issue, there are some files in the stdlib (e.g., Lib/sqlite/test/dbapi.py) which are encoded in Latin-1 and contain code points which are outside the valid range for UTF-8. Luckily Python 3.2 introduced tokenize.open() which allowed me to easily write a patch for the problem.

2011-01-22

PSF core grant, day 11 (devguide is in beta)

I consider the devguide beta quality. I have some bookkeeping things to do in terms of URLs, etc., but no more new docs are currently planned. Most of my day was spent proofreading and verifying the coverage instructions worked (which they technically do, but they triggered some bugs that need fixing).

My semi-regular reminder that Python 3 is on schedule

It seems to me that there has been an uptick in the past week or so online about people going on about how Python 3 is having adoption problems, etc. Probably the biggest thing that has recently set this off is people misunderstanding a blog post by Armin Ronacher. If you don't pay attention to what the blog post says it can come off as anti-Python 3 as Armin says to continue to create Web projects in Python 2.

But if you pay attention to what Armin wrote, you will notice that he is not being negative on Python 3 at all but simply telling people to continue to start projects in Python 2 while being forward-compatible with Python 3 for ease of development today with an eye to porting in the future (you can read his comments on Y Combinator to know I am not misrepresenting Armin). That's totally reasonable advice that I personally do not view as negative on Python 3 but instead as being positive. Armin even wrote a follow-up post giving advice on how to be forwards-compatible.

In actuality Python 3 adoption is doing quite well. Numpy supports Python 3. The next version of SciPy (which in beta) will support Python 3. Django should have support by the end of the summer. Other projects are working on Python 3 support without a specific timeline, like PIL or Armin's own projects like Flask. And specifically for the Web world, PEP 3333 was accepted so that should help move things along in that community.

Something that ALL the Python 3 naysayers forget when they complain about library support is that python-dev has said from the beginning that the timeline for solid Python 3 adoption is 5 YEARS! Python 3.0 was released December 3rd, 2008. That puts us at 2 years and a little bit today. We are not even half way through the timeline python-dev set out for solid adoption! Heck, the absolute earliest I ever remember people saying base libraries everyone relies upon being ported was 3 years. We already have two of the major scientific libraries already ported or on their way and one of the major Web frameworks aiming for this year. And all of this is before even our earliest hope of gaining traction! I would say we are totally on schedule without issues.

My point in all of this is that Python 3 is not a failure. It is not going anywhere and it is not about to suddenly become irrelevant and unused. The community is moving forward and embracing Python 3 at a rate that python-dev expected Python 3 support to move at. In other words, it's all going according to plan.

2011-01-21

PyCon stuff & PSF core grant, day 10

I am going to start this post with me pimping PyCon 2011. To start, early-bird registration is on until January 25th, so make sure to register ASAP. I would not procrastinate as attendance is capped at 1,500 and there are already about 50% more registrations now than there were last year when attendance hit 1,200. There is absolutely no reason to think we will not hit the cap this year and have to turn people away because they did not register soon enough.

Also don't forget to register for any tutorials you want to attend now rather than later. The most popular ones are already either full or practically there and this always leads to a cascading effect where other tutorials continue to fill up. So once again, don't procrastinate and register ASAP for tutorials.

As usual, there are some new things being tried out at PyCon this year. The latest one to be announced is Startup Row. Basically a bunch of startups are going to be highlighting there use of Python. Should be interesting as startups typically push Python pretty hard and end up developing very cool technology stacks.

And finally, a tradition amongst Python bloggers who attend PyCon is to list the five talks you are looking forward to the most. As usual that's a bloody hard list to make; I started with 30 tabs filled with talks I want to attend! But here are five I do plan to attend:


  1. Through the Side Channel: Timing and Implementation Attacks in Python
  2. Packaging, from Distutils to Distutils2
  3. Using Python 3 to Build a Cloud Computing Service for my Superboard II
  4. Writing great documentation
  5. Why is Python slow and how PyPy can help?
There is also the Python VMs panel that I organized, but I figured I can't list that as a talk I want to attend since that seems a bit presumptuous. =)

In case my enthusiasm for PyCon in general and this year in particular is not coming through my prose, I'm excited! I have been attending PyCon since its founding in 2003 and have always had such a fantastic time that I leave the conference looking forward to the next one.


Now that I have raved about PyCon, I can talk about what the heck I did yesterday for the PSF.

After the usual fixes I went through and cleaned up the Misc directory. There was outdated stuff in there along with files that belonged more in the devguide than there; previously there just wasn't a good place to put stuff like a README on how to get Emacs set up. So I went through and pruned the directory down.

While I have been working on the devguide I had a copy of the dev FAQ that I was chopping up to keep track of what I needed to cover. Some people were worried I was going to cut out all of the questions from the FAQ concerning svn or SSH; people liked that I had bothered to answer common tool questions there over the years so that they didn't have to try to search for a solution. So I stripped out anything that would be repetitive so its more of a tools FAQ.

Because I am in the home stretch I dropped some todo items and them into issues. They revolve around creating some code which can lead to tasks for people to help with in the future.

I also went through the old dev docs and made sure I was not about to lose any critical links.

2011-01-19

PSF core grant, day 9

Two new documents today. First is a doc to give people pointers as to where to get help when during development on Python. This is so people don't feel like they don't have a place to turn when they need some question answered to move forward with their work.

The other document is a move of Misc/maintainers.rst to the devguide and making it the Experts Index. This list helps triagers know who to add to the nosy list on an issue for expert guidance.

I have also outlined how I plan to change the Misc directory. There is still some discussion as to what exactly should stay in the directory for ease of access while developing and what belongs in the devguide. I obviously have my view and we will see if that wins out or not.

2011-01-18

PSF core grant, day 8

Today was one of those big, single document days. I wrote a guide on how triagers should triage. This is for people who have gotten the Developer role on the issue tracker and need to know how to properly file issues away.

Otherwise the usual typo fixes occurred. I even got help from Antoine Pitrou who did some commits on his own! I'm glad that other core developers are starting to feel comfortable about editing the docs as they have been made purposefully accessible to core devs so that I never become a bottleneck, just the initial author.

2011-01-17

PSF core grant, day 7

I took the day off to postpone starting the corrections on my dissertation, so I actually got to put a full day's work in! This puts me as having worked through January 12th.

After my now morning ritual of fixing typos from yesterday's commits (thanks go to Sean Reifschneider and Sandro Tosi for those) and integrating various suggestions from people, I started work on new stuff.

First, I created the Misc/README.gdb file. This was a file that was just screaming at me to be written compared to everything else in the Misc directory.

Next, I made sure that there were instructions on how to (un-)apply a patch.

Third, I withdrew PEPs 306 and 339 and made them resource pages on changing CPython's grammar and the design of CPython's compiler, respectively. This removes the CPython-specific PEPs and puts them in a more fitting place.

Finally, I gave more explicit instructions on what to do after you created a patch to improve test coverage. Since it is the intro task someone suggested that I make sure it was clear exactly what needs to be set in the issue that one files.

I did other stuff, e.g., emailing python-dev about some Misc directory re-organization, but that has not come to fruition yet.

2011-01-16

PSF core grant, day 6 (more or less)

With my Ph.D. defence over and successful, I am hoping to get my final corrections done quickly so that I can start playing catch-up with my work. Currently I consider myself having put in equivalent time as if this post represents the end of the day on January 11th. My goal is to be complete and caught up by PyCon with my grant work.

Anyway, the highlight of the latest chunk of work is documents on how to propose changes to Python. First there is a doc covering how to propose additions to the stdlib. There is no specific doc on changes as that is essentially a bug report. Second, there is a doc on how to propose a change for the language. The goal is that people will read these and realize what they are in for and thus know how to properly prepare an idea instead of being told they need to go and do X things first.

2011-01-12

my feeble attempt at designing an NFC-based mobile phone payment system

I have a Nexus S (thanks, Mom!), which means I now have a phone that supports NFC. That has led me to think about how I would create a payment system that uses my mobile phone that is completely secure not only from sniffing but from phone theft as well. In other words something better than EMV (as recently shown again to be imperfect). A Buzz by Tim O'Reilly on the topic finally caused me to decide to blog about this.

Two things should be stated upfront. First, I am no security expert; these are just some ideas I have on the matter. Second, this is not meant to make sense to your parents; I'm assuming only people comfortable with a smartphone to use this system. I am more worried about a completely secure system than I am about my parents being able to use this approach.

So, to start, the payment app will require some passphrase protection. Whether this is a PIN, password, lock screen, or biometric I honestly don't care as long as it can be used to perform some encryption. The key point is that the user must know some secret to use the payment app properly. This protects the user from having their dropped or stolen phone being used to make a ton of charges.

Now, with the user logged into the payment app they visit their bank's website (I might be saying "bank", but I mean whomever is going to handle your transactions and knows your account number). There they will have a QR Code (should be a simple enough QR Code to not require a high-end camera feature on the phone) to scan with their payment app. The barcode will contain a very large account number (think 100 digits), an increment value, some identifier of who the bank is, and a protocol version number. Thanks to having a camera in the phone, the app just scans the QR Code and the user has to manually type nothing. And all the info is encrypted on the phone based on the passphrase as entered by the user. This prevents a clever thief from nabbing your phone and simply reading the memory to get the details necessary to clone your phone to make charges. The app will also reset its transaction count to zero.

With the payment app and phone ready to go, you can now pay for something. Let's say you are at In-N-Out and just bought some food for $6.54. You hold your phone up to the NFC pad of the POS system and two pieces of data are sent to your phone: name of the business and the amount to be paid. Your payment app will prompt you saying that In-N-Out would like you to pay them $6.54. This helps prevent random people trying to charge you under a fradulent name and the cost will be used as a way to verify with the bank how much you are authorizing to charge. If you decide to authorize the charge, you enter your passphrase and your details are decrypted and the payment app gets down to business.

First, it temporarily increments your transaction count, multiplies it against the increment value, appends it to your account number, and then hashes it. This creates a number identifying your account unique per transaction. It also allows the bank to easily associate the transaction with your account without any overally expensive process (hashes are relatively cheap). I am using a unique multiplier to help prevent people guessing the increment amount by having you do two transactions in a row at the same POS system (security-by-obscurity might be bad to rely on, but it doesn't hurt to make input values into a hash function harder to guess).

Next, the $6.54 cost has your account appended to it and that string is hashed. This will provide a way for the bank to easily verify that the amount being charged is what you want to be charged.

These two hashed values along with your bank's identifier are sent back to the POS system. All of this is sent to your bank along with the amount In-N-Out wants to charge you. The bank can easily look up your account by the hashed value. It can then easily hash the charge amount with your account to verify the amount be asked matches what you authorized. The bank clears the charge.

Back at the POS system, it tells your phone the charge went through and the transaction counter is permanently incremented by one. And the whole transaction is finished without any repeatable values being sent from your phone to the bank that can be sniffed. Nor is the charge amount unverifiable by the bank to prevent the shop from altering the charged amount without you knowing. And because of the hashing there is no easy way to brute force discover your account number.

I see two issues with all of this. One is that I used the account number for both hashed values. This could be a problem as it means the POS system has two separate hashes involving your account number, which could make it easier to reverse the hash (but very hard to do). While the value used in hashing the charge amount could be changed (e.g., the account + increment or something), I don't know how much that solves the problem (if it is a problem). Another solution is that the protocol version number can change what hash is used. This would allow, e.g., switching from SHA-2 to SHA-3 when it is selected.

The other is my worry that the transaction count could get out of sync. I see three possible solutions to this is. One is not to worry about it and rely on the phone waiting for the bank's response to increment the count. Two, the bank could assume one or two missed transaction increments (e.g., it was cancelled at the POS system) and simply check two or three possible identification values. Third, a back-channel communication from the payment app to the bank could be made to help keep the numbers in sync (something as simple as an HTTPS connection).

I think this covers phone theft (by encrypting the data on the phone), POS system lying about the charge amount (by creating a hash with the charge amount), sniffing (by hashing everything in a way that is unique per transaction with no repeated values as seen by the POS system) and replay attacks (by using a transaction counter in a hash).

PSF core grant, day 5 (sort of)

As I am actively preparing for my Ph.D. defence I am once again putting in part-time on the PSF work with the plan to either catch up in the future or simply work into March until I have put in the proper amount of time.

Anyway, the latest day's worth of work has been focused on getting how to become a core dev written up. That required expressing how Python's development is communicated to the world. We have a lot of mailing lists.

I also got the doc written explaining what it takes to become a core developer: from how to ask for commit rights to the responsibilities once you get them and everything in between. This led to the writing of the doc explaining how to commit a patch.

This completes the first pass at the Contributing section of the new devguide. I still need to do the section covering proposing changes to Python as well as flesh out the Resources section (e.g., move some PEPs and stuff from Misc over). I also have some todos to still go through before I call the first complete draft of the devguide done, but it still feels good to at least have the basics done!

2011-01-10

PSF core grant, day 4 (roughly)

Since I took the day off from doing Python work on Friday to work on my Ph.D. defence slides (defence is on Friday; assuming that plus changes means my Ph.D. shouldn't be in the way past the end of this month), I spent the equivalent of Friday's workday on Saturday and Sunday.

The "day"'s work was spent mostly on task documentation. For beginners they now have a way to help with the docs. For advanced contributors (i.e., people who are comfortable working on Python), I wrote up tasks on silencing warnings emitted by the test suite, fixing issues, and helping triage issues.

I also wrote the doc explaining how to get the "Developer" role on the issue tracker. This is the first doc explaining how to get your first promotion in Python's development process. I still need to do the one on how to become a core developer, but that will come later. I also included the first task people with the "Developer" role can do: mark issues as "languishing".

The order of the documents has also been played with in the index to the devguide. A primary goal of this work is to ease people into contributing, so I am trying to come up with a good order for tasks so people can ramp up their contributions without feeling stymied by their difficulty.

2011-01-06

PSF core grant, day 3

Today the new format of the devguide compared to the old dev docs began to take shape. A long running complaint from people wishing to contribute to Python has been that it's very hard to figure out how to start. The old answer of "search the issue tracker for something" doesn't pan out for people just getting started as many things on the tracker are still there because they are difficult, not because people have simply not been able to get to them.

So today I did the first task-oriented page: increasing test coverage. I figured people in general know how to write tests and anyone can find out what coverage various tests have on their own computer, so it seems like a good starter task for people. I have a couple other potential starter tasks I think I will write up to add to the list (you can see those ideas as todo items and as dead links on the devguide front page).

And as a bonus I found a bug in test.regrtest that I fixed. =)

PSF core grant, day 2

Yesterday was day 2 of my PSF grant. I continued to work on developer documentation that focused on people new to Python's development.

But the really cool thing is that Georg Brandl wrote a commit hook so that the devguide is online while it is being developed! Now this might not be the permanent URL so don't be shocked if the link stops working. But it is nice to have it up so people can easily peruse the docs while I am writing.

As for new docs written yesterday, I managed to get Lifecycle of a Patch done which explains the process of creating and submitting a patch. I also got instructions on how to run the test suite and writing tests for it finished.

2011-01-04

PSF core grant, day 1

In early 2010 I submitted a grant proposal to the Python Software Foundation (PSF) to have me be paid for two months to work full-time on the Python core. Specifically, I said I wanted to rewrite the devguide  (nee dev docs), write a HOWTO on porting code from Python 2 to 3, and time permitting improve the test suite for Python by helping make it more robust and uniform.

Well, the grant was accepted and I started working on it today (technically I started yesterday, but it was not a full day so I am calling this day 1). To start I am working on the devguide (the repo is at http://hg.python.org/devguide). At this point I have an outline of the pages I want to write along with a rewrite of both the original "getting set up" guide and the original patches guide.

My over-arching goal with this new devguide is to make it much more task-oriented. I want people to be able to read the list of docs and be able to contribute quickly with very pointed suggestions on how to get started. Once someone feels more comfortable they can move on in the docs and do some more advanced contributions. The last bullet point is getting commit privileges (with a sub-bullet on how to do an actual commit), so I want people to be able to work their way over time from not knowing how to contribute to Python to becoming a core developer.

I do plan to post daily on what the heck I managed to get done each day. I also plan to be be sitting in #python-dev while I work to help there however I can.