runem 0.0.32__py3-none-any.whl → 0.1.1__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.
runem/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.32
1
+ 0.1.1
runem/command_line.py CHANGED
@@ -12,6 +12,45 @@ from runem.types import JobNames, OptionConfig, OptionsWritable
12
12
  from runem.utils import printable_set
13
13
 
14
14
 
15
+ class HelpFormatterFixedWidth(argparse.HelpFormatter):
16
+ """This works around test issues via constant width helo output.
17
+
18
+ This ensures that we get more constant for help-text by fixing the width to
19
+ something reasonable.
20
+ """
21
+
22
+ def __init__(self, prog: typing.Any) -> None:
23
+ # Override the default width with a fixed width, for tests.
24
+ super().__init__(
25
+ prog,
26
+ # Pretty wide so we do not get wrapping on directories
27
+ # or process-count output.
28
+ width=1000,
29
+ )
30
+
31
+
32
+ def _get_argparse_help_formatter() -> typing.Any:
33
+ """Returns a help-formatter for argparse.
34
+
35
+ This is for tests only to fake terminals of a constant with when rendering help
36
+ output.
37
+ """
38
+ # Check environment variable to see if we're in tests and need a fixed width
39
+ # help output.
40
+ use_fixed_width = os.getenv("RUNEM_FIXED_HELP_WIDTH", None)
41
+
42
+ if use_fixed_width:
43
+ # Use custom formatter with the width specified in the environment variable
44
+ return (
45
+ lambda prog: HelpFormatterFixedWidth( # pylint: disable=unnecessary-lambda
46
+ prog
47
+ )
48
+ )
49
+
50
+ # Use default formatter
51
+ return argparse.HelpFormatter
52
+
53
+
15
54
  def parse_args(
16
55
  config_metadata: ConfigMetadata, argv: typing.List[str]
17
56
  ) -> ConfigMetadata:
@@ -23,7 +62,9 @@ def parse_args(
23
62
  Returns the parsed args, the jobs_names_to_run, job_phases_to_run, job_tags_to_run
24
63
  """
25
64
  parser = argparse.ArgumentParser(
26
- add_help=False, description="Runs the Lursight Lang test-suite"
65
+ add_help=False,
66
+ description="Runs the Lursight Lang test-suite",
67
+ formatter_class=_get_argparse_help_formatter(),
27
68
  )
28
69
  parser.add_argument(
29
70
  "-H", "--help", action="help", help="show this help message and exit"
runem/hook_manager.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import typing
2
2
  from collections import defaultdict
3
3
 
4
+ from typing_extensions import Unpack
5
+
4
6
  from runem.config_metadata import ConfigMetadata
5
7
  from runem.job import Job
6
8
  from runem.job_execute import job_execute
@@ -10,6 +12,7 @@ from runem.types import (
10
12
  HookConfig,
11
13
  HookName,
12
14
  Hooks,
15
+ HookSpecificKwargs,
13
16
  HooksStore,
14
17
  JobConfig,
15
18
  )
@@ -69,7 +72,7 @@ class HookManager:
69
72
  self,
70
73
  hook_name: HookName,
71
74
  config_metadata: ConfigMetadata,
72
- **kwargs: typing.Any,
75
+ **kwargs: Unpack[HookSpecificKwargs],
73
76
  ) -> None:
74
77
  """Invokes all functions registered to a specific hook."""
75
78
  hooks: typing.List[HookConfig] = self.hooks_store.get(hook_name, [])
@@ -92,6 +95,7 @@ class HookManager:
92
95
  job_execute(
93
96
  job_config,
94
97
  running_jobs={},
98
+ completed_jobs={},
95
99
  config_metadata=config_metadata,
96
100
  file_lists=file_lists,
97
101
  **kwargs,
runem/job_execute.py CHANGED
@@ -5,13 +5,17 @@ import uuid
5
5
  from datetime import timedelta
6
6
  from timeit import default_timer as timer
7
7
 
8
+ from typing_extensions import Unpack
9
+
8
10
  from runem.config_metadata import ConfigMetadata
9
11
  from runem.informative_dict import ReadOnlyInformativeDict
10
12
  from runem.job import Job
11
13
  from runem.job_wrapper import get_job_wrapper
12
14
  from runem.log import error, log
13
15
  from runem.types import (
16
+ FilePathList,
14
17
  FilePathListLookup,
18
+ HookSpecificKwargs,
15
19
  JobConfig,
16
20
  JobFunction,
17
21
  JobReturn,
@@ -26,7 +30,7 @@ def job_execute_inner(
26
30
  job_config: JobConfig,
27
31
  config_metadata: ConfigMetadata,
28
32
  file_lists: FilePathListLookup,
29
- **kwargs: typing.Any,
33
+ **kwargs: Unpack[HookSpecificKwargs],
30
34
  ) -> typing.Tuple[JobTiming, JobReturn]:
31
35
  """Wrapper for running a job inside a sub-process.
32
36
 
@@ -36,13 +40,12 @@ def job_execute_inner(
36
40
  if config_metadata.args.verbose:
37
41
  log(f"START: '{label}'")
38
42
  root_path: pathlib.Path = config_metadata.cfg_filepath.parent
39
- function: JobFunction
40
43
  job_tags: typing.Optional[JobTags] = Job.get_job_tags(job_config)
41
44
  os.chdir(root_path)
42
- function = get_job_wrapper(job_config, config_metadata.cfg_filepath)
45
+ function: JobFunction = get_job_wrapper(job_config, config_metadata.cfg_filepath)
43
46
 
44
47
  # get the files for all files found for this job's tags
45
- file_list = Job.get_job_files(file_lists, job_tags)
48
+ file_list: FilePathList = Job.get_job_files(file_lists, job_tags)
46
49
 
47
50
  if not file_list:
48
51
  # no files to work on
@@ -77,8 +80,9 @@ def job_execute_inner(
77
80
  log(f"job: running: '{Job.get_job_name(job_config)}'")
78
81
  reports: JobReturn
79
82
  try:
83
+ assert isinstance(function, JobFunction)
80
84
  reports = function(
81
- options=ReadOnlyInformativeDict(config_metadata.options), # type: ignore
85
+ options=ReadOnlyInformativeDict(config_metadata.options),
82
86
  file_list=file_list,
83
87
  procs=config_metadata.args.procs,
84
88
  root_path=root_path,
@@ -107,9 +111,10 @@ def job_execute_inner(
107
111
  def job_execute(
108
112
  job_config: JobConfig,
109
113
  running_jobs: typing.Dict[str, str],
114
+ completed_jobs: typing.Dict[str, str],
110
115
  config_metadata: ConfigMetadata,
111
116
  file_lists: FilePathListLookup,
112
- **kwargs: typing.Any,
117
+ **kwargs: Unpack[HookSpecificKwargs],
113
118
  ) -> typing.Tuple[JobTiming, JobReturn]:
114
119
  """Thin-wrapper around job_execute_inner needed for mocking in tests.
115
120
 
@@ -123,5 +128,6 @@ def job_execute(
123
128
  file_lists,
124
129
  **kwargs,
125
130
  )
131
+ completed_jobs[this_id] = running_jobs[this_id]
126
132
  del running_jobs[this_id]
127
133
  return results
runem/job_wrapper.py CHANGED
@@ -18,7 +18,7 @@ def get_job_wrapper(job_wrapper: JobWrapper, cfg_filepath: pathlib.Path) -> JobF
18
18
  # validate that the command is "understandable" and usable.
19
19
  command_string: str = job_wrapper["command"]
20
20
  validate_simple_command(command_string)
21
- return job_runner_simple_command # type: ignore # NO_COMMIT
21
+ return job_runner_simple_command
22
22
 
23
23
  # if we do not have a simple command address assume we have just an addressed
24
24
  # function
runem/runem.py CHANGED
@@ -28,17 +28,19 @@ import typing
28
28
  from collections import defaultdict
29
29
  from datetime import timedelta
30
30
  from itertools import repeat
31
- from multiprocessing.managers import DictProxy, ListProxy, ValueProxy
31
+ from multiprocessing.managers import DictProxy, ValueProxy
32
32
  from timeit import default_timer as timer
33
+ from types import TracebackType
33
34
 
34
- from halo import Halo
35
+ from rich.console import Console, ConsoleOptions, ConsoleRenderable, RenderResult
36
+ from rich.spinner import Spinner
37
+ from rich.text import Text
35
38
 
36
39
  from runem.command_line import parse_args
37
40
  from runem.config import load_project_config, load_user_configs
38
41
  from runem.config_metadata import ConfigMetadata
39
42
  from runem.config_parse import load_config_metadata
40
43
  from runem.files import find_files
41
- from runem.job import Job
42
44
  from runem.job_execute import job_execute
43
45
  from runem.job_filter import filter_jobs
44
46
  from runem.log import error, log, warn
@@ -58,6 +60,8 @@ from runem.types import (
58
60
  )
59
61
  from runem.utils import printable_set
60
62
 
63
+ rich_console = Console()
64
+
61
65
 
62
66
  def _determine_run_parameters(argv: typing.List[str]) -> ConfigMetadata:
63
67
  """Loads config, parsing cli input and produces the run config.
@@ -85,10 +89,40 @@ def _determine_run_parameters(argv: typing.List[str]) -> ConfigMetadata:
85
89
  return config_metadata
86
90
 
87
91
 
92
+ class DummySpinner(ConsoleRenderable): # pragma: no cover
93
+ """A dummy spinner for when spinners are disabled."""
94
+
95
+ def __init__(self) -> None:
96
+ self.text = ""
97
+
98
+ def __rich__(self) -> Text:
99
+ """Return a rich Text object for rendering."""
100
+ return Text(self.text)
101
+
102
+ def __rich_console__(
103
+ self, console: Console, options: ConsoleOptions
104
+ ) -> RenderResult:
105
+ """Yield an empty string or placeholder text."""
106
+ yield Text(self.text)
107
+
108
+ def __enter__(self) -> None:
109
+ """Support for context manager."""
110
+ pass
111
+
112
+ def __exit__(
113
+ self,
114
+ exc_type: typing.Optional[typing.Type[BaseException]],
115
+ exc_value: typing.Optional[BaseException],
116
+ traceback: typing.Optional[TracebackType],
117
+ ) -> None:
118
+ """Support for context manager."""
119
+ pass
120
+
121
+
88
122
  def _update_progress(
89
123
  label: str,
90
124
  running_jobs: typing.Dict[str, str],
91
- seen_jobs: typing.List[str],
125
+ completed_jobs: typing.Dict[str, str],
92
126
  all_jobs: Jobs,
93
127
  is_running: ValueProxy[bool],
94
128
  num_workers: int,
@@ -99,57 +133,37 @@ def _update_progress(
99
133
  Args:
100
134
  label (str): The identifier.
101
135
  running_jobs (Dict[str, str]): The currently running jobs.
102
- seen_jobs (List[str]): Jobs that the function has previously tracked.
103
136
  all_jobs (Jobs): All jobs, encompassing both completed and running jobs.
104
137
  is_running (ValueProxy[bool]): Flag indicating if jobs are still running.
105
138
  num_workers (int): Indicates the number of workers performing the jobs.
106
139
  """
107
- # Using Halo library to show a loading spinner on console
140
+ # Using the `rich` module to show a loading spinner on console
141
+ spinner: typing.Union[Spinner, DummySpinner]
108
142
  if show_spinner:
109
- spinner = Halo(text="", spinner="dots")
110
- spinner.start()
111
-
112
- # The set of all job labels, and the set of completed jobs
113
- all_job_names: typing.Set[str] = {Job.get_job_name(job) for job in all_jobs}
114
- completed_jobs: typing.Set[str] = set()
143
+ spinner = Spinner("dots", text="Starting tasks...")
144
+ else:
145
+ spinner = DummySpinner()
115
146
 
116
- # This dataset is used to track changes between iterations
117
147
  last_running_jobs_set: typing.Set[str] = set()
118
148
 
119
- while is_running.value:
120
- running_jobs_set: typing.Set[str] = set(running_jobs.values())
121
- seen_jobs = list(running_jobs_set.union(seen_jobs)) # Update the seen jobs
149
+ with rich_console.status(spinner):
150
+ while is_running.value:
151
+ running_jobs_set: typing.Set[str] = set(running_jobs.values())
122
152
 
123
- # Jobs that have disappeared since last check
124
- disappeared_jobs: typing.Set[str] = last_running_jobs_set - running_jobs_set
153
+ # Progress report
154
+ progress: str = f"{len(completed_jobs)}/{len(all_jobs)}"
155
+ running_jobs_list = printable_set(
156
+ running_jobs_set
157
+ ) # Reflect current running jobs accurately
158
+ report: str = f"{label}: {progress}({num_workers}): {running_jobs_list}"
159
+ if show_spinner:
160
+ spinner.text = report
161
+ else:
162
+ if last_running_jobs_set != running_jobs_set:
163
+ rich_console.log(report)
125
164
 
126
- # Jobs that have not yet completed
127
- remaining_jobs: typing.Set[str] = all_job_names - completed_jobs
128
-
129
- # Check if we're closing to completion
130
- workers_retiring: bool = len(remaining_jobs) <= num_workers
131
-
132
- if workers_retiring:
133
- # Handle edge case: a task may have disappeared whilst process was sleeping
134
- all_completed_jobs: typing.Set[str] = all_job_names - remaining_jobs
135
- disappeared_jobs.update(all_completed_jobs - running_jobs_set)
136
-
137
- completed_jobs.update(disappeared_jobs)
138
-
139
- # Prepare progress report
140
- progress: str = f"{len(completed_jobs)}/{len(all_jobs)}"
141
- running_jobs_list = printable_set(running_jobs_set)
142
- if show_spinner:
143
- spinner.text = f"{label}: {progress}({num_workers}): {running_jobs_list}"
144
-
145
- # Update the tracked dataset for the next iteration
146
- last_running_jobs_set = running_jobs_set
147
-
148
- # Sleep to decrease frequency of updates and reduce CPU usage
149
- time.sleep(0.1)
150
-
151
- if show_spinner:
152
- spinner.stop()
165
+ # Sleep for reduced CPU usage
166
+ time.sleep(0.1)
153
167
 
154
168
 
155
169
  def _process_jobs(
@@ -187,8 +201,8 @@ def _process_jobs(
187
201
  subprocess_error: typing.Optional[BaseException] = None
188
202
 
189
203
  with multiprocessing.Manager() as manager:
190
- seen_jobs: ListProxy[str] = manager.list()
191
204
  running_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
205
+ completed_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
192
206
  is_running: ValueProxy[bool] = manager.Value("b", True)
193
207
 
194
208
  terminal_writer_process = multiprocessing.Process(
@@ -196,7 +210,7 @@ def _process_jobs(
196
210
  args=(
197
211
  phase,
198
212
  running_jobs,
199
- seen_jobs,
213
+ completed_jobs,
200
214
  jobs,
201
215
  is_running,
202
216
  num_concurrent_procs,
@@ -209,10 +223,11 @@ def _process_jobs(
209
223
  with multiprocessing.Pool(processes=num_concurrent_procs) as pool:
210
224
  # use starmap so we can pass down the job-configs and the args and the files
211
225
  in_out_job_run_metadatas[phase] = pool.starmap(
212
- job_execute,
226
+ job_execute, # no kwargs passed for jobs here
213
227
  zip(
214
228
  jobs,
215
229
  repeat(running_jobs),
230
+ repeat(completed_jobs),
216
231
  repeat(config_metadata),
217
232
  repeat(file_lists),
218
233
  ),
runem/types.py CHANGED
@@ -1,9 +1,37 @@
1
- import argparse
1
+ """
2
+
3
+ Some note on Unpack and kwargs:
4
+ We *try* to strongly type `**kwargs` for clarity.
5
+ We have tried several ways to define a Generic type that encapsulates
6
+ `**kwargs: SingleType`
7
+ ... but none of the solutions worked with python 3.9 -> 3.12 and mypy 1.9.0,
8
+ so we have to recommend instead using:
9
+ `**kwargs: Unpack[KwArgsType]`
10
+
11
+ For this to work across versions of python where support for Unpack changes;
12
+ for example `Unpack` is a python 3.12 feature, but available in the
13
+ `typing_extensions` module.
14
+
15
+ So, for now, it looks like we get away with importing `Unpack` from the
16
+ `typing_extensions` module, even in python 3.12, so we will use, and
17
+ recommend using, the `typing_extensions` of `Unpack`, until it becomes
18
+ obsolete.
19
+
20
+ Alternatively, we can use the following, but it's unnecessarily verbose.
21
+
22
+ if sys.version_info >= (3, 12): # pragma: no coverage
23
+ from typing import Unpack
24
+ else: # pragma: no coverage
25
+ from typing_extensions import Unpack
26
+ """
27
+
2
28
  import pathlib
3
29
  import typing
4
30
  from datetime import timedelta
5
31
  from enum import Enum
6
32
 
33
+ from typing_extensions import Unpack
34
+
7
35
  from runem.informative_dict import InformativeDict, ReadOnlyInformativeDict
8
36
 
9
37
 
@@ -96,12 +124,6 @@ FilePathSerialise = str
96
124
  FilePathList = typing.List[FilePathSerialise]
97
125
  FilePathListLookup = typing.DefaultDict[JobTag, FilePathList]
98
126
 
99
- # FIXME: this type is no-longer the actual spec of the test-functions
100
- JobFunction = typing.Union[
101
- typing.Callable[[argparse.Namespace, OptionsWritable, FilePathList], None],
102
- typing.Callable[[typing.Any], None],
103
- ]
104
-
105
127
 
106
128
  class JobParamConfig(typing.TypedDict):
107
129
  """Configures what parameters are passed to the test-callable.
@@ -250,3 +272,86 @@ ConfigNodes = typing.Union[
250
272
  Config = typing.List[ConfigNodes]
251
273
 
252
274
  UserConfigMetadata = typing.List[typing.Tuple[Config, pathlib.Path]]
275
+
276
+
277
+ class CommonKwargs(
278
+ typing.TypedDict,
279
+ total=True, # each of these are guaranteed to exist in jobs and hooks
280
+ ):
281
+ """Defines the base args that are passed to all jobs.
282
+
283
+ As we call hooks and job-task in the same manner, this defines the variables that we
284
+ can access from both hooks and job-tasks.
285
+ """
286
+
287
+ root_path: pathlib.Path # the path where the .runem.yml file is
288
+ job: JobConfig # the job or hook task spec ¢ TODO: rename this
289
+ label: str # the name of the hook or the job-label
290
+ options: Options # options passed in on the command line
291
+ procs: int # the max number of concurrent procs to run
292
+ verbose: bool # control log verbosity
293
+
294
+
295
+ class HookSpecificKwargs(typing.TypedDict, total=False):
296
+ """Defines the args that are passed down to the hooks.
297
+
298
+ NOTE: that although these however
299
+ outside of the *hook* context, the data will not be present. Such is the
300
+ difficulty in dynamic programming.
301
+ """
302
+
303
+ wall_clock_time_saved: timedelta # only on `HookName.ON_EXIT`
304
+
305
+
306
+ class JobTaskKwargs(
307
+ typing.TypedDict,
308
+ total=False, # for now, we don't enforce these types for job-context, but we should.
309
+ ):
310
+ """Defines the task-specific args for job-task functions."""
311
+
312
+ file_list: FilePathList
313
+ record_sub_job_time: typing.Optional[typing.Callable[[str, timedelta], None]]
314
+
315
+
316
+ class HookKwargs(CommonKwargs, HookSpecificKwargs):
317
+ """A merged set of kwargs for runem-hooks."""
318
+
319
+ pass
320
+
321
+
322
+ class JobKwargs(CommonKwargs, JobTaskKwargs):
323
+ """A merged set of kwargs for job-tasks."""
324
+
325
+ pass
326
+
327
+
328
+ class AllKwargs(CommonKwargs, JobTaskKwargs, HookSpecificKwargs):
329
+ """A merged set of kwargs for al job-functions."""
330
+
331
+ pass
332
+
333
+
334
+ @typing.runtime_checkable
335
+ class JobFunction(typing.Protocol):
336
+ def __call__(self, **kwargs: Unpack[AllKwargs]) -> JobReturn: # pragma: no cover
337
+ """Defines the call() protocol's abstract pattern for job-tasks."""
338
+
339
+ @property
340
+ def __name__(self) -> str: # pragma: no cover
341
+ """Defines the name protocol for job-task functions.
342
+
343
+ This is primarily used for internal tests but can be useful for introspection.
344
+ """
345
+
346
+
347
+ def _hook_example(
348
+ wall_clock_time_saved: timedelta,
349
+ **kwargs: typing.Any,
350
+ ) -> None:
351
+ """An example hook."""
352
+
353
+
354
+ def _job_task_example(
355
+ **kwargs: Unpack[JobKwargs],
356
+ ) -> None:
357
+ """An example job-task function."""
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runem
3
- Version: 0.0.32
3
+ Version: 0.1.1
4
4
  Summary: Awesome runem created by lursight
5
5
  Home-page: https://github.com/lursight/runem/
6
6
  Author: lursight
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: halo
10
9
  Requires-Dist: packaging
11
10
  Requires-Dist: PyYAML
11
+ Requires-Dist: rich
12
+ Requires-Dist: typing-extensions
12
13
  Provides-Extra: tests
13
- Requires-Dist: black==24.3.0; extra == "tests"
14
- Requires-Dist: coverage==7.4.4; extra == "tests"
14
+ Requires-Dist: black==24.10.0; extra == "tests"
15
+ Requires-Dist: coverage==7.5; extra == "tests"
15
16
  Requires-Dist: docformatter==1.7.5; extra == "tests"
16
17
  Requires-Dist: flake8-bugbear==24.2.6; extra == "tests"
17
18
  Requires-Dist: flake8==7.0.0; extra == "tests"
@@ -22,10 +23,10 @@ Requires-Dist: mypy==1.9.0; extra == "tests"
22
23
  Requires-Dist: pydocstyle==6.3.0; extra == "tests"
23
24
  Requires-Dist: pylint==3.1.0; extra == "tests"
24
25
  Requires-Dist: pylama==8.4.1; extra == "tests"
25
- Requires-Dist: pytest-cov==4.1.0; extra == "tests"
26
+ Requires-Dist: pytest-cov==6.0.0; extra == "tests"
26
27
  Requires-Dist: pytest-profiling==1.7.0; extra == "tests"
27
- Requires-Dist: pytest-xdist==3.5.0; extra == "tests"
28
- Requires-Dist: pytest==8.1.1; extra == "tests"
28
+ Requires-Dist: pytest-xdist==3.6.1; extra == "tests"
29
+ Requires-Dist: pytest==8.3.3; extra == "tests"
29
30
  Requires-Dist: setuptools; extra == "tests"
30
31
  Requires-Dist: termplotlib==0.3.9; extra == "tests"
31
32
  Requires-Dist: tox; extra == "tests"
@@ -1,33 +1,33 @@
1
- runem/VERSION,sha256=-h82hZtjHMX-cbkdelMOLC62FvWkuDkAAR466UOFbEc,7
1
+ runem/VERSION,sha256=gERzFlKfxiAU-6oEf7Z8Uaww4ygwzaNwQFNJ2NZtGWw,6
2
2
  runem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  runem/__main__.py,sha256=dsOiVZegpfK9JOs5n7UmbX5iwwbj7iFkEbLoVeEgAn4,136
4
4
  runem/base.py,sha256=EZfR7FIlwEdU9Vfe47Wk2DOO8GQqpKxxLNKp6YHueZ4,316
5
5
  runem/blocking_print.py,sha256=S9dtgAeuTzc2-ht-vk9Wl6l-0PwS2tYbHDHDQQitrlA,841
6
6
  runem/cli.py,sha256=wEt_Jnumhl8SiOdKdSJzLkJpWv6n3_Odhi_HeIixr1k,134
7
- runem/command_line.py,sha256=QwtmDTx2BJ1OfMpULUzOcWBibwgEiRHfz58prZgkEyo,12402
7
+ runem/command_line.py,sha256=mJbs7uUtAxSnbMxO3C08feOkVusK36x_LIQaeJHivzA,13700
8
8
  runem/config.py,sha256=y-e6j84FDiLSKKw9ShDzRlnS5t2e81MW8fKSKtxtJtg,5935
9
9
  runem/config_metadata.py,sha256=Vy7dx8F-Z5jEp16OP2y6vHHoGkyhoCaTG4KIVkMWR7M,3232
10
10
  runem/config_parse.py,sha256=6mCamzWu7HTotmqFJmLZg9FFE6qe1-rpmo8_v5ESPW8,13401
11
11
  runem/files.py,sha256=QZqPS7OA98lEwlhJNtnaSWlEeTlI8_yn-zjf3QAPoJk,4384
12
- runem/hook_manager.py,sha256=9T-4omyjBPZ6ua_37UWpT1dwNMbb4SKwvxYcN6fVxLE,4163
12
+ runem/hook_manager.py,sha256=8zbMuY4FTSm4kDk2wLBa60zl9NfavnmcOR-qL5jhKTc,4276
13
13
  runem/informative_dict.py,sha256=U7p9z78UwOT4TAfng1iDXCEyeYz6C-XZlx9Z1pWNVrI,1548
14
14
  runem/job.py,sha256=QVXvzz67fJk__-h0womFQsB80-w41E3XRcHpxmRnv3o,2912
15
- runem/job_execute.py,sha256=xmH-O0qnsS2P_s3NCpXmla_kjsGgGCy0QdFH4pbY8iI,4000
15
+ runem/job_execute.py,sha256=eUyvmtjFdJ49AaIOtqJbRXinkF_UFuz20vBGVrYvt0U,4241
16
16
  runem/job_filter.py,sha256=fuxyKCHpTB4HlT_QagBk-IhhmWMlOr9Y9s5voP4yzYU,5370
17
17
  runem/job_runner_simple_command.py,sha256=jxBukPm9bTLNhfQCkqNG5VepvB2ysmWAZwhBPHoTA6o,1091
18
- runem/job_wrapper.py,sha256=wwPeWkMqhORl_Po6V-ofYZ-v9Ho0cFMprLRpY-30DyQ,964
18
+ runem/job_wrapper.py,sha256=H57b52Apcg3q3LyMxus6vNrlv615J4CXiWaQrv8vOgY,936
19
19
  runem/job_wrapper_python.py,sha256=m5xbWQxkDRtawjCcgxctzouv_Pny6bKiG2OPVE1hlgo,4226
20
20
  runem/log.py,sha256=dIrocigvIJs1ZGkAzTogXkAK-0ZW3q5FkjpDgLdeW-E,630
21
21
  runem/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  runem/report.py,sha256=IBCtMgGwnvVbEEqDWbYBGUZmTIzBLnpXqYSK5uu3vgk,8987
23
23
  runem/run_command.py,sha256=Egl_j4bJ9mwi2JEFCsl0W6WH2IRgIdpMN7qdj8voClQ,6386
24
- runem/runem.py,sha256=RIKF-l_ziGs0oKEueVkfygmnc_xiIdQo2qNDpiA-2Zs,13013
24
+ runem/runem.py,sha256=TPBVLiZwEAmKNoQZVHBrrMVgHQfcsJM4uVY9lrrp8X8,13207
25
25
  runem/runem_version.py,sha256=MbETwZO2Tb1Y3hX_OYZjKepEMKA1cjNvr-7Cqhz6e3s,271
26
- runem/types.py,sha256=TLagRdB6-4gKqETAeJzo7-HFwBqQWGTwHcw2slSKN0U,7445
26
+ runem/types.py,sha256=-RHnskpWEtvqNlrHx3mHrkx6ujEdkCxcU7hu3_lHrJU,10671
27
27
  runem/utils.py,sha256=3N_kel9LsriiMq7kOjT14XhfxUOgz4hdDg97wlLKm3U,221
28
- runem-0.0.32.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
29
- runem-0.0.32.dist-info/METADATA,sha256=Xa7dVT07j4ihVpqFouck31Exs4691zseqaqbnCdcY8c,5811
30
- runem-0.0.32.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
31
- runem-0.0.32.dist-info/entry_points.txt,sha256=nu0g_vBeuPihYtimbtlNusxWovylMppvJ8UxdJlJfvM,46
32
- runem-0.0.32.dist-info/top_level.txt,sha256=gK6iqh9OfHDDpErioCC9ul_zx2Q5zWTALtcuGU7Vil4,6
33
- runem-0.0.32.dist-info/RECORD,,
28
+ runem-0.1.1.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
29
+ runem-0.1.1.dist-info/METADATA,sha256=Eve8gICbiXMPHxQUUtXqB7hU7Dgjq5Gaq-whnLvzCtk,5842
30
+ runem-0.1.1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
31
+ runem-0.1.1.dist-info/entry_points.txt,sha256=nu0g_vBeuPihYtimbtlNusxWovylMppvJ8UxdJlJfvM,46
32
+ runem-0.1.1.dist-info/top_level.txt,sha256=gK6iqh9OfHDDpErioCC9ul_zx2Q5zWTALtcuGU7Vil4,6
33
+ runem-0.1.1.dist-info/RECORD,,
File without changes