agentx-python 0.8.7__tar.gz → 0.8.8__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 (91) hide show
  1. {agentx_python-0.8.7 → agentx_python-0.8.8}/PKG-INFO +1 -1
  2. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/cli.py +85 -11
  3. agentx_python-0.8.8/agentx/version.py +1 -0
  4. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/PKG-INFO +1 -1
  5. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/SOURCES.txt +1 -0
  6. agentx_python-0.8.8/tests/test_cli_launcher.py +89 -0
  7. agentx_python-0.8.7/agentx/version.py +0 -1
  8. {agentx_python-0.8.7 → agentx_python-0.8.8}/LICENSE +0 -0
  9. {agentx_python-0.8.7 → agentx_python-0.8.8}/README.md +0 -0
  10. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/__init__.py +0 -0
  11. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/agentx.py +0 -0
  12. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/__init__.py +0 -0
  13. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/_term.py +0 -0
  14. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/adapters/__init__.py +0 -0
  15. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  16. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/adapters/precomputed.py +0 -0
  17. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/adapters/raw.py +0 -0
  18. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/client.py +0 -0
  19. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/datasets.py +0 -0
  20. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/evaluation_settings.py +0 -0
  21. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/models.py +0 -0
  22. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/prompts.py +0 -0
  23. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/reporting.py +0 -0
  24. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/results.py +0 -0
  25. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/runner.py +0 -0
  26. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/tool_schemas.py +0 -0
  27. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/evaluations/tracing.py +0 -0
  28. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/exceptions.py +0 -0
  29. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/export.py +0 -0
  30. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/feedback.py +0 -0
  31. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/__init__.py +0 -0
  32. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/_traced_call.py +0 -0
  33. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/anthropic.py +0 -0
  34. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/autogen.py +0 -0
  35. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/crewai.py +0 -0
  36. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/databricks.py +0 -0
  37. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/google_adk.py +0 -0
  38. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/google_genai.py +0 -0
  39. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/langchain.py +0 -0
  40. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/litellm.py +0 -0
  41. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/llamaindex.py +0 -0
  42. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/moveworks.py +0 -0
  43. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/openai.py +0 -0
  44. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/integrations/openai_agents.py +0 -0
  45. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/__init__.py +0 -0
  46. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/agents.py +0 -0
  47. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/client.py +0 -0
  48. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/judge_scorers.py +0 -0
  49. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/models.py +0 -0
  50. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/online_evaluators.py +0 -0
  51. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/patterns.py +0 -0
  52. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/profile.py +0 -0
  53. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/review_queue.py +0 -0
  54. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/rules.py +0 -0
  55. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/scorers.py +0 -0
  56. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/sessions.py +0 -0
  57. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/monitor/signals.py +0 -0
  58. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/outcomes.py +0 -0
  59. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/projects.py +0 -0
  60. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/py.typed +0 -0
  61. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/resources/__init__.py +0 -0
  62. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/resources/agent.py +0 -0
  63. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/resources/conversation.py +0 -0
  64. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/resources/workforce.py +0 -0
  65. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/testing.py +0 -0
  66. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/traces.py +0 -0
  67. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/tracing/__init__.py +0 -0
  68. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/tracing/ci_types.py +0 -0
  69. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/tracing/eval_scope.py +0 -0
  70. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/tracing/ingest_client.py +0 -0
  71. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/tracing/tracer.py +0 -0
  72. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx/util.py +0 -0
  73. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/dependency_links.txt +0 -0
  74. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/entry_points.txt +0 -0
  75. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/not-zip-safe +0 -0
  76. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/requires.txt +0 -0
  77. {agentx_python-0.8.7 → agentx_python-0.8.8}/agentx_python.egg-info/top_level.txt +0 -0
  78. {agentx_python-0.8.7 → agentx_python-0.8.8}/setup.cfg +0 -0
  79. {agentx_python-0.8.7 → agentx_python-0.8.8}/setup.py +0 -0
  80. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_deep_dive_fixes.py +0 -0
  81. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_docs_match_sdk.py +0 -0
  82. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_eval_scope.py +0 -0
  83. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_integration.py +0 -0
  84. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_integrations.py +0 -0
  85. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_judge_scorers.py +0 -0
  86. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_pairwise.py +0 -0
  87. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_review_queue.py +0 -0
  88. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_runner_features.py +0 -0
  89. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_selfhost_analysis_fallback.py +0 -0
  90. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_span_tree.py +0 -0
  91. {agentx_python-0.8.7 → agentx_python-0.8.8}/tests/test_testing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.7
3
+ Version: 0.8.8
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -7,26 +7,35 @@ AgentX SaaS and would never touch it, so it isn't bundled in this package. Inste
7
7
  downloads the matching release into ~/.agentx/bin the first time it's needed (mirroring
8
8
  AgentX-trace-eval's own install.sh) and then hands off to the real `agentx-server` binary.
9
9
 
10
+ The installed release tag is stamped in ~/.agentx/bin/.version. The launcher never silently
11
+ re-downloads, but it re-installs when you ask: `--update` fetches the newest release (engine +
12
+ dashboard), and setting AGENTX_TRACE_EVAL_VERSION to a tag other than the stamped one switches
13
+ to that release. When running "latest", a quick fail-open check against GitHub tells you if a
14
+ newer release exists.
15
+
10
16
  Usage:
11
17
  agentx-trace-eval --dev
18
+ agentx-trace-eval --update --dev
12
19
  agentx-trace-eval --port 5000 --db-url postgres://...
13
20
  """
14
21
 
15
22
  import os
16
23
  import platform
24
+ import re
17
25
  import shutil
18
26
  import stat
19
27
  import sys
20
28
  import tarfile
21
29
  import tempfile
22
30
  from pathlib import Path
23
- from typing import Tuple
31
+ from typing import Optional, Tuple
24
32
 
25
33
  import requests
26
34
 
27
35
  REPO = "AgentX-ai/AgentX-trace-eval"
28
36
  INSTALL_DIR = Path(os.environ.get("AGENTX_INSTALL_DIR", str(Path.home() / ".agentx" / "bin")))
29
37
  _BIN_NAMES = ("agentx", "agentx-server", "agentx-engine")
38
+ _VERSION_STAMP = ".version"
30
39
 
31
40
 
32
41
  def _platform_tag() -> Tuple[str, str]:
@@ -57,12 +66,22 @@ def _release_url(asset: str, version: str) -> str:
57
66
  return f"https://github.com/{REPO}/releases/download/{version}/{asset}"
58
67
 
59
68
 
60
- def _download(url: str, dest: Path) -> None:
69
+ def _tag_from_release_url(url: str) -> Optional[str]:
70
+ # GitHub redirects releases/latest/download/... to releases/download/<tag>/...; the final
71
+ # URL is the one place the resolved tag shows up without a second API call.
72
+ match = re.search(r"/releases/download/([^/]+)/", url)
73
+ return match.group(1) if match else None
74
+
75
+
76
+ def _download(url: str, dest: Path) -> Optional[str]:
77
+ """Downloads url to dest; returns the release tag resolved from the final (post-redirect)
78
+ URL, or None if it can't be determined."""
61
79
  response = requests.get(url, stream=True, timeout=60)
62
80
  response.raise_for_status()
63
81
  with open(dest, "wb") as f:
64
82
  for chunk in response.iter_content(chunk_size=1 << 16):
65
83
  f.write(chunk)
84
+ return _tag_from_release_url(response.url)
66
85
 
67
86
 
68
87
  def _extract_tar(archive: Path, dest_dir: Path) -> None:
@@ -76,6 +95,25 @@ def _extract_tar(archive: Path, dest_dir: Path) -> None:
76
95
  tar.extractall(dest_dir) # Python < 3.12: filter kwarg doesn't exist yet
77
96
 
78
97
 
98
+ def _stamped_version() -> Optional[str]:
99
+ try:
100
+ return (INSTALL_DIR / _VERSION_STAMP).read_text().strip() or None
101
+ except OSError:
102
+ return None
103
+
104
+
105
+ def _latest_release_tag() -> Optional[str]:
106
+ """The newest published tag, or None when it can't be determined (offline, rate-limited).
107
+ Fail-open by design: an update NOTICE must never break launching the engine."""
108
+ try:
109
+ response = requests.get(f"https://api.github.com/repos/{REPO}/releases/latest", timeout=3)
110
+ response.raise_for_status()
111
+ tag = response.json().get("tag_name")
112
+ return tag if isinstance(tag, str) and tag else None
113
+ except Exception:
114
+ return None
115
+
116
+
79
117
  def _install(version: str = "latest") -> None:
80
118
  os_name, arch = _platform_tag()
81
119
  INSTALL_DIR.mkdir(parents=True, exist_ok=True)
@@ -86,7 +124,7 @@ def _install(version: str = "latest") -> None:
86
124
  archive = tmp_dir / "agentx.tar.gz"
87
125
  url = _release_url(f"agentx_{os_name}_{arch}.tar.gz", version)
88
126
  try:
89
- _download(url, archive)
127
+ resolved_tag = _download(url, archive)
90
128
  except requests.HTTPError as exc:
91
129
  raise SystemExit(
92
130
  f"agentx-trace-eval: failed to download {url} ({exc}).\n"
@@ -108,6 +146,11 @@ def _install(version: str = "latest") -> None:
108
146
  if not found_any:
109
147
  raise SystemExit(f"agentx-trace-eval: downloaded archive from {url} didn't contain any of {_BIN_NAMES}")
110
148
 
149
+ stamp = resolved_tag or (version if version != "latest" else None)
150
+ if stamp:
151
+ (INSTALL_DIR / _VERSION_STAMP).write_text(stamp + "\n")
152
+ print(f"agentx-trace-eval: installed {stamp}", file=sys.stderr)
153
+
111
154
  if os.environ.get("AGENTX_TRACE_EVAL_SKIP_WEB"):
112
155
  return
113
156
 
@@ -118,7 +161,9 @@ def _install(version: str = "latest") -> None:
118
161
  web_dir = INSTALL_DIR / "web"
119
162
  with tempfile.TemporaryDirectory() as tmp:
120
163
  web_archive = Path(tmp) / "agentx-web.tar.gz"
121
- web_url = _release_url("agentx-web.tar.gz", version)
164
+ # Pin the dashboard to the tag the engine actually resolved to, so the two halves of one
165
+ # install can't skew (releases/latest assets are updated independently).
166
+ web_url = _release_url("agentx-web.tar.gz", stamp or version)
122
167
  try:
123
168
  _download(web_url, web_archive)
124
169
  except requests.HTTPError:
@@ -133,25 +178,54 @@ def _install(version: str = "latest") -> None:
133
178
  _extract_tar(web_archive, web_dir)
134
179
 
135
180
 
136
- def ensure_installed(version: str = "latest") -> Path:
137
- """Downloads agentx-server (+ its engine) into ~/.agentx/bin if not already present there.
138
- Returns the path to the agentx-server executable. Set AGENTX_INSTALL_DIR to change where
139
- this looks/installs; set AGENTX_TRACE_EVAL_VERSION to pin a release tag instead of latest."""
181
+ def ensure_installed(version: str = "latest", force: bool = False) -> Path:
182
+ """Downloads agentx-server (+ its engine and dashboard) into ~/.agentx/bin when it's missing
183
+ there, when `force` is set, or when `version` is a specific tag that differs from the
184
+ installed one. Returns the path to the agentx-server executable. Set AGENTX_INSTALL_DIR to
185
+ change where this looks/installs; set AGENTX_TRACE_EVAL_VERSION to pin a release tag."""
140
186
  server_path = INSTALL_DIR / "agentx-server"
141
- if not server_path.exists():
187
+ installed = _stamped_version()
188
+ pin_changed = version != "latest" and installed is not None and installed != version
189
+ if force or pin_changed or not server_path.exists():
142
190
  _install(version=version)
143
191
  return server_path
144
192
 
145
193
 
194
+ def _maybe_print_update_notice(installed: Optional[str]) -> None:
195
+ latest = _latest_release_tag()
196
+ if installed is None:
197
+ # Pre-stamp install (or a wiped stamp): age unknown, so always point at --update.
198
+ print(
199
+ "agentx-trace-eval: installed engine version unknown"
200
+ + (f" (newest release is {latest})" if latest else "")
201
+ + "; run with --update to fetch the newest release",
202
+ file=sys.stderr,
203
+ )
204
+ elif latest and latest != installed:
205
+ print(
206
+ f"agentx-trace-eval: engine {installed} is installed but {latest} is available; "
207
+ "run with --update to upgrade",
208
+ file=sys.stderr,
209
+ )
210
+
211
+
146
212
  def main() -> None:
213
+ args = sys.argv[1:]
214
+ force_update = "--update" in args
215
+ # --update belongs to this launcher, not to agentx-server - consume it before the handoff.
216
+ args = [a for a in args if a != "--update"]
217
+
147
218
  version = os.environ.get("AGENTX_TRACE_EVAL_VERSION", "latest")
148
- server_path = ensure_installed(version=version)
219
+ server_path = ensure_installed(version=version, force=force_update)
149
220
  if not server_path.exists():
150
221
  raise SystemExit(f"agentx-trace-eval: {server_path} still missing after install, giving up")
151
222
 
223
+ if not force_update and version == "latest":
224
+ _maybe_print_update_notice(_stamped_version())
225
+
152
226
  # os.execv replaces this process rather than spawning a subprocess: signals, stdio, and the
153
227
  # exit code all pass straight through to agentx-server, same as invoking it directly.
154
- os.execv(str(server_path), [str(server_path)] + sys.argv[1:])
228
+ os.execv(str(server_path), [str(server_path)] + args)
155
229
 
156
230
 
157
231
  if __name__ == "__main__":
@@ -0,0 +1 @@
1
+ VERSION = "0.8.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.7
3
+ Version: 0.8.8
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -73,6 +73,7 @@ agentx_python.egg-info/entry_points.txt
73
73
  agentx_python.egg-info/not-zip-safe
74
74
  agentx_python.egg-info/requires.txt
75
75
  agentx_python.egg-info/top_level.txt
76
+ tests/test_cli_launcher.py
76
77
  tests/test_deep_dive_fixes.py
77
78
  tests/test_docs_match_sdk.py
78
79
  tests/test_eval_scope.py
@@ -0,0 +1,89 @@
1
+ """The agentx-trace-eval launcher's staleness fixes: before these, ensure_installed downloaded
2
+ once and then ran that binary forever - a July install silently served a July engine months
3
+ later, with no version stamp, no way to ask for an update, and the dashboard fetched from
4
+ releases/latest independently of the engine (so the two halves of one install could skew)."""
5
+
6
+ import io
7
+ import sys
8
+
9
+ import pytest
10
+
11
+ from agentx import cli
12
+
13
+
14
+ @pytest.fixture
15
+ def install_dir(tmp_path, monkeypatch):
16
+ monkeypatch.setattr(cli, "INSTALL_DIR", tmp_path)
17
+ return tmp_path
18
+
19
+
20
+ def _fake_install(calls):
21
+ def fake(version="latest"):
22
+ calls.append(version)
23
+
24
+ return fake
25
+
26
+
27
+ def test_tag_resolved_from_redirected_release_url():
28
+ url = "https://github.com/AgentX-ai/AgentX-trace-eval/releases/download/v0.3.0/agentx_darwin_arm64.tar.gz"
29
+ assert cli._tag_from_release_url(url) == "v0.3.0"
30
+ assert cli._tag_from_release_url("https://example.com/not-a-release") is None
31
+
32
+
33
+ def test_existing_install_is_not_redownloaded(install_dir, monkeypatch):
34
+ (install_dir / "agentx-server").write_text("bin")
35
+ calls = []
36
+ monkeypatch.setattr(cli, "_install", _fake_install(calls))
37
+ cli.ensure_installed(version="latest")
38
+ assert calls == []
39
+
40
+
41
+ def test_force_reinstalls_over_existing(install_dir, monkeypatch):
42
+ (install_dir / "agentx-server").write_text("bin")
43
+ calls = []
44
+ monkeypatch.setattr(cli, "_install", _fake_install(calls))
45
+ cli.ensure_installed(version="latest", force=True)
46
+ assert calls == ["latest"]
47
+
48
+
49
+ def test_changed_version_pin_reinstalls(install_dir, monkeypatch):
50
+ (install_dir / "agentx-server").write_text("bin")
51
+ (install_dir / ".version").write_text("v0.2.0\n")
52
+ calls = []
53
+ monkeypatch.setattr(cli, "_install", _fake_install(calls))
54
+
55
+ # Same pin: no download. Different pin: download. "latest": trusts what's there.
56
+ cli.ensure_installed(version="v0.2.0")
57
+ assert calls == []
58
+ cli.ensure_installed(version="v0.3.0")
59
+ assert calls == ["v0.3.0"]
60
+ calls.clear()
61
+ cli.ensure_installed(version="latest")
62
+ assert calls == []
63
+
64
+
65
+ def test_update_notice_names_both_versions(install_dir, monkeypatch, capsys):
66
+ monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.9.0")
67
+ cli._maybe_print_update_notice("v0.2.0")
68
+ err = capsys.readouterr().err
69
+ assert "v0.2.0" in err and "v0.9.0" in err and "--update" in err
70
+
71
+
72
+ def test_update_notice_handles_unstamped_install(install_dir, monkeypatch, capsys):
73
+ monkeypatch.setattr(cli, "_latest_release_tag", lambda: None)
74
+ cli._maybe_print_update_notice(None)
75
+ assert "--update" in capsys.readouterr().err
76
+
77
+
78
+ def test_update_notice_is_silent_when_current(install_dir, monkeypatch, capsys):
79
+ monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.2.0")
80
+ cli._maybe_print_update_notice("v0.2.0")
81
+ assert capsys.readouterr().err == ""
82
+
83
+
84
+ def test_latest_release_tag_fails_open(monkeypatch):
85
+ def boom(*args, **kwargs):
86
+ raise OSError("offline")
87
+
88
+ monkeypatch.setattr(cli.requests, "get", boom)
89
+ assert cli._latest_release_tag() is None
@@ -1 +0,0 @@
1
- VERSION = "0.8.7"
File without changes
File without changes
File without changes
File without changes