Something Like PyArg_ParseTuple For Ruby

Hackety - Monday, November 17, 2008 @ 11:58 PM

Calling into Ruby from C is great, but I’ve noticed that I spend a lot of time casting arguments coming into each function.

 rb_scan_args(argc, argv, "11", &port, &opts);
 if (rb_respond_to(port, rb_intern("to_str"))
   StringValue(port);
 else if (!rb_respond_to(port, rb_intern("read")))
   rb_raise(rb_eArgError, "a String or IO object only, please");
 if (TYPE(opts) != T_HASH && !NIL_P(opts))
   rb_raise(rb_eArgError, "options must be a hash");

Ruby is dynamically typed, but object types are a bit more reified in C. The TYPE macro can check an object to see if it’s a T_FIXNUM, T_HASH, T_STRING, T_ICLASS, etc. You can duck type all you want, but when you’re inside an extension, you’ll need to know the type before calling rb_str_cat or rb_hash_aref.

And StringValue does this, it’ll cast using to_str and then make sure you’ve actually a real T_STRING.


I’m disappointed with rb_scan_args. It’s wimpy. The function signatures it uses aren’t very expressive. It’s basically describing arity and that’s it.

One thing I like better in Python’s API, though, is the PyArg_ParseTuple function and its cousins.

 const char *file;
 const char *mode = "r";
 int bufsize = 0;
 ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
 /* A string, and optionally another string and an integer */
 /* Possible Python calls:
    f('spam')
    f('spam', 'w')
    f('spam', 'wb', 100000) */

The function signature is more expressive here ("s|si"), indicating which types are allowed. You don’t have to check the types individually, nor do you need to throw individual exceptions.


Here’s an equivalent I’m working on for Ruby:

 rb_arg_list args;
 rb_parse_args(argc, argv, "s|h,-|h", &args);
 /* a string and optionally a hash OR an IO and an optional hash */

The rb_parse_args function returns the number of the match. If the first signature ("s|h") is matched, you get 1. If the second signature is matched, you get 2.

I’m planting this in a switch statement when I want to overload a method.

 rb_arg_list args;
 /* "k" means Class, "h" means Hash */
 switch (rb_parse_args(argc, argv, "kh,h,", &args))
 {
   case 1: /* "kh" - style(Link, :background => white) */ break;
   case 2: /* "h"  - style(:width => "100%") */ break;
   case 3: /* ""   - style() => {...hash of styles...} */ break;
 }

This is saving me some tedium. In typical shoddy form, my error messages blow. I am undisciplined’s middle name.

Ah, how good it feels to be inspired by Python. A bit good, a bit slimey. I’m like Joe Lieberman, guys.

Eric Florenzano: WSGI middlware is awesome, and Django should use it more

Django Community - Monday, November 17, 2008 @ 09:00 PM

Most people in the Django community are deploying their apps these days with mod_wsgi. If not, then you're at least using WSGI as a communication layer with your application server, in one way or another. The great thing about WSGI is that it gives everyone a common interface through which to talk. It also has the added benefit of being a common abstraction that many people have built these great, really useful tools on top of.

Consider Repoze. If you navigate to the middleware section of their website, they have some really cool stuff available! There are utilities for logging, authentication, security, profiling, templating, etc. All of these pieces of middleware are designed to be totally pluggable, because they are designed to work solely based on what's available through WSGI.

My personal favorite of that lot is repoze.profile. It accumulates Python profiling information about whatever app is being run, and allows you to view that profile information via a web interface by visiting a special URL. There is absolutely no reason that the Pylons, TurboGears, or CherryPy guys should be able to get away with keeping this stuff for themselves, so I want to show just how easy it is to integrate this profiling module with Django.

First, though, here's a typical .wsgi file that might be used in conjunction with mod_wsgi:

import os, sys
sys.stdout = sys.stderr

os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

There's really nothing special going on here, and if you would like to learn more about how to set up this WSGI file, visit mod_wsgi's documentation on the subject. Now if you'll notice, application is simply an instance of WSGIHandler, which is simply a callable. A WSGI middleware is just a wrapper around that callable. Here's how easy it is to add the profiling middleware:

from repoze.profile.profiler import AccumulatingProfileMiddleware
application = AccumulatingProfileMiddleware(
    application,
    log_filename='/tmp/djangoprofile.log',
    discard_first_request=True,
    flush_at_shutdown=True,
    path='/__profile__')

There we go! We have imported the profiling middleware, and passed the Django WSGI application as the first argument. The rest is just setting options for the middleware. You can restart apache and the WSGI profiling middleware is already working.

Sometimes, though, you don't want all of Apache just to run some middleware. You want to be able to do the same thing, but locally. Believe it or not, Django's local development server is just a WSGI server itself, so one option would be to do the wrapping directly in django, right here. But you really don't want to be hacking inside of Django internals if you don't have to. Fortunately there are many alternative WSGI servers out there. Brian Rosner has created a custom management command to use the excellent CherryPy WSGI server with Django, on his blog.

Let's say you just want to try this out quickly after reading this blog post, though. If you're running Python 2.5 or greater, you're in luck, because a script less than 10 lines long can get you up and running:

#!/usr/bin/env python

import sys
from wsgiref.simple_server import make_server

if __name__ == "__main__":
    execfile(sys.argv[1])
    httpd = make_server('', 8000, application)
    httpd.serve_forever()

Now, to run it, simply invoke it like this:

python runserver.py my_wsgi_file.wsgi

Now, navigate around your app for a little bit and then point your browser to the profile url and see how freaking awesome middleware can be.

I'm not trying to stir up any controversy, I'm not saying we should stop making Django middleware or anything like that. But I seriously, seriously hope that someone tries this out and realizes the multitudes of great WSGI apps out there that can be taken advantage of. Mark Ramm wasn't full of hot air when he talked about this at DjangoCon or blogged about it later. He was right, and I for one wish I had listened sooner.

Gustavo Picón: Release: django-treebeard 1.0

Django Community - Monday, November 17, 2008 @ 04:01 PM
django-treebeard 1.0 has been released. Direct link: django-treebeard-1.0.tar.gz (don’t forget to read the UPDATING file). I will be focusing now in another project: django-taggable, so stay tuned. Share and enjoy.

Alex Gaynor: Running the Django Test Suite

Django Community - Monday, November 17, 2008 @ 12:01 PM
This question came up in IRC yesterday, so I figured I'd run through it today. Django has a very extensive test suite that tests all of the components of Django itself. If you've ever written a patch for Django you probably know that tests are a requirement, for both new features and bug fixes. I'm going to try to run down how to setup your own testing envrioment.

First you need to have Django installed somewhere, for now I'll assume you have it in ~/django_src/. Somewhere on your python path, go ahead are use django-admin.py to create a new project. I've named this project django_test. Next, inside of that project create a folder named settings, and move settings.py into that folder and renmae it __init__.py. The reason we're going to have a settings directory is so that we can have subsettings for individual scenarios. Put some default settings for the various field in there now, for example my default settings provides the necessary options for SQLite. Now if there are any other subsettings you wish to setup create a file for them in the settings directory, and at the top of this file put from django_test.settings import *, followed by whatever settings you wish to overide. For example I have a mysql.py that overides my default SQLite database settings with MySQL values.

Now that we have our test settings, go to the directory where you have Django installed. To run the tests do ./tests/runtests.py --settings=django_test.settings. You can also use the DJANGO_SETTINGS_MODULE enviromental variable in place of passing in the settings like this. You can also provide a verbosity argument, the default is 0, I prefer to run with verbosity=1 because this keeps you up to date on the progress of the tests, without too much extraneus output.

Usually when you are working on a patch you don't need to run the entire test suite, your patch only affects a few tests. Therefore, you can provide a list of tests you'd like to run, so for example you could do ./tests/runtests.py --settings=django_test.settings.mysql -v 1 model_forms model_formsets. This will run the model_forms and model_formsets tests, with your mysql settings, at verbosity level 1.

And that's all it takes to run the Django test suite.

Alex Gaynor: What I'm excited about in Django 1.1

Django Community - Monday, November 17, 2008 @ 11:01 AM
This past week, Jacob Kaplan-Moss put together the list of all of the features proposed for Django 1.1, and began to solicit comments on them. This is going to be a list of features I'm excited about.
  • Making admin URLs reversable. Currently the Django Admin uses a bit of a convulted scheme to route URLs. The proposal is to have them work using the current URL scheme. This is something I've been working on for a while, and hoping to see it to completion.
  • Comment-utils inclusion. The proposal is to include the moderation level features from comment-utils in Django. I think this is a great idea, and can't wait to see what sort of spam check schemes people implement once moderation facilities are included.
  • Message passing for anonymous users. This is basically session level message passing. This is something I've had to implement in past, so I'm looking forward to this.
  • ORM aggregation, as part of the Google Summer of Code Nicolas Lara, mentored by Russell Keith-Magee, implemented this. I love the API design, and it's a hugely requested feature, I can't wait to point new users to the docs, rather than explaining to them that it's coming soon.
  • ORM expression support, this work was also done by Nicolas Lara, and will let you do things like Model.objects.filter(height__gt=F('width')), or Model.objects.update(salary = F('salary')*1.2).
  • Model Validation, before 1.0 I implemented unique, and unique_together checks for model forms. That's pretty much a massive special case of this.
  • Class-based generic views, often one of the first things a new user to Django will learn to do is create a view that simply wraps a generic view, in order to do some custom filtering on a queryset. This is a great solution for that usecase, however as users want to inject more and more flexibility into a generic view it can lead to a huge number of settings. Rather than this, subclassing a generic view could provide a nice clean solution.

These will all be great features, and there are many more proposed(you can see them all here), however these features only happen because people write code for them. If there's a feature you're excited about, or interested in making a reality, try to contribute to it, even if it's just writing some unit tests.

Joaquim Rocha: Introducing Rancho!

Django Community - Monday, November 17, 2008 @ 11:00 AM
Since a while ago I started a project with friends to develop an Open Source group/project management tool with the best web framework out there - Django! Today is the day! Rancho 0.1 alpha is out!!! Rancho goal is to make it easy for any company or group of people to manage its projects and people. Here are [...]

Greg Newman: Emacs: Dired Directory Management

Django Community - Monday, November 17, 2008 @ 10:01 AM

Picking back up on the Emacs series I started earlier this month I would like to take some time to discuss the Dired mode for directory management.

I've used ECB in the past and found it nice, feature rich but in my opinion it had too much information and was in my way, not to mention it drastically increases emacs load time. So I removed it from my configs and now only use Dired. Once you get used to it it really is very powerful.

Django Brasil: Lançado Django 1.0.1

Django Community - Monday, November 17, 2008 @ 05:00 AM

Seguindo o cronograma de lançamento, o Projeto Django lançou na última sexta-feira o Django 1.0.1. Esta versão possui apenas correções de bugs e melhorias no código do Django 1.0, e sua atualização é recomendada.

Para mais detalhes desta versão, veja as notas de lançamento (em português :-).

Andy McKay: syncdb and Django

Django Community - Monday, November 17, 2008 @ 02:01 AM

This has cropped up a couple of times on irc, so here's a recipe I have for restarting my Django instance during development. I'm not the sort of person who takes a long time to think it all through and can write a model and call it done, I iterate quickly and often. For the quickest way to do that during the initial development, I drop the db, re run syncdb and import my fixtures regularly.

Once a production version is out and running I then get rid of this script and do migrations.

There's no easy way from the command line to create a user neither syncdb --noinput or createsuperuser --noinput accept a password. Leaving lazy people like me wanting. I used to use pexpect but I find just creating a user object nice and easy. So for a project called collector, the following works well for me:

#!/usr/local/bin/python
import os
os.environ["DJANGO_SETTINGS_MODULE"] = "settings"

# dump the data to fixtures
os.system('python manage.py dumpdata collector --indent=2  collector/modes/fixtures/data.json')
# drop and recreate the db (this is for postgres)
os.system('dropdb authy')
os.system('createdb authy --encoding=UTF-8')
# sync the db
os.system('python manage.py syncdb --noinput')

# create a super user
from django.contrib.auth.models import User
u = User.objects.create(
    username='xxx',
    first_name='',
    last_name='',
    email='amckay@bluefountain.com',
    is_superuser=True,
    is_staff=True,
    is_active=True
)
u.set_password('xxx')
u.save()
print "User account created"

# load the fixtures back in
os.system('python manage.py loaddata sample.json')
# run the server
os.system('python manage.py runserver')

I can then quickly get in the cycle of creating a model, entering data in admin interface, dumping it to a fixture and then writing unit tests.