implement UserDict.get

behaves more like one would expect (same as try get-key, except: return default)
without relying on cache presence or underlying key type (integer only)
This commit is contained in:
Min RK
2020-06-04 10:20:50 +02:00
parent fd28e224f2
commit aba55cc093
3 changed files with 74 additions and 4 deletions

View File

@@ -676,9 +676,10 @@ class BaseHandler(RequestHandler):
raise ValueError("Username doesn't match! %s != %s" % (username, user.name))
if user is None:
new_user = username not in self.users
user = self.user_from_username(username)
user = self.find_user(username)
new_user = user is None
if new_user:
user = self.user_from_username(username)
await maybe_future(self.authenticator.add_user(user))
# Only set `admin` if the authenticator returned an explicit value.
if admin is not None and admin != user.admin:

View File

@@ -0,0 +1,22 @@
import pytest
from ..user import UserDict
from .utils import add_user
@pytest.mark.parametrize("attr", ["self", "id", "name"])
async def test_userdict_get(db, attr):
u = add_user(db, name="rey", app=False)
userdict = UserDict(db_factory=lambda: db, settings={})
if attr == "self":
key = u
else:
key = getattr(u, attr)
# `in` checks cache only
assert key not in userdict
assert userdict.get(key)
assert userdict.get(key).id == u.id
# `in` should find it now
assert key in userdict

View File

@@ -34,7 +34,23 @@ from .utils import url_path_join
class UserDict(dict):
"""Like defaultdict, but for users
Getting by a user id OR an orm.User instance returns a User wrapper around the orm user.
Users can be retrieved by:
- integer database id
- orm.User object
- username str
A User wrapper object is always returned.
This dict contains at least all active users,
but not necessarily all users in the database.
Checking `key in userdict` returns whether
an item is already in the cache,
*not* whether it is in the database.
.. versionchanged:: 1.2
``'username' in userdict`` pattern is now supported
"""
def __init__(self, db_factory, settings):
@@ -57,11 +73,28 @@ class UserDict(dict):
return self[orm_user.id]
def __contains__(self, key):
"""key in userdict checks presence in the cache
it does not check if the user is in the database
"""
if isinstance(key, (User, orm.User)):
key = key.id
elif isinstance(key, str):
# username lookup, O(N)
for user in self.values():
if user.name == key:
key = user.id
break
return dict.__contains__(self, key)
def __getitem__(self, key):
"""UserDict allows retrieval of user by any of:
- User object
- orm.User object
- username (str)
- orm.User.id int (actual key used in underlying dict)
"""
if isinstance(key, User):
key = key.id
elif isinstance(key, str):
@@ -69,7 +102,7 @@ class UserDict(dict):
if orm_user is None:
raise KeyError("No such user: %s" % key)
else:
key = orm_user
key = orm_user.id
if isinstance(key, orm.User):
# users[orm_user] returns User(orm_user)
orm_user = key
@@ -92,6 +125,20 @@ class UserDict(dict):
else:
raise KeyError(repr(key))
def get(self, key, default=None):
"""Retrieve a User object if it can be found, else default
Lookup can be by User object, id, or name
.. versionchanged:: 1.2
``get()`` accesses the database instead of just the cache by integer id,
so is equivalent to catching KeyErrors on attempted lookup.
"""
try:
return self[key]
except KeyError:
return default
def __delitem__(self, key):
user = self[key]
for orm_spawner in user.orm_user._orm_spawners: