experimaestro 1.5.1__py3-none-any.whl → 2.0.0a8__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.

Potentially problematic release.


This version of experimaestro might be problematic. Click here for more details.

Files changed (118) hide show
  1. experimaestro/__init__.py +14 -4
  2. experimaestro/__main__.py +3 -423
  3. experimaestro/annotations.py +14 -4
  4. experimaestro/cli/__init__.py +311 -0
  5. experimaestro/{filter.py → cli/filter.py} +23 -9
  6. experimaestro/cli/jobs.py +268 -0
  7. experimaestro/cli/progress.py +269 -0
  8. experimaestro/click.py +0 -35
  9. experimaestro/commandline.py +3 -7
  10. experimaestro/connectors/__init__.py +29 -14
  11. experimaestro/connectors/local.py +19 -10
  12. experimaestro/connectors/ssh.py +27 -8
  13. experimaestro/core/arguments.py +45 -3
  14. experimaestro/core/callbacks.py +52 -0
  15. experimaestro/core/context.py +8 -9
  16. experimaestro/core/identifier.py +310 -0
  17. experimaestro/core/objects/__init__.py +44 -0
  18. experimaestro/core/{objects.py → objects/config.py} +399 -772
  19. experimaestro/core/objects/config_utils.py +58 -0
  20. experimaestro/core/objects/config_walk.py +151 -0
  21. experimaestro/core/objects.pyi +15 -45
  22. experimaestro/core/serialization.py +63 -9
  23. experimaestro/core/serializers.py +1 -8
  24. experimaestro/core/types.py +104 -66
  25. experimaestro/experiments/cli.py +154 -72
  26. experimaestro/experiments/configuration.py +10 -1
  27. experimaestro/generators.py +6 -1
  28. experimaestro/ipc.py +4 -1
  29. experimaestro/launcherfinder/__init__.py +1 -1
  30. experimaestro/launcherfinder/base.py +2 -18
  31. experimaestro/launcherfinder/parser.py +8 -3
  32. experimaestro/launcherfinder/registry.py +52 -140
  33. experimaestro/launcherfinder/specs.py +49 -10
  34. experimaestro/launchers/direct.py +0 -47
  35. experimaestro/launchers/slurm/base.py +54 -14
  36. experimaestro/mkdocs/__init__.py +1 -1
  37. experimaestro/mkdocs/base.py +6 -8
  38. experimaestro/notifications.py +38 -12
  39. experimaestro/progress.py +406 -0
  40. experimaestro/run.py +24 -3
  41. experimaestro/scheduler/__init__.py +18 -1
  42. experimaestro/scheduler/base.py +108 -808
  43. experimaestro/scheduler/dynamic_outputs.py +184 -0
  44. experimaestro/scheduler/experiment.py +387 -0
  45. experimaestro/scheduler/jobs.py +475 -0
  46. experimaestro/scheduler/signal_handler.py +32 -0
  47. experimaestro/scheduler/state.py +75 -0
  48. experimaestro/scheduler/workspace.py +27 -8
  49. experimaestro/scriptbuilder.py +18 -3
  50. experimaestro/server/__init__.py +36 -5
  51. experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  52. experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  53. experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  54. experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  55. experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  56. experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  57. experimaestro/server/data/index.css +5187 -5068
  58. experimaestro/server/data/index.css.map +1 -1
  59. experimaestro/server/data/index.js +68887 -68064
  60. experimaestro/server/data/index.js.map +1 -1
  61. experimaestro/settings.py +45 -5
  62. experimaestro/sphinx/__init__.py +7 -17
  63. experimaestro/taskglobals.py +7 -2
  64. experimaestro/tests/core/__init__.py +0 -0
  65. experimaestro/tests/core/test_generics.py +206 -0
  66. experimaestro/tests/definitions_types.py +5 -3
  67. experimaestro/tests/launchers/bin/sbatch +34 -7
  68. experimaestro/tests/launchers/bin/srun +5 -0
  69. experimaestro/tests/launchers/common.py +17 -5
  70. experimaestro/tests/launchers/config_slurm/launchers.py +25 -0
  71. experimaestro/tests/restart.py +10 -5
  72. experimaestro/tests/tasks/all.py +23 -10
  73. experimaestro/tests/tasks/foreign.py +2 -4
  74. experimaestro/tests/test_checkers.py +2 -2
  75. experimaestro/tests/test_dependencies.py +11 -17
  76. experimaestro/tests/test_experiment.py +73 -0
  77. experimaestro/tests/test_file_progress.py +425 -0
  78. experimaestro/tests/test_file_progress_integration.py +477 -0
  79. experimaestro/tests/test_findlauncher.py +12 -5
  80. experimaestro/tests/test_forward.py +5 -5
  81. experimaestro/tests/test_generators.py +93 -0
  82. experimaestro/tests/test_identifier.py +182 -158
  83. experimaestro/tests/test_instance.py +19 -27
  84. experimaestro/tests/test_objects.py +13 -20
  85. experimaestro/tests/test_outputs.py +6 -6
  86. experimaestro/tests/test_param.py +68 -30
  87. experimaestro/tests/test_progress.py +4 -4
  88. experimaestro/tests/test_serializers.py +24 -64
  89. experimaestro/tests/test_ssh.py +7 -0
  90. experimaestro/tests/test_tags.py +50 -21
  91. experimaestro/tests/test_tasks.py +42 -51
  92. experimaestro/tests/test_tokens.py +11 -8
  93. experimaestro/tests/test_types.py +24 -21
  94. experimaestro/tests/test_validation.py +67 -110
  95. experimaestro/tests/token_reschedule.py +1 -1
  96. experimaestro/tokens.py +24 -13
  97. experimaestro/tools/diff.py +8 -1
  98. experimaestro/typingutils.py +20 -11
  99. experimaestro/utils/asyncio.py +6 -2
  100. experimaestro/utils/multiprocessing.py +44 -0
  101. experimaestro/utils/resources.py +11 -3
  102. {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/METADATA +28 -36
  103. experimaestro-2.0.0a8.dist-info/RECORD +166 -0
  104. {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/WHEEL +1 -1
  105. {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/entry_points.txt +0 -4
  106. experimaestro/launchers/slurm/cli.py +0 -29
  107. experimaestro/launchers/slurm/configuration.py +0 -597
  108. experimaestro/scheduler/environment.py +0 -94
  109. experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  110. experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  111. experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  112. experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  113. experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  114. experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  115. experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -134
  116. experimaestro/utils/yaml.py +0 -202
  117. experimaestro-1.5.1.dist-info/RECORD +0 -148
  118. {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info/licenses}/LICENSE +0 -0
experimaestro/__init__.py CHANGED
@@ -27,7 +27,15 @@ from .annotations import (
27
27
  # Method
28
28
  config_only,
29
29
  )
30
- from .core.serialization import load, save, state_dict, from_state_dict, from_task_dir
30
+ from .core.serialization import (
31
+ load,
32
+ save,
33
+ state_dict,
34
+ from_state_dict,
35
+ from_task_dir,
36
+ serialize,
37
+ deserialize,
38
+ )
31
39
  from .core.arguments import (
32
40
  # Types
33
41
  Param,
@@ -36,15 +44,17 @@ from .core.arguments import (
36
44
  DataPath,
37
45
  Annotated,
38
46
  Constant,
47
+ field,
39
48
  # Annotations helpers
40
49
  help,
41
50
  default,
42
51
  )
43
- from .generators import pathgenerator
52
+ from .generators import pathgenerator, PathGenerator
44
53
  from .core.objects import (
45
54
  Config,
46
55
  copyconfig,
47
56
  setmeta,
57
+ DependentMarker,
48
58
  Task,
49
59
  LightweightTask,
50
60
  ObjectStore,
@@ -53,9 +63,9 @@ from .core.context import SerializationContext
53
63
  from .core.serializers import SerializationLWTask, PathSerializationLWTask
54
64
  from .core.types import Any, SubmitHook
55
65
  from .launchers import Launcher
56
- from .scheduler.environment import Environment
57
- from .scheduler.workspace import Workspace, RunMode
58
66
  from .scheduler import Scheduler, experiment, FailedExperiment
67
+ from .scheduler.workspace import Workspace, RunMode
68
+ from .scheduler.state import get_experiment
59
69
  from .notifications import progress, tqdm
60
70
  from .checkers import Choices
61
71
  from .xpmutils import DirectoryContext
experimaestro/__main__.py CHANGED
@@ -1,427 +1,7 @@
1
- # flake8: noqa: T201
2
- import sys
3
- from types import ModuleType
4
- from typing import Set, Optional
5
- import pkg_resources
6
- from itertools import chain
7
- from shutil import rmtree
8
- import click
9
- import logging
10
- from functools import cached_property, update_wrapper
11
- from pathlib import Path
12
- import subprocess
13
- from termcolor import colored, cprint
1
+ from experimaestro.cli import cli
14
2
 
15
- import experimaestro
16
- from experimaestro.experiments.cli import experiments_cli
17
- import experimaestro.launcherfinder.registry as launcher_registry
18
-
19
- # --- Command line main options
20
- logging.basicConfig(level=logging.INFO)
21
-
22
-
23
- def pass_cfg(f):
24
- """Pass configuration information"""
25
-
26
- @click.pass_context
27
- def new_func(ctx, *args, **kwargs):
28
- return ctx.invoke(f, ctx.obj, *args, **kwargs)
29
-
30
- return update_wrapper(new_func, f)
31
-
32
-
33
- def check_xp_path(ctx, self, path: Path):
34
- if not (path / ".__experimaestro__").is_file():
35
- cprint(f"{path} is not an experimaestro working directory", "red")
36
- for path in path.parents:
37
- if (path / ".__experimaestro__").is_file():
38
- cprint(f"{path} could be the folder you want", "green")
39
- if click.confirm("Do you want to use this folder?"):
40
- return path
41
- sys.exit(1)
42
-
43
- return path
44
-
45
-
46
- class RunConfig:
47
- def __init__(self):
48
- self.traceback = False
49
-
50
-
51
- def pass_cfg(f):
52
- """Pass configuration information"""
53
-
54
- @click.pass_context
55
- def new_func(ctx, *args, **kwargs):
56
- return ctx.invoke(f, ctx.obj, *args, **kwargs)
57
-
58
- return update_wrapper(new_func, f)
59
-
60
-
61
- @click.group()
62
- @click.option("--quiet", is_flag=True, help="Be quiet")
63
- @click.option("--debug", is_flag=True, help="Be even more verbose (implies traceback)")
64
- @click.option(
65
- "--traceback", is_flag=True, help="Display traceback if an exception occurs"
66
- )
67
- @click.pass_context
68
- def cli(ctx, quiet, debug, traceback):
69
- if quiet:
70
- logging.getLogger().setLevel(logging.WARN)
71
- elif debug:
72
- logging.getLogger().setLevel(logging.DEBUG)
73
-
74
- ctx.obj = RunConfig()
75
- ctx.obj.traceback = traceback
76
-
77
-
78
- # Adds the run-experiment command
79
- cli.add_command(experiments_cli, "run-experiment")
80
-
81
-
82
- @cli.command(help="Get version")
83
- def version():
84
- print(experimaestro.__version__)
85
-
86
-
87
- @click.argument("parameters", type=Path)
88
- @cli.command(context_settings={"allow_extra_args": True})
89
- def run(parameters):
90
- """Run a task"""
91
-
92
- from experimaestro.run import run as do_run
93
-
94
- do_run(parameters)
95
-
96
-
97
- @click.argument("path2", type=Path)
98
- @click.argument("path1", type=Path)
99
- @cli.command(context_settings={"allow_extra_args": True})
100
- def parameters_difference(path1, path2):
101
- """Compute the difference between two configurations"""
102
-
103
- from experimaestro.tools.diff import diff
104
-
105
- diff(path1, path2)
106
-
107
-
108
- @click.option(
109
- "--clean", is_flag=True, help="Remove the socket file and its enclosing directory"
110
- )
111
- @click.argument("unix-path", type=Path)
112
- @cli.command()
113
- def rpyc_server(unix_path, clean):
114
- """Start an rPyC server"""
115
- from experimaestro.rpyc import start_server
116
-
117
- start_server(unix_path, clean=clean)
118
-
119
-
120
- @cli.group()
121
- def deprecated():
122
- """Manage identifier changes"""
123
- pass
124
-
125
-
126
- @click.option("--fix", is_flag=True, help="Generate links to new IDs")
127
- @click.option("--cleanup", is_flag=True, help="Remove symbolic links and move folders")
128
- @click.argument("path", type=Path, callback=check_xp_path)
129
- @deprecated.command(name="list")
130
- def deprecated_list(path: Path, fix: bool, cleanup: bool):
131
- """List deprecated jobs"""
132
- from experimaestro.tools.jobs import fix_deprecated
133
-
134
- if cleanup and not fix:
135
- logging.warning("Ignoring --cleanup since we are not fixing old IDs")
136
- fix_deprecated(path, fix, cleanup)
137
-
138
-
139
- @click.argument("path", type=Path, callback=check_xp_path)
140
- @deprecated.command()
141
- def diff(path: Path):
142
- """Show the reason of the identifier change for a job"""
143
- from experimaestro.tools.jobs import load_job
144
- from experimaestro import Config
145
- from experimaestro.core.objects import ConfigWalkContext
146
-
147
- _, job = load_job(path / "params.json", discard_id=False)
148
- _, new_job = load_job(path / "params.json")
149
-
150
- def check(path: str, value, new_value, done: Set[int]):
151
- if isinstance(value, Config):
152
- if id(value) in done:
153
- return
154
- done.add(id(value))
155
-
156
- old_id = value.__xpm__.identifier.all.hex()
157
- new_id = new_value.__xpm__.identifier.all.hex()
158
-
159
- if new_id != old_id:
160
- print(f"{path} differ: {new_id} vs {old_id}")
161
-
162
- for arg in value.__xpmtype__.arguments.values():
163
- arg_value = getattr(value, arg.name)
164
- arg_newvalue = getattr(new_value, arg.name)
165
- check(f"{path}/{arg.name}", arg_value, arg_newvalue, done)
166
-
167
- elif isinstance(value, list):
168
- for ix, (array_value, array_newvalue) in enumerate(zip(value, new_value)):
169
- check(f"{path}.{ix}", array_value, array_newvalue, done)
170
-
171
- elif isinstance(value, dict):
172
- for key, dict_value in value.items():
173
- check(f"{path}.{key}", dict_value, new_value[key], done)
174
-
175
- check(".", job, new_job, set())
176
-
177
-
178
- @click.argument("path", type=Path, callback=check_xp_path)
179
- @click.option("--experiment", default=None, help="Restrict to this experiment")
180
- @click.option("--tags", is_flag=True, help="Show tags")
181
- @click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
182
- @click.option("--filter", default="", help="Filter expression")
183
- @click.option(
184
- "--force",
185
- is_flag=True,
186
- help="Force operation even if experiment has not been completed yet",
187
- )
188
- @click.option("--kill", is_flag=True, help="Kill filtered tasks (when/before running)")
189
- @click.option("--clean", is_flag=True, help="Remove finished tasks directories")
190
- @cli.command()
191
- def jobs(
192
- path: Path,
193
- experiment: str,
194
- filter: str,
195
- tags: bool,
196
- ready: bool,
197
- kill: bool,
198
- clean: bool,
199
- force: bool,
200
- ):
201
- """Job control: list, kill and clean
202
-
203
- The job filter is a boolean expression where tags (alphanumeric)
204
- and special job information (@state for job state, @name for job full
205
- name) can be compared to a given value (using '~' for regex matching,
206
- '=', 'not in', or 'in')
207
-
208
- For instance,
209
-
210
- model = "bm25" and mode in ["a", b"] and @state = "RUNNING"
211
-
212
- selects jobs where the tag model is "bm25", the tag mode is either
213
- "a" or "b", and the state is running.
214
-
215
- """
216
- for p in (path / "xp").glob("*"):
217
- if experiment and p.name != experiment:
218
- continue
219
-
220
- from .filter import createFilter, JobInformation
221
- from experimaestro.scheduler import JobState
222
-
223
- _filter = createFilter(filter) if filter else lambda x: True
224
-
225
- print(f"* Experiment {p.name}")
226
- if (p / "jobs.bak").is_dir():
227
- cprint(" Experiment has not finished yet", "red")
228
- if not force and (kill or clean):
229
- cprint(" Preventing kill/clean (use --force if you want to)", "yellow")
230
- kill = False
231
- clean = False
232
- print()
233
-
234
- for job in p.glob("jobs/*/*"):
235
- info = None
236
- p = job.resolve()
237
- if p.is_dir():
238
- *_, scriptname = p.parent.name.rsplit(".", 1)
239
-
240
- info = JobInformation(p, scriptname)
241
- if filter:
242
- if not _filter(info):
243
- continue
244
-
245
- if info.state is None:
246
- print(
247
- colored(f"NODIR {job.parent.name}/{job.name}", "red"), end=""
248
- )
249
- elif info.state.running():
250
- if kill:
251
- process = info.getprocess()
252
- print("KILLING", process)
253
- process.kill()
254
- print(
255
- colored(
256
- f"{info.state.name:8}{job.parent.name}/{job.name}", "yellow"
257
- ),
258
- end="",
259
- )
260
- elif info.state == JobState.DONE:
261
- print(
262
- colored(f"DONE {job.parent.name}/{job.name}", "green"),
263
- end="",
264
- )
265
- elif info.state == JobState.ERROR:
266
- print(
267
- colored(f"FAIL {job.parent.name}/{job.name}", "red"), end=""
268
- )
269
- else:
270
- print(
271
- colored(
272
- f"{info.state.name:8}{job.parent.name}/{job.name}", "red"
273
- ),
274
- end="",
275
- )
276
-
277
- else:
278
- if not ready:
279
- continue
280
- print(colored(f"READY {job.parent.name}/{job.name}", "yellow"), end="")
281
-
282
- if tags:
283
- print(f""" {" ".join(f"{k}={v}" for k, v in info.tags.items())}""")
284
- else:
285
- print()
286
-
287
- if clean and info.state and info.state.finished():
288
- cprint("Cleaning...", "red")
289
- rmtree(p)
290
- print()
291
-
292
-
293
- @click.option("--show-all", is_flag=True, help="Show even not orphans")
294
- @click.option(
295
- "--ignore-old", is_flag=True, help="Ignore old jobs for unfinished experiments"
296
- )
297
- @click.option("--clean", is_flag=True, help="Prune the orphan folders")
298
- @click.option("--size", is_flag=True, help="Show size of each folder")
299
- @click.argument("path", type=Path, callback=check_xp_path)
300
- @cli.command()
301
- def orphans(path: Path, clean: bool, size: bool, show_all: bool, ignore_old: bool):
302
- """Check for tasks that are not part of an experimental plan"""
303
-
304
- jobspath = path / "jobs"
305
-
306
- def getjobs(path: Path):
307
- return ((str(p.relative_to(path)), p) for p in path.glob("*/*") if p.is_dir())
308
-
309
- def show(key: str, prefix=""):
310
- if size:
311
- print(
312
- prefix,
313
- subprocess.check_output(["du", "-hs", key], cwd=jobspath)
314
- .decode("utf-8")
315
- .strip(),
316
- sep=None,
317
- )
318
- else:
319
- print(prefix, key, sep=None)
320
-
321
- for p in (path / "xp").glob("*/jobs.bak"):
322
- logging.warning("Experiment %s has not completed successfully", p.parent.name)
323
-
324
- # Retrieve the jobs within expedriments (jobs and jobs.bak folder within experiments)
325
- xpjobs = set()
326
- if ignore_old:
327
- paths = (path / "xp").glob("*/jobs")
328
- else:
329
- paths = chain((path / "xp").glob("*/jobs"), (path / "xp").glob("*/jobs.bak"))
330
-
331
- for p in paths:
332
- if p.is_dir():
333
- for relpath, path in getjobs(p):
334
- xpjobs.add(relpath)
335
-
336
- # Now, look at stored jobs
337
- found = 0
338
- for key, jobpath in getjobs(jobspath):
339
- if key not in xpjobs:
340
- show(key)
341
- if clean:
342
- logging.info("Removing data in %s", jobpath)
343
- rmtree(jobpath)
344
- else:
345
- if show_all:
346
- show(key, prefix="[not orphan] ")
347
- found += 1
348
-
349
- print(f"{found} jobs are not orphans")
350
-
351
-
352
- def arg_split(ctx, param, value):
353
- # split columns by ',' and remove whitespace
354
- return set(c.strip() for c in value.split(","))
355
-
356
-
357
- @click.option("--skip", default=set(), callback=arg_split)
358
- @click.argument("package", type=str)
359
- @click.argument("objects", type=Path)
360
- @cli.command()
361
- def check_documentation(objects, package, skip):
362
- """Check that all the configuration and tasks are documented within a
363
- package, relying on the sphinx objects.inv file"""
364
- from experimaestro.tools.documentation import documented_from_objects, undocumented
365
-
366
- documented = documented_from_objects(objects)
367
- errors, configs = undocumented([package], documented, skip)
368
- for config in configs:
369
- cprint(f"{config.__module__}.{config.__qualname__}", "red")
370
-
371
- if errors > 0 or configs:
372
- sys.exit(1)
373
-
374
-
375
- @click.option("--config", type=Path, help="Show size of each folder")
376
- @click.argument("spec", type=str)
377
- @cli.command()
378
- def find_launchers(config: Optional[Path], spec: str):
379
- """Find launchers matching a specification"""
380
- if config is not None:
381
- launcher_registry.LauncherRegistry.set_config_dir(config)
382
-
383
- print(launcher_registry.find_launcher(spec))
384
-
385
-
386
- class Launchers(click.MultiCommand):
387
- """Connectors commands"""
388
-
389
- @cached_property
390
- def commands(self):
391
- map = {}
392
- for ep in pkg_resources.iter_entry_points(f"experimaestro.{self.name}"):
393
- if get_cli := getattr(ep.load(), "get_cli", None):
394
- map[ep.name] = get_cli()
395
- return map
396
-
397
- def list_commands(self, ctx):
398
- return self.commands.keys()
399
-
400
- def get_command(self, ctx, name):
401
- return self.commands[name]
402
-
403
-
404
- cli.add_command(Launchers("launchers", help="Launcher specific commands"))
405
- cli.add_command(Launchers("connectors", help="Connector specific commands"))
406
- cli.add_command(Launchers("tokens", help="Token specific commands"))
407
-
408
-
409
- @cli.group()
410
- @click.argument("workdir", type=Path, callback=check_xp_path)
411
- @click.pass_context
412
- def experiments(ctx, workdir):
413
- """Manage experiments"""
414
- ctx.obj = workdir
415
-
416
-
417
- @experiments.command()
418
- @pass_cfg
419
- def list(workdir: Path):
420
- for p in (workdir / "xp").iterdir():
421
- if (p / "jobs.bak").exists():
422
- cprint(f"[unfinished] {p.name}", "yellow")
423
- else:
424
- cprint(p.name, "cyan")
3
+ # flake8: noqa: F401
4
+ import experimaestro.cli.jobs
425
5
 
426
6
 
427
7
  def main():
@@ -8,7 +8,7 @@ import experimaestro.core.objects as objects
8
8
  import experimaestro.core.types as types
9
9
  from experimaestro.generators import PathGenerator
10
10
 
11
- from .core.arguments import Argument as CoreArgument
11
+ from .core.arguments import Argument as CoreArgument, field
12
12
  from .core.objects import Config
13
13
  from .core.types import Any, Identifier, TypeProxy, Type, ObjectType
14
14
  from .utils import logger
@@ -71,7 +71,7 @@ class config:
71
71
  assert inspect.isclass(tp), f"{tp} is not a class"
72
72
 
73
73
  # Adds to xpminfo for on demand creation of information
74
- return ObjectType(tp, identifier=self.identifier).basetype
74
+ return ObjectType(tp, identifier=self.identifier).valuetype
75
75
 
76
76
 
77
77
  class Array(TypeProxy):
@@ -134,12 +134,22 @@ class param:
134
134
  self.type = Type.fromType(type) if type else None
135
135
  self.help = help
136
136
  self.ignored = ignored
137
- self.default = default
138
137
  self.required = required
139
- self.generator = None
140
138
  self.checker = checker
141
139
  self.constant = constant
142
140
 
141
+ self.generator = None
142
+ self.default = None
143
+
144
+ # Set default or generator
145
+ if isinstance(default, field):
146
+ if default.default is not None:
147
+ self.default = default
148
+ elif default.default_factory is not None:
149
+ self.generator = default.default_factory
150
+ else:
151
+ self.default = default
152
+
143
153
  def __call__(self, tp):
144
154
  # Don't annotate in task mode
145
155
  tp.__getxpmtype__().addAnnotation(self)