modal-rb 0.0.0
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.
- checksums.yaml +7 -0
- data/lib/modal/api_client.rb +111 -0
- data/lib/modal/app.rb +65 -0
- data/lib/modal/cls.rb +131 -0
- data/lib/modal/config.rb +69 -0
- data/lib/modal/errors.rb +11 -0
- data/lib/modal/function.rb +122 -0
- data/lib/modal/function_call.rb +30 -0
- data/lib/modal/image.rb +75 -0
- data/lib/modal/invocation.rb +134 -0
- data/lib/modal/pickle.rb +50 -0
- data/lib/modal/sandbox.rb +340 -0
- data/lib/modal/sandbox_filesystem.rb +229 -0
- data/lib/modal/streams.rb +73 -0
- data/lib/modal/version.rb +3 -0
- data/lib/modal.rb +15 -0
- data/lib/modal_proto/api_pb.rb +480 -0
- data/lib/modal_proto/api_services_pb.rb +218 -0
- data/lib/modal_proto/options_pb.rb +18 -0
- metadata +58 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
module Modal
|
2
|
+
class SandboxFile
|
3
|
+
attr_reader :file_descriptor, :task_id
|
4
|
+
|
5
|
+
def initialize(file_descriptor, task_id)
|
6
|
+
@file_descriptor = file_descriptor
|
7
|
+
@task_id = task_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def read
|
11
|
+
request = Modal::Client::ContainerFilesystemExecRequest.new(
|
12
|
+
file_read_request: Modal::Client::ContainerFileReadRequest.new(
|
13
|
+
file_descriptor: @file_descriptor
|
14
|
+
),
|
15
|
+
task_id: @task_id
|
16
|
+
)
|
17
|
+
|
18
|
+
resp = Modal.client.call(:container_filesystem_exec, request)
|
19
|
+
exec_id = resp.exec_id
|
20
|
+
|
21
|
+
data = ""
|
22
|
+
completed = false
|
23
|
+
retries = 20
|
24
|
+
|
25
|
+
while !completed && retries > 0
|
26
|
+
begin
|
27
|
+
output_request = Modal::Client::ContainerFilesystemExecGetOutputRequest.new(
|
28
|
+
exec_id: exec_id,
|
29
|
+
timeout: 10
|
30
|
+
)
|
31
|
+
|
32
|
+
stream = Modal.client.call(:container_filesystem_exec_get_output, output_request)
|
33
|
+
|
34
|
+
stream.each do |batch|
|
35
|
+
|
36
|
+
if batch.respond_to?(:output) && batch.output && batch.output.any?
|
37
|
+
if batch.output.first.is_a?(String)
|
38
|
+
chunk = batch.output.join('')
|
39
|
+
elsif batch.output.first.is_a?(Integer)
|
40
|
+
chunk = batch.output.pack('C*')
|
41
|
+
else
|
42
|
+
chunk = batch.output.map(&:to_s).join('')
|
43
|
+
end
|
44
|
+
|
45
|
+
data += chunk.force_encoding('UTF-8')
|
46
|
+
end
|
47
|
+
|
48
|
+
if batch.respond_to?(:error) && batch.error
|
49
|
+
raise SandboxFilesystemError.new("Read failed: #{batch.error.error_message}")
|
50
|
+
end
|
51
|
+
|
52
|
+
if batch.respond_to?(:eof) && batch.eof
|
53
|
+
completed = true
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
unless completed
|
59
|
+
retries -= 1
|
60
|
+
sleep(0.1)
|
61
|
+
end
|
62
|
+
|
63
|
+
rescue GRPC::BadStatus => e
|
64
|
+
if e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
65
|
+
retries -= 1
|
66
|
+
sleep(0.1)
|
67
|
+
next
|
68
|
+
else
|
69
|
+
raise e
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
data
|
75
|
+
end
|
76
|
+
|
77
|
+
def write(data)
|
78
|
+
binary_data = if data.is_a?(String)
|
79
|
+
data.force_encoding('BINARY')
|
80
|
+
else
|
81
|
+
data.to_s.force_encoding('BINARY')
|
82
|
+
end
|
83
|
+
|
84
|
+
request = Modal::Client::ContainerFilesystemExecRequest.new(
|
85
|
+
file_write_request: Modal::Client::ContainerFileWriteRequest.new(
|
86
|
+
file_descriptor: @file_descriptor,
|
87
|
+
data: binary_data
|
88
|
+
),
|
89
|
+
task_id: @task_id
|
90
|
+
)
|
91
|
+
|
92
|
+
|
93
|
+
resp = Modal.client.call(:container_filesystem_exec, request)
|
94
|
+
exec_id = resp.exec_id
|
95
|
+
|
96
|
+
|
97
|
+
completed = false
|
98
|
+
retries = 20
|
99
|
+
|
100
|
+
while !completed && retries > 0
|
101
|
+
begin
|
102
|
+
output_request = Modal::Client::ContainerFilesystemExecGetOutputRequest.new(
|
103
|
+
exec_id: exec_id,
|
104
|
+
timeout: 10
|
105
|
+
)
|
106
|
+
|
107
|
+
stream = Modal.client.call(:container_filesystem_exec_get_output, output_request)
|
108
|
+
|
109
|
+
stream.each do |batch|
|
110
|
+
if batch.respond_to?(:error) && batch.error
|
111
|
+
raise SandboxFilesystemError.new("Write failed: #{batch.error.error_message}")
|
112
|
+
end
|
113
|
+
if batch.respond_to?(:eof) && batch.eof
|
114
|
+
completed = true
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
unless completed
|
120
|
+
retries -= 1
|
121
|
+
sleep(0.1)
|
122
|
+
end
|
123
|
+
|
124
|
+
rescue GRPC::BadStatus => e
|
125
|
+
if e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
126
|
+
retries -= 1
|
127
|
+
sleep(0.1)
|
128
|
+
next
|
129
|
+
else
|
130
|
+
raise e
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
unless completed
|
136
|
+
end
|
137
|
+
|
138
|
+
data.length
|
139
|
+
end
|
140
|
+
|
141
|
+
def flush
|
142
|
+
request = Modal::Client::ContainerFilesystemExecRequest.new(
|
143
|
+
file_flush_request: Modal::Client::ContainerFileFlushRequest.new(
|
144
|
+
file_descriptor: @file_descriptor
|
145
|
+
),
|
146
|
+
task_id: @task_id
|
147
|
+
)
|
148
|
+
|
149
|
+
resp = Modal.client.call(:container_filesystem_exec, request)
|
150
|
+
exec_id = resp.exec_id
|
151
|
+
|
152
|
+
retries = 10
|
153
|
+
|
154
|
+
while retries > 0
|
155
|
+
begin
|
156
|
+
output_request = Modal::Client::ContainerFilesystemExecGetOutputRequest.new(
|
157
|
+
exec_id: exec_id,
|
158
|
+
timeout: 5
|
159
|
+
)
|
160
|
+
|
161
|
+
stream = Modal.client.call(:container_filesystem_exec_get_output, output_request)
|
162
|
+
stream.each do |batch|
|
163
|
+
if batch.respond_to?(:error) && batch.error
|
164
|
+
raise SandboxFilesystemError.new("Flush failed: #{batch.error.error_message}")
|
165
|
+
end
|
166
|
+
if batch.respond_to?(:eof) && batch.eof
|
167
|
+
return
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
retries -= 1
|
172
|
+
sleep(0.1)
|
173
|
+
|
174
|
+
rescue GRPC::BadStatus => e
|
175
|
+
if e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
176
|
+
retries -= 1
|
177
|
+
next
|
178
|
+
else
|
179
|
+
raise e
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def close
|
186
|
+
request = Modal::Client::ContainerFilesystemExecRequest.new(
|
187
|
+
file_close_request: Modal::Client::ContainerFileCloseRequest.new(
|
188
|
+
file_descriptor: @file_descriptor
|
189
|
+
),
|
190
|
+
task_id: @task_id
|
191
|
+
)
|
192
|
+
|
193
|
+
resp = Modal.client.call(:container_filesystem_exec, request)
|
194
|
+
exec_id = resp.exec_id
|
195
|
+
|
196
|
+
retries = 10
|
197
|
+
|
198
|
+
while retries > 0
|
199
|
+
begin
|
200
|
+
output_request = Modal::Client::ContainerFilesystemExecGetOutputRequest.new(
|
201
|
+
exec_id: exec_id,
|
202
|
+
timeout: 5
|
203
|
+
)
|
204
|
+
|
205
|
+
stream = Modal.client.call(:container_filesystem_exec_get_output, output_request)
|
206
|
+
stream.each do |batch|
|
207
|
+
if batch.respond_to?(:error) && batch.error
|
208
|
+
raise SandboxFilesystemError.new("Close failed: #{batch.error.error_message}")
|
209
|
+
end
|
210
|
+
if batch.respond_to?(:eof) && batch.eof
|
211
|
+
return
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
retries -= 1
|
216
|
+
sleep(0.1)
|
217
|
+
|
218
|
+
rescue GRPC::BadStatus => e
|
219
|
+
if e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
220
|
+
retries -= 1
|
221
|
+
next
|
222
|
+
else
|
223
|
+
raise e
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Modal
|
4
|
+
class ModalReadStream
|
5
|
+
def initialize(source_iterable)
|
6
|
+
@source_iterable = source_iterable
|
7
|
+
end
|
8
|
+
|
9
|
+
def read
|
10
|
+
read_text
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_text
|
14
|
+
chunks = []
|
15
|
+
@source_iterable.each do |bytes|
|
16
|
+
chunks << bytes.dup.force_encoding('UTF-8')
|
17
|
+
end
|
18
|
+
chunks.join('')
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_bytes
|
22
|
+
chunks = []
|
23
|
+
@source_iterable.each do |bytes|
|
24
|
+
chunks << bytes.dup
|
25
|
+
end
|
26
|
+
chunks.join('').bytes.pack('C*')
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
@source_iterable.each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def close
|
34
|
+
@source_iterable.close if @source_iterable.respond_to?(:close)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ModalWriteStream
|
39
|
+
def initialize(sink_writable)
|
40
|
+
@sink_writable = sink_writable
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def write(data)
|
45
|
+
if data.is_a?(String)
|
46
|
+
if data.encoding == Encoding::BINARY
|
47
|
+
write_bytes(data)
|
48
|
+
else
|
49
|
+
write_text(data)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
|
53
|
+
write_bytes(data.to_s)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def write_text(text)
|
58
|
+
@sink_writable.write(text)
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_bytes(bytes)
|
62
|
+
@sink_writable.write(bytes)
|
63
|
+
end
|
64
|
+
|
65
|
+
def close
|
66
|
+
@sink_writable.close if @sink_writable.respond_to?(:close)
|
67
|
+
end
|
68
|
+
|
69
|
+
def flush
|
70
|
+
@sink_writable.flush if @sink_writable.respond_to?(:flush)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/modal.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Modal
|
2
|
+
require 'modal_proto/api_services_pb'
|
3
|
+
|
4
|
+
require 'modal/version'
|
5
|
+
require 'modal/errors'
|
6
|
+
require 'modal/config'
|
7
|
+
require 'modal/api_client'
|
8
|
+
require 'modal/app'
|
9
|
+
require 'modal/cls'
|
10
|
+
require 'modal/image'
|
11
|
+
require 'modal/function'
|
12
|
+
require 'modal/function_call'
|
13
|
+
require 'modal/sandbox'
|
14
|
+
require 'modal/streams'
|
15
|
+
end
|