Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

remove mako bindings

  • Loading branch information...
commit a8b2df61a4cc9e91ff4a8b972f35c11b80aa6ada 1 parent 3494279
@cguardia cguardia authored reedobrien committed
View
4 pyramid/config/rendering.py
@@ -14,13 +14,9 @@
chameleon_zpt,
)
-from pyramid.mako_templating import renderer_factory as mako_renderer_factory
-
DEFAULT_RENDERERS = (
('.txt', chameleon_text.renderer_factory),
('.pt', chameleon_zpt.renderer_factory),
- ('.mak', mako_renderer_factory),
- ('.mako', mako_renderer_factory),
('json', renderers.json_renderer_factory),
('string', renderers.string_renderer_factory),
)
View
187 pyramid/mako_templating.py
@@ -1,187 +0,0 @@
-import os
-import sys
-import threading
-
-from zope.interface import (
- implementer,
- Interface,
- )
-
-from pyramid.asset import (
- resolve_asset_spec,
- abspath_from_asset_spec,
- )
-
-from pyramid.compat import (
- is_nonstr_iter,
- reraise,
- )
-
-from pyramid.interfaces import ITemplateRenderer
-from pyramid.settings import asbool
-from pyramid.util import DottedNameResolver
-
-from mako.lookup import TemplateLookup
-from mako import exceptions
-
-class IMakoLookup(Interface):
- pass
-
-class PkgResourceTemplateLookup(TemplateLookup):
- """TemplateLookup subclass that handles asset specification URIs"""
- def adjust_uri(self, uri, relativeto):
- """Called from within a Mako template, avoids adjusting the
- uri if it looks like an asset specification"""
- # Don't adjust asset spec names
- if ':' in uri:
- return uri
- return TemplateLookup.adjust_uri(self, uri, relativeto)
-
- def get_template(self, uri):
- """Fetch a template from the cache, or check the filesystem
- for it
-
- In addition to the basic filesystem lookup, this subclass will
- use pkg_resource to load a file using the asset
- specification syntax.
-
- """
- isabs = os.path.isabs(uri)
- if (not isabs) and (':' in uri):
- try:
- if self.filesystem_checks:
- return self._check(uri, self._collection[uri])
- else:
- return self._collection[uri]
- except KeyError:
- pname, path = resolve_asset_spec(uri)
- srcfile = abspath_from_asset_spec(path, pname)
- if os.path.isfile(srcfile):
- return self._load(srcfile, uri)
- raise exceptions.TopLevelLookupException(
- "Can not locate template for uri %r" % uri)
- return TemplateLookup.get_template(self, uri)
-
-
-registry_lock = threading.Lock()
-
-class MakoRendererFactoryHelper(object):
- def __init__(self, settings_prefix=None):
- self.settings_prefix = settings_prefix
-
- def __call__(self, info):
- path = info.name
- registry = info.registry
- settings = info.settings
- settings_prefix = self.settings_prefix
-
- if settings_prefix is None:
- settings_prefix = info.type +'.'
-
- lookup = registry.queryUtility(IMakoLookup, name=settings_prefix)
-
- def sget(name, default=None):
- return settings.get(settings_prefix + name, default)
-
- if lookup is None:
- reload_templates = settings.get('pyramid.reload_templates', None)
- if reload_templates is None:
- reload_templates = settings.get('reload_templates', False)
- reload_templates = asbool(reload_templates)
- directories = sget('directories', [])
- module_directory = sget('module_directory', None)
- input_encoding = sget('input_encoding', 'utf-8')
- error_handler = sget('error_handler', None)
- default_filters = sget('default_filters', 'h')
- imports = sget('imports', None)
- strict_undefined = asbool(sget('strict_undefined', False))
- preprocessor = sget('preprocessor', None)
- if not is_nonstr_iter(directories):
- directories = list(filter(None, directories.splitlines()))
- directories = [ abspath_from_asset_spec(d) for d in directories ]
- if module_directory is not None:
- module_directory = abspath_from_asset_spec(module_directory)
- if error_handler is not None:
- dotted = DottedNameResolver(info.package)
- error_handler = dotted.maybe_resolve(error_handler)
- if default_filters is not None:
- if not is_nonstr_iter(default_filters):
- default_filters = list(filter(
- None, default_filters.splitlines()))
- if imports is not None:
- if not is_nonstr_iter(imports):
- imports = list(filter(None, imports.splitlines()))
- if preprocessor is not None:
- dotted = DottedNameResolver(info.package)
- preprocessor = dotted.maybe_resolve(preprocessor)
-
-
- lookup = PkgResourceTemplateLookup(
- directories=directories,
- module_directory=module_directory,
- input_encoding=input_encoding,
- error_handler=error_handler,
- default_filters=default_filters,
- imports=imports,
- filesystem_checks=reload_templates,
- strict_undefined=strict_undefined,
- preprocessor=preprocessor
- )
-
- registry_lock.acquire()
- try:
- registry.registerUtility(lookup, IMakoLookup,
- name=settings_prefix)
- finally:
- registry_lock.release()
-
- return MakoLookupTemplateRenderer(path, lookup)
-
-renderer_factory = MakoRendererFactoryHelper('mako.')
-
-class MakoRenderingException(Exception):
- def __init__(self, text):
- self.text = text
-
- def __repr__(self):
- return self.text
-
- __str__ = __repr__
-
-@implementer(ITemplateRenderer)
-class MakoLookupTemplateRenderer(object):
- def __init__(self, path, lookup):
- self.path = path
- self.lookup = lookup
-
- def implementation(self):
- return self.lookup.get_template(self.path)
-
- def __call__(self, value, system):
- context = system.pop('context', None)
- if context is not None:
- system['_context'] = context
- def_name = None
- if isinstance(value, tuple):
- def_name, value = value
- try:
- system.update(value)
- except (TypeError, ValueError):
- raise ValueError('renderer was passed non-dictionary as value')
- template = self.implementation()
- if def_name is not None:
- template = template.get_def(def_name)
- try:
- result = template.render_unicode(**system)
- except:
- try:
- exc_info = sys.exc_info()
- errtext = exceptions.text_error_template().render(
- error=exc_info[1],
- traceback=exc_info[2]
- )
- reraise(MakoRenderingException(errtext), None, exc_info[2])
- finally:
- del exc_info
-
- return result
View
2  pyramid/tests/test_config/test_init.py
@@ -72,8 +72,6 @@ def test_ctor_no_registry(self):
if not PYPY:
self.assertTrue(config.registry.getUtility(IRendererFactory, '.pt'))
self.assertTrue(config.registry.getUtility(IRendererFactory,'.txt'))
- self.assertTrue(config.registry.getUtility(IRendererFactory, '.mak'))
- self.assertTrue(config.registry.getUtility(IRendererFactory, '.mako'))
def test_begin(self):
from pyramid.config import Configurator
View
507 pyramid/tests/test_mako_templating.py
@@ -1,507 +0,0 @@
-## come on python gimme some of that sweet, sweet -*- coding: utf-8 -*-
-
-import unittest
-from pyramid import testing
-from pyramid.compat import (
- text_,
- text_type,
- )
-
-class Base(object):
- def setUp(self):
- self.config = testing.setUp()
- self.config.begin()
- import os
- here = os.path.abspath(os.path.dirname(__file__))
- self.templates_dir = os.path.join(here, 'fixtures')
-
- def tearDown(self):
- self.config.end()
-
-class Test_renderer_factory(Base, unittest.TestCase):
- def _callFUT(self, info):
- from pyramid.mako_templating import renderer_factory
- return renderer_factory(info)
-
- def _getLookup(self, name='mako.'):
- from pyramid.mako_templating import IMakoLookup
- return self.config.registry.getUtility(IMakoLookup, name=name)
-
- def test_no_directories(self):
- info = DummyRendererInfo({
- 'name':'pyramid.tests:fixtures/helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':{},
- })
- renderer = self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.directories, [])
- self.assertEqual(lookup.filesystem_checks, False)
- self.assertEqual(renderer.path,
- 'pyramid.tests:fixtures/helloworld.mak')
- self.assertEqual(renderer.lookup, lookup)
-
- def test_no_lookup(self):
- settings = {'mako.directories':self.templates_dir}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- renderer = self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.directories, [self.templates_dir])
- self.assertEqual(lookup.filesystem_checks, False)
- self.assertEqual(renderer.path, 'helloworld.mak')
- self.assertEqual(renderer.lookup, lookup)
-
- def test_composite_directories_path(self):
- twice = '\n' + self.templates_dir + '\n' + self.templates_dir + '\n'
- settings = {'mako.directories':twice}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.directories, [self.templates_dir]*2)
-
- def test_directories_list(self):
- import sys
- import os.path
- settings = {'mako.directories':['a', 'b']}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- module_path = os.path.dirname(
- sys.modules['__main__'].__file__).rstrip('.') # ./setup.py
- self.assertEqual(lookup.directories, [
- os.path.join(module_path, 'a'),
- os.path.join(module_path, 'b')])
-
- def test_with_module_directory_asset_spec(self):
- import os
- module_directory = 'pyramid.tests:fixtures'
- settings = {'mako.directories':self.templates_dir,
- 'mako.module_directory':module_directory}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
- self.assertEqual(lookup.module_directory, fixtures)
-
- def test_with_module_directory_asset_abspath(self):
- import os
- fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
- settings = {'mako.directories':self.templates_dir,
- 'mako.module_directory':fixtures}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.module_directory, fixtures)
-
- def test_with_input_encoding(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.input_encoding':'utf-16'}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['input_encoding'], 'utf-16')
-
- def test_with_error_handler(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.error_handler':'pyramid.tests'}
- import pyramid.tests
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['error_handler'], pyramid.tests)
-
- def test_with_preprocessor(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.preprocessor':'pyramid.tests'}
- import pyramid.tests
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['preprocessor'], pyramid.tests)
-
- def test_with_default_filters(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.default_filters':'\nh\ng\n\n'}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
-
- def test_with_default_filters_list(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.default_filters':['h', 'g']}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
-
- def test_with_imports(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.imports':'\none\ntwo\n\n'}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
-
- def test_with_imports_list(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.imports':['one', 'two']}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
-
- def test_with_strict_undefined_true(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.strict_undefined':'true'}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['strict_undefined'], True)
-
- def test_with_strict_undefined_false(self):
- settings = {'mako.directories':self.templates_dir,
- 'mako.strict_undefined':'false'}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- })
- self._callFUT(info)
- lookup = self._getLookup()
- self.assertEqual(lookup.template_args['strict_undefined'], False)
-
- def test_with_lookup(self):
- from pyramid.mako_templating import IMakoLookup
- lookup = dict()
- self.config.registry.registerUtility(lookup, IMakoLookup, name='mako.')
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':{},
- })
- renderer = self._callFUT(info)
- self.assertEqual(renderer.lookup, lookup)
- self.assertEqual(renderer.path, 'helloworld.mak')
-
-class MakoRendererFactoryHelperTests(Base, unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.mako_templating import MakoRendererFactoryHelper
- return MakoRendererFactoryHelper
-
- def _makeOne(self, *arg, **kw):
- klass = self._getTargetClass()
- return klass(*arg, **kw)
-
- def _getLookup(self, name='mako.'):
- from pyramid.mako_templating import IMakoLookup
- return self.config.registry.getUtility(IMakoLookup, name=name)
-
- def test_no_settings_prefix(self):
- settings = {'foo.directories':self.templates_dir}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- 'type':'foo',
- })
- helper = self._makeOne()
- renderer = helper(info)
- lookup = self._getLookup('foo.')
- self.assertEqual(lookup.directories, [self.templates_dir])
- self.assertEqual(lookup.filesystem_checks, False)
- self.assertEqual(renderer.path, 'helloworld.mak')
- self.assertEqual(renderer.lookup, lookup)
-
- def test_custom_settings_prefix(self):
- settings = {'bar.directories':self.templates_dir}
- info = DummyRendererInfo({
- 'name':'helloworld.mak',
- 'package':None,
- 'registry':self.config.registry,
- 'settings':settings,
- 'type':'foo',
- })
- helper = self._makeOne('bar.')
- renderer = helper(info)
- lookup = self._getLookup('bar.')
- self.assertEqual(lookup.directories, [self.templates_dir])
- self.assertEqual(lookup.filesystem_checks, False)
- self.assertEqual(renderer.path, 'helloworld.mak')
- self.assertEqual(renderer.lookup, lookup)
-
-class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.mako_templating import MakoLookupTemplateRenderer
- return MakoLookupTemplateRenderer
-
- def _makeOne(self, *arg, **kw):
- klass = self._getTargetClass()
- return klass(*arg, **kw)
-
- def test_instance_implements_ITemplate(self):
- from zope.interface.verify import verifyObject
- from pyramid.interfaces import ITemplateRenderer
- verifyObject(ITemplateRenderer, self._makeOne(None, None))
-
- def test_class_implements_ITemplate(self):
- from zope.interface.verify import verifyClass
- from pyramid.interfaces import ITemplateRenderer
- verifyClass(ITemplateRenderer, self._getTargetClass())
-
- def test_call(self):
- lookup = DummyLookup()
- instance = self._makeOne('path', lookup)
- result = instance({}, {'system':1})
- self.assertTrue(isinstance(result, text_type))
- self.assertEqual(result, text_('result'))
-
- def test_call_with_system_context(self):
- # lame
- lookup = DummyLookup()
- instance = self._makeOne('path', lookup)
- result = instance({}, {'context':1})
- self.assertTrue(isinstance(result, text_type))
- self.assertEqual(result, text_('result'))
- self.assertEqual(lookup.values, {'_context':1})
-
- def test_call_with_tuple_value(self):
- lookup = DummyLookup()
- instance = self._makeOne('path', lookup)
- result = instance(('fub', {}), {'context':1})
- self.assertEqual(lookup.deffed, 'fub')
- self.assertEqual(result, text_('result'))
- self.assertEqual(lookup.values, {'_context':1})
-
- def test_call_with_nondict_value(self):
- lookup = DummyLookup()
- instance = self._makeOne('path', lookup)
- self.assertRaises(ValueError, instance, None, {})
-
- def test_call_render_raises(self):
- from pyramid.mako_templating import MakoRenderingException
- lookup = DummyLookup(exc=NotImplementedError)
- instance = self._makeOne('path', lookup)
- try:
- instance({}, {})
- except MakoRenderingException as e:
- self.assertTrue('NotImplementedError' in e.text)
- else: # pragma: no cover
- raise AssertionError
-
- def test_implementation(self):
- lookup = DummyLookup()
- instance = self._makeOne('path', lookup)
- result = instance.implementation().render_unicode()
- self.assertTrue(isinstance(result, text_type))
- self.assertEqual(result, text_('result'))
-
-class TestIntegration(unittest.TestCase):
- def setUp(self):
- import pyramid.mako_templating
- self.config = testing.setUp()
- self.config.add_settings({'mako.directories':
- 'pyramid.tests:fixtures'})
- self.config.add_renderer('.mak',
- pyramid.mako_templating.renderer_factory)
-
- def tearDown(self):
- self.config.end()
-
- def test_render(self):
- from pyramid.renderers import render
- result = render('helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
-
- def test_render_from_fs(self):
- from pyramid.renderers import render
- self.config.add_settings({'reload_templates': True})
- result = render('helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
-
- def test_render_inheritance(self):
- from pyramid.renderers import render
- result = render('helloinherit.mak', {}).replace('\r','')
- self.assertEqual(result, text_('Layout\nHello World!\n'))
-
- def test_render_inheritance_pkg_spec(self):
- from pyramid.renderers import render
- result = render('hello_inherit_pkg.mak', {}).replace('\r','')
- self.assertEqual(result, text_('Layout\nHello World!\n'))
-
- def test_render_to_response(self):
- from pyramid.renderers import render_to_response
- result = render_to_response('helloworld.mak', {'a':1})
- self.assertEqual(result.ubody.replace('\r',''),
- text_('\nHello föö\n', 'utf-8'))
-
- def test_render_to_response_pkg_spec(self):
- from pyramid.renderers import render_to_response
- result = render_to_response('pyramid.tests:fixtures/helloworld.mak',
- {'a':1})
- self.assertEqual(result.ubody.replace('\r', ''),
- text_('\nHello föö\n', 'utf-8'))
-
- def test_render_with_abs_path(self):
- from pyramid.renderers import render
- result = render('/helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
-
- def test_get_renderer(self):
- from pyramid.renderers import get_renderer
- result = get_renderer('helloworld.mak')
- self.assertEqual(
- result.implementation().render_unicode().replace('\r',''),
- text_('\nHello föö\n', 'utf-8'))
-
- def test_template_not_found(self):
- from pyramid.renderers import render
- from mako.exceptions import TemplateLookupException
- self.assertRaises(TemplateLookupException, render,
- 'helloworld_not_here.mak', {})
-
- def test_template_default_escaping(self):
- from pyramid.renderers import render
- result = render('nonminimal.mak',
- {'name':'<b>fred</b>'}).replace('\r','')
- self.assertEqual(result, text_('Hello, &lt;b&gt;fred&lt;/b&gt;!\n'))
-
-class TestPkgResourceTemplateLookup(unittest.TestCase):
- def _makeOne(self, **kw):
- from pyramid.mako_templating import PkgResourceTemplateLookup
- return PkgResourceTemplateLookup(**kw)
-
- def get_fixturedir(self):
- import os
- import pyramid.tests
- return os.path.join(os.path.dirname(pyramid.tests.__file__), 'fixtures')
-
- def test_adjust_uri_not_asset_spec(self):
- inst = self._makeOne()
- result = inst.adjust_uri('a', None)
- self.assertEqual(result, '/a')
-
- def test_adjust_uri_asset_spec(self):
- inst = self._makeOne()
- result = inst.adjust_uri('a:b', None)
- self.assertEqual(result, 'a:b')
-
- def test_get_template_not_asset_spec(self):
- fixturedir = self.get_fixturedir()
- inst = self._makeOne(directories=[fixturedir])
- result = inst.get_template('helloworld.mak')
- self.assertFalse(result is None)
-
- def test_get_template_asset_spec_with_filesystem_checks(self):
- inst = self._makeOne(filesystem_checks=True)
- result = inst.get_template('pyramid.tests:fixtures/helloworld.mak')
- self.assertFalse(result is None)
-
- def test_get_template_asset_spec_missing(self):
- from mako.exceptions import TopLevelLookupException
- fixturedir = self.get_fixturedir()
- inst = self._makeOne(filesystem_checks=True, directories=[fixturedir])
- self.assertRaises(TopLevelLookupException, inst.get_template,
- 'pyramid.tests:fixtures/notthere.mak')
-
-class TestMakoRenderingException(unittest.TestCase):
- def _makeOne(self, text):
- from pyramid.mako_templating import MakoRenderingException
- return MakoRenderingException(text)
-
- def test_repr_and_str(self):
- exc = self._makeOne('text')
- self.assertEqual(str(exc), 'text')
- self.assertEqual(repr(exc), 'text')
-
-class DummyLookup(object):
- def __init__(self, exc=None):
- self.exc = exc
-
- def get_template(self, path):
- self.path = path
- return self
-
- def get_def(self, path):
- self.deffed = path
- return self
-
- def render_unicode(self, **values):
- if self.exc:
- raise self.exc
- self.values = values
- return text_('result')
-
-class DummyRendererInfo(object):
- def __init__(self, kw):
- self.__dict__.update(kw)
-
View
1  setup.py
@@ -38,7 +38,6 @@
install_requires=[
'setuptools',
'Chameleon >= 1.2.3',
- 'Mako >= 0.3.6', # strict_undefined
'WebOb >= 1.2dev', # response.text / py3 compat
'repoze.lru >= 0.4', # py3 compat
'zope.interface >= 3.8.0', # has zope.interface.registry
Please sign in to comment.
Something went wrong with that request. Please try again.