Skip to content

Commit 82be63d

Browse files
authored
Merge pull request #164 from ticosax/annotations-II
Introduce typing annotations
2 parents 52ece8e + 48fd8d1 commit 82be63d

File tree

3 files changed

+35
-23
lines changed

3 files changed

+35
-23
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ global-exclude *.py[cod] __pycache__
88
global-exclude .coverage
99
global-exclude .DS_Store
1010
exclude .pre-commit-config.yaml tox.ini
11+
recursive-include rules *.typed

rules/predicates.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import threading
44
from functools import partial, update_wrapper
55
from inspect import getfullargspec, isfunction, ismethod
6+
from typing import Any, Callable, List, Optional, Tuple, Union
67

78
logger = logging.getLogger("rules")
89

910

10-
def assert_has_kwonlydefaults(fn, msg):
11+
def assert_has_kwonlydefaults(fn: Callable[..., Any], msg: str) -> None:
1112
argspec = getfullargspec(fn)
1213
if hasattr(argspec, "kwonlyargs"):
1314
if not argspec.kwonlyargs:
@@ -19,21 +20,21 @@ def assert_has_kwonlydefaults(fn, msg):
1920

2021

2122
class Context(dict):
22-
def __init__(self, args):
23+
def __init__(self, args: Tuple[Any, ...]) -> None:
2324
super(Context, self).__init__()
2425
self.args = args
2526

2627

2728
class localcontext(threading.local):
28-
def __init__(self):
29-
self.stack = []
29+
def __init__(self) -> None:
30+
self.stack: List[Context] = []
3031

3132

3233
_context = localcontext()
3334

3435

3536
class NoValueSentinel(object):
36-
def __bool__(self):
37+
def __bool__(self) -> bool:
3738
return False
3839

3940
__nonzero__ = __bool__ # python 2
@@ -45,7 +46,17 @@ def __bool__(self):
4546

4647

4748
class Predicate(object):
48-
def __init__(self, fn, name=None, bind=False):
49+
fn: Callable[..., Any]
50+
num_args: int
51+
var_args: bool
52+
name: str
53+
54+
def __init__(
55+
self,
56+
fn: Union["Predicate", Callable[..., Any]],
57+
name: Optional[str] = None,
58+
bind: bool = False,
59+
) -> None:
4960
# fn can be a callable with any of the following signatures:
5061
# - fn(obj=None, target=None)
5162
# - fn(obj=None)
@@ -98,13 +109,13 @@ def __init__(self, fn, name=None, bind=False):
98109
self.name = name or fn.__name__
99110
self.bind = bind
100111

101-
def __repr__(self):
112+
def __repr__(self) -> str:
102113
return "<%s:%s object at %s>" % (type(self).__name__, str(self), hex(id(self)))
103114

104-
def __str__(self):
115+
def __str__(self) -> str:
105116
return self.name
106117

107-
def __call__(self, *args, **kwargs):
118+
def __call__(self, *args, **kwargs) -> Any:
108119
# this method is defined as variadic in order to not mask the
109120
# underlying callable's signature that was most likely decorated
110121
# as a predicate. internally we consistently call ``_apply`` that
@@ -114,7 +125,7 @@ def __call__(self, *args, **kwargs):
114125
return self.fn(*args, **kwargs)
115126

116127
@property
117-
def context(self):
128+
def context(self) -> Optional[Context]:
118129
"""
119130
The currently active invocation context. A new context is created as a
120131
result of invoking ``test()`` and is only valid for the duration of
@@ -150,7 +161,7 @@ def context(self):
150161
except IndexError:
151162
return None
152163

153-
def test(self, obj=NO_VALUE, target=NO_VALUE):
164+
def test(self, obj: Any = NO_VALUE, target: Any = NO_VALUE) -> bool:
154165
"""
155166
The canonical method to invoke predicates.
156167
"""
@@ -162,25 +173,25 @@ def test(self, obj=NO_VALUE, target=NO_VALUE):
162173
finally:
163174
_context.stack.pop()
164175

165-
def __and__(self, other):
176+
def __and__(self, other) -> "Predicate":
166177
def AND(*args):
167178
return self._combine(other, operator.and_, args)
168179

169180
return type(self)(AND, "(%s & %s)" % (self.name, other.name))
170181

171-
def __or__(self, other):
182+
def __or__(self, other) -> "Predicate":
172183
def OR(*args):
173184
return self._combine(other, operator.or_, args)
174185

175186
return type(self)(OR, "(%s | %s)" % (self.name, other.name))
176187

177-
def __xor__(self, other):
188+
def __xor__(self, other) -> "Predicate":
178189
def XOR(*args):
179190
return self._combine(other, operator.xor, args)
180191

181192
return type(self)(XOR, "(%s ^ %s)" % (self.name, other.name))
182193

183-
def __invert__(self):
194+
def __invert__(self) -> "Predicate":
184195
def INVERT(*args):
185196
result = self._apply(*args)
186197
return None if result is None else not result
@@ -208,7 +219,7 @@ def _combine(self, other, op, args):
208219

209220
return op(self_result, other_result)
210221

211-
def _apply(self, *args):
222+
def _apply(self, *args) -> Optional[bool]:
212223
# Internal method that is used to invoke the predicate with the
213224
# proper number of positional arguments, inside the current
214225
# invocation context.
@@ -268,12 +279,12 @@ def inner(fn):
268279
always_deny = predicate(lambda: False, name="always_deny")
269280

270281

271-
def is_bool_like(obj):
282+
def is_bool_like(obj) -> bool:
272283
return hasattr(obj, "__bool__") or hasattr(obj, "__nonzero__")
273284

274285

275286
@predicate
276-
def is_authenticated(user):
287+
def is_authenticated(user) -> bool:
277288
if not hasattr(user, "is_authenticated"):
278289
return False # not a user model
279290
if not is_bool_like(user.is_authenticated): # pragma: no cover
@@ -283,27 +294,27 @@ def is_authenticated(user):
283294

284295

285296
@predicate
286-
def is_superuser(user):
297+
def is_superuser(user) -> bool:
287298
if not hasattr(user, "is_superuser"):
288299
return False # swapped user model, doesn't support is_superuser
289300
return user.is_superuser
290301

291302

292303
@predicate
293-
def is_staff(user):
304+
def is_staff(user) -> bool:
294305
if not hasattr(user, "is_staff"):
295306
return False # swapped user model, doesn't support is_staff
296307
return user.is_staff
297308

298309

299310
@predicate
300-
def is_active(user):
311+
def is_active(user) -> bool:
301312
if not hasattr(user, "is_active"):
302313
return False # swapped user model, doesn't support is_active
303314
return user.is_active
304315

305316

306-
def is_group_member(*groups):
317+
def is_group_member(*groups) -> Callable[..., Any]:
307318
assert len(groups) > 0, "You must provide at least one group name"
308319

309320
if len(groups) > 3:
@@ -314,7 +325,7 @@ def is_group_member(*groups):
314325
name = "is_group_member:%s" % ",".join(g)
315326

316327
@predicate(name)
317-
def fn(user):
328+
def fn(user) -> bool:
318329
if not hasattr(user, "groups"):
319330
return False # swapped user model, doesn't support groups
320331
if not hasattr(user, "_group_names_cache"): # pragma: no cover

rules/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)