[SCM] Control pipeline recipes from the European Southern Observatory branch, upstream, updated. upstream/0.3.5.1-5-gcb4a635

Ole Streicher debian at liska.ath.cx
Mon Nov 26 10:57:17 UTC 2012


The following commit has been merged in the upstream branch:
commit cb4a635b5d311c0d99a85175dd026554f7e2b194
Author: Ole Streicher <debian at liska.ath.cx>
Date:   Mon Nov 26 11:57:03 2012 +0100

    New upstream version 0.4

diff --git a/PKG-INFO b/PKG-INFO
index 3447f4e..d2fef1b 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,53 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: python-cpl
-Version: 0.3.8
+Version: 0.4
 Summary: Python interface for the Common Pipeline Library.
-Home-page: http://www.aip.de/~oles/python-cpl/
+Home-page: http://www.aip.de/~oles/python-cpl
 Author: Ole Streicher
 Author-email: python-cpl at liska.ath.cx
 License: GPL
-Description: Non-official library to access CPL modules via Python. 
-        It is not meant as part of the MUSE pipeline software, but 
-        may be useful for testing.
+Download-URL: http://www.aip.de/~oles/python-cpl/python-cpl-0.4.tar.gz
+Description: This module can list, configure and execute CPL-based recipes from Python.
+        The input, calibration and output data can be specified as FITS files
+        or as pyfits objects in memory.
+        
+        The Common Pipeline Library (CPL) comprises a set of ISO-C libraries that
+        provide a comprehensive, efficient and robust software toolkit. It forms a
+        basis for the creation of automated astronomical data-reduction tasks. One of
+        the features provided by the CPL is the ability to create data-reduction
+        algorithms that run as plugins (dynamic libraries). These are called "recipes"
+        and are one of the main aspects of the CPL data-reduction development
+        environment. More information about the CPL can be found here:
+        
+          http://www.eso.org/sci/software/cpl/
+        
+        The interface may be used to run ESO pipeline recipes linked to CPL 
+        versions 4.0 to 6.2.
+        
+        Build instructions
+        ------------------
+        
+        python-cpl requires:
+        
+           - Python 2.6 or later
+           - pyfits
+        
+        python-cpl uses the standard Python distutils system to build and install
+        itself.  From the command line run::
+        
+            python setup.py install
+        
+        to install python-cpl.
 Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Unix
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2 
+Classifier: Topic :: Scientific/Engineering :: Astronomy
+Requires: pyfits
+Provides: cpl
diff --git a/README b/README
index 4b42c11..0764987 100644
--- a/README
+++ b/README
@@ -1,29 +1,33 @@
-This subdir contains an non-official library to access CPL modules via
-Python. It is not meant as part of the MUSE pipeline software, but may be
-useful for testing.
+Python interface for the Common Pipeline Library.
 
-Installation
-============
+This module can list, configure and execute CPL-based recipes from Python.
+The input, calibration and output data can be specified as FITS files
+or as pyfits objects in memory.
 
-1. Check requirements:
-   - CPL 5.X (Esorex is not needed)
-   - Python 2.6 
-   - pyfits
+The Common Pipeline Library (CPL) comprises a set of ISO-C libraries that
+provide a comprehensive, efficient and robust software toolkit. It forms a
+basis for the creation of automated astronomical data-reduction tasks. One of
+the features provided by the CPL is the ability to create data-reduction
+algorithms that run as plugins (dynamic libraries). These are called "recipes"
+and are one of the main aspects of the CPL data-reduction development
+environment. More information about the CPL can be found here:
+
+  http://www.eso.org/sci/software/cpl/
 
-2. Determine where to put the compiled python package. Standard for
-   compilations is /usr/local.
+The interface may be used to run ESO pipeline recipes linked to CPL 
+versions 4.0 to 6.2.
 
-3. In this directory, run 
+Build instructions
+------------------
 
-   python setup.py install --prefix=<PREFIX>
+python-cpl requires:
 
-   where the prefix determines the path for the compiled package. The package 
-   will be installed in the subdir lib/python2.6/site-packages 
-   (lib64/python2.6/site-packages on 64 bit systems) of <PREFIX>
+   - Python 2.6 or later
+   - pyfits
 
-   To specify non-standard include and library paths for CPL, the environment
-   variables LIBRARY_PATH and INCLUDE_PATH may be used.
+python-cpl uses the standard Python distutils system to build and install
+itself.  From the command line run::
 
-4. Add the directory <PREFIX>/lib[64]/python2.6/site-packages to your 
-   environment variable PYTHONPATH.
+    python setup.py install
 
+to install python-cpl.
diff --git a/cpl/CPL_library.c b/cpl/CPL_library.c
new file mode 100644
index 0000000..cc9fcbf
--- /dev/null
+++ b/cpl/CPL_library.c
@@ -0,0 +1,214 @@
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "CPL_library.h"
+
+unsigned long supported_versions[] = {
+    CPL_VERSION(6,2,0),
+    CPL_VERSION(6,1,1),
+    CPL_VERSION(6,0,1),
+    CPL_VERSION(6,0,0),
+    CPL_VERSION(5,3,1),
+    CPL_VERSION(5,2,0),
+    CPL_VERSION(5,1,0),
+    CPL_VERSION(5,0,1),
+    CPL_VERSION(5,0,0),
+    CPL_VERSION(4,2,0),
+    CPL_VERSION(4,1,0),
+    CPL_VERSION(4,0,1),
+    CPL_VERSION(4,0,0),
+    0
+};
+
+static cpl_library_t **libraries = NULL;
+
+/* This module provides all needed functions to run a recipe from the
+   framework. These functions are extracted from the recipe shared lib
+   (resp. the CPL linked to that) by their names. I checked that the API
+   didn't change from CPL 4.0 which is now the minimal supported version here.
+
+   Since some constants changed over the time, all used constants are
+   also included in the structure. The constants are set directly depending on
+   the CPL version number.
+
+   Note that beta releases have to be taken very cautiously here since they
+   may contain incompatible changes here.
+*/
+
+cpl_library_t *create_library(const char *fname) {
+    void *handle = dlopen(fname, RTLD_LAZY);
+    if (handle == NULL) {
+	return NULL;
+    }
+
+    char *error = dlerror();
+    typeof(cpl_init) *init = dlsym(handle, "cpl_init");
+    error = dlerror();
+    if (error != NULL) {
+	dlclose(handle);
+	return NULL;
+    }
+
+    if (libraries == NULL) {
+	libraries = malloc(sizeof(cpl_library_t *));
+	libraries[0] = NULL;
+    }
+
+    int i;
+    for (i = 0; libraries[i] != NULL; i++) {
+	if (init == libraries[i]->init) {
+	    dlclose(handle);
+	    return libraries[i];
+	}
+    }
+
+    cpl_library_t *cpl = malloc(sizeof(cpl_library_t));
+    cpl->init = init;
+
+    cpl->get_description = dlsym(handle, "cpl_get_description");
+    cpl->memory_dump = dlsym(handle, "cpl_memory_dump");
+    cpl->memory_is_empty = dlsym(handle, "cpl_memory_is_empty");
+    cpl->free = dlsym(handle, "cpl_free");
+
+    cpl->plugin_get_author = dlsym(handle, "cpl_plugin_get_author");
+    cpl->plugin_get_copyright = dlsym(handle, "cpl_plugin_get_copyright");
+    cpl->plugin_get_deinit = dlsym(handle, "cpl_plugin_get_deinit");
+    cpl->plugin_get_description = dlsym(handle, "cpl_plugin_get_description");
+    cpl->plugin_get_email = dlsym(handle, "cpl_plugin_get_email");
+    cpl->plugin_get_exec = dlsym(handle, "cpl_plugin_get_exec");
+    cpl->plugin_get_init = dlsym(handle, "cpl_plugin_get_init");
+    cpl->plugin_get_name = dlsym(handle, "cpl_plugin_get_name");
+    cpl->plugin_get_synopsis = dlsym(handle, "cpl_plugin_get_synopsis");
+    cpl->plugin_get_version = dlsym(handle, "cpl_plugin_get_version");
+    cpl->plugin_get_version_string = dlsym(handle, "cpl_plugin_get_version_string");
+    cpl->pluginlist_delete = dlsym(handle, "cpl_pluginlist_delete");
+    cpl->pluginlist_find = dlsym(handle, "cpl_pluginlist_find");
+    cpl->pluginlist_get_first = dlsym(handle, "cpl_pluginlist_get_first");
+    cpl->pluginlist_get_next = dlsym(handle, "cpl_pluginlist_get_next");
+    cpl->pluginlist_new = dlsym(handle, "cpl_pluginlist_new");
+
+    cpl->dfs_update_product_header = dlsym(handle, "cpl_dfs_update_product_header");
+
+    cpl->error_get_code = dlsym(handle, "cpl_error_get_code");
+    cpl->error_get_file = dlsym(handle, "cpl_error_get_file");
+    cpl->error_get_function = dlsym(handle, "cpl_error_get_function");
+    cpl->error_get_line = dlsym(handle, "cpl_error_get_line");
+    cpl->error_get_message = dlsym(handle, "cpl_error_get_message");
+    cpl->error_reset = dlsym(handle, "cpl_error_reset");
+    cpl->error_set_message_macro = dlsym(handle, "cpl_error_set_message_macro");
+    cpl->errorstate_dump = dlsym(handle, "cpl_errorstate_dump");
+    cpl->errorstate_get = dlsym(handle, "cpl_errorstate_get");
+
+    cpl->frame_get_filename = dlsym(handle, "cpl_frame_get_filename");
+    cpl->frame_get_group = dlsym(handle, "cpl_frame_get_group");
+    cpl->frame_get_tag = dlsym(handle, "cpl_frame_get_tag");
+    cpl->frame_new = dlsym(handle, "cpl_frame_new");
+    cpl->frame_set_filename = dlsym(handle, "cpl_frame_set_filename");
+    cpl->frame_set_tag = dlsym(handle, "cpl_frame_set_tag");
+    cpl->frameset_delete = dlsym(handle, "cpl_frameset_delete");
+    cpl->frameset_get_frame = dlsym(handle, "cpl_frameset_get_frame");
+    cpl->frameset_get_size = dlsym(handle, "cpl_frameset_get_size");
+    cpl->frameset_insert = dlsym(handle, "cpl_frameset_insert");
+    cpl->frameset_new = dlsym(handle, "cpl_frameset_new");
+
+    cpl->msg_error = dlsym(handle, "cpl_msg_error");
+    cpl->msg_set_level = dlsym(handle, "cpl_msg_set_level");
+    cpl->msg_set_log_level = dlsym(handle, "cpl_msg_set_log_level");
+    cpl->msg_set_log_name = dlsym(handle, "cpl_msg_set_log_name");
+    cpl->msg_stop_log = dlsym(handle, "cpl_msg_stop_log");
+
+    cpl->parameter_get_alias = dlsym(handle, "cpl_parameter_get_alias");
+    cpl->parameter_get_class = dlsym(handle, "cpl_parameter_get_class");
+    cpl->parameter_get_context = dlsym(handle, "cpl_parameter_get_context");
+    cpl->parameter_get_default_bool = dlsym(handle, "cpl_parameter_get_default_bool");
+    cpl->parameter_get_default_double = dlsym(handle, "cpl_parameter_get_default_double");
+    cpl->parameter_get_default_int = dlsym(handle, "cpl_parameter_get_default_int");
+    cpl->parameter_get_default_string = dlsym(handle, "cpl_parameter_get_default_string");
+    cpl->parameter_get_enum_double = dlsym(handle, "cpl_parameter_get_enum_double");
+    cpl->parameter_get_enum_int = dlsym(handle, "cpl_parameter_get_enum_int");
+    cpl->parameter_get_enum_size = dlsym(handle, "cpl_parameter_get_enum_size");
+    cpl->parameter_get_enum_string = dlsym(handle, "cpl_parameter_get_enum_string");
+    cpl->parameter_get_help = dlsym(handle, "cpl_parameter_get_help");
+    cpl->parameter_get_name = dlsym(handle, "cpl_parameter_get_name");
+    cpl->parameter_get_range_max_double = dlsym(handle, "cpl_parameter_get_range_max_double");
+    cpl->parameter_get_range_max_int = dlsym(handle, "cpl_parameter_get_range_max_int");
+    cpl->parameter_get_range_min_double = dlsym(handle, "cpl_parameter_get_range_min_double");
+    cpl->parameter_get_range_min_int = dlsym(handle, "cpl_parameter_get_range_min_int");
+    cpl->parameter_get_type = dlsym(handle, "cpl_parameter_get_type");
+    cpl->parameter_set_bool = dlsym(handle, "cpl_parameter_set_bool");
+    cpl->parameter_set_double = dlsym(handle, "cpl_parameter_set_double");
+    cpl->parameter_set_int = dlsym(handle, "cpl_parameter_set_int");
+    cpl->parameter_set_string = dlsym(handle, "cpl_parameter_set_string");
+    cpl->parameterlist_find = dlsym(handle, "cpl_parameterlist_find");
+    cpl->parameterlist_get_first = dlsym(handle, "cpl_parameterlist_get_first");
+    cpl->parameterlist_get_next = dlsym(handle, "cpl_parameterlist_get_next");
+    cpl->parameterlist_get_size = dlsym(handle, "cpl_parameterlist_get_size");
+
+    cpl->recipeconfig_get_inputs = dlsym(handle, "cpl_recipeconfig_get_inputs");
+    cpl->recipeconfig_get_max_count = dlsym(handle, "cpl_recipeconfig_get_max_count");
+    cpl->recipeconfig_get_min_count = dlsym(handle, "cpl_recipeconfig_get_min_count");
+    cpl->recipeconfig_get_outputs = dlsym(handle, "cpl_recipeconfig_get_outputs");
+    cpl->recipeconfig_get_tags = dlsym(handle, "cpl_recipeconfig_get_tags");
+    cpl->version_get_version = dlsym(handle, "cpl_version_get_version");
+
+    error = dlerror();
+    if (error != NULL) {
+	dlclose(handle);
+	free(cpl);
+	return NULL;
+    }
+    cpl->get_recipeconfig = dlsym(handle, "muse_processing_get_recipeconfig");
+    dlerror();
+
+    cpl->init(CPL_INIT_DEFAULT);
+
+    typeof(cpl_version_get_major) *get_major = dlsym(handle,
+						     "cpl_version_get_major");
+    typeof(cpl_version_get_minor) *get_minor = dlsym(handle,
+						     "cpl_version_get_minor");
+    typeof(cpl_version_get_micro) *get_micro = dlsym(handle,
+						     "cpl_version_get_micro");
+    cpl->TYPE_BOOL = CPL_TYPE_BOOL;
+    cpl->TYPE_INT = CPL_TYPE_INT;
+    cpl->TYPE_DOUBLE = CPL_TYPE_DOUBLE;
+    cpl->TYPE_STRING = CPL_TYPE_STRING;
+
+    cpl->version = CPL_VERSION(get_major(), get_minor(), get_micro());
+
+    cpl->is_supported = 0;
+    for (i = 0; supported_versions[i] != 0; i++) {
+	if (cpl->version == supported_versions[i]) {
+	    cpl->is_supported = 1;
+	}
+    }
+
+    /* Between 5.3.1 and 6.0, the cpl_type enum changed.
+       http://upstream-tracker.org/compat_reports/cpl/5.3.1_to_6.0/abi_compat_report.html#Medium_Risk_Problems
+       for these changes; the numbers were taken from there. According to
+       upstream-tracker, this seems to be the only relevant API change between
+       4.0.0 and 6.2.0.
+
+       Also the cpl_size is newly introduced (former it was int), in
+
+       cpl_frame *cpl_frameset_get_frame(cpl_frameset *self, cpl_size position);
+       cpl_size cpl_frameset_get_size(const cpl_frameset *self);
+       cpl_size cpl_parameterlist_get_size(const cpl_parameterlist *self);
+       cpl_size cpl_recipeconfig_get_min_count(const cpl_recipeconfig* self,
+                                            const char* tag, const char* input);
+       cpl_size cpl_recipeconfig_get_max_count(const cpl_recipeconfig* self,
+                                            const char* tag, const char* input);
+
+       Currently, we just ignore this :-)
+
+    */
+    if (cpl->version < CPL_VERSION(6,0,0)) {
+	cpl->TYPE_INT = (1 << 8);
+	cpl->TYPE_DOUBLE = (1 << 13);
+    }
+
+    libraries = realloc(libraries, sizeof(cpl_library_t *) * (i+2));
+    libraries[i] = cpl;
+    libraries[i+1] = NULL;
+    return cpl;
+}
+
diff --git a/cpl/CPL_library.h b/cpl/CPL_library.h
new file mode 100644
index 0000000..027a1db
--- /dev/null
+++ b/cpl/CPL_library.h
@@ -0,0 +1,125 @@
+
+#ifndef CPL_LIBRARY_H
+#define CPL_LIBRARY_H
+
+/* For the header, either the CPL one can be used, or the header that was
+   extracted from the 6.1.1 release. For API safety, it is better to include
+   the one provided with python-cpl. The other option is just for the adoption
+   to a new CPL version.
+   */
+#ifdef USE_INSTALLED_CPL_HEADER
+#include <cpl.h>
+#else
+#include "cpl_api.h"
+#endif
+
+#if CPL_VERSION_CODE < CPL_VERSION(6,0,0)
+#error CPL version too old. Minimum required version is 6.0.0.
+#endif
+#if CPL_VERSION_CODE > CPL_VERSION(6,2,0)
+#warning Newer CPL version: check API compability with 6.2.0 at http://upstream-tracker.org/versions/cpl.html
+#endif
+
+extern unsigned long supported_versions[];
+
+typedef struct {
+    unsigned long version;
+    int is_supported;
+
+    typeof(cpl_init) *init;
+    typeof(cpl_get_description) *get_description;
+    typeof(cpl_memory_dump) *memory_dump;
+    typeof(cpl_memory_is_empty) *memory_is_empty;
+    typeof(cpl_free) *free;
+
+    typeof(cpl_plugin_get_author) *plugin_get_author;
+    typeof(cpl_plugin_get_copyright) *plugin_get_copyright;
+    typeof(cpl_plugin_get_deinit) *plugin_get_deinit;
+    typeof(cpl_plugin_get_description) *plugin_get_description;
+    typeof(cpl_plugin_get_email) *plugin_get_email;
+    typeof(cpl_plugin_get_exec) *plugin_get_exec;
+    typeof(cpl_plugin_get_init) *plugin_get_init;
+    typeof(cpl_plugin_get_name) *plugin_get_name;
+    typeof(cpl_plugin_get_synopsis) *plugin_get_synopsis;
+    typeof(cpl_plugin_get_version) *plugin_get_version;
+    typeof(cpl_plugin_get_version_string) *plugin_get_version_string;
+    typeof(cpl_pluginlist_delete) *pluginlist_delete;
+    typeof(cpl_pluginlist_find) *pluginlist_find;
+    typeof(cpl_pluginlist_get_first) *pluginlist_get_first;
+    typeof(cpl_pluginlist_get_next) *pluginlist_get_next;
+    typeof(cpl_pluginlist_new) *pluginlist_new;
+
+    typeof(cpl_dfs_update_product_header) *dfs_update_product_header;
+
+    typeof(cpl_error_get_code) *error_get_code;
+    typeof(cpl_error_get_file) *error_get_file;
+    typeof(cpl_error_get_function) *error_get_function;
+    typeof(cpl_error_get_line) *error_get_line;
+    typeof(cpl_error_get_message) *error_get_message;
+    typeof(cpl_error_reset) *error_reset;
+    typeof(cpl_error_set_message_macro) *error_set_message_macro;
+    typeof(cpl_errorstate_dump) *errorstate_dump;
+    typeof(cpl_errorstate_get) *errorstate_get;
+
+    typeof(cpl_frame_get_filename) *frame_get_filename;
+    typeof(cpl_frame_get_group) *frame_get_group;
+    typeof(cpl_frame_get_tag) *frame_get_tag;
+    typeof(cpl_frame_new) *frame_new;
+    typeof(cpl_frame_set_filename) *frame_set_filename;
+    typeof(cpl_frame_set_tag) *frame_set_tag;
+    typeof(cpl_frameset_delete) *frameset_delete;
+    typeof(cpl_frameset_get_frame) *frameset_get_frame;
+    typeof(cpl_frameset_get_size) *frameset_get_size;
+    typeof(cpl_frameset_insert) *frameset_insert;
+    typeof(cpl_frameset_new) *frameset_new;
+
+    typeof(cpl_msg_error) *msg_error;
+    typeof(cpl_msg_set_level) *msg_set_level;
+    typeof(cpl_msg_set_log_level) *msg_set_log_level;
+    typeof(cpl_msg_set_log_name) *msg_set_log_name;
+    typeof(cpl_msg_stop_log) *msg_stop_log;
+
+    typeof(cpl_parameter_get_alias) *parameter_get_alias;
+    typeof(cpl_parameter_get_class) *parameter_get_class;
+    typeof(cpl_parameter_get_context) *parameter_get_context;
+    typeof(cpl_parameter_get_default_bool) *parameter_get_default_bool;
+    typeof(cpl_parameter_get_default_double) *parameter_get_default_double;
+    typeof(cpl_parameter_get_default_int) *parameter_get_default_int;
+    typeof(cpl_parameter_get_default_string) *parameter_get_default_string;
+    typeof(cpl_parameter_get_enum_double) *parameter_get_enum_double;
+    typeof(cpl_parameter_get_enum_int) *parameter_get_enum_int;
+    typeof(cpl_parameter_get_enum_size) *parameter_get_enum_size;
+    typeof(cpl_parameter_get_enum_string) *parameter_get_enum_string;
+    typeof(cpl_parameter_get_help) *parameter_get_help;
+    typeof(cpl_parameter_get_name) *parameter_get_name;
+    typeof(cpl_parameter_get_range_max_double) *parameter_get_range_max_double;
+    typeof(cpl_parameter_get_range_max_int) *parameter_get_range_max_int;
+    typeof(cpl_parameter_get_range_min_double) *parameter_get_range_min_double;
+    typeof(cpl_parameter_get_range_min_int) *parameter_get_range_min_int;
+    typeof(cpl_parameter_get_type) *parameter_get_type;
+    typeof(cpl_parameter_set_bool) *parameter_set_bool;
+    typeof(cpl_parameter_set_double) *parameter_set_double;
+    typeof(cpl_parameter_set_int) *parameter_set_int;
+    typeof(cpl_parameter_set_string) *parameter_set_string;
+    typeof(cpl_parameterlist_find) *parameterlist_find;
+    typeof(cpl_parameterlist_get_first) *parameterlist_get_first;
+    typeof(cpl_parameterlist_get_next) *parameterlist_get_next;
+    typeof(cpl_parameterlist_get_size) *parameterlist_get_size;
+
+    typeof(cpl_recipeconfig_get_inputs) *recipeconfig_get_inputs;
+    typeof(cpl_recipeconfig_get_max_count) *recipeconfig_get_max_count;
+    typeof(cpl_recipeconfig_get_min_count) *recipeconfig_get_min_count;
+    typeof(cpl_recipeconfig_get_outputs) *recipeconfig_get_outputs;
+    typeof(cpl_recipeconfig_get_tags) *recipeconfig_get_tags;
+    typeof(cpl_version_get_version) *version_get_version;
+    cpl_recipeconfig *(*get_recipeconfig)(cpl_recipe *);
+	
+    cpl_type TYPE_BOOL;
+    cpl_type TYPE_INT;
+    cpl_type TYPE_DOUBLE;
+    cpl_type TYPE_STRING;
+} cpl_library_t;
+
+cpl_library_t *create_library(const char *fname);
+
+#endif /* CPL_LIBRARY_H */
diff --git a/cpl/CPL_recipe.c b/cpl/CPL_recipe.c
index 22ee312..6babb63 100644
--- a/cpl/CPL_recipe.c
+++ b/cpl/CPL_recipe.c
@@ -15,23 +15,7 @@
 #define HAVE_MCHECK
 #endif
 
-#include <cpl.h>
-
-#define CPL_version_doc \
-    "Get the CPL version string."
-
-static PyObject *
-CPL_version(PyObject *self) {
-    return Py_BuildValue("s", cpl_version_get_version());
-}
-
-#define CPL_description_doc \
-    "Get the string of version numbers of CPL and its libraries."
-
-static PyObject *
-CPL_description(PyObject *self) {
-    return Py_BuildValue("s", cpl_get_description(CPL_DESCRIPTION_DEFAULT));
-}
+#include "CPL_library.h"
 
 #define CPL_list_doc \
     "List all CPL recipe names contained in a shared library."
@@ -49,240 +33,62 @@ CPL_list(PyObject *self, PyObject *args) {
 	return Py_None;
     }
 
-    dlerror();
+    char *error =dlerror();
     int (*cpl_plugin_get_info)(cpl_pluginlist *) = dlsym(handle,
 							 "cpl_plugin_get_info");
-    char *error = dlerror();
+    error = dlerror();
     if (error != NULL)  {
+	dlclose(handle);
 	Py_INCREF(Py_None);
 	return Py_None;
     }
+
+    cpl_library_t *cpl = create_library(file);
+
     PyObject *res = PyList_New(0);
     Py_INCREF(res);
-    cpl_pluginlist *list = cpl_pluginlist_new();
+    cpl_pluginlist *list = cpl->pluginlist_new();
     (*cpl_plugin_get_info)(list);
     cpl_plugin *plugin;
-    for (plugin = cpl_pluginlist_get_first(list);
+    for (plugin = cpl->pluginlist_get_first(list);
 	 plugin != NULL;
-	 plugin = cpl_pluginlist_get_next(list)) {
-	cpl_error_reset();
-	cpl_plugin_get_init(plugin)(plugin);
+	 plugin = cpl->pluginlist_get_next(list)) {
+	cpl->error_reset();
+	cpl->plugin_get_init(plugin)(plugin);
 	PyList_Append(res, Py_BuildValue("sis", 
-					 cpl_plugin_get_name(plugin),
-					 cpl_plugin_get_version(plugin),
-					 cpl_plugin_get_version_string(plugin)));
-	cpl_plugin_get_deinit(plugin)(plugin);
+					 cpl->plugin_get_name(plugin),
+					 cpl->plugin_get_version(plugin),
+					 cpl->plugin_get_version_string(plugin)));
+	cpl->plugin_get_deinit(plugin)(plugin);
     }
-    cpl_pluginlist_delete(list);
-    cpl_error_reset();
+    cpl->pluginlist_delete(list);
+    cpl->error_reset();
     dlclose(handle);
     return res;
 }
 
-#define CPL_set_msg_level_doc \
-    "Set verbosity level of output to terminal."
-
-static PyObject *
-CPL_set_msg_level(PyObject *self, PyObject *args) {
-    int level;
-
-    if (!PyArg_ParseTuple(args, "i", &level))
-        return NULL;
-    cpl_msg_set_level(level);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_get_msg_level_doc \
-    "Get current terminal verbosity level."
-
-static PyObject *
-CPL_get_msg_level(PyObject *self) {
-    return Py_BuildValue("i", cpl_msg_get_level());
-}
-
-#define CPL_set_msg_time_doc \
-    "Enable or disable the time tag in output messages."
-
-static PyObject *
-CPL_set_msg_time(PyObject *self, PyObject *args) {
-    PyObject *enable;
-    if (!PyArg_ParseTuple(args, "O", &enable))
-        return NULL;
-
-    if (PyObject_IsTrue(enable)) {
-	cpl_msg_set_time_on();
-    } else {
-	cpl_msg_set_time_off();
-    }
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_set_log_level_doc \
-    "Set verbosity level of output to logfile."
-
-static PyObject *
-CPL_set_log_level(PyObject *self, PyObject *args) {
-    int level;
-
-    if (!PyArg_ParseTuple(args, "i", &level))
-        return NULL;
-    cpl_msg_set_log_level(level);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_get_log_level_doc \
-    "Get current logfile verbosity level."
-
-static PyObject *
-CPL_get_log_level(PyObject *self) {
-    return Py_BuildValue("i", cpl_msg_get_log_level());
-}
-
-#define CPL_set_log_file_doc \
-    "Set the log file name."
-
-static PyObject *
-CPL_set_log_file(PyObject *self, PyObject *args) {
-    const char *name;
-
-    if (!PyArg_ParseTuple(args, "s", &name))
-        return NULL;
-    cpl_error_code r = cpl_msg_set_log_name(name);
-    if (r != CPL_ERROR_NONE) {
-	PyErr_SetString(PyExc_IOError, cpl_error_get_message());
-	return NULL;
-    }
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_get_log_file_doc \
-    "Get the log file name."
-
-static PyObject *
-CPL_get_log_file(PyObject *self) {
-    return Py_BuildValue("s", cpl_msg_get_log_name());
-}
-
-#define CPL_set_log_domain_doc \
-    "Set the log domain name."
-
-static PyObject *
-CPL_set_log_domain(PyObject *self, PyObject *args) {
-    const char *name;
-
-    if (!PyArg_ParseTuple(args, "s", &name))
-        return NULL;
-    cpl_msg_set_domain(name);
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_get_log_domain_doc \
-    "Get the log domain name."
-
-static PyObject *
-CPL_get_log_domain(PyObject *self) {
-    return Py_BuildValue("s", cpl_msg_get_domain());
-}
-
-#define CPL_log_doc \
-    "Write a log message."
+#define CPL_supported_versions_doc \
+    "List all supported CPL versions."
 
 static PyObject *
-CPL_log(PyObject *self, PyObject *args) {
-    int level;
-    const char *msg;
-    const char *caller;
-    if (!PyArg_ParseTuple(args, "iss", &level, &caller, &msg))
-        return NULL;
-    if (level < 0) level = 0;
-    typedef void (*msg_func_t)(const char *, const char *,...);
-    msg_func_t msg_func[]= {
-	cpl_msg_debug,
-	cpl_msg_info,
-	cpl_msg_warning,
-	cpl_msg_error
-    };
-    if (level <=3) {
-	msg_func[level](caller, "%s", msg);
+CPL_supported_versions(PyObject *self, PyObject *args) {
+    PyObject *res = PyList_New(0);
+    Py_INCREF(res);
+    int i;
+    for (i = 0; supported_versions[i] != 0; i++) {
+	PyList_Append(res, Py_BuildValue(
+			  "iii", 
+			  CPL_VERSION_MAJOR_CODE(supported_versions[i]),
+			  CPL_VERSION_MINOR_CODE(supported_versions[i]),
+			  CPL_VERSION_MICRO_CODE(supported_versions[i])));
     }
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_log_indent_more_doc \
-    "Increase the message indentation by one indentation step."
-
-static PyObject *
-CPL_log_indent_more(PyObject *self) {
-    cpl_msg_indent_more();
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_log_indent_less_doc \
-    "Decrease the message indentation by one indentation step."
-
-static PyObject *
-CPL_log_indent_less(PyObject *self) {
-    cpl_msg_indent_less();
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_memory_dump_doc \
-    "Display the memory status."
-
-static PyObject *
-CPL_memory_dump(PyObject *self) {
-    cpl_memory_dump();
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-#define CPL_memory_is_empty_doc \
-    "Tell if there is some memory allocated."
-
-static PyObject *
-CPL_memory_is_empty(PyObject *self) {
-    return Py_BuildValue("i", cpl_memory_is_empty());
+    return res;
 }
 
 static PyMethodDef CPL_methods[] = {
-    {"version", (PyCFunction)CPL_version, METH_NOARGS, CPL_version_doc},
-    {"description", (PyCFunction)CPL_description, METH_NOARGS, CPL_version_doc},
     {"list", CPL_list, METH_VARARGS, CPL_list_doc},
-    {"set_msg_level", CPL_set_msg_level, METH_VARARGS, CPL_set_msg_level_doc},
-    {"get_msg_level", (PyCFunction)CPL_get_msg_level, METH_NOARGS, 
-     CPL_get_msg_level_doc},
-    {"set_msg_time", CPL_set_msg_time, METH_VARARGS, CPL_set_msg_time_doc },
-    {"set_log_level", CPL_set_log_level, METH_VARARGS, CPL_set_log_level_doc},
-    {"get_log_level", (PyCFunction)CPL_get_log_level, METH_NOARGS, 
-     CPL_get_log_level_doc},
-    {"set_log_file", CPL_set_log_file, METH_VARARGS, CPL_set_log_file_doc},
-    {"get_log_file", (PyCFunction)CPL_get_log_file, METH_NOARGS, 
-     CPL_get_log_file_doc },
-    {"set_log_domain", CPL_set_log_domain, METH_VARARGS, 
-     CPL_set_log_domain_doc},
-    {"get_log_domain", (PyCFunction)CPL_get_log_domain, METH_NOARGS, 
-     CPL_get_log_domain_doc },
-    {"log", CPL_log, METH_VARARGS, CPL_log_doc}, 
-    {"log_indent_more", (PyCFunction)CPL_log_indent_more, METH_NOARGS, 
-     CPL_log_indent_more_doc},
-    {"log_indent_less", (PyCFunction)CPL_log_indent_less, METH_NOARGS, 
-     CPL_log_indent_less_doc},
-    {"memory_dump", (PyCFunction)CPL_memory_dump, METH_NOARGS, 
-     CPL_memory_dump_doc},
-    {"memory_is_empty", (PyCFunction)CPL_memory_is_empty, METH_NOARGS, 
-     CPL_memory_is_empty_doc},
+    {"cpl_versions", CPL_supported_versions, METH_NOARGS, 
+     CPL_supported_versions_doc},
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };
 
@@ -292,15 +98,16 @@ typedef struct {
     cpl_pluginlist *pluginlist;
     void *handle;
     cpl_recipeconfig *recipeconfig;
+    cpl_library_t *cpl;
 } CPL_recipe;
 
 static void
 CPL_recipe_dealloc(CPL_recipe* self) {
     if (self->plugin != NULL) {
-	cpl_plugin_get_deinit(self->plugin)(self->plugin);
+	self->cpl->plugin_get_deinit(self->plugin)(self->plugin);
     }
     if (self->pluginlist != NULL) {
-	cpl_pluginlist_delete(self->pluginlist);
+	self->cpl->pluginlist_delete(self->pluginlist);
     }
     if (self->handle != NULL) {
 	dlclose(self->handle);
@@ -316,6 +123,7 @@ CPL_recipe_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
 	self->pluginlist = NULL;
 	self->handle = NULL;
 	self->recipeconfig = NULL;
+	self->cpl = NULL;
     }
     return (PyObject *)self;
 }
@@ -346,106 +154,112 @@ CPL_recipe_init(CPL_recipe *self, PyObject *args, PyObject *kwds) {
 	PyErr_SetString(PyExc_IOError, error);
 	return -1;
     }
-    cpl_error_reset();
-    self->pluginlist = cpl_pluginlist_new();
+
+    self->cpl = create_library(file);
+    self->cpl->error_reset();
+    self->pluginlist = self->cpl->pluginlist_new();
     (*cpl_plugin_get_info)(self->pluginlist);
-    self->plugin = cpl_pluginlist_find(self->pluginlist, recipe);
+    self->plugin = self->cpl->pluginlist_find(self->pluginlist, recipe);
     if (self->plugin == NULL) {
 	PyErr_SetString(PyExc_IOError, "cannot find recipe in shared library");
 	return -1;
     } else {
-	cpl_plugin_get_init(self->plugin)(self->plugin);
+	self->cpl->plugin_get_init(self->plugin)(self->plugin);
     }
     
-    cpl_recipeconfig *(*get_recipeconfig)(cpl_recipe *) 
-	= dlsym(self->handle, "muse_processing_get_recipeconfig");
-    if (dlerror() == NULL && get_recipeconfig != NULL) {
-	self->recipeconfig = get_recipeconfig((cpl_recipe *)self->plugin);
+    if (self->cpl->get_recipeconfig != NULL) {
+	self->recipeconfig = self->cpl->get_recipeconfig((cpl_recipe *)self->plugin);
     } else {
 	self->recipeconfig = NULL;
     }
     return 0;
 }
 
+#define CPL_is_supported_doc \
+    "Check whether the CPL version is supported by python-cpl."
+
+static PyObject *
+CPL_is_supported(CPL_recipe *self) {
+    return (self->cpl->is_supported)?Py_True:Py_False;
+}
+
+#define CPL_version_doc \
+    "Get the CPL version string."
+
+static PyObject *
+CPL_version(CPL_recipe *self) {
+    return Py_BuildValue("s", self->cpl->version_get_version());
+}
+
+#define CPL_description_doc \
+    "Get the string of version numbers of CPL and its libraries."
+
+static PyObject *
+CPL_description(CPL_recipe *self) {
+    return Py_BuildValue("s", self->cpl->get_description(CPL_DESCRIPTION_DEFAULT));
+}
+
 static PyObject *
-getParameter(cpl_parameter *param) {
-    cpl_type type = cpl_parameter_get_type(param);
-    cpl_parameter_class class = cpl_parameter_get_class(param);
-    const char *name = cpl_parameter_get_alias(param, 
+getParameter(CPL_recipe *self, cpl_parameter *param) {
+    cpl_type type = self->cpl->parameter_get_type(param);
+    cpl_parameter_class class = self->cpl->parameter_get_class(param);
+    const char *name = self->cpl->parameter_get_alias(param, 
 					       CPL_PARAMETER_MODE_CLI);
-    const char *fullname = cpl_parameter_get_name(param);
-    const char *context = cpl_parameter_get_context(param);
-    const char *help = cpl_parameter_get_help(param);
+    const char *fullname = self->cpl->parameter_get_name(param);
+    const char *context = self->cpl->parameter_get_context(param);
+    const char *help = self->cpl->parameter_get_help(param);
     PyObject *range = Py_None;
     if (class == CPL_PARAMETER_CLASS_RANGE) {
-	switch (type) {
-	    case CPL_TYPE_INT:
-		range = Py_BuildValue("ii",
-				      cpl_parameter_get_range_min_int(param),
-				      cpl_parameter_get_range_max_int(param));
-		break;
-	    case CPL_TYPE_DOUBLE:
-		range = Py_BuildValue("dd",
-				      cpl_parameter_get_range_min_double(param),
-				      cpl_parameter_get_range_max_double(param));
-		break;
-	    default:
-		break;
+	if (type == self->cpl->TYPE_INT) {
+	    range = Py_BuildValue("ii",
+				  self->cpl->parameter_get_range_min_int(param),
+				  self->cpl->parameter_get_range_max_int(param));
+	} else if (type == self->cpl->TYPE_DOUBLE) {
+	    range = Py_BuildValue("dd",
+				  self->cpl->parameter_get_range_min_double(param),
+				  self->cpl->parameter_get_range_max_double(param));
 	}
     }
     Py_INCREF(range);
     PyObject *sequence = Py_None;
     if (class == CPL_PARAMETER_CLASS_ENUM) {
 	sequence = PyList_New(0);
-	int n_enum = cpl_parameter_get_enum_size(param);
+	int n_enum = self->cpl->parameter_get_enum_size(param);
 	int i;
 	for (i = 0; i < n_enum; i++) {
-	    switch (type) {
-		case CPL_TYPE_INT:
-		    PyList_Append(
-			sequence, 
-			Py_BuildValue("i",
-				      cpl_parameter_get_enum_int(param, i)));
-		    break;
-		case CPL_TYPE_DOUBLE:
-		    PyList_Append(
-			sequence, 
-			Py_BuildValue("d",
-				      cpl_parameter_get_enum_double(param, i)));
-		    break;
-		case CPL_TYPE_STRING:
-		    PyList_Append(
-			sequence, 
-			Py_BuildValue("s",
-				      cpl_parameter_get_enum_string(param, i)));
-		    break;
-		default:
-		    break;
+	    if (type == self->cpl->TYPE_INT) {
+		PyList_Append(
+		    sequence, 
+		    Py_BuildValue("i",
+				  self->cpl->parameter_get_enum_int(param, i)));
+	    } else if (type == self->cpl->TYPE_DOUBLE) {
+		PyList_Append(
+		    sequence, 
+		    Py_BuildValue("d",
+				  self->cpl->parameter_get_enum_double(param, i)));
+	    } else if (type == self->cpl->TYPE_STRING) {
+		PyList_Append(
+		    sequence, 
+		    Py_BuildValue("s",
+				  self->cpl->parameter_get_enum_string(param, i)));
 	    }
 	}
     }
     Py_INCREF(sequence);
     PyObject *deflt = Py_None;
     PyObject *ptype = Py_None;
-    switch (type) {
-	case CPL_TYPE_BOOL:
-	    ptype = (PyObject *)&PyBool_Type;
-	    deflt = (cpl_parameter_get_default_bool(param))?Py_True:Py_False;
-	    break;
-	case CPL_TYPE_INT:
-	    ptype = (PyObject *)&PyInt_Type;
-	    deflt = Py_BuildValue("i", cpl_parameter_get_default_int(param));
-	    break;
-	case CPL_TYPE_DOUBLE:
-	    ptype = (PyObject *)&PyFloat_Type;
-	    deflt = Py_BuildValue("d", cpl_parameter_get_default_double(param));
-	    break;
-	case CPL_TYPE_STRING:
-	    ptype = (PyObject *)&PyString_Type;
-	    deflt = Py_BuildValue("s", cpl_parameter_get_default_string(param));
-	    break;
-	default:
-	    break;
+    if (type == self->cpl->TYPE_BOOL) {
+	ptype = (PyObject *)&PyBool_Type;
+	deflt = (self->cpl->parameter_get_default_bool(param))?Py_True:Py_False;
+    } else if (type == self->cpl->TYPE_INT) {
+	ptype = (PyObject *)&PyInt_Type;
+	deflt = Py_BuildValue("i", self->cpl->parameter_get_default_int(param));
+    } else if (type == self->cpl->TYPE_DOUBLE) {
+	ptype = (PyObject *)&PyFloat_Type;
+	deflt = Py_BuildValue("d", self->cpl->parameter_get_default_double(param));
+    } else if (type == self->cpl->TYPE_STRING) {
+	ptype = (PyObject *)&PyString_Type;
+	deflt = Py_BuildValue("s", self->cpl->parameter_get_default_string(param));
     }
     Py_INCREF(deflt);
     Py_INCREF(ptype);
@@ -476,12 +290,12 @@ CPL_recipe_get_params(CPL_recipe *self) {
     }
     cpl_parameterlist *pars = ((cpl_recipe *)self->plugin)->parameters;
     PyObject *res = PyList_New(0);
-    if (pars && cpl_parameterlist_get_size(pars)) {
+    if (pars && self->cpl->parameterlist_get_size(pars)) {
 	cpl_parameter *param;
-	for (param = cpl_parameterlist_get_first(pars);
+	for (param = self->cpl->parameterlist_get_first(pars);
 	     param != NULL;  
-	     param = cpl_parameterlist_get_next(pars)) {
-	    PyList_Append(res, getParameter(param));
+	     param = self->cpl->parameterlist_get_next(pars)) {
+	    PyList_Append(res, getParameter(self, param));
 	}
     }
     Py_INCREF(res);
@@ -500,8 +314,8 @@ CPL_recipe_get_author(CPL_recipe *self) {
 	return NULL;
     }
     return Py_BuildValue("ss", 
-			 cpl_plugin_get_author(self->plugin),
-			 cpl_plugin_get_email(self->plugin));
+			 self->cpl->plugin_get_author(self->plugin),
+			 self->cpl->plugin_get_email(self->plugin));
 }
 
 #define CPL_recipe_get_description_doc                                      \
@@ -516,8 +330,8 @@ CPL_recipe_get_description(CPL_recipe *self) {
 	return NULL;
     }
     return Py_BuildValue("ss", 
-			 cpl_plugin_get_synopsis(self->plugin),
-			 cpl_plugin_get_description(self->plugin));
+			 self->cpl->plugin_get_synopsis(self->plugin),
+			 self->cpl->plugin_get_description(self->plugin));
 }
 
 #define CPL_recipe_get_version_doc                                            \
@@ -532,8 +346,8 @@ CPL_recipe_get_version(CPL_recipe *self) {
 	return NULL;
     }
     return Py_BuildValue("is", 
-			 cpl_plugin_get_version(self->plugin),
-			 cpl_plugin_get_version_string(self->plugin));
+			 self->cpl->plugin_get_version(self->plugin),
+			 self->cpl->plugin_get_version_string(self->plugin));
 }
 
 #define CPL_recipe_get_copyright_doc                                          \
@@ -546,7 +360,7 @@ CPL_recipe_get_copyright(CPL_recipe *self) {
 	return NULL;
     }
     return Py_BuildValue("s", 
-			 cpl_plugin_get_copyright(self->plugin));
+			 self->cpl->plugin_get_copyright(self->plugin));
 }
 
 #define CPL_recipe_get_frameconfig_doc                                        \
@@ -571,63 +385,63 @@ CPL_recipe_get_frameconfig(CPL_recipe *self) {
 	return Py_None;
     }
     PyObject *res = PyList_New(0);
-    char **tags = cpl_recipeconfig_get_tags(self->recipeconfig);
+    char **tags = self->cpl->recipeconfig_get_tags(self->recipeconfig);
     int i_tag;
     for (i_tag = 0; tags[i_tag] != NULL; i_tag++) {
-	int min = cpl_recipeconfig_get_min_count(self->recipeconfig,
+	int min = self->cpl->recipeconfig_get_min_count(self->recipeconfig,
 						 tags[i_tag], tags[i_tag]);
-	int max = cpl_recipeconfig_get_max_count(self->recipeconfig,
+	int max = self->cpl->recipeconfig_get_max_count(self->recipeconfig,
 						 tags[i_tag], tags[i_tag]);
 	PyObject *raw = Py_BuildValue("sii", tags[i_tag], min, max);
 	PyObject *calib = PyList_New(0);
 
-	char **inputs = cpl_recipeconfig_get_inputs(self->recipeconfig,
+	char **inputs = self->cpl->recipeconfig_get_inputs(self->recipeconfig,
 						    tags[i_tag]);
 	int i_input;
 	for (i_input = 0; inputs[i_input] != NULL; i_input++) {
-	    int min = cpl_recipeconfig_get_min_count(self->recipeconfig,
+	    int min = self->cpl->recipeconfig_get_min_count(self->recipeconfig,
 						     tags[i_tag], 
 						     inputs[i_input]);
-	    int max = cpl_recipeconfig_get_max_count(self->recipeconfig,
+	    int max = self->cpl->recipeconfig_get_max_count(self->recipeconfig,
 						     tags[i_tag], 
 						     inputs[i_input]);
 	    PyList_Append(calib, Py_BuildValue("sii", inputs[i_input], 
 					       min, max));
-	    cpl_free(inputs[i_input]);
+	    self->cpl->free(inputs[i_input]);
 	}
-	cpl_free(inputs);
+	self->cpl->free(inputs);
 
 	PyObject *output = PyList_New(0);
-	char **outputs = cpl_recipeconfig_get_outputs(self->recipeconfig,
+	char **outputs = self->cpl->recipeconfig_get_outputs(self->recipeconfig,
 						      tags[i_tag]);
 	int i_output;
 	for (i_output = 0; outputs[i_output] != NULL; i_output++) {
 	    PyList_Append(output, Py_BuildValue("s", outputs[i_output]));
-	    cpl_free(outputs[i_output]);
+	    self->cpl->free(outputs[i_output]);
 	}
-	cpl_free(outputs);
+	self->cpl->free(outputs);
 	
 	PyList_Append(res, Py_BuildValue("OOO", raw, calib, output));
 	
-	cpl_free(tags[i_tag]);
+	self->cpl->free(tags[i_tag]);
     }
-    cpl_free(tags);
+    self->cpl->free(tags);
     return res;
 }
 
 static cpl_frameset *
-get_frames(PyObject *framelist) {
-    cpl_frameset *frames = cpl_frameset_new();
+get_frames(CPL_recipe *self, PyObject *framelist) {
+    cpl_frameset *frames = self->cpl->frameset_new();
     PyObject *iter = PyObject_GetIter(framelist);
     PyObject *item;
     while ((item = PyIter_Next(iter))) {
 	const char *tag;
 	const char* file;
 	PyArg_ParseTuple(item, "ss", &tag, &file);
-	cpl_frame *frame = cpl_frame_new();
-	cpl_frame_set_filename(frame, file);
-	cpl_frame_set_tag(frame, tag);
-	cpl_frameset_insert(frames, frame);
+	cpl_frame *frame = self->cpl->frame_new();
+	self->cpl->frame_set_filename(frame, file);
+	self->cpl->frame_set_tag(frame, tag);
+	self->cpl->frameset_insert(frames, frame);
 	Py_DECREF(item);
     }
     Py_DECREF(iter);
@@ -635,74 +449,81 @@ get_frames(PyObject *framelist) {
 }
 
 static void 
-clear_parameters(cpl_parameterlist *parameters) {
-    cpl_parameter *par = cpl_parameterlist_get_first(parameters);
+clear_parameters(CPL_recipe *self, cpl_parameterlist *parameters) {
+    cpl_parameter *par = self->cpl->parameterlist_get_first(parameters);
     while (par != NULL) {
-	cpl_type type = cpl_parameter_get_type(par);
-	switch(type) {
-	    case CPL_TYPE_STRING: {
-		const char *default_value = cpl_parameter_get_default_string(par);
-		if (default_value == NULL) {
-		    default_value = "";
-		}
-		cpl_parameter_set_string(par, default_value);
+	cpl_type type = self->cpl->parameter_get_type(par);
+	if (type == self->cpl->TYPE_STRING) {
+	    const char *default_value = self->cpl->parameter_get_default_string(par);
+	    if (default_value == NULL) {
+		default_value = "";
 	    }
-		break;
-	    case CPL_TYPE_INT:
-		cpl_parameter_set_int(par, 
-				      cpl_parameter_get_default_int(par));
-		break;
-	    case CPL_TYPE_DOUBLE:
-		cpl_parameter_set_double(par, 
-					 cpl_parameter_get_default_double(par));
-		break;
-	    case CPL_TYPE_BOOL:
-		cpl_parameter_set_bool(par, 
-				       cpl_parameter_get_default_bool(par));
-		break;
-	    default:
-		break;
+	    self->cpl->parameter_set_string(par, default_value);
+	} else if (type == self->cpl->TYPE_INT) {
+	    self->cpl->parameter_set_int(par, 
+				 self->cpl->parameter_get_default_int(par));
+	} else if (type == self->cpl->TYPE_DOUBLE) {
+	    self->cpl->parameter_set_double(par, 
+				 self->cpl->parameter_get_default_double(par));
+	} else if (type == self->cpl->TYPE_BOOL) {
+	    self->cpl->parameter_set_bool(par, 
+				 self->cpl->parameter_get_default_bool(par));
 	}
 	
-	par = cpl_parameterlist_get_next(parameters);
+	par = self->cpl->parameterlist_get_next(parameters);
     }
     
 }
 
 static void
-set_parameters(cpl_parameterlist *parameters, PyObject *parlist) {
+set_parameters(CPL_recipe *self, cpl_parameterlist *parameters, PyObject *parlist) {
     PyObject *iter = PyObject_GetIter(parlist);
     PyObject *item;
     while ((item = PyIter_Next(iter))) {
 	const char *name;
 	PyObject *value;
 	PyArg_ParseTuple(item, "sO", &name, &value);
-	cpl_parameter *par = cpl_parameterlist_find(parameters, name);
+	cpl_parameter *par = self->cpl->parameterlist_find(parameters, name);
 	if (par == NULL) {
 	    continue;
 	}
-	cpl_type type = cpl_parameter_get_type(par);
-	switch(type) {
-	    case CPL_TYPE_STRING:
-		if (PyString_Check(value)) {
-		    cpl_parameter_set_string(par, PyString_AsString(value));
-		}
-		break;
-	    case CPL_TYPE_INT:
-		if (PyInt_Check(value)) {
-		    cpl_parameter_set_int(par, PyInt_AsLong(value));
-		}
-		break;
-	    case CPL_TYPE_DOUBLE:
-		if (PyFloat_Check(value)) {
-		    cpl_parameter_set_double(par, PyFloat_AsDouble(value));
-		}
-		break;
-	    case CPL_TYPE_BOOL:
-		cpl_parameter_set_bool(par, PyObject_IsTrue(value));
-		break;
-	    default:
-		break;
+	cpl_type type = self->cpl->parameter_get_type(par);
+	if (type == self->cpl->TYPE_STRING) {
+	    if (PyString_Check(value)) {
+		self->cpl->parameter_set_string(par, PyString_AsString(value));
+	    }
+	} else if (type == self->cpl->TYPE_INT) {
+	    if (PyInt_Check(value)) {
+		self->cpl->parameter_set_int(par, PyInt_AsLong(value));
+	    }
+	} else if (type == self->cpl->TYPE_DOUBLE) {
+	    if (PyFloat_Check(value)) {
+		self->cpl->parameter_set_double(par, PyFloat_AsDouble(value));
+	    }
+	} else if (type == self->cpl->TYPE_BOOL) {
+	    self->cpl->parameter_set_bool(par, PyObject_IsTrue(value));
+	}
+	Py_DECREF(item);
+    }
+    Py_DECREF(iter);
+}
+
+static void
+set_environment(PyObject *runenv) {
+    PyObject *iter = PyObject_GetIter(runenv);
+    PyObject *item;
+    while ((item = PyIter_Next(iter))) {
+	const char *name;
+	PyObject *value;
+	PyArg_ParseTuple(item, "sO", &name, &value);
+	if ((name == NULL) || (value == NULL)) {
+	    continue;
+	}
+	if (PyString_Check(value)) {
+	    setenv(name, PyString_AsString(value), 1);
+	}
+	if (value == Py_None) {
+	    unsetenv(name);
 	}
 	Py_DECREF(item);
     }
@@ -750,58 +571,60 @@ exec_build_retval(void *ptr) {
 }
 
 static void *sbuffer_append_string(void *buf, const char *str) {
-    buf = cpl_realloc(buf, ((long *)buf)[0] + strlen(str) + 1);
+    buf = realloc(buf, ((long *)buf)[0] + strlen(str) + 1);
     strcpy(buf + *((long *)buf), str);
     *((long *)buf) += strlen(str) + 1;
     return buf;
 }
 
 static void *sbuffer_append_bytes(void *buf, const void *src, size_t nbytes) {
-    buf = cpl_realloc(buf, ((long *)buf)[0] + nbytes);
+    buf = realloc(buf, ((long *)buf)[0] + nbytes);
     memcpy(buf + *((long *)buf), src, nbytes);
     *((long *)buf) += nbytes;
     return buf;
 }
 
 static void *sbuffer_append_long(void *buf, long val) {
-    buf = cpl_realloc(buf, *((long *)buf) + sizeof(long));
+    buf = realloc(buf, *((long *)buf) + sizeof(long));
     *((long *)(buf + ((long *)buf)[0])) = val;
     *((long *)buf) += sizeof(long);
     return buf;
 }
 
 static void *serialized_error_ptr = NULL;
+static cpl_library_t *serialized_cpl = NULL;
 
 static void 
 exec_serialize_one_error(unsigned self, unsigned first, unsigned last) {
     if (serialized_error_ptr == NULL) {
-	serialized_error_ptr = cpl_malloc(sizeof(long));
+	serialized_error_ptr = malloc(sizeof(long));
 	((long *)serialized_error_ptr)[0] = sizeof(long);
 	serialized_error_ptr = sbuffer_append_long(serialized_error_ptr, 0);
     }
-    if (cpl_error_get_code() == CPL_ERROR_NONE) {
+    if (serialized_cpl->error_get_code() == CPL_ERROR_NONE) {
 	return;
     }
     ((long *)serialized_error_ptr)[1]++; // number of errors
 
     serialized_error_ptr = sbuffer_append_long(serialized_error_ptr, 
-					       cpl_error_get_code());
+					       serialized_cpl->error_get_code());
     serialized_error_ptr = sbuffer_append_long(serialized_error_ptr, 
-					       cpl_error_get_line());
+					       serialized_cpl->error_get_line());
     serialized_error_ptr = sbuffer_append_string(serialized_error_ptr, 
-						 cpl_error_get_message());
+						 serialized_cpl->error_get_message());
     serialized_error_ptr = sbuffer_append_string(serialized_error_ptr, 
-						 cpl_error_get_file());
+						 serialized_cpl->error_get_file());
     serialized_error_ptr = sbuffer_append_string(serialized_error_ptr, 
-						 cpl_error_get_function());
+						 serialized_cpl->error_get_function());
 }
 
 static void *
-exec_serialize_retval(cpl_frameset *frames, cpl_errorstate prestate, int retval, 
+exec_serialize_retval(CPL_recipe *self, cpl_frameset *frames, 
+		      cpl_errorstate prestate, int retval, 
 		      const struct tms *tms_clock) {
-    int n_frames = cpl_frameset_get_size(frames);
+    int n_frames = self->cpl->frameset_get_size(frames);
     int i_frame;
-    void *ptr = cpl_malloc(sizeof(long));
+    void *ptr = malloc(sizeof(long));
     ((long *)ptr)[0] = sizeof(long);
     ptr = sbuffer_append_long(ptr, retval);
     ptr = sbuffer_append_long(ptr, 1000000L * 
@@ -810,21 +633,23 @@ exec_serialize_retval(cpl_frameset *frames, cpl_errorstate prestate, int retval,
     ptr = sbuffer_append_long(ptr, 1000000L * 
 			      (tms_clock->tms_stime + tms_clock->tms_cstime)
 			      / sysconf(_SC_CLK_TCK));
-    ptr = sbuffer_append_long(ptr, cpl_memory_is_empty());
+    ptr = sbuffer_append_long(ptr, self->cpl->memory_is_empty());
 
-    cpl_errorstate_dump(prestate, CPL_FALSE, exec_serialize_one_error);
+    serialized_cpl = self->cpl;
+    self->cpl->errorstate_dump(prestate, CPL_FALSE, exec_serialize_one_error);
     ptr = sbuffer_append_bytes(ptr, serialized_error_ptr + sizeof(long),
 			       ((long *)serialized_error_ptr)[0] - sizeof(long));
-    cpl_free(serialized_error_ptr);
+    free(serialized_error_ptr);
     serialized_error_ptr = NULL;
+    serialized_cpl = NULL;
 
     for (i_frame = 0; i_frame < n_frames; i_frame++) {
-	cpl_frame *f = cpl_frameset_get_frame(frames, i_frame);
-	if (cpl_frame_get_group(f) != CPL_FRAME_GROUP_PRODUCT) {
+	cpl_frame *f = self->cpl->frameset_get_frame(frames, i_frame);
+	if (self->cpl->frame_get_group(f) != CPL_FRAME_GROUP_PRODUCT) {
 	    continue;
 	}
-	ptr = sbuffer_append_string(ptr, cpl_frame_get_tag(f));
-	ptr = sbuffer_append_string(ptr, cpl_frame_get_filename(f));
+	ptr = sbuffer_append_string(ptr, self->cpl->frame_get_tag(f));
+	ptr = sbuffer_append_string(ptr, self->cpl->frame_get_filename(f));
     }
     return ptr;
 }
@@ -878,7 +703,7 @@ static void setup_tracing(CPL_recipe *self) {
 #endif
 #ifdef PR_SET_NAME
 /*  Set  the  process  name  for  the  calling  process */
-    prctl(PR_SET_NAME, cpl_plugin_get_name(self->plugin), 0, 0, 0);
+    prctl(PR_SET_NAME, self->cpl->plugin_get_name(self->plugin), 0, 0, 0);
 #endif
 #endif
 #ifdef HAVE_MCHECK
@@ -908,12 +733,13 @@ static PyObject *
 CPL_recipe_exec(CPL_recipe *self, PyObject *args) {
     PyObject *parlist;
     PyObject *soflist;
+    PyObject *runenv;
     const char *dirname;
     const char *logfile;
     int loglevel;
     int memory_dump;
-    if (!PyArg_ParseTuple(args, "sOOsii", &dirname, &parlist, &soflist,
-			  &logfile, &loglevel, &memory_dump))
+    if (!PyArg_ParseTuple(args, "sOOOsii", &dirname, &parlist, &soflist,
+			  &runenv, &logfile, &loglevel, &memory_dump))
         return NULL;
     if (!PySequence_Check(parlist)) {
 	PyErr_SetString(PyExc_TypeError, "Second parameter not a list");
@@ -923,18 +749,22 @@ CPL_recipe_exec(CPL_recipe *self, PyObject *args) {
 	PyErr_SetString(PyExc_TypeError, "Third parameter not a list");
 	return NULL;
     }
+    if (!PySequence_Check(runenv)) {
+	PyErr_SetString(PyExc_TypeError, "Fourth parameter not a list");
+	return NULL;
+    }
 
     if (self->plugin == NULL) {
 	PyErr_SetString(PyExc_IOError, "NULL recipe");
 	return NULL;
     }
-    cpl_error_reset();
+    self->cpl->error_reset();
     cpl_recipe *recipe = (cpl_recipe *)self->plugin;
-    cpl_frameset_delete(recipe->frames);
-    recipe->frames = get_frames(soflist);
-    clear_parameters(recipe->parameters);
-    set_parameters(recipe->parameters, parlist);
-    if (cpl_error_get_code() != CPL_ERROR_NONE) {
+    self->cpl->frameset_delete(recipe->frames);
+    recipe->frames = get_frames(self, soflist);
+    clear_parameters(self, recipe->parameters);
+    set_parameters(self, recipe->parameters, parlist);
+    if (self->cpl->error_get_code() != CPL_ERROR_NONE) {
 	PyErr_SetString(PyExc_IOError, "CPL error on inititalization");
 	return NULL;	
     }
@@ -953,33 +783,35 @@ CPL_recipe_exec(CPL_recipe *self, PyObject *args) {
 	close(fd[0]);
 	int retval;
 	struct tms clock_end;
-	cpl_msg_set_log_name(logfile);
-	cpl_msg_set_log_level(loglevel);
-	cpl_errorstate prestate = cpl_errorstate_get();
+	set_environment(runenv);
+	self->cpl->msg_set_log_name(logfile);
+	self->cpl->msg_set_log_level(loglevel);
+	self->cpl->msg_set_level(CPL_MSG_OFF);
+	cpl_errorstate prestate = self->cpl->errorstate_get();
 	if (chdir(dirname) == 0) {
 	    struct tms clock_start;
 	    times(&clock_start);
 	    setup_tracing(self);
-	    retval = cpl_plugin_get_exec(self->plugin)(self->plugin);
-	    int reto = cpl_dfs_update_product_header(recipe->frames);
+	    retval = self->cpl->plugin_get_exec(self->plugin)(self->plugin);
+	    int reto = self->cpl->dfs_update_product_header(recipe->frames);
 	    if (reto != CPL_ERROR_NONE) {
-		cpl_msg_error (__func__, "could not update the product header");
+		self->cpl->msg_error (__func__, "could not update the product header");
 	    }
 	    times(&clock_end);
 	    clock_end.tms_utime -= clock_start.tms_utime;
 	    clock_end.tms_stime -= clock_start.tms_stime;
 	    clock_end.tms_cutime -= clock_start.tms_cutime;
 	    clock_end.tms_cstime -= clock_start.tms_cstime;
-	    cpl_msg_stop_log();
+	    self->cpl->msg_stop_log();
 	} else {
 	    retval = CPL_ERROR_FILE_NOT_CREATED;
-	    cpl_error_set(__func__, retval);
+	    self->cpl->error_set_message_macro(__func__, retval, __FILE__, __LINE__, " ");
 	}
 	if ((memory_dump > 1) 
-	    || ((memory_dump > 0) && (!cpl_memory_is_empty()))) {
-	  cpl_memory_dump();
+	    || ((memory_dump > 0) && (!self->cpl->memory_is_empty()))) {
+	  self->cpl->memory_dump();
 	}
-	void *ptr = exec_serialize_retval(recipe->frames, prestate,
+	void *ptr = exec_serialize_retval(self, recipe->frames, prestate,
 					  retval, &clock_end);
 	long n_bytes = write(fd[1], ptr, ((long *)ptr)[0]);
 	close(fd[1]);
@@ -988,11 +820,11 @@ CPL_recipe_exec(CPL_recipe *self, PyObject *args) {
     
     close(fd[1]);
     long nbytes;
-    void *ptr = cpl_malloc(2 * sizeof(long));
+    void *ptr = malloc(2 * sizeof(long));
 Py_BEGIN_ALLOW_THREADS
     nbytes = read(fd[0], ptr, 2 * sizeof(long));
     if (nbytes == 2 * sizeof(long)) {
-	ptr = cpl_realloc(ptr, ((long *)ptr)[0]);
+	ptr = realloc(ptr, ((long *)ptr)[0]);
 	nbytes += read(fd[0], ptr + 2 * sizeof(long), 
 		       ((long *)ptr)[0] - 2 * sizeof(long));
     } else { // broken pipe while reading first two bytes
@@ -1006,7 +838,7 @@ Py_END_ALLOW_THREADS
 	return NULL;
     }
     PyObject *retval = exec_build_retval(ptr);
-    cpl_free(ptr);
+    free(ptr);
     return retval;
 }
 
@@ -1025,6 +857,10 @@ static PyMethodDef CPL_recipe_methods[] = {
      CPL_recipe_get_frameconfig_doc},
     {"run",  (PyCFunction)CPL_recipe_exec, METH_VARARGS,
      CPL_recipe_exec_doc},
+    {"cpl_is_supported", (PyCFunction)CPL_is_supported, METH_NOARGS,
+     CPL_is_supported_doc},
+    {"cpl_version", (PyCFunction)CPL_version, METH_NOARGS, CPL_version_doc},
+    {"cpl_description", (PyCFunction)CPL_description, METH_NOARGS, CPL_version_doc},
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };
 
@@ -1077,8 +913,6 @@ static PyTypeObject CPL_recipeType = {
 PyMODINIT_FUNC
 initCPL_recipe(void)
 {
-    cpl_init(CPL_INIT_DEFAULT);
-
     CPL_recipeType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&CPL_recipeType) < 0)
         return;
diff --git a/cpl/__init__.py b/cpl/__init__.py
index b31ad61..70bce66 100644
--- a/cpl/__init__.py
+++ b/cpl/__init__.py
@@ -7,15 +7,12 @@ from version import email as __email__
 from version import license_ as __license__
 from version import doc as __doc__
 
-from recipes import Recipe
-from parameters import Parameter
+from recipe import Recipe
+from param import Parameter
 from frames import FrameConfig
 from result import Result, CplError, RecipeCrash
-from log import msg, lib_version, lib_description
 import dfs
 import esorex
 
-msg.domain = os.path.basename(sys.argv[0])
-msg.level = msg.OFF
-
 Recipe.dir = '.'
+cpl_versions = [ '%i.%i.%i' % ver for ver in CPL_recipe.cpl_versions() ]
diff --git a/cpl/cpl_api.h b/cpl/cpl_api.h
new file mode 100644
index 0000000..db46f69
--- /dev/null
+++ b/cpl/cpl_api.h
@@ -0,0 +1,189 @@
+/*
+
+ This header file is compiled from the original CPL header files to contain
+ just the functions and macros that we need in the framework.
+ 
+ Since it is mainly copied and pasted, here is the original license
+ statement from /usr/include/cpl.h:
+ 
+ * $Id: cpl.h,v 1.31 2009/12/02 10:29:45 lbilbao Exp $
+ *
+ * This file is part of the ESO Common Pipeline Library
+ * Copyright (C) 2001-2008 European Southern Observatory
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+#ifndef CPL_API_H
+#define CPL_API_H
+
+#define CPL_VERSION(major, minor, micro) \
+        (((major) * 65536) + ((minor) * 256) + (micro))
+
+#define CPL_VERSION_MAJOR_CODE(code) (((code) >> 16) & 0xff)
+#define CPL_VERSION_MINOR_CODE(code) (((code) >> 8) & 0xff)
+#define CPL_VERSION_MICRO_CODE(code) ((code) & 0xff)
+
+#define CPL_VERSION_CODE CPL_VERSION(6,1,1)
+
+typedef int cpl_error_code, cpl_errorstate, cpl_boolean, cpl_frame_group,
+    cpl_parameter_mode, cpl_parameter_class, cpl_type, cpl_msg_severity;
+typedef size_t cpl_size;
+typedef void cpl_pluginlist, cpl_frameset, cpl_frame, 
+    cpl_parameter, cpl_parameterlist, cpl_recipeconfig;
+
+typedef struct _cpl_plugin_ cpl_plugin;
+typedef int (*cpl_plugin_func)(cpl_plugin *);
+struct _cpl_plugin_ {
+    unsigned int api;
+    unsigned long version;
+    unsigned long type;
+    const char *name;
+    const char *synopsis;
+    const char *description;
+    const char *author;
+    const char *email;
+    const char *copyright;
+    cpl_plugin_func initialize;
+    cpl_plugin_func execute;
+    cpl_plugin_func deinitialize;
+};
+
+struct _cpl_recipe_ {
+    cpl_plugin interface;
+    cpl_parameterlist *parameters;
+    cpl_frameset *frames;
+};
+typedef struct _cpl_recipe_ cpl_recipe;
+
+unsigned int cpl_version_get_major(void);
+unsigned int cpl_version_get_minor(void);
+unsigned int cpl_version_get_micro(void);
+
+void cpl_init(unsigned);
+const char * cpl_get_description(unsigned);
+int cpl_memory_is_empty(void);
+void cpl_memory_dump(void);
+void cpl_free(void *);
+const char *cpl_plugin_get_author(const cpl_plugin *self);
+const char *cpl_plugin_get_copyright(const cpl_plugin *self);
+cpl_plugin_func cpl_plugin_get_deinit(const cpl_plugin *self);
+const char *cpl_plugin_get_description(const cpl_plugin *self);
+const char *cpl_plugin_get_email(const cpl_plugin *self);
+cpl_plugin_func cpl_plugin_get_exec(const cpl_plugin *self);
+cpl_plugin_func cpl_plugin_get_init(const cpl_plugin *self);
+const char *cpl_plugin_get_name(const cpl_plugin *self);
+const char *cpl_plugin_get_synopsis(const cpl_plugin *self);
+unsigned long cpl_plugin_get_version(const cpl_plugin *self);
+char *cpl_plugin_get_version_string(const cpl_plugin *self);
+void cpl_pluginlist_delete(cpl_pluginlist *);
+cpl_plugin *cpl_pluginlist_find(cpl_pluginlist *, const char *);
+cpl_plugin *cpl_pluginlist_get_first(cpl_pluginlist *);
+cpl_plugin *cpl_pluginlist_get_next(cpl_pluginlist *);
+cpl_pluginlist *cpl_pluginlist_new(void);
+cpl_error_code cpl_dfs_update_product_header(cpl_frameset *);
+
+void cpl_msg_error(const char *, const char *, ...);
+cpl_error_code cpl_error_get_code(void);
+const char *cpl_error_get_file(void);
+const char *cpl_error_get_function(void);
+unsigned cpl_error_get_line(void);
+const char *cpl_error_get_message(void);
+void cpl_error_reset(void);
+cpl_error_code
+cpl_error_set_message_macro(const char *, cpl_error_code,
+                            const char *, unsigned,
+                            const char *, ...);
+void cpl_errorstate_dump(cpl_errorstate,
+                         cpl_boolean,
+                         void (*)(unsigned, unsigned, unsigned));
+cpl_errorstate cpl_errorstate_get(void);
+
+const char *cpl_frame_get_filename(const cpl_frame *self);
+cpl_frame_group cpl_frame_get_group(const cpl_frame *self);
+const char *cpl_frame_get_tag(const cpl_frame *self);
+cpl_frame *cpl_frame_new(void);
+cpl_error_code cpl_frame_set_filename(cpl_frame *self, const char *filename);
+cpl_error_code cpl_frame_set_tag(cpl_frame *self, const char *tag);
+
+void cpl_frameset_delete(cpl_frameset *self);
+cpl_frame *cpl_frameset_get_frame(cpl_frameset *self, cpl_size position);
+cpl_size cpl_frameset_get_size(const cpl_frameset *self);
+cpl_error_code cpl_frameset_insert(cpl_frameset *self, cpl_frame *frame);
+cpl_frameset *cpl_frameset_new(void);
+void cpl_msg_set_level(cpl_msg_severity);
+cpl_error_code cpl_msg_set_log_level(cpl_msg_severity);
+cpl_error_code cpl_msg_set_log_name(const char *);
+cpl_error_code cpl_msg_stop_log(void);
+
+const char *cpl_parameter_get_alias(const cpl_parameter *self,
+                                    cpl_parameter_mode mode);
+cpl_parameter_class cpl_parameter_get_class(const cpl_parameter *self);
+const char *cpl_parameter_get_context(const cpl_parameter *self);
+int cpl_parameter_get_default_bool(const cpl_parameter *self);
+int cpl_parameter_get_default_int(const cpl_parameter *self);
+double cpl_parameter_get_default_double(const cpl_parameter *self);
+const char *cpl_parameter_get_default_string(const cpl_parameter *self);
+int cpl_parameter_get_enum_size(const cpl_parameter *self);
+int cpl_parameter_get_enum_int(const cpl_parameter *self, int position);
+double cpl_parameter_get_enum_double(const cpl_parameter *self, int position);
+const char *cpl_parameter_get_enum_string(const cpl_parameter *self,
+                                          int position);
+const char *cpl_parameter_get_help(const cpl_parameter *self);
+const char *cpl_parameter_get_name(const cpl_parameter *self);
+int cpl_parameter_get_range_min_int(const cpl_parameter *self);
+double cpl_parameter_get_range_min_double(const cpl_parameter *self);
+int cpl_parameter_get_range_max_int(const cpl_parameter *self);
+double cpl_parameter_get_range_max_double(const cpl_parameter *self);
+cpl_type cpl_parameter_get_type(const cpl_parameter *self);
+cpl_error_code cpl_parameter_set_bool(cpl_parameter *self, int value);
+cpl_error_code cpl_parameter_set_int(cpl_parameter *self, int value);
+cpl_error_code cpl_parameter_set_double(cpl_parameter *self, double value);
+cpl_error_code cpl_parameter_set_string(cpl_parameter *self,
+                                        const char *value);
+cpl_parameter *cpl_parameterlist_find(cpl_parameterlist *self,
+                                      const char *name);
+cpl_parameter *cpl_parameterlist_get_first(cpl_parameterlist *self);
+cpl_parameter *cpl_parameterlist_get_next(cpl_parameterlist *self);
+cpl_size cpl_parameterlist_get_size(const cpl_parameterlist *self);
+
+char** cpl_recipeconfig_get_inputs(const cpl_recipeconfig* self,
+                                   const char* tag);
+cpl_size cpl_recipeconfig_get_min_count(const cpl_recipeconfig* self,
+                                        const char* tag, const char* input);
+cpl_size cpl_recipeconfig_get_max_count(const cpl_recipeconfig* self,
+                                        const char* tag, const char* input);
+char** cpl_recipeconfig_get_outputs(const cpl_recipeconfig* self,
+                                    const char* tag);
+char** cpl_recipeconfig_get_tags(const cpl_recipeconfig* self);
+const char *cpl_version_get_version(void);
+
+
+#define CPL_INIT_DEFAULT 0
+#define CPL_DESCRIPTION_DEFAULT 0
+#define CPL_MSG_OFF 4
+#define CPL_FALSE 0
+#define CPL_ERROR_NONE 0
+#define CPL_ERROR_FILE_NOT_CREATED 8
+#define CPL_FRAME_GROUP_PRODUCT 3
+#define CPL_PARAMETER_CLASS_ENUM (1 << 3)
+#define CPL_PARAMETER_CLASS_RANGE (1 << 2)
+#define CPL_PARAMETER_MODE_CLI (1 << 0)
+#define CPL_TYPE_BOOL (1 << 7)
+#define CPL_TYPE_DOUBLE (1 << 17)
+#define CPL_TYPE_INT (1 << 10)
+#define CPL_TYPE_STRING ((1 << 5)|(1 << 0))
+
+#endif /* CPL_API_H */
diff --git a/cpl/dfs.py b/cpl/dfs.py
index c3fba86..7b8b10c 100644
--- a/cpl/dfs.py
+++ b/cpl/dfs.py
@@ -34,29 +34,33 @@ class ProcessingInfo(object):
     .. attribute:: calib
 
        Calibration frames from a FITS file processed with CPL.
-       The result of this function may directly set as :attr:`Recipe.calib`
+       The result of this function may directly set as :attr:`cpl.Recipe.calib`
        attribute::
     
          import cpl
          myrecipe = cpl.Recipe('muse_bias')
          myrecipe.calib = cpl.dfs.ProcessingInfo('MASTER_BIAS_0.fits').calib
 
-       .. note:: This will not work properly for files that had
-          :attr:`pyfits.HDUlist` inputs since they have assigned a temporary
+       .. note::
+
+          This will not work properly for files that had
+          :class:`pyfits.HDUList` inputs since they have assigned a temporary
           file name only.
 
     .. attribute:: raw
 
        Raw (input) frames
 
-       .. note:: This will not work properly for files that had
-          :attr:`pyfits.HDUlist` inputs since they have assigned a temporary
+       .. note::
+
+          This will not work properly for files that had
+          :class:`pyfits.HDUList` inputs since they have assigned a temporary
           file name only.
 
     .. attribute:: param
 
        Processing parameters.
-       The result of this function may directly set as :attr:`Recipe.param`
+       The result of this function may directly set as :attr:`cpl.Recipe.param`
        attribute::
     
          import cpl
@@ -73,17 +77,19 @@ class ProcessingInfo(object):
        MD5 sums of the input and calibration files. :class:`dict` with the
        file name as key and the corresponding MD5 sum as value.
 
-       .. note:: Due to a design decision in CPL, the raw input files are not
+       .. note::
+
+          Due to a design decision in CPL, the raw input files are not
           accompanied with the MD5 sum.
     '''
 
     def __init__(self, source, datapaths = None):
         '''
         :param source: Object pointing to the result file header
-        :type source: :class:`str` or :class:`PyFits.HDUList` 
-                      or :class:`PyFits.PrimaryHDU` or :class:`PyFits.Header` 
+        :type source: :class:`str` or :class:`pyfits.HDUList`
+                      or :class:`pyfits.PrimaryHDU` or :class:`pyfits.Header`
         :param datapaths: Dictionary with frame tags as keys and directory paths
-                        as values to provide a full path for the raw and 
+                        as values to provide a full path for the raw and
                         calibration frames. Optional.
         :type datapaths: :class:`dict`
         '''
diff --git a/cpl/esorex.py b/cpl/esorex.py
index 924f460..3201c26 100644
--- a/cpl/esorex.py
+++ b/cpl/esorex.py
@@ -1,36 +1,36 @@
-'''This module contains some limited support for reading esorex SOF and
-configuration files.
-
-Esorex is a standard execution environment for CPL recipes provided by
-ESO. See http://www.eso.org/sci/software/cpl/esorex.html for
-details.
+'''`EsoRex <http://www.eso.org/sci/software/cpl/esorex.html>`_ is a standard
+execution environment for CPL recipes provided by `ESO <http://www.eso.org>`_.
 '''
 
 import os
 import cpl
+import logging
 
 def load_sof(source):
-    '''Read an esorex sof file. 
+    '''Read an :program:`EsoRex` SOF file.
 
-    :param source: SOF file object or string with sof file content.
+    :param source: SOF ("Set Of Files") file object or string with SOF
+        file content.
     :type source: :class:`str` or :class:`file`
 
     These files contain the raw and calibration files for a recipe.  The
     content of the file is returned as a map with the tag as key and the list
     of file names as value.
 
-    The result of this function may directly set as :attr:`Recipe.calib`
+    The result of this function may directly set as :attr:`cpl.Recipe.calib`
     attribute::
     
       import cpl
       myrecipe = cpl.Recipe('muse_bias')
       myrecipe.calib = cpl.esorex.read_sof(file('muse_bias.sof'))
+    
+    .. note::
 
-    .. note:: The raw data frame is silently ignored wenn setting
-      :attr:`Recipe.calib` for MUSE recipes. Other recipes ignore ths raw data
-      frame only if it was set manually as :attr:`Recipe.tag` or in
-      :attr:`Recipe.tags` since there is no way to automatically distinguish
-      between them.
+      The raw data frame is silently ignored wenn setting
+      :attr:`cpl.Recipe.calib` for MUSE recipes. Other recipes ignore the raw
+      data frame only if it was set manually as :attr:`cpl.Recipe.tag` or in
+      :attr:`cpl.Recipe.tags` since there is no way to automatically
+      distinguish between them.
 
     '''
     if isinstance(source, str):
@@ -55,27 +55,24 @@ def load_sof(source):
                          source.__class__.__name__)
 
 def load_rc(source = None):
-    '''Read an esorex configuration file.
+    '''Read an :program:`EsoRex` configuration file.
 
     :param source: Configuration file object, or string with file content. 
-                   If not set, the esorex config file
+                   If not set, the :program:`EsoRex` config file
                    :file:`~/.esorex/esorex.rc` is used.
     :type source: :class:`str` or :class:`file`
 
-    These files contain configuration parameters for esorex or recipes. The
-    content of the file is returned as a map with the (full) parameter name as
-    key and its setting as string value.
+    These files contain configuration parameters for :program:`EsoRex` or
+    recipes. The content of the file is returned as a map with the (full)
+    parameter name as key and its setting as string value.
 
-    The result of this function may directly set as :attr:`Recipe.param`
+    The result of this function may directly set as :attr:`cpl.Recipe.param`
     attribute::
     
       import cpl
       myrecipe = cpl.Recipe('muse_bias')
       myrecipe.param = cpl.esorex.load_rc('muse_bias.rc')
 
-    .. note:: Unknown parameters are silently ignored wenn setting
-              :attr:`Recipe.param`.
-
     '''
     if source is None:
         source = file(os.path.expanduser('~/.esorex/esorex.rc'))
@@ -96,7 +93,7 @@ def load_rc(source = None):
                          source.__class__.__name__)
 
 def init(source = None):
-    '''Set the message verbosity and recipe search path from the
+    '''Set up the logging and the recipe search path from the
     :file:`esorex.rc` file.
 
     :param source: Configuration file object, or string with file content. 
@@ -108,5 +105,176 @@ def init(source = None):
     if rc.has_key('esorex.caller.recipe-dir'):
         cpl.Recipe.path = rc['esorex.caller.recipe-dir'].split(':')
     if rc.has_key('esorex.caller.msg-level'):
-        cpl.msg.level = cpl.log.level[rc['esorex.caller.msg-level'].upper()]
+        msg.level = rc['esorex.caller.msg-level']
+    if rc.has_key('esorex.caller.log-level'):
+        log.level = rc['esorex.caller.log-level']
+    if rc.has_key('esorex.caller.log-dir'):
+        log.dir = rc['esorex.caller.log-dir']
+    if rc.has_key('esorex.caller.log-file'):
+        log.filename = rc['esorex.caller.log-file']
+
+class CplLogger(object):
+    DEBUG = logging.DEBUG
+    INFO = logging.INFO
+    WARN = logging.WARN
+    ERROR = logging.ERROR
+    OFF = logging.CRITICAL + 1
+
+    def __init__(self, msg = True):
+        self.handler = None
+        self._component = False
+        self._time = False
+        self._threadid = False
+        self.format = None
+        self._filename = None
+        self.dir = None
+        self._msg = msg
+        self._level = CplLogger.OFF
+
+    def _init_handler(self):
+        if not self.handler:
+            if self._msg:
+                self.handler = logging.StreamHandler()
+            elif self._filename:
+                if self.dir:
+                    fname = os.path.join(self.dir, self._filename)
+                    self.handler = logging.FileHandler(fname)
+                else:
+                    self.handler = logging.FileHandler(self._filename)
+            else:
+                self.handler = None
+            if self.handler:
+                logging.getLogger().addHandler(self.handler)
+                self.handler.setFormatter(logging.Formatter(self.format,
+                                                            '%H:%M:%S'))
+
+    def _shutdown_handler(self):
+        if self.handler:
+            logging.getLogger().removeHandler(self.handler)
+            self.handler = None
+
+    @property
+    def level(self):
+        '''Log level for output to the terminal. Any of
+        [ DEBUG, INFO, WARN, ERROR, OFF ].
+        '''
+        return self._level
+
+    @level.setter
+    def level(self, level):
+        if isinstance(level, (str, unicode)):
+            level = cpl.logger.level[level.upper()]
+        if level == CplLogger.OFF:
+            self._shutdown_handler()
+        else:
+            self._init_handler()
+            logging.getLogger().setLevel(logging.DEBUG)
+            if self.handler:
+                self.handler.setLevel(level)
+        self._level = level
+
+    @property
+    def format(self):
+        '''Output format. 
+
+        .. seealso :: `logging.LogRecord attributes <http://docs.python.org/library/logging.html#logrecord-attributes>`_ 
+
+           Key mappings in the logging output.'''
+        return self._format
+
+    @format.setter
+    def format(self, fmt):
+        if fmt == None:
+            fmt = '%(asctime)s ' if self._time else ''
+            fmt += '[%(levelname)7s]'
+            fmt += '[%(threadName)s] ' if self._threadid else ' '
+            fmt += '%(name)s: ' if self._component else ''
+            fmt += '%(message)s'
+        if self.handler:
+            self.handler.setFormatter(logging.Formatter(fmt, '%H:%M:%S'))
+        self._format = fmt
+
+    @property
+    def component(self):
+        '''If :obj:`True`, attach the component name to output messages.
+        '''
+        return self._component
 
+    @component.setter
+    def component(self, enable):
+        self._component = enable
+        self.format = None
+
+    @property
+    def time(self):
+        '''If :obj:`True`, attach a time tag to output messages.
+        '''
+        return self._time
+
+    @time.setter
+    def time(self, enable):
+        self._time = enable
+        self.format = None
+
+    @property
+    def threadid(self):
+        '''If :obj:`True`, attach a thread tag to output messages.
+        '''
+        return self._threadid
+
+    @threadid.setter
+    def threadid(self, enable):
+        self._threadid = enable
+        self.format = None
+
+    @property
+    def filename(self):
+        '''Log file name.
+        '''
+        return self._filename
+
+    @filename.setter
+    def filename(self, name):
+        if self._msg:
+            raise AttributeError('Cannot set file name of message output')
+        if self._filename != name:
+            self._shutdown_handler()
+            self._filename = name
+            self._init_handler()
+
+    def __repr__(self):
+        return 'cpl.esorex.CplLogger(msg=%s)' % `self._msg`
+
+msg = CplLogger(msg = True)
+'''This variable is a :class:`CplLogger` instance that provides a convienience
+stream handler similar to the terminal logging functionality of the CPL. It
+basically does the same as::
+
+  import logging
+
+  log = logging.getLogger()
+  log.setLevel(logging.INFO)
+  ch = logging.StreamHandler()
+  ch.setLevel(logging.INFO)
+  ch.setFormatter(logging.Formatter('[%(levelname)7s] %(message)s'))
+  log.addHandler(ch)
+'''
+
+log = CplLogger(msg = False)
+'''This variable is a :class:`CplLogger` instance that provides a convienience
+file handler similar to the file logging functionality of the CPL. It
+basically does the same as::
+
+  import logging
+
+  log = logging.getLogger()
+  log.setLevel(logging.INFO)
+  ch = logging.FileHandler(filename)
+  ch.setLevel(logging.INFO)
+  ch.setFormatter(logging.Formatter('%(asctime)s [%(levelname)7s] %(funcName)s: %(message)s'))
+  log.addHandler(ch)
+'''
+log.threadid = True
+log.component = True
+log.time = True
+log.level = log.INFO
diff --git a/cpl/frames.py b/cpl/frames.py
index 2ab4240..ad4b8be 100644
--- a/cpl/frames.py
+++ b/cpl/frames.py
@@ -25,16 +25,16 @@ class FrameConfig(object):
 
     .. attribute:: min
 
-       Minimal number of frames, or :attr:`None` if not specified. A frame is
+       Minimal number of frames, or :obj:`None` if not specified. A frame is
        required if the :attr:`min` is set to a value greater than 0.
 
     .. attribute:: max 
 
-       Maximal number of frames, or :attr:`None` if not specified
+       Maximal number of frames, or :obj:`None` if not specified
 
     .. attribute:: frames
 
-       List of frames (file names or pyfits.HDUList objects) that are 
+       List of frames (file names or :class:`pyfits.HDUList` objects) that are 
        assigned to this frame type.
     '''
     def __init__(self, tag, min_frames = 0, max_frames = 0, frames = None):
@@ -86,7 +86,7 @@ class FrameList(object):
         self._recipe = recipe
         self._values = dict()
         if isinstance(other, self.__class__):
-            self._set_items((o.name, o.value) for o in other)
+            self._set_items((o.tag, o.frames) for o in other)
         elif isinstance(other, dict):
             self._set_items(other.iteritems())
         elif other:
@@ -94,11 +94,7 @@ class FrameList(object):
 
     def _set_items(self, l):
         for o in l:
-            if o[1] is not None:
-                try:
-                    self[o[0]] = o[1]
-                except:
-                    pass
+            self[o[0]] = o[1]
 
     @property
     def _cpl_dict(self):
@@ -174,21 +170,11 @@ class FrameList(object):
             r += '%s: %s\n' % (self._key(s), s.__doc__)
         return r        
 
-    def _aslist(self, **ndata):
-        frames = dict()
-        for f in self:
-            frames[f.tag] = f.frames
-        if ndata:
-            for name, tdata in ndata.items():
-                if name.startswith('calib_'):
-                    tag = name.split('_', 1)[1]
-                    frames[tag] = tdata
-            try:
-                for name, tdata in ndata['calib'].items():
-                    frames[name] = tdata
-            except KeyError:
-                pass
-        return list(frames.iteritems())
+    def _aslist(self, frames):
+        flist = FrameList(self._recipe, self)
+        if frames is not None:
+            flist._set_items(frames.items())
+        return [(f.tag, f.frames) for f in flist]
 
 def mkabspath(frames, tmpdir):
     '''Convert all filenames in the frames list into absolute paths.
diff --git a/cpl/log.py b/cpl/log.py
deleted file mode 100644
index 2a2ada4..0000000
--- a/cpl/log.py
+++ /dev/null
@@ -1,249 +0,0 @@
-import datetime
-import logging
-import os
-import sys
-import tempfile
-import threading
-
-import CPL_recipe
-
-class NullHandler(logging.Handler):
-    def emit(self, record):
-        pass
-
-logging.getLogger('cpl').addHandler(NullHandler())
-
-level = { "DEBUG":logging.DEBUG, "INFO":logging.INFO, "WARNING":logging.WARN, 
-          "ERROR":logging.ERROR }
-
-class LogServer(threading.Thread):
-
-    def __init__(self, name, level):
-        threading.Thread.__init__(self)
-        self.name = name
-        self.logger = logging.getLogger(name)
-        self.level = CplLogger.verbosity.index(level)
-        self.entries = LogList()
-        tmphdl, self.logfile = tempfile.mkstemp(prefix = 'cpl', suffix='.log')
-        os.close(tmphdl)
-        os.remove(self.logfile)
-        os.mkfifo(self.logfile)
-        self.start()
-
-    def run(self):
-        try:
-            logfile = open(self.logfile, buffering = 0)
-        except:
-            pass
-        try:
-            line = logfile.readline()
-            os.remove(self.logfile)
-            while line:
-                self.log(line)
-                line = logfile.readline()
-        except:
-            pass
-
-    def log(self, s):
-        try:
-            creation_date = datetime.datetime.combine(
-                datetime.date.today(), 
-                datetime.time(int(s[0:2]),int(s[3:5]),int(s[6:8])))
-            lvl = level.get(s[10:17].strip(), logging.NOTSET)
-            func = s[19:].split(':', 1)[0]
-            msg = s[19:].split(':', 1)[1][1:-1]
-            if msg.startswith('[tid='):
-                threadid = int(msg[5:8])
-                msg = msg[10:] if threadid > 0 else msg[12:]
-            else:
-                threadid = None
-            log = logging.getLogger('%s.%s' % (self.logger.name, func))
-            record = logging.LogRecord(log.name, lvl, None, None, msg, 
-                                       None, None, func)
-            created = float(creation_date.strftime('%s'))
-            if record.created < created:
-                created -= 86400
-            record.relativeCreated -= record.msecs
-            record.relativeCreated += 1000*(created - record.created + 1) 
-            record.created = created
-            record.msecs = 0.0
-            record.threadid = threadid
-            record.threadName = ('Cpl-%03i' % threadid) if threadid else 'CplThread'
-            self.entries.append(record)
-            if log.isEnabledFor(lvl) and log.filter(record):
-                log.handle(record)
-        except:
-            pass
-
-class LogList(list):
-    '''List of log messages.
-
-    Accessing this :class:`list` directly will return the
-    :class:`logging.LogRecord` instances. 
-
-    Example::
-
-      res = muse_bias(bias_frames)
-      for logrecord in res.log:
-          print '%s: %s' % (entry.funcname, entry.msg)
-
-    To get them formatted as string, use the :attr:`error`, :attr:`warning`,
-    :attr:`info` or :attr:`debug` attributes::
-
-      res = muse_bias(bias_frames)
-      for line in res.log.info:
-          print line
-
-    '''
-    def filter(self, level):
-        return [ '%s: %s' % (entry.funcName, entry.msg) for entry in self 
-                 if entry.levelno >= level ]
-
-    @property
-    def error(self):
-        '''Error messages as list of :class:`str`
-        '''
-        return self.filter(logging.ERROR)
-
-    @property
-    def warning(self):
-        '''Warnings and error messages as list of :class:`str`
-        '''
-        return self.filter(logging.WARN)
-
-    @property
-    def info(self):
-        '''Info, warning and error messages as list of :class:`str`
-        '''
-        return self.filter(logging.INFO)
-
-    @property
-    def debug(self):
-        '''Debug, info, warning, and error messages as list of :class:`str`
-        '''
-        return self.filter(logging.DEBUG)
-
-class CplLogger(object):
-    DEBUG = logging.DEBUG
-    INFO = logging.INFO
-    WARN = logging.WARN
-    ERROR = logging.ERROR
-    OFF = 101
-
-    verbosity = [ DEBUG, INFO, WARN, ERROR, OFF ]
-
-    _time_enabled = False
-
-    def __init__(self, name = 'cpl'):
-        self.name = name
-
-    @property
-    def level(self):
-        '''Log level for output to the terminal. Any of
-        [ DEBUG, INFO, WARN, ERROR, OFF ]
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Logger.setLevel` 
-        '''
-        return CplLogger.verbosity[CPL_recipe.get_msg_level()]
-
-    @level.setter
-    def level(self, level):
-        CPL_recipe.set_msg_level(CplLogger.verbosity.index(level))
-
-    @property
-    def time(self):
-        '''Specify whether time tag shall be included in the terminal output
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Handler.setFormatter` 
-        '''
-        return CplLogger._time_enabled
-
-    @time.setter
-    def time(self, enable):
-        CPL_recipe.set_msg_time(enable);
-        CplLogger._time_enabled = not not enable
-
-    @property
-    def domain(self):
-        '''The domain tag in the header of the log file.
-
-        .. deprecated:: 0.3
-           Use :func:`logging.getLogger` 
-        '''
-        return CPL_recipe.get_log_domain()
-
-    @domain.setter
-    def domain(self, domain):
-        CPL_recipe.set_log_domain(domain)
-
-    def log(self, level, msg, caller = None):
-        if caller == None:
-            caller = CPL_recipe.get_log_domain()
-        logging.getLogger('%s.%s' % (self.name, caller)).log(level, msg)
-        CPL_recipe.log(CplLogger.verbosity.index(level), caller, msg)
-
-    def debug(self, msg, caller = None):
-        '''Put a 'debug' message to the log.
-
-        :param msg: Message to put
-        :type msg: :class:`str`
-        :param caller: Name of the function generating the message.
-        :type caller: :class:`str`
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Logger.debug` 
-        '''
-        self.log(CplLogger.DEBUG, msg, caller)
-
-    def info(self, msg, caller = None):
-        '''Put an 'info' message to the log.
-
-        :param msg: Message to put
-        :type msg: :class:`str`
-        :param caller: Name of the function generating the message.
-        :type caller: :class:`str`
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Logger.info` 
-        '''
-        self.log(CplLogger.INFO, msg, caller)
-
-    def warn(self, msg, caller = None):
-        '''Put a 'warn' message to the log.
-
-        :param msg: Message to put
-        :type msg: :class:`str`
-        :param caller: Name of the function generating the message.
-        :type caller: :class:`str`
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Logger.warn` 
-        '''
-        self.log(CplLogger.WARN, msg, caller)
-
-    def error(self, msg, caller = None):
-        '''Put an 'error' message to the log.
-
-        :param msg: Message to put
-        :type msg: :class:`str`
-        :param caller: Name of the function generating the message.
-        :type caller: :class:`str`
-
-        .. deprecated:: 0.3
-           Use :func:`logging.Logger.error` 
-        '''
-        self.log(CplLogger.ERROR, msg, caller)
-
-    def indent_more(self):
-        '''Indent the output more.'''
-        CPL_recipe.log_indent_more()
-
-    def indent_less(self):
-        '''Indent the output less.'''
-        CPL_recipe.log_indent_less()
-
-msg = CplLogger()
-lib_version = CPL_recipe.version()
-lib_description = CPL_recipe.description()
diff --git a/cpl/logger.py b/cpl/logger.py
new file mode 100644
index 0000000..3a58319
--- /dev/null
+++ b/cpl/logger.py
@@ -0,0 +1,155 @@
+import datetime
+import logging
+import os
+import re
+import sys
+import tempfile
+import threading
+
+import CPL_recipe
+
+class NullHandler(logging.Handler):
+    def emit(self, record):
+        pass
+
+logging.getLogger('cpl').addHandler(NullHandler())
+
+level = { "DEBUG":logging.DEBUG, "INFO":logging.INFO, "WARNING":logging.WARN, 
+          "ERROR":logging.ERROR, "OFF":(logging.CRITICAL + 1)}
+
+cpl_verbosity = [ logging.DEBUG, logging.INFO, logging.WARN,
+                  logging.ERROR, logging.CRITICAL + 1 ]
+
+class LogServer(threading.Thread):
+
+    def __init__(self, name, level = None):
+        threading.Thread.__init__(self)
+        self.name = name
+        self.logger = logging.getLogger(name)
+        self.level = cpl_verbosity.index(level) if level is not None else 0
+        self.entries = LogList()
+        self.regexp = re.compile('(\\d\\d):(\\d\\d):(\\d\\d)' +
+                                 '\\s\\[\\s*(\\w+)\\s*\\]' + 
+                                 '\\s(\\w+):' +
+                                 '(\\s\\[tid=(\\d+)\\])?' +
+                                 '\\s(.+)')
+        tmphdl, self.logfile = tempfile.mkstemp(prefix = 'cpl', suffix='.log')
+        os.close(tmphdl)
+        os.remove(self.logfile)
+        os.mkfifo(self.logfile)
+        self.start()
+
+    def run(self):
+        try:
+            logfile = open(self.logfile, buffering = 0)
+        except:
+            pass
+        try:
+            line = logfile.readline()
+            os.remove(self.logfile)
+            while line:
+                self.log(line)
+                line = logfile.readline()
+        except:
+            pass
+
+    def log(self, s):
+        '''Convert CPL log messages into python log records.
+        
+        A typical CPL log message looks like
+
+         10:35:25 [WARNING] rtest: [tid=000] No file tagged with FLAT
+
+        '''
+        try:
+            m = self.regexp.match(s)
+            if m is not None:
+                g = m.groups()
+                creation_date = datetime.datetime.combine(
+                    datetime.date.today(), 
+                    datetime.time(int(g[0]),int(g[1]),int(g[2])))
+                lvl = level.get(g[3], logging.NOTSET)
+                func = g[4]
+                log = logging.getLogger('%s.%s' % (self.logger.name, func))
+                threadid = int(g[6]) if g[6] else None
+                msg = g[-1]
+                record = logging.LogRecord(log.name, lvl, None, None, 
+                                           msg, None, None, func)
+                created = float(creation_date.strftime('%s'))
+                if record.created < created:
+                    created -= 86400
+                record.relativeCreated -= record.msecs
+                record.relativeCreated += 1000*(created - record.created + 1) 
+                record.created = created
+                record.msecs = 0.0
+                record.threadid = threadid
+                record.threadName = ('Cpl-%03i' % threadid) if threadid \
+                    else 'CplThread'
+            elif self.entries:
+                r0 = self.entries[-1]
+                msg = s.rstrip()
+                lvl = r0.levelno
+                log = logging.getLogger(r0.name)
+                record = logging.LogRecord(r0.name, lvl, None, None, 
+                                           msg, None, None, r0.funcName)
+                record.relativeCreated = r0.relativeCreated
+                record.created = r0.created
+                record.msecs = r0.msecs
+                record.threadid = r0.threadid
+                record.threadName = r0.threadName
+            else:
+                return
+            self.entries.append(record)
+            if log.isEnabledFor(lvl) and log.filter(record):
+                log.handle(record)
+        except:
+            pass
+
+class LogList(list):
+    '''List of log messages.
+
+    Accessing this :class:`list` directly will return the
+    :class:`logging.LogRecord` instances. 
+
+    Example::
+
+      res = muse_bias(bias_frames)
+      for logrecord in res.log:
+          print '%s: %s' % (entry.funcname, entry.msg)
+
+    To get them formatted as string, use the :attr:`error`, :attr:`warning`,
+    :attr:`info` or :attr:`debug` attributes::
+
+      res = muse_bias(bias_frames)
+      for line in res.log.info:
+          print line
+
+    '''
+    def filter(self, level):
+        return [ '%s: %s' % (entry.funcName, entry.msg) for entry in self 
+                 if entry.levelno >= level ]
+
+    @property
+    def error(self):
+        '''Error messages as list of :class:`str`
+        '''
+        return self.filter(logging.ERROR)
+
+    @property
+    def warning(self):
+        '''Warnings and error messages as list of :class:`str`
+        '''
+        return self.filter(logging.WARN)
+
+    @property
+    def info(self):
+        '''Info, warning and error messages as list of :class:`str`
+        '''
+        return self.filter(logging.INFO)
+
+    @property
+    def debug(self):
+        '''Debug, info, warning, and error messages as list of :class:`str`
+        '''
+        return self.filter(logging.DEBUG)
+
diff --git a/cpl/parameters.py b/cpl/param.py
similarity index 64%
rename from cpl/parameters.py
rename to cpl/param.py
index 0bd4e7a..92c54ca 100644
--- a/cpl/parameters.py
+++ b/cpl/param.py
@@ -14,7 +14,7 @@ class Parameter(object):
 
     .. attribute:: Parameter.value
 
-       The value of the parameter, or :attr:`None` if set to default
+       The value of the parameter, or :obj:`None` if set to default
 
     .. attribute:: Parameter.default
 
@@ -38,7 +38,7 @@ class Parameter(object):
 
     .. attribute:: Parameter.range 
 
-      The numeric range of a parameter, or :attr:`None` if the parameter range
+      The numeric range of a parameter, or :obj:`None` if the parameter range
       is unlimited (readonly).
 
     .. attribute:: Parameter.sequence
@@ -115,73 +115,91 @@ class Parameter(object):
 
 
 class ParameterList(object):
-    def __init__(self, recipe, other = None):
+    def __init__(self, recipe, other = None, prefix = None):
         self._recipe = recipe
-        self._values = dict()
-        if isinstance(other, self.__class__):
-            self._set_items((o.name, o.value) for o in other)
-        elif isinstance(other, dict):
-            self._set_items(other.iteritems())
-        elif other:
+        self._dict = dict()
+        self._pars = list()
+        self._prefix = prefix
+        childs = set()
+        for name, context, fullname, desc, prange, sequence, deflt, ptype \
+                in recipe._recipe.params():
+            if prefix:
+                if name.startswith(prefix + '.'):
+                    aname = name[len(prefix)+1:]
+                else:
+                    continue
+            else:
+                aname = name
+            if '.' in aname:
+                aname = aname.split('.', 1)[0]
+                if prefix:
+                    aname = prefix + '.' + aname
+                childs.add(aname)
+            else:
+                par = Parameter(name)
+                par._set_attributes(context, fullname, deflt,
+                                    desc, prange, sequence, ptype)
+                self._dict[name] = par
+                self._dict[fullname] = par
+                self._dict[self._paramname(aname)] = par
+                self._pars.append(par)
+        for name in childs:
+            clist = ParameterList(recipe, prefix = name)
+            self._dict[name] = clist
+            for par in clist._pars:
+                self._dict[par.name] = par
+                self._dict[par.fullname] = par
+                self._pars.append(par)
+            aname = self._paramname(name)
+            self._dict[aname] = clist
+        if other:
             self._set_items(other)
 
-    def _set_items(self, l):
+    def _set_items(self, other):
+        if isinstance(other, self.__class__):
+            l = ((o.name, o.value) for o in other)
+        elif isinstance(other, dict):
+            l = other.iteritems()
+        else:
+            l = other
         for o in l:
-            if o[1] is not None:
-                try:
-                    self[o[0]] = o[1]
-                except:
-                    pass
+            self[o[0]] = o[1]
 
-    @property
-    def _cpl_dict(self):
-        if self._recipe is None or self._recipe._recipe is None:
-            return None
-        cpl_params = self._recipe._recipe.params()
-        if cpl_params is None:
-            return None
-        s = dict()
-        for pd in cpl_params:
-            (name, context, fullname, desc, _range, sequence, deflt, type) = pd
-            pname = name.replace('.', '_')
-            if pname in s:
-                continue
+    def _del_items(self):
+        for p in self:
+            del p.value
+
+    @staticmethod
+    def _paramname(s):
+        for c in [ '-', ' ' ]:
+            if isinstance(c, tuple):
+                s = s.replace(c[0], c[1])
             else:
-                s[pname] = self._values.setdefault(name, Parameter(name))
-                s[pname]._set_attributes(context, fullname, deflt, 
-                                         desc, _range, sequence, type)
+                s = s.replace(c, '_')
         return s
 
-    @property
-    def _dict(self):
-        return self._cpl_dict or self._values
-
-    @property
-    def _dict_full(self):
-        d = self._dict
-        return dict(d.items() +
-                    [ (p.fullname, p) for p in d.values() if p.fullname ] +
-                    [ (p.name, p) for p in d.values() if p.name not in d])
-
     def __iter__(self):
-        return self._dict.itervalues()
+        return self._pars.__iter__()
 
     def __getitem__(self, key):
-        return self._dict_full[key]
+        return self._dict[key]
 
     def __setitem__(self, key, value):
-        d = self._cpl_dict
-        if d is not None:
-            d = dict(d.items() +
-                     [ (p.fullname, p) for p in d.values() if p.fullname] +
-                     [ (p.name, p) for p in d.values() if p.name not in d])
-            d[key].value = value
+        p = self[key]
+        if isinstance(p, self.__class__):
+            if value is not None:
+                p._set_items(value)
+            else:
+                p._del_items()
         else:
-            self._values.setdefault(key.replace('.', '_'), 
-                                    Parameter(key)).value = value
+            p.value = value
 
     def __delitem__(self, key):
-        del self._dict_full[key].value
+        p = self[key]
+        if isinstance(p, self.__class__):
+            p._del_items()
+        else:
+            del p.value
 
     def __str__(self):
         r = ''
@@ -191,10 +209,10 @@ class ParameterList(object):
         return r
     
     def __contains__(self, key):
-        return key in self._dict_full
+        return key in self._dict
 
     def __len__(self):
-        return len(self._dict)
+        return len(self._pars)
         
     def __getattr__(self, key):
         return self[key]
@@ -209,7 +227,8 @@ class ParameterList(object):
         del self[key]
 
     def __dir__(self):
-        return self._dict.keys()
+        return list(set(self._paramname(d) 
+                        for d in self._dict.keys() if '.' not in d))
 
     def __repr__(self):
         return `list(self)`
@@ -227,25 +246,14 @@ class ParameterList(object):
                 p.name.rjust(maxlen), p.__doc__, `p.default`)
         return r        
 
-    def _aslist(self, **ndata):
-        parlist = dict([ ( param.fullname, param.value ) 
-                         for param in self
-                         if param.value is not None 
-                         and (ndata is None or param.name not in ndata) ])
-        if ndata:
-            for name, tdata in ndata.items():
-                if name.startswith('param_'):
-                    try:
-                        parlist[self[name.split('_', 1)[1]].fullname] = tdata
-                    except KeyError:
-                        pass
-            try:
-                for pname, tdata in ndata['param'].items():
-                    try:
-                        parlist[self[pname].fullname] = tdata
-                    except KeyError:
-                        pass
-            except KeyError:
-                pass
-        return list(parlist.iteritems())
-
+    def _aslist(self, par):
+        parlist = ParameterList(self._recipe, self)
+        if par is not None:
+            parlist._set_items(par.items())
+        l = list()
+        for param in parlist:
+            if isinstance(param, Parameter):
+                l.append((param.fullname, param.value))
+            else:
+                l += param._aslist(par)
+        return l
diff --git a/cpl/recipes.py b/cpl/recipe.py
similarity index 77%
rename from cpl/recipes.py
rename to cpl/recipe.py
index 57c5d53..a3dd8ae 100644
--- a/cpl/recipes.py
+++ b/cpl/recipe.py
@@ -9,38 +9,34 @@ import CPL_recipe
 import esorex
 from frames import FrameList, mkabspath, expandframelist
 from result import Result, RecipeCrash
-from parameters import ParameterList
-from log import LogServer, msg
+from param import ParameterList
+from logger import LogServer
 
 class Recipe(object):
     '''Pluggable Data Reduction Module (PDRM) from a ESO pipeline. 
 
     Recipes are loaded from shared libraries that are provided with the
-    pipeline library of the instrument.
+    pipeline library of the instrument. The module does not need to be
+    linked to the same library version as the one used for the compilation
+    of python-cpl. Currently, recipes compiled with CPL versions from 4.0
+    are supported. The list of supported versions is stored as
+    :attr:`cpl.cpl_versions`.
 
     The libraries are searched in the directories specified by the class
     attribute :attr:`Recipe.path` or its subdirectories. The search path is
     automatically set to the esorex path when :func:`cpl.esorex.init()`
     is called.
-
-    Attributes:
-
-    __name__: Recipe name
-    __filename__: Shared library file name
-    param: Parameter list
-    calib: Calibration frame list
-    tag: default tag
-    tags: list of possible tags
-    __author__: author name
-    __email__: author's email address
-    description: (synopsis, description) pair
-    version: (versionnumber, versionstring) pair
-    memory_dump: If set to 1, a memory dump is issued to stdout if the memory was not
-      totally freed after the execution. If set to 2, the dump is always issued.
-      Standard is 0: nothing dumped.
     '''
 
     path = [ '.' ]
+    '''Search path for the recipes. It may be set to either a string, or to a
+    list of strings. All shared libraries in the search path and their
+    subdirectories are searched for CPL recipes. On default, the path is
+    set to the current directory.
+
+    The search path is automatically set to the esorex path when
+    :func:`cpl.esorex.init()` is called.
+    '''
 
     def __init__(self, name, filename = None, version = None, threaded = False):
         '''Try to load a recipe with the specified name in the directory
@@ -66,31 +62,51 @@ class Recipe(object):
         '''
         self._recipe = None
         self.__name__ = name
+        '''Recipe name.'''
+
         if not filename:
             filename = Recipe.get_recipefilename(name, version)
             if not filename:
                 raise IOError('Recipe %s not found at path %s' 
                               % (`name`, `Recipe.path`))
         self.__file__ = filename
+        '''Shared library file name.'''
+
         self._recipe = CPL_recipe.recipe(filename, name)
         if version and version not in self.version:
             raise IOError('wrong version %s (requested %s) for %s in %s' %
                           (str(self.version), str(version), name, filename))
+        if not self._recipe.cpl_is_supported():
+            warn("Unsupported CPL version %s linked to %s" %
+                 self._recipe.version()[1], filename)
         self._param = ParameterList(self)
         self._calib = FrameList(self)
+
+        self.env = dict()
+        '''Bla'''
+
         self._tags = None
+
         self.tag = self.tags[0] if self.tags else None
+        '''Default tag when the recipe is called. This is set automatically
+        only if the recipe provided the information about input
+        tags. Otherwise this tag has to be set manually.
+        '''
+
         self.output_dir = None
+
         self.temp_dir = '.'
+        '''Base directory for temporary directories where the recipe is
+        executed. The working dir is created as a subdir with a random file
+        name. If set to :obj:`None`, the system temp dir is used.  Defaults to
+        :literal:`'.'`.
+        '''
+
         self.memory_dump = 0
-        self.threaded = threaded
 
-    def reload(self):
-        '''Reload the recipe. 
+        self.threaded = threaded
 
-        All recipe settings remain unchanged.
-        '''
-        self._recipe = CPL_recipe.recipe(self.__file__, self.__name__)
+        self.__doc__ = self._doc()
 
     @property
     def __author__(self):
@@ -119,12 +135,24 @@ class Recipe(object):
 
     @property
     def __copyright__(self):
-        '''Copyright string'''
+        '''Copyright string of the recipe'''
         return self._recipe.copyright()
 
     @property
+    def cpl_version(self):
+        '''Version of the CPL library that is linked to the recipe,
+        as a string'''
+        return self._recipe.cpl_version()
+
+    @property
+    def cpl_description(self):
+        '''Version numbers of CPL and its libraries that were linked to
+        the recipe, as a string.'''
+        return self._recipe.cpl_description()
+
+    @property
     def tags(self):
-        '''Possible tags for the raw input frames, or ':attr:`None` if this
+        '''Possible tags for the raw input frames, or ':obj:`None` if this
         information is not provided by the recipe.'''
         frameconfig = self._recipe.frameConfig()
         return [ c[0][0] for c in frameconfig ] if frameconfig else self._tags
@@ -167,11 +195,13 @@ class Recipe(object):
         BADPIX_TABLE None None ['badpix_1.fits', 'badpix_2.fits']
         MASTER_FLAT None 1 None
 
-        .. note:: Only MUSE recipes are able to provide the full list of
+        .. note::
+
+           Only MUSE recipes are able to provide the full list of
            calibration frames and the minimal/maximal number of calibration
            frames. For other recipes, only frames that were set by the users are
            returned here. Their minimum and maximum value will be set to
-           :attr:`None`.
+           :obj:`None`.
 
         In order to assing a FITS file to a tag, the file name or the
         :class:`pyfits.HDUList` is assigned to the calibration attribute:
@@ -204,6 +234,12 @@ class Recipe(object):
 
         >>> muse_scibasic.calib = { 'MASTER_BIAS':'master_bias_0.fits', 
         ...                'BADPIX_TABLE':[ 'badpix_1.fits', 'badpix_2.fits' ] }
+
+        In a recipe call, the calibration frame lists may be overwritten by
+        specifying them in a :class:`dict`:
+
+        >>> res = muse_scibasic( ..., calib = {'MASTER_BIAS':'master_bias_1.fits'})
+
         '''
         return self._calib
 
@@ -249,14 +285,14 @@ class Recipe(object):
         >>> muse_scibasic.param.nifu = 1
 
         The new value is checked against parameter type, and possible value
-        limitations provided by the recipe. Dots in parameter names are
+        limitations provided by the recipe. Hyphens in parameter names are
         converted to underscores. In a recipe call, the same parameter can be
-        specified as
+        specified as :class:`dict`:
 
-        >>> res = muse_scibasic( ..., param_nifu = 1)
+        >>> res = muse_scibasic( ..., param = {'nifu':1})
 
         To reset a value to its default, it is either deleted, or set to
-        :attr:`None`. The following two lines:
+        :obj:`None`. The following two lines:
 
         >>> muse_scibasic.param.nifu = None
         >>> del muse_scibasic.param.nifu
@@ -282,31 +318,24 @@ class Recipe(object):
     def param(self):
         self._param = ParameterList(self, None)
 
-    def output(self, tag = None):
-        '''Return the list of output frame tags.
+    @property
+    def output(self):
+        '''Return a dictionary of output frame tags.
 
-        If the recipe does not provide this information, an exception is raised.
-        
-        :param tag: Input (raw) frame tag. Defaults to the :attr:`Recipe.tag` 
-            attribute if not specified. 
-        :type tag: :class:`str`
+        Keys are the tag names, values are the corresponding list of output
+        tags. If the recipe does not provide this information, an exception is
+        raised.
         '''
-        if tag is None:
-            tag = self.tag
-        for c in self._recipe.frameConfig():
-            if tag == c[0][0]:
-                return c[2]
+        return dict((c[0][0], c[2]) for c in self._recipe.frameConfig())
 
     def __call__(self, *data, **ndata):
         '''Call the recipes execution with a certain input frame.
         
-        :param data:       Data input frames, using the default tag.
-        :type data: :class:`pyfits.HDUlist` or :class:`str` or a :class:`list` 
-            of them.
+        :param raw: Data input frames.
+        :type raw: :class:`pyfits.HDUlist` or :class:`str` or a :class:`list` 
+            of them, or :class:`dict`
         :param tag: Overwrite the :attr:`tag` attribute (optional).
         :type tag: :class:`str`
-        :param raw: Data input frames, sorted by tag
-        :type raw: :class:`dict`
         :param threaded: overwrite the :attr:`threaded` attribute (optional).
         :type threaded: :class:`bool`
         :param loglevel: set the log level for python :mod:`logging` (optional).
@@ -315,6 +344,7 @@ class Recipe(object):
             :class:`logging.Logger` (optional, default is 'cpl.' + recipename).
         :type logname: :class:`str`
         :param output_dir: Set or overwrite the :attr:`output_dir` attribute.
+            (optional)
         :type output_dir: :class:`str`
         :param param: overwrite the CPL parameters of the recipe specified
             as keys with their dictionary values (optional). 
@@ -322,38 +352,46 @@ class Recipe(object):
         :param calib: Overwrite the calibration frame lists for the tags 
             specified as keys with their dictionary values (optional).
         :type calib: :class:`dict`
+        :param env: overwrite environment variables for the recipe call 
+            (optional). 
+        :type env: :class:`dict`
         :return: The object with the return frames as :class:`pyfits.HDUList` 
             objects
         :rtype: :class:`cpl.Result`
-        :raise: :class:`exceptions.ValueError` If the invocation parameters 
+        :raise: :exc:`exceptions.ValueError` If the invocation parameters
                 are incorrect.
-        :raise: :class:`exceptions.IOError` If the temporary directory could 
+        :raise: :exc:`exceptions.IOError` If the temporary directory could
                 not be built, the recipe could not start or the files could not 
-                be read/written. This error is also raised if the recipe crashed
-                by a segementation fault or similar.
-        :raise: :class:`cpl.CplError` If the recipe returns an error.
+                be read/written.
+        :raise: :exc:`cpl.CplError` If the recipe returns an error.
+        :raise: :exc:`cpl.RecipeCrash` If the CPL recipe crashes with a
+                SIGSEV or a SIGBUS
 
-        .. note:: If the recipe is executed in the background 
+        .. note::
+
+            If the recipe is executed in the background 
             (``threaded = True``) and an exception occurs, this exception is 
             raised whenever result fields are accessed.
         '''
         tmpfiles = []
         threaded = ndata.get('threaded', self.threaded)
-        loglevel = ndata.get('loglevel', msg.DEBUG)
+        loglevel = ndata.get('loglevel')
         logname = ndata.get('logname', 'cpl.%s' % self.__name__)
         output_dir = ndata.get('output_dir', self.output_dir)
         output_format = str if output_dir else pyfits.HDUList
         if output_dir is None:
             output_dir = tempfile.mkdtemp(dir = self.temp_dir, 
                                           prefix = self.__name__ + "-") 
-        parlist = self.param._aslist(**ndata)
+        parlist = self.param._aslist(ndata.get('param'))
         raw_frames = self._get_raw_frames(*data, **ndata)
         if len(raw_frames) < 1:
             raise ValueError('No raw frames specified.')
         input_len = -1 if isinstance(raw_frames[0][1], pyfits.HDUList) else \
             len(raw_frames[0][1]) if isinstance(raw_frames[0][1], list) else -1
-        calib_frames = self.calib._aslist(**ndata)
+        calib_frames = self.calib._aslist(ndata.get('calib'))
         framelist = expandframelist(raw_frames + calib_frames)
+        runenv = dict(self.env)
+        runenv.update(ndata.get('env', dict()))
         logger = None
         delete = output_format == pyfits.HDUList
         try:
@@ -368,19 +406,21 @@ class Recipe(object):
                 pass
             raise
         if not threaded:
-            return self._exec(output_dir, parlist, framelist, input_len, 
-                              logger, output_format, delete)
+            return self._exec(output_dir, parlist, framelist, runenv, 
+                              input_len, logger, output_format, delete)
         else:
             return  Threaded(
-                self._exec, output_dir, parlist, framelist, input_len, 
-                logger, output_format, delete)
+                self._exec, output_dir, parlist, framelist, runenv, 
+                input_len, logger, output_format, delete)
 
-    def _exec(self, output_dir, parlist, framelist, input_len, 
-              logger, output_format, delete):
+    def _exec(self, output_dir, parlist, framelist, runenv,
+              input_len, logger, output_format, delete):
         try:
             return Result(self._recipe.frameConfig(), output_dir,
                           self._recipe.run(output_dir, parlist, framelist,
-                                           logger.logfile, logger.level, self.memory_dump),
+                                           runenv.items(), 
+                                           logger.logfile, logger.level,
+                                           self.memory_dump),
                           input_len, logger, output_format)
         finally:
             self._cleanup(output_dir, logger, delete)
@@ -391,27 +431,23 @@ class Recipe(object):
         Returns a :class:`list` with (tag, the input frame(s)) pairs. Note
         that more than one input tag is not allowed here.
         '''
-        m = ndata.get('raw', { })
+        data = list(data)
+        if 'raw' in ndata:
+            data.append(ndata['raw'])
         tag = ndata.get('tag', self.tag)
-        if tag is None:
-            if data:
+        m = { }
+        for f in data:
+            if isinstance(f, dict):
+                m.update(f)
+            elif tag is None:
                 raise ValueError('No raw input tag')
-        else:
-            for f in data:
-                if self.tag not in m:
-                    m[tag] = f
-                elif isinstance(m[tag], list) \
-                        and not isinstance(m[tag], pyfits.HDUList):
-                    m[tag].append(f)
-                else:
-                    m[tag] = [ m[tag], f ]
-
-        if ndata is not None:
-            for name, tdata in ndata.items():
-                if name.startswith('raw_'):
-                    tag = name.split('_', 1)[1]
-                    m[tag] = tdata
-
+            elif tag not in m:
+                m[tag] = f
+            elif isinstance(m[tag], list) \
+                    and not isinstance(m[tag], pyfits.HDUList):
+                m[tag].append(f)
+            else:
+                m[tag] = [ m[tag], f ]
         return list(m.iteritems())
 
     def _cleanup(self, output_dir, logger, delete):
@@ -428,8 +464,7 @@ class Recipe(object):
             if delete:
                 shutil.rmtree(output_dir)
 
-    @property
-    def __doc__(self):
+    def _doc(self):
         s = '%s\n\n%s\n\n' % (self.description[0], self.description[1])
         
         r = 'Parameters:\n' 
@@ -446,10 +481,10 @@ class Recipe(object):
             t = 'Raw and product frames:\n'
             maxlen = max(len(f) for f in self.tags)
             for f in self.tags:
-                t += ' %s --> %s\n' % (f.rjust(maxlen), `self.output(f)`)
+                t += ' %s --> %s\n' % (f.rjust(maxlen), `self.output[f]`)
         else:
             t = ''
-        print s + r + c + t + '\n\n'
+        return s + r + c + t + '\n\n'
 
     def __repr__(self):
         return 'Recipe(%s, version = %s)' % (`self.__name__`, `self.version[0]`)
@@ -500,8 +535,12 @@ class Recipe(object):
     def set_maxthreads(n):
         '''Set the maximal number of threads to be executed in parallel.
 
-        .. note:: This affects only threads that are started afterwards with
+        .. note::
+
+            This affects only threads that are started afterwards with
             the ``threaded = True`` flag.
+
+        .. seealso:: :ref:`parallel`
         '''
         Threaded.set_maxthreads(n)
 
diff --git a/cpl/result.py b/cpl/result.py
index 60cdb41..a22f663 100644
--- a/cpl/result.py
+++ b/cpl/result.py
@@ -104,7 +104,7 @@ class CplError(StandardError):
 
     The exception is raised on recipe invocation, or when accessing the result
     frames if the recipe was started in background
-    (:attr:`cpl.Recipe.threaded` set to :attr:`True`).
+    (:attr:`cpl.Recipe.threaded` set to :obj:`True`).
 
     Attributes:
 
@@ -128,11 +128,11 @@ class CplError(StandardError):
 
        Log lines of the recipe that lead to this exception.
 
-       .. seealso:: :class:`cpl.log.LogList`
+       .. seealso:: :class:`cpl.logger.LogList`
 
     .. attribute:: next
      
-       Next error, or :attr:`None`.
+       Next error, or :obj:`None`.
 
     '''
     def __init__(self, retval, res, logger = None):
@@ -187,7 +187,7 @@ class RecipeCrash(StandardError):
 
     The exception is raised on recipe invocation, or when accessing the result
     frames if the recipe was started in background
-    (:attr:`cpl.Recipe.threaded` set to :attr:`True`).
+    (:attr:`cpl.Recipe.threaded` set to :obj:`True`).
 
     Attributes:
 
@@ -195,7 +195,7 @@ class RecipeCrash(StandardError):
 
        List of stack elements, with the most recent element (the one that
        caused the crash) at the end. Each stack element is a 
-       :class:`collections.namedtuple` with the following attributes:
+       :func:`collections.namedtuple` with the following attributes:
 
        .. attribute:: filename
  
diff --git a/cpl/version.py b/cpl/version.py
index 26bae34..60b30c3 100644
--- a/cpl/version.py
+++ b/cpl/version.py
@@ -1,5 +1,5 @@
-version = '0.3.8'
+version = '0.4'
 author = 'Ole Streicher'
 email = 'python-cpl at liska.ath.cx'
 license_ = 'GPL'
-doc = 'Python interface for the Common Pipeline Library.\n\nNon-official library to access CPL modules via Python. \nIt is not meant as part of the MUSE pipeline software, but \nmay be useful for testing.'
+doc = 'Python interface for the Common Pipeline Library.\nThis module can list, configure and execute CPL-based recipes from Python.\nThe input, calibration and output data can be specified as FITS files\nor as pyfits objects in memory.\n\nThe Common Pipeline Library (CPL) comprises a set of ISO-C libraries that\nprovide a comprehensive, efficient and robust software toolkit. It forms a\nbasis for the creation of automated astronomical data-reduction tasks. One of\nthe features provided by the CPL is the ability to create data-reduction\nalgorithms that run as plugins (dynamic libraries). These are called "recipes"\nand are one of the main aspects of the CPL data-reduction development\nenvironment. More information about the CPL can be found here:\n\n  http://www.eso.org/sci/software/cpl/\n\nThe interface may be used to run ESO pipeline recipes linked to CPL \nversions 4.0 to 6.2.\n\n'
diff --git a/setup.py b/setup.py
index 3a2cfba..25f332f 100644
--- a/setup.py
+++ b/setup.py
@@ -4,15 +4,28 @@ from distutils.core import setup, Extension
 author = 'Ole Streicher'
 email = 'python-cpl at liska.ath.cx'
 license_ = 'GPL'
-cpl_version = '0.3.8'
-doc = '''Python interface for the Common Pipeline Library.
+cpl_version = '0.4'
+with open('README') as readme:
+    description = readme.read().splitlines()
+    long_description = "\n".join(description[2:])
+    description = description[0]
+
+doc = '%s\n%s' % (description, 
+                  long_description[:long_description.find('Build instructions')])
+pkgname = 'python-cpl'
+baseurl = 'http://www.aip.de/~oles/%s' % pkgname
+classifiers = '''Development Status :: 4 - Beta
+Intended Audience :: Science/Research
+License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
+Operating System :: MacOS :: MacOS X
+Operating System :: POSIX
+Operating System :: Unix
+Programming Language :: C
+Programming Language :: Python
+Programming Language :: Python :: 2 
+Topic :: Scientific/Engineering :: Astronomy
+'''.splitlines()
 
-Non-official library to access CPL modules via Python. 
-It is not meant as part of the MUSE pipeline software, but 
-may be useful for testing.'''
-description = doc.splitlines()
-long_description = "\n".join(description[2:])
-description = description[0]
 
 def create_version_file(cpl_version = cpl_version):
     vfile = open(os.path.join('cpl', 'version.py'), 'w')
@@ -22,20 +35,25 @@ def create_version_file(cpl_version = cpl_version):
     vfile.write("license_ = %s\n" % repr(license_))
     vfile.write("doc = %s\n" % repr(doc))
     vfile.close()
-include_dirs = os.environ.get('INCLUDE_PATH', '.').split(':') 
-library_dirs = os.environ.get('LIBRARY_PATH', '.').split(':') 
-module1 = Extension('cpl.CPL_recipe',
-                    include_dirs = include_dirs,
-                    library_dirs = library_dirs,
-                    libraries = [ 'cplcore', 'cpldfs', 'cplui', 'cpldrs', 
-                                  'gomp', 'mcheck' ],
-                    sources = ['cpl/CPL_recipe.c'])
 
 create_version_file()
-setup (name = 'python-cpl',
-       version = cpl_version, author = author, author_email = email, 
-       description = description, long_description = long_description,  
-       license = license_, url = 'http://www.aip.de/~oles/python-cpl/',
-       packages = ['cpl'],
-       ext_modules = [module1])
 
+module1 = Extension('cpl.CPL_recipe',
+                    sources = ['cpl/CPL_recipe.c', 'cpl/CPL_library.c'])
+
+setup(
+    name = pkgname,
+    version = cpl_version,
+    author = author,
+    author_email = email,
+    description = description,
+    long_description = long_description,
+    license = license_,
+    url = baseurl,
+    download_url = '%s/%s-%s.tar.gz' % (baseurl, pkgname, cpl_version),
+    classifiers = classifiers,
+    requires = ['pyfits'],
+    provides = ['cpl'],
+    packages = ['cpl'],
+    ext_modules = [module1]
+    )

-- 
Control pipeline recipes from the European Southern Observatory



More information about the debian-science-commits mailing list