libres 0.7.3__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
libres/db/scheduler.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import sedate
2
4
 
3
5
  from datetime import datetime, time, timedelta
@@ -16,8 +18,14 @@ from libres.modules import rasterizer
16
18
  from libres.modules import utils
17
19
 
18
20
 
19
- import typing as _t
20
- if _t.TYPE_CHECKING:
21
+ from typing import overload
22
+ from typing import Any
23
+ from typing import Literal
24
+ from typing import TYPE_CHECKING
25
+ if TYPE_CHECKING:
26
+ from collections.abc import Collection
27
+ from collections.abc import Iterable
28
+ from collections.abc import Iterator
21
29
  from sedate.types import DateLike
22
30
  from sqlalchemy.orm import Query
23
31
  from typing_extensions import NotRequired, Self, TypeAlias, TypedDict
@@ -25,32 +33,32 @@ if _t.TYPE_CHECKING:
25
33
  from libres.context.core import Context
26
34
  from libres.modules.rasterizer import Raster
27
35
 
28
- _dtrange: TypeAlias = _t.Tuple[datetime, datetime]
36
+ _dtrange: TypeAlias = tuple[datetime, datetime] # noqa: PYI042
29
37
 
30
38
  class _ReserveArgs1(TypedDict):
31
39
  email: str
32
- dates: _t.Union['_dtrange', _t.Collection['_dtrange']]
33
- data: NotRequired[_t.Optional[_t.Any]]
34
- session_id: NotRequired[_t.Optional[UUID]]
40
+ dates: _dtrange | Collection[_dtrange]
41
+ data: NotRequired[Any | None]
42
+ session_id: NotRequired[UUID | None]
35
43
  quota: NotRequired[int]
36
44
  single_token_per_session: NotRequired[bool]
37
45
 
38
46
  class _ReserveArgs2(TypedDict):
39
47
  email: str
40
48
  group: UUID
41
- data: NotRequired[_t.Optional[_t.Any]]
42
- session_id: NotRequired[_t.Optional[UUID]]
49
+ data: NotRequired[Any | None]
50
+ session_id: NotRequired[UUID | None]
43
51
  quota: NotRequired[int]
44
52
  single_token_per_session: NotRequired[bool]
45
53
 
46
- _ReserveArgs: TypeAlias = _t.Union[_ReserveArgs1, _ReserveArgs2]
54
+ _ReserveArgs: TypeAlias = '_ReserveArgs1 | _ReserveArgs2'
47
55
 
48
56
 
49
57
  missing = object()
50
58
 
51
- Day = _t.Literal['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
52
- DayNumber = _t.Literal[0, 1, 2, 3, 4, 5, 6]
53
- DAYS_MAP: _t.Dict[Day, DayNumber] = {
59
+ Day: TypeAlias = Literal['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
60
+ DayNumber: TypeAlias = Literal[0, 1, 2, 3, 4, 5, 6]
61
+ DAYS_MAP: dict[Day, DayNumber] = {
54
62
  'mo': 0,
55
63
  'tu': 1,
56
64
  'we': 2,
@@ -68,12 +76,12 @@ class Scheduler(ContextServicesMixin):
68
76
 
69
77
  def __init__(
70
78
  self,
71
- context: 'Context',
79
+ context: Context,
72
80
  name: str,
73
81
  # FIXME: Not quite sure why we don't allow a PyTzInfo
74
82
  timezone: str,
75
- allocation_cls: _t.Type[Allocation] = Allocation,
76
- reservation_cls: _t.Type[Reservation] = Reservation
83
+ allocation_cls: type[Allocation] = Allocation,
84
+ reservation_cls: type[Reservation] = Reservation
77
85
  ):
78
86
  """ Initializeds a new Scheduler instance.
79
87
 
@@ -112,7 +120,7 @@ class Scheduler(ContextServicesMixin):
112
120
  self.allocation_cls = allocation_cls
113
121
  self.reservation_cls = reservation_cls
114
122
 
115
- def clone(self) -> 'Self':
123
+ def clone(self) -> Self:
116
124
  """ Clones the scheduler. The result will be a new scheduler using the
117
125
  same context, name, settings and attributes.
118
126
 
@@ -145,8 +153,8 @@ class Scheduler(ContextServicesMixin):
145
153
 
146
154
  def _prepare_dates(
147
155
  self,
148
- dates: 'utils._NestedIterable[datetime]'
149
- ) -> _t.List[_t.Tuple[datetime, datetime]]:
156
+ dates: utils._NestedIterable[datetime]
157
+ ) -> list[tuple[datetime, datetime]]:
150
158
  return [
151
159
  (
152
160
  sedate.standardize_date(s, self.timezone),
@@ -158,20 +166,20 @@ class Scheduler(ContextServicesMixin):
158
166
  self,
159
167
  start: datetime,
160
168
  end: datetime
161
- ) -> _t.Tuple[datetime, datetime]:
169
+ ) -> tuple[datetime, datetime]:
162
170
  return (
163
171
  sedate.standardize_date(start, self.timezone),
164
172
  sedate.standardize_date(end, self.timezone)
165
173
  )
166
174
 
167
- def managed_allocations(self) -> 'Query[Allocation]':
175
+ def managed_allocations(self) -> Query[Allocation]:
168
176
  """ The allocations managed by this scheduler / resource. """
169
177
  query = self.session.query(Allocation)
170
178
  query = query.filter(Allocation.mirror_of == self.resource)
171
179
 
172
180
  return query
173
181
 
174
- def managed_reserved_slots(self) -> 'Query[ReservedSlot]':
182
+ def managed_reserved_slots(self) -> Query[ReservedSlot]:
175
183
  """ The reserved_slots managed by this scheduler / resource. """
176
184
  uuids = self.managed_allocations().with_entities(Allocation.resource)
177
185
 
@@ -180,7 +188,7 @@ class Scheduler(ContextServicesMixin):
180
188
 
181
189
  return query
182
190
 
183
- def managed_reservations(self) -> 'Query[Reservation]':
191
+ def managed_reservations(self) -> Query[Reservation]:
184
192
  """ The reservations managed by this scheduler / resource. """
185
193
  query = self.session.query(Reservation)
186
194
  query = query.filter(Reservation.resource == self.resource)
@@ -205,8 +213,8 @@ class Scheduler(ContextServicesMixin):
205
213
 
206
214
  def allocations_by_ids(
207
215
  self,
208
- ids: _t.Collection[int]
209
- ) -> 'Query[Allocation]':
216
+ ids: Collection[int]
217
+ ) -> Query[Allocation]:
210
218
  query = self.managed_allocations()
211
219
  query = query.filter(Allocation.id.in_(ids))
212
220
  query = query.order_by(Allocation._start)
@@ -216,14 +224,14 @@ class Scheduler(ContextServicesMixin):
216
224
  self,
217
225
  group: UUID,
218
226
  masters_only: bool = True
219
- ) -> 'Query[Allocation]':
227
+ ) -> Query[Allocation]:
220
228
  return self.allocations_by_groups([group], masters_only=masters_only)
221
229
 
222
230
  def allocations_by_groups(
223
231
  self,
224
- groups: _t.Collection[UUID],
232
+ groups: Collection[UUID],
225
233
  masters_only: bool = True
226
- ) -> 'Query[Allocation]':
234
+ ) -> Query[Allocation]:
227
235
 
228
236
  query = self.managed_allocations()
229
237
  query = query.filter(Allocation.group.in_(groups))
@@ -236,8 +244,8 @@ class Scheduler(ContextServicesMixin):
236
244
  def allocations_by_reservation(
237
245
  self,
238
246
  token: UUID,
239
- id: _t.Optional[int] = None
240
- ) -> 'Query[Allocation]':
247
+ id: int | None = None
248
+ ) -> Query[Allocation]:
241
249
  """ Returns the allocations for the reservation if it was *approved*,
242
250
  pending reservations return nothing. If you need to get the allocation
243
251
  a pending reservation might be targeting, use _target_allocations
@@ -276,7 +284,7 @@ class Scheduler(ContextServicesMixin):
276
284
  start: datetime,
277
285
  end: datetime,
278
286
  masters_only: bool = True
279
- ) -> 'Query[Allocation]':
287
+ ) -> Query[Allocation]:
280
288
 
281
289
  start, end = self._prepare_range(start, end)
282
290
 
@@ -295,7 +303,7 @@ class Scheduler(ContextServicesMixin):
295
303
  def allocation_dates_by_group(
296
304
  self,
297
305
  group: UUID
298
- ) -> _t.List[_t.Tuple[datetime, datetime]]:
306
+ ) -> list[tuple[datetime, datetime]]:
299
307
 
300
308
  query = self.allocations_by_group(group)
301
309
  dates_query = query.with_entities(Allocation._start, Allocation._end)
@@ -304,15 +312,15 @@ class Scheduler(ContextServicesMixin):
304
312
  def allocation_mirrors_by_master(
305
313
  self,
306
314
  master: Allocation
307
- ) -> _t.List[Allocation]:
315
+ ) -> list[Allocation]:
308
316
  return [s for s in master.siblings() if not s.is_master]
309
317
 
310
318
  def allocation_dates_by_ids(
311
319
  self,
312
- ids: _t.Collection[int],
313
- start_time: _t.Optional[time] = None,
314
- end_time: _t.Optional[time] = None
315
- ) -> _t.Iterator[_t.Tuple[datetime, datetime]]:
320
+ ids: Collection[int],
321
+ start_time: time | None = None,
322
+ end_time: time | None = None
323
+ ) -> Iterator[tuple[datetime, datetime]]:
316
324
 
317
325
  for allocation in self.allocations_by_ids(ids).all():
318
326
 
@@ -323,7 +331,7 @@ class Scheduler(ContextServicesMixin):
323
331
 
324
332
  yield s_dt, e_dt - timedelta(microseconds=1)
325
333
 
326
- def manual_approval_required(self, ids: _t.Collection[int]) -> bool:
334
+ def manual_approval_required(self, ids: Collection[int]) -> bool:
327
335
  """ Returns True if any of the allocations require manual approval. """
328
336
  query = self.allocations_by_ids(ids)
329
337
  query = query.filter(Allocation.approve_manually == True)
@@ -332,17 +340,17 @@ class Scheduler(ContextServicesMixin):
332
340
 
333
341
  def allocate(
334
342
  self,
335
- dates: _t.Union['_dtrange', _t.Iterable['_dtrange']],
343
+ dates: _dtrange | Iterable[_dtrange],
336
344
  partly_available: bool = False,
337
- raster: 'Raster' = rasterizer.MIN_RASTER,
345
+ raster: Raster = rasterizer.MIN_RASTER,
338
346
  whole_day: bool = False,
339
- quota: _t.Optional[int] = None,
347
+ quota: int | None = None,
340
348
  quota_limit: int = 0,
341
349
  grouped: bool = False,
342
- data: _t.Optional[_t.Any] = None,
350
+ data: Any | None = None,
343
351
  approve_manually: bool = False,
344
352
  skip_overlapping: bool = False,
345
- ) -> _t.List[Allocation]:
353
+ ) -> list[Allocation]:
346
354
  """ Allocates a spot in the sedate.
347
355
 
348
356
  An allocation defines a timerange which can be reserved. No
@@ -596,7 +604,7 @@ class Scheduler(ContextServicesMixin):
596
604
 
597
605
  """
598
606
 
599
- assert new_quota > 0, "Quota must be greater than 0"
607
+ assert new_quota > 0, 'Quota must be greater than 0'
600
608
 
601
609
  if new_quota == master.quota:
602
610
  return
@@ -607,7 +615,7 @@ class Scheduler(ContextServicesMixin):
607
615
 
608
616
  # Make sure that the quota can be decreased
609
617
  mirrors = self.allocation_mirrors_by_master(master)
610
- allocations = [master] + mirrors
618
+ allocations = [master, *mirrors]
611
619
 
612
620
  free_allocations = [a for a in allocations if a.is_available()]
613
621
 
@@ -666,9 +674,9 @@ class Scheduler(ContextServicesMixin):
666
674
 
667
675
  def reordered_keylist(
668
676
  self,
669
- allocations: _t.Collection[Allocation],
677
+ allocations: Collection[Allocation],
670
678
  new_quota: int
671
- ) -> _t.Dict[UUID, _t.Optional[UUID]]:
679
+ ) -> dict[UUID, UUID | None]:
672
680
  """ Creates the map for the keylist reorganzation.
673
681
 
674
682
  Each key of the returned dictionary is a resource uuid pointing to the
@@ -687,7 +695,7 @@ class Scheduler(ContextServicesMixin):
687
695
  keylist.extend(utils.generate_uuids(master.resource, master.quota))
688
696
 
689
697
  # prefill the map
690
- reordered: _t.Dict[UUID, _t.Optional[UUID]] = {
698
+ reordered: dict[UUID, UUID | None] = {
691
699
  k: None
692
700
  for k in keylist
693
701
  }
@@ -705,8 +713,8 @@ class Scheduler(ContextServicesMixin):
705
713
 
706
714
  def availability(
707
715
  self,
708
- start: _t.Optional[datetime] = None,
709
- end: _t.Optional[datetime] = None
716
+ start: datetime | None = None,
717
+ end: datetime | None = None
710
718
  ) -> float:
711
719
  """Goes through all allocations and sums up the availability."""
712
720
 
@@ -720,14 +728,14 @@ class Scheduler(ContextServicesMixin):
720
728
  def move_allocation(
721
729
  self,
722
730
  master_id: int,
723
- new_start: _t.Optional[datetime] = None,
724
- new_end: _t.Optional[datetime] = None,
725
- group: _t.Optional[UUID] = None,
726
- new_quota: _t.Optional[int] = None,
727
- approve_manually: _t.Optional[bool] = None,
731
+ new_start: datetime | None = None,
732
+ new_end: datetime | None = None,
733
+ group: UUID | None = None,
734
+ new_quota: int | None = None,
735
+ approve_manually: bool | None = None,
728
736
  quota_limit: int = 0,
729
- whole_day: _t.Optional[bool] = None,
730
- data: _t.Optional[_t.Any] = missing
737
+ whole_day: bool | None = None,
738
+ data: Any | None = missing
731
739
  ) -> None:
732
740
 
733
741
  assert master_id
@@ -740,7 +748,7 @@ class Scheduler(ContextServicesMixin):
740
748
  master = self.allocation_by_id(master_id)
741
749
  mirrors = self.allocation_mirrors_by_master(master)
742
750
 
743
- changing = [master] + mirrors
751
+ changing = [master, *mirrors]
744
752
  ids = [c.id for c in changing]
745
753
 
746
754
  assert master.timezone == self.timezone, """
@@ -834,24 +842,24 @@ class Scheduler(ContextServicesMixin):
834
842
  if data is not missing:
835
843
  change.data = data
836
844
 
837
- @_t.overload
845
+ @overload
838
846
  def remove_allocation(self, id: int) -> None: ...
839
847
 
840
- @_t.overload
848
+ @overload
841
849
  def remove_allocation(
842
850
  self,
843
851
  id: None = ...,
844
852
  *,
845
- groups: _t.Collection[UUID]
853
+ groups: Collection[UUID]
846
854
  ) -> None: ...
847
855
 
848
856
  def remove_allocation(
849
857
  self,
850
- id: _t.Optional[int] = None,
851
- groups: _t.Optional[_t.Collection[UUID]] = None
858
+ id: int | None = None,
859
+ groups: Collection[UUID] | None = None
852
860
  ) -> None:
853
861
 
854
- allocations: _t.Iterable[Allocation]
862
+ allocations: Iterable[Allocation]
855
863
  if id:
856
864
  # FIXME: We probably should `assert groups is None`
857
865
  # since the parameter does nothing if `id` is
@@ -894,9 +902,9 @@ class Scheduler(ContextServicesMixin):
894
902
 
895
903
  def remove_unused_allocations(
896
904
  self,
897
- start: 'DateLike',
898
- end: 'DateLike',
899
- days: _t.Optional[_t.Iterable[_t.Union['Day', 'DayNumber']]] = None,
905
+ start: DateLike,
906
+ end: DateLike,
907
+ days: Iterable[Day | DayNumber] | None = None,
900
908
  exclude_groups: bool = False
901
909
  ) -> int:
902
910
  """ Removes all allocations without reservations between start and
@@ -922,7 +930,7 @@ class Scheduler(ContextServicesMixin):
922
930
  sedate.as_datetime(end)
923
931
  )
924
932
 
925
- day_numbers: _t.Optional[_t.Set[DayNumber]] = None
933
+ day_numbers: set[DayNumber] | None = None
926
934
  if days:
927
935
  # get the day from the map - if impossible take the verbatim value
928
936
  # this allows for using strings or integers
@@ -1001,39 +1009,39 @@ class Scheduler(ContextServicesMixin):
1001
1009
  deleted += 1
1002
1010
  return deleted
1003
1011
 
1004
- @_t.overload
1012
+ @overload
1005
1013
  def reserve(
1006
1014
  self,
1007
1015
  email: str,
1008
- dates: _t.Union['_dtrange', _t.Collection['_dtrange']],
1016
+ dates: _dtrange | Collection[_dtrange],
1009
1017
  group: None = ...,
1010
- data: _t.Optional[_t.Any] = ...,
1011
- session_id: _t.Optional[UUID] = ...,
1018
+ data: Any | None = ...,
1019
+ session_id: UUID | None = ...,
1012
1020
  quota: int = ...,
1013
1021
  single_token_per_session: bool = ...
1014
1022
  ) -> UUID: ...
1015
1023
 
1016
- @_t.overload
1024
+ @overload
1017
1025
  def reserve(
1018
1026
  self,
1019
1027
  email: str,
1020
1028
  dates: None,
1021
1029
  group: UUID,
1022
- data: _t.Optional[_t.Any] = ...,
1023
- session_id: _t.Optional[UUID] = ...,
1030
+ data: Any | None = ...,
1031
+ session_id: UUID | None = ...,
1024
1032
  quota: int = ...,
1025
1033
  single_token_per_session: bool = ...
1026
1034
  ) -> UUID: ...
1027
1035
 
1028
- @_t.overload
1036
+ @overload
1029
1037
  def reserve(
1030
1038
  self,
1031
1039
  email: str,
1032
1040
  dates: None = ...,
1033
1041
  *,
1034
1042
  group: UUID,
1035
- data: _t.Optional[_t.Any] = ...,
1036
- session_id: _t.Optional[UUID] = ...,
1043
+ data: Any | None = ...,
1044
+ session_id: UUID | None = ...,
1037
1045
  quota: int = ...,
1038
1046
  single_token_per_session: bool = ...
1039
1047
  ) -> UUID: ...
@@ -1041,10 +1049,10 @@ class Scheduler(ContextServicesMixin):
1041
1049
  def reserve(
1042
1050
  self,
1043
1051
  email: str,
1044
- dates: _t.Union['_dtrange', _t.Collection['_dtrange'], None] = None,
1045
- group: _t.Optional[UUID] = None,
1046
- data: _t.Optional[_t.Any] = None,
1047
- session_id: _t.Optional[UUID] = None,
1052
+ dates: _dtrange | Collection[_dtrange] | None = None,
1053
+ group: UUID | None = None,
1054
+ data: Any | None = None,
1055
+ session_id: UUID | None = None,
1048
1056
  quota: int = 1,
1049
1057
  single_token_per_session: bool = False
1050
1058
  ) -> UUID:
@@ -1173,9 +1181,8 @@ class Scheduler(ContextServicesMixin):
1173
1181
  if not allocation.contains(start, end):
1174
1182
  raise errors.TimerangeTooLong()
1175
1183
 
1176
- if allocation.quota_limit > 0:
1177
- if allocation.quota_limit < quota:
1178
- raise errors.QuotaOverLimit
1184
+ if 0 < allocation.quota_limit < quota:
1185
+ raise errors.QuotaOverLimit
1179
1186
 
1180
1187
  if allocation.quota < quota:
1181
1188
  raise errors.QuotaImpossible
@@ -1194,8 +1201,8 @@ class Scheduler(ContextServicesMixin):
1194
1201
  # or none of them. As such there's no start / end date which is defined
1195
1202
  # implicitly by the allocation
1196
1203
  def new_reservations_by_group(
1197
- group: _t.Optional[UUID]
1198
- ) -> _t.Iterator[Reservation]:
1204
+ group: UUID | None
1205
+ ) -> Iterator[Reservation]:
1199
1206
 
1200
1207
  if group:
1201
1208
  reservation = self.reservation_cls()
@@ -1213,8 +1220,8 @@ class Scheduler(ContextServicesMixin):
1213
1220
 
1214
1221
  # all other reservations are reserved by start/end date
1215
1222
  def new_reservations_by_dates(
1216
- dates: _t.List[_t.Tuple[datetime, datetime]]
1217
- ) -> _t.Iterator[Reservation]:
1223
+ dates: list[tuple[datetime, datetime]]
1224
+ ) -> Iterator[Reservation]:
1218
1225
 
1219
1226
  already_reserved_groups = set()
1220
1227
 
@@ -1283,12 +1290,12 @@ class Scheduler(ContextServicesMixin):
1283
1290
  def _approve_reservation_record(
1284
1291
  self,
1285
1292
  reservation: Reservation
1286
- ) -> _t.List[ReservedSlot]:
1293
+ ) -> list[ReservedSlot]:
1287
1294
 
1288
1295
  # write out the slots
1289
1296
  slots_to_reserve = []
1290
1297
 
1291
- dates: _t.Union[_t.Tuple['_dtrange', ...], _t.List['_dtrange']]
1298
+ dates: tuple[_dtrange, ...] | list[_dtrange]
1292
1299
  if reservation.target_type == 'group':
1293
1300
  dates = self.allocation_dates_by_group(reservation.target)
1294
1301
  else:
@@ -1330,7 +1337,7 @@ class Scheduler(ContextServicesMixin):
1330
1337
 
1331
1338
  return slots_to_reserve
1332
1339
 
1333
- def approve_reservations(self, token: UUID) -> _t.List[ReservedSlot]:
1340
+ def approve_reservations(self, token: UUID) -> list[ReservedSlot]:
1334
1341
  """ This function approves an existing reservation and writes the
1335
1342
  reserved slots accordingly.
1336
1343
 
@@ -1342,14 +1349,14 @@ class Scheduler(ContextServicesMixin):
1342
1349
 
1343
1350
  reservations = self.reservations_by_token(token).all()
1344
1351
 
1345
- for reservation in reservations:
1346
- try:
1352
+ try:
1353
+ for reservation in reservations:
1347
1354
  slots_to_reserve.extend(
1348
1355
  self._approve_reservation_record(reservation)
1349
1356
  )
1350
- except errors.LibresError as e:
1351
- e.reservation = reservation
1352
- raise e
1357
+ except errors.LibresError as e:
1358
+ e.reservation = reservation
1359
+ raise e
1353
1360
 
1354
1361
  events.on_reservations_approved(self.context, reservations)
1355
1362
 
@@ -1373,7 +1380,7 @@ class Scheduler(ContextServicesMixin):
1373
1380
  def remove_reservation(
1374
1381
  self,
1375
1382
  token: UUID,
1376
- id: _t.Optional[int] = None
1383
+ id: int | None = None
1377
1384
  ) -> None:
1378
1385
  """ Removes all reserved slots of the given reservation token.
1379
1386
 
@@ -1411,7 +1418,7 @@ class Scheduler(ContextServicesMixin):
1411
1418
  def change_reservation_data(
1412
1419
  self,
1413
1420
  token: UUID,
1414
- data: _t.Optional[_t.Any]
1421
+ data: Any | None
1415
1422
  ) -> None:
1416
1423
 
1417
1424
  for reservation in self.reservations_by_token(token).all():
@@ -1419,8 +1426,8 @@ class Scheduler(ContextServicesMixin):
1419
1426
 
1420
1427
  def change_reservation_time_candidates(
1421
1428
  self,
1422
- tokens: _t.Optional[_t.Collection[UUID]] = None
1423
- ) -> 'Query[Reservation]':
1429
+ tokens: Collection[UUID] | None = None
1430
+ ) -> Query[Reservation]:
1424
1431
  """ Returns the reservations that fullfill the restrictions
1425
1432
  imposed by change_reservation_time.
1426
1433
 
@@ -1448,7 +1455,7 @@ class Scheduler(ContextServicesMixin):
1448
1455
  id: int,
1449
1456
  new_start: datetime,
1450
1457
  new_end: datetime
1451
- ) -> _t.Optional[Reservation]:
1458
+ ) -> Reservation | None:
1452
1459
  """ Kept for backwards compatibility, use :meth:`change_reservation`
1453
1460
  instead.
1454
1461
 
@@ -1468,8 +1475,8 @@ class Scheduler(ContextServicesMixin):
1468
1475
  id: int,
1469
1476
  new_start: datetime,
1470
1477
  new_end: datetime,
1471
- quota: _t.Optional[int] = None
1472
- ) -> _t.Optional[Reservation]:
1478
+ quota: int | None = None
1479
+ ) -> Reservation | None:
1473
1480
  """ Allows to change the timespan of a reservation under certain
1474
1481
  conditions:
1475
1482
 
@@ -1493,7 +1500,7 @@ class Scheduler(ContextServicesMixin):
1493
1500
  existing_reservation = self.reservations_by_token(token, id).one()
1494
1501
 
1495
1502
  # if there's nothing to change, do not change
1496
- if quota is None or existing_reservation.quota == quota:
1503
+ if quota is None or existing_reservation.quota == quota: # noqa: SIM102
1497
1504
  if existing_reservation.start == new_start:
1498
1505
  ends = (new_end, new_end - timedelta(microseconds=1))
1499
1506
 
@@ -1511,7 +1518,7 @@ class Scheduler(ContextServicesMixin):
1511
1518
  if not allocation.contains(new_start, new_end):
1512
1519
  raise errors.TimerangeTooLong()
1513
1520
 
1514
- reservation_arguments: '_ReserveArgs' = {
1521
+ reservation_arguments: _ReserveArgs = {
1515
1522
  'email': existing_reservation.email,
1516
1523
  'dates': (new_start, new_end),
1517
1524
  'data': existing_reservation.data,
@@ -1549,13 +1556,13 @@ class Scheduler(ContextServicesMixin):
1549
1556
  self,
1550
1557
  start: datetime,
1551
1558
  end: datetime,
1552
- days: _t.Optional[_t.Collection[_t.Union[Day, DayNumber]]] = None,
1559
+ days: Collection[Day | DayNumber] | None = None,
1553
1560
  minspots: int = 0,
1554
1561
  available_only: bool = False,
1555
- whole_day: _t.Literal['any', 'yes', 'no'] = 'any',
1556
- groups: _t.Literal['any', 'yes', 'no'] = 'any',
1562
+ whole_day: Literal['any', 'yes', 'no'] = 'any',
1563
+ groups: Literal['any', 'yes', 'no'] = 'any',
1557
1564
  strict: bool = False
1558
- ) -> _t.List[Allocation]:
1565
+ ) -> list[Allocation]:
1559
1566
  """ Search allocations using a number of options. The date is split
1560
1567
  into date/time. All allocations between start and end date within
1561
1568
  the given time (on each day) are included.
@@ -1623,7 +1630,7 @@ class Scheduler(ContextServicesMixin):
1623
1630
  assert whole_day in ('yes', 'no', 'any')
1624
1631
  assert groups in ('yes', 'no', 'any')
1625
1632
 
1626
- day_numbers: _t.Optional[_t.Set[DayNumber]]
1633
+ day_numbers: set[DayNumber] | None
1627
1634
  if days:
1628
1635
  # get the day from the map - if impossible take the verbatim value
1629
1636
  # this allows for using strings or integers
@@ -1657,9 +1664,11 @@ class Scheduler(ContextServicesMixin):
1657
1664
  if not allocation.overlaps(s, e):
1658
1665
  continue
1659
1666
 
1660
- if day_numbers:
1661
- if allocation.display_start().weekday() not in day_numbers:
1662
- continue
1667
+ if (
1668
+ day_numbers
1669
+ and allocation.display_start().weekday() not in day_numbers
1670
+ ):
1671
+ continue
1663
1672
 
1664
1673
  if whole_day != 'any':
1665
1674
  if whole_day == 'yes' and not allocation.whole_day:
@@ -1673,14 +1682,11 @@ class Scheduler(ContextServicesMixin):
1673
1682
  #
1674
1683
  # the spots are later checked again for actual availability, but
1675
1684
  # that is a heavier check, so it doesn't belong here.
1676
- if minspots:
1677
- if allocation.quota_limit > 0:
1678
- if allocation.quota_limit < minspots:
1679
- continue
1685
+ if minspots and (0 < allocation.quota_limit < minspots):
1686
+ continue
1680
1687
 
1681
- if available_only:
1682
- if not allocation.find_spot(s, e):
1683
- continue
1688
+ if available_only and not allocation.find_spot(s, e):
1689
+ continue
1684
1690
 
1685
1691
  if minspots:
1686
1692
  availability = self.availability(
@@ -1750,7 +1756,7 @@ class Scheduler(ContextServicesMixin):
1750
1756
  self,
1751
1757
  start: datetime,
1752
1758
  end: datetime
1753
- ) -> _t.List[Allocation]:
1759
+ ) -> list[Allocation]:
1754
1760
  """ Returns a list of allocations that are free within start and end.
1755
1761
  These allocations may come from the master or any of the mirrors.
1756
1762
 
@@ -1777,8 +1783,8 @@ class Scheduler(ContextServicesMixin):
1777
1783
  def reserved_slots_by_reservation(
1778
1784
  self,
1779
1785
  token: UUID,
1780
- id: _t.Optional[int] = None
1781
- ) -> 'Query[ReservedSlot]':
1786
+ id: int | None = None
1787
+ ) -> Query[ReservedSlot]:
1782
1788
  """ Returns all reserved slots of the given reservation.
1783
1789
  The id is optional and may be used only return the slots from a
1784
1790
  specific reservation matching token and id.
@@ -1796,7 +1802,7 @@ class Scheduler(ContextServicesMixin):
1796
1802
  ids = allocations.with_entities(Allocation.id)
1797
1803
  return query.filter(ReservedSlot.allocation_id.in_(ids))
1798
1804
 
1799
- def reservations_by_group(self, group: UUID) -> 'Query[Reservation]':
1805
+ def reservations_by_group(self, group: UUID) -> Query[Reservation]:
1800
1806
  tokens = self.managed_reservations().with_entities(Reservation.token)
1801
1807
  tokens = tokens.filter(Reservation.target == group)
1802
1808
 
@@ -1807,7 +1813,7 @@ class Scheduler(ContextServicesMixin):
1807
1813
  def reservations_by_allocation(
1808
1814
  self,
1809
1815
  allocation_id: int
1810
- ) -> 'Query[Reservation]':
1816
+ ) -> Query[Reservation]:
1811
1817
 
1812
1818
  master = self.allocation_by_id(allocation_id)
1813
1819
  return self.reservations_by_group(master.group)
@@ -1815,8 +1821,8 @@ class Scheduler(ContextServicesMixin):
1815
1821
  def reservations_by_token(
1816
1822
  self,
1817
1823
  token: UUID,
1818
- id: _t.Optional[int] = None
1819
- ) -> 'Query[Reservation]':
1824
+ id: int | None = None
1825
+ ) -> Query[Reservation]:
1820
1826
 
1821
1827
  query = self.managed_reservations()
1822
1828
  query = query.filter(Reservation.token == token)