nextmv 0.31.0__py3-none-any.whl → 0.32.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.
@@ -6,19 +6,8 @@ including application management, running applications, and managing inputs.
6
6
 
7
7
  Classes
8
8
  -------
9
- DownloadURL
10
- Result of getting a download URL.
11
- PollingOptions
12
- Options for polling when waiting for run results.
13
- UploadURL
14
- Result of getting an upload URL.
15
9
  Application
16
- Class for interacting with applications in Nextmv Cloud.
17
-
18
- Functions
19
- ---------
20
- poll
21
- Function to poll for results with configurable options.
10
+ Class for interacting with local Nextmv Applications.
22
11
  """
23
12
 
24
13
  import json
@@ -33,15 +22,22 @@ from typing import Any, Optional, Union
33
22
  from nextmv import cloud
34
23
  from nextmv._serialization import deflated_serialize_json
35
24
  from nextmv.base_model import BaseModel
36
- from nextmv.input import DEFAULT_INPUT_JSON_FILE, INPUTS_KEY, Input, InputFormat
37
- from nextmv.local.executor import LOGS_FILE
25
+ from nextmv.input import INPUTS_KEY, Input, InputFormat
26
+ from nextmv.local.local import (
27
+ DEFAULT_INPUT_JSON_FILE,
28
+ DEFAULT_OUTPUT_JSON_FILE,
29
+ LOGS_FILE,
30
+ LOGS_KEY,
31
+ NEXTMV_DIR,
32
+ RUNS_KEY,
33
+ )
38
34
  from nextmv.local.runner import run
39
35
  from nextmv.logger import log
40
36
  from nextmv.manifest import Manifest
41
37
  from nextmv.options import Options
42
- from nextmv.output import DEFAULT_OUTPUT_JSON_FILE, LOGS_KEY, OUTPUTS_KEY, SOLUTIONS_KEY, OutputFormat
38
+ from nextmv.output import OUTPUTS_KEY, SOLUTIONS_KEY, OutputFormat
43
39
  from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
44
- from nextmv.run import ErrorLog, Format, RunConfiguration, RunInformation, RunResult, TrackedRun, TrackedRunStatus
40
+ from nextmv.run import ErrorLog, Format, Run, RunConfiguration, RunInformation, RunResult, TrackedRun, TrackedRunStatus
45
41
  from nextmv.safe import safe_id
46
42
  from nextmv.status import StatusV2
47
43
 
@@ -152,224 +148,35 @@ class Application:
152
148
  description=description,
153
149
  )
154
150
 
155
- def run_metadata(self, run_id: str) -> RunInformation:
151
+ def list_runs(self) -> list[Run]:
156
152
  """
157
- Get the metadata of a local run.
158
-
159
- This method is the local equivalent to
160
- `cloud.Application.run_metadata`, which retrieves the metadata of a
161
- remote run in Nextmv Cloud. This method is used to get the metadata of
162
- a run that was executed locally using the `new_run` or
163
- `new_run_with_result` method.
164
-
165
- Retrieves information about a run without including the run output.
166
- This is useful when you only need the run's status and metadata.
167
-
168
- Parameters
169
- ----------
170
- run_id : str
171
- ID of the run to retrieve metadata for.
153
+ List all runs for the application.
172
154
 
173
155
  Returns
174
156
  -------
175
- RunInformation
176
- Metadata of the run (run information without output).
177
-
178
- Raises
179
- ------
180
- ValueError
181
- If the `.nextmv/runs` directory does not exist at the application
182
- source, or if the specified run ID does not exist.
183
-
184
- Examples
185
- --------
186
- >>> metadata = app.run_metadata("run-789")
187
- >>> print(metadata.metadata.status_v2)
188
- StatusV2.succeeded
157
+ list[Run]
158
+ A list of all runs associated with the application.
189
159
  """
190
160
 
191
- runs_dir = os.path.join(self.src, ".nextmv", "runs")
161
+ runs_dir = os.path.join(self.src, NEXTMV_DIR, RUNS_KEY)
192
162
  if not os.path.exists(runs_dir):
193
163
  raise ValueError(f"`.nextmv/runs` dir does not exist at app source: {self.src}")
194
164
 
195
- run_dir = os.path.join(runs_dir, run_id)
196
- if not os.path.exists(run_dir):
197
- raise ValueError(f"`{run_id}` run dir does not exist at: {runs_dir}")
198
-
199
- info_file = os.path.join(run_dir, f"{run_id}.json")
200
- if not os.path.exists(info_file):
201
- raise ValueError(f"`{info_file}` file does not exist at: {run_dir}")
202
-
203
- with open(info_file) as f:
204
- info_dict = json.load(f)
205
-
206
- info = RunInformation.from_dict(info_dict)
207
-
208
- return info
209
-
210
- def run_result(self, run_id: str, output_dir_path: Optional[str] = ".") -> RunResult:
211
- """
212
- Get the local result of a run.
213
-
214
- This method is the local equivalent to `cloud.Application.run_result`,
215
- which retrieves the result of a remote run in Nextmv Cloud. This method
216
- is used to get the result of a run that was executed locally using the
217
- `new_run` or `new_run_with_result` method.
218
-
219
- Retrieves the complete result of a run, including the run output.
165
+ dirs = os.listdir(runs_dir)
166
+ if not dirs:
167
+ return []
220
168
 
221
- Parameters
222
- ----------
223
- run_id : str
224
- ID of the run to get results for.
225
- output_dir_path : Optional[str], default="."
226
- Path to a directory where non-JSON output files will be saved. This
227
- is required if the output is non-JSON. If the directory does not
228
- exist, it will be created. Uses the current directory by default.
169
+ run_ids = [d for d in dirs if os.path.isdir(os.path.join(runs_dir, d))]
170
+ if not run_ids:
171
+ return []
229
172
 
230
- Returns
231
- -------
232
- RunResult
233
- Result of the run, including output.
234
-
235
- Raises
236
- ------
237
- ValueError
238
- If the `.nextmv/runs` directory does not exist at the application
239
- source, or if the specified run ID does not exist.
173
+ runs = []
174
+ for run_id in run_ids:
175
+ info = self.run_metadata(run_id=run_id)
176
+ run = info.to_run()
177
+ runs.append(run)
240
178
 
241
- Examples
242
- --------
243
- >>> result = app.run_result("run-123")
244
- >>> print(result.metadata.status_v2)
245
- 'succeeded'
246
- """
247
-
248
- run_information = self.run_metadata(run_id=run_id)
249
-
250
- return self.__run_result(
251
- run_id=run_id,
252
- run_information=run_information,
253
- output_dir_path=output_dir_path,
254
- )
255
-
256
- def run_result_with_polling(
257
- self,
258
- run_id: str,
259
- polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
260
- output_dir_path: Optional[str] = ".",
261
- ) -> RunResult:
262
- """
263
- Get the result of a local run with polling.
264
-
265
- This method is the local equivalent to
266
- `cloud.Application.run_result_with_polling`, which retrieves the result
267
- of a remote run in Nextmv Cloud. This method is used to get the result
268
- of a run that was executed locally using the `new_run` or
269
- `new_run_with_result` method.
270
-
271
- Retrieves the result of a run including the run output. This method
272
- polls for the result until the run finishes executing or the polling
273
- strategy is exhausted.
274
-
275
- Parameters
276
- ----------
277
- run_id : str
278
- ID of the run to retrieve the result for.
279
- polling_options : PollingOptions, default=_DEFAULT_POLLING_OPTIONS
280
- Options to use when polling for the run result.
281
- output_dir_path : Optional[str], default="."
282
- Path to a directory where non-JSON output files will be saved. This
283
- is required if the output is non-JSON. If the directory does not
284
- exist, it will be created. Uses the current directory by default.
285
-
286
- Returns
287
- -------
288
- RunResult
289
- Complete result of the run including output data.
290
-
291
- Raises
292
- ------
293
- requests.HTTPError
294
- If the response status code is not 2xx.
295
- TimeoutError
296
- If the run does not complete after the polling strategy is
297
- exhausted based on time duration.
298
- RuntimeError
299
- If the run does not complete after the polling strategy is
300
- exhausted based on number of tries.
301
-
302
- Examples
303
- --------
304
- >>> from nextmv.cloud import PollingOptions
305
- >>> # Create custom polling options
306
- >>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
307
- >>> # Get run result with polling
308
- >>> result = app.run_result_with_polling("run-123", polling_opts)
309
- >>> print(result.output)
310
- {'solution': {...}}
311
- """
312
-
313
- def polling_func() -> tuple[Any, bool]:
314
- run_information = self.run_metadata(run_id=run_id)
315
- if run_information.metadata.status_v2 in {
316
- StatusV2.succeeded,
317
- StatusV2.failed,
318
- StatusV2.canceled,
319
- }:
320
- return run_information, True
321
-
322
- return None, False
323
-
324
- run_information = poll(polling_options=polling_options, polling_func=polling_func)
325
-
326
- return self.__run_result(
327
- run_id=run_id,
328
- run_information=run_information,
329
- output_dir_path=output_dir_path,
330
- )
331
-
332
- def run_visuals(self, run_id: str) -> None:
333
- """
334
- Open the local run visuals in a web browser.
335
-
336
- This method opens the visual representation of a locally executed run
337
- in the default web browser. It assumes that the run was executed locally
338
- using the `new_run` or `new_run_with_result` method and that
339
- the necessary visualization files are present.
340
-
341
- If the run was correctly configured to produce visual assets, then the
342
- run will contain a `visuals` directory with one or more HTML files.
343
- Each file is opened in a new tab in the default web browser.
344
-
345
- Parameters
346
- ----------
347
- run_id : str
348
- ID of the local run to visualize.
349
-
350
- Raises
351
- ------
352
- ValueError
353
- If the `.nextmv/runs` directory does not exist at the application
354
- source, or if the specified run ID does not exist.
355
- """
356
-
357
- runs_dir = os.path.join(self.src, ".nextmv", "runs")
358
- if not os.path.exists(runs_dir):
359
- raise ValueError(f"`.nextmv/runs` dir does not exist at app source: {self.src}")
360
-
361
- run_dir = os.path.join(runs_dir, run_id)
362
- if not os.path.exists(run_dir):
363
- raise ValueError(f"`{run_id}` run dir does not exist at: {runs_dir}")
364
-
365
- visuals_dir = os.path.join(run_dir, "visuals")
366
- if not os.path.exists(visuals_dir):
367
- raise ValueError(f"`visuals` dir does not exist at: {run_dir}")
368
-
369
- for file in os.listdir(visuals_dir):
370
- if file.endswith(".html"):
371
- file_path = os.path.join(visuals_dir, file)
372
- webbrowser.open_new_tab(f"file://{os.path.realpath(file_path)}")
179
+ return runs
373
180
 
374
181
  def new_run(
375
182
  self,
@@ -642,6 +449,225 @@ class Application:
642
449
  output_dir_path=output_dir_path,
643
450
  )
644
451
 
452
+ def run_metadata(self, run_id: str) -> RunInformation:
453
+ """
454
+ Get the metadata of a local run.
455
+
456
+ This method is the local equivalent to
457
+ `cloud.Application.run_metadata`, which retrieves the metadata of a
458
+ remote run in Nextmv Cloud. This method is used to get the metadata of
459
+ a run that was executed locally using the `new_run` or
460
+ `new_run_with_result` method.
461
+
462
+ Retrieves information about a run without including the run output.
463
+ This is useful when you only need the run's status and metadata.
464
+
465
+ Parameters
466
+ ----------
467
+ run_id : str
468
+ ID of the run to retrieve metadata for.
469
+
470
+ Returns
471
+ -------
472
+ RunInformation
473
+ Metadata of the run (run information without output).
474
+
475
+ Raises
476
+ ------
477
+ ValueError
478
+ If the `.nextmv/runs` directory does not exist at the application
479
+ source, or if the specified run ID does not exist.
480
+
481
+ Examples
482
+ --------
483
+ >>> metadata = app.run_metadata("run-789")
484
+ >>> print(metadata.metadata.status_v2)
485
+ StatusV2.succeeded
486
+ """
487
+
488
+ runs_dir = os.path.join(self.src, NEXTMV_DIR, RUNS_KEY)
489
+ if not os.path.exists(runs_dir):
490
+ raise ValueError(f"`.nextmv/runs` dir does not exist at app source: {self.src}")
491
+
492
+ run_dir = os.path.join(runs_dir, run_id)
493
+ if not os.path.exists(run_dir):
494
+ raise ValueError(f"`{run_id}` run dir does not exist at: {runs_dir}")
495
+
496
+ info_file = os.path.join(run_dir, f"{run_id}.json")
497
+ if not os.path.exists(info_file):
498
+ raise ValueError(f"`{info_file}` file does not exist at: {run_dir}")
499
+
500
+ with open(info_file) as f:
501
+ info_dict = json.load(f)
502
+
503
+ info = RunInformation.from_dict(info_dict)
504
+
505
+ return info
506
+
507
+ def run_result(self, run_id: str, output_dir_path: Optional[str] = ".") -> RunResult:
508
+ """
509
+ Get the local result of a run.
510
+
511
+ This method is the local equivalent to `cloud.Application.run_result`,
512
+ which retrieves the result of a remote run in Nextmv Cloud. This method
513
+ is used to get the result of a run that was executed locally using the
514
+ `new_run` or `new_run_with_result` method.
515
+
516
+ Retrieves the complete result of a run, including the run output.
517
+
518
+ Parameters
519
+ ----------
520
+ run_id : str
521
+ ID of the run to get results for.
522
+ output_dir_path : Optional[str], default="."
523
+ Path to a directory where non-JSON output files will be saved. This
524
+ is required if the output is non-JSON. If the directory does not
525
+ exist, it will be created. Uses the current directory by default.
526
+
527
+ Returns
528
+ -------
529
+ RunResult
530
+ Result of the run, including output.
531
+
532
+ Raises
533
+ ------
534
+ ValueError
535
+ If the `.nextmv/runs` directory does not exist at the application
536
+ source, or if the specified run ID does not exist.
537
+
538
+ Examples
539
+ --------
540
+ >>> result = app.run_result("run-123")
541
+ >>> print(result.metadata.status_v2)
542
+ 'succeeded'
543
+ """
544
+
545
+ run_information = self.run_metadata(run_id=run_id)
546
+
547
+ return self.__run_result(
548
+ run_id=run_id,
549
+ run_information=run_information,
550
+ output_dir_path=output_dir_path,
551
+ )
552
+
553
+ def run_result_with_polling(
554
+ self,
555
+ run_id: str,
556
+ polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
557
+ output_dir_path: Optional[str] = ".",
558
+ ) -> RunResult:
559
+ """
560
+ Get the result of a local run with polling.
561
+
562
+ This method is the local equivalent to
563
+ `cloud.Application.run_result_with_polling`, which retrieves the result
564
+ of a remote run in Nextmv Cloud. This method is used to get the result
565
+ of a run that was executed locally using the `new_run` or
566
+ `new_run_with_result` method.
567
+
568
+ Retrieves the result of a run including the run output. This method
569
+ polls for the result until the run finishes executing or the polling
570
+ strategy is exhausted.
571
+
572
+ Parameters
573
+ ----------
574
+ run_id : str
575
+ ID of the run to retrieve the result for.
576
+ polling_options : PollingOptions, default=_DEFAULT_POLLING_OPTIONS
577
+ Options to use when polling for the run result.
578
+ output_dir_path : Optional[str], default="."
579
+ Path to a directory where non-JSON output files will be saved. This
580
+ is required if the output is non-JSON. If the directory does not
581
+ exist, it will be created. Uses the current directory by default.
582
+
583
+ Returns
584
+ -------
585
+ RunResult
586
+ Complete result of the run including output data.
587
+
588
+ Raises
589
+ ------
590
+ requests.HTTPError
591
+ If the response status code is not 2xx.
592
+ TimeoutError
593
+ If the run does not complete after the polling strategy is
594
+ exhausted based on time duration.
595
+ RuntimeError
596
+ If the run does not complete after the polling strategy is
597
+ exhausted based on number of tries.
598
+
599
+ Examples
600
+ --------
601
+ >>> from nextmv.cloud import PollingOptions
602
+ >>> # Create custom polling options
603
+ >>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
604
+ >>> # Get run result with polling
605
+ >>> result = app.run_result_with_polling("run-123", polling_opts)
606
+ >>> print(result.output)
607
+ {'solution': {...}}
608
+ """
609
+
610
+ def polling_func() -> tuple[Any, bool]:
611
+ run_information = self.run_metadata(run_id=run_id)
612
+ if run_information.metadata.status_v2 in {
613
+ StatusV2.succeeded,
614
+ StatusV2.failed,
615
+ StatusV2.canceled,
616
+ }:
617
+ return run_information, True
618
+
619
+ return None, False
620
+
621
+ run_information = poll(polling_options=polling_options, polling_func=polling_func)
622
+
623
+ return self.__run_result(
624
+ run_id=run_id,
625
+ run_information=run_information,
626
+ output_dir_path=output_dir_path,
627
+ )
628
+
629
+ def run_visuals(self, run_id: str) -> None:
630
+ """
631
+ Open the local run visuals in a web browser.
632
+
633
+ This method opens the visual representation of a locally executed run
634
+ in the default web browser. It assumes that the run was executed locally
635
+ using the `new_run` or `new_run_with_result` method and that
636
+ the necessary visualization files are present.
637
+
638
+ If the run was correctly configured to produce visual assets, then the
639
+ run will contain a `visuals` directory with one or more HTML files.
640
+ Each file is opened in a new tab in the default web browser.
641
+
642
+ Parameters
643
+ ----------
644
+ run_id : str
645
+ ID of the local run to visualize.
646
+
647
+ Raises
648
+ ------
649
+ ValueError
650
+ If the `.nextmv/runs` directory does not exist at the application
651
+ source, or if the specified run ID does not exist.
652
+ """
653
+
654
+ runs_dir = os.path.join(self.src, NEXTMV_DIR, RUNS_KEY)
655
+ if not os.path.exists(runs_dir):
656
+ raise ValueError(f"`.nextmv/runs` dir does not exist at app source: {self.src}")
657
+
658
+ run_dir = os.path.join(runs_dir, run_id)
659
+ if not os.path.exists(run_dir):
660
+ raise ValueError(f"`{run_id}` run dir does not exist at: {runs_dir}")
661
+
662
+ visuals_dir = os.path.join(run_dir, "visuals")
663
+ if not os.path.exists(visuals_dir):
664
+ raise ValueError(f"`visuals` dir does not exist at: {run_dir}")
665
+
666
+ for file in os.listdir(visuals_dir):
667
+ if file.endswith(".html"):
668
+ file_path = os.path.join(visuals_dir, file)
669
+ webbrowser.open_new_tab(f"file://{os.path.realpath(file_path)}")
670
+
645
671
  def sync( # noqa: C901
646
672
  self,
647
673
  target: cloud.Application,
@@ -714,7 +740,7 @@ class Application:
714
740
  # ".". During the sync process, we don't need to keep these outputs, so
715
741
  # we can use a temp dir that will be deleted after the sync is done.
716
742
  with tempfile.TemporaryDirectory(prefix="nextmv-sync-run-") as temp_results_dir:
717
- runs_dir = os.path.join(self.src, ".nextmv", "runs")
743
+ runs_dir = os.path.join(self.src, NEXTMV_DIR, RUNS_KEY)
718
744
  if run_ids is None:
719
745
  # If runs are not specified, by default we sync all local runs that
720
746
  # can be found.
@@ -800,7 +826,7 @@ class Application:
800
826
  "The output format is not JSON: an `output_dir_path` must be provided.",
801
827
  )
802
828
 
803
- runs_dir = os.path.join(self.src, ".nextmv", "runs")
829
+ runs_dir = os.path.join(self.src, NEXTMV_DIR, RUNS_KEY)
804
830
  solutions_dir = os.path.join(runs_dir, run_id, OUTPUTS_KEY, SOLUTIONS_KEY)
805
831
 
806
832
  if output_type == OutputFormat.JSON:
nextmv/local/executor.py CHANGED
@@ -38,22 +38,17 @@ from typing import Any, Optional, Union
38
38
 
39
39
  from nextmv.input import INPUTS_KEY, InputFormat, load
40
40
  from nextmv.local.geojson_handler import handle_geojson_visual
41
- from nextmv.local.plotly_handler import handle_plotly_visual
42
- from nextmv.local.runner import calculate_files_size
43
- from nextmv.manifest import Manifest
44
- from nextmv.output import (
45
- ASSETS_KEY,
41
+ from nextmv.local.local import (
46
42
  DEFAULT_OUTPUT_JSON_FILE,
47
43
  LOGS_FILE,
48
44
  LOGS_KEY,
45
+ NEXTMV_DIR,
49
46
  OUTPUT_KEY,
50
- OUTPUTS_KEY,
51
- SOLUTIONS_KEY,
52
- STATISTICS_KEY,
53
- Asset,
54
- OutputFormat,
55
- VisualSchema,
47
+ calculate_files_size,
56
48
  )
49
+ from nextmv.local.plotly_handler import handle_plotly_visual
50
+ from nextmv.manifest import Manifest
51
+ from nextmv.output import ASSETS_KEY, OUTPUTS_KEY, SOLUTIONS_KEY, STATISTICS_KEY, Asset, OutputFormat, VisualSchema
57
52
  from nextmv.status import StatusV2
58
53
 
59
54
 
@@ -123,7 +118,7 @@ def execute_run(
123
118
  # place to work from, and be cleaned up afterwards.
124
119
  with tempfile.TemporaryDirectory() as temp_dir:
125
120
  temp_src = os.path.join(temp_dir, "src")
126
- shutil.copytree(src, temp_src, ignore=shutil.ignore_patterns(".nextmv"))
121
+ shutil.copytree(src, temp_src, ignore=shutil.ignore_patterns(NEXTMV_DIR))
127
122
 
128
123
  manifest = Manifest.from_dict(manifest_dict)
129
124
 
nextmv/local/local.py ADDED
@@ -0,0 +1,97 @@
1
+ """
2
+ Local module to hold convenience functions used in the `local` package.
3
+
4
+ Functions
5
+ ----------
6
+ calculate_files_size
7
+ Function to calculate the total size of files in a directory.
8
+
9
+ Attributes
10
+ ----------
11
+ OUTPUT_KEY : str
12
+ Output key constant used for identifying output in the run output.
13
+ LOGS_KEY : str
14
+ Logs key constant used for identifying logs in the run output.
15
+ LOGS_FILE : str
16
+ Constant used for identifying the file used for logging.
17
+ DEFAULT_OUTPUT_JSON_FILE : str
18
+ Constant for the default output JSON file name.
19
+ RUNS_KEY : str
20
+ Runs key constant used for identifying the runs directory in the nextmv
21
+ location.
22
+ NEXTMV_DIR : str
23
+ Constant for the Nextmv directory name.
24
+ DEFAULT_INPUT_JSON_FILE : str
25
+ Constant for the default input JSON file name.
26
+ """
27
+
28
+ import json
29
+ import os
30
+
31
+ OUTPUT_KEY = "output"
32
+ """
33
+ Output key constant used for identifying output in the run output.
34
+ """
35
+ LOGS_KEY = "logs"
36
+ """
37
+ Logs key constant used for identifying logs in the run output.
38
+ """
39
+ LOGS_FILE = "stderr.log"
40
+ """
41
+ Constant used for identifying the file used for logging.
42
+ """
43
+ DEFAULT_OUTPUT_JSON_FILE = "solution.json"
44
+ """
45
+ Constant for the default output JSON file name.
46
+ """
47
+ RUNS_KEY = "runs"
48
+ """
49
+ Runs key constant used for identifying the runs directory in the nextmv
50
+ location.
51
+ """
52
+ NEXTMV_DIR = ".nextmv"
53
+ """
54
+ Constant for the Nextmv directory name.
55
+ """
56
+ DEFAULT_INPUT_JSON_FILE = "input.json"
57
+ """
58
+ Constant for the default input JSON file name.
59
+ """
60
+
61
+
62
+ def calculate_files_size(run_dir: str, run_id: str, dir_path: str, metadata_key: str) -> None:
63
+ """
64
+ Calculates the total size of the files in a directory, in bytes.
65
+
66
+ The calculated size is stored in the run information metadata under the
67
+ specified key.
68
+
69
+ Parameters
70
+ ----------
71
+ run_dir : str
72
+ The path to the run directory.
73
+ run_id : str
74
+ The ID of the run.
75
+ dir_path : str
76
+ The path to the directory whose size is to be calculated.
77
+ metadata_key : str
78
+ The key under which to store the calculated size in the run information
79
+ metadata.
80
+ """
81
+
82
+ total_size = 0
83
+ for dirpath, _, filenames in os.walk(dir_path):
84
+ for f in filenames:
85
+ fp = os.path.join(dirpath, f)
86
+ # Skip if it is a symbolic link
87
+ if os.path.islink(fp):
88
+ continue
89
+ total_size += os.path.getsize(fp)
90
+
91
+ info_file = os.path.join(run_dir, f"{run_id}.json")
92
+ with open(info_file, "r+") as f:
93
+ info = json.load(f)
94
+ info["metadata"][metadata_key] = total_size
95
+ f.seek(0)
96
+ json.dump(info, f, indent=2)
97
+ f.truncate()