Skip to content

Commit 76e4ef4

Browse files
committed
[TRLC] include argument to output to logfile instead of stdout
1 parent 2e58b5b commit 76e4ef4

2 files changed

Lines changed: 72 additions & 17 deletions

File tree

trlc/errors.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ class Message_Handler:
149149
:attribute brief: When true displays as much context as possible
150150
:type: Boolean
151151
152+
:attribute out: Output stream (stdout if None)
153+
:type: file or None
154+
155+
:attribute strip_prefix: Prefix stripped from file paths in messages
156+
:type: str or None
157+
152158
:attribute warnings: Number of system or user warnings raised
153159
:type: int
154160
@@ -158,16 +164,44 @@ class Message_Handler:
158164
:attribute supressed: Number of messages supressed by policy
159165
:type: int
160166
167+
Can be used as a context manager (``with Message_Handler(...) as
168+
mh:``), which will automatically close any file opened via
169+
``out_path``.
170+
161171
"""
162-
def __init__(self, brief=False, detailed_info=True):
172+
def __init__(self, brief=False, detailed_info=True,
173+
out=None, strip_prefix=None, out_path=None):
163174
assert isinstance(brief, bool)
175+
assert isinstance(strip_prefix, str) or strip_prefix is None
176+
assert isinstance(out_path, str) or out_path is None
164177
self.brief = brief
165178
self.show_details = detailed_info
166179
self.warnings = 0
167180
self.errors = 0
168181
self.suppressed = 0
169182
self.sm = None
170183
self.suppress_kind = set()
184+
self.strip_prefix = strip_prefix
185+
if out_path is not None:
186+
self._owned_file = open(out_path, "w", encoding="UTF-8") # pylint: disable=consider-using-with
187+
self.out = self._owned_file
188+
else:
189+
self._owned_file = None
190+
self.out = out
191+
192+
def close(self):
193+
if self._owned_file is not None:
194+
self._owned_file.close()
195+
self._owned_file = None
196+
197+
def __enter__(self):
198+
return self
199+
200+
def __exit__(self, *_):
201+
self.close()
202+
203+
def __del__(self):
204+
self.close()
171205

172206
def suppress(self, kind):
173207
assert isinstance(kind, Kind)
@@ -178,8 +212,7 @@ def cross_file_reference(self, location):
178212

179213
if self.sm is None:
180214
return location.to_string(include_column=False)
181-
else:
182-
return self.sm.cross_file_reference(location)
215+
return self.sm.cross_file_reference(location)
183216

184217
def emit(self,
185218
location,
@@ -195,15 +228,21 @@ def emit(self,
195228
assert isinstance(extrainfo, str) or extrainfo is None
196229
assert isinstance(category, str) or category is None
197230

231+
def _loc_str(include_column=True):
232+
loc = location.to_string(include_column)
233+
if self.strip_prefix and loc.startswith(self.strip_prefix):
234+
loc = loc[len(self.strip_prefix):]
235+
return loc
236+
198237
if self.brief:
199238
context = None
200-
msg = "%s: trlc %s: %s" % (location.to_string(),
239+
msg = "%s: trlc %s: %s" % (_loc_str(),
201240
str(kind),
202241
message)
203242

204243
else:
205244
context = location.context_lines()
206-
msg = "%s: %s: %s" % (location.to_string(len(context) == 0),
245+
msg = "%s: %s: %s" % (_loc_str(len(context) == 0),
207246
str(kind),
208247
message)
209248

@@ -216,10 +255,10 @@ def emit(self,
216255
else:
217256
if context:
218257
assert len(context) == 2
219-
print(context[0].replace("\t", " "))
220-
print(context[1].replace("\t", " "), msg)
258+
print(context[0].replace("\t", " "), file=self.out)
259+
print(context[1].replace("\t", " "), msg, file=self.out)
221260
else:
222-
print(msg)
261+
print(msg, file=self.out)
223262

224263
if not self.brief \
225264
and self.show_details \
@@ -230,7 +269,7 @@ def emit(self,
230269
indent = 0
231270
for line in extrainfo.splitlines():
232271
print("%s| %s" % (" " * indent,
233-
line.rstrip()))
272+
line.rstrip()), file=self.out)
234273

235274
if fatal:
236275
raise TRLC_Error(location, kind, message)

trlc/trlc.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ def trlc():
636636
action="store_true",
637637
help=("If there are no errors, produce a summary"
638638
" naming every file processed."))
639+
og_output.add_argument("--log",
640+
nargs = 2,
641+
metavar = ("FILE", "PREFIX"),
642+
default = None,
643+
help = ("Write all output to FILE, strip PREFIX"
644+
" from file paths in messages."
645+
" Intended for use as a Bazel build"
646+
" action."))
639647
og_output.add_argument("--error-on-warnings",
640648
action="store_true",
641649
help=("If there are warnings, return status code"
@@ -688,7 +696,10 @@ def trlc():
688696
except subprocess.CalledProcessError:
689697
ap.error("cannot run %s" % options.use_cvc5_binary)
690698

691-
mh = Message_Handler(options.brief, not options.no_detailed_info)
699+
mh = Message_Handler(options.brief,
700+
not options.no_detailed_info,
701+
out_path = options.log[0] if options.log else None,
702+
strip_prefix = options.log[1] if options.log else None)
692703

693704
if options.no_user_warnings: # pragma: no cover
694705
mh.suppress(Kind.USER_WARNING)
@@ -731,6 +742,7 @@ def trlc():
731742
ok &= sm.register_directory(".")
732743

733744
if not ok:
745+
mh.close()
734746
return 1
735747

736748
if sm.process() is None:
@@ -747,7 +759,7 @@ def trlc():
747759
if isinstance(tmp[obj.name][key], Fraction):
748760
tmp[obj.name][key] = float(tmp[obj.name][key])
749761

750-
print(json.dumps(tmp, indent=2, sort_keys=True))
762+
print(json.dumps(tmp, indent=2, sort_keys=True), file=mh.out)
751763

752764
total_models = len(sm.rsl_files)
753765
parsed_models = len([item
@@ -791,7 +803,7 @@ def count(parsed, total, what):
791803
if mh.suppressed: # pragma: no cover
792804
summary += " with %u supressed messages" % mh.suppressed
793805

794-
print(summary)
806+
print(summary, file=mh.out)
795807

796808
if options.show_file_list and ok: # pragma: no cover
797809
def get_status(parser):
@@ -807,21 +819,25 @@ def get_status(parser):
807819
print("> %s Model %s (Package %s)" %
808820
(get_status(parser),
809821
filename,
810-
parser.cu.package.name))
822+
parser.cu.package.name), file=mh.out)
811823
if not options.skip_trlc_files:
812824
for filename in sorted(sm.trlc_files):
813825
parser = sm.trlc_files[filename]
814826
print("> %s Requirements %s (Package %s)" %
815827
(get_status(parser),
816828
filename,
817-
parser.cu.package.name))
829+
parser.cu.package.name), file=mh.out)
818830

819831
if ok:
820832
if options.error_on_warnings and mh.warnings \
821833
or mh.errors: # pragma: no cover
822-
return 1
823-
return 0
824-
return 1
834+
rv = 1
835+
else:
836+
rv = 0
837+
else:
838+
rv = 1
839+
mh.close()
840+
return rv
825841

826842

827843
def main():

0 commit comments

Comments
 (0)