Python has a system for packages and sub packages which maps to the
filesystem; there are many different ways to import symbols into the
Python interpreter context. These things are exploited in the ert python
bindings, here we try to document the python apporach to packages and
importing in general, and the conventions used in ert python in special.

Example module (file: example.py)
--------------
   
   def example_sum(a,b):
       return a+b

   example_variable = 100

   class ExampleClass:
       def  __init__(self , **kwargs):
           ....
       
       def method( self , arg1 , arg2 )
           ....

We will use this example module in the following.





1. Modules and symbols
----------------------

A module is a python source file (or alternatively a shared library with
a prescribed structure). When importing a module all the symbols in the
module will become available in calling scope, with the module name as
namespace prefix:

   import example

   var = example.example_variable    
   sum = example.example_sum( 100 , 100)
   instance = example.ExampleClass()

Observe that even though we have imported the 'example' module, we
still need to use the 'example' prefix when referring to the symbols
found in the example module. It is important to remember that the
module object is only a collection of symbols - you can not use the
module itself, only the symbols contained in the module can be put to
useful work.

It is also possible to import the symbols from 'example' directly into
the current namespace, without creating the intermediate namespace
'example', that is done using the 'from <module> import <symbol_list>'
statement:

    from example import example_variable,example_sum,ExampleClass

    var = example_variable
    sum = example_sum
    inst = ExampleClass()

With this approach the symbols from 'example' are imported directly into
the current namespace, and the 'example' prefix should not be used when
referring to the 'example_variable','example_sum' and 'ExampleClass'
symbols. In the example above we have explicitly named the symbols we are
after, it is also possible to import all symbols undiscriminately:

    from example import *

    ....
    ....

Finally it is possible to perform name transliteration during the
import:

     from example import example_variable as EXAMPLE_VARIABLE

     print "100 = %s??" % EXAMPLE_VARIABLE

Here we have imported the example_variable symbol from the 'example'
module and renamed it to 'EXAMPLE_VARIABLE' in the calling scope.
  


2. Packages
-----------

It is convenient to group related modules together in a package
structure; a module is just a python source file. In the same manner a
package is just a directory - WITH THE MAGICAL FILE __init.py__.

For instance for ert the top level package is just the the 'ert'
directory. Looking on the filesystem for an ert installation it will
look something like this:

   ert/                      <-- Top level package / directory.
   ert/__init__.py           <-- Magic file signalling that 'ert' is a package.
   ert/ecl/                  <-- ecl is subpackage.
   ert/ecl/__init__.py       <-- Magic file signalling that 'ecl' is a package.
   ert/ecl/ecl_grid.py       <-- Normal module in the ecl package.
   ert/ecl/ecl_sum.py        <-- Normal module in the ecl package.
   ....
   ert/util/                 <-- The util subpackage.
   ert/util/__init__.py      <-- Magic file that 'util' is a package.
   ert/util/stringlist.py    <-- Normal module in the util package.
   ert/util/tvector.py       <-- Normal module in the util package.

   ert/

The important thing about packages is that they are normal filesystem
directories, with the magic __init__.py file which signals that this is
indeed a package. When importing a package the python code in the
corresponding __init__.py file will be loaded and imported, that can be
used as a means to perform package initialisation of various kinds.

The statement:

    import ert

works, but as long as ert is a package (i.e. only a directory) this is
not immediately very interesting[1], we have to continue importing until
we have actually imported a module with real content:

   import ert.ecl

Observe how the directories/subdirectories of the filesystem is
translated to a dotted notation in package/module space. The statement
'import ert.ecl' will import the ert.ecl subpackage, but this is still
not any symbols usable for anything. Observe that the 'import ert.ecl'
statement will evaluate the '__init__.py' files found along the
way. ecl_grid.py is a module in the ecl package, so this statement will
finally give us something usable:

   import ert.ecl.ecl_grid

The module ecl_grid contains the implementation of the EclGrid class
which can actually be used for something. To instantiate an EclGrid
instance we have to go through the following hoops:

   import ert.ecl.ecl_grid
   ....
   ....
   grid = ert.ecl.ecl_grid.EclGrid( "ECLIPSE.EGRID" )
           |   |     |        | 
           |   |     |        |
  Package--/   |     |        \-------- Symbol (Class definition)
               |     |
 Subpackage ---/     \---- Module

The 'ert.ecl.ecl_grid' namespace is quite massive. It is easy to
simplify this using the from <module> import <symbol_list> as:

  from ert.ecl.ecl_grid import EclGrid    <-- Explicitly import symbol
  ....                                        into current namespace.
  ....
  grid = EclGrid( "ECLIPSE.EGRID" )  
   
By convention the ert python distribution offers some limited
simplifications of this procedure.


3. Interaction with PYTHONPATH

When issuing the import statement:

   import ert.ecl.ecl_grid

The directory 'ert/' must be on PYTHONPATH. In principle it is possible
to circumvent the lowest level packge, and go directly to the 'ecl'
subpackage:

    import ecl.ecl_grid

but then the 'ecl/' directory must be on PYTHONPATH. For the default
ert-python distribution only the 'ert/' directory will be on the import
path, i.e. the "import ecl.ecl_grid' will not work directly.


4. Conventions applied in ert

In the ert-python distribution some conventions have been adhered to;
they can of course be changed:

  1. All the __init__.py files are empty, i.e. no automagic namespace
     manipulations. It is tempting to fill up the __init__.py files
     with further import statements in an attempt to shorten and
     simplify the imports. Some of that was done in the initial
     dsitributions of ert, but it gets very easy to be too smart and
     shoot oneself in the foot.

  2. All subpackages have a module with the same name as the package,
     i.e. the ecl package has a module ecl - this module imports all
     the symbols from all the modules in the package with no namespace
     prefix, i.e. the ecl module looks like:

        from ecl_grid import EclGrid
        from ecl_region import EclRegion
        from ecl_kw import EclKW
        .....

     The point of this is a minor simplification, you can then issue
     e.g.

        from ert.ecl.ecl import *
        ....
        grid = EclGrid("ECLIPSE.EGRID")

     To get all the symbols from the ecl package directly into the
     current namespace, or alternatively you can use 

        import ert.ecl.ecl as ecl
        ....
        grid = ecl.EclGrid("ECLIPSE.EGRID")

     and then use all the symbols from the ecl package under the
     common 'ecl' prefix. The functionality of the xxx module in the
     xxx package could have been achieved with the __init__.py - but
     then you would get it unconditionally.
     
  3. The ert python distribution is organized with one subpackage per
     library in the ERT C distribution. In each subpackage there is a
     module with the same name as the library which this package
     wraps, i.e. the ecl package, which wraps the libecl library,
     contains a module libecl.py. This module again contains the
     ctypes trickery to actually load the libecl.so shared library.
     

[1]: The big disclaimer here is that you can use the '__init__.py' to
     perform additional action when importing the 'ert' package. In
     particular all symbols defined in the '__init__.py' module/file
     will be part of the ert packages namespace. As described in the
     chapter '4. Conventions applied in ert' no such trickery is applied
     in the ert distribution, all the __init__.py files are empty.
