Home

CKAN Developer Discovers C Headers

At some point at university I borrowed, read a tiny amount of and got fined for the late return of Large Scale C++ software design. I think somewhere at the beginning there was a section on using headers to avoid circular imports. ckan is a large enough project that we can learn a thing or two here.

So we have a ckan python style guide, where we used to recommend avoiding

from blah import banana

style imports and instead recommended

import blah.banana as banana

Eventually we decided to allow from imports, because the above is ugly, but the docs were updated to include some additional rules about what you can and cannot import to avoid circular imports.

ckan.model often gets imported all over the shop and I believe it was the cause of many circular import problems. Stuff like get_action ended up creating stuff to get around it. A simple example of ckan.model ending up everywhere would be importing

from ckan.logic import schema

schema would contain all the action functions (schema.default_show_package_schema, etc), but it also has schema.model (really ckan.model), schema.maintain (ckan.lib.maintain). These are all implementation details of the schema functions default_show_package_schema etc.

current schema imports

What if we changed ckan/logic/schema.py so that it imports only the action function into it?

alternative schema imports

This way when user of the code import the schema functions

from ckan.logic import schema

schema will have a list of functions without dirtying up their namespace with implementation details of ckan.model/maintain/whatever. We already do this for the models, as I think this is more standard for code that uses sqlalchemy, but we should really do this everywhere.

Seeing as many people write extensions for ckan we really should be doing this to define a good interface for people to use without exposing internal details to them when they import a module.

I see this as similar-ish to having a .h header and a .c file in C. We're hiding the implementation details from ckan.logic.schema by only allowing imports from elsewhere. Will this solve the circular import issues ckan had before? I don't know, but it feels better than

import ckan.model as model
import ckan.helpers as helpers
# ...

everywhere.