ACL uses enums, some more tests, other stuff.
authorScott Gasch <[email protected]>
Sat, 10 Jul 2021 01:31:02 +0000 (18:31 -0700)
committerScott Gasch <[email protected]>
Sat, 10 Jul 2021 01:31:02 +0000 (18:31 -0700)
15 files changed:
acl.py
argparse_utils.py
bootstrap.py
config.py
constants.py
dateparse/dateparse_utils.py
datetime_utils.py
decorator_utils.py
deferred_operand.py
exceptions.py
id_generator.py
logging_utils.py
smart_future.py
tests/acl_test.py
thread_utils.py

diff --git a/acl.py b/acl.py
index 5040304a7913fe4e452ceb2dc5ef5f2a76b36491..e6bb9033f89319eb986adab2574a59171dbe899d 100644 (file)
--- a/acl.py
+++ b/acl.py
@@ -1,17 +1,25 @@
 #!/usr/bin/env python3
 
 from abc import ABC, abstractmethod
 #!/usr/bin/env python3
 
 from abc import ABC, abstractmethod
+import enum
 import fnmatch
 import logging
 import re
 from typing import Any, Callable, List, Optional, Set
 
 import fnmatch
 import logging
 import re
 from typing import Any, Callable, List, Optional, Set
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
 
 logger = logging.getLogger(__name__)
 
 
 
 logger = logging.getLogger(__name__)
 
 
-ACL_ORDER_ALLOW_DENY = 1
-ACL_ORDER_DENY_ALLOW = 2
+class Order(enum.Enum):
+    """A helper to express the order of evaluation for allows/denies
+    in an Access Control List.
+    """
+    UNDEFINED = 0
+    ALLOW_DENY = 1
+    DENY_ALLOW = 2
 
 
 class SimpleACL(ABC):
 
 
 class SimpleACL(ABC):
@@ -20,22 +28,22 @@ class SimpleACL(ABC):
     def __init__(
         self,
         *,
     def __init__(
         self,
         *,
-        order_to_check_allow_deny: int,
+        order_to_check_allow_deny: Order,
         default_answer: bool
     ):
         if order_to_check_allow_deny not in (
         default_answer: bool
     ):
         if order_to_check_allow_deny not in (
-                ACL_ORDER_ALLOW_DENY, ACL_ORDER_DENY_ALLOW
+                Order.ALLOW_DENY, Order.DENY_ALLOW
         ):
             raise Exception(
         ):
             raise Exception(
-                'order_to_check_allow_deny must be ACL_ORDER_ALLOW_DENY or ' +
-                'ACL_ORDER_DENY_ALLOW')
+                'order_to_check_allow_deny must be Order.ALLOW_DENY or ' +
+                'Order.DENY_ALLOW')
         self.order_to_check_allow_deny = order_to_check_allow_deny
         self.default_answer = default_answer
 
     def __call__(self, x: Any) -> bool:
         """Returns True if x is allowed, False otherwise."""
         logger.debug(f'SimpleACL checking {x}')
         self.order_to_check_allow_deny = order_to_check_allow_deny
         self.default_answer = default_answer
 
     def __call__(self, x: Any) -> bool:
         """Returns True if x is allowed, False otherwise."""
         logger.debug(f'SimpleACL checking {x}')
-        if self.order_to_check_allow_deny == ACL_ORDER_ALLOW_DENY:
+        if self.order_to_check_allow_deny == Order.ALLOW_DENY:
             logger.debug('Checking allowed first...')
             if self.check_allowed(x):
                 logger.debug(f'{x} was allowed explicitly.')
             logger.debug('Checking allowed first...')
             if self.check_allowed(x):
                 logger.debug(f'{x} was allowed explicitly.')
@@ -44,7 +52,7 @@ class SimpleACL(ABC):
             if self.check_denied(x):
                 logger.debug(f'{x} was denied explicitly.')
                 return False
             if self.check_denied(x):
                 logger.debug(f'{x} was denied explicitly.')
                 return False
-        elif self.order_to_check_allow_deny == ACL_ORDER_DENY_ALLOW:
+        elif self.order_to_check_allow_deny == Order.DENY_ALLOW:
             logger.debug('Checking denied first...')
             if self.check_denied(x):
                 logger.debug(f'{x} was denied explicitly.')
             logger.debug('Checking denied first...')
             if self.check_denied(x):
                 logger.debug(f'{x} was denied explicitly.')
@@ -76,7 +84,7 @@ class SetBasedACL(SimpleACL):
                  *,
                  allow_set: Optional[Set[Any]] = None,
                  deny_set: Optional[Set[Any]] = None,
                  *,
                  allow_set: Optional[Set[Any]] = None,
                  deny_set: Optional[Set[Any]] = None,
-                 order_to_check_allow_deny: int,
+                 order_to_check_allow_deny: Order,
                  default_answer: bool) -> None:
         super().__init__(
             order_to_check_allow_deny=order_to_check_allow_deny,
                  default_answer: bool) -> None:
         super().__init__(
             order_to_check_allow_deny=order_to_check_allow_deny,
@@ -96,13 +104,39 @@ class SetBasedACL(SimpleACL):
         return x in self.deny_set
 
 
         return x in self.deny_set
 
 
+class AllowListACL(SetBasedACL):
+    """Convenience subclass for a list that only allows known items.
+    i.e. a 'whitelist'
+    """
+    def __init__(self,
+                 *,
+                 allow_set: Optional[Set[Any]]) -> None:
+        super().__init__(
+            allow_set = allow_set,
+            order_to_check_allow_deny = Order.ALLOW_DENY,
+            default_answer = False)
+
+
+class DenyListACL(SetBasedACL):
+    """Convenience subclass for a list that only disallows known items.
+    i.e. a 'blacklist'
+    """
+    def __init__(self,
+                 *,
+                 deny_set: Optional[Set[Any]]) -> None:
+        super().__init__(
+            deny_set = deny_set,
+            order_to_check_allow_deny = Order.ALLOW_DENY,
+            default_answer = True)
+
+
 class PredicateListBasedACL(SimpleACL):
     """An ACL that allows or denies by applying predicates."""
     def __init__(self,
                  *,
                  allow_predicate_list: List[Callable[[Any], bool]] = None,
                  deny_predicate_list: List[Callable[[Any], bool]] = None,
 class PredicateListBasedACL(SimpleACL):
     """An ACL that allows or denies by applying predicates."""
     def __init__(self,
                  *,
                  allow_predicate_list: List[Callable[[Any], bool]] = None,
                  deny_predicate_list: List[Callable[[Any], bool]] = None,
-                 order_to_check_allow_deny: int,
+                 order_to_check_allow_deny: Order,
                  default_answer: bool) -> None:
         super().__init__(
             order_to_check_allow_deny=order_to_check_allow_deny,
                  default_answer: bool) -> None:
         super().__init__(
             order_to_check_allow_deny=order_to_check_allow_deny,
@@ -128,7 +162,7 @@ class StringWildcardBasedACL(PredicateListBasedACL):
                  *,
                  allowed_patterns: Optional[List[str]] = None,
                  denied_patterns: Optional[List[str]] = None,
                  *,
                  allowed_patterns: Optional[List[str]] = None,
                  denied_patterns: Optional[List[str]] = None,
-                 order_to_check_allow_deny: int,
+                 order_to_check_allow_deny: Order,
                  default_answer: bool) -> None:
         allow_predicates = []
         if allowed_patterns is not None:
                  default_answer: bool) -> None:
         allow_predicates = []
         if allowed_patterns is not None:
@@ -158,7 +192,7 @@ class StringREBasedACL(PredicateListBasedACL):
                  *,
                  allowed_regexs: Optional[List[re.Pattern]] = None,
                  denied_regexs: Optional[List[re.Pattern]] = None,
                  *,
                  allowed_regexs: Optional[List[re.Pattern]] = None,
                  denied_regexs: Optional[List[re.Pattern]] = None,
-                 order_to_check_allow_deny: int,
+                 order_to_check_allow_deny: Order,
                  default_answer: bool) -> None:
         allow_predicates = None
         if allowed_regexs is not None:
                  default_answer: bool) -> None:
         allow_predicates = None
         if allowed_regexs is not None:
@@ -182,21 +216,49 @@ class StringREBasedACL(PredicateListBasedACL):
         )
 
 
         )
 
 
-class AnyCompoundACL(object):
+class AnyCompoundACL(SimpleACL):
     """An ACL that allows if any of its subacls allow."""
     """An ACL that allows if any of its subacls allow."""
-    def __init__(self, subacls: List[SimpleACL]):
-        assert subacls is not None
+    def __init__(self,
+                 *,
+                 subacls: Optional[List[SimpleACL]] = None,
+                 order_to_check_allow_deny: Order,
+                 default_answer: bool) -> None:
+        super().__init__(
+            order_to_check_allow_deny = order_to_check_allow_deny,
+            default_answer = default_answer
+        )
         self.subacls = subacls
 
         self.subacls = subacls
 
-    def __call__(self, x: Any):
+    def check_allowed(self, x: Any) -> bool:
+        if self.subacls is None:
+            return False
         return any(acl(x) for acl in self.subacls)
 
         return any(acl(x) for acl in self.subacls)
 
+    def check_denied(self, x: Any) -> bool:
+        if self.subacls is None:
+            return False
+        return any(not acl(x) for acl in self.subacls)
+
 
 
-class AllCompoundACL(object):
+class AllCompoundACL(SimpleACL):
     """An ACL that allows if all of its subacls allow."""
     """An ACL that allows if all of its subacls allow."""
-    def __init__(self, subacls: List[SimpleACL]):
-        assert subacls is not None
+    def __init__(self,
+                 *,
+                 subacls: Optional[List[SimpleACL]] = None,
+                 order_to_check_allow_deny: Order,
+                 default_answer: bool) -> None:
+        super().__init__(
+            order_to_check_allow_deny = order_to_check_allow_deny,
+            default_answer = default_answer
+        )
         self.subacls = subacls
 
         self.subacls = subacls
 
-    def __call__(self, x: Any):
+    def check_allowed(self, x: Any) -> bool:
+        if self.subacls is None:
+            return False
         return all(acl(x) for acl in self.subacls)
         return all(acl(x) for acl in self.subacls)
+
+    def check_denied(self, x: Any) -> bool:
+        if self.subacls is None:
+            return False
+        return any(not acl(x) for acl in self.subacls)
index 02db0f08e762e9a430c9f03f7ce08a4a0acdb7b6..3799a47ccbc1a34a599cdd443eaf5dfa33a64772 100644 (file)
@@ -5,6 +5,9 @@ import datetime
 import logging
 import os
 
 import logging
 import os
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 logger = logging.getLogger(__name__)
 
 
 logger = logging.getLogger(__name__)
 
 
index 94c8e9c0ecde3347832276d0a2177dd9ee76a410..da421b6f0a35dd2c0c269728f71427750e7e6708 100644 (file)
@@ -7,6 +7,8 @@ import sys
 import time
 import traceback
 
 import time
 import traceback
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
 import argparse_utils
 import config
 
 import argparse_utils
 import config
 
index 58a5e83eb75e8d80f942d59a9309de5f5d4c27f2..e7094f3a5ba6d6c4646a579d2e5e1e47cc8e330a 100644 (file)
--- a/config.py
+++ b/config.py
@@ -71,6 +71,9 @@ import re
 import sys
 from typing import Any, Dict, List
 
 import sys
 from typing import Any, Dict, List
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 # Note: at this point in time, logging hasn't been configured and
 # anything we log will come out the root logger.
 
 # Note: at this point in time, logging hasn't been configured and
 # anything we log will come out the root logger.
 
index d321737106d685e5de8c8395e066d4cc9204883f..fdc533bff82d0a51c2e39ee666ae60882ee6597f 100644 (file)
@@ -2,6 +2,9 @@
 
 """Universal constants."""
 
 
 """Universal constants."""
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 # Date/time based constants
 SECONDS_PER_MINUTE = 60
 SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE
 # Date/time based constants
 SECONDS_PER_MINUTE = 60
 SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE
index ad92ccd5d8d3e672bc0adc55ff2197912a833455..05bee8dee7d95d4124192d0b6a2ee553f7d2a9f7 100755 (executable)
@@ -15,10 +15,13 @@ import pytz
 
 import acl
 import bootstrap
 
 import acl
 import bootstrap
-from decorator_utils import decorate_matching_methods_with
+from datetime_utils import (
+    TimeUnit, n_timeunits_from_base, datetime_to_date, date_to_datetime
+)
 from dateparse.dateparse_utilsLexer import dateparse_utilsLexer  # type: ignore
 from dateparse.dateparse_utilsListener import dateparse_utilsListener  # type: ignore
 from dateparse.dateparse_utilsParser import dateparse_utilsParser  # type: ignore
 from dateparse.dateparse_utilsLexer import dateparse_utilsLexer  # type: ignore
 from dateparse.dateparse_utilsListener import dateparse_utilsListener  # type: ignore
 from dateparse.dateparse_utilsParser import dateparse_utilsParser  # type: ignore
+from decorator_utils import decorate_matching_methods_with
 
 
 logger = logging.getLogger(__name__)
 
 
 logger = logging.getLogger(__name__)
@@ -85,7 +88,7 @@ class RaisingErrorListener(antlr4.DiagnosticErrorListener):
             'exit*',
         ],
         denied_patterns=None,
             'exit*',
         ],
         denied_patterns=None,
-        order_to_check_allow_deny=acl.ACL_ORDER_DENY_ALLOW,
+        order_to_check_allow_deny=acl.Order.DENY_ALLOW,
         default_answer=False
     )
 )
         default_answer=False
     )
 )
@@ -105,7 +108,6 @@ class DateParser(dateparse_utilsListener):
         idea of "now" so that the code can be more easily unittested.
         Leave as None for real use cases.
         """
         idea of "now" so that the code can be more easily unittested.
         Leave as None for real use cases.
         """
-        from datetime_utils import TimeUnit
         self.month_name_to_number = {
             'jan': 1,
             'feb': 2,
         self.month_name_to_number = {
             'jan': 1,
             'feb': 2,
@@ -231,7 +233,6 @@ class DateParser(dateparse_utilsListener):
 
     def _reset(self):
         """Reset at init and between parses."""
 
     def _reset(self):
         """Reset at init and between parses."""
-        from datetime_utils import datetime_to_date
         if self.override_now_for_test_purposes is None:
             self.now_datetime = datetime.datetime.now()
             self.today = datetime.date.today()
         if self.override_now_for_test_purposes is None:
             self.now_datetime = datetime.datetime.now()
             self.today = datetime.date.today()
@@ -259,9 +260,8 @@ class DateParser(dateparse_utilsListener):
         name = name.replace('washi', 'presi')
         return name
 
         name = name.replace('washi', 'presi')
         return name
 
-    def _figure_out_date_unit(self, orig: str) -> int:
+    def _figure_out_date_unit(self, orig: str) -> TimeUnit:
         """Figure out what unit a date expression piece is talking about."""
         """Figure out what unit a date expression piece is talking about."""
-        from datetime_utils import TimeUnit
         if 'month' in orig:
             return TimeUnit.MONTHS
         txt = orig.lower()[:3]
         if 'month' in orig:
             return TimeUnit.MONTHS
         txt = orig.lower()[:3]
@@ -469,9 +469,6 @@ class DateParser(dateparse_utilsListener):
 
     def exitDateExpr(self, ctx: dateparse_utilsParser.DateExprContext) -> None:
         """When we leave the date expression, populate self.date."""
 
     def exitDateExpr(self, ctx: dateparse_utilsParser.DateExprContext) -> None:
         """When we leave the date expression, populate self.date."""
-        from datetime_utils import (
-            n_timeunits_from_base, datetime_to_date, date_to_datetime
-        )
         if 'special' in self.context:
             self.date = self._parse_special_date(self.context['special'])
         else:
         if 'special' in self.context:
             self.date = self._parse_special_date(self.context['special'])
         else:
@@ -514,7 +511,6 @@ class DateParser(dateparse_utilsListener):
 
     def exitTimeExpr(self, ctx: dateparse_utilsParser.TimeExprContext) -> None:
         # Simple time?
 
     def exitTimeExpr(self, ctx: dateparse_utilsParser.TimeExprContext) -> None:
         # Simple time?
-        from datetime_utils import TimeUnit
         self.time = datetime.time(
             self.context['hour'],
             self.context['minute'],
         self.time = datetime.time(
             self.context['hour'],
             self.context['minute'],
@@ -641,7 +637,6 @@ class DateParser(dateparse_utilsListener):
     def exitDeltaTimeFraction(
             self, ctx: dateparse_utilsParser.DeltaTimeFractionContext
     ) -> None:
     def exitDeltaTimeFraction(
             self, ctx: dateparse_utilsParser.DeltaTimeFractionContext
     ) -> None:
-        from datetime_utils import TimeUnit
         try:
             txt = ctx.getText().lower()[:4]
             if txt == 'quar':
         try:
             txt = ctx.getText().lower()[:4]
             if txt == 'quar':
@@ -874,7 +869,6 @@ class DateParser(dateparse_utilsListener):
     def exitNFoosFromTodayAgoExpr(
         self, ctx: dateparse_utilsParser.NFoosFromTodayAgoExprContext
     ) -> None:
     def exitNFoosFromTodayAgoExpr(
         self, ctx: dateparse_utilsParser.NFoosFromTodayAgoExprContext
     ) -> None:
-        from datetime_utils import n_timeunits_from_base
         d = self.now_datetime
         try:
             count = self._get_int(ctx.unsignedInt().getText())
         d = self.now_datetime
         try:
             count = self._get_int(ctx.unsignedInt().getText())
@@ -900,7 +894,6 @@ class DateParser(dateparse_utilsListener):
     def exitDeltaRelativeToTodayExpr(
         self, ctx: dateparse_utilsParser.DeltaRelativeToTodayExprContext
     ) -> None:
     def exitDeltaRelativeToTodayExpr(
         self, ctx: dateparse_utilsParser.DeltaRelativeToTodayExprContext
     ) -> None:
-        from datetime_utils import n_timeunits_from_base
         d = self.now_datetime
         try:
             mod = ctx.thisNextLast()
         d = self.now_datetime
         try:
             mod = ctx.thisNextLast()
@@ -1055,6 +1048,7 @@ class DateParser(dateparse_utilsListener):
             pass
 
 
             pass
 
 
 def main() -> None:
     parser = DateParser()
     for line in sys.stdin:
 def main() -> None:
     parser = DateParser()
     for line in sys.stdin:
@@ -1072,5 +1066,4 @@ def main() -> None:
 
 
 if __name__ == "__main__":
 
 
 if __name__ == "__main__":
-    main = bootstrap.initialize(main)
     main()
     main()
index 0b94283b01df595300ecc448f108303c2dba2b2e..795b427c31b4e57a4551aab26badd1f81b68c9a2 100644 (file)
@@ -6,7 +6,7 @@ import datetime
 import enum
 import logging
 import re
 import enum
 import logging
 import re
-from typing import NewType, Tuple
+from typing import Any, NewType, Tuple
 
 import holidays  # type: ignore
 import pytz
 
 import holidays  # type: ignore
 import pytz
@@ -77,12 +77,28 @@ class TimeUnit(enum.Enum):
     MONTHS = 13
     YEARS = 14
 
     MONTHS = 13
     YEARS = 14
 
+    @classmethod
+    def is_valid(cls, value: Any):
+        if type(value) is int:
+            print("int")
+            return value in cls._value2member_map_
+        elif type(value) is TimeUnit:
+            print("TimeUnit")
+            return value.value in cls._value2member_map_
+        elif type(value) is str:
+            print("str")
+            return value in cls._member_names_
+        else:
+            print(type(value))
+            return False
+
 
 def n_timeunits_from_base(
     count: int,
     unit: TimeUnit,
     base: datetime.datetime
 ) -> datetime.datetime:
 
 def n_timeunits_from_base(
     count: int,
     unit: TimeUnit,
     base: datetime.datetime
 ) -> datetime.datetime:
+    assert TimeUnit.is_valid(unit)
     if count == 0:
         return base
 
     if count == 0:
         return base
 
index 375cbad436feca20d57750b8276537c94f66f060..2817239c88c2396b0e5dcc56e7c535b8afdd99d9 100644 (file)
@@ -18,6 +18,8 @@ import traceback
 from typing import Callable, Optional
 import warnings
 
 from typing import Callable, Optional
 import warnings
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
 import exceptions
 
 
 import exceptions
 
 
index f2af66c4cc3ee908767af2b024a37bba096ff5d9..4b12279b5726a2eb3a97eed1c20b1f708870143b 100644 (file)
@@ -3,6 +3,9 @@
 from abc import ABC, abstractmethod
 from typing import Any, Generic, TypeVar
 
 from abc import ABC, abstractmethod
 from typing import Any, Generic, TypeVar
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 T = TypeVar('T')
 
 
 T = TypeVar('T')
 
 
index 3e0a2d080b56742d71d1d02ee45ac13fd8d5a146..59aa262d786a11297e1462ff782ed2e47034c16d 100644 (file)
@@ -1,5 +1,8 @@
 #!/usr/bin/env python3
 
 #!/usr/bin/env python3
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 class PreconditionException(AssertionError):
     pass
 
 class PreconditionException(AssertionError):
     pass
 
index cc287bb5819e0f31db1e504f0a136b1333f79dfe..c5a0d93e6908838c1f382b386a64957f3c2ea3fc 100644 (file)
@@ -3,6 +3,9 @@
 import itertools
 import logging
 
 import itertools
 import logging
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 logger = logging.getLogger(__name__)
 generators = {}
 
 logger = logging.getLogger(__name__)
 generators = {}
 
index ba5270fad87a1e62ccc8be99fe7eaba786583dc8..700bfabcf9b9bbba8b28ca199253de40296a6ccc 100644 (file)
@@ -10,6 +10,8 @@ import os
 import pytz
 import sys
 
 import pytz
 import sys
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
 import argparse_utils
 import config
 
 import argparse_utils
 import config
 
index e4832d43d5b1674988628e5dae43a67cf8ed0565..1c95973f48ab3bc3a0c9bcd90fa13498f851b8a9 100644 (file)
@@ -6,6 +6,8 @@ import concurrent.futures as fut
 import time
 from typing import Callable, List, TypeVar
 
 import time
 from typing import Callable, List, TypeVar
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
 from deferred_operand import DeferredOperand
 import id_generator
 
 from deferred_operand import DeferredOperand
 import id_generator
 
index 810aedf48bd7bd925132a1c5b940cf62c09f1462..4c1cf21457d783f9baeaf886cdc68c7c8647917b 100755 (executable)
@@ -13,7 +13,7 @@ class TestSimpleACL(unittest.TestCase):
         even = acl.SetBasedACL(
             allow_set = set([2, 4, 6, 8, 10]),
             deny_set = set([1, 3, 5, 7, 9]),
         even = acl.SetBasedACL(
             allow_set = set([2, 4, 6, 8, 10]),
             deny_set = set([1, 3, 5, 7, 9]),
-            order_to_check_allow_deny = acl.ACL_ORDER_ALLOW_DENY,
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
             default_answer = False
         )
         self.assertTrue(even(2))
             default_answer = False
         )
         self.assertTrue(even(2))
@@ -23,12 +23,12 @@ class TestSimpleACL(unittest.TestCase):
     def test_wildcard_based_acl(self):
         a_or_b = acl.StringWildcardBasedACL(
             allowed_patterns = ['a*', 'b*'],
     def test_wildcard_based_acl(self):
         a_or_b = acl.StringWildcardBasedACL(
             allowed_patterns = ['a*', 'b*'],
-            order_to_check_allow_deny = acl.ACL_ORDER_ALLOW_DENY,
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
             default_answer = False
         )
         self.assertTrue(a_or_b('aardvark'))
             default_answer = False
         )
         self.assertTrue(a_or_b('aardvark'))
-        self.assertTrue(a_or_b('bubblegum'))
-        self.assertFalse(a_or_b('charlie'))
+        self.assertTrue(a_or_b('baboon'))
+        self.assertFalse(a_or_b('cheetah'))
 
     def test_re_based_acl(self):
         weird = acl.StringREBasedACL(
 
     def test_re_based_acl(self):
         weird = acl.StringREBasedACL(
@@ -36,12 +36,56 @@ class TestSimpleACL(unittest.TestCase):
                 re.compile('^a.*a$'),
                 re.compile('^b.*b$')
             ],
                 re.compile('^a.*a$'),
                 re.compile('^b.*b$')
             ],
-            order_to_check_allow_deny = acl.ACL_ORDER_ALLOW_DENY,
+            order_to_check_allow_deny = acl.Order.DENY_ALLOW,
             default_answer = True
         )
         self.assertTrue(weird('aardvark'))
         self.assertFalse(weird('anaconda'))
             default_answer = True
         )
         self.assertTrue(weird('aardvark'))
         self.assertFalse(weird('anaconda'))
-        self.assertFalse(weird('beelzebub'))
+        self.assertFalse(weird('blackneb'))
+        self.assertTrue(weird('crow'))
+
+    def test_compound_acls_disjunction(self):
+        a_b_c = acl.StringWildcardBasedACL(
+            allowed_patterns = ['a*', 'b*', 'c*'],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False
+        )
+        c_d_e = acl.StringWildcardBasedACL(
+            allowed_patterns = ['c*', 'd*', 'e*'],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False
+        )
+        disjunction = acl.AnyCompoundACL(
+            subacls = [a_b_c, c_d_e],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False,
+        )
+        self.assertTrue(disjunction('aardvark'))
+        self.assertTrue(disjunction('caribou'))
+        self.assertTrue(disjunction('eagle'))
+        self.assertFalse(disjunction('newt'))
+
+    def test_compound_acls_conjunction(self):
+        a_b_c = acl.StringWildcardBasedACL(
+            allowed_patterns = ['a*', 'b*', 'c*'],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False
+        )
+        c_d_e = acl.StringWildcardBasedACL(
+            allowed_patterns = ['c*', 'd*', 'e*'],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False
+        )
+        conjunction = acl.AllCompoundACL(
+            subacls = [a_b_c, c_d_e],
+            order_to_check_allow_deny = acl.Order.ALLOW_DENY,
+            default_answer = False,
+        )
+        self.assertFalse(conjunction('aardvark'))
+        self.assertTrue(conjunction('caribou'))
+        self.assertTrue(conjunction('condor'))
+        self.assertFalse(conjunction('eagle'))
+        self.assertFalse(conjunction('newt'))
 
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':
index af6e0e1abe840946a8754e426ca93bd653545ca5..bb15c034b9e4e02f273b98dbc77410072878e089 100644 (file)
@@ -6,6 +6,9 @@ import os
 import threading
 from typing import Callable, Optional, Tuple
 
 import threading
 from typing import Callable, Optional, Tuple
 
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
+
 logger = logging.getLogger(__name__)
 
 
 logger = logging.getLogger(__name__)