I promised to write about the structure and implementation of Spineless Jelly. Since there was a request about describing the high-level design instead of specific systems, here it is. Please note though that this design might change - even quite significantly - before the 0.3 release.
This rewrite of Spineless is actually quite a bit less framework-like and more library-like than the previous implementation. In other words you, the programmer, have more freedom to do what you like, without the engine trying to impose specific ways of doing things, specific classes to use etc. I feel this is much more Pythonic. One example are the math classes: the renderer will accept any value that can be converted to a 3-item ctypes array as a 3D vector. Of course, this includes a ctypes array, which is the fastest way of passing arrays to the renderer. Another example are the plugins, which I'm going to talk about in a bit.
When the engine is initialized and some basic stuff is set up (exception hook, logging), config is searched for plugins to load. Engine config can be modified directly or loaded from a config file, which is just a Python script. In Spineless Jelly, a plugin is a module or package conforming to the interface of that plugin category. Current plugin categories are renderer and windowmanager, with a single implementation each: OpenGL and Pygame, respectively. Future categories will probably include at least audio, network and physics. With a bit of hackery (__path__ and __dict__.update()), the category package is effectively replaced by the plugin package or module. In other words, if you load the opengl plugin for the renderer category, "from renderer import mesh" will actually import renderer.opengl.mesh.
The caveat with this solution is that global variables in plugin packages or modules don't work correctly. Why not? The short version is that functions defined in the plugin package will continue existing in that scope (eg. renderer.opengl), while global variables will be moved to the plugin category scope (eg. renderer). The less-than-perfect but adequate solution is to write getter functions for the global variables. For example, renderer.get_max_lights() instead of renderer.max_lights.
Actually running the engine is based on tasks and phases. Each phase contains a list of tasks, and a tick of the engine consists of updating all the tasks in each phase in order. The phase list can be freely edited, and phases are actually just strings (but they could be any objects). Currently the default phases are, in order: "input", "logic", "output". For example, the Pygame window manager has an EventPollTask which belongs to the "input" phase. Game logic belongs to the "logic" phase, while rendering and audio would go to the "output" phase.
At the moment, tasks are just simple classes with start(), stop() and update() methods, but later more advanced task types might be added. For example remote (networked), delayed or coroutine-based tasks.
And that's basically it at the moment. Resource management also works, but little has changed and I've described it in a previous post. You can now use multiple loaders for a resource type, such as PygameLoader and PILLoader for Images, but otherwise it's more or less the same.
I'm currently rewriting scene management and rendering, while also working on GLSL integration and COLLADA loading, so I'm not yet going to talk about the new scene management system. It more or less resembles the old system, but with a cleaner design and other changes.