Limit which classes in a .py file are importable from elsewhere
Question: I have a python source file with a class defined in it, and a class from
another module imported into it. Essentially, this structure:

    
    
    from parent import SuperClass
    from other import ClassA
    
    class ClassB(SuperClass):
        def __init__(self): pass
    

What I want to do is look in this module for all the classes defined in there,
and only to find ClassB (and to overlook ClassA). Both ClassA and ClassB
extend SuperClass.

The reason for this is that I have a directory of plugins which are loaded at
runtime, and I get a full list of the plugin classes by introspecting on each
.py file and loading the classes which extend SuperClass. In this particular
case, ClassB uses the plugin ClassA to do some work for it, so is dependent
upon it (ClassA, meanwhile, is not dependent on ClassB). The problem is that
when I load the plugins from the directory, I get 2 instances of ClassA, as it
gets one from ClassA's file, and one from ClassB's file.

For packages there is the approach:

    
    
    __all__ = ['module_a', 'module-b']
    

to explicitly list the modules that you can import, but this lives in the
`__init__.py` file, and each of the plugins is a .py file not a directory in
its own right.

The question, then, is: can I limit access to the classes in a .py file, or do
I have to make each one of them a directory with its own init file? Or, is
there some other clever way that I could distinguish between these two
classes?


Answer: You meant "for packages there is the approach...". Actually, that works for
every module (`__init__.py` **is** a module, just with special semantics). Use
`__all__` inside the plugin modules and that's it.

But remember: `__all__` only limits what you import using `from xxxx import
*`; you can still access the rest of the module, and there's no way to avoid
that using the standard Python import mechanism.

If you're using some kind of active introspection technique (eg. exploring the
namespace in the module and then importing classes from it), you could check
if the class comes from the same file as the module itself.

You could also implement your own import mechanism (using `importlib`, for
example), but that may be overkill...

Edit: for the "check if the class come from the same module":

Say that I have two modules, `mod1.py`:

    
    
    class A(object):
        pass
    

and `mod2.py`:

    
    
    from mod1 import A
    
    class B(object):
        pass
    

Now, if I do:

    
    
    from mod2 import *
    

I've imported both `A` and `B`. But...

    
    
    >>> A
    <class 'mod1.A'>
    >>> B
    <class 'mod2.B'>
    

as you see, the classes carry information about where did they originated. And
actually you can check it right away:

    
    
    >>> A.__module__
    'mod1'
    >>> B.__module__
    'mod2'
    

Using that information you can discriminate them easily.

