summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2021-07-09 18:31:02 -0700
committerScott Gasch <[email protected]>2021-07-09 18:31:02 -0700
commit09e6d10face80d98a4578ff54192b5c8bec007d7 (patch)
tree1565e645e739ff2f607730029c91055d29c02aa5
parent11eeb8574b7b4620ac6fd440cb251f8aa2458f5b (diff)
ACL uses enums, some more tests, other stuff.
-rw-r--r--acl.py102
-rw-r--r--argparse_utils.py3
-rw-r--r--bootstrap.py2
-rw-r--r--config.py3
-rw-r--r--constants.py3
-rwxr-xr-xdateparse/dateparse_utils.py21
-rw-r--r--datetime_utils.py18
-rw-r--r--decorator_utils.py2
-rw-r--r--deferred_operand.py3
-rw-r--r--exceptions.py3
-rw-r--r--id_generator.py3
-rw-r--r--logging_utils.py2
-rw-r--r--smart_future.py2
-rwxr-xr-xtests/acl_test.py56
-rw-r--r--thread_utils.py3
15 files changed, 185 insertions, 41 deletions
diff --git a/acl.py b/acl.py
index 5040304..e6bb903 100644
--- a/acl.py
+++ b/acl.py
@@ -1,17 +1,25 @@
#!/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
+# This module is commonly used by others in here and should avoid
+# taking any unnecessary dependencies back on them.
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):
@@ -20,22 +28,22 @@ class SimpleACL(ABC):
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 (
- ACL_ORDER_ALLOW_DENY, ACL_ORDER_DENY_ALLOW
+ Order.ALLOW_DENY, Order.DENY_ALLOW
):
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}')
- 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.')
@@ -44,7 +52,7 @@ class SimpleACL(ABC):
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.')
@@ -76,7 +84,7 @@ class SetBasedACL(SimpleACL):
*,
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,
@@ -96,13 +104,39 @@ class SetBasedACL(SimpleACL):
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,
- 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,
@@ -128,7 +162,7 @@ class StringWildcardBasedACL(PredicateListBasedACL):
*,
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:
@@ -158,7 +192,7 @@ class StringREBasedACL(PredicateListBasedACL):
*,
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:
@@ -182,21 +216,49 @@ class StringREBasedACL(PredicateListBasedACL):
)
-class AnyCompoundACL(object):
+class AnyCompoundACL(SimpleACL):
"""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
- 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)
+ 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."""
- 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
- 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)
+
+ def check_denied(self, x: Any) -> bool:
+ if self.subacls is None:
+ return False
+ return any(not acl(x) for acl in self.subacls)
diff --git a/argparse_utils.py b/argparse_utils.py
index 02db0f0..3799a47 100644
--- a/argparse_utils.py
+++ b/argparse_utils.py
@@ -5,6 +5,9 @@ import datetime
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__)
diff --git a/bootstrap.py b/bootstrap.py
index 94c8e9c..da421b6 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -7,6 +7,8 @@ import sys
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
diff --git a/config.py b/config.py
index 58a5e83..e7094f3 100644
--- a/config.py
+++ b/config.py
@@ -71,6 +71,9 @@ import re
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.
diff --git a/constants.py b/constants.py
index d321737..fdc533b 100644
--- a/constants.py
+++ b/constants.py
@@ -2,6 +2,9 @@
"""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
diff --git a/dateparse/dateparse_utils.py b/dateparse/dateparse_utils.py
index ad92ccd..05bee8d 100755
--- a/dateparse/dateparse_utils.py
+++ b/dateparse/dateparse_utils.py
@@ -15,10 +15,13 @@ import pytz
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 decorator_utils import decorate_matching_methods_with
logger = logging.getLogger(__name__)
@@ -85,7 +88,7 @@ class RaisingErrorListener(antlr4.DiagnosticErrorListener):
'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
)
)
@@ -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.
"""
- from datetime_utils import TimeUnit
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."""
- 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()
@@ -259,9 +260,8 @@ class DateParser(dateparse_utilsListener):
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."""
- from datetime_utils import TimeUnit
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."""
- 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:
@@ -514,7 +511,6 @@ class DateParser(dateparse_utilsListener):
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'],
@@ -641,7 +637,6 @@ class DateParser(dateparse_utilsListener):
def exitDeltaTimeFraction(
self, ctx: dateparse_utilsParser.DeltaTimeFractionContext
) -> None:
- from datetime_utils import TimeUnit
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:
- from datetime_utils import n_timeunits_from_base
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:
- from datetime_utils import n_timeunits_from_base
d = self.now_datetime
try:
mod = ctx.thisNextLast()
@@ -1055,6 +1048,7 @@ class DateParser(dateparse_utilsListener):
pass
def main() -> None:
parser = DateParser()
for line in sys.stdin:
@@ -1072,5 +1066,4 @@ def main() -> None:
if __name__ == "__main__":
- main = bootstrap.initialize(main)
main()
diff --git a/datetime_utils.py b/datetime_utils.py
index 0b94283..795b427 100644
--- a/datetime_utils.py
+++ b/datetime_utils.py
@@ -6,7 +6,7 @@ import datetime
import enum
import logging
import re
-from typing import NewType, Tuple
+from typing import Any, NewType, Tuple
import holidays # type: ignore
import pytz
@@ -77,12 +77,28 @@ class TimeUnit(enum.Enum):
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:
+ assert TimeUnit.is_valid(unit)
if count == 0:
return base
diff --git a/decorator_utils.py b/decorator_utils.py
index 375cbad..2817239 100644
--- a/decorator_utils.py
+++ b/decorator_utils.py
@@ -18,6 +18,8 @@ import traceback
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
diff --git a/deferred_operand.py b/deferred_operand.py
index f2af66c..4b12279 100644
--- a/deferred_operand.py
+++ b/deferred_operand.py
@@ -3,6 +3,9 @@
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')
diff --git a/exceptions.py b/exceptions.py
index 3e0a2d0..59aa262 100644
--- a/exceptions.py
+++ b/exceptions.py
@@ -1,5 +1,8 @@
#!/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
diff --git a/id_generator.py b/id_generator.py
index cc287bb..c5a0d93 100644
--- a/id_generator.py
+++ b/id_generator.py
@@ -3,6 +3,9 @@
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 = {}
diff --git a/logging_utils.py b/logging_utils.py
index ba5270f..700bfab 100644
--- a/logging_utils.py
+++ b/logging_utils.py
@@ -10,6 +10,8 @@ import os
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
diff --git a/smart_future.py b/smart_future.py
index e4832d4..1c95973 100644
--- a/smart_future.py
+++ b/smart_future.py
@@ -6,6 +6,8 @@ import concurrent.futures as fut
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
diff --git a/tests/acl_test.py b/tests/acl_test.py
index 810aedf..4c1cf21 100755
--- a/tests/acl_test.py
+++ b/tests/acl_test.py
@@ -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]),
- 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))
@@ -23,12 +23,12 @@ class TestSimpleACL(unittest.TestCase):
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'))
- 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(
@@ -36,12 +36,56 @@ class TestSimpleACL(unittest.TestCase):
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'))
- 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__':
diff --git a/thread_utils.py b/thread_utils.py
index af6e0e1..bb15c03 100644
--- a/thread_utils.py
+++ b/thread_utils.py
@@ -6,6 +6,9 @@ import os
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__)