"""
Default scope implementation relying on bit shifting. See
:attr:`provider.constants.SCOPES` for the list of available scopes.
Scopes can be combined, such as ``"read write"``. Note that a single
``"write"`` scope is *not* the same as ``"read write"``.
See :class:`provider.scope.to_int` on how scopes are combined.
"""
from .constants import SCOPES
SCOPE_NAMES = [(name, name) for (value, name) in SCOPES]
SCOPE_NAME_DICT = dict([(name, value) for (value, name) in SCOPES])
SCOPE_VALUE_DICT = dict([(value, name) for (value, name) in SCOPES])
[docs]def check(wants, has):
"""
Check if a desired scope ``wants`` is part of an available scope ``has``.
Returns ``False`` if not, return ``True`` if yes.
:example:
If a list of scopes such as
::
READ = 1 << 1
WRITE = 1 << 2
READ_WRITE = READ | WRITE
SCOPES = (
(READ, 'read'),
(WRITE, 'write'),
(READ_WRITE, 'read+write'),
)
is defined, we can check if a given scope is part of another:
::
>>> from provider import scope
>>> scope.check(READ, READ)
True
>>> scope.check(WRITE, READ)
False
>>> scope.check(WRITE, WRITE)
True
>>> scope.check(READ, WRITE)
False
>>> scope.check(READ, READ_WRITE)
True
>>> scope.check(WRITE, READ_WRITE)
True
"""
if wants & has == 0:
return False
if wants & has < wants:
return False
return True
[docs]def to_names(scope):
"""
Returns a list of scope names as defined in
:attr:`provider.constants.SCOPES` for a given scope integer.
>>> assert ['read', 'write'] == provider.scope.names(provider.constants.READ_WRITE)
"""
return [
name
for (name, value) in SCOPE_NAME_DICT.iteritems()
if check(value, scope)
]
# Keep it compatible
names = to_names
[docs]def to_int(*names, **kwargs):
"""
Turns a list of scope names into an integer value.
::
>>> scope.to_int('read')
2
>>> scope.to_int('write')
6
>>> scope.to_int('read', 'write')
6
>>> scope.to_int('invalid')
0
>>> scope.to_int('invalid', default = 1)
1
"""
return reduce(lambda prev, next: (prev | SCOPE_NAME_DICT.get(next, 0)),
names, kwargs.pop('default', 0))