deltachat-rpc-client 2.28.0__tar.gz → 2.30.0__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.
- {deltachat_rpc_client-2.28.0/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.30.0}/PKG-INFO +12 -5
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/README.md +9 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/pyproject.toml +4 -6
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/const.py +1 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/message.py +4 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/pytestplugin.py +148 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/rpc.py +24 -49
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +12 -5
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +1 -0
- deltachat_rpc_client-2.30.0/tests/test_cross_core.py +44 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_multitransport.py +45 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_something.py +20 -11
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/LICENSE +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/setup.cfg +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/__init__.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/_utils.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/account.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/chat.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/client.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/contact.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/deltachat.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/events.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/py.typed +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_account_events.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_calls.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_chatlist_events.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_folders.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_iroh_webxdc.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_key_transfer.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_multidevice.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_securejoin.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_vcard.py +0 -0
- {deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/tests/test_webxdc.py +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltachat-rpc-client
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.30.0
|
|
4
4
|
Summary: Python client for Delta Chat core JSON-RPC interface
|
|
5
|
+
License-Expression: MPL-2.0
|
|
5
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
7
|
Classifier: Intended Audience :: Developers
|
|
7
|
-
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
8
8
|
Classifier: Operating System :: POSIX :: Linux
|
|
9
9
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
16
|
Classifier: Topic :: Communications :: Chat
|
|
19
17
|
Classifier: Topic :: Communications :: Email
|
|
20
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
License-File: LICENSE
|
|
23
21
|
Dynamic: license-file
|
|
@@ -54,6 +52,15 @@ $ pip install .
|
|
|
54
52
|
|
|
55
53
|
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
|
56
54
|
|
|
55
|
+
|
|
56
|
+
## Activating current checkout of deltachat-rpc-client and -server for development
|
|
57
|
+
|
|
58
|
+
Go to root repository directory and run:
|
|
59
|
+
```
|
|
60
|
+
$ scripts/make-rpc-testenv.sh
|
|
61
|
+
$ source venv/bin/activate
|
|
62
|
+
```
|
|
63
|
+
|
|
57
64
|
## Using in REPL
|
|
58
65
|
|
|
59
66
|
Setup a development environment:
|
|
@@ -30,6 +30,15 @@ $ pip install .
|
|
|
30
30
|
|
|
31
31
|
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
|
32
32
|
|
|
33
|
+
|
|
34
|
+
## Activating current checkout of deltachat-rpc-client and -server for development
|
|
35
|
+
|
|
36
|
+
Go to root repository directory and run:
|
|
37
|
+
```
|
|
38
|
+
$ scripts/make-rpc-testenv.sh
|
|
39
|
+
$ source venv/bin/activate
|
|
40
|
+
```
|
|
41
|
+
|
|
33
42
|
## Using in REPL
|
|
34
43
|
|
|
35
44
|
Setup a development environment:
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deltachat-rpc-client"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.30.0"
|
|
8
|
+
license = "MPL-2.0"
|
|
8
9
|
description = "Python client for Delta Chat core JSON-RPC interface"
|
|
9
10
|
classifiers = [
|
|
10
11
|
"Development Status :: 5 - Production/Stable",
|
|
11
12
|
"Intended Audience :: Developers",
|
|
12
|
-
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
|
13
13
|
"Operating System :: POSIX :: Linux",
|
|
14
14
|
"Operating System :: MacOS :: MacOS X",
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"Programming Language :: Python :: 3.8",
|
|
17
|
-
"Programming Language :: Python :: 3.9",
|
|
18
16
|
"Programming Language :: Python :: 3.10",
|
|
19
17
|
"Programming Language :: Python :: 3.11",
|
|
20
18
|
"Programming Language :: Python :: 3.12",
|
|
@@ -24,7 +22,7 @@ classifiers = [
|
|
|
24
22
|
"Topic :: Communications :: Email"
|
|
25
23
|
]
|
|
26
24
|
readme = "README.md"
|
|
27
|
-
requires-python = ">=3.
|
|
25
|
+
requires-python = ">=3.10"
|
|
28
26
|
|
|
29
27
|
[tool.setuptools.package-data]
|
|
30
28
|
deltachat_rpc_client = [
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/const.py
RENAMED
|
@@ -80,6 +80,7 @@ class EventType(str, Enum):
|
|
|
80
80
|
CONFIG_SYNCED = "ConfigSynced"
|
|
81
81
|
WEBXDC_REALTIME_DATA = "WebxdcRealtimeData"
|
|
82
82
|
WEBXDC_REALTIME_ADVERTISEMENT_RECEIVED = "WebxdcRealtimeAdvertisementReceived"
|
|
83
|
+
TRANSPORTS_MODIFIED = "TransportsModified"
|
|
83
84
|
|
|
84
85
|
|
|
85
86
|
class ChatId(IntEnum):
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/message.py
RENAMED
|
@@ -60,6 +60,10 @@ class Message:
|
|
|
60
60
|
"""Mark the message as seen."""
|
|
61
61
|
self._rpc.markseen_msgs(self.account.id, [self.id])
|
|
62
62
|
|
|
63
|
+
def exists(self) -> bool:
|
|
64
|
+
"""Return True if the message exists."""
|
|
65
|
+
return bool(self._rpc.get_existing_msg_ids(self.account.id, [self.id]))
|
|
66
|
+
|
|
63
67
|
def continue_autocrypt_key_transfer(self, setup_code: str) -> None:
|
|
64
68
|
"""Continue the Autocrypt Setup Message key transfer.
|
|
65
69
|
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/pytestplugin.py
RENAMED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import pathlib
|
|
7
|
+
import platform
|
|
6
8
|
import random
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
7
11
|
from typing import AsyncGenerator, Optional
|
|
8
12
|
|
|
13
|
+
import execnet
|
|
9
14
|
import py
|
|
10
15
|
import pytest
|
|
11
16
|
|
|
@@ -20,6 +25,18 @@ Currently this is "End-to-end encryption available".
|
|
|
20
25
|
"""
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
def pytest_report_header():
|
|
29
|
+
for base in os.get_exec_path():
|
|
30
|
+
fn = pathlib.Path(base).joinpath(base, "deltachat-rpc-server")
|
|
31
|
+
if fn.exists():
|
|
32
|
+
proc = subprocess.Popen([str(fn), "--version"], stderr=subprocess.PIPE)
|
|
33
|
+
proc.wait()
|
|
34
|
+
version = proc.stderr.read().decode().strip()
|
|
35
|
+
return f"deltachat-rpc-server: {fn} [{version}]"
|
|
36
|
+
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
23
40
|
class ACFactory:
|
|
24
41
|
"""Test account factory."""
|
|
25
42
|
|
|
@@ -197,3 +214,134 @@ def log():
|
|
|
197
214
|
print(" " + msg)
|
|
198
215
|
|
|
199
216
|
return Printer()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
# support for testing against different deltachat-rpc-server/clients
|
|
221
|
+
# installed into a temporary virtualenv and connected via 'execnet' channels
|
|
222
|
+
#
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def find_path(venv, name):
|
|
226
|
+
is_windows = platform.system() == "Windows"
|
|
227
|
+
bin = venv / ("bin" if not is_windows else "Scripts")
|
|
228
|
+
|
|
229
|
+
tryadd = [""]
|
|
230
|
+
if is_windows:
|
|
231
|
+
tryadd += os.environ["PATHEXT"].split(os.pathsep)
|
|
232
|
+
for ext in tryadd:
|
|
233
|
+
p = bin.joinpath(name + ext)
|
|
234
|
+
if p.exists():
|
|
235
|
+
return str(p)
|
|
236
|
+
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.fixture(scope="session")
|
|
241
|
+
def get_core_python_env(tmp_path_factory):
|
|
242
|
+
"""Return a factory to create virtualenv environments with rpc server/client packages
|
|
243
|
+
installed.
|
|
244
|
+
|
|
245
|
+
The factory takes a version and returns a (python_path, rpc_server_path) tuple
|
|
246
|
+
of the respective binaries in the virtualenv.
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
envs = {}
|
|
250
|
+
|
|
251
|
+
def get_versioned_venv(core_version):
|
|
252
|
+
venv = envs.get(core_version)
|
|
253
|
+
if not venv:
|
|
254
|
+
venv = tmp_path_factory.mktemp(f"temp-{core_version}")
|
|
255
|
+
subprocess.check_call([sys.executable, "-m", "venv", venv])
|
|
256
|
+
|
|
257
|
+
python = find_path(venv, "python")
|
|
258
|
+
pkgs = [f"deltachat-rpc-server=={core_version}", f"deltachat-rpc-client=={core_version}", "pytest"]
|
|
259
|
+
subprocess.check_call([python, "-m", "pip", "install"] + pkgs)
|
|
260
|
+
|
|
261
|
+
envs[core_version] = venv
|
|
262
|
+
python = find_path(venv, "python")
|
|
263
|
+
rpc_server_path = find_path(venv, "deltachat-rpc-server")
|
|
264
|
+
print(f"python={python}\nrpc_server={rpc_server_path}")
|
|
265
|
+
return python, rpc_server_path
|
|
266
|
+
|
|
267
|
+
return get_versioned_venv
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@pytest.fixture
|
|
271
|
+
def alice_and_remote_bob(tmp_path, acfactory, get_core_python_env):
|
|
272
|
+
"""return local Alice account, a contact to bob, and a remote 'eval' function for bob.
|
|
273
|
+
|
|
274
|
+
The 'eval' function allows to remote-execute arbitrary expressions
|
|
275
|
+
that can use the `bob` online account, and the `bob_contact_alice`.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
def factory(core_version):
|
|
279
|
+
python, rpc_server_path = get_core_python_env(core_version)
|
|
280
|
+
gw = execnet.makegateway(f"popen//python={python}")
|
|
281
|
+
|
|
282
|
+
accounts_dir = str(tmp_path.joinpath("account1_venv1"))
|
|
283
|
+
channel = gw.remote_exec(remote_bob_loop)
|
|
284
|
+
cm = os.environ.get("CHATMAIL_DOMAIN")
|
|
285
|
+
|
|
286
|
+
# trigger getting an online account on bob's side
|
|
287
|
+
channel.send((accounts_dir, str(rpc_server_path), cm))
|
|
288
|
+
|
|
289
|
+
# meanwhile get a local alice account
|
|
290
|
+
alice = acfactory.get_online_account()
|
|
291
|
+
channel.send(alice.self_contact.make_vcard())
|
|
292
|
+
|
|
293
|
+
# wait for bob to have started
|
|
294
|
+
sysinfo = channel.receive()
|
|
295
|
+
assert sysinfo == f"v{core_version}"
|
|
296
|
+
bob_vcard = channel.receive()
|
|
297
|
+
[alice_contact_bob] = alice.import_vcard(bob_vcard)
|
|
298
|
+
|
|
299
|
+
def eval(eval_str):
|
|
300
|
+
channel.send(eval_str)
|
|
301
|
+
return channel.receive()
|
|
302
|
+
|
|
303
|
+
return alice, alice_contact_bob, eval
|
|
304
|
+
|
|
305
|
+
return factory
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def remote_bob_loop(channel):
|
|
309
|
+
# This function executes with versioned
|
|
310
|
+
# deltachat-rpc-client/server packages
|
|
311
|
+
# installed into the virtualenv.
|
|
312
|
+
#
|
|
313
|
+
# The "channel" argument is a send/receive pipe
|
|
314
|
+
# to the process that runs the corresponding remote_exec(remote_bob_loop)
|
|
315
|
+
|
|
316
|
+
import os
|
|
317
|
+
|
|
318
|
+
from deltachat_rpc_client import DeltaChat, Rpc
|
|
319
|
+
from deltachat_rpc_client.pytestplugin import ACFactory
|
|
320
|
+
|
|
321
|
+
accounts_dir, rpc_server_path, chatmail_domain = channel.receive()
|
|
322
|
+
os.environ["CHATMAIL_DOMAIN"] = chatmail_domain
|
|
323
|
+
|
|
324
|
+
# older core versions don't support specifying rpc_server_path
|
|
325
|
+
# so we can't just pass `rpc_server_path` argument to Rpc constructor
|
|
326
|
+
basepath = os.path.dirname(rpc_server_path)
|
|
327
|
+
os.environ["PATH"] = os.pathsep.join([basepath, os.environ["PATH"]])
|
|
328
|
+
rpc = Rpc(accounts_dir=accounts_dir)
|
|
329
|
+
|
|
330
|
+
with rpc:
|
|
331
|
+
dc = DeltaChat(rpc)
|
|
332
|
+
channel.send(dc.rpc.get_system_info()["deltachat_core_version"])
|
|
333
|
+
acfactory = ACFactory(dc)
|
|
334
|
+
bob = acfactory.get_online_account()
|
|
335
|
+
alice_vcard = channel.receive()
|
|
336
|
+
[alice_contact] = bob.import_vcard(alice_vcard)
|
|
337
|
+
ns = {"bob": bob, "bob_contact_alice": alice_contact}
|
|
338
|
+
channel.send(bob.self_contact.make_vcard())
|
|
339
|
+
|
|
340
|
+
while 1:
|
|
341
|
+
eval_str = channel.receive()
|
|
342
|
+
res = eval(eval_str, ns)
|
|
343
|
+
try:
|
|
344
|
+
channel.send(res)
|
|
345
|
+
except Exception:
|
|
346
|
+
# some unserializable result
|
|
347
|
+
channel.send(None)
|
|
@@ -9,7 +9,7 @@ import os
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import sys
|
|
11
11
|
from queue import Empty, Queue
|
|
12
|
-
from threading import
|
|
12
|
+
from threading import Thread
|
|
13
13
|
from typing import Any, Iterator, Optional
|
|
14
14
|
|
|
15
15
|
|
|
@@ -17,25 +17,6 @@ class JsonRpcError(Exception):
|
|
|
17
17
|
"""JSON-RPC error."""
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class RpcFuture:
|
|
21
|
-
"""RPC future waiting for RPC call result."""
|
|
22
|
-
|
|
23
|
-
def __init__(self, rpc: "Rpc", request_id: int, event: Event):
|
|
24
|
-
self.rpc = rpc
|
|
25
|
-
self.request_id = request_id
|
|
26
|
-
self.event = event
|
|
27
|
-
|
|
28
|
-
def __call__(self):
|
|
29
|
-
"""Wait for the future to return the result."""
|
|
30
|
-
self.event.wait()
|
|
31
|
-
response = self.rpc.request_results.pop(self.request_id)
|
|
32
|
-
if "error" in response:
|
|
33
|
-
raise JsonRpcError(response["error"])
|
|
34
|
-
if "result" in response:
|
|
35
|
-
return response["result"]
|
|
36
|
-
return None
|
|
37
|
-
|
|
38
|
-
|
|
39
20
|
class RpcMethod:
|
|
40
21
|
"""RPC method."""
|
|
41
22
|
|
|
@@ -57,20 +38,26 @@ class RpcMethod:
|
|
|
57
38
|
"params": args,
|
|
58
39
|
"id": request_id,
|
|
59
40
|
}
|
|
60
|
-
|
|
61
|
-
self.rpc.request_events[request_id] = event
|
|
41
|
+
self.rpc.request_results[request_id] = queue = Queue()
|
|
62
42
|
self.rpc.request_queue.put(request)
|
|
63
43
|
|
|
64
|
-
|
|
44
|
+
def rpc_future():
|
|
45
|
+
"""Wait for the request to receive a result."""
|
|
46
|
+
response = queue.get()
|
|
47
|
+
if "error" in response:
|
|
48
|
+
raise JsonRpcError(response["error"])
|
|
49
|
+
return response.get("result", None)
|
|
50
|
+
|
|
51
|
+
return rpc_future
|
|
65
52
|
|
|
66
53
|
|
|
67
54
|
class Rpc:
|
|
68
55
|
"""RPC client."""
|
|
69
56
|
|
|
70
|
-
def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
|
|
57
|
+
def __init__(self, accounts_dir: Optional[str] = None, rpc_server_path="deltachat-rpc-server", **kwargs):
|
|
71
58
|
"""Initialize RPC client.
|
|
72
59
|
|
|
73
|
-
The
|
|
60
|
+
The 'kwargs' arguments will be passed to subprocess.Popen().
|
|
74
61
|
"""
|
|
75
62
|
if accounts_dir:
|
|
76
63
|
kwargs["env"] = {
|
|
@@ -79,13 +66,12 @@ class Rpc:
|
|
|
79
66
|
}
|
|
80
67
|
|
|
81
68
|
self._kwargs = kwargs
|
|
69
|
+
self.rpc_server_path = rpc_server_path
|
|
82
70
|
self.process: subprocess.Popen
|
|
83
71
|
self.id_iterator: Iterator[int]
|
|
84
72
|
self.event_queues: dict[int, Queue]
|
|
85
|
-
# Map from request ID to
|
|
86
|
-
self.
|
|
87
|
-
# Map from request ID to the result.
|
|
88
|
-
self.request_results: dict[int, Any]
|
|
73
|
+
# Map from request ID to a Queue which provides a single result
|
|
74
|
+
self.request_results: dict[int, Queue]
|
|
89
75
|
self.request_queue: Queue[Any]
|
|
90
76
|
self.closing: bool
|
|
91
77
|
self.reader_thread: Thread
|
|
@@ -94,27 +80,18 @@ class Rpc:
|
|
|
94
80
|
|
|
95
81
|
def start(self) -> None:
|
|
96
82
|
"""Start RPC server subprocess."""
|
|
83
|
+
popen_kwargs = {"stdin": subprocess.PIPE, "stdout": subprocess.PIPE}
|
|
97
84
|
if sys.version_info >= (3, 11):
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
stdin=subprocess.PIPE,
|
|
101
|
-
stdout=subprocess.PIPE,
|
|
102
|
-
# Prevent subprocess from capturing SIGINT.
|
|
103
|
-
process_group=0,
|
|
104
|
-
**self._kwargs,
|
|
105
|
-
)
|
|
85
|
+
# Prevent subprocess from capturing SIGINT.
|
|
86
|
+
popen_kwargs["process_group"] = 0
|
|
106
87
|
else:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
preexec_fn=os.setpgrp, # noqa: PLW1509
|
|
113
|
-
**self._kwargs,
|
|
114
|
-
)
|
|
88
|
+
# `process_group` is not supported before Python 3.11.
|
|
89
|
+
popen_kwargs["preexec_fn"] = os.setpgrp # noqa: PLW1509
|
|
90
|
+
|
|
91
|
+
popen_kwargs.update(self._kwargs)
|
|
92
|
+
self.process = subprocess.Popen(self.rpc_server_path, **popen_kwargs)
|
|
115
93
|
self.id_iterator = itertools.count(start=1)
|
|
116
94
|
self.event_queues = {}
|
|
117
|
-
self.request_events = {}
|
|
118
95
|
self.request_results = {}
|
|
119
96
|
self.request_queue = Queue()
|
|
120
97
|
self.closing = False
|
|
@@ -149,9 +126,7 @@ class Rpc:
|
|
|
149
126
|
response = json.loads(line)
|
|
150
127
|
if "id" in response:
|
|
151
128
|
response_id = response["id"]
|
|
152
|
-
|
|
153
|
-
self.request_results[response_id] = response
|
|
154
|
-
event.set()
|
|
129
|
+
self.request_results.pop(response_id).put(response)
|
|
155
130
|
else:
|
|
156
131
|
logging.warning("Got a response without ID: %s", response)
|
|
157
132
|
except Exception:
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deltachat-rpc-client
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.30.0
|
|
4
4
|
Summary: Python client for Delta Chat core JSON-RPC interface
|
|
5
|
+
License-Expression: MPL-2.0
|
|
5
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
7
|
Classifier: Intended Audience :: Developers
|
|
7
|
-
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
8
8
|
Classifier: Operating System :: POSIX :: Linux
|
|
9
9
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
16
|
Classifier: Topic :: Communications :: Chat
|
|
19
17
|
Classifier: Topic :: Communications :: Email
|
|
20
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
License-File: LICENSE
|
|
23
21
|
Dynamic: license-file
|
|
@@ -54,6 +52,15 @@ $ pip install .
|
|
|
54
52
|
|
|
55
53
|
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
|
56
54
|
|
|
55
|
+
|
|
56
|
+
## Activating current checkout of deltachat-rpc-client and -server for development
|
|
57
|
+
|
|
58
|
+
Go to root repository directory and run:
|
|
59
|
+
```
|
|
60
|
+
$ scripts/make-rpc-testenv.sh
|
|
61
|
+
$ source venv/bin/activate
|
|
62
|
+
```
|
|
63
|
+
|
|
57
64
|
## Using in REPL
|
|
58
65
|
|
|
59
66
|
Setup a development environment:
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from deltachat_rpc_client import DeltaChat, Rpc
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_install_venv_and_use_other_core(tmp_path, get_core_python_env):
|
|
9
|
+
python, rpc_server_path = get_core_python_env("2.24.0")
|
|
10
|
+
subprocess.check_call([python, "-m", "pip", "install", "deltachat-rpc-server==2.24.0"])
|
|
11
|
+
rpc = Rpc(accounts_dir=tmp_path.joinpath("accounts"), rpc_server_path=rpc_server_path)
|
|
12
|
+
|
|
13
|
+
with rpc:
|
|
14
|
+
dc = DeltaChat(rpc)
|
|
15
|
+
assert dc.rpc.get_system_info()["deltachat_core_version"] == "v2.24.0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize("version", ["2.24.0"])
|
|
19
|
+
def test_qr_setup_contact(alice_and_remote_bob, version) -> None:
|
|
20
|
+
"""Test other-core Bob profile can do securejoin with Alice on current core."""
|
|
21
|
+
alice, alice_contact_bob, remote_eval = alice_and_remote_bob(version)
|
|
22
|
+
|
|
23
|
+
qr_code = alice.get_qr_code()
|
|
24
|
+
remote_eval(f"bob.secure_join({qr_code!r})")
|
|
25
|
+
alice.wait_for_securejoin_inviter_success()
|
|
26
|
+
|
|
27
|
+
# Test that Alice verified Bob's profile.
|
|
28
|
+
alice_contact_bob_snapshot = alice_contact_bob.get_snapshot()
|
|
29
|
+
assert alice_contact_bob_snapshot.is_verified
|
|
30
|
+
|
|
31
|
+
remote_eval("bob.wait_for_securejoin_joiner_success()")
|
|
32
|
+
|
|
33
|
+
# Test that Bob verified Alice's profile.
|
|
34
|
+
assert remote_eval("bob_contact_alice.get_snapshot().is_verified")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_send_and_receive_message(alice_and_remote_bob) -> None:
|
|
38
|
+
"""Test other-core Bob profile can send a message to Alice on current core."""
|
|
39
|
+
alice, alice_contact_bob, remote_eval = alice_and_remote_bob("2.20.0")
|
|
40
|
+
|
|
41
|
+
remote_eval("bob_contact_alice.create_chat().send_text('hello')")
|
|
42
|
+
|
|
43
|
+
msg = alice.wait_for_incoming_msg()
|
|
44
|
+
assert msg.get_snapshot().text == "hello"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
+
from deltachat_rpc_client import EventType
|
|
3
4
|
from deltachat_rpc_client.rpc import JsonRpcError
|
|
4
5
|
|
|
5
6
|
|
|
@@ -156,3 +157,47 @@ def test_reconfigure_transport(acfactory) -> None:
|
|
|
156
157
|
# Reconfiguring the transport should not reset
|
|
157
158
|
# the settings as if when configuring the first transport.
|
|
158
159
|
assert account.get_config("mvbox_move") == "1"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_transport_synchronization(acfactory, log) -> None:
|
|
163
|
+
"""Test synchronization of transports between devices."""
|
|
164
|
+
ac1, ac2 = acfactory.get_online_accounts(2)
|
|
165
|
+
ac1_clone = ac1.clone()
|
|
166
|
+
ac1_clone.bring_online()
|
|
167
|
+
|
|
168
|
+
qr = acfactory.get_account_qr()
|
|
169
|
+
|
|
170
|
+
ac1.add_transport_from_qr(qr)
|
|
171
|
+
ac1_clone.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
172
|
+
assert len(ac1.list_transports()) == 2
|
|
173
|
+
assert len(ac1_clone.list_transports()) == 2
|
|
174
|
+
|
|
175
|
+
ac1_clone.add_transport_from_qr(qr)
|
|
176
|
+
ac1.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
177
|
+
assert len(ac1.list_transports()) == 3
|
|
178
|
+
assert len(ac1_clone.list_transports()) == 3
|
|
179
|
+
|
|
180
|
+
log.section("ac1 clone removes second transport")
|
|
181
|
+
[transport1, transport2, transport3] = ac1_clone.list_transports()
|
|
182
|
+
addr3 = transport3["addr"]
|
|
183
|
+
ac1_clone.delete_transport(transport2["addr"])
|
|
184
|
+
|
|
185
|
+
ac1.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
186
|
+
[transport1, transport3] = ac1.list_transports()
|
|
187
|
+
|
|
188
|
+
log.section("ac1 changes the primary transport")
|
|
189
|
+
ac1.set_config("configured_addr", transport3["addr"])
|
|
190
|
+
|
|
191
|
+
log.section("ac1 removes the first transport")
|
|
192
|
+
ac1.delete_transport(transport1["addr"])
|
|
193
|
+
|
|
194
|
+
ac1_clone.wait_for_event(EventType.TRANSPORTS_MODIFIED)
|
|
195
|
+
[transport3] = ac1_clone.list_transports()
|
|
196
|
+
assert transport3["addr"] == addr3
|
|
197
|
+
assert ac1_clone.get_config("configured_addr") == addr3
|
|
198
|
+
|
|
199
|
+
ac2_chat = ac2.create_chat(ac1)
|
|
200
|
+
ac2_chat.send_text("Hello!")
|
|
201
|
+
|
|
202
|
+
assert ac1.wait_for_incoming_msg().get_snapshot().text == "Hello!"
|
|
203
|
+
assert ac1_clone.wait_for_incoming_msg().get_snapshot().text == "Hello!"
|
|
@@ -661,8 +661,6 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
|
661
661
|
contact = alice.create_contact(account)
|
|
662
662
|
alice_group.add_contact(contact)
|
|
663
663
|
|
|
664
|
-
if n_accounts == 2:
|
|
665
|
-
bob_chat_alice = bob.create_chat(alice)
|
|
666
664
|
bob.set_config("download_limit", str(download_limit))
|
|
667
665
|
|
|
668
666
|
alice_group.send_text("hi")
|
|
@@ -678,15 +676,7 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
|
|
|
678
676
|
alice_group.send_file(str(path))
|
|
679
677
|
snapshot = bob.wait_for_incoming_msg().get_snapshot()
|
|
680
678
|
assert snapshot.download_state == DownloadState.AVAILABLE
|
|
681
|
-
|
|
682
|
-
assert snapshot.chat == bob_group
|
|
683
|
-
else:
|
|
684
|
-
# Group contains only Alice and Bob,
|
|
685
|
-
# so partially downloaded messages are
|
|
686
|
-
# hard to distinguish from private replies to group messages.
|
|
687
|
-
#
|
|
688
|
-
# Message may be a private reply, so we assign it to 1:1 chat with Alice.
|
|
689
|
-
assert snapshot.chat == bob_chat_alice
|
|
679
|
+
assert snapshot.chat == bob_group
|
|
690
680
|
|
|
691
681
|
|
|
692
682
|
def test_markseen_contact_request(acfactory):
|
|
@@ -1003,3 +993,22 @@ def test_background_fetch(acfactory, dc):
|
|
|
1003
993
|
snapshot = messages[-1].get_snapshot()
|
|
1004
994
|
if snapshot.text == "Hello again!":
|
|
1005
995
|
break
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def test_message_exists(acfactory):
|
|
999
|
+
ac1, ac2 = acfactory.get_online_accounts(2)
|
|
1000
|
+
chat = ac1.create_chat(ac2)
|
|
1001
|
+
message1 = chat.send_text("Hello!")
|
|
1002
|
+
message2 = chat.send_text("Hello again!")
|
|
1003
|
+
assert message1.exists()
|
|
1004
|
+
assert message2.exists()
|
|
1005
|
+
|
|
1006
|
+
ac1.delete_messages([message1])
|
|
1007
|
+
assert not message1.exists()
|
|
1008
|
+
assert message2.exists()
|
|
1009
|
+
|
|
1010
|
+
# There is no error when checking if
|
|
1011
|
+
# the message exists for deleted account.
|
|
1012
|
+
ac1.remove()
|
|
1013
|
+
assert not message1.exists()
|
|
1014
|
+
assert not message2.exists()
|
|
File without changes
|
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/__init__.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/_utils.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/account.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/chat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/client.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/contact.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/deltachat.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/events.py
RENAMED
|
File without changes
|
{deltachat_rpc_client-2.28.0 → deltachat_rpc_client-2.30.0}/src/deltachat_rpc_client/py.typed
RENAMED
|
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
|