cubething_psync 0.3.0__tar.gz → 0.4.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.
- {cubething_psync-0.3.0/src/cubething_psync.egg-info → cubething_psync-0.4.0}/PKG-INFO +1 -1
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/pyproject.toml +6 -1
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/client/args.py +17 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/client/main.py +25 -1
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/common/data.py +4 -4
- {cubething_psync-0.3.0 → cubething_psync-0.4.0/src/cubething_psync.egg-info}/PKG-INFO +1 -1
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/server/main.py +15 -7
- cubething_psync-0.4.0/test/test_integration.py +153 -0
- cubething_psync-0.3.0/test/test_integration.py +0 -83
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/LICENSE +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/README.md +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/setup.cfg +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/client/__init__.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/client/__main__.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/common/__init__.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/common/log.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/SOURCES.txt +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/dependency_links.txt +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/entry_points.txt +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/requires.txt +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/top_level.txt +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/server/__init__.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/server/__main__.py +0 -0
- {cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/server/args.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cubething_psync"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Simple project synchronization tool."
|
|
5
5
|
authors = [{name = "ada mandala", email="ada@cubething.dev"}]
|
|
6
6
|
readme = "README.md"
|
|
@@ -41,3 +41,8 @@ psync-client = "client.main:main"
|
|
|
41
41
|
[pytest]
|
|
42
42
|
testpaths="test"
|
|
43
43
|
addopts = ["--import-mode=importlib"]
|
|
44
|
+
|
|
45
|
+
[tool.basedpyright]
|
|
46
|
+
ignore=["**/__pycache__", "build", "dist"]
|
|
47
|
+
include=["src", "test"]
|
|
48
|
+
failOnWarnings=false
|
|
@@ -7,6 +7,12 @@ from os.path import basename
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import shlex
|
|
9
9
|
from common.data import deserialize_env
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from _typeshed import SupportsWrite
|
|
13
|
+
Logfile = SupportsWrite[str] | Path | None
|
|
14
|
+
else:
|
|
15
|
+
Logfile = Any
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
@dataclass
|
|
@@ -96,6 +102,13 @@ class Args:
|
|
|
96
102
|
variable.
|
|
97
103
|
"""
|
|
98
104
|
|
|
105
|
+
logfile: Logfile = None
|
|
106
|
+
"""
|
|
107
|
+
environ: ``PSYNC_LOG_FILE``
|
|
108
|
+
|
|
109
|
+
Optional file where the executable's logs will be output.
|
|
110
|
+
"""
|
|
111
|
+
|
|
99
112
|
def project_hash(self) -> str:
|
|
100
113
|
"""
|
|
101
114
|
Hash value generated from the target path. Used as the directory name for the project.
|
|
@@ -132,8 +145,12 @@ PSYNC_SERVER_DEST | /home/psync/
|
|
|
132
145
|
PSYNC_SSH_ARGS | -l psync
|
|
133
146
|
PSYNC_CERT_PATH | ~/.local/share/psync/cert.pem
|
|
134
147
|
PSYNC_CLIENT_ORIGIN | 127.0.0.1
|
|
148
|
+
PSYNC_LOG_FILE | None (stdout)
|
|
135
149
|
|
|
136
150
|
SSH arguments will be append with "-p {PSYNC_SSH_PORT}"
|
|
151
|
+
|
|
152
|
+
For more info, please read the docs:
|
|
153
|
+
https://psync.readthedocs.io/
|
|
137
154
|
""",
|
|
138
155
|
)
|
|
139
156
|
_action = parser.add_argument(
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
psync client
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from io import TextIOWrapper
|
|
6
|
+
import sys
|
|
5
7
|
import asyncio
|
|
6
8
|
import os
|
|
7
9
|
from pathlib import Path
|
|
@@ -28,6 +30,14 @@ from client.args import (
|
|
|
28
30
|
parse_args,
|
|
29
31
|
)
|
|
30
32
|
|
|
33
|
+
from typing import TYPE_CHECKING, Any
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from _typeshed import SupportsWrite
|
|
37
|
+
Logfile = SupportsWrite[str]
|
|
38
|
+
else:
|
|
39
|
+
Logfile = Any
|
|
40
|
+
|
|
31
41
|
|
|
32
42
|
class PsyncClient:
|
|
33
43
|
"""
|
|
@@ -39,9 +49,23 @@ class PsyncClient:
|
|
|
39
49
|
pid: int | None = None
|
|
40
50
|
"""ID of the current connection."""
|
|
41
51
|
__force_exit: bool = False
|
|
52
|
+
__outfile: Logfile
|
|
42
53
|
|
|
43
54
|
def __init__(self, args: Args):
|
|
44
55
|
self.args = args
|
|
56
|
+
if isinstance(self.args.logfile, Path):
|
|
57
|
+
self.__outfile = open(self.args.logfile, "w")
|
|
58
|
+
elif self.args.logfile is not None:
|
|
59
|
+
self.__outfile = self.args.logfile
|
|
60
|
+
else:
|
|
61
|
+
self.__outfile = sys.stdout
|
|
62
|
+
|
|
63
|
+
def __enter__(self):
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self):
|
|
67
|
+
if isinstance(self.__outfile, TextIOWrapper):
|
|
68
|
+
self.__outfile.close()
|
|
45
69
|
|
|
46
70
|
def __mk_handler(self, ws: websockets.ClientConnection):
|
|
47
71
|
async def inner():
|
|
@@ -99,7 +123,7 @@ class PsyncClient:
|
|
|
99
123
|
|
|
100
124
|
match resp:
|
|
101
125
|
case LogResp():
|
|
102
|
-
print(resp.msg, end="")
|
|
126
|
+
print(resp.msg, end="", file=self.__outfile)
|
|
103
127
|
case ErrorResp():
|
|
104
128
|
logging.error(f"Received server error: {resp.msg}")
|
|
105
129
|
await ws.close()
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from collections.abc import Mapping
|
|
2
1
|
from dataclasses import dataclass
|
|
3
2
|
import logging
|
|
4
3
|
from pathlib import Path
|
|
@@ -30,7 +29,7 @@ class RespKind(Enum):
|
|
|
30
29
|
class OpenReq:
|
|
31
30
|
path: Path
|
|
32
31
|
args: list[str]
|
|
33
|
-
env:
|
|
32
|
+
env: dict[str,str]
|
|
34
33
|
kind: ReqKind = ReqKind.Open
|
|
35
34
|
|
|
36
35
|
|
|
@@ -84,8 +83,8 @@ def serialize(msg: Req | Resp) -> str:
|
|
|
84
83
|
case OpenReq():
|
|
85
84
|
args = " ".join(msg.args)
|
|
86
85
|
env: list[str] = []
|
|
87
|
-
for
|
|
88
|
-
env.append(f'{
|
|
86
|
+
for k, v in msg.env.items():
|
|
87
|
+
env.append(f'{k}="{v}"')
|
|
89
88
|
return f"{value} path='{msg.path}' args='{args}' env='{' '.join(env)}'"
|
|
90
89
|
case KillReq():
|
|
91
90
|
return f"{value} {msg.pid}"
|
|
@@ -127,6 +126,7 @@ def deserialize(msg: str) -> Req | Resp:
|
|
|
127
126
|
[kind, rest] = msg.split(" ", 1)
|
|
128
127
|
except Exception:
|
|
129
128
|
kind = msg.strip()
|
|
129
|
+
rest = ""
|
|
130
130
|
|
|
131
131
|
match kind:
|
|
132
132
|
case ReqKind.Open.value:
|
|
@@ -9,7 +9,6 @@ import asyncio
|
|
|
9
9
|
from asyncio.tasks import Task
|
|
10
10
|
from asyncio.subprocess import Process
|
|
11
11
|
from collections.abc import Awaitable
|
|
12
|
-
from os.path import basename
|
|
13
12
|
import pathlib
|
|
14
13
|
import signal
|
|
15
14
|
import ssl
|
|
@@ -166,10 +165,18 @@ class PsyncServer:
|
|
|
166
165
|
async def __open(self, req: OpenReq, ws: ServerConnection):
|
|
167
166
|
host = self.__get_host(ws)
|
|
168
167
|
path = pathlib.Path.expanduser(req.path).resolve()
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
base_env = environ.copy() if self.args.use_base_env else {}
|
|
169
|
+
if not self.args.use_base_env:
|
|
170
|
+
# still get path and etc
|
|
171
|
+
for var in ['PATH','HOME','USER','SHELL']:
|
|
172
|
+
if var in environ:
|
|
173
|
+
base_env[var] = environ[var]
|
|
174
|
+
if 'VIRTUAL_ENV' in environ:
|
|
175
|
+
base_env['VIRUTAL_ENV'] = environ['VIRTUAL_ENV']
|
|
176
|
+
base_env["PATH"] = f"{environ['VIRTUAL_ENV']}/bin:{base_env["PATH"]}"
|
|
177
|
+
env = base_env | req.env
|
|
178
|
+
|
|
179
|
+
info_log = f"Running `{path} {' '.join(req.args)}`"
|
|
173
180
|
if env != {}:
|
|
174
181
|
info_log += f"\n... with env {pprint(env)}"
|
|
175
182
|
if self.args.user is not None:
|
|
@@ -179,7 +186,8 @@ class PsyncServer:
|
|
|
179
186
|
|
|
180
187
|
try:
|
|
181
188
|
p = await asyncio.create_subprocess_exec(
|
|
182
|
-
|
|
189
|
+
path,
|
|
190
|
+
*req.args,
|
|
183
191
|
env=env,
|
|
184
192
|
stdout=asyncio.subprocess.PIPE,
|
|
185
193
|
stderr=asyncio.subprocess.STDOUT,
|
|
@@ -196,7 +204,7 @@ class PsyncServer:
|
|
|
196
204
|
await ws.send(serialize(resp))
|
|
197
205
|
|
|
198
206
|
except Exception as e:
|
|
199
|
-
logging.error(f"Failed to start process `{
|
|
207
|
+
logging.error(f"Failed to start process `{path}` with error {e}")
|
|
200
208
|
resp = ErrorResp(f"Server error: {e}")
|
|
201
209
|
await ws.send(serialize(resp))
|
|
202
210
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import multiprocessing
|
|
3
|
+
import os
|
|
4
|
+
from os.path import basename
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from signal import Signals
|
|
8
|
+
from io import StringIO
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from testcontainers.core.generic import DockerContainer # pyright: ignore[reportMissingTypeStubs]
|
|
12
|
+
|
|
13
|
+
from client.args import Args as ClientArgs
|
|
14
|
+
from client.main import PsyncClient
|
|
15
|
+
from client.main import __rsync as rsync # pyright: ignore[reportPrivateUsage]
|
|
16
|
+
from test.conftest import assets_path
|
|
17
|
+
|
|
18
|
+
OUTFILE = StringIO()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Logs:
|
|
23
|
+
stdout: str
|
|
24
|
+
stderr: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_logs(server: DockerContainer) -> Logs:
|
|
28
|
+
slogs = server.get_logs()
|
|
29
|
+
return Logs(stdout=slogs[0].decode(), stderr=slogs[1].decode())
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def template(args: ClientArgs, server: DockerContainer, kill: bool = False):
|
|
33
|
+
try:
|
|
34
|
+
rsync(args)
|
|
35
|
+
|
|
36
|
+
def find(path: str):
|
|
37
|
+
exec_result = server.exec(["ls", str(path)])
|
|
38
|
+
ok = exec_result.output.decode().__contains__(basename(args.target_path))
|
|
39
|
+
if not ok:
|
|
40
|
+
print(f"Could not find path {path}")
|
|
41
|
+
assert False
|
|
42
|
+
|
|
43
|
+
find(str(args.destination_path()))
|
|
44
|
+
for file in args.assets:
|
|
45
|
+
find(args.target_path + "/" + file)
|
|
46
|
+
|
|
47
|
+
client = PsyncClient(args)
|
|
48
|
+
|
|
49
|
+
def run(code: int):
|
|
50
|
+
try:
|
|
51
|
+
asyncio.run(client.run())
|
|
52
|
+
except SystemExit as e:
|
|
53
|
+
assert str(e.code) == str(code)
|
|
54
|
+
|
|
55
|
+
if not kill:
|
|
56
|
+
run(0)
|
|
57
|
+
else:
|
|
58
|
+
p = multiprocessing.Process(target=run, args=[130])
|
|
59
|
+
p.start()
|
|
60
|
+
while p.pid is None:
|
|
61
|
+
pass
|
|
62
|
+
asyncio.run(asyncio.sleep(1))
|
|
63
|
+
os.kill(p.pid, Signals.SIGINT)
|
|
64
|
+
p.join(3)
|
|
65
|
+
|
|
66
|
+
# check that the pid closed
|
|
67
|
+
logs = get_logs(server)
|
|
68
|
+
|
|
69
|
+
pat = re.compile(r"Running process with PID (\d+)")
|
|
70
|
+
res = pat.search(logs.stderr)
|
|
71
|
+
if res is None:
|
|
72
|
+
raise Exception("Could not get PID from stdout!")
|
|
73
|
+
pid = res.group(1)
|
|
74
|
+
|
|
75
|
+
client_logs = OUTFILE.getvalue()
|
|
76
|
+
|
|
77
|
+
if args.args != []:
|
|
78
|
+
pat = re.compile(r"argv1=\w+")
|
|
79
|
+
res = pat.search(client_logs)
|
|
80
|
+
if res is None:
|
|
81
|
+
raise Exception("Did not find argv1 value!")
|
|
82
|
+
|
|
83
|
+
if args.env.get("TEST") is not None:
|
|
84
|
+
pat = re.compile(r"'TEST':\s*'TEST'")
|
|
85
|
+
res = pat.search(client_logs)
|
|
86
|
+
if res is None:
|
|
87
|
+
raise Exception("Env value TEST was not set!")
|
|
88
|
+
|
|
89
|
+
exec_result = server.exec(
|
|
90
|
+
["sh", "-c", f"ps -p {pid} > /dev/null; echo $?"],
|
|
91
|
+
)
|
|
92
|
+
assert exec_result.output.decode().strip() == "1"
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print(f"Got exception:\n {e}", file=sys.stderr)
|
|
96
|
+
logs = get_logs(server)
|
|
97
|
+
print(
|
|
98
|
+
f"Server logs:\n--- stdout ---\n{logs.stdout}\n--- stderr ---\n{logs.stderr}",
|
|
99
|
+
file=sys.stderr,
|
|
100
|
+
)
|
|
101
|
+
print(
|
|
102
|
+
f"OUTFILE:\n--- stdout ---\n{OUTFILE.getvalue()}",
|
|
103
|
+
file=sys.stderr,
|
|
104
|
+
)
|
|
105
|
+
assert False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_test_args(file: str, server: DockerContainer):
|
|
109
|
+
return ClientArgs(
|
|
110
|
+
target_path=assets_path.joinpath(file).__str__(),
|
|
111
|
+
ssh_args=f"-i {(assets_path / 'ssh-key').resolve()} -l psync -o StrictHostKeyChecking=no",
|
|
112
|
+
ssl_cert_path=(assets_path / "cert.pem").resolve().__str__(),
|
|
113
|
+
server_ip="127.0.0.1",
|
|
114
|
+
server_port=server.get_exposed_port(5000),
|
|
115
|
+
server_ssh_port=server.get_exposed_port(5022),
|
|
116
|
+
logfile=OUTFILE,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_basic(server: DockerContainer):
|
|
121
|
+
args = get_test_args("example_basic.py", server)
|
|
122
|
+
template(args, server)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_sigint(server: DockerContainer):
|
|
126
|
+
args = get_test_args("example.py", server)
|
|
127
|
+
template(args, server, True)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_env(server: DockerContainer):
|
|
131
|
+
args = get_test_args("example_basic.py", server)
|
|
132
|
+
args.env = {"PYTHONUNBUFERED": "1", "TEST": "TEST"}
|
|
133
|
+
template(args, server)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_assets(server: DockerContainer):
|
|
137
|
+
args = get_test_args("example_basic.py", server)
|
|
138
|
+
args.assets = ["./test/assets/wizard.png", "./test/assets/test-dir"]
|
|
139
|
+
template(args, server)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_args(server: DockerContainer):
|
|
143
|
+
args = get_test_args("example_basic.py", server)
|
|
144
|
+
args.args = ["test"]
|
|
145
|
+
template(args, server)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_full(server: DockerContainer):
|
|
149
|
+
args = get_test_args("example_basic.py", server)
|
|
150
|
+
args.args = ["test"]
|
|
151
|
+
args.assets = ["./test/assets/wizard.png", "./test/assets/test-dir"]
|
|
152
|
+
args.env = {"PYTHONUNBUFERED": "1", "TEST": "TEST"}
|
|
153
|
+
template(args, server)
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import multiprocessing
|
|
3
|
-
import os
|
|
4
|
-
from os.path import basename
|
|
5
|
-
import re
|
|
6
|
-
import sys
|
|
7
|
-
from signal import Signals
|
|
8
|
-
|
|
9
|
-
from testcontainers.core.generic import DockerContainer # pyright: ignore[reportMissingTypeStubs]
|
|
10
|
-
|
|
11
|
-
from client.args import Args as ClientArgs
|
|
12
|
-
from client.main import PsyncClient
|
|
13
|
-
from client.main import __rsync as rsync # pyright: ignore[reportPrivateUsage]
|
|
14
|
-
from test.conftest import assets_path
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def template(args: ClientArgs, server: DockerContainer, kill: bool = False):
|
|
18
|
-
try:
|
|
19
|
-
rsync(args)
|
|
20
|
-
exec_result = server.exec(["ls", str(args.destination_path())])
|
|
21
|
-
print(f"ls {args.destination_path()}\n --- \n {exec_result.output}")
|
|
22
|
-
assert exec_result.output.decode().__contains__(basename(args.target_path))
|
|
23
|
-
client = PsyncClient(args)
|
|
24
|
-
|
|
25
|
-
def run(code: int):
|
|
26
|
-
try:
|
|
27
|
-
asyncio.run(client.run())
|
|
28
|
-
except SystemExit as e:
|
|
29
|
-
assert str(e.code) == str(code)
|
|
30
|
-
|
|
31
|
-
if not kill:
|
|
32
|
-
run(0)
|
|
33
|
-
else:
|
|
34
|
-
p = multiprocessing.Process(target=run, args=[130])
|
|
35
|
-
p.start()
|
|
36
|
-
while p.pid is None:
|
|
37
|
-
pass
|
|
38
|
-
asyncio.run(asyncio.sleep(1))
|
|
39
|
-
os.kill(p.pid, Signals.SIGINT)
|
|
40
|
-
p.join(3)
|
|
41
|
-
|
|
42
|
-
# check that the pid closed
|
|
43
|
-
stdout, stderr = server.get_logs()
|
|
44
|
-
pat = re.compile(r"Running process with PID (\d+)")
|
|
45
|
-
res = pat.search(stderr.decode())
|
|
46
|
-
if res is None:
|
|
47
|
-
raise Exception("Could not get PID from stdout!")
|
|
48
|
-
pid = res.group(1)
|
|
49
|
-
|
|
50
|
-
exec_result = server.exec(
|
|
51
|
-
["sh", "-c", f"ps -p {pid} > /dev/null; echo $?"],
|
|
52
|
-
)
|
|
53
|
-
assert exec_result.output.decode().strip() == "1"
|
|
54
|
-
|
|
55
|
-
except Exception as e:
|
|
56
|
-
print(f"Got exception:\n {e}", file=sys.stderr)
|
|
57
|
-
stdout, stderr = server.get_logs()
|
|
58
|
-
print(
|
|
59
|
-
f"Server logs:\n--- stdout ---\n{stdout.decode()}\n--- stderr ---\n{stderr.decode()}",
|
|
60
|
-
file=sys.stderr,
|
|
61
|
-
)
|
|
62
|
-
assert False
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def get_test_args(file: str, server: DockerContainer):
|
|
66
|
-
return ClientArgs(
|
|
67
|
-
target_path=assets_path.joinpath(file).__str__(),
|
|
68
|
-
ssh_args=f"-i {(assets_path / 'ssh-key').resolve()} -l psync -o StrictHostKeyChecking=no",
|
|
69
|
-
ssl_cert_path=(assets_path / "cert.pem").resolve().__str__(),
|
|
70
|
-
server_ip="127.0.0.1",
|
|
71
|
-
server_port=server.get_exposed_port(5000),
|
|
72
|
-
server_ssh_port=server.get_exposed_port(5022),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def test_basic(server: DockerContainer):
|
|
77
|
-
args = get_test_args("example_basic.py", server)
|
|
78
|
-
template(args, server)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def test_sigint(server: DockerContainer):
|
|
82
|
-
args = get_test_args("example.py", server)
|
|
83
|
-
template(args, server, True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{cubething_psync-0.3.0 → cubething_psync-0.4.0}/src/cubething_psync.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|