Skip to content

Commit

Permalink
Implementation of proposal #18
Browse files Browse the repository at this point in the history
  • Loading branch information
pysailor committed Feb 8, 2019
1 parent 4989fa7 commit c4bac66
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 7 deletions.
6 changes: 5 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ Breaking changes:

New features:

- *add item here*
- New option ``former_dotted_names`` that allows to register the former name under
which a behavior used to be registerd. This can be useful to ensure a smooth
transition in case a behavior's dotted name is changed.
Refs: #18
[pysailor]

Bug fixes:

Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ The directive supports the attributes:

Must be one element (no multiadapters, applies also for auto-detection).

``former_dotted_names``
In case a behavior is modified so that its dotted name changes, this field can be used to register the old name(s). Therefore, it is possible to retrieve the name(s) under which a behavior was formerly registered under.

If a call to ``lookup_behavior_registration`` does not find a behavior under the given name, it will look at the former dotted names to try and find the behavior.


ZCML Examples
-------------
Expand Down
12 changes: 11 additions & 1 deletion plone/behavior/metaconfigure.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ class IBehaviorDirective(Interface):
u'provides under a different name.',
required=False)

former_dotted_names = TextLine(
title=u'Space-separated list of dotted names that this behavior was '
u'formerly registered under',
description=u'Use this field in case you change the dotted name, '
u'so that the current behavior can be looked up under '
u'its former name.',
required=False)


def _detect_for(factory, marker):
"""if no explicit for is given we need to figure it out.
Expand All @@ -92,7 +100,8 @@ def _detect_for(factory, marker):


def behaviorDirective(_context, title, provides, name=None, description=None,
marker=None, factory=None, for_=None, name_only=False):
marker=None, factory=None, for_=None, name_only=False,
former_dotted_names=''):

if marker is None and factory is None:
# a schema only behavior means usually direct attribute settings on the
Expand Down Expand Up @@ -124,6 +133,7 @@ def behaviorDirective(_context, title, provides, name=None, description=None,
marker=marker,
factory=factory,
name=name,
former_dotted_names=former_dotted_names,
)
# the behavior registration can be looked up as a named utility.
# the name of the utility is either the full dotted path of the interface
Expand Down
31 changes: 26 additions & 5 deletions plone/behavior/registration.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from plone.behavior import logger
from plone.behavior.interfaces import IBehavior
from zope.component import ComponentLookupError
from zope.component import getUtilitiesFor
from zope.component import getUtility
from zope.interface import implementer

Expand All @@ -20,21 +22,22 @@
marker: {marker}
factory: {factory}
title: {title}
{description}
{description}{extra_info}
>"""


@implementer(IBehavior)
class BehaviorRegistration(object):

def __init__(self, title, description, interface,
marker, factory, name=None):
marker, factory, name=None, former_dotted_names=''):
self.title = title
self.description = description
self.interface = interface
self.marker = marker
self.factory = factory
self.name = name
self.former_dotted_names = former_dotted_names

def __repr__(self):
if self.marker is not None:
Expand All @@ -54,7 +57,11 @@ def __repr__(self):
'description': textwrap.fill(
self.description or '(no description)',
subsequent_indent=' '
)
),
'extra_info': (
self.former_dotted_names and
'\n former dotted names: {}'.format(self.former_dotted_names)
),
}
return REGISTRATION_REPR.format(**info)

Expand All @@ -64,8 +71,11 @@ class BehaviorRegistrationNotFound(Exception):
"""


def lookup_behavior_registration(name=None, identifier=None):
"""Lookup behavior registration either by name or interface identifier.
def lookup_behavior_registration(
name=None, identifier=None, warn_about_fallback=True):
"""Look up behavior registration either by name or interface identifier.
Fall back to checking the former_dotted_names if the lookup is not
successful.
``ValueError`` is thrown if function call is incomplete.
``BehaviorRegistrationNotFound`` is thrown if lookup fails.
Expand All @@ -80,4 +90,15 @@ def lookup_behavior_registration(name=None, identifier=None):
try:
return getUtility(IBehavior, name=name)
except ComponentLookupError:
for id_, behavior in getUtilitiesFor(IBehavior):
# Before we raise an error, iterate over all behaviors and check
# if the requested name is registered as a former dotted name.
if name in behavior.former_dotted_names:
if warn_about_fallback:
logger.warn(
'The dotted name "{}" is deprecated. It has been '
'changed to "{}"'.format(
name, behavior.interface.__identifier__)
)
return behavior
raise BehaviorRegistrationNotFound(name)

0 comments on commit c4bac66

Please sign in to comment.