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.
- {libres-0.9.0 → libres-0.9.1}/HISTORY.rst +7 -0
- {libres-0.9.0/src/libres.egg-info → libres-0.9.1}/PKG-INFO +8 -1
- {libres-0.9.0 → libres-0.9.1}/pyproject.toml +70 -54
- {libres-0.9.0 → libres-0.9.1}/src/libres/__init__.py +1 -1
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/registry.py +2 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/scheduler.py +15 -11
- {libres-0.9.0 → libres-0.9.1/src/libres.egg-info}/PKG-INFO +8 -1
- {libres-0.9.0 → libres-0.9.1}/tests/test_allocation.py +44 -29
- {libres-0.9.0 → libres-0.9.1}/tests/test_registry.py +19 -13
- {libres-0.9.0 → libres-0.9.1}/tests/test_reservation.py +13 -6
- {libres-0.9.0 → libres-0.9.1}/tests/test_reserved_slot.py +12 -5
- {libres-0.9.0 → libres-0.9.1}/tests/test_scheduler.py +133 -105
- {libres-0.9.0 → libres-0.9.1}/tests/test_session.py +30 -19
- {libres-0.9.0 → libres-0.9.1}/tests/test_test.py +12 -2
- {libres-0.9.0 → libres-0.9.1}/tests/test_types.py +4 -2
- {libres-0.9.0 → libres-0.9.1}/tests/test_utils.py +3 -1
- {libres-0.9.0 → libres-0.9.1}/LICENSE +0 -0
- {libres-0.9.0 → libres-0.9.1}/MANIFEST.in +0 -0
- {libres-0.9.0 → libres-0.9.1}/README.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/Makefile +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/_static/custom.css +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/_static/favicon.ico +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/_static/logo.svg +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/api.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/concepts.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/conf.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/customizations.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/faq.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/index.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/requirements.txt +0 -0
- {libres-0.9.0 → libres-0.9.1}/docs/under_the_hood.rst +0 -0
- {libres-0.9.0 → libres-0.9.1}/setup.cfg +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/.gitignore +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/__init__.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/core.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/exposure.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/session.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/context/settings.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/__init__.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/__init__.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/allocation.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/base.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/other.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/reservation.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/reserved_slot.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/timestamp.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/__init__.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/json_type.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/utcdatetime.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/models/types/uuid_type.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/db/queries.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/modules/__init__.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/modules/errors.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/modules/events.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/modules/rasterizer.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/modules/utils.py +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres/py.typed +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/SOURCES.txt +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/dependency_links.txt +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/not-zip-safe +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/requires.txt +0 -0
- {libres-0.9.0 → libres-0.9.1}/src/libres.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: libres
|
|
3
|
-
Version: 0.9.
|
|
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.
|
|
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
|
-
|
|
223
|
-
[
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
3.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
[
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
[
|
|
243
|
-
|
|
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
|
-
|
|
247
|
-
commands = ruff check
|
|
259
|
+
deps = ["ruff"]
|
|
260
|
+
commands = [["ruff", "check"]]
|
|
248
261
|
|
|
249
|
-
[
|
|
250
|
-
|
|
262
|
+
[tool.tox.env.flake8]
|
|
263
|
+
base_python = ["python3.11"]
|
|
251
264
|
skip_install = true
|
|
252
|
-
deps =
|
|
253
|
-
flake8
|
|
254
|
-
flake8-type-checking
|
|
255
|
-
|
|
265
|
+
deps = [
|
|
266
|
+
"flake8",
|
|
267
|
+
"flake8-type-checking",
|
|
268
|
+
]
|
|
269
|
+
commands = [["flake8", "src{/}", "tests{/}"]]
|
|
256
270
|
|
|
257
|
-
[
|
|
258
|
-
|
|
271
|
+
[tool.tox.env.bandit]
|
|
272
|
+
base_python = ["python3.11"]
|
|
259
273
|
skip_install = true
|
|
260
|
-
deps =
|
|
261
|
-
|
|
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
|
-
[
|
|
265
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
+
]
|
|
@@ -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
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
s = sedate.
|
|
1666
|
-
|
|
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.
|
|
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
|
-
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from libres.db.scheduler import Scheduler
|
|
16
|
+
|
|
12
17
|
|
|
13
|
-
|
|
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()
|
|
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(
|
|
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()
|
|
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()
|
|
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(
|
|
341
|
-
|
|
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()
|
|
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()
|
|
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
|
-
|
|
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
|
|