experimaestro 1.10.0__py3-none-any.whl → 1.16.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.
Files changed (58) hide show
  1. experimaestro/cli/__init__.py +2 -2
  2. experimaestro/cli/filter.py +1 -1
  3. experimaestro/connectors/__init__.py +2 -2
  4. experimaestro/core/arguments.py +11 -8
  5. experimaestro/core/identifier.py +11 -6
  6. experimaestro/core/objects/config.py +127 -193
  7. experimaestro/core/objects/config_walk.py +4 -6
  8. experimaestro/core/objects.pyi +2 -6
  9. experimaestro/core/serializers.py +1 -8
  10. experimaestro/core/types.py +1 -4
  11. experimaestro/launcherfinder/registry.py +6 -6
  12. experimaestro/launcherfinder/specs.py +8 -1
  13. experimaestro/launchers/slurm/base.py +1 -1
  14. experimaestro/run.py +2 -0
  15. experimaestro/scheduler/base.py +0 -2
  16. experimaestro/scheduler/workspace.py +44 -1
  17. experimaestro/server/__init__.py +12 -6
  18. experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  19. experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  20. experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  21. experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  22. experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  23. experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  24. experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  25. experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  26. experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  27. experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  28. experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  29. experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  30. experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  31. experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  32. experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  33. experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  34. experimaestro/server/data/favicon.ico +0 -0
  35. experimaestro/server/data/index.css +22963 -0
  36. experimaestro/server/data/index.css.map +1 -0
  37. experimaestro/server/data/index.html +27 -0
  38. experimaestro/server/data/index.js +101770 -0
  39. experimaestro/server/data/index.js.map +1 -0
  40. experimaestro/server/data/login.html +22 -0
  41. experimaestro/server/data/manifest.json +15 -0
  42. experimaestro/tests/tasks/all.py +7 -0
  43. experimaestro/tests/test_dependencies.py +0 -6
  44. experimaestro/tests/test_generators.py +93 -0
  45. experimaestro/tests/test_identifier.py +87 -76
  46. experimaestro/tests/test_instance.py +0 -12
  47. experimaestro/tests/test_param.py +1 -4
  48. experimaestro/tests/test_serializers.py +0 -59
  49. experimaestro/tests/test_tasks.py +10 -23
  50. experimaestro/tests/test_types.py +2 -2
  51. experimaestro/utils/multiprocessing.py +44 -0
  52. experimaestro/utils/resources.py +1 -1
  53. {experimaestro-1.10.0.dist-info → experimaestro-1.16.0.dist-info}/METADATA +5 -4
  54. {experimaestro-1.10.0.dist-info → experimaestro-1.16.0.dist-info}/RECORD +57 -32
  55. {experimaestro-1.10.0.dist-info → experimaestro-1.16.0.dist-info}/WHEEL +1 -1
  56. experimaestro/compat.py +0 -6
  57. {experimaestro-1.10.0.dist-info → experimaestro-1.16.0.dist-info}/entry_points.txt +0 -0
  58. {experimaestro-1.10.0.dist-info → experimaestro-1.16.0.dist-info/licenses}/LICENSE +0 -0
@@ -6,10 +6,10 @@ from typing import ClassVar, Dict, Optional, Set, Type, Union
6
6
  from pathlib import Path
7
7
  import typing
8
8
  from omegaconf import DictConfig, OmegaConf, SCMode
9
- import pkg_resources
10
9
  from experimaestro.utils import logger
11
10
  from .base import ConnectorConfiguration, TokenConfiguration
12
11
  from .specs import HostRequirement, RequirementUnion
12
+ from importlib.metadata import entry_points
13
13
 
14
14
  if typing.TYPE_CHECKING:
15
15
  from experimaestro.launchers import Launcher
@@ -59,9 +59,9 @@ class LauncherRegistry:
59
59
  ).expanduser()
60
60
 
61
61
  if LauncherRegistry.CURRENT_CONFIG_DIR not in LauncherRegistry.INSTANCES:
62
- LauncherRegistry.INSTANCES[
63
- LauncherRegistry.CURRENT_CONFIG_DIR
64
- ] = LauncherRegistry(LauncherRegistry.CURRENT_CONFIG_DIR)
62
+ LauncherRegistry.INSTANCES[LauncherRegistry.CURRENT_CONFIG_DIR] = (
63
+ LauncherRegistry(LauncherRegistry.CURRENT_CONFIG_DIR)
64
+ )
65
65
 
66
66
  return LauncherRegistry.INSTANCES[LauncherRegistry.CURRENT_CONFIG_DIR]
67
67
 
@@ -75,10 +75,10 @@ class LauncherRegistry:
75
75
  self.find_launcher_fn = None
76
76
 
77
77
  # Use entry points for connectors and launchers
78
- for entry_point in pkg_resources.iter_entry_points("experimaestro.connectors"):
78
+ for entry_point in entry_points(group="experimaestro.connectors"):
79
79
  entry_point.load().init_registry(self)
80
80
 
81
- for entry_point in pkg_resources.iter_entry_points("experimaestro.tokens"):
81
+ for entry_point in entry_points(group="experimaestro.tokens"):
82
82
  entry_point.load().init_registry(self)
83
83
 
84
84
  # Register the find launcher function if it exists
@@ -29,7 +29,7 @@ class CudaSpecification:
29
29
  def __repr__(self):
30
30
  return (
31
31
  f"CUDA({self.model} "
32
- f"max={format_size(self.memory)}/min={format_size(self.min_memory)})"
32
+ f"max={format_size(self.memory, binary=True)}/min={format_size(self.min_memory, binary=True)})"
33
33
  )
34
34
 
35
35
 
@@ -47,6 +47,13 @@ class CPUSpecification:
47
47
  cpu_per_gpu: int = 0
48
48
  """Number of CPU per GPU (0 if not defined)"""
49
49
 
50
+ def __repr__(self):
51
+ return (
52
+ f"CPU("
53
+ f"mem={format_size(self.memory, binary=True)}, cores={self.cores}"
54
+ ")"
55
+ )
56
+
50
57
  def match(self, other: "CPUSpecification"):
51
58
  return (self.memory >= other.memory) and (self.cores >= other.cores)
52
59
 
@@ -20,7 +20,7 @@ from experimaestro.launcherfinder.registry import (
20
20
  from experimaestro.utils import ThreadingCondition
21
21
  from experimaestro.tests.connectors.utils import OutputCaptureHandler
22
22
  from experimaestro.utils.asyncio import asyncThreadcheck
23
- from experimaestro.compat import cached_property
23
+ from functools import cached_property
24
24
  from experimaestro.launchers import Launcher
25
25
  from experimaestro.scriptbuilder import PythonScriptBuilder
26
26
  from experimaestro.connectors import (
experimaestro/run.py CHANGED
@@ -8,6 +8,7 @@ import json
8
8
  from typing import List
9
9
  import fasteners
10
10
  from experimaestro.notifications import progress, report_eoj
11
+ from experimaestro.utils.multiprocessing import delayed_shutdown
11
12
  from .core.types import ObjectType
12
13
  from experimaestro.utils import logger
13
14
  from experimaestro.core.objects import ConfigInformation
@@ -96,6 +97,7 @@ class TaskRunner:
96
97
  self.failedpath.write_text(str(code))
97
98
  self.cleanup()
98
99
  logger.info("Exiting")
100
+ delayed_shutdown(60, exit_code=code)
99
101
  sys.exit(1)
100
102
 
101
103
  def run(self):
@@ -992,9 +992,7 @@ class experiment:
992
992
  self.server.start()
993
993
 
994
994
  self.workspace.__enter__()
995
- (self.workspace.path / ".__experimaestro__").touch()
996
995
 
997
- global SIGNAL_HANDLER
998
996
  # Number of unfinished jobs
999
997
  self.unfinishedJobs = 0
1000
998
  self.taskOutputQueueSize = 0
@@ -2,10 +2,14 @@ from collections import ChainMap
2
2
  from enum import Enum
3
3
  from functools import cached_property
4
4
  from pathlib import Path
5
- from typing import Iterator, Optional
5
+ from typing import Optional
6
6
  from experimaestro.settings import WorkspaceSettings, Settings
7
7
 
8
8
 
9
+ # Current workspace version
10
+ WORKSPACE_VERSION = 0
11
+
12
+
9
13
  class RunMode(str, Enum):
10
14
  NORMAL = "normal"
11
15
  """Normal run"""
@@ -54,6 +58,45 @@ class Workspace:
54
58
  self.env = ChainMap({}, workspace_settings.env, settings.env)
55
59
 
56
60
  def __enter__(self):
61
+ # Check and update workspace version
62
+ version_file = self.path / ".__experimaestro__"
63
+
64
+ if version_file.exists():
65
+ # Read existing version
66
+ content = version_file.read_text().strip()
67
+ if content == "":
68
+ # Empty file = v0
69
+ workspace_version = 0
70
+ else:
71
+ try:
72
+ workspace_version = int(content)
73
+ except ValueError:
74
+ raise RuntimeError(
75
+ f"Invalid workspace version file at {version_file}: "
76
+ f"expected integer, got '{content}'"
77
+ )
78
+
79
+ # Check if workspace version is supported
80
+ if workspace_version > WORKSPACE_VERSION:
81
+ raise RuntimeError(
82
+ f"Workspace version {workspace_version} is not supported by "
83
+ f"this version of experimaestro (supports up to version "
84
+ f"{WORKSPACE_VERSION}). Please upgrade experimaestro."
85
+ )
86
+ if workspace_version < WORKSPACE_VERSION:
87
+ raise RuntimeError(
88
+ f"Workspace version {workspace_version} is not supported by "
89
+ "this version of experimaestro (please upgrade the experimaestro "
90
+ "workspace)"
91
+ )
92
+ else:
93
+ # New workspace - create the file
94
+ workspace_version = WORKSPACE_VERSION
95
+
96
+ # Write current version to file (update empty v0 workspaces)
97
+ if not version_file.exists() or version_file.read_text().strip() == "":
98
+ version_file.write_text(str(WORKSPACE_VERSION))
99
+
57
100
  self.old_workspace = Workspace.CURRENT
58
101
  Workspace.CURRENT = self
59
102
 
@@ -5,7 +5,7 @@ import platform
5
5
  import socket
6
6
  import uuid
7
7
  from experimaestro.scheduler.base import Job
8
- import pkg_resources
8
+ from importlib.metadata import files
9
9
  import http
10
10
  import threading
11
11
  from typing import Optional, Tuple
@@ -143,7 +143,7 @@ def proxy_response(base_url: str, request: Request, path: str):
143
143
  return flask_response
144
144
 
145
145
 
146
- def start_app(server: "Server"):
146
+ def start_app(server: "Server"): # noqa: C901
147
147
  logging.debug("Starting Flask server...")
148
148
  app = Flask("experimaestro")
149
149
 
@@ -256,10 +256,16 @@ def start_app(server: "Server"):
256
256
 
257
257
  datapath = "data/%s" % path
258
258
  logging.debug("Looking for %s", datapath)
259
- if pkg_resources.resource_exists("experimaestro.server", datapath):
260
- mimetype = MIMETYPES[datapath.rsplit(".", 1)[1]]
261
- content = pkg_resources.resource_string("experimaestro.server", datapath)
262
- return Response(content, mimetype=mimetype)
259
+ try:
260
+ package_files = files("experimaestro.server")
261
+ resource_file = package_files / datapath
262
+ if resource_file.is_file():
263
+ mimetype = MIMETYPES[datapath.rsplit(".", 1)[1]]
264
+ content = resource_file.read_bytes()
265
+ return Response(content, mimetype=mimetype)
266
+ except (FileNotFoundError, KeyError):
267
+ pass
268
+
263
269
  return Response("Page not found", status=404)
264
270
 
265
271
  # Start the app
Binary file