artefacts-cli 0.9.4__tar.gz → 0.9.5__tar.gz

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 (84) hide show
  1. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/CHANGELOG.md +6 -1
  2. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/PKG-INFO +4 -1
  3. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/__init__.py +17 -7
  4. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/config.py +18 -9
  5. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/locales/art.pot +15 -14
  6. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/locales/base.pot +15 -14
  7. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/locales/click.pot +2 -2
  8. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/locales/ja_JP/LC_MESSAGES/artefacts.po +18 -15
  9. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/version.py +2 -2
  10. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/PKG-INFO +4 -1
  11. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/SOURCES.txt +1 -0
  12. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/requires.txt +4 -0
  13. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/bin/release +5 -0
  14. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/pyproject.toml +5 -0
  15. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/pytest.ini +4 -0
  16. artefacts_cli-0.9.5/scripts/whats_latest.rb +11 -0
  17. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_warp.py +137 -3
  18. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_warp_run.py +62 -1
  19. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/conftest.py +11 -2
  20. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/.pyre_configuration +0 -0
  21. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/README.md +0 -0
  22. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/README_INTERNAL.md +0 -0
  23. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/__init__.py +0 -0
  24. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/app.py +0 -0
  25. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/app_containers.py +0 -0
  26. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/app_containers.pyi +0 -0
  27. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/bagparser.py +0 -0
  28. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/constants.py +0 -0
  29. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/containers/__init__.py +0 -0
  30. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/containers/docker_cm.py +0 -0
  31. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/containers/docker_utils.py +0 -0
  32. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/containers/utils.py +0 -0
  33. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/errors.py +0 -0
  34. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/helpers.py +0 -0
  35. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/i18n.py +0 -0
  36. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/logger.py +0 -0
  37. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/other.py +0 -0
  38. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/parameters.py +0 -0
  39. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/ros1.py +0 -0
  40. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/ros2.py +0 -0
  41. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/utils.py +0 -0
  42. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/cli/utils_ros.py +0 -0
  43. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/copava/__init__.py +0 -0
  44. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts/wrappers/artefacts_ros1_meta.launch +0 -0
  45. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts.yaml +0 -0
  46. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/dependency_links.txt +0 -0
  47. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/entry_points.txt +0 -0
  48. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/top_level.txt +0 -0
  49. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/test_run_remote.yaml +0 -0
  50. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/CMakeLists.txt +0 -0
  51. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/test_meta.launch +0 -0
  52. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/test_turtle.launch +0 -0
  53. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/turtle_odometry.launch +0 -0
  54. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/package.xml +0 -0
  55. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/setup.py +0 -0
  56. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/TestTurtle.py +0 -0
  57. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/__init__.py +0 -0
  58. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_odom.py +0 -0
  59. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_post_process.py +0 -0
  60. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_trajectory.py +0 -0
  61. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/test/viz_turtle_odom.xml +0 -0
  62. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim2/launch_turtle.py +0 -0
  63. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/infra-tests/turtlesim2/sample_node.py +0 -0
  64. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/setup.cfg +0 -0
  65. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/__init__.py +0 -0
  66. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/__init__.py +0 -0
  67. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/containers/__init__.py +0 -0
  68. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/containers/test_docker.py +0 -0
  69. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/containers/test_utils.py +0 -0
  70. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_app_containers.py +0 -0
  71. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_cli.py +0 -0
  72. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_config_validation.py +0 -0
  73. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_other.py +0 -0
  74. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_parameters.py +0 -0
  75. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_ros1.py +0 -0
  76. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_ros2.py +0 -0
  77. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_utils.py +0 -0
  78. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/cli/test_utils_ros.py +0 -0
  79. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/fixtures/artefacts.yaml.template +0 -0
  80. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/fixtures/artefacts_deprecated.yaml +0 -0
  81. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/fixtures/bad_launch_test.py +0 -0
  82. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/fixtures/test_junit.xml +0 -0
  83. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/fixtures/warp-env-param.yaml +0 -0
  84. {artefacts_cli-0.9.4 → artefacts_cli-0.9.5}/tests/utils/docker_mock.py +0 -0
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ TODO and upcoming
11
+
12
+ ## [0.9.5] - 2025-06-30
13
+
10
14
  ### Added
11
15
 
12
16
  - API calls are automatically retried up to 3 times on server-side 502, 503, 504 errors (#264)
@@ -116,7 +120,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
116
120
  - Local metrics errors do not block publication of results.
117
121
  - Introduction of Black formatting.
118
122
 
119
- [unreleased]: https://github.com/art-e-fact/artefacts-client/compare/0.9.3...HEAD
123
+ [unreleased]: https://github.com/art-e-fact/artefacts-client/compare/0.9.5...HEAD
124
+ [0.9.5]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.9.5
120
125
  [0.9.3]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.9.3
121
126
  [0.8.0]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.8.0
122
127
  [0.7.0]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.7.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: artefacts_cli
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Author-email: FD <fabian@artefacts.com>, AGC <alejandro@artefacts.com>, TN <tomo@artefacts.com>, EP <eric@artefacts.com>
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/art-e-fact/artefacts-client
@@ -42,6 +42,9 @@ Requires-Dist: python-markdown-math; extra == "dev"
42
42
  Requires-Dist: ruff>=0.9.2; extra == "dev"
43
43
  Requires-Dist: setuptools_scm>=8; extra == "dev"
44
44
  Requires-Dist: twine; extra == "dev"
45
+ Provides-Extra: dev-extended
46
+ Requires-Dist: artefacts_cli[dev]; extra == "dev-extended"
47
+ Requires-Dist: pytest-watch; extra == "dev-extended"
45
48
 
46
49
  # Artefacts CLI
47
50
 
@@ -32,7 +32,15 @@ except PackageNotFoundError:
32
32
  __version__ = "0.0.0"
33
33
 
34
34
 
35
- class AuthenticationError(Exception):
35
+ class ArtefactsAPIError(Exception):
36
+ """
37
+ Tentative base error class for Artefacts API interactions
38
+ """
39
+
40
+ pass
41
+
42
+
43
+ class AuthenticationError(ArtefactsAPIError):
36
44
  """Raised when artefacts authentication failed"""
37
45
 
38
46
  pass
@@ -74,6 +82,7 @@ class WarpJob:
74
82
  if self.job_id is None:
75
83
  # Only create a new job if job_id is not specified
76
84
  data = {
85
+ "project_id": self.project_id,
77
86
  "start": round(self.start),
78
87
  "status": "in progress",
79
88
  "params": json.dumps(self.params),
@@ -100,7 +109,7 @@ class WarpJob:
100
109
  )
101
110
  )
102
111
  logger.warning(response.text)
103
- raise AuthenticationError(str(response.status_code))
112
+ raise ArtefactsAPIError(str(response.status_code))
104
113
  self.job_id = response.json()["job_id"]
105
114
  self.output_path = self.params.get("output_path", f"/tmp/{self.job_id}")
106
115
  os.makedirs(self.output_path, exist_ok=True)
@@ -109,20 +118,21 @@ class WarpJob:
109
118
  def log_tests_result(self, success):
110
119
  self.success = success
111
120
 
112
- def update(self, last_run_success: bool):
121
+ def update(self, last_run_success: bool) -> bool:
113
122
  end = datetime.now(timezone.utc).timestamp()
114
123
  if self.dryrun:
115
- return
124
+ return True
116
125
  # Log metadata
117
126
  data = {
127
+ "project_id": self.project_id,
118
128
  "end": round(end),
119
129
  "duration": round(end - self.start),
120
130
  "success": last_run_success,
121
131
  "status": "finished", # need to be determined based on all runs
122
132
  }
123
- self.api_conf.update("job", self.job_id, data)
133
+ response = self.api_conf.update("job", self.job_id, data)
124
134
 
125
- return
135
+ return response.status_code == 200
126
136
 
127
137
  def new_run(self, scenario):
128
138
  run = WarpRun(self, scenario["name"], scenario, self.n_runs)
@@ -173,7 +183,7 @@ class WarpRun:
173
183
  )
174
184
  )
175
185
  self.logger.warning(response.text)
176
- raise AuthenticationError(str(response.status_code))
186
+ raise ArtefactsAPIError(str(response.status_code))
177
187
  return
178
188
 
179
189
  def log_params(self, params):
@@ -25,7 +25,11 @@ urllib3logger.setLevel(logging.ERROR)
25
25
 
26
26
  class APIConf:
27
27
  def __init__(
28
- self, project_name: str, api_version: str, job_name: Optional[str] = None
28
+ self,
29
+ project_name: str,
30
+ api_version: str,
31
+ job_name: Optional[str] = None,
32
+ session: Optional[Session] = None,
29
33
  ) -> None:
30
34
  config = get_conf_from_file()
31
35
  if project_name in config:
@@ -76,7 +80,7 @@ class APIConf:
76
80
  #
77
81
  # Retry settings
78
82
  #
79
- self.session = Session()
83
+ self.session = session or Session()
80
84
  retries = Retry(
81
85
  total=3,
82
86
  backoff_factor=0.1,
@@ -84,21 +88,23 @@ class APIConf:
84
88
  allowed_methods=Retry.DEFAULT_ALLOWED_METHODS | {"POST"},
85
89
  )
86
90
  # Default connect timeout set to a small value above the default 3s for TCP
87
- # Default read timeout a bit aggressive to ensure snappy experience
91
+ # Default read timeout a typical value. Does not scale when too aggressive
88
92
  # (note: read timeout is between byte sent, not the whole read)
89
- self.request_timeout = (3.03, 7)
93
+ self.request_timeout = (3.03, 27)
90
94
  self.session.mount("https://", HTTPAdapter(max_retries=retries))
91
95
 
92
96
  @contextmanager
93
97
  def _api(self):
94
98
  try:
95
99
  yield self.session
96
- except ConnectionError:
100
+ except ConnectionError as e:
97
101
  raise click.ClickException(
98
102
  localise(
99
- "Unable to interact with the Artefacts API at this time. "
100
- "Our apologies, this looks like us. Please retry "
101
- "later, or perhaps confirm your internet connectivity."
103
+ "Unable to complete the operation: Network error.\n"
104
+ "This may be a problem with an Artefacts server, or your network.\n"
105
+ "Please try again in a moment or confirm your internet connection.\n"
106
+ "If the problem persists, please contact us (info@artefacts.com)!\n"
107
+ f"All we know: {e}"
102
108
  )
103
109
  )
104
110
 
@@ -173,13 +179,16 @@ class APIConf:
173
179
 
174
180
  Note this is temporary helper, as we expect to turn files as
175
181
  first-order resource at the API level, so move to CRUD model, etc.
182
+
183
+ This facility disables all timeouts, as uploads can be very
184
+ long, and we'd better wait.
176
185
  """
177
186
  with self._api() as session:
178
187
  return session.post(
179
188
  url,
180
189
  data=data,
181
190
  files=files,
182
- timeout=self.request_timeout,
191
+ timeout=None,
183
192
  )
184
193
 
185
194
  def direct(self, verb: str) -> Callable:
@@ -6,9 +6,9 @@
6
6
  #, fuzzy
7
7
  msgid ""
8
8
  msgstr ""
9
- "Project-Id-Version: artefacts 0.9.4.dev18\n"
9
+ "Project-Id-Version: artefacts 0.9.5.dev7\n"
10
10
  "Report-Msgid-Bugs-To: dev@artefacts.com\n"
11
- "POT-Creation-Date: 2025-06-18 09:50+0900\n"
11
+ "POT-Creation-Date: 2025-06-24 09:46+0900\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,33 +22,33 @@ msgstr ""
22
22
  msgid "Could not determine package version: {error_message}. Default to 0.0.0"
23
23
  msgstr ""
24
24
 
25
- #: artefacts/cli/__init__.py:97
25
+ #: artefacts/cli/__init__.py:98
26
26
  #, python-brace-format
27
27
  msgid "Error on job creation: {status_code}"
28
28
  msgstr ""
29
29
 
30
- #: artefacts/cli/__init__.py:170
30
+ #: artefacts/cli/__init__.py:172
31
31
  #, python-brace-format
32
32
  msgid "Error on scenario creation: {status_code}"
33
33
  msgstr ""
34
34
 
35
- #: artefacts/cli/__init__.py:279
35
+ #: artefacts/cli/__init__.py:281
36
36
  msgid ""
37
37
  "Files generated by the job are not uploaded to Artefacts, including the "
38
38
  "ones specified in output_dirs"
39
39
  msgstr ""
40
40
 
41
- #: artefacts/cli/__init__.py:291
41
+ #: artefacts/cli/__init__.py:293
42
42
  #, python-brace-format
43
43
  msgid "Uploading {file_name} ({file_size:.2f} MB)"
44
44
  msgstr ""
45
45
 
46
- #: artefacts/cli/__init__.py:302
46
+ #: artefacts/cli/__init__.py:304
47
47
  #, python-brace-format
48
48
  msgid "File too large: {file_name} could not be uploaded"
49
49
  msgstr ""
50
50
 
51
- #: artefacts/cli/__init__.py:310
51
+ #: artefacts/cli/__init__.py:312
52
52
  #, python-brace-format
53
53
  msgid "Error uploading {file_name}: {error_message}, skipping"
54
54
  msgstr ""
@@ -424,21 +424,22 @@ msgstr ""
424
424
  msgid "Package run failed:"
425
425
  msgstr ""
426
426
 
427
- #: artefacts/cli/config.py:43
427
+ #: artefacts/cli/config.py:47
428
428
  #, python-brace-format
429
429
  msgid "No API KEY set. Please run `artefacts config add {project_name}`"
430
430
  msgstr ""
431
431
 
432
- #: artefacts/cli/config.py:62 artefacts/cli/config.py:70
432
+ #: artefacts/cli/config.py:66 artefacts/cli/config.py:74
433
433
  #, python-brace-format
434
434
  msgid "Connecting to {api_url} using {auth_type}"
435
435
  msgstr ""
436
436
 
437
- #: artefacts/cli/config.py:99
437
+ #: artefacts/cli/config.py:103
438
438
  msgid ""
439
- "Unable to interact with the Artefacts API at this time. Our apologies, "
440
- "this looks like us. Please retry later, or perhaps confirm your internet "
441
- "connectivity."
439
+ "Unable to complete the operation: Network error.\n"
440
+ "This may be a problem with an Artefacts server, or your network.\n"
441
+ "Please try again in a moment or confirm your internet connection.\n"
442
+ "If the problem persists, please contact us (info@artefacts.com)!\n"
442
443
  msgstr ""
443
444
 
444
445
  #: artefacts/cli/ros1.py:55
@@ -6,9 +6,9 @@
6
6
  #, fuzzy
7
7
  msgid ""
8
8
  msgstr ""
9
- "Project-Id-Version: artefacts 0.9.4.dev18\n"
9
+ "Project-Id-Version: artefacts 0.9.5.dev7\n"
10
10
  "Report-Msgid-Bugs-To: dev@artefacts.com\n"
11
- "POT-Creation-Date: 2025-06-18 09:50+0900\n"
11
+ "POT-Creation-Date: 2025-06-24 09:46+0900\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,33 +22,33 @@ msgstr ""
22
22
  msgid "Could not determine package version: {error_message}. Default to 0.0.0"
23
23
  msgstr ""
24
24
 
25
- #: artefacts/cli/__init__.py:97
25
+ #: artefacts/cli/__init__.py:98
26
26
  #, python-brace-format
27
27
  msgid "Error on job creation: {status_code}"
28
28
  msgstr ""
29
29
 
30
- #: artefacts/cli/__init__.py:170
30
+ #: artefacts/cli/__init__.py:172
31
31
  #, python-brace-format
32
32
  msgid "Error on scenario creation: {status_code}"
33
33
  msgstr ""
34
34
 
35
- #: artefacts/cli/__init__.py:279
35
+ #: artefacts/cli/__init__.py:281
36
36
  msgid ""
37
37
  "Files generated by the job are not uploaded to Artefacts, including the ones "
38
38
  "specified in output_dirs"
39
39
  msgstr ""
40
40
 
41
- #: artefacts/cli/__init__.py:291
41
+ #: artefacts/cli/__init__.py:293
42
42
  #, python-brace-format
43
43
  msgid "Uploading {file_name} ({file_size:.2f} MB)"
44
44
  msgstr ""
45
45
 
46
- #: artefacts/cli/__init__.py:302
46
+ #: artefacts/cli/__init__.py:304
47
47
  #, python-brace-format
48
48
  msgid "File too large: {file_name} could not be uploaded"
49
49
  msgstr ""
50
50
 
51
- #: artefacts/cli/__init__.py:310
51
+ #: artefacts/cli/__init__.py:312
52
52
  #, python-brace-format
53
53
  msgid "Error uploading {file_name}: {error_message}, skipping"
54
54
  msgstr ""
@@ -421,21 +421,22 @@ msgstr ""
421
421
  msgid "Package run failed:"
422
422
  msgstr ""
423
423
 
424
- #: artefacts/cli/config.py:43
424
+ #: artefacts/cli/config.py:47
425
425
  #, python-brace-format
426
426
  msgid "No API KEY set. Please run `artefacts config add {project_name}`"
427
427
  msgstr ""
428
428
 
429
- #: artefacts/cli/config.py:62 artefacts/cli/config.py:70
429
+ #: artefacts/cli/config.py:66 artefacts/cli/config.py:74
430
430
  #, python-brace-format
431
431
  msgid "Connecting to {api_url} using {auth_type}"
432
432
  msgstr ""
433
433
 
434
- #: artefacts/cli/config.py:99
434
+ #: artefacts/cli/config.py:103
435
435
  msgid ""
436
- "Unable to interact with the Artefacts API at this time. Our apologies, this "
437
- "looks like us. Please retry later, or perhaps confirm your internet "
438
- "connectivity."
436
+ "Unable to complete the operation: Network error.\n"
437
+ "This may be a problem with an Artefacts server, or your network.\n"
438
+ "Please try again in a moment or confirm your internet connection.\n"
439
+ "If the problem persists, please contact us (info@artefacts.com)!\n"
439
440
  msgstr ""
440
441
 
441
442
  #: artefacts/cli/ros1.py:55
@@ -6,9 +6,9 @@
6
6
  #, fuzzy
7
7
  msgid ""
8
8
  msgstr ""
9
- "Project-Id-Version: artefacts 0.9.4.dev18\n"
9
+ "Project-Id-Version: artefacts 0.9.5.dev7\n"
10
10
  "Report-Msgid-Bugs-To: dev@artefacts.com\n"
11
- "POT-Creation-Date: 2025-06-18 09:50+0900\n"
11
+ "POT-Creation-Date: 2025-06-24 09:46+0900\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -7,7 +7,7 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: artefacts VERSION\n"
9
9
  "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
10
- "POT-Creation-Date: 2025-06-18 09:50+0900\n"
10
+ "POT-Creation-Date: 2025-06-24 09:46+0900\n"
11
11
  "PO-Revision-Date: 2025-04-15 15:16+0900\n"
12
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
13
  "Language: ja_JP\n"
@@ -23,33 +23,33 @@ msgstr ""
23
23
  msgid "Could not determine package version: {error_message}. Default to 0.0.0"
24
24
  msgstr "パッケージバージョン認識エラー:{error_message}. 0.0.0に設定して実行します"
25
25
 
26
- #: artefacts/cli/__init__.py:97
26
+ #: artefacts/cli/__init__.py:98
27
27
  #, python-brace-format
28
28
  msgid "Error on job creation: {status_code}"
29
29
  msgstr "ジョブ作成エラー:{status_code}"
30
30
 
31
- #: artefacts/cli/__init__.py:170
31
+ #: artefacts/cli/__init__.py:172
32
32
  #, python-brace-format
33
33
  msgid "Error on scenario creation: {status_code}"
34
34
  msgstr "シナリオ作成エラー:{status_code}"
35
35
 
36
- #: artefacts/cli/__init__.py:279
36
+ #: artefacts/cli/__init__.py:281
37
37
  msgid ""
38
38
  "Files generated by the job are not uploaded to Artefacts, including the "
39
39
  "ones specified in output_dirs"
40
40
  msgstr "ジョブによって生成されたファイルは、output_dirsで指定されたものも含め、Artefactsにアップロードされません"
41
41
 
42
- #: artefacts/cli/__init__.py:291
42
+ #: artefacts/cli/__init__.py:293
43
43
  #, python-brace-format
44
44
  msgid "Uploading {file_name} ({file_size:.2f} MB)"
45
45
  msgstr "{file_name}をアップロード中({file_size:.2f} MB)"
46
46
 
47
- #: artefacts/cli/__init__.py:302
47
+ #: artefacts/cli/__init__.py:304
48
48
  #, python-brace-format
49
49
  msgid "File too large: {file_name} could not be uploaded"
50
50
  msgstr "ファイルが大きすぎます:{file_name}をアップロードできませんでした"
51
51
 
52
- #: artefacts/cli/__init__.py:310
52
+ #: artefacts/cli/__init__.py:312
53
53
  #, python-brace-format
54
54
  msgid "Error uploading {file_name}: {error_message}, skipping"
55
55
  msgstr "{file_name}のアップロードエラー:{error_message}、スキップします"
@@ -442,24 +442,27 @@ msgstr "コンテナ実行完了:検査用のコンテナID:{container_id}"
442
442
  msgid "Package run failed:"
443
443
  msgstr "パッケージ実行失敗:"
444
444
 
445
- #: artefacts/cli/config.py:43
445
+ #: artefacts/cli/config.py:47
446
446
  #, python-brace-format
447
447
  msgid "No API KEY set. Please run `artefacts config add {project_name}`"
448
448
  msgstr "APIキーが設定されていません。`artefacts config add {project_name}`を実行してください"
449
449
 
450
- #: artefacts/cli/config.py:62 artefacts/cli/config.py:70
450
+ #: artefacts/cli/config.py:66 artefacts/cli/config.py:74
451
451
  #, python-brace-format
452
452
  msgid "Connecting to {api_url} using {auth_type}"
453
453
  msgstr "{auth_type}を使用して{api_url}に接続中"
454
454
 
455
- #: artefacts/cli/config.py:99
455
+ #: artefacts/cli/config.py:103
456
456
  msgid ""
457
- "Unable to interact with the Artefacts API at this time. Our apologies, "
458
- "this looks like us. Please retry later, or perhaps confirm your internet "
459
- "connectivity."
457
+ "Unable to complete the operation: Network error.\n"
458
+ "This may be a problem with an Artefacts server, or your network.\n"
459
+ "Please try again in a moment or confirm your internet connection.\n"
460
+ "If the problem persists, please contact us (info@artefacts.com)!\n"
460
461
  msgstr ""
461
- "現在、Artefacts APIとの通信ができません。申し訳ございませんが、"
462
- "こちら側の問題のようです。後ほど再試行していただくか、インターネット接続をご確認ください。"
462
+ "操作を完了できません:ネットワークエラー。\n"
463
+ "これはArtefactsサーバーまたはお客様のネットワークの問題である可能性があります。\n"
464
+ "しばらくしてから再度お試しいただくか、インターネット接続をご確認ください。\n"
465
+ "問題が解決しない場合は、お気軽にお問い合わせください(info@artefacts.com)!\n"
463
466
 
464
467
  #: artefacts/cli/ros1.py:55
465
468
  msgid ""
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.9.4'
21
- __version_tuple__ = version_tuple = (0, 9, 4)
20
+ __version__ = version = '0.9.5'
21
+ __version_tuple__ = version_tuple = (0, 9, 5)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: artefacts_cli
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Author-email: FD <fabian@artefacts.com>, AGC <alejandro@artefacts.com>, TN <tomo@artefacts.com>, EP <eric@artefacts.com>
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/art-e-fact/artefacts-client
@@ -42,6 +42,9 @@ Requires-Dist: python-markdown-math; extra == "dev"
42
42
  Requires-Dist: ruff>=0.9.2; extra == "dev"
43
43
  Requires-Dist: setuptools_scm>=8; extra == "dev"
44
44
  Requires-Dist: twine; extra == "dev"
45
+ Provides-Extra: dev-extended
46
+ Requires-Dist: artefacts_cli[dev]; extra == "dev-extended"
47
+ Requires-Dist: pytest-watch; extra == "dev-extended"
45
48
 
46
49
  # Artefacts CLI
47
50
 
@@ -56,6 +56,7 @@ infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_trajectory.p
56
56
  infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/test/viz_turtle_odom.xml
57
57
  infra-tests/turtlesim2/launch_turtle.py
58
58
  infra-tests/turtlesim2/sample_node.py
59
+ scripts/whats_latest.rb
59
60
  tests/__init__.py
60
61
  tests/conftest.py
61
62
  tests/cli/__init__.py
@@ -31,3 +31,7 @@ python-markdown-math
31
31
  ruff>=0.9.2
32
32
  setuptools_scm>=8
33
33
  twine
34
+
35
+ [dev-extended]
36
+ artefacts_cli[dev]
37
+ pytest-watch
@@ -3,6 +3,7 @@
3
3
  set -e
4
4
 
5
5
  production=${1:-no}
6
+ version_override=${2:-no}
6
7
 
7
8
  if [[ -d venv ]]
8
9
  then
@@ -10,6 +11,10 @@ then
10
11
  fi
11
12
 
12
13
  # Get the new version
14
+ if [[ "$version_override" != "no" ]]
15
+ then
16
+ export SETUPTOOLS_SCM_PRETEND_VERSION=$version_override
17
+ fi
13
18
  new_version=$(python -m setuptools_scm)
14
19
 
15
20
  if [[ ! "$new_version" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]+\.?\d?\e?\v?[0-9]?$ ]] && [[ "$production" != "production" ]]
@@ -68,6 +68,11 @@ dev = [
68
68
  "twine",
69
69
  ]
70
70
 
71
+ dev-extended = [
72
+ "artefacts_cli[dev]",
73
+ "pytest-watch",
74
+ ]
75
+
71
76
  [project.urls]
72
77
  "Homepage" = "https://github.com/art-e-fact/artefacts-client"
73
78
  "Bug Tracker" = "https://github.com/art-e-fact/artefacts-client/issues"
@@ -5,3 +5,7 @@ env =
5
5
  ARTEFACTS_CLI_ENV=test
6
6
  markers =
7
7
  ros2: mark a test as it requires ROS2 environment.
8
+
9
+ [pytest-watch]
10
+ clear = True
11
+ nobeep = True
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Parse our CHANGELOG.md file to get the latest release "block"
5
+ # and prints it to stdout.
6
+ #
7
+
8
+ cl = open("CHANGELOG.md").read
9
+ start = cl.index /## \[Unreleased\]/
10
+ finish = cl.index /## \[\d\.\d+\.\d+\] - \d{4}-\d{2}-\d{2}/
11
+ print cl[start...finish].chomp
@@ -1,9 +1,16 @@
1
1
  import pytest
2
2
  from datetime import datetime, timezone
3
+ from uuid import uuid4
3
4
 
4
5
 
5
- from artefacts.cli import generate_scenarios, WarpJob
6
+ from artefacts.cli import (
7
+ ArtefactsAPIError,
8
+ AuthenticationError,
9
+ generate_scenarios,
10
+ WarpJob,
11
+ )
6
12
  from artefacts.cli.app import run
13
+ from artefacts.cli.config import APIConf
7
14
 
8
15
 
9
16
  def test_generate_scenarios(loaded_valid_conf):
@@ -119,15 +126,142 @@ def test_default_params_overwritten_by_launch_argument(loaded_valid_conf):
119
126
  }
120
127
 
121
128
 
122
- def test_job_empty_init():
129
+ def test_job_empty_init_dryrun():
123
130
  """
124
- Plain basic test the WarpJob object initialises as expected.
131
+ Plain basic test the WarpJob object initialises as expected on dryrun.
125
132
  """
126
133
  job = WarpJob("project", {}, "jobname", {}, dryrun=True)
127
134
  assert job.success is False
128
135
  assert job.start < datetime.now(timezone.utc).timestamp()
129
136
 
130
137
 
138
+ def test_job_empty_init(mocker, valid_project_settings):
139
+ """
140
+ Plain basic test the WarpJob object initialises as expected.
141
+ """
142
+ success = mocker.Mock()
143
+ success.status_code = 200
144
+ success.json.return_value = {"job_id": str(uuid4())}
145
+ test_session = mocker.Mock()
146
+ test_session.post.return_value = success
147
+ job = WarpJob(
148
+ "project",
149
+ APIConf(
150
+ valid_project_settings["full_project_name"],
151
+ "test_version",
152
+ session=test_session,
153
+ ),
154
+ "jobname",
155
+ {},
156
+ )
157
+ assert job.success is False
158
+ assert job.start < datetime.now(timezone.utc).timestamp()
159
+
160
+
161
+ def test_job_update(mocker, valid_project_settings):
162
+ """
163
+ Test on the update function of WarpJob objects.
164
+ """
165
+ success = mocker.Mock()
166
+ success.status_code = 200
167
+ success.json.return_value = {"job_id": str(uuid4())}
168
+ test_session = mocker.Mock()
169
+ test_session.post.return_value = success
170
+ test_session.put.return_value = success
171
+ job = WarpJob(
172
+ "project",
173
+ APIConf(
174
+ valid_project_settings["full_project_name"],
175
+ "test_version",
176
+ session=test_session,
177
+ ),
178
+ "jobname",
179
+ {},
180
+ )
181
+ assert job.update(True)
182
+
183
+
184
+ def test_job_valid_context(mocker, valid_project_settings):
185
+ """
186
+ Check that a custom context param is processed as expected.
187
+
188
+ A job creation currently invokes the API at init time,
189
+ which is supposed to send any optional `context` data.
190
+ """
191
+ now = datetime.now()
192
+ time_mock = mocker.patch("datetime.datetime")
193
+ time_mock.now.return_value = now
194
+ success = mocker.Mock()
195
+ success.status_code = 200
196
+ success.json.return_value = {"job_id": str(uuid4())}
197
+ api_conf = mocker.Mock()
198
+ api_conf.create = mocker.Mock(return_value=success)
199
+ WarpJob(
200
+ "project",
201
+ api_conf,
202
+ "jobname",
203
+ {},
204
+ context={
205
+ "description": "test",
206
+ "commit": "testcommithash",
207
+ "ref": "testref",
208
+ },
209
+ )
210
+ api_conf.create.assert_called_with(
211
+ "job",
212
+ dict(
213
+ project_id="project",
214
+ start=round(now.timestamp()),
215
+ status="in progress",
216
+ params="{}",
217
+ project="project",
218
+ jobname="jobname",
219
+ timeout=300,
220
+ n_subjobs=1,
221
+ message="test",
222
+ commit="testcommithash",
223
+ ref="testref",
224
+ ),
225
+ )
226
+
227
+
228
+ def test_job_creation_403(mocker):
229
+ """
230
+ Check bad authentication is handled.
231
+ """
232
+ error403 = mocker.Mock()
233
+ error403.status_code = 403
234
+ error403.json.return_value = {"message": "bad auth"}
235
+ api_conf = mocker.Mock()
236
+ api_conf.create = mocker.Mock(return_value=error403)
237
+ with pytest.raises(AuthenticationError) as error:
238
+ WarpJob(
239
+ "project",
240
+ api_conf,
241
+ "jobname",
242
+ {},
243
+ )
244
+ assert "bad auth" in str(error)
245
+
246
+
247
+ def test_job_creation_non_403_error(mocker):
248
+ """
249
+ Check non-403 errors are handled.
250
+ """
251
+ error401 = mocker.Mock()
252
+ error401.status_code = 401
253
+ api_conf = mocker.Mock()
254
+ api_conf.create = mocker.Mock(return_value=error401)
255
+ with pytest.raises(ArtefactsAPIError) as error:
256
+ WarpJob(
257
+ "project",
258
+ api_conf,
259
+ "jobname",
260
+ {},
261
+ )
262
+ assert "401" in str(error)
263
+
264
+
131
265
  @pytest.mark.ros2
132
266
  def test_job_update_called_after_every_run(
133
267
  cli_runner, authorised_project_with_conf, mocker
@@ -1,8 +1,10 @@
1
+ import pytest
2
+
1
3
  import json
2
4
 
3
5
  from requests import Response
4
6
 
5
- from artefacts.cli import WarpRun
7
+ from artefacts.cli import ArtefactsAPIError, AuthenticationError, WarpJob, WarpRun
6
8
 
7
9
 
8
10
  def test_run_with_upload_on_stop(mocker, artefacts_run):
@@ -137,3 +139,62 @@ def test_run_uses_scenario_name_on_stop_for_all_parameterised_runs(
137
139
  run.stop()
138
140
  assert "scenario_name" in spy.call_args.args[2]
139
141
  assert expected_name == spy.call_args.args[2]["scenario_name"]
142
+
143
+
144
+ def test_run_creation_403(mocker):
145
+ """
146
+ Check bad authentication is handled.
147
+ """
148
+ success = mocker.Mock()
149
+ success.status_code = 200
150
+ success.json.return_value = {"job_id": "test"}
151
+ error403 = mocker.Mock()
152
+ error403.status_code = 403
153
+ error403.json.return_value = {"message": "bad auth"}
154
+
155
+ def behaviour(obj, data):
156
+ if "job" == obj:
157
+ return success
158
+ elif "run" == obj:
159
+ return error403
160
+
161
+ api_conf = mocker.Mock()
162
+ api_conf.create = mocker.Mock(side_effect=behaviour)
163
+ job = WarpJob(
164
+ "project",
165
+ api_conf,
166
+ "jobname",
167
+ {},
168
+ )
169
+ with pytest.raises(AuthenticationError) as error:
170
+ WarpRun(job=job, name="run", params={}, run_n=1)
171
+ assert "bad auth" in str(error)
172
+
173
+
174
+ def test_run_creation_non_403_error(mocker):
175
+ """
176
+ Check non-403 errors are handled.
177
+ """
178
+ success = mocker.Mock()
179
+ success.status_code = 200
180
+ success.json.return_value = {"job_id": "test"}
181
+ error401 = mocker.Mock()
182
+ error401.status_code = 401
183
+
184
+ def behaviour(obj, data):
185
+ if "job" == obj:
186
+ return success
187
+ elif "run" == obj:
188
+ return error401
189
+
190
+ api_conf = mocker.Mock()
191
+ api_conf.create = mocker.Mock(side_effect=behaviour)
192
+ job = WarpJob(
193
+ "project",
194
+ api_conf,
195
+ "jobname",
196
+ {},
197
+ )
198
+ with pytest.raises(ArtefactsAPIError) as error:
199
+ WarpRun(job=job, name="run", params={}, run_n=1)
200
+ assert "401" in str(error)
@@ -154,10 +154,19 @@ def sample_artefacts_config():
154
154
 
155
155
 
156
156
  @pytest.fixture(scope="function")
157
- def artefacts_job(valid_project_settings):
157
+ def test_session(mocker):
158
+ return mocker.Mock()
159
+
160
+
161
+ @pytest.fixture(scope="function")
162
+ def artefacts_job(valid_project_settings, test_session):
158
163
  return WarpJob(
159
164
  valid_project_settings["full_project_name"],
160
- APIConf(valid_project_settings["full_project_name"], "test_version"),
165
+ APIConf(
166
+ valid_project_settings["full_project_name"],
167
+ "test_version",
168
+ session=test_session,
169
+ ),
161
170
  "jobname",
162
171
  {},
163
172
  dryrun=True,
File without changes
File without changes