views:

83

answers:

4

I have a little internal DSL written in a single Python file that has grown to a point where I would like to split the contents across a number of different directories + files.

The new directory structure currently looks like this:

dsl/
    __init__.py
    types/
        __init__.py
        type1.py
        type2.py

and each type file contains a class (e.g. Type1).

My problem is that I would like to keep the implementation of code that uses this DSL as simple as possible, something like:

import dsl
x = Type1()
...

This means that all of the important symbols should be available directly in the user's namespace. I have tried updating the top-level __init__.py file to import the relevant symbols:

from types.type1 import Type1
from types.type2 import Type2
...
print globals()

the output shows that the symbols are imported correctly, but they still aren't present in the caller's code (the code that's doing the import dsl). I think that the problem is that the symbols are actually being imported to the 'dsl' namespace. How can I change this so that the classes are also directly available in the caller's namespace?

+1  A: 

You'll have to say

from dsl import *
Eli Courtwright
Actually the symbols are one level down from dsl. e.g. dsl.types.type1.
Wai Yip Tung
@WaiYipTung: Paul C says that he's updated the top-level `__init__.py` to include those symbols, so doing a `from dsl import *` should still work, even those symbols are originally defined one level down.
Eli Courtwright
+1  A: 

You can't do that. It's up to the user what they import into their own code. If they want to import everything into their namespace, they can do from dsl import *, but you can't dictate this.

Daniel Roseman
A: 

This is what I would do

In dsl/init__.py, add

def import_symbols(namespace):
    namespace['type1'] = dsl.types.type1
    namespace['type2'] = dsl.types.type2

From the caller, do

import dsl

dsl.import_symbols(globals())

No only can you import symbols from a second level package into your current namespace, by defining you own import_symbols(), you also have more explicit control on which symbol to import rather than importing everythin with import *.

Wai Yip Tung
A: 

@Eli, @Daniel thanks for the "aha" answer. I was almost there but needed the extra nudge...

Basically the solution was split into two stages: to use the package initialiser to pull the 'exported' second-level symbols into the top-level dsl package (this bit I already had) and then to pull those symbols into the caller's code with from dsl import *. This makes sense as the caller should really have control over what they pull into their namespace.

from pkg import * seems to be generally frowned on, but in this case I think it's a reasonable solution as the number of symbols exported by my package will be limited.

Paul C