-
-
Notifications
You must be signed in to change notification settings - Fork 390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Capture-solver-log #525
base: master
Are you sure you want to change the base?
Capture-solver-log #525
Changes from all commits
0029c44
6201443
b8d7d1c
997b446
5f30707
3034477
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,9 @@ | |
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.""" | ||
|
||
from .core import LpSolver_CMD, LpSolver, subprocess, PulpSolverError, clock, log | ||
import asyncio | ||
import sys | ||
from .core import LpSolver_CMD, LpSolver, PulpSolverError, clock, log | ||
from .core import cbc_path, pulp_cbc_path, coinMP_path, devnull | ||
import os | ||
from .. import constants | ||
|
@@ -176,31 +178,70 @@ def solve_CBC(self, lp, use_mps=True): | |
cmds += "initialSolve " | ||
cmds += "printingOptions all " | ||
cmds += "solution " + tmpSol + " " | ||
if self.msg: | ||
pipe = None | ||
else: | ||
pipe = open(os.devnull, "w") | ||
|
||
logPath = self.optionsDict.get("logPath") | ||
if logPath: | ||
if self.msg: | ||
warnings.warn( | ||
"`logPath` argument replaces `msg=1`. The output will be redirected to the log file." | ||
) | ||
pipe = open(self.optionsDict["logPath"], "w") | ||
|
||
log.debug(self.path + cmds) | ||
args = [] | ||
args.append(self.path) | ||
args.extend(cmds[1:].split()) | ||
cbc = subprocess.Popen(args, stdout=pipe, stderr=pipe, stdin=devnull) | ||
if cbc.wait() != 0: | ||
if pipe: | ||
pipe.close() | ||
|
||
class SubProcessRunner: | ||
def __init__(self, print_to_stdout, logPath=None): | ||
self.loop = self.get_event_loop() | ||
self.output = [] | ||
self.full_output = None | ||
self.print_to_stdout = print_to_stdout | ||
self.logPath = logPath or os.devnull | ||
|
||
@staticmethod | ||
def get_event_loop(): | ||
if sys.platform == "win32": | ||
loop = asyncio.ProactorEventLoop() | ||
else: | ||
loop = asyncio.new_event_loop() | ||
asyncio.set_event_loop(loop) | ||
return loop | ||
|
||
async def watch(self, stream): | ||
with open(self.logPath, "w") as f: | ||
async for line in stream: | ||
line = line.decode().strip() | ||
self.output.append(line) | ||
|
||
if self.print_to_stdout: | ||
print(line) | ||
f.write(line + "\n") | ||
|
||
async def run_sub(self, cmd): | ||
p = await asyncio.create_subprocess_exec( | ||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE | ||
) | ||
await asyncio.gather(self.watch(p.stdout)) | ||
await p.wait() | ||
return p.returncode | ||
|
||
def run(self, args): | ||
return_code = self.loop.run_until_complete(self.run_sub(args)) | ||
self.full_output = "\n".join(self.output) | ||
self.loop.close() | ||
return return_code | ||
|
||
sub_process_runner = SubProcessRunner(print_to_stdout=self.msg, logPath=logPath) | ||
return_code = sub_process_runner.run(args) | ||
|
||
if return_code != 0: | ||
print(return_code) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should remove this, otherwise this can break standardized logging |
||
raise PulpSolverError( | ||
"Pulp: Error while trying to execute, use msg=True for more details" | ||
+ self.path | ||
+ "return_code={}\noutput={}".format( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception would be hard to read with return code having no space before it, maybe newline here? Or remove this? I'd also suggest rewording the full exception, because the suggestion to |
||
return_code, sub_process_runner.full_output | ||
) | ||
) | ||
if pipe: | ||
pipe.close() | ||
|
||
self.solver_output = sub_process_runner.full_output | ||
|
||
if not os.path.exists(tmpSol): | ||
raise PulpSolverError("Pulp: Error while executing " + self.path) | ||
( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not the maintainer of the project so this is just an opinion, but to make testing easier, this class could be split out at a higher level and tested independently, rather than having the class declared inline.