robotcode-runner 2.7.0__py3-none-any.whl → 2.8.0.dev8__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.
@@ -1,7 +1,8 @@
1
1
  import os
2
2
  import weakref
3
+ from dataclasses import dataclass, field
3
4
  from pathlib import Path
4
- from typing import Any, Dict, List, Optional, Set, Tuple, Union, cast
5
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
5
6
 
6
7
  import click
7
8
  from robot.errors import DataError, Information
@@ -12,16 +13,18 @@ from robot.version import get_full_version
12
13
  import robotcode.modifiers
13
14
  from robotcode.core.ignore_spec import DEFAULT_SPEC_RULES, GIT_IGNORE_FILE, ROBOT_IGNORE_FILE, IgnoreSpec
14
15
  from robotcode.core.utils.path import path_is_relative_to
16
+ from robotcode.modifiers.metadata_modifiers import MetadataPattern
15
17
  from robotcode.plugin import Application, pass_application
16
18
  from robotcode.plugin.click_helper.aliases import AliasedCommand
17
19
  from robotcode.plugin.click_helper.types import add_options
18
- from robotcode.plugin.click_helper.wrappable import wrappable
20
+ from robotcode.plugin.click_helper.wrappable import invoked_command_is_wrappable, run_under_wrapper, wrappable
19
21
  from robotcode.robot.config.loader import load_robot_config_from_path
20
22
  from robotcode.robot.config.model import RobotBaseProfile
21
23
  from robotcode.robot.config.utils import get_config_files
22
24
  from robotcode.robot.utils import RF_VERSION
23
25
 
24
26
  from ..__version__ import __version__
27
+ from ._robot_version import RobotVersionOption
25
28
  from ._search import SearchMatcher, SearchModifier
26
29
 
27
30
  _app: Optional[Application] = None
@@ -160,6 +163,8 @@ class RobotFrameworkEx(RobotFramework):
160
163
  by_longname: Tuple[str, ...] = (),
161
164
  exclude_by_longname: Tuple[str, ...] = (),
162
165
  search_matcher: Optional[SearchMatcher] = None,
166
+ by_test_metadata: Tuple[str, ...] = (),
167
+ exclude_by_test_metadata: Tuple[str, ...] = (),
163
168
  ) -> None:
164
169
  super().__init__()
165
170
  self.app = app
@@ -170,6 +175,8 @@ class RobotFrameworkEx(RobotFramework):
170
175
  self.by_longname = by_longname
171
176
  self.exclude_by_longname = exclude_by_longname
172
177
  self.search_matcher = search_matcher
178
+ self.by_test_metadata = by_test_metadata
179
+ self.exclude_by_test_metadata = exclude_by_test_metadata
173
180
 
174
181
  def parse_arguments(self, cli_args: Any) -> Any:
175
182
  try:
@@ -179,7 +186,13 @@ class RobotFrameworkEx(RobotFramework):
179
186
  if Path(arg).is_absolute():
180
187
  continue
181
188
 
182
- arguments[i] = str((self._orig_cwd / Path(arg)).absolute().relative_to(self.root_folder))
189
+ absolute = (self._orig_cwd / Path(arg)).absolute()
190
+ try:
191
+ arguments[i] = str(absolute.relative_to(self.root_folder))
192
+ except ValueError:
193
+ # The path lies outside the project root, which the run changes into,
194
+ # so it stays valid only as an absolute path.
195
+ arguments[i] = str(absolute)
183
196
 
184
197
  except DataError:
185
198
  options, arguments = super().parse_arguments((*cli_args, *self.paths))
@@ -187,6 +200,11 @@ class RobotFrameworkEx(RobotFramework):
187
200
  if not arguments:
188
201
  arguments = self.paths
189
202
 
203
+ # Parsed before anything runs: an invalid pattern raises `DataError`,
204
+ # which Robot reports like an invalid option.
205
+ test_metadata_patterns = [MetadataPattern(p) for p in self.by_test_metadata]
206
+ excluded_test_metadata_patterns = [MetadataPattern(p) for p in self.exclude_by_test_metadata]
207
+
190
208
  if self.dry:
191
209
  line_end = "\n"
192
210
  raise Information(
@@ -204,6 +222,12 @@ class RobotFrameworkEx(RobotFramework):
204
222
  if self.exclude_by_longname:
205
223
  modifiers.append(robotcode.modifiers.ExcludedByLongName(*self.exclude_by_longname, root_name=root_name))
206
224
 
225
+ if test_metadata_patterns:
226
+ modifiers.append(robotcode.modifiers.ByTestMetadata(*test_metadata_patterns))
227
+
228
+ if excluded_test_metadata_patterns:
229
+ modifiers.append(robotcode.modifiers.ExcludedByTestMetadata(*excluded_test_metadata_patterns))
230
+
207
231
  if self.search_matcher is not None:
208
232
  modifiers.append(SearchModifier(self.search_matcher))
209
233
 
@@ -214,21 +238,21 @@ class RobotFrameworkEx(RobotFramework):
214
238
 
215
239
 
216
240
  # mypy: disable-error-code="arg-type"
217
- ROBOT_VERSION_OPTIONS = {
241
+ ROBOT_VERSION_OPTIONS = [
218
242
  click.version_option(
219
243
  version=__version__,
220
244
  package_name="robotcode.runner",
221
245
  prog_name="RobotCode Runner",
222
246
  message=f"%(prog)s %(version)s\n{USAGE.splitlines()[0].split(' -- ')[0].strip()} {get_full_version()}",
223
247
  ),
224
- }
248
+ ]
225
249
 
226
- ROBOT_SIMPLE_OPTIONS: Set[click.Command] = {
250
+ ROBOT_SIMPLE_OPTIONS = [
227
251
  *ROBOT_VERSION_OPTIONS,
228
252
  click.argument("robot_options_and_args", nargs=-1, type=click.Path()),
229
- }
253
+ ]
230
254
 
231
- ROBOT_OPTIONS: Set[click.Command] = {
255
+ ROBOT_OPTIONS = [
232
256
  click.option(
233
257
  "--by-longname",
234
258
  "-bl",
@@ -243,18 +267,61 @@ ROBOT_OPTIONS: Set[click.Command] = {
243
267
  multiple=True,
244
268
  help="Excludes tests/tasks or suites by longname.",
245
269
  ),
270
+ # test metadata exists since Robot Framework 7.5
271
+ click.option(
272
+ "--by-test-metadata",
273
+ "-btm",
274
+ cls=RobotVersionOption,
275
+ since=(7, 5),
276
+ type=str,
277
+ multiple=True,
278
+ metavar="PATTERN",
279
+ help="Select tests/tasks by their metadata, for example `Issue:4409 AND Author:Hans*`.",
280
+ ),
281
+ click.option(
282
+ "--exclude-by-test-metadata",
283
+ "-ebtm",
284
+ cls=RobotVersionOption,
285
+ since=(7, 5),
286
+ type=str,
287
+ multiple=True,
288
+ metavar="PATTERN",
289
+ help="Excludes tests/tasks by their metadata. Same patterns as `--by-test-metadata`.",
290
+ ),
246
291
  *ROBOT_SIMPLE_OPTIONS,
247
- }
292
+ ]
248
293
 
249
294
 
250
- def handle_robot_options(
251
- app: Application, robot_options_and_args: Tuple[str, ...]
252
- ) -> Tuple[Optional[Path], RobotBaseProfile, List[str]]:
253
- global _app
254
- _app = app
295
+ _LOADED_PROFILES = "robotcode.runner.loaded_profiles"
296
+
297
+ _Message = Union[str, Callable[[], Any]]
298
+
299
+
300
+ @dataclass
301
+ class LoadedRobotProfile:
302
+ """The configuration of a command, loaded once, with what loading it reported."""
303
+
304
+ root_folder: Optional[Path] = None
305
+ profile: Optional[RobotBaseProfile] = None
306
+ error: Optional[Exception] = None
307
+ messages: List[Tuple[Callable[[_Message], None], _Message]] = field(default_factory=list)
308
+
309
+ def show_messages(self) -> None:
310
+ messages, self.messages = self.messages, []
311
+ for show, message in messages:
312
+ show(message)
255
313
 
256
- _patch()
257
314
 
315
+ def load_robot_profile(app: Application, robot_options_and_args: Tuple[str, ...]) -> LoadedRobotProfile:
316
+ """Load the configuration of a command once, from the paths among its arguments.
317
+
318
+ The result is kept for the whole command, with the messages and the error of the
319
+ loading, for later calls with the same paths, such as the one in `handle_robot_options`,
320
+ which shows them. A command that runs Robot Framework (`@wrappable`) is then started
321
+ again under its `wrapper`. Such a command calls this first, before it starts a server,
322
+ an interpreter or a debugger, so that they run under the `wrapper` too, while an error
323
+ of the configuration still appears where `handle_robot_options` raises it.
324
+ """
258
325
  robot_arguments: Optional[List[Union[str, Path]]] = None
259
326
 
260
327
  try:
@@ -263,21 +330,68 @@ def handle_robot_options(
263
330
  except (DataError, Information):
264
331
  pass
265
332
 
266
- config_files, root_folder, _ = get_config_files(
267
- robot_arguments,
268
- app.config.config_files,
269
- root_folder=app.config.root,
270
- no_vcs=app.config.no_vcs,
271
- verbose_callback=app.verbose,
333
+ ctx = click.get_current_context(silent=True)
334
+ loaded: Dict[Tuple[str, ...], LoadedRobotProfile] = (
335
+ ctx.find_root().meta.setdefault(_LOADED_PROFILES, {}) if ctx is not None else {}
272
336
  )
337
+ key = tuple(str(Path(a).absolute()) for a in robot_arguments or ())
338
+ if key in loaded:
339
+ return loaded[key]
340
+
341
+ result = LoadedRobotProfile()
342
+ loaded[key] = result
343
+
344
+ def verbose(message: _Message) -> None:
345
+ result.messages.append((app.verbose, message))
346
+
347
+ def error(message: _Message) -> None:
348
+ result.messages.append((app.error, message))
349
+
273
350
  try:
274
- profile = (
275
- load_robot_config_from_path(*config_files, verbose_callback=app.verbose)
276
- .combine_profiles(*(app.config.profiles or []), verbose_callback=app.verbose, error_callback=app.error)
277
- .evaluated_with_env(verbose_callback=app.verbose, error_callback=app.error)
351
+ config_files, result.root_folder, _ = get_config_files(
352
+ robot_arguments,
353
+ app.config.config_files,
354
+ root_folder=app.config.root,
355
+ no_vcs=app.config.no_vcs,
356
+ verbose_callback=verbose,
278
357
  )
279
- except (TypeError, ValueError) as e:
280
- raise click.ClickException(str(e)) from e
358
+ result.profile = (
359
+ load_robot_config_from_path(*config_files, verbose_callback=verbose)
360
+ .combine_profiles(*(app.config.profiles or []), verbose_callback=verbose, error_callback=error)
361
+ .evaluated_with_env(verbose_callback=verbose, error_callback=error)
362
+ )
363
+ except Exception as e:
364
+ result.error = e
365
+
366
+ if result.profile is not None and ctx is not None and invoked_command_is_wrappable(ctx):
367
+ try:
368
+ run_under_wrapper(app, result.profile.wrapper)
369
+ except click.ClickException:
370
+ # The wrapper did not start, so robotcode under it will not show them.
371
+ result.show_messages()
372
+ raise
373
+
374
+ return result
375
+
376
+
377
+ def handle_robot_options(
378
+ app: Application, robot_options_and_args: Tuple[str, ...]
379
+ ) -> Tuple[Optional[Path], RobotBaseProfile, List[str]]:
380
+ global _app
381
+ _app = app
382
+
383
+ _patch()
384
+
385
+ loaded = load_robot_profile(app, robot_options_and_args)
386
+ loaded.show_messages()
387
+
388
+ if loaded.error is not None:
389
+ if isinstance(loaded.error, (TypeError, ValueError)):
390
+ raise click.ClickException(str(loaded.error)) from loaded.error
391
+ raise loaded.error
392
+
393
+ root_folder = loaded.root_folder
394
+ profile = cast(RobotBaseProfile, loaded.profile)
281
395
 
282
396
  cmd_options = profile.build_command_line()
283
397
 
@@ -308,6 +422,8 @@ def robot(
308
422
  app: Application,
309
423
  by_longname: Tuple[str, ...],
310
424
  exclude_by_longname: Tuple[str, ...],
425
+ by_test_metadata: Tuple[str, ...],
426
+ exclude_by_test_metadata: Tuple[str, ...],
311
427
  robot_options_and_args: Tuple[str, ...],
312
428
  ) -> None:
313
429
  """Runs `robot` with the selected configuration, profiles, options and arguments.
@@ -354,6 +470,8 @@ def robot(
354
470
  orig_folder,
355
471
  by_longname,
356
472
  exclude_by_longname,
473
+ by_test_metadata=by_test_metadata,
474
+ exclude_by_test_metadata=exclude_by_test_metadata,
357
475
  ).execute_cli((*cmd_options, *console_links_args, *robot_options_and_args), exit=False),
358
476
  )
359
477
  )
@@ -6,6 +6,7 @@ from robot.testdoc import USAGE, TestDoc
6
6
  from robot.version import get_full_version
7
7
 
8
8
  from robotcode.plugin import Application, pass_application
9
+ from robotcode.plugin.click_helper.exit_codes import robot_exit_codes
9
10
  from robotcode.robot.config.loader import load_robot_config_from_path
10
11
  from robotcode.robot.config.model import TestDocProfile
11
12
  from robotcode.robot.config.utils import get_config_files
@@ -35,6 +36,7 @@ class TestDocEx(TestDoc):
35
36
  return super().main(arguments, **options)
36
37
 
37
38
 
39
+ @robot_exit_codes
38
40
  @click.command(
39
41
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
40
42
  add_help_option=True,
@@ -80,11 +82,12 @@ def testdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None:
80
82
  except (TypeError, ValueError) as e:
81
83
  raise click.ClickException(str(e)) from e
82
84
 
83
- testdoc_options = profile.testdoc
84
- if testdoc_options is None:
85
- testdoc_options = TestDocProfile()
86
-
85
+ # `testdoc` gets the settings outside of `[testdoc]` that it shares with `robot`. `[testdoc]` comes last, so
86
+ # it wins and its `extend-` settings add to them.
87
+ testdoc_options = TestDocProfile()
87
88
  testdoc_options.add_options(profile)
89
+ if profile.testdoc is not None:
90
+ testdoc_options.add_options(profile.testdoc)
88
91
 
89
92
  options = testdoc_options.build_command_line()
90
93
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: robotcode-runner
3
- Version: 2.7.0
3
+ Version: 2.8.0-dev.8
4
4
  Summary: RobotCode runner for Robot Framework
5
5
  Project-URL: Homepage, https://robotcode.io
6
6
  Project-URL: Donate, https://opencollective.com/robotcode
@@ -20,18 +20,19 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.15
23
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
25
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
26
  Classifier: Topic :: Utilities
26
27
  Classifier: Typing :: Typed
27
28
  Requires-Python: >=3.10
28
- Requires-Dist: robotcode-modifiers==2.7.0
29
- Requires-Dist: robotcode-plugin==2.7.0
30
- Requires-Dist: robotcode-robot==2.7.0
31
- Requires-Dist: robotcode==2.7.0
29
+ Requires-Dist: robotcode-modifiers==2.8.0-dev.8
30
+ Requires-Dist: robotcode-plugin==2.8.0-dev.8
31
+ Requires-Dist: robotcode-robot==2.8.0-dev.8
32
+ Requires-Dist: robotcode==2.8.0-dev.8
32
33
  Requires-Dist: robotframework>=5.0.0
33
34
  Provides-Extra: html
34
- Requires-Dist: html-to-markdown>=2.0; extra == 'html'
35
+ Requires-Dist: html-to-markdown>=3.18; extra == 'html'
35
36
  Description-Content-Type: text/markdown
36
37
 
37
38
  # robotcode-runner
@@ -0,0 +1,25 @@
1
+ robotcode/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ robotcode/runner/__version__.py,sha256=JvoVcYcO_7K_aZkOPOeHH31iT5EQpvrGTx1sNBjoeYU,28
3
+ robotcode/runner/hooks.py,sha256=MSqEHc3wQIBgCXf9U9B0sA_zE8vbiTdoAlfaWz_3uIo,270
4
+ robotcode/runner/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
5
+ robotcode/runner/cli/__init__.py,sha256=ozQtrMXxWIlJz3QKZhaGEIDjSCL0TPtF1XeP8WL8HrE,240
6
+ robotcode/runner/cli/_markdown.py,sha256=4tHR4514XiHZiat-eMzxMclO_E7bwtdMTIo11rap80Q,12474
7
+ robotcode/runner/cli/_robot_version.py,sha256=aXpMkrA4IHtCvqi_Dv9hr2D6UqBEI7j8PwsMgMJ0P-E,961
8
+ robotcode/runner/cli/_search.py,sha256=JhKCDAdu_Mv89ug0p6nMumxdMAAwgbtdLt2CoJEZjsI,15874
9
+ robotcode/runner/cli/libdoc.py,sha256=IanGjNOkm45VESVO_3pNoMpXtjpXMX9Li-J8lhPL2X8,3782
10
+ robotcode/runner/cli/rebot.py,sha256=QIKYJD-4w7wquv46EygylNhBWXq1hj7-jV7vZvAHuHw,4922
11
+ robotcode/runner/cli/robot.py,sha256=fxQz5zIdSafUalYivN7L-1di7YG4aJ7MJu2ffwiHbVw,16651
12
+ robotcode/runner/cli/testdoc.py,sha256=UMpa1GOuyAIENGziepSprM4vHmR0QNfyBYWKN9cqd38,3756
13
+ robotcode/runner/cli/discover/__init__.py,sha256=CRFSvVDlOX1avV3kx-d2QUOugDvgKPy8RBApsAUq15s,55
14
+ robotcode/runner/cli/discover/_models.py,sha256=mkRwJTyQUWLVGbupCtYMVAHFhUouuMJ3TfR7_gz_lZI,2127
15
+ robotcode/runner/cli/discover/_render.py,sha256=3GYv0bNPVOLDAabg6FYXmbCDLLztjeLFrEhj-1inUvY,20091
16
+ robotcode/runner/cli/discover/discover.py,sha256=_YFeseR5pGPHIhltiwuNrxbHlk_uCcyB0cygyHPpOQM,46997
17
+ robotcode/runner/cli/results/__init__.py,sha256=aEyNen0WWvq3kF_VTRLz99_B5CMChM0aelpzWKW6Rac,52
18
+ robotcode/runner/cli/results/_html.py,sha256=VxfsMXeHA-CBpcHot-PRxoXXqNDvpJyPItofTRf-J3I,26353
19
+ robotcode/runner/cli/results/_models.py,sha256=ESn9VKIF-mySCnE144wNoYMmYMKuniZKQW5O61lko5I,7458
20
+ robotcode/runner/cli/results/_render.py,sha256=9DNk1d7F01bGQBCKBgtcqw_asvmnX1pJ02-kU02ptss,32471
21
+ robotcode/runner/cli/results/results.py,sha256=FtSPgsHS4D-6Guvur0zn6f3lYsPZigZaMpRzNBrxAdw,75501
22
+ robotcode_runner-2.8.0.dev8.dist-info/METADATA,sha256=ETbyZQyRGrplGbSZMZBO9iWmryQdtdRi6r8FBh06D2I,3050
23
+ robotcode_runner-2.8.0.dev8.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
24
+ robotcode_runner-2.8.0.dev8.dist-info/entry_points.txt,sha256=igwjQYHlUfgCN-SohGVeQOnxOtqUzd2Jq94Ie8Se1Aw,44
25
+ robotcode_runner-2.8.0.dev8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.31.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,24 +0,0 @@
1
- robotcode/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- robotcode/runner/__version__.py,sha256=EtKWW0Hnl5oWglRNH0HZigvcDT2FEs58ek8buJdwW1E,22
3
- robotcode/runner/hooks.py,sha256=MSqEHc3wQIBgCXf9U9B0sA_zE8vbiTdoAlfaWz_3uIo,270
4
- robotcode/runner/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
5
- robotcode/runner/cli/__init__.py,sha256=ozQtrMXxWIlJz3QKZhaGEIDjSCL0TPtF1XeP8WL8HrE,240
6
- robotcode/runner/cli/_markdown.py,sha256=6zXGB3wYngaKg5tu-GuwCuZvvRKSxfl97jUxjRSkfEU,12022
7
- robotcode/runner/cli/_search.py,sha256=r6Uh-0ftc8WnkNOfkIgNBgJ2tSRWzgO0M_bM6cPFnxY,14421
8
- robotcode/runner/cli/libdoc.py,sha256=VOKb0Xpct9mGgZkCdUng13o3ZpvxnPg_LqWNP13BEJU,3468
9
- robotcode/runner/cli/rebot.py,sha256=LhMvkn92PQBQaniA1vRlu9J_XJ9N_wj95v48sdIvlG4,3943
10
- robotcode/runner/cli/robot.py,sha256=syHOpewghJg0EjdYe4hZCfPCvSMa38dA2pa4qeIplZc,11868
11
- robotcode/runner/cli/testdoc.py,sha256=qc11KKwgGVXUydqsPS1M6x-j69wrtkFACeZEBeGXZBk,3479
12
- robotcode/runner/cli/discover/__init__.py,sha256=CRFSvVDlOX1avV3kx-d2QUOugDvgKPy8RBApsAUq15s,55
13
- robotcode/runner/cli/discover/_models.py,sha256=YsJ9zn5KZH9TmsocAS3IeioU8-K3KqEl63TQa4HVAuc,1768
14
- robotcode/runner/cli/discover/_render.py,sha256=C1tee_gitlS4JsF1NVIuYczzqnTTKXOnTiEAJfGtCW0,16523
15
- robotcode/runner/cli/discover/discover.py,sha256=q-XAMG1pz4emyKNZOe66bIWU6awa5PxUQPBI2KpcSl0,38673
16
- robotcode/runner/cli/results/__init__.py,sha256=aEyNen0WWvq3kF_VTRLz99_B5CMChM0aelpzWKW6Rac,52
17
- robotcode/runner/cli/results/_html.py,sha256=MD_6RZMeYNORFOINB8RJA8UhKEgXl6pldCToF51Y3qo,19422
18
- robotcode/runner/cli/results/_models.py,sha256=1zo-LoR-_lLIrnEwZh61qAryCgirQWkJNcZ5yzognKo,7271
19
- robotcode/runner/cli/results/_render.py,sha256=IVQiD_plufMyEU3WIDntGx_aqTiFc-Pg10jtS0dfXFk,32104
20
- robotcode/runner/cli/results/results.py,sha256=Vofh1RAYxM_uTJAjAyjYB7vJFROOHD6S9KkQzeGyzHI,67126
21
- robotcode_runner-2.7.0.dist-info/METADATA,sha256=_PxtY6zq5PTEwHaNQN9rh84nY0SG1Jq_rfrdNf40bW8,2968
22
- robotcode_runner-2.7.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
23
- robotcode_runner-2.7.0.dist-info/entry_points.txt,sha256=igwjQYHlUfgCN-SohGVeQOnxOtqUzd2Jq94Ie8Se1Aw,44
24
- robotcode_runner-2.7.0.dist-info/RECORD,,