chartremotely 0.3.9__tar.gz → 0.3.10__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.
- {chartremotely-0.3.9 → chartremotely-0.3.10}/PKG-INFO +2 -2
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/doctor.py +36 -9
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/PKG-INFO +2 -2
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/requires.txt +1 -1
- {chartremotely-0.3.9 → chartremotely-0.3.10}/pyproject.toml +2 -2
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_doctor.py +41 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/LICENSE +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/README.md +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/__init__.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/answer.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/assets/shortcut.plist +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/ax.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/cli.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/config.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/displays.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/guilock.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/hearing.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/holder.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/keystore.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/layout.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/mcpclient.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/patron.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/popups.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/push.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/recent.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/registry.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/relay.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/requestlog.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/resolve.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/scales.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/screen.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/server.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/services.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/setup.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/shortcut.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/snapshot.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/studies.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/symbol.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/tailnet.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/terminal.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/timeframe.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/understand.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/vocab.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/voice.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely/window.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/SOURCES.txt +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/dependency_links.txt +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/entry_points.txt +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/chartremotely.egg-info/top_level.txt +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/setup.cfg +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_changelog.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_hearing.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_patron.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_popups.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_push.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_recent.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_registry.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_relay.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_requestlog.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_resolve.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_scales.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_screen.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_setup.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_snapshot.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_stamp_changelog.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_timeframe.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_understand.py +0 -0
- {chartremotely-0.3.9 → chartremotely-0.3.10}/tests/test_voice.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chartremotely
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: Remote control of desktop charting applications on macOS
|
|
5
5
|
Author: Lonnie VanZandt
|
|
6
6
|
License:
|
|
@@ -219,7 +219,7 @@ Requires-Dist: pyobjc-framework-Quartz>=10; extra == "macos"
|
|
|
219
219
|
Requires-Dist: pyobjc-framework-Security>=10; extra == "macos"
|
|
220
220
|
Requires-Dist: mlx-whisper>=0.4.3; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "macos"
|
|
221
221
|
Provides-Extra: setup
|
|
222
|
-
Requires-Dist: tollbooth-dpyc[nostr]==0.
|
|
222
|
+
Requires-Dist: tollbooth-dpyc[nostr]==0.109.3; extra == "setup"
|
|
223
223
|
Provides-Extra: dev
|
|
224
224
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
225
225
|
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
@@ -106,20 +106,47 @@ def _check_registry() -> tuple[str, str]:
|
|
|
106
106
|
return OK, f"{len(rows)} securities cached"
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
#: A free lookup the listener answers from a table: no operator trip, no
|
|
110
|
+
#: fare, and the chart stays where it is. "read" was all three.
|
|
111
|
+
LISTENER_PROBE = b"scale half"
|
|
112
|
+
LISTENER_SECONDS = 5
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _post_probe(port: int, token: str) -> str:
|
|
116
|
+
request = urllib.request.Request(
|
|
117
|
+
f"http://127.0.0.1:{port}/chart", data=LISTENER_PROBE,
|
|
118
|
+
headers={"X-Token": token}, method="POST")
|
|
119
|
+
with urllib.request.urlopen(request, timeout=LISTENER_SECONDS) as response:
|
|
120
|
+
return response.read().decode().strip()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def listener_verdict(port: int, token: str, post=_post_probe) -> tuple[str, str]:
|
|
124
|
+
"""Ask the listener a free table lookup and judge the answer."""
|
|
125
|
+
try:
|
|
126
|
+
reply = post(port, token)
|
|
127
|
+
except urllib.error.HTTPError as exc:
|
|
128
|
+
if exc.code == 403:
|
|
129
|
+
return BAD, f"listener on :{port} refused the token in config.json - restart it: chartremotely setup"
|
|
130
|
+
return BAD, f"listener on :{port} answered HTTP {exc.code}"
|
|
131
|
+
except urllib.error.URLError as exc:
|
|
132
|
+
if isinstance(exc.reason, TimeoutError):
|
|
133
|
+
return BAD, (f"listener on :{port} took over {LISTENER_SECONDS} s to answer a table lookup - "
|
|
134
|
+
"restart it: chartremotely setup")
|
|
135
|
+
return WARN, f"nothing listening on :{port} - run: chartremotely serve"
|
|
136
|
+
except TimeoutError:
|
|
137
|
+
return BAD, (f"listener on :{port} took over {LISTENER_SECONDS} s to answer a table lookup - "
|
|
138
|
+
"restart it: chartremotely setup")
|
|
139
|
+
if reply != "half":
|
|
140
|
+
return BAD, f"listener on :{port} answered {reply[:60]!r} to a table lookup"
|
|
141
|
+
return OK, f"listener answering on :{port}"
|
|
142
|
+
|
|
143
|
+
|
|
109
144
|
def _check_listener() -> tuple[str, str]:
|
|
110
145
|
cfg = config.load()
|
|
111
|
-
port = cfg["port"]
|
|
112
146
|
token = cfg.get("token")
|
|
113
147
|
if not token:
|
|
114
148
|
return WARN, "no token yet - it is generated when the listener first starts"
|
|
115
|
-
|
|
116
|
-
f"http://127.0.0.1:{port}/chart", data=b"read",
|
|
117
|
-
headers={"X-Token": token}, method="POST")
|
|
118
|
-
try:
|
|
119
|
-
with urllib.request.urlopen(request, timeout=5) as response:
|
|
120
|
-
return OK, f"listener answering on :{port} - {response.read().decode().strip()}"
|
|
121
|
-
except urllib.error.URLError:
|
|
122
|
-
return WARN, f"nothing listening on :{port} - run: chartremotely serve"
|
|
149
|
+
return listener_verdict(cfg["port"], token)
|
|
123
150
|
|
|
124
151
|
|
|
125
152
|
CHECKS = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chartremotely
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: Remote control of desktop charting applications on macOS
|
|
5
5
|
Author: Lonnie VanZandt
|
|
6
6
|
License:
|
|
@@ -219,7 +219,7 @@ Requires-Dist: pyobjc-framework-Quartz>=10; extra == "macos"
|
|
|
219
219
|
Requires-Dist: pyobjc-framework-Security>=10; extra == "macos"
|
|
220
220
|
Requires-Dist: mlx-whisper>=0.4.3; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "macos"
|
|
221
221
|
Provides-Extra: setup
|
|
222
|
-
Requires-Dist: tollbooth-dpyc[nostr]==0.
|
|
222
|
+
Requires-Dist: tollbooth-dpyc[nostr]==0.109.3; extra == "setup"
|
|
223
223
|
Provides-Extra: dev
|
|
224
224
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
225
225
|
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "chartremotely"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.10"
|
|
4
4
|
description = "Remote control of desktop charting applications on macOS"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -25,7 +25,7 @@ macos = [
|
|
|
25
25
|
]
|
|
26
26
|
# `chartremotely setup` makes and proves Nostr identities. The key and proof
|
|
27
27
|
# code is the tollbooth-dpyc SDK's own, never rewritten here.
|
|
28
|
-
setup = ["tollbooth-dpyc[nostr]==0.
|
|
28
|
+
setup = ["tollbooth-dpyc[nostr]==0.109.3"]
|
|
29
29
|
dev = ["pytest>=8", "ruff>=0.6"]
|
|
30
30
|
|
|
31
31
|
[project.urls]
|
|
@@ -40,3 +40,44 @@ def test_both_running_on_a_live_python_is_ok():
|
|
|
40
40
|
def test_launchctl_print_yields_the_pid_only_while_running():
|
|
41
41
|
assert services.pid_in("com.chartremotely.relay = {\n\tstate = running\n\tpid = 12804\n}") == 12804
|
|
42
42
|
assert services.pid_in("com.chartremotely.relay = {\n\tstate = not running\n}") is None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# -- the listener: a free table lookup, never a paid chart read ----------------------------
|
|
46
|
+
|
|
47
|
+
import urllib.error
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_the_listener_probe_is_a_free_lookup_never_read():
|
|
51
|
+
# server.LOOKUPS needs Quartz to import; the verb is pinned here instead.
|
|
52
|
+
assert doctor.LISTENER_PROBE.split()[0] == b"scale"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_a_listener_that_answers_the_lookup_is_ok():
|
|
56
|
+
assert doctor.listener_verdict(8899, "t", post=lambda port, token: "half")[0] == doctor.OK
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _raises(exc):
|
|
60
|
+
def post(port, token):
|
|
61
|
+
raise exc
|
|
62
|
+
return post
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_a_slow_listener_fails_and_says_so():
|
|
66
|
+
for exc in (TimeoutError("timed out"), urllib.error.URLError(TimeoutError("timed out"))):
|
|
67
|
+
status, detail = doctor.listener_verdict(8899, "t", post=_raises(exc))
|
|
68
|
+
assert status == doctor.BAD and "took over" in detail
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_nothing_listening_points_at_serve():
|
|
72
|
+
status, detail = doctor.listener_verdict(8899, "t", post=_raises(urllib.error.URLError(ConnectionRefusedError())))
|
|
73
|
+
assert status == doctor.WARN and "chartremotely serve" in detail
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_a_refused_token_is_named():
|
|
77
|
+
refused = urllib.error.HTTPError("http://127.0.0.1:8899/chart", 403, "Forbidden", {}, None)
|
|
78
|
+
status, detail = doctor.listener_verdict(8899, "t", post=_raises(refused))
|
|
79
|
+
assert status == doctor.BAD and "token" in detail
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_a_wrong_answer_fails():
|
|
83
|
+
assert doctor.listener_verdict(8899, "t", post=lambda port, token: "ERR nope")[0] == doctor.BAD
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|