Skip to content

Commit 7337b68

Browse files
committed
Prove Python TUS path input source
1 parent b7af40f commit 7337b68

2 files changed

Lines changed: 140 additions & 1 deletion

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""Read a path-backed source as a TUS upload input."""
2+
3+
import sys
4+
from pathlib import Path
5+
from tempfile import TemporaryDirectory
6+
7+
from tusclient import client as tus
8+
9+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
10+
from api2devdock import (
11+
TusConformancePlanServer,
12+
conformance_input_options,
13+
conformance_input_source_bytes,
14+
conformance_input_source_kind,
15+
conformance_scenario_wants_event,
16+
fail,
17+
load_scenario,
18+
object_value,
19+
scenario_id,
20+
write_result,
21+
)
22+
23+
24+
def append_source_open_event(events, conformance_scenario, input_kind, size):
25+
if not conformance_scenario_wants_event(conformance_scenario, "source-open"):
26+
return events
27+
28+
return events + [
29+
{
30+
"inputKind": input_kind,
31+
"kind": "source-open",
32+
"size": size,
33+
}
34+
]
35+
36+
37+
def append_source_close_event(events, conformance_scenario):
38+
if not conformance_scenario_wants_event(conformance_scenario, "source-close"):
39+
return events
40+
41+
return events + [{"kind": "source-close"}]
42+
43+
44+
def append_success_event(events, conformance_scenario):
45+
if not conformance_scenario_wants_event(conformance_scenario, "success"):
46+
return events
47+
48+
return events + [{"kind": "success"}]
49+
50+
51+
def upload_with_node_path_input_source(conformance_scenario):
52+
input_options = conformance_input_options(conformance_scenario)
53+
content = conformance_input_source_bytes(conformance_scenario)
54+
input_kind = conformance_input_source_kind(conformance_scenario)
55+
endpoint_url = input_options["endpointUrl"]
56+
events = []
57+
58+
with TemporaryDirectory(prefix="api2-python-tus-node-path-input-source-") as tmp_dir:
59+
input_path = Path(tmp_dir) / "input.txt"
60+
input_path.write_bytes(content)
61+
events = append_source_open_event(
62+
events,
63+
conformance_scenario,
64+
input_kind,
65+
len(content),
66+
)
67+
68+
with TusConformancePlanServer(conformance_scenario, endpoint_url) as conformance_server:
69+
uploader = tus.TusClient(conformance_server.endpoint_url()).uploader(
70+
chunk_size=len(content),
71+
file_path=str(input_path),
72+
metadata=object_value(input_options["metadata"], "metadata"),
73+
)
74+
uploader.upload()
75+
events = append_success_event(events, conformance_scenario)
76+
77+
if not uploader.url:
78+
fail("node-path TUS upload did not expose an upload URL")
79+
if uploader.offset != len(content):
80+
fail(
81+
"node-path TUS upload offset {}, expected {}".format(
82+
uploader.offset,
83+
len(content),
84+
)
85+
)
86+
87+
events = append_source_close_event(events, conformance_scenario)
88+
conformance_server.assert_exhausted()
89+
result = conformance_server.result()
90+
result["events"] = events
91+
result["inputKind"] = input_kind
92+
result["uploadUrl"] = conformance_server.canonical_url(uploader.url)
93+
return result
94+
95+
96+
def main():
97+
scenario = load_scenario(Path(__file__).with_name("api2-scenario.json"))
98+
conformance_scenario = object_value(
99+
scenario["conformanceScenario"],
100+
"conformanceScenario",
101+
)
102+
result = upload_with_node_path_input_source(conformance_scenario)
103+
write_result(result)
104+
print(
105+
"Python TUS SDK devdock scenario {} read {} for {}".format(
106+
scenario_id(scenario),
107+
result["inputKind"],
108+
result["uploadUrl"],
109+
)
110+
)
111+
112+
113+
if __name__ == "__main__":
114+
main()

examples/api2devdock.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def conformance_input_source_bytes(conformance_scenario):
122122
"conformanceScenario.inputSource",
123123
)
124124
kind = string_value(input_source["kind"], "conformanceScenario.inputSource.kind")
125-
if kind != "blob":
125+
if kind not in ("blob", "node-path-reference"):
126126
fail("unsupported conformance input source kind {!r}".format(kind))
127127

128128
return string_value(
@@ -131,6 +131,31 @@ def conformance_input_source_bytes(conformance_scenario):
131131
).encode("utf-8")
132132

133133

134+
def conformance_input_source_kind(conformance_scenario):
135+
input_source = object_value(
136+
conformance_scenario["inputSource"],
137+
"conformanceScenario.inputSource",
138+
)
139+
return string_value(input_source["kind"], "conformanceScenario.inputSource.kind")
140+
141+
142+
def conformance_scenario_wants_event(conformance_scenario, event_kind):
143+
events = conformance_scenario.get("events", [])
144+
if not isinstance(events, list):
145+
fail("conformanceScenario.events must be a list")
146+
147+
for index, event in enumerate(events):
148+
event = object_value(event, "conformanceScenario.events[{}]".format(index))
149+
kind = string_value(
150+
event["kind"],
151+
"conformanceScenario.events[{}].kind".format(index),
152+
)
153+
if kind == event_kind:
154+
return True
155+
156+
return False
157+
158+
134159
def resolve_value(value_spec, context, label):
135160
if "value" in value_spec:
136161
return value_spec["value"]

0 commit comments

Comments
 (0)