Wednesday, January 29, 2014

CherryPy Server has Awful Documentation

Just an observation. If I ever figure out how to do what I want with it I will have to write some introductory material because it is a shame a project as stable as CherryPy is so completely unapproachable. I am trying to have CherryPy serve a non-CherryPy WSGI app, as well as serve static files. (The WSGI app in question is a Django app.) I'll write a longer post if I ever crack this puzzle.

Friday, January 24, 2014

TastyPie's Validation.is_valid: a case of careless naming

TastyPie is a utility to add RESTful APIs to your Django app. TastyPie abstracts some of the more mechanical aspects of REST API implementation and provides high-level interfaces that make it easy to get up and running.

One of the hooks provided by TastyPie is validation. It provides its own Validation class for you to subclass if you need to execute some custom validation logic. All your logic goes in an is_valid() method. This method returns a dictionary of error messages. If there are no validation errors, an empty dictionary gets returned, signalling that all is well.

...wait, what? So this is how it works, ideally:

>>> my_validator.is_valid('good data')
{}
>>> bool(my_validator.is_valid('good_data'))
False
>>> my_validator.is_valid('bad data')
{'__all__': 'Bad input'}
>>> bool(my_validator.is_valid('bad data'))
True

Does this prevent TastyPie from achieving its goal? No. But was this name poorly chosen? I think so. Small mental mixups like this add up fast. And especially in a language like Python, where the emphasis is on concise, easy-to-read code, it seems like quite a blunder. Frameworks should be used when they simplify and enhance development, so the names should be well-chosen and meaningful.

Thursday, October 3, 2013

A small example of Django's elegance

Python is an interactive language, thanks to its REPL. And since it's free from typing constraints that weigh down languages like Java, you're free to design an API that is as flexible and easy to understand as you desire.

Want to introspect a field on your Django model? Easy!! Say you have a Foo model and a bar field you want to get.

Something like this:

class Foo(Model):
    bar = CharField(max_length=25)

Let's try and access that bar field!

>>> Foo.bar
AttributeError: type object 'Foo' has no attribute 'bar'

Hm! Well... Huh. Look at that. Take two?

>>> Foo._meta.get_field_by_name('bar')
(<django.db.models.fields.CharField: bar>, None, True, False)

...Right, of course. The old "None, True, False" flags.

>>> Foo._meta.get_field_by_name('bar')[0]
<django.db.models.fields.CharField: bar>

There's that famous Python elegance. That's why they call it executable psuedocode, folks. What do those flags even do? Django's got documentation of famous quality, so let's see what those extra values mean!

https://docs.djangoproject.com/search/?q=_meta&release=7

Hm. Well. Well it's not documented. But they do use it in examples in their documentation. Hm. Well there is that good ol' REPL.

>>> help(Foo._meta.get_field_by_name)
get_field_by_name(self, name) method of django.db.models.options.Options instance
    Returns the (field_object, model, direct, m2m), where field_object is
    the Field instance for the given name, model is the model containing
    this field (None for local fields), direct is True if the field exists
    on this model, and m2m is True for many-to-many relations. When
    'direct' is False, 'field_object' is the corresponding RelatedObject
    for this field (since the field doesn't have an instance associated
    with it).
    
    Uses a cache internally, so after the first access, this is very fast.

Ah of course. That explains it all. That's what I like about Python. Clear, simple, and easy to use. Of course you find those exact same values all across their community, like in Kenneth Reitz's requests library. Or Django. Right?

Monday, January 14, 2013

Django projects at a glance

I have decided to write this after experiencing an extreme amount of frustration at the Django web application framework for Python. Some things are simply not clearly spelled out in the docs.

Django project structure: When possible, make any model a separate app

Any Django project should be a collection of apps. Now, if you're a normal human, you may think of "Chrome" as an app, and maybe "iTunes" and "Microsoft Word" and things like that. However, in Django, app is something on a much smaller scale. If you have blog posts and comments, that's a whole app. That app is separate from a blog authors app. Essentially, you want to break a single web project into as many small apps as you can, even if the apps that make up your site are co-dependent and not usable on their own.

One of the largest benefits to splitting your project into as many apps as possible is it keeps the models.py files a sane size. There is simply no straightforward, supported way to break up models.py. I tried to create a models/ directory with an __init__.py that had `from x import *` for each model module x in my project. This unfortunately lead to my Django fixtures breaking, and as a consequence, half of my tests failing.

Templates and static files go in the app directory

This will help keep all your static files logically separated. Single-use Javascript widgets will get to go in the folder closer to their actual use, while broadly used files go into a central location.

Given your Django app is called 'app': Templates go into app/templates/app. Static files go into app/static/app. The reason you make a new app folder in templates/ and static/ is that Django doesn't split up different app files into different namespaces in any way. Maybe (hopefully) this will be added in a future version of Django, but for now, you must create a root folder for you static files and templates to prevent filename collision with other apps.

As for those global-use files: an ideal central location is not yet known to me. I am still in the process of restructuring my Django project. However, I have a hunch that I'd put project-global files in the the static/ and templates/ directory in the main app (the app with settings.py). I have not yet attempted yet.

More details

For more detail, go to this StackOverflow question. I stumbled upon it just now when trying to figure out

Friday, August 24, 2012

Installing openSUSE 12.1 KDE version with a GeForce GTX 580

For my first post on my mostly directionless blog, I will write something which may be helpful to other people! Sweet!

Some Backstory and Context

If you want, skip this entire part for a tl;dr summary at the bottom

I randomly decided to switch from Windows 7 to Linux for my home desktop after having a mostly positive experience with it at work, and being a fan of the color green, lizards, and KDE, I decided to give openSUSE a shot. The version I downloaded (the latest at the time) was openSUSE 12.1 64-bit KDE version.

The system failed to boot, freezing up at the green openSUSE splash screen. I mashed Ctrl+Alt+Fn (where n is 1 through 7) to try and see any information and found some seemingly random error messages. Mine were "Checking Hybrid disk device(s)...not found" and "Couldn't find Live image configuration file". After these two it said "rebootException: reboot in 120 sec..." although it didn't actually reboot in 120 seconds as it indicated.

I then tried Knoppix, Fedora 17, and Kubuntu, all of which gave me various errors. Fedora's were the most baffling: "Started NFS file locking service. [OK] Started Sendmail Mail Transport Agent [OK] Started Sendmail Mail Transport Client [OK]." And then a freeze. On another tty (using the Ctrl+Alt+F-key trick again) I saw "FAT-fs (sdb1): bogus number of reserved sectors" and all sorts of other scary stuff. Or Fedora would boot and freeze after login. Either way, it was a pretty stellar time.

After many agonizing hours I eventually learned the real culprit was actually my video card the whole time. Apparently the current state of affairs is most distributions come with an open-source nVidia driver called Nouveau, and this Nouveau driver for some reason or another is causing all these distros to freeze at some point in the boot process. The solution is to pass "nouveau.accel=0" as an option to the Linux kernel before it boots. Most live CDs allow you to set boot options in a menu, and most distros install Grub which will also let you set boot options.

What I Ended Up Doing (the tl;dr summary)

  • As mentioned above, prevent the boot errors by using nouveau.accel=0 as a boot option.
  • I personally used this boot option workaround to boot into openSUSE's Live CD to install, and then again to boot openSUSE a few times as I figured out the rest.
  • Assuming you're on openSUSE, use YaST to install the proprietary nVidia drivers, which tend to work well. Detailed instructions for 64 bit computers can be found here: http://www.suse.de/~sndirsch/nvidia-installer-HOWTO.html#2
If you're not using a 64 bit version, just scroll around on that page; it lists directions for other architectures as well.

That is essentially the end of the post, but as an addendum, I just want to say that I think it's quite odd that so many distros seem to all be affected by this bug, and for me it wasn't a showstopper but for someone less technical it is a complete nightmare. The combination of A: the buggy driver being widespread and B: the boot process for the various distros not doing decent error recovery or even displaying helpful error information runs contrary to my usual experience with Linux systems, where they fail loudly and are generally compatible with tons of hardware out-of-the-box.

Hopefully this gets addressed! Until then I hope this guide helps other people in my situation. It should also be noted that this bug apparently effects other GeForce 2xx, 3xx, 4xx, 5xx, 6xx etc. video cards.