libres 0.9.0__tar.gz → 0.9.1__tar.gz

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.
Files changed (62) hide show
  1. {libres-0.9.0 → libres-0.9.1}/HISTORY.rst +7 -0
  2. {libres-0.9.0/src/libres.egg-info → libres-0.9.1}/PKG-INFO +8 -1
  3. {libres-0.9.0 → libres-0.9.1}/pyproject.toml +70 -54
  4. {libres-0.9.0 → libres-0.9.1}/src/libres/__init__.py +1 -1
  5. {libres-0.9.0 → libres-0.9.1}/src/libres/context/registry.py +2 -0
  6. {libres-0.9.0 → libres-0.9.1}/src/libres/db/scheduler.py +15 -11
  7. {libres-0.9.0 → libres-0.9.1/src/libres.egg-info}/PKG-INFO +8 -1
  8. {libres-0.9.0 → libres-0.9.1}/tests/test_allocation.py +44 -29
  9. {libres-0.9.0 → libres-0.9.1}/tests/test_registry.py +19 -13
  10. {libres-0.9.0 → libres-0.9.1}/tests/test_reservation.py +13 -6
  11. {libres-0.9.0 → libres-0.9.1}/tests/test_reserved_slot.py +12 -5
  12. {libres-0.9.0 → libres-0.9.1}/tests/test_scheduler.py +133 -105
  13. {libres-0.9.0 → libres-0.9.1}/tests/test_session.py +30 -19
  14. {libres-0.9.0 → libres-0.9.1}/tests/test_test.py +12 -2
  15. {libres-0.9.0 → libres-0.9.1}/tests/test_types.py +4 -2
  16. {libres-0.9.0 → libres-0.9.1}/tests/test_utils.py +3 -1
  17. {libres-0.9.0 → libres-0.9.1}/LICENSE +0 -0
  18. {libres-0.9.0 → libres-0.9.1}/MANIFEST.in +0 -0
  19. {libres-0.9.0 → libres-0.9.1}/README.rst +0 -0
  20. {libres-0.9.0 → libres-0.9.1}/docs/Makefile +0 -0
  21. {libres-0.9.0 → libres-0.9.1}/docs/_static/custom.css +0 -0
  22. {libres-0.9.0 → libres-0.9.1}/docs/_static/favicon.ico +0 -0
  23. {libres-0.9.0 → libres-0.9.1}/docs/_static/logo.svg +0 -0
  24. {libres-0.9.0 → libres-0.9.1}/docs/api.rst +0 -0
  25. {libres-0.9.0 → libres-0.9.1}/docs/concepts.rst +0 -0
  26. {libres-0.9.0 → libres-0.9.1}/docs/conf.py +0 -0
  27. {libres-0.9.0 → libres-0.9.1}/docs/customizations.rst +0 -0
  28. {libres-0.9.0 → libres-0.9.1}/docs/faq.rst +0 -0
  29. {libres-0.9.0 → libres-0.9.1}/docs/index.rst +0 -0
  30. {libres-0.9.0 → libres-0.9.1}/docs/requirements.txt +0 -0
  31. {libres-0.9.0 → libres-0.9.1}/docs/under_the_hood.rst +0 -0
  32. {libres-0.9.0 → libres-0.9.1}/setup.cfg +0 -0
  33. {libres-0.9.0 → libres-0.9.1}/src/libres/.gitignore +0 -0
  34. {libres-0.9.0 → libres-0.9.1}/src/libres/context/__init__.py +0 -0
  35. {libres-0.9.0 → libres-0.9.1}/src/libres/context/core.py +0 -0
  36. {libres-0.9.0 → libres-0.9.1}/src/libres/context/exposure.py +0 -0
  37. {libres-0.9.0 → libres-0.9.1}/src/libres/context/session.py +0 -0
  38. {libres-0.9.0 → libres-0.9.1}/src/libres/context/settings.py +0 -0
  39. {libres-0.9.0 → libres-0.9.1}/src/libres/db/__init__.py +0 -0
  40. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/__init__.py +0 -0
  41. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/allocation.py +0 -0
  42. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/base.py +0 -0
  43. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/other.py +0 -0
  44. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/reservation.py +0 -0
  45. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/reserved_slot.py +0 -0
  46. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/timestamp.py +0 -0
  47. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/__init__.py +0 -0
  48. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/json_type.py +0 -0
  49. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/utcdatetime.py +0 -0
  50. {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/uuid_type.py +0 -0
  51. {libres-0.9.0 → libres-0.9.1}/src/libres/db/queries.py +0 -0
  52. {libres-0.9.0 → libres-0.9.1}/src/libres/modules/__init__.py +0 -0
  53. {libres-0.9.0 → libres-0.9.1}/src/libres/modules/errors.py +0 -0
  54. {libres-0.9.0 → libres-0.9.1}/src/libres/modules/events.py +0 -0
  55. {libres-0.9.0 → libres-0.9.1}/src/libres/modules/rasterizer.py +0 -0
  56. {libres-0.9.0 → libres-0.9.1}/src/libres/modules/utils.py +0 -0
  57. {libres-0.9.0 → libres-0.9.1}/src/libres/py.typed +0 -0
  58. {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/SOURCES.txt +0 -0
  59. {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/dependency_links.txt +0 -0
  60. {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/not-zip-safe +0 -0
  61. {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/requires.txt +0 -0
  62. {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/top_level.txt +0 -0
@@ -1,6 +1,13 @@
1
1
  Changelog
2
2
  ---------
3
3
 
4
+ 0.9.1 (05.08.2025)
5
+ ~~~~~~~~~~~~~~~~~~~
6
+
7
+ - Fixes bug in `Scheduler.search_allocations` when the searched
8
+ time range contains multiple DST <-> ST transitions.
9
+ [Daverball]
10
+
4
11
  0.9.0 (23.05.2025)
5
12
  ~~~~~~~~~~~~~~~~~~~
6
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libres
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: A library to reserve things
5
5
  Home-page: http://github.com/seantis/libres/
6
6
  Author: Denis Krienbühl
@@ -142,6 +142,13 @@ After this, create a new release on Github.
142
142
  Changelog
143
143
  ---------
144
144
 
145
+ 0.9.1 (05.08.2025)
146
+ ~~~~~~~~~~~~~~~~~~~
147
+
148
+ - Fixes bug in `Scheduler.search_allocations` when the searched
149
+ time range contains multiple DST <-> ST transitions.
150
+ [Daverball]
151
+
145
152
  0.9.0 (23.05.2025)
146
153
  ~~~~~~~~~~~~~~~~~~~
147
154
 
@@ -11,7 +11,7 @@ branch = true
11
11
  source = ["src"]
12
12
 
13
13
  [tool.bumpversion]
14
- current_version = "0.9.0"
14
+ current_version = "0.9.1"
15
15
  commit = true
16
16
  message = "Release {new_version}"
17
17
  tag = true
@@ -108,6 +108,7 @@ select = [
108
108
  "PERF",
109
109
  "PGH004",
110
110
  "PIE",
111
+ "PT",
111
112
  "PYI",
112
113
  "Q",
113
114
  "RUF",
@@ -164,11 +165,11 @@ allowed-confusables = ["×"]
164
165
  preview = true
165
166
 
166
167
  [tool.ruff.lint.extend-per-file-ignores]
168
+ "src/**/*.py" = ["PT"]
167
169
  "tests/**/*.py" = [
168
170
  "C4",
169
171
  "D",
170
172
  "FLY002",
171
- "I002",
172
173
  "ISC",
173
174
  "N",
174
175
  "Q",
@@ -219,65 +220,80 @@ docstring-code-format = true
219
220
  docstring-code-line-length = "dynamic"
220
221
 
221
222
  [tool.tox]
222
- legacy_tox_ini = """
223
- [tox]
224
- envlist = py39,py310,py311,flake8,bandit,mypy,report
225
-
226
- [gh-actions]
227
- python =
228
- 3.9: py39
229
- 3.10: py310
230
- 3.11: py311,flake8,bandit,mypy
231
- 3.12: py312
232
- 3.13: py313
223
+ requires = ["tox>=4.21"]
224
+ envlist = [
225
+ "3.9",
226
+ "3.10",
227
+ "3.11",
228
+ "3.12",
229
+ "3.13",
230
+ "flake8",
231
+ "ruff",
232
+ "bandit",
233
+ "mypy",
234
+ "report"
235
+ ]
233
236
 
234
- [testenv]
235
- usedevelop = true
236
- setenv =
237
- py{39,310,311,312,313}: COVERAGE_FILE = .coverage.{envname}
238
- deps =
239
- -e{toxinidir}[test]
240
- commands = pytest --cov --cov-report= {posargs}
237
+ [tool.tox.gh.python]
238
+ "3.9" = ["3.9"]
239
+ "3.10" = ["3.10"]
240
+ "3.11" = ["3.11", "flake8", "bandit", "ruff", "mypy"]
241
+ "3.12" = ["3.12"]
242
+ "3.13" = ["3.13"]
241
243
 
242
- [testenv:ruff]
243
- basepython = python3.11
244
+ [tool.tox.env_run_base]
245
+ use_develop = true
246
+ set_env = { COVERAGE_FILE = ".coverage.{envname}" }
247
+ deps = ["-e{toxinidir}[test]"]
248
+ commands = [
249
+ [
250
+ "pytest",
251
+ "--cov",
252
+ "--cov-report=",
253
+ { replace = "posargs", default = [], extend = true }
254
+ ],
255
+ ]
256
+ [tool.tox.env.ruff]
257
+ base_python = ["python3.11"]
244
258
  skip_install = true
245
- deps =
246
- ruff
247
- commands = ruff check
259
+ deps = ["ruff"]
260
+ commands = [["ruff", "check"]]
248
261
 
249
- [testenv:flake8]
250
- basepython = python3.11
262
+ [tool.tox.env.flake8]
263
+ base_python = ["python3.11"]
251
264
  skip_install = true
252
- deps =
253
- flake8
254
- flake8-type-checking
255
- commands = flake8 src/ tests/
265
+ deps = [
266
+ "flake8",
267
+ "flake8-type-checking",
268
+ ]
269
+ commands = [["flake8", "src{/}", "tests{/}"]]
256
270
 
257
- [testenv:bandit]
258
- basepython = python3.11
271
+ [tool.tox.env.bandit]
272
+ base_python = ["python3.11"]
259
273
  skip_install = true
260
- deps =
261
- bandit[toml]
262
- commands = bandit -q -c pyproject.toml -r src
274
+ deps = ["bandit[toml]"]
275
+ commands = [["bandit", "-q", "-c", "pyproject.toml", "-r", "src"]]
263
276
 
264
- [testenv:mypy]
265
- basepython = python3.11
266
- deps =
267
- -e{toxinidir}[mypy]
268
- commands =
269
- mypy -p libres --python-version 3.9
270
- mypy -p libres --python-version 3.10
271
- mypy -p libres --python-version 3.11
272
- mypy -p libres --python-version 3.12
273
- mypy -p libres --python-version 3.13
274
-
275
- [testenv:report]
276
- deps =
277
- coverage
277
+ [tool.tox.env.mypy]
278
+ base_python = ["python3.11"]
278
279
  skip_install = true
279
- commands =
280
- coverage combine
281
- coverage report -m
280
+ deps = ["-e{toxinidir}[mypy]"]
281
+ commands = [
282
+ ["mypy", "-p", "libres", "--python-version", "3.9"],
283
+ ["mypy", "-p", "libres", "--python-version", "3.10"],
284
+ ["mypy", "-p", "libres", "--python-version", "3.11"],
285
+ ["mypy", "-p", "libres", "--python-version", "3.12"],
286
+ ["mypy", "-p", "libres", "--python-version", "3.13"],
287
+ ]
282
288
 
283
- """
289
+ [tool.tox.env.report]
290
+ base_python = ["python3.11"]
291
+ skip_install = true
292
+ ignore_errors = true
293
+ ignore_outcome = true
294
+ set_env = { COVERAGE_FILE = ".coverage" }
295
+ deps = ["coverage"]
296
+ commands = [
297
+ ["coverage", "combine", "{toxinidir}"],
298
+ ["coverage", "report", "-m"],
299
+ ]
@@ -5,7 +5,7 @@ from libres.db import new_scheduler
5
5
 
6
6
  registry = create_default_registry()
7
7
 
8
- __version__ = '0.9.0'
8
+ __version__ = '0.9.1'
9
9
  __all__ = (
10
10
  'new_scheduler',
11
11
  'registry'
@@ -82,6 +82,8 @@ class Registry:
82
82
  """
83
83
 
84
84
  contexts: dict[str, Context]
85
+ # FIXME: Why do we allow this to be None? Do we make use
86
+ # of this anywhere?
85
87
  master_context: Context | None = None
86
88
 
87
89
  def __init__(self) -> None:
@@ -269,7 +269,7 @@ class Scheduler(ContextServicesMixin):
269
269
  allocations = allocations.with_entities(Allocation.id)
270
270
  allocations = allocations.filter(Allocation.group.in_(groups))
271
271
 
272
- query = self.managed_allocations()
272
+ query: Query[Allocation] = self.managed_allocations()
273
273
  query = query.join(ReservedSlot)
274
274
  query = query.filter(
275
275
  ReservedSlot.reservation_token == token
@@ -1629,8 +1629,6 @@ class Scheduler(ContextServicesMixin):
1629
1629
  assert start
1630
1630
  assert end
1631
1631
 
1632
- start, end = self._prepare_range(start, end)
1633
-
1634
1632
  assert whole_day in ('yes', 'no', 'any')
1635
1633
  assert groups in ('yes', 'no', 'any')
1636
1634
 
@@ -1645,7 +1643,7 @@ class Scheduler(ContextServicesMixin):
1645
1643
  else:
1646
1644
  day_numbers = None
1647
1645
 
1648
- query = self.allocations_in_range(start, end)
1646
+ query = self.allocations_in_range(*self._prepare_range(start, end))
1649
1647
  query = query.order_by(Allocation._start)
1650
1648
 
1651
1649
  allocations = []
@@ -1657,13 +1655,19 @@ class Scheduler(ContextServicesMixin):
1657
1655
 
1658
1656
  if not self.is_allocation_exposed(allocation):
1659
1657
  continue
1660
-
1661
- s = datetime.combine(allocation.start.date(), start.time())
1662
- e = datetime.combine(allocation.end.date(), end.time())
1663
-
1664
- # the raw dates will be UTC
1665
- s = sedate.replace_timezone(s, 'UTC')
1666
- e = sedate.replace_timezone(e, 'UTC')
1658
+ allocation_start = allocation.display_start()
1659
+ # NOTE: We want the correct timezone, but we don't want the
1660
+ # date to be on the next day for a full-day reservation
1661
+ # so we skip the microsecond addition
1662
+ allocation_end = sedate.to_timezone(allocation.end, self.timezone)
1663
+ s = sedate.standardize_date(datetime.combine(
1664
+ allocation_start.date(),
1665
+ start.time()
1666
+ ), self.timezone)
1667
+ e = sedate.standardize_date(datetime.combine(
1668
+ allocation_end.date(),
1669
+ end.time()
1670
+ ), self.timezone)
1667
1671
 
1668
1672
  if not allocation.overlaps(s, e):
1669
1673
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libres
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: A library to reserve things
5
5
  Home-page: http://github.com/seantis/libres/
6
6
  Author: Denis Krienbühl
@@ -142,6 +142,13 @@ After this, create a new release on Github.
142
142
  Changelog
143
143
  ---------
144
144
 
145
+ 0.9.1 (05.08.2025)
146
+ ~~~~~~~~~~~~~~~~~~~
147
+
148
+ - Fixes bug in `Scheduler.search_allocations` when the searched
149
+ time range contains multiple DST <-> ST transitions.
150
+ [Daverball]
151
+
145
152
  0.9.0 (23.05.2025)
146
153
  ~~~~~~~~~~~~~~~~~~~
147
154
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import pytest
2
4
 
3
5
  from datetime import datetime, time
@@ -8,12 +10,17 @@ from sqlalchemy.exc import IntegrityError
8
10
  from uuid import uuid4 as new_uuid
9
11
 
10
12
 
11
- def test_add_allocation(scheduler):
13
+ from typing import TYPE_CHECKING
14
+ if TYPE_CHECKING:
15
+ from libres.db.scheduler import Scheduler
16
+
12
17
 
13
- allocation = Allocation(raster=15, resource=scheduler.resource)
18
+ def test_add_allocation(scheduler: Scheduler) -> None:
19
+
20
+ allocation = Allocation(raster=15, resource=scheduler.resource) # type: ignore[misc]
14
21
  allocation.start = datetime(2011, 1, 1, 15, tzinfo=utc)
15
22
  allocation.end = datetime(2011, 1, 1, 15, 59, tzinfo=utc)
16
- allocation.group = new_uuid().hex
23
+ allocation.group = new_uuid()
17
24
  allocation.mirror_of = scheduler.resource
18
25
 
19
26
  scheduler.session.add(allocation)
@@ -22,14 +29,14 @@ def test_add_allocation(scheduler):
22
29
  assert scheduler.session.query(Allocation).count() == 1
23
30
 
24
31
 
25
- def test_add_invalid_allocation(scheduler):
26
- scheduler.session.add(Allocation(raster=15))
32
+ def test_add_invalid_allocation(scheduler: Scheduler) -> None:
33
+ scheduler.session.add(Allocation(raster=15)) # type: ignore[misc]
27
34
 
28
35
  with pytest.raises(IntegrityError):
29
36
  scheduler.commit()
30
37
 
31
38
 
32
- def test_get_master(scheduler):
39
+ def test_get_master(scheduler: Scheduler) -> None:
33
40
  dates = [
34
41
  (datetime(2015, 2, 6, 12), datetime(2015, 2, 6, 13)),
35
42
  (datetime(2015, 2, 7, 12), datetime(2015, 2, 7, 13))
@@ -50,7 +57,7 @@ def test_get_master(scheduler):
50
57
  assert siblings[1].get_master().id == allocations[0].id
51
58
 
52
59
 
53
- def test_imaginary_siblings(scheduler):
60
+ def test_imaginary_siblings(scheduler: Scheduler) -> None:
54
61
  dates = [(datetime(2015, 2, 6, 12), datetime(2015, 2, 6, 13))]
55
62
  allocations = scheduler.allocate(dates=dates, quota=2)
56
63
 
@@ -65,8 +72,8 @@ def test_imaginary_siblings(scheduler):
65
72
  imaginary_allocation.siblings(imaginary=False)
66
73
 
67
74
 
68
- def test_date_functions(scheduler):
69
- allocation = Allocation(raster=60, resource=scheduler.resource)
75
+ def test_date_functions(scheduler: Scheduler) -> None:
76
+ allocation = Allocation(raster=60, resource=scheduler.resource) # type: ignore[misc]
70
77
  allocation.timezone = 'UTC'
71
78
  allocation.start = datetime(2011, 1, 1, 12, 30, tzinfo=utc)
72
79
  allocation.end = datetime(2011, 1, 1, 14, 00, tzinfo=utc)
@@ -89,8 +96,8 @@ def test_date_functions(scheduler):
89
96
  assert not allocation.contains(start, end)
90
97
 
91
98
 
92
- def test_whole_day():
93
- allocation = Allocation(
99
+ def test_whole_day() -> None:
100
+ allocation = Allocation( # type: ignore[misc]
94
101
  raster=15, resource=new_uuid(), timezone='Europe/Zurich'
95
102
  )
96
103
 
@@ -113,11 +120,11 @@ def test_whole_day():
113
120
  allocation.start = datetime(2013, 1, 1, 15, 0, tzinfo=utc)
114
121
  allocation.end = datetime(2013, 1, 1, 0, 0, tzinfo=utc)
115
122
 
116
- with pytest.raises(ValueError):
123
+ with pytest.raises(ValueError, match=r'equal or greater than the start'):
117
124
  _ = allocation.whole_day
118
125
 
119
126
 
120
- def test_separate_allocation(scheduler):
127
+ def test_separate_allocation(scheduler: Scheduler) -> None:
121
128
 
122
129
  # allocations which are partly available are never in a group
123
130
  # (some code, for example allocation.is_separarate, depends on that)
@@ -147,10 +154,10 @@ def test_separate_allocation(scheduler):
147
154
  assert allocations[0].is_separate
148
155
 
149
156
 
150
- def test_limit_timespan():
157
+ def test_limit_timespan() -> None:
151
158
 
152
159
  # if not partly availabe the limit is always the same
153
- allocation = Allocation(
160
+ allocation = Allocation( # type: ignore[misc]
154
161
  raster=15, resource=new_uuid(), partly_available=False, timezone='UTC'
155
162
  )
156
163
 
@@ -166,7 +173,7 @@ def test_limit_timespan():
166
173
  )
167
174
 
168
175
  # if partly available, more complex things happen
169
- allocation = Allocation(
176
+ allocation = Allocation( # type: ignore[misc]
170
177
  raster=15, resource=new_uuid(), partly_available=True, timezone='UTC'
171
178
  )
172
179
 
@@ -227,7 +234,12 @@ def test_limit_timespan():
227
234
  )
228
235
 
229
236
 
230
- def add_reservation(scheduler, allocation, start, end):
237
+ def add_reservation(
238
+ scheduler: Scheduler,
239
+ allocation: Allocation,
240
+ start: datetime,
241
+ end: datetime
242
+ ) -> None:
231
243
  # small helper to reserve all the slots between start and end
232
244
  reservation = new_uuid()
233
245
  for s, e in allocation.all_slots():
@@ -246,14 +258,14 @@ def add_reservation(scheduler, allocation, start, end):
246
258
  scheduler.session.refresh(allocation)
247
259
 
248
260
 
249
- def test_availability_partitions(scheduler):
250
- allocation = Allocation(
261
+ def test_availability_partitions(scheduler: Scheduler) -> None:
262
+ allocation = Allocation( # type: ignore[misc]
251
263
  raster=15, resource=new_uuid(), partly_available=True,
252
264
  timezone='Europe/Zurich'
253
265
  )
254
266
  allocation.start = datetime(2022, 9, 29, 22, tzinfo=utc)
255
267
  allocation.end = datetime(2022, 9, 30, 1, 59, 59, 999999, tzinfo=utc)
256
- allocation.group = new_uuid().hex
268
+ allocation.group = new_uuid()
257
269
  allocation.mirror_of = scheduler.resource
258
270
  scheduler.session.add(allocation)
259
271
  scheduler.session.flush()
@@ -281,14 +293,14 @@ def test_availability_partitions(scheduler):
281
293
  assert allocation.availability == 75.0
282
294
 
283
295
 
284
- def test_availability_partitions_dst_to_st(scheduler):
285
- allocation = Allocation(
296
+ def test_availability_partitions_dst_to_st(scheduler: Scheduler) -> None:
297
+ allocation = Allocation( # type: ignore[misc]
286
298
  raster=15, resource=new_uuid(), partly_available=True,
287
299
  timezone='Europe/Zurich'
288
300
  )
289
301
  allocation.start = datetime(2022, 10, 29, 22, tzinfo=utc)
290
302
  allocation.end = datetime(2022, 10, 30, 2, 59, 59, 999999, tzinfo=utc)
291
- allocation.group = new_uuid().hex
303
+ allocation.group = new_uuid()
292
304
  allocation.mirror_of = scheduler.resource
293
305
  scheduler.session.add(allocation)
294
306
  scheduler.session.flush()
@@ -337,15 +349,18 @@ def test_availability_partitions_dst_to_st(scheduler):
337
349
  assert allocation.availability == 60.0
338
350
 
339
351
 
340
- def test_availability_partitions_dst_to_st_during_ambiguous_time(scheduler):
341
- allocation = Allocation(
352
+ def test_availability_partitions_dst_to_st_during_ambiguous_time(
353
+ scheduler: Scheduler
354
+ ) -> None:
355
+
356
+ allocation = Allocation( # type: ignore[misc]
342
357
  raster=5, resource=new_uuid(), partly_available=True,
343
358
  timezone='Europe/Zurich'
344
359
  )
345
360
  # our allocation starts during the ambigious time period we skip
346
361
  allocation.start = datetime(2022, 10, 30, 0, 40, tzinfo=utc)
347
362
  allocation.end = datetime(2022, 10, 30, 22, 59, 59, 999999, tzinfo=utc)
348
- allocation.group = new_uuid().hex
363
+ allocation.group = new_uuid()
349
364
  allocation.mirror_of = scheduler.resource
350
365
  scheduler.session.add(allocation)
351
366
  scheduler.session.flush()
@@ -365,14 +380,14 @@ def test_availability_partitions_dst_to_st_during_ambiguous_time(scheduler):
365
380
  assert allocation.normalized_availability == 75.0
366
381
 
367
382
 
368
- def test_availability_partitions_st_to_dst(scheduler):
369
- allocation = Allocation(
383
+ def test_availability_partitions_st_to_dst(scheduler: Scheduler) -> None:
384
+ allocation = Allocation( # type: ignore[misc]
370
385
  raster=15, resource=new_uuid(), partly_available=True,
371
386
  timezone='Europe/Zurich'
372
387
  )
373
388
  allocation.start = datetime(2022, 3, 26, 23, tzinfo=utc)
374
389
  allocation.end = datetime(2022, 3, 27, 2, 59, 59, 999999, tzinfo=utc)
375
- allocation.group = new_uuid().hex
390
+ allocation.group = new_uuid()
376
391
  allocation.mirror_of = scheduler.resource
377
392
  scheduler.session.add(allocation)
378
393
  scheduler.session.flush()
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import threading
2
4
  import pytest
3
5
  import random
@@ -6,9 +8,10 @@ from libres.modules import errors
6
8
  from libres.context.registry import Registry
7
9
 
8
10
 
9
- def test_registry_contexts():
11
+ def test_registry_contexts() -> None:
10
12
  r = Registry()
11
13
 
14
+ assert r.master_context is not None
12
15
  assert r.master_context.name == 'master'
13
16
  assert r.master_context is r.current_context
14
17
  assert r.is_existing_context('master')
@@ -36,7 +39,7 @@ def test_registry_contexts():
36
39
  assert r.current_context.name == 'bar'
37
40
 
38
41
 
39
- def test_autocreate():
42
+ def test_autocreate() -> None:
40
43
  r = Registry()
41
44
 
42
45
  ctx = r.get_context('yo', autocreate=True)
@@ -45,7 +48,7 @@ def test_autocreate():
45
48
  assert r.get_context('yo') is ctx
46
49
 
47
50
 
48
- def test_assert_existence():
51
+ def test_assert_existence() -> None:
49
52
  r = Registry()
50
53
 
51
54
  with pytest.raises(errors.UnknownContext):
@@ -57,7 +60,7 @@ def test_assert_existence():
57
60
  r.assert_does_not_exist('foo')
58
61
 
59
62
 
60
- def test_replace():
63
+ def test_replace() -> None:
61
64
  r = Registry()
62
65
 
63
66
  ctx = r.register_context('gabba gabba')
@@ -74,7 +77,7 @@ def test_replace():
74
77
  ctx = r.register_context('gabba gabba', replace=True)
75
78
 
76
79
 
77
- def test_different_registries():
80
+ def test_different_registries() -> None:
78
81
  r1 = Registry()
79
82
  r2 = Registry()
80
83
 
@@ -84,7 +87,7 @@ def test_different_registries():
84
87
  r2.register_context('foo')
85
88
 
86
89
 
87
- def test_locked_contexts():
90
+ def test_locked_contexts() -> None:
88
91
  r = Registry()
89
92
 
90
93
  context = r.register_context('test')
@@ -95,9 +98,10 @@ def test_locked_contexts():
95
98
  context.set('foo', 'bar')
96
99
 
97
100
 
98
- def test_master_fallback():
101
+ def test_master_fallback() -> None:
99
102
  r = Registry()
100
103
 
104
+ assert r.master_context is not None
101
105
  r.master_context.set_setting('host', 'localhost')
102
106
  assert r.master_context.get_setting('host') == 'localhost'
103
107
 
@@ -116,9 +120,10 @@ def test_master_fallback():
116
120
  assert another_app.get_setting('host') == 'localhost'
117
121
 
118
122
 
119
- def test_services():
123
+ def test_services() -> None:
120
124
  r = Registry()
121
125
 
126
+ assert r.master_context is not None
122
127
  r.master_context.set_service('service', factory=lambda ctx: object())
123
128
  first_call = r.master_context.get_service('service')
124
129
  second_call = r.master_context.get_service('service')
@@ -126,9 +131,10 @@ def test_services():
126
131
  assert first_call is not second_call
127
132
 
128
133
 
129
- def test_services_cache():
134
+ def test_services_cache() -> None:
130
135
  r = Registry()
131
136
 
137
+ assert r.master_context is not None
132
138
  r.master_context.set_service(
133
139
  'service', factory=lambda ctx: object(), cache=True
134
140
  )
@@ -139,17 +145,17 @@ def test_services_cache():
139
145
  assert first_call is second_call
140
146
 
141
147
 
142
- def test_threading_contexts():
148
+ def test_threading_contexts() -> None:
143
149
  r = Registry()
144
150
 
145
151
  class Application(threading.Thread):
146
152
 
147
- def __init__(self, name, registry):
153
+ def __init__(self, name: str, registry: Registry) -> None:
148
154
  threading.Thread.__init__(self)
149
155
  self.registry = registry
150
156
  self.name = name
151
157
 
152
- def run(self):
158
+ def run(self) -> None:
153
159
  if self.registry.is_existing_context(self.name):
154
160
  self.registry.get_context(self.name).switch_to()
155
161
  else:
@@ -157,7 +163,7 @@ def test_threading_contexts():
157
163
 
158
164
  self.result = self.registry.get_current_context().name
159
165
 
160
- def join(self):
166
+ def join(self) -> str: # type: ignore[override]
161
167
  threading.Thread.join(self)
162
168
  return self.result
163
169
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import pytest
2
4
 
3
5
  from datetime import datetime, timedelta
@@ -7,16 +9,21 @@ from sedate import standardize_date
7
9
  from uuid import uuid4
8
10
 
9
11
 
10
- def test_reservation_title():
12
+ from typing import TYPE_CHECKING
13
+ if TYPE_CHECKING:
14
+ from libres.db.scheduler import Scheduler
15
+
16
+
17
+ def test_reservation_title() -> None:
11
18
  assert Reservation(email='test@example.org').title == 'test@example.org'
12
19
 
13
20
 
14
- def test_unknown_target_type():
21
+ def test_unknown_target_type() -> None:
15
22
  with pytest.raises(NotImplementedError):
16
- Reservation(target_type='foo').timespans()
23
+ Reservation(target_type='foo').timespans() # type: ignore[misc]
17
24
 
18
25
 
19
- def test_reservation_timespans(scheduler):
26
+ def test_reservation_timespans(scheduler: Scheduler) -> None:
20
27
  start = datetime(2015, 2, 5, 15)
21
28
  end = datetime(2015, 2, 5, 16)
22
29
 
@@ -33,7 +40,7 @@ def test_reservation_timespans(scheduler):
33
40
  assert timespans[0].end == reservation.end + timedelta(microseconds=1)
34
41
 
35
42
 
36
- def test_group_reservation_timespans(scheduler):
43
+ def test_group_reservation_timespans(scheduler: Scheduler) -> None:
37
44
  dates = [
38
45
  (datetime(2015, 2, 5, 15), datetime(2015, 2, 5, 16)),
39
46
  (datetime(2015, 2, 6, 15), datetime(2015, 2, 6, 16))
@@ -57,7 +64,7 @@ def test_group_reservation_timespans(scheduler):
57
64
  - timedelta(microseconds=1)
58
65
 
59
66
 
60
- def test_overlapping_reservations(scheduler):
67
+ def test_overlapping_reservations(scheduler: Scheduler) -> None:
61
68
  start = datetime(2015, 2, 5, 15)
62
69
  end = datetime(2015, 2, 5, 16)
63
70