Skip to content

Commit

Permalink
Merge pull request #7656 from tdesveaux/typing/db/schedulers
Browse files Browse the repository at this point in the history
typing: Add SchedulerModel dataclass
  • Loading branch information
p12tic committed May 21, 2024
2 parents ebb7390 + 2c43fef commit 3a160e3
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 97 deletions.
22 changes: 14 additions & 8 deletions master/buildbot/data/schedulers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#
# Copyright Buildbot Team Members

from __future__ import annotations

from typing import TYPE_CHECKING

from twisted.internet import defer

Expand All @@ -21,17 +24,20 @@
from buildbot.data import types
from buildbot.db.schedulers import SchedulerAlreadyClaimedError

if TYPE_CHECKING:
from buildbot.db.schedulers import SchedulerModel


class Db2DataMixin:
@defer.inlineCallbacks
def db2data(self, dbdict):
def db2data(self, dbdict: SchedulerModel):
master = None
if dbdict['masterid'] is not None:
master = yield self.master.data.get(('masters', dbdict['masterid']))
if dbdict.masterid is not None:
master = yield self.master.data.get(('masters', dbdict.masterid))
data = {
'schedulerid': dbdict['id'],
'name': dbdict['name'],
'enabled': dbdict['enabled'],
'schedulerid': dbdict.id,
'name': dbdict.name,
'enabled': dbdict.enabled,
'master': master,
}
return data
Expand All @@ -48,7 +54,7 @@ class SchedulerEndpoint(Db2DataMixin, base.Endpoint):
def get(self, resultSpec, kwargs):
dbdict = yield self.master.db.schedulers.getScheduler(kwargs['schedulerid'])
if 'masterid' in kwargs:
if dbdict['masterid'] != kwargs['masterid']:
if dbdict.masterid != kwargs['masterid']:
return None
return (yield self.db2data(dbdict)) if dbdict else None

Expand Down Expand Up @@ -135,4 +141,4 @@ def trapAlreadyClaimedError(why):
def _masterDeactivated(self, masterid):
schedulers = yield self.master.db.schedulers.getSchedulers(masterid=masterid)
for sch in schedulers:
yield self.master.db.schedulers.setSchedulerMaster(sch['id'], None)
yield self.master.db.schedulers.setSchedulerMaster(sch.id, None)
104 changes: 74 additions & 30 deletions master/buildbot/db/schedulers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,67 @@
#
# Copyright Buildbot Team Members


from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

import sqlalchemy as sa
import sqlalchemy.exc
from twisted.internet import defer

from buildbot.db import NULL
from buildbot.db import base
from buildbot.warnings import warn_deprecated

if TYPE_CHECKING:
from typing import Literal


class SchedulerAlreadyClaimedError(Exception):
pass


@dataclass
class SchedulerModel:
id: int
name: str
enabled: bool = True

masterid: int | None = None

# For backward compatibility
def __getitem__(self, key: str):
warn_deprecated(
'4.1.0',
(
'SchedulersConnectorComponent '
'getScheduler, and getSchedulers '
'no longer return Scheduler as dictionnaries. '
'Usage of [] accessor is deprecated: please access the member directly'
),
)

if hasattr(self, key):
return getattr(self, key)

raise KeyError(key)


class SchedulersConnectorComponent(base.DBConnectorComponent):
# returns a Deferred that returns None
def enable(self, schedulerid, v):
def thd(conn):
def enable(self, schedulerid: int, v: bool) -> defer.Deferred[None]:
def thd(conn) -> None:
tbl = self.db.model.schedulers
q = tbl.update().where(tbl.c.id == schedulerid)
conn.execute(q.values(enabled=int(v)))

return self.db.pool.do(thd)

# returns a Deferred that returns None
def classifyChanges(self, schedulerid, classifications):
def thd(conn):
def classifyChanges(
self, schedulerid: int, classifications: dict[int, bool]
) -> defer.Deferred[None]:
def thd(conn) -> None:
tbl = self.db.model.scheduler_changes
ins_q = tbl.insert()
upd_q = tbl.update().where(
Expand All @@ -62,9 +98,10 @@ def thd(conn):

return self.db.pool.do(thd)

# returns a Deferred that returns None
def flushChangeClassifications(self, schedulerid, less_than=None):
def thd(conn):
def flushChangeClassifications(
self, schedulerid: int, less_than: int | None = None
) -> defer.Deferred[None]:
def thd(conn) -> None:
sch_ch_tbl = self.db.model.scheduler_changes
wc = sch_ch_tbl.c.schedulerid == schedulerid
if less_than is not None:
Expand All @@ -74,13 +111,17 @@ def thd(conn):

return self.db.pool.do(thd)

# returns a Deferred that returns a value
def getChangeClassifications(
self, schedulerid, branch=-1, repository=-1, project=-1, codebase=-1
):
self,
schedulerid: int,
branch: str | None | Literal[-1] = -1,
repository: str | None | Literal[-1] = -1,
project: str | None | Literal[-1] = -1,
codebase: str | None | Literal[-1] = -1,
) -> defer.Deferred[dict[int, bool]]:
# -1 here stands for "argument not given", since None has meaning
# as a branch
def thd(conn):
def thd(conn) -> dict[int, bool]:
sch_ch_tbl = self.db.model.scheduler_changes
ch_tbl = self.db.model.changes

Expand Down Expand Up @@ -109,7 +150,7 @@ def thd(conn):

return self.db.pool.do(thd)

def findSchedulerId(self, name):
def findSchedulerId(self, name: str) -> int:
tbl = self.db.model.schedulers
name_hash = self.hashColumns(name)
return self.findSomethingId(
Expand All @@ -118,9 +159,8 @@ def findSchedulerId(self, name):
insert_values={"name": name, "name_hash": name_hash},
)

# returns a Deferred that returns None
def setSchedulerMaster(self, schedulerid, masterid):
def thd(conn):
def setSchedulerMaster(self, schedulerid: int, masterid: int | None) -> defer.Deferred[None]:
def thd(conn) -> None:
sch_mst_tbl = self.db.model.scheduler_masters

# handle the masterid=None case to get it out of the way
Expand Down Expand Up @@ -157,15 +197,19 @@ def thd(conn):
return self.db.pool.do(thd)

@defer.inlineCallbacks
def getScheduler(self, schedulerid):
def getScheduler(self, schedulerid: int):
sch = yield self.getSchedulers(_schedulerid=schedulerid)
if sch:
return sch[0]
return None

# returns a Deferred that returns a value
def getSchedulers(self, active=None, masterid=None, _schedulerid=None):
def thd(conn):
def getSchedulers(
self,
active: bool | None = None,
masterid: int | None = None,
_schedulerid: int | None = None,
) -> defer.Deferred[list[SchedulerModel]]:
def thd(conn) -> list[SchedulerModel]:
sch_tbl = self.db.model.schedulers
sch_mst_tbl = self.db.model.scheduler_masters

Expand Down Expand Up @@ -197,14 +241,14 @@ def thd(conn):
if wc is not None:
q = q.where(wc)

return [
{
"id": row.id,
"name": row.name,
"enabled": bool(row.enabled),
"masterid": row.masterid,
}
for row in conn.execute(q).fetchall()
]
return [self._model_from_row(row) for row in conn.execute(q).fetchall()]

return self.db.pool.do(thd)

def _model_from_row(self, row):
return SchedulerModel(
id=row.id,
name=row.name,
enabled=bool(row.enabled),
masterid=row.masterid,
)
2 changes: 1 addition & 1 deletion master/buildbot/schedulers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def activate(self):
schedulerData = yield self._getScheduler(self.serviceid)

if schedulerData:
self.enabled = schedulerData['enabled']
self.enabled = schedulerData.enabled

if not self._enable_consumer:
yield self.startConsumingEnableEvents()
Expand Down
23 changes: 11 additions & 12 deletions master/buildbot/test/fakedb/schedulers.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,14 @@ def findSchedulerId(self, name):

def getScheduler(self, schedulerid):
if schedulerid in self.schedulers:
rv = {
"id": schedulerid,
"name": self.schedulers[schedulerid],
"enabled": self.enabled.get(schedulerid, True),
"masterid": None,
}
# only set masterid if the relevant scheduler master exists and
# is active
rv['masterid'] = self.scheduler_masters.get(schedulerid)
rv = schedulers.SchedulerModel(
id=schedulerid,
name=self.schedulers[schedulerid],
enabled=self.enabled.get(schedulerid, True),
# only set masterid if the relevant scheduler master exists and
# is active
masterid=self.scheduler_masters.get(schedulerid),
)
return defer.succeed(rv)
return None

Expand All @@ -174,12 +173,12 @@ def filter(results):
results = [r[1] for r in results]
# filter for masterid
if masterid is not None:
results = [r for r in results if r['masterid'] == masterid]
results = [r for r in results if r.masterid == masterid]
# filter for active or inactive if necessary
if active:
results = [r for r in results if r['masterid'] is not None]
results = [r for r in results if r.masterid is not None]
elif active is not None:
results = [r for r in results if r['masterid'] is None]
results = [r for r in results if r.masterid is None]
return results

return d
Expand Down

0 comments on commit 3a160e3

Please sign in to comment.