Skip to content

Commit 3fdfdda

Browse files
committed
Refactor Sessions to clarify their usage
DBSession has been renamed to HandlerSession. The following exist now: - HandlerSession: a DB session scoped to the current handler; should therefore only be used inside handlers. - ThreadSession: a DB session scoped to the current thread; should be used outside of handlers, e.g. when firing off tasks in threads, or inside of services. A special HandlerSession, with knowledge of the current user, and that verifies permissions on commit, is available as `self.Session` on the base handler. This is the *preferred way of accessing* HandlerSession. To make it easier to get hold of the current engine, the Sessions each have a `.engine` attribute: `HandlerSession.engine`. This avoids having to access the engine as `HandlerSession.session_factory.kw["bind"]`.
1 parent 57b7faa commit 3fdfdda

File tree

9 files changed

+127
-85
lines changed

9 files changed

+127
-85
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repos:
2323
pass_filenames: true
2424
exclude: baselayer|node_modules|static
2525
- repo: https://github.com/pycqa/flake8
26-
rev: 3.8.4
26+
rev: 6.1.0
2727
hooks:
2828
- id: flake8
2929
pass_filenames: true

app/access.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from sqlalchemy.orm import joinedload
66

77
from baselayer.app.custom_exceptions import AccessError # noqa: F401
8-
from baselayer.app.models import DBSession, Role, Token, User # noqa: F401
8+
from baselayer.app.models import HandlerSession, Role, Token, User # noqa: F401
99

1010

1111
def auth_or_token(method):
@@ -26,7 +26,7 @@ def wrapper(self, *args, **kwargs):
2626
token_header = self.request.headers.get("Authorization", None)
2727
if token_header is not None and token_header.startswith("token "):
2828
token_id = token_header.replace("token", "").strip()
29-
with DBSession() as session:
29+
with HandlerSession() as session:
3030
token = session.scalars(
3131
sa.select(Token)
3232
.options(

app/handlers/base.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
from ..env import load_env
2323
from ..flow import Flow
2424
from ..json_util import to_json
25-
from ..models import DBSession, User, VerifiedSession, bulk_verify, session_context_id
25+
from ..models import (
26+
HandlerSession,
27+
User,
28+
VerifiedSession,
29+
bulk_verify,
30+
session_context_id,
31+
)
2632

2733
env, cfg = load_env()
2834
log = make_log("basehandler")
@@ -49,7 +55,7 @@ def get_current_user(self):
4955
user_id = int(self.user_id())
5056
oauth_uid = self.get_secure_cookie("user_oauth_uid")
5157
if user_id and oauth_uid:
52-
with DBSession() as session:
58+
with HandlerSession() as session:
5359
try:
5460
user = session.scalars(
5561
sqlalchemy.select(User).where(User.id == user_id)
@@ -74,7 +80,7 @@ def get_current_user(self):
7480
return None
7581

7682
def login_user(self, user):
77-
with DBSession() as session:
83+
with HandlerSession() as session:
7884
try:
7985
self.set_secure_cookie("user_id", str(user.id))
8086
user = session.scalars(
@@ -120,7 +126,7 @@ def log_exception(self, typ=None, value=None, tb=None):
120126
)
121127

122128
def on_finish(self):
123-
DBSession.remove()
129+
HandlerSession.remove()
124130

125131

126132
class BaseHandler(PSABaseHandler):
@@ -153,7 +159,7 @@ def Session(self):
153159
# must merge the user object with the current session
154160
# ref: https://docs.sqlalchemy.org/en/14/orm/session_basics.html#adding-new-or-existing-items
155161
session.add(self.current_user)
156-
session.bind = DBSession.session_factory.kw["bind"]
162+
session.bind = HandlerSession.engine
157163
yield session
158164

159165
def verify_permissions(self):
@@ -164,20 +170,20 @@ def verify_permissions(self):
164170
"""
165171

166172
# get items to be inserted
167-
new_rows = [row for row in DBSession().new]
173+
new_rows = [row for row in HandlerSession().new]
168174

169175
# get items to be updated
170176
updated_rows = [
171-
row for row in DBSession().dirty if DBSession().is_modified(row)
177+
row for row in HandlerSession().dirty if HandlerSession().is_modified(row)
172178
]
173179

174180
# get items to be deleted
175-
deleted_rows = [row for row in DBSession().deleted]
181+
deleted_rows = [row for row in HandlerSession().deleted]
176182

177183
# get items that were read
178184
read_rows = [
179185
row
180-
for row in set(DBSession().identity_map.values())
186+
for row in set(HandlerSession().identity_map.values())
181187
- (set(updated_rows) | set(new_rows) | set(deleted_rows))
182188
]
183189

@@ -194,15 +200,15 @@ def verify_permissions(self):
194200
# update transaction state in DB, but don't commit yet. this updates
195201
# or adds rows in the database and uses their new state in joins,
196202
# for permissions checking purposes.
197-
DBSession().flush()
203+
HandlerSession().flush()
198204
bulk_verify("create", new_rows, self.current_user)
199205

200206
def verify_and_commit(self):
201207
"""Verify permissions on the current database session and commit if
202208
successful, otherwise raise an AccessError.
203209
"""
204210
self.verify_permissions()
205-
DBSession().commit()
211+
HandlerSession().commit()
206212

207213
def prepare(self):
208214
self.cfg = self.application.cfg
@@ -225,7 +231,7 @@ def prepare(self):
225231
N = 5
226232
for i in range(1, N + 1):
227233
try:
228-
assert DBSession.session_factory.kw["bind"] is not None
234+
assert HandlerSession.engine is not None
229235
except Exception as e:
230236
if i == N:
231237
raise e

app/model_util.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ def status(message):
2121
else:
2222
print(f"\r[✓] {message}")
2323
finally:
24-
models.DBSession().commit()
24+
models.HandlerSession().commit()
2525

2626

2727
def drop_tables():
28-
conn = models.DBSession.session_factory.kw["bind"]
29-
print(f"Dropping tables on database {conn.url.database}")
28+
engine = models.HandlerSession.engine
29+
print(f"Dropping tables on database {engine.url.database}")
3030
meta = sa.MetaData()
31-
meta.reflect(bind=conn)
32-
meta.drop_all(bind=conn)
31+
meta.reflect(bind=engine)
32+
meta.drop_all(bind=engine)
3333

3434

3535
def create_tables(retry=5, add=True):
@@ -45,17 +45,16 @@ def create_tables(retry=5, add=True):
4545
tables.
4646
4747
"""
48-
conn = models.DBSession.session_factory.kw["bind"]
4948
tables = models.Base.metadata.sorted_tables
5049
if tables and not add:
5150
print("Existing tables found; not creating additional tables")
5251
return
5352

5453
for i in range(1, retry + 1):
5554
try:
56-
conn = models.DBSession.session_factory.kw["bind"]
57-
print(f"Creating tables on database {conn.url.database}")
58-
models.Base.metadata.create_all(conn)
55+
engine = models.HandlerSession.engine
56+
print(f"Creating tables on database {engine.url.database}")
57+
models.Base.metadata.create_all(engine)
5958

6059
table_list = ", ".join(list(models.Base.metadata.tables.keys()))
6160
print(f"Refreshed tables: {table_list}")

0 commit comments

Comments
 (0)