11from typing import Optional , Union
22
3- from .base_types import Flavor , TestCase
3+ from .base_types import Flavor , TestCase , LanguageAlias
44from .clients import Client
55from .retry import RegularPeriodRetry , RetryMechanism
66from .submission import Submission
77
8+ from .filesystem import Filesystem , File
9+ import textwrap
10+
811
912def resolve_client (
1013 client : Optional [Union [Client , Flavor ]] = None ,
@@ -133,13 +136,111 @@ def create_submissions_from_test_cases(
133136 return all_submissions
134137
135138
139+ def safe_format (f , arg ):
140+ try :
141+ return (f or "" ) % (arg or "" )
142+ except TypeError :
143+ return f or ""
144+
145+
146+ def create_fs (client , submission ):
147+ fs = Filesystem (submission .additional_files )
148+ language_id = client .get_language_id (submission .language )
149+ if language_id != LanguageAlias .MULTI_FILE : # Multi-file program
150+ language = client .get_language (language_id )
151+ fs .files .append (File (language ["source_file" ], submission .source_code ))
152+ fs .files .append (
153+ File (
154+ "compile.sh" ,
155+ safe_format (language ["compile_cmd" ], submission .compiler_options ),
156+ )
157+ )
158+ fs .files .append (
159+ File ("run.sh" , f'{ language ["run_cmd" ]} { submission .command_line_arguments } ' )
160+ )
161+ return fs
162+
163+
164+ def create_submission_with_interactive_producer (
165+ client , submission , interactive_producer
166+ ):
167+ interactive_producer .command_line_arguments = "/box/stdin.fifo /box/stdout.fifo"
168+
169+ consumer_fs = create_fs (client , submission )
170+ producer_fs = create_fs (client , interactive_producer )
171+
172+ final_fs = Filesystem ()
173+ for f in consumer_fs .files :
174+ final_fs .files .append (File (f"./consumer/{ f .name } " , f .content ))
175+
176+ for f in producer_fs .files :
177+ final_fs .files .append (File (f"./producer/{ f .name } " , f .content ))
178+
179+ final_fs .files .append (
180+ File (
181+ "compile.sh" ,
182+ textwrap .dedent (
183+ """
184+ cd /box/consumer && bash compile.sh
185+ cd /box/producer && bash compile.sh
186+ """
187+ ),
188+ )
189+ )
190+
191+ final_fs .files .append (
192+ File (
193+ "run.sh" ,
194+ textwrap .dedent (
195+ """
196+ mkfifo /box/stdin.fifo /box/stdout.fifo
197+
198+ cd /box/consumer
199+ tee >(bash run.sh 2> /box/user.stderr | tee /box/stdout.fifo /box/user.stdout &> /dev/null) /box/user.stdin < /box/stdin.fifo &> /dev/null &
200+
201+ cd /box/producer
202+ bash run.sh < /dev/stdin &
203+ PRODUCER_PID=$!
204+
205+ wait $PRODUCER_PID
206+
207+ rm -f /box/stdin.fifo /box/stdout.fifo
208+ """
209+ ),
210+ )
211+ )
212+
213+ return Submission (
214+ source_code = "" ,
215+ additional_files = final_fs ,
216+ language = LanguageAlias .MULTI_FILE ,
217+ )
218+
219+
220+ def create_submissions_with_interactive_producer (
221+ client , submissions , interactive_producer
222+ ):
223+ if isinstance (submissions , Submission ):
224+ return create_submission_with_interactive_producer (
225+ client , submissions , interactive_producer
226+ )
227+ else :
228+ return [
229+ create_submission_with_interactive_producer (
230+ client , submission , interactive_producer
231+ )
232+ for submission in submissions
233+ ]
234+
235+
136236def _execute (
137237 * ,
138238 client : Optional [Union [Client , Flavor ]] = None ,
139239 submissions : Optional [Union [Submission , list [Submission ]]] = None ,
140240 source_code : Optional [str ] = None ,
141241 wait_for_result : bool = False ,
142242 test_cases : Optional [Union [TestCase , list [TestCase ]]] = None ,
243+ interactive_producer : Optional [Submission ] = None ,
143244 ** kwargs ,
144245) -> Union [Submission , list [Submission ]]:
145246 if submissions is not None and source_code is not None :
@@ -164,6 +265,11 @@ def _execute(
164265
165266 client = resolve_client (client , submissions = submissions )
166267
268+ if interactive_producer is not None :
269+ submissions = create_submissions_with_interactive_producer (
270+ client , submissions , interactive_producer
271+ )
272+
167273 all_submissions = create_submissions_from_test_cases (submissions , test_cases )
168274
169275 # We differentiate between creating a single submission and multiple
@@ -189,6 +295,7 @@ def async_execute(
189295 submissions : Optional [Union [Submission , list [Submission ]]] = None ,
190296 source_code : Optional [str ] = None ,
191297 test_cases : Optional [Union [TestCase , list [TestCase ]]] = None ,
298+ interactive_producer : Optional [Submission ] = None ,
192299 ** kwargs ,
193300) -> Union [Submission , list [Submission ]]:
194301 return _execute (
@@ -197,6 +304,7 @@ def async_execute(
197304 source_code = source_code ,
198305 wait_for_result = False ,
199306 test_cases = test_cases ,
307+ interactive_producer = interactive_producer ,
200308 ** kwargs ,
201309 )
202310
@@ -207,6 +315,7 @@ def sync_execute(
207315 submissions : Optional [Union [Submission , list [Submission ]]] = None ,
208316 source_code : Optional [str ] = None ,
209317 test_cases : Optional [Union [TestCase , list [TestCase ]]] = None ,
318+ interactive_producer : Optional [Submission ] = None ,
210319 ** kwargs ,
211320) -> Union [Submission , list [Submission ]]:
212321 return _execute (
@@ -215,6 +324,7 @@ def sync_execute(
215324 source_code = source_code ,
216325 wait_for_result = True ,
217326 test_cases = test_cases ,
327+ interactive_producer = interactive_producer ,
218328 ** kwargs ,
219329 )
220330
0 commit comments