# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, division
import six


class Registry(dict):
    """A rule registry dictionary.

    The key format is (<bearer>[, <target>][, <permission>])
    """

    def __missing__(self, key):
        # No specific rule; iterate through and attempt to use
        # inheritance to find a rule.
        strs = six.string_types
        cls = key[-2] if isinstance(key[-1], strs) else key[-1]
        for bkey, base_rule in six.iteritems(self):
            bcls = bkey[-2] if isinstance(bkey[-1], strs) else bkey[-1]
            if issubclass(cls, bcls):
                break

        else:
            if isinstance(key[-1], six.string_types):
                # No rule is defined for this rule check if we are
                # permission-specific and then check for a matching global rule.
                global_key = key[:-1]
                value = self[key] = self[global_key]
                return value

            raise KeyError

        # Use the base class.
        value = self[key] = base_rule
        return value


registry = Registry()


class ExpressionRegistry(dict):
    """An expression registry dictionary.

    The key format is (<bearer>[, <target>][, <permission>])
    """

    def __missing__(self, key):
        return registry[key]


expression = ExpressionRegistry()

@@ -84,16 +84,56 @@
 ivision%0A
+from six.moves import filter as ifilter%0A
 import s
@@ -143,16 +143,22 @@
 %0A%0Aclass 
+Bearer
 Registry
@@ -176,366 +176,497 @@
  %22%22%22
-A rule registry dictionary.%0A%0A    The key format is (%3Cbearer%3E%5B, %3Ctarget%3E%5D%5B, %3Cpermission%3E%5D)%0A    %22%22%22%0A%0A    def __missing__(self, key):%0A        # No specific rule; iterate through and attempt to use%0A        # inheritance to find a rule.%0A        strs = six.string_types%0A        cls = key%5B-2%5D if isinstance(key%5B-1%5D, strs) else key%5B-1%5D%0A        for bkey, base_rule in
+Key is (%3Cbearer%3E, %3Cpermission%3E)%22%22%22%0A%0A%0Abearer_registry = BearerRegistry()%0A%0A%0Aclass TargetRegistry(dict):%0A    def __missing__(self, key):%0A        %22%22%22Keys is (%3Cbearer%3E, %3Ctarget%3E)%22%22%22%0A        # Delegate to the bearer registry if this is a generic permission check%0A        if len(key) %3C 2:%0A            return bearer_registry%5Bkey%5D%0A%0A        bearer, target = key%0A%0A        # Iterate over keys where the bearer matches the bearer we were given.%0A        for base_key in ifilter(lambda x: bearer == x%5B0%5D,
  six
@@ -674,19 +674,19 @@
 iter
-item
+key
 s(self)
+)
 :%0A  
@@ -699,105 +699,148 @@
     
-bcls = bkey%5B-2%5D if isinstance(bkey%5B-1%5D, strs) else bkey%5B-1%5D%0A            if issubclass(cls, bcls):
+# Is target a subclass of a base target we have stored%0A            if issubclass(target, base_key%5B1%5D):%0A                rule = self%5Bbase_key%5D
 %0A   
@@ -858,17 +858,16 @@
   break%0A
-%0A
         
@@ -888,355 +888,1172 @@
     
-if isinstance(key%5B-1%5D, six.string_types):%0A                # No rule is defined for this rule check if we are%0A                # permission-specific and then check for a matching global rule.%0A                global_key = key%5B:-1%5D%0A                value = self%5Bkey%5D = self%5Bglobal_key%5D%0A                return value%0A%0A            raise KeyError%0A%0A
+raise KeyError(key)%0A%0A        self%5Bkey%5D = rule%0A        return rule%0A%0A%0Atarget_registry = TargetRegistry()%0A%0A%0Aclass PermissionRegistry(dict):%0A    def __missing__(self, key):%0A        %22%22%22Key format is (%3Cbearer%3E, %3Ctarget%3E, %3Cpermission%3E)%22%22%22%0A        # Delegate to the target registry if this is not an individual%0A        # permission check%0A        if len(key) %3C 3:%0A            return target_registry%5Bkey%5D%0A%0A        bearer, target, permission = key%0A%0A        # This should probably be moved elsewhere%0A        if target is None:%0A            return bearer_registry%5Bbearer, permission%5D%0A%0A        # We only want to iterate over items where the bearer and permission%0A        # matches.%0A        check_bearer = lambda x: bearer == x%5B0%5D%0A        check_perm = lambda x: permission == x%5B2%5D%0A        check = lambda x: all((check_bearer(x), check_perm(x)))%0A%0A        for base_key in ifilter(check, six.iterkeys(self)):%0A            base_bearer, base_target, base_perm = base_key%0A%0A            # Inheritance check for the target.%0A            if issubclass(target, base_target):%0A                # Found a match!%0A                rule = self%5Bbase_key%5D%0A                break%0A        else:%0A   
         
 # Us
@@ -2052,45 +2052,113 @@
     
+ 
 # 
-Use the base class.%0A        value =
+No entry found, check the target registry.%0A            rule = target_registry%5Bbearer, target%5D%0A%0A       
  sel
@@ -2166,21 +2166,16 @@
 %5Bkey%5D = 
-base_
 rule%0A   
@@ -2190,12 +2190,11 @@
 urn 
-valu
+rul
 e%0A%0A%0A
@@ -2204,16 +2204,26 @@
 istry = 
+Permission
 Registry

Rewrite registry to have fallbacks be a bit more sane