[ckan-changes] [okfn/ckan] c7d88f: new improved plugins.toolkit
GitHub
noreply at github.com
Fri Apr 20 11:48:34 UTC 2012
Branch: refs/heads/master
Home: https://github.com/okfn/ckan
Commit: c7d88f37b04d3165974d87652f5cbb0d42a08aeb
https://github.com/okfn/ckan/commit/c7d88f37b04d3165974d87652f5cbb0d42a08aeb
Author: Toby <toby.junk at gmail.com>
Date: 2012-04-20 (Fri, 20 Apr 2012)
Changed paths:
M ckan/plugins/__init__.py
M ckan/plugins/toolkit.py
Log Message:
-----------
new improved plugins.toolkit
diff --git a/ckan/plugins/__init__.py b/ckan/plugins/__init__.py
index e11e909..719a24a 100644
--- a/ckan/plugins/__init__.py
+++ b/ckan/plugins/__init__.py
@@ -1,19 +1,7 @@
from ckan.plugins.core import *
from ckan.plugins.interfaces import *
-
-class _Toolkit(object):
- ''' This object allows us to avoid circular imports while making
- functions/objects available to plugins. '''
-
- def __init__(self):
- self.toolkit = None
-
- def __getattr__(self, name):
- if not self.toolkit:
- import toolkit
- self.toolkit = toolkit
- return getattr(self.toolkit, name)
-
-toolkit = _Toolkit()
-del _Toolkit
+# Expose the toolkit object without doing an import *
+import toolkit as _toolkit
+toolkit = _toolkit.toolkit
+del _toolkit
diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index e635c43..c784eb0 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -1,10 +1,3 @@
-## This file is intended to make functions/objects consistently
-## available to plugins whilst giving developers the ability move code
-## around or change underlying frameworks etc. It should not be used
-## internally within ckan only by extensions. Functions should only be
-## removed from this file after reasonable depreciation notice has
-## been given.
-
import inspect
import os
import re
@@ -13,122 +6,184 @@
import paste.deploy.converters as converters
import webhelpers.html.tags
-import ckan
-import ckan.lib.base as base
-import ckan.logic as logic
-import ckan.lib.cli as cli
-
-
-
-__all__ = [
- ## Imported functions/objects ##
- '_', # i18n translation
- 'c', # template context
- 'request', # http request object
- 'render', # template render function
- 'render_text', # Genshi NewTextTemplate render function
- 'render_snippet', # snippet render function
- 'asbool', # converts an object to a boolean
- 'asint', # converts an object to an integer
- 'aslist', # converts an object to a list
- 'literal', # stop tags in a string being escaped
- 'get_action', # get logic action function
- 'check_access', # check logic function authorisation
- 'ActionNotFound', # action not found exception (ckan.logic.NotFound)
- 'NotAuthorized', # action not authorized exception
- 'ValidationError', # model update validation error
- 'CkanCommand', # class for providing cli interfaces
-
- ## Fully defined in this file ##
- 'add_template_directory',
- 'add_public_directory',
- 'requires_ckan_version',
- 'check_ckan_version',
- 'CkanVersionException',
-]
-
-_ = pylons.i18n._
-c = pylons.c
-request = pylons.request
-render = base.render
-render_text = base.render_text
-asbool = converters.asbool
-asint = converters.asint
-aslist = converters.aslist
-literal = webhelpers.html.tags.literal
-
-get_action = logic.get_action
-check_access = logic.check_access
-ActionNotFound = logic.NotFound ## Name change intentional
-NotAuthorized = logic.NotAuthorized
-ValidationError = logic.ValidationError
-
-CkanCommand = cli.CkanCommand
-
-# wrappers
-def render_snippet(template, data=None):
- data = data or {}
- return base.render_snippet(template, **data)
-
-
-# new functions
-def add_template_directory(config, relative_path):
- ''' Function to aid adding extra template paths to the config.
- The path is relative to the file calling this function. '''
- _add_served_directory(config, relative_path, 'extra_template_paths')
-
-def add_public_directory(config, relative_path):
- ''' Function to aid adding extra public paths to the config.
- The path is relative to the file calling this function. '''
- _add_served_directory(config, relative_path, 'extra_public_paths')
-
-def _add_served_directory(config, relative_path, config_var):
- ''' Add extra public/template directories to config. '''
- assert config_var in ('extra_template_paths', 'extra_public_paths')
- # we want the filename that of the function caller but they will
- # have used one of the available helper functions
- frame, filename, line_number, function_name, lines, index =\
- inspect.getouterframes(inspect.currentframe())[2]
-
- this_dir = os.path.dirname(filename)
- absolute_path = os.path.join(this_dir, relative_path)
- if absolute_path not in config.get(config_var, ''):
- if config.get(config_var):
- config[config_var] += ',' + absolute_path
- else:
- config[config_var] = absolute_path
+__all__ = ['toolkit']
class CkanVersionException(Exception):
''' Exception raised if required ckan version is not available. '''
pass
-
-def _version_str_2_list(v_str):
- ''' conver a version string into a list of ints
- eg 1.6.1b --> [1, 6, 1] '''
- v_str = re.sub(r'[^0-9.]', '', v_str)
- return [int(part) for part in v_str.split('.')]
-
-def check_ckan_version(min_version=None, max_version=None):
- ''' Check that the ckan version is correct for the plugin. '''
- current = _version_str_2_list(ckan.__version__)
-
- if min_version:
- min_required = _version_str_2_list(min_version)
- if current < min_required:
- return False
- if max_version:
- max_required = _version_str_2_list(max_version)
- if current > max_required:
- return False
- return True
-
-def requires_ckan_version(min_version, max_version=None):
- ''' Check that the ckan version is correct for the plugin. '''
- if not check_ckan_version(min_version=min_version, max_version=max_version):
- if not max_version:
- error = 'Requires ckan version %s or higher' % min_version
+class _Toolkit(object):
+ '''This class is intended to make functions/objects consistently
+ available to plugins, whilst giving developers the ability move
+ code around or change underlying frameworks etc. This object allows
+ us to avoid circular imports while making functions/objects
+ available to plugins.
+
+ It should not be used internally within ckan only by extensions.
+
+ Functions should only be removed from this file after reasonable
+ depreciation notice has been given.'''
+
+ # contents should describe the available functions/objects. We check
+ # that this list matches the actual availables in the initialisation
+ contents = [
+ ## Imported functions/objects ##
+ '_', # i18n translation
+ 'c', # template context
+ 'request', # http request object
+ 'render', # template render function
+ 'render_text', # Genshi NewTextTemplate render function
+ 'render_snippet', # snippet render function
+ 'asbool', # converts an object to a boolean
+ 'asint', # converts an object to an integer
+ 'aslist', # converts an object to a list
+ 'literal', # stop tags in a string being escaped
+ 'get_action', # get logic action function
+ 'check_access', # check logic function authorisation
+ 'ActionNotFound', # action not found exception (ckan.logic.NotFound)
+ 'NotAuthorized', # action not authorized exception
+ 'ValidationError', # model update validation error
+ 'CkanCommand', # class for providing cli interfaces
+
+ ## Fully defined in this file ##
+ 'add_template_directory',
+ 'add_public_directory',
+ 'requires_ckan_version',
+ 'check_ckan_version',
+ 'CkanVersionException',
+ ]
+
+
+ def __init__(self):
+ self._toolkit = {}
+
+ def _initialize(self):
+ ''' get the required functions/objects, store them for later
+ access and check that they match the contents dict. '''
+
+ import ckan
+ import ckan.lib.base as base
+ import ckan.logic as logic
+ import ckan.lib.cli as cli
+
+ # Allow class
+ self.__class__.ckan = ckan
+ self.__class__.base = base
+
+ t = self._toolkit
+
+ # imported functions
+ t['_'] = pylons.i18n._
+ t['c'] = pylons.c
+ t['request'] = pylons.request
+ t['render'] = base.render
+ t['render_text'] = base.render_text
+ t['asbool'] = converters.asbool
+ t['asint'] = converters.asint
+ t['aslist'] = converters.aslist
+ t['literal'] = webhelpers.html.tags.literal
+
+ t['get_action'] = logic.get_action
+ t['check_access'] = logic.check_access
+ t['ActionNotFound'] = logic.NotFound ## Name change intentional
+ t['NotAuthorized'] = logic.NotAuthorized
+ t['ValidationError'] = logic.ValidationError
+
+ t['CkanCommand'] = cli.CkanCommand
+
+ # class functions
+ t['render_snippet'] = self._render_snippet
+ t['add_template_directory'] = self._add_template_directory
+ t['add_public_directory'] = self._add_public_directory
+ t['requires_ckan_version'] = self._requires_ckan_version
+ t['check_ckan_version'] = self._check_ckan_version
+ t['CkanVersionException'] = CkanVersionException
+
+ # check contents list correct
+ errors = set(t).symmetric_difference(set(self.contents))
+ if errors:
+ raise Exception('Plugin toolkit error %s not matching' % errors)
+
+ # wrappers
+ @classmethod
+ def _render_snippet(cls, template, data=None):
+ ''' helper for the render_snippet function as it uses keywords
+ rather than dict to pass data '''
+ data = data or {}
+ return cls.base.render_snippet(template, **data)
+
+ # new functions
+ @classmethod
+ def _add_template_directory(cls, config, relative_path):
+ ''' Function to aid adding extra template paths to the config.
+ The path is relative to the file calling this function. '''
+ cls._add_served_directory(config, relative_path, 'extra_template_paths')
+
+ @classmethod
+ def _add_public_directory(cls, config, relative_path):
+ ''' Function to aid adding extra public paths to the config.
+ The path is relative to the file calling this function. '''
+ cls._add_served_directory(config, relative_path, 'extra_public_paths')
+
+ @classmethod
+ def _add_served_directory(cls, config, relative_path, config_var):
+ ''' Add extra public/template directories to config. '''
+ assert config_var in ('extra_template_paths', 'extra_public_paths')
+ # we want the filename that of the function caller but they will
+ # have used one of the available helper functions
+ frame, filename, line_number, function_name, lines, index =\
+ inspect.getouterframes(inspect.currentframe())[2]
+
+ this_dir = os.path.dirname(filename)
+ absolute_path = os.path.join(this_dir, relative_path)
+ if absolute_path not in config.get(config_var, ''):
+ if config.get(config_var):
+ config[config_var] += ',' + absolute_path
+ else:
+ config[config_var] = absolute_path
+
+ @classmethod
+ def _version_str_2_list(cls, v_str):
+ ''' convert a version string into a list of ints
+ eg 1.6.1b --> [1, 6, 1] '''
+ v_str = re.sub(r'[^0-9.]', '', v_str)
+ return [int(part) for part in v_str.split('.')]
+
+ @classmethod
+ def _check_ckan_version(cls, min_version=None, max_version=None):
+ ''' Check that the ckan version is correct for the plugin. '''
+ current = cls._version_str_2_list(cls.ckan.__version__)
+
+ if min_version:
+ min_required = cls._version_str_2_list(min_version)
+ if current < min_required:
+ return False
+ if max_version:
+ max_required = cls._version_str_2_list(max_version)
+ if current > max_required:
+ return False
+ return True
+
+ @classmethod
+ def _requires_ckan_version(cls, min_version, max_version=None):
+ ''' Check that the ckan version is correct for the plugin. '''
+ if not cls._check_ckan_version(min_version=min_version,
+ max_version=max_version):
+ if not max_version:
+ error = 'Requires ckan version %s or higher' % min_version
+ else:
+ error = 'Requires ckan version between %s and %s' % \
+ (min_version, max_version)
+ raise cls.CkanVersionException(error)
+
+ def __getattr__(self, name):
+ ''' return the function/object requested '''
+ if not self._toolkit:
+ self._initialize()
+ if name in self._toolkit:
+ return self._toolkit[name]
else:
- error = 'Requires ckan version between %s and %s' % \
- (min_version, max_version)
- raise CkanVersionException(error)
+ raise Exception('`%s` not found in plugins toolkit' % name)
+
+toolkit = _Toolkit()
+del _Toolkit
================================================================
More information about the ckan-changes
mailing list