1010import sys
1111import tempfile
1212import warnings
13+ from asyncio import StreamReader
1314from dataclasses import field , make_dataclass
1415from datetime import datetime , timedelta
1516from enum import EnumMeta
@@ -253,7 +254,8 @@ async def solutions(
253254 intermediate_solutions = False ,
254255 free_search : bool = False ,
255256 optimisation_level : Optional [int ] = None ,
256- ignore_errors = False ,
257+ verbose : bool = False ,
258+ debug_output : Optional [Path ] = None ,
257259 ** kwargs ,
258260 ):
259261 # Set standard command line arguments
@@ -319,6 +321,9 @@ async def solutions(
319321 if timeout is not None :
320322 cmd .extend (["--time-limit" , str (int (timeout .total_seconds () * 1000 ))])
321323
324+ if verbose :
325+ cmd .append ("-v" )
326+
322327 for flag , value in kwargs .items ():
323328 if not flag .startswith ("-" ):
324329 flag = "--" + flag
@@ -334,35 +339,23 @@ async def solutions(
334339 cmd .extend (files )
335340 # Run the MiniZinc process
336341 proc = await self ._driver .create_process (cmd , solver = solver )
337- assert proc .stderr is not None
338- assert proc .stdout is not None
342+ assert isinstance (proc .stderr , StreamReader )
343+ assert isinstance (proc .stdout , StreamReader )
344+
345+ # Python 3.7+: replace with asyncio.create_task
346+ read_stderr = asyncio .ensure_future (_read_all (proc .stderr ))
339347
340348 status = Status .UNKNOWN
341349 code = 0
342- deadline = None
343- if timeout is not None :
344- deadline = datetime .now () + timeout + timedelta (seconds = 5 )
345350
346351 remainder : Optional [bytes ] = None
347352 try :
348- raw_sol : bytes = b""
349- while not proc .stdout .at_eof ():
350- try :
351- if deadline is None :
352- raw_sol += await proc .stdout .readuntil (SEPARATOR )
353- else :
354- t = deadline - datetime .now ()
355- raw_sol += await asyncio .wait_for (
356- proc .stdout .readuntil (SEPARATOR ), t .total_seconds ()
357- )
358- status = Status .SATISFIED
359- solution , statistics = parse_solution (
360- raw_sol , self .output_type , self ._enum_map
361- )
362- yield Result (Status .SATISFIED , solution , statistics )
363- raw_sol = b""
364- except asyncio .LimitOverrunError as err :
365- raw_sol += await proc .stdout .readexactly (err .consumed )
353+ async for raw_sol in _seperate_solutions (proc .stdout , timeout ):
354+ status = Status .SATISFIED
355+ solution , statistics = parse_solution (
356+ raw_sol , self .output_type , self ._enum_map
357+ )
358+ yield Result (Status .SATISFIED , solution , statistics )
366359
367360 code = await proc .wait ()
368361 except asyncio .IncompleteReadError as err :
@@ -391,9 +384,14 @@ async def solutions(
391384 )
392385 yield Result (status , solution , statistics )
393386 # Raise error if required
387+ stderr = None
394388 if code != 0 or status == Status .ERROR :
395- stderr = await proc . stderr . read ()
389+ stderr = await read_stderr
396390 raise parse_error (stderr )
391+ if debug_output is not None :
392+ if stderr is None :
393+ stderr = await read_stderr
394+ debug_output .write_bytes (stderr )
397395
398396 async def solve_async (
399397 self ,
@@ -514,3 +512,34 @@ def add_file(self, file: ParPath, parse_data: bool = True) -> None:
514512 def add_string (self , code : str ) -> None :
515513 self ._reset_analysis ()
516514 return super ().add_string (code )
515+
516+
517+ async def _seperate_solutions (stream : StreamReader , timeout : Optional [timedelta ]):
518+ deadline = None
519+ if timeout is not None :
520+ deadline = datetime .now () + timeout + timedelta (seconds = 1 )
521+ solution : bytes = b""
522+ while not stream .at_eof ():
523+ try :
524+ if deadline is None :
525+ solution += await stream .readuntil (SEPARATOR )
526+ else :
527+ t = deadline - datetime .now ()
528+ solution += await asyncio .wait_for (
529+ stream .readuntil (SEPARATOR ), t .total_seconds ()
530+ )
531+ yield solution
532+ solution = b""
533+ except asyncio .LimitOverrunError as err :
534+ solution += await stream .readexactly (err .consumed )
535+
536+
537+ async def _read_all (stream : StreamReader ):
538+ output : bytes = b""
539+ while not stream .at_eof ():
540+ try :
541+ output += await stream .read ()
542+ return output
543+ except asyncio .LimitOverrunError as err :
544+ output += await stream .readexactly (err .consumed )
545+ return output
0 commit comments