Spaces:
Runtime error
Runtime error
#!/usr/bin/env python3 | |
# -*- coding: utf-8; -*- | |
import argparse; | |
import json; | |
import multiprocessing as mp; | |
import re; | |
import sys; | |
import time; | |
from pathlib import Path; | |
from zipfile import ZipFile; | |
import codec.amr; | |
import codec.conllu; | |
import codec.eds; | |
import codec.mrp; | |
import codec.norec; | |
import codec.pmb; | |
import codec.sdp; | |
import codec.treex; | |
import codec.ucca; | |
import inspector; | |
import score.edm; | |
import score.mces; | |
import score.sdp; | |
import score.smatch; | |
import score.ucca; | |
import validate.core; | |
from analyzer import analyze; | |
__author__ = "oe" | |
ENCODING = "utf-8"; | |
NORMALIZATIONS = {"anchors", "case", "edges", "attributes"}; | |
VALIDATIONS = {"input", "anchors", "edges", | |
"amr", "eds", "sdp", "ucca"} | |
def read_graphs(stream, format = None, | |
full = False, normalize = False, reify = False, node_centric = False, | |
frameworks = None, prefix = None, text = None, filter = None, | |
trace = 0, strict = 0, quiet = False, robust = False, | |
alignment = None, anchors = None, pretty = False, | |
id = None, n = None, i = None): | |
name = getattr(stream, "name", ""); | |
if name.endswith(".zip"): | |
with ZipFile(name) as zip: | |
stream = None; | |
for entry in zip.namelist(): | |
if entry.endswith(".mrp"): | |
if stream is not None: | |
print("read_graphs(): multiple MRP entries in ‘{}’; exit." | |
"".format(name), file = sys.stderr); | |
sys.exit(1); | |
stream = zip.open(entry); | |
if stream is None: | |
print("read_graphs(): missing MRP entry in ‘{}’; exit." | |
"".format(name), file = sys.stderr); | |
sys.exit(1); | |
generator = None; | |
if format in {"amr", "camr"}: | |
generator \ | |
= codec.amr.read(stream, full = full, reify = reify, | |
text = text, camr = format == "camr", | |
alignment = alignment, quiet = quiet, trace = trace); | |
elif format in {"ccd", "dm", "pas", "psd"}: | |
generator = codec.sdp.read(stream, framework = format, text = text); | |
elif format == "eds": | |
generator = codec.eds.read(stream, reify = reify, text = text); | |
elif format == "mrp": | |
generator = codec.mrp.read(stream, text = text, robust = robust); | |
elif format == "norec": | |
generator = codec.norec.read(stream, text = text, node_centric = node_centric); | |
elif format == "pmb": | |
generator = codec.pmb.read(stream, full = full, | |
reify = reify, text = text, | |
trace = trace, strict = strict); | |
elif format == "treex": | |
generator = codec.treex.read(stream) | |
elif format == "ucca": | |
generator = codec.ucca.read(stream, text = text, prefix = prefix); | |
elif format == "conllu" or format == "ud": | |
generator = codec.conllu.read(stream, framework = format, text = text, | |
anchors = anchors, trace = trace); | |
elif format == "eud": | |
generator = codec.conllu.read(stream, framework = format, text = text, | |
anchors = anchors, trace = trace, | |
enhanced_graph = True); | |
else: | |
print("read_graphs(): invalid input codec {}; exit." | |
"".format(format), file = sys.stderr); | |
sys.exit(1); | |
if generator is None: | |
return None, None; | |
# | |
# (for now) break out of the generators, for downstream simplicity | |
# | |
graphs = []; | |
overlays = []; | |
j = 0; | |
while n is None or n < 1 or j < n: | |
try: | |
graph, overlay = next(generator); | |
if frameworks is not None and graph.framework not in frameworks: continue; | |
if filter is not None and graph.id not in filter: continue; | |
if id is not None: | |
if graph.id == id: | |
graphs.append(graph); overlays.append(overlay); | |
elif i is not None and i >= 0: | |
if j == i: | |
graphs.append(graph); overlays.append(overlay); | |
break; | |
else: | |
graphs.append(graph); overlays.append(overlay); | |
j += 1; | |
except StopIteration: | |
break; | |
except Exception as error: | |
print(error, file = sys.stderr); | |
pass; | |
if pretty: | |
for graph in graphs: graph.prettify(trace); | |
if normalize: | |
for graph in graphs: graph.normalize(normalize, trace); | |
return graphs, overlays; | |
def main(args=None): | |
parser = argparse.ArgumentParser(description = "MRP Graph Toolkit"); | |
parser.add_argument("--inspect", action = "store_true"); | |
parser.add_argument("--analyze", action = "store_true"); | |
parser.add_argument("--normalize", action = "append", default = []); | |
parser.add_argument("--full", action = "store_true"); | |
parser.add_argument("--reify", action = "store_true"); | |
parser.add_argument("--node_centric", action = "store_true"); | |
parser.add_argument("--unique", action = "store_true"); | |
parser.add_argument("--ids", action = "store_true"); | |
parser.add_argument("--strings", action = "store_true"); | |
parser.add_argument("--framework", action = "append", default = []); | |
parser.add_argument("--gold", | |
type = argparse.FileType("r", encoding = ENCODING)); | |
parser.add_argument("--alignment", | |
type = argparse.FileType("r", encoding = ENCODING)); | |
parser.add_argument("--overlay", | |
type = argparse.FileType("w", encoding = ENCODING)); | |
parser.add_argument("--format"); | |
parser.add_argument("--score"); | |
parser.add_argument("--validate", action = "append", default = []); | |
parser.add_argument("--limit"); | |
parser.add_argument("--read", required = True); | |
parser.add_argument("--write"); | |
parser.add_argument("--text"); | |
parser.add_argument("--inverse", action = "store_true"); | |
parser.add_argument("--anchors", | |
type = argparse.FileType("r", encoding = ENCODING)); | |
parser.add_argument("--prefix"); | |
parser.add_argument("--source"); | |
parser.add_argument("--targets"); | |
parser.add_argument("--pretty", action = "store_true"); | |
parser.add_argument("--inject"); | |
parser.add_argument("--version", type = float, default = 1.1); | |
parser.add_argument("--cores", type = int, default = 1); | |
parser.add_argument("--i", type = int); | |
parser.add_argument("--n", type = int); | |
parser.add_argument("--id"); | |
parser.add_argument("--filter"); | |
parser.add_argument("--quiet", action = "store_true"); | |
parser.add_argument("--robust", action = "store_true"); | |
parser.add_argument("--trace", "-t", action = "count", default = 0); | |
parser.add_argument("--strict", action = "count", default = 0); | |
parser.add_argument("--errors", | |
type = argparse.FileType("w", encoding = ENCODING)); | |
parser.add_argument("input", nargs = "?", | |
type = argparse.FileType("r", encoding = ENCODING), | |
default = sys.stdin); | |
parser.add_argument("output", nargs = "?", | |
type = argparse.FileType("w", encoding = ENCODING), | |
default = sys.stdout); | |
if args is None: | |
args = sys.argv | |
arguments = parser.parse_args(args); | |
text = None; | |
if arguments.text is not None: | |
path = Path(arguments.text); | |
if path.is_file(): | |
text = {}; | |
with path.open() as stream: | |
for line in stream: | |
id, string = line.split("\t", maxsplit = 1); | |
if string.endswith("\n"): string = string[:len(string) - 1]; | |
if arguments.inverse: text[string] = id; | |
else: text[id] = string; | |
elif path.is_dir(): | |
text = path; | |
elif arguments.inverse: | |
print("main.py(): option ‘--inverse’ requires ‘--text’; exit.", | |
file = sys.stderr); | |
sys.exit(1); | |
if arguments.read not in {"mrp", | |
"ccd", "dm", "pas", "psd", "treex", | |
"eds", "ucca", | |
"amr", "camr", "pmb", | |
"conllu", "ud", "eud", | |
"norec"}: | |
print("main.py(): invalid input format: {}; exit." | |
"".format(arguments.read), file = sys.stderr); | |
sys.exit(1); | |
filter = None; | |
if arguments.filter is not None: | |
try: | |
path = Path(arguments.filter); | |
filter = set(); | |
with path.open() as stream: | |
for line in stream: | |
filter.add(line.split("\t", maxsplit = 1)[0]); | |
except: | |
print("main.py(): invalid ‘--filter’: {}; exit." | |
"".format(arguments.write), file = sys.stderr); | |
sys.exit(1); | |
if filter is not None and len(filter) == 0: filter = None; | |
if arguments.write is not None and \ | |
arguments.write not in \ | |
{"dot", "tikz", "displacy", "evaluation", "id", "json", "mrp", | |
"source", "targets", "txt", "ucca", "norec"}: | |
print("main.py(): invalid output format: {}; exit." | |
"".format(arguments.write), file = sys.stderr); | |
sys.exit(1); | |
# | |
# backwards compatibility: desirable until august 2019, say | |
# | |
if arguments.score == "mces": arguments.score = "mrp"; | |
if arguments.score is not None and \ | |
arguments.score not in {"mrp", "sdp", "edm", "ucca", "smatch"}: | |
print("main.py(): invalid evaluation metric: {}; exit." | |
"".format(arguments.score), file = sys.stderr); | |
sys.exit(1); | |
if arguments.format and \ | |
arguments.format not in {"mrp", | |
"ccd", "dm", "pas", "psd", | |
"eds", "ucca", | |
"amr", "camr", "pmb", | |
"conllu", "ud", "eud"}: | |
print("main.py(): invalid gold format: {}; exit." | |
"".format(arguments.read), file = sys.stderr); | |
sys.exit(1); | |
if len(arguments.normalize) == 1 and arguments.normalize[0] == "all": | |
normalize = NORMALIZATIONS; | |
else: | |
normalize = set(); | |
for action in arguments.normalize: | |
if action in NORMALIZATIONS: | |
normalize.add(action); | |
else: | |
print("main.py(): invalid type of normalization: {}; exit." | |
"".format(action), file = sys.stderr); | |
sys.exit(1); | |
if arguments.score is not None and len(normalize) == 0: | |
normalize = NORMALIZATIONS; | |
if arguments.targets == "gather" and not arguments.unique: | |
print("main.py(): option ‘--targets gather’ requires ‘--unique’; exit.", | |
file = sys.stderr); | |
sys.exit(1); | |
if arguments.alignment is not None and arguments.overlay is None: | |
print("main.py(): option ‘--alignment’ requires ‘--overlay’; exit.", | |
file = sys.stderr); | |
sys.exit(1); | |
if len(arguments.framework) == 0: arguments.framework = None; | |
if arguments.cores == 0: arguments.cores = mp.cpu_count(); | |
graphs, overlays \ | |
= read_graphs(arguments.input, format = arguments.read, | |
full = arguments.full, normalize = normalize, | |
reify = arguments.reify, frameworks = arguments.framework, | |
text = text, filter = filter, alignment = arguments.alignment, | |
anchors = arguments.anchors, pretty = arguments.pretty, | |
trace = arguments.trace, strict = arguments.strict, node_centric = arguments.node_centric, | |
quiet = arguments.quiet, robust = arguments.robust, | |
id = arguments.id, n = arguments.n, i = arguments.i); | |
if graphs is None: | |
print("main.py(): unable to read input graphs: {}; exit." | |
"".format(arguments.input.name), file = sys.stderr); | |
sys.exit(1); | |
if arguments.unique: | |
targets = dict(); | |
if arguments.targets == "gather": | |
for graph in graphs: | |
if graph.id in targets: targets[graph.id].add(graph.framework); | |
else: targets[graph.id] = {graph.framework}; | |
arguments.targets = None; | |
unique = list(); | |
ids = set(); | |
for graph in graphs: | |
id = graph.id; | |
if id in targets: graph.targets(list(targets[id])); | |
if id not in ids: | |
ids.add(id); | |
unique.append(graph); | |
graphs = unique; | |
# | |
# inject any additional information provided on the command line | |
# | |
if arguments.source: | |
for graph in graphs: graph.source(arguments.source); | |
if arguments.inject: | |
for graph in graphs: graph.inject(arguments.inject); | |
if arguments.validate == ["all"]: | |
actions = VALIDATIONS; | |
else: | |
actions = set(); | |
for action in arguments.validate: | |
if action in VALIDATIONS: | |
actions.add(action); | |
else: | |
print("main.py(): invalid type of validation: {}; exit." | |
"".format(action), file = sys.stderr); | |
sys.exit(1); | |
if arguments.quiet: arguments.trace = 0; | |
if actions: | |
for graph in graphs: | |
validate.core.test(graph, actions, stream = sys.stderr); | |
if arguments.analyze: | |
analyze(graphs); | |
gold = None; | |
if arguments.gold and arguments.score or arguments.inspect: | |
if arguments.format is None: arguments.format = arguments.read; | |
gold, _ = read_graphs(arguments.gold, format = arguments.format, | |
full = arguments.full, normalize = normalize, | |
reify = arguments.reify, node_centric = arguments.node_centric, | |
frameworks = arguments.framework, | |
text = text, filter = filter, | |
trace = arguments.trace, quiet = arguments.quiet, | |
robust = arguments.robust, | |
id = arguments.id, n = arguments.n, i = arguments.i); | |
if gold is None: | |
print("main.py(): unable to read gold graphs: {}; exit." | |
"".format(arguments.gold.name), file = sys.stderr); | |
sys.exit(1); | |
if arguments.inspect: | |
result = inspector.summarize(graphs, gold); | |
if arguments.write == "json" or True: | |
json.dump(result, arguments.output, indent = None); | |
print(file = arguments.output); | |
sys.exit(0); | |
if arguments.score: | |
limits = {"rrhc": None, "mces": None}; | |
for metric in arguments.score.split(","): | |
if arguments.limit is not None: | |
try: | |
match = re.search(r"([0-9]+):([0-9]+)", arguments.limit) | |
if match: | |
limits["rrhc"] = int(match.group(1)); | |
limits["mces"] = int(match.group(2)); | |
else: | |
if metric == "smatch": | |
limits["rrhc"] = int(arguments.limit); | |
else: | |
limits["mces"] = int(arguments.limit); | |
except: | |
print("main.py(): invalid ‘--limit’ {}; exit." | |
"".format(arguments.limit), | |
file = sys.stderr); | |
sys.exit(1); | |
errors = dict() if arguments.errors else None; | |
result = None; | |
launch = time.time(), time.process_time(); | |
if metric == "edm": | |
result = score.edm.evaluate(gold, graphs, | |
format = arguments.write, | |
trace = arguments.trace); | |
elif metric == "mrp": | |
result = score.mces.evaluate(gold, graphs, | |
format = arguments.write, | |
limits = limits, | |
cores = arguments.cores, | |
trace = arguments.trace, | |
errors = errors, | |
quiet = arguments.quiet); | |
elif metric == "sdp": | |
result = score.sdp.evaluate(gold, graphs, | |
format = arguments.write, | |
trace = arguments.trace); | |
elif metric == "smatch": | |
result = score.smatch.evaluate(gold, graphs, | |
format = arguments.write, | |
limit = limits["rrhc"], | |
values = {"tops", "labels", | |
"properties", "anchors", | |
"edges", "attributes"}, | |
trace = arguments.trace); | |
elif metric == "ucca": | |
result = score.ucca.evaluate(gold, graphs, | |
format = arguments.write, | |
trace = arguments.trace); | |
if result is not None: | |
result["time"] = time.time() - launch[0]; | |
result["cpu"] = time.process_time() - launch[1]; | |
if arguments.write == "json" or True: | |
# | |
# _fix_me_ | |
# we should write a genuine custom JSON encoder | |
# | |
print("{", file = arguments.output, end = ""); | |
start = True; | |
for key in result: | |
if start: start = False; | |
else: print(",\n ", file = arguments.output, end = ""); | |
print("\"{}\": ".format(key), file = arguments.output, end = ""); | |
json.dump(result[key], arguments.output, indent = None); | |
print("}", file = arguments.output); | |
if errors is not None: | |
if arguments.write == "dot": | |
for graph in gold: | |
graph.dot(arguments.errors, | |
ids = arguments.ids, strings = arguments.strings, | |
errors = errors[graph.framework][graph.id]); | |
elif arguments.write == "json" or True: | |
json.dump(errors, arguments.errors, indent = None); | |
sys.exit(0); | |
for graph in graphs: | |
if arguments.write in {"mrp", "evaluation"}: | |
if arguments.write == "evaluation": | |
graph.flavor = graph.framework = graph.nodes = graph.edges = None; | |
if arguments.targets is not None: | |
graph.targets(arguments.targets.split(",")); | |
json.dump(graph.encode(arguments.version), arguments.output, | |
indent = None, ensure_ascii = False); | |
print(file = arguments.output); | |
elif arguments.write == "dot": | |
graph.dot(arguments.output, | |
ids = arguments.ids, strings = arguments.strings); | |
print(file = arguments.output); | |
elif arguments.write == "tikz": | |
graph.tikz(arguments.output); | |
elif arguments.write == "displacy": | |
graph.displacy(arguments.output); | |
elif arguments.write == "id": | |
print("{}".format(graph.id), file = arguments.output); | |
elif arguments.write == "source": | |
print("{}\t{}".format(graph.id, graph.source()), file = arguments.output); | |
elif arguments.write == "targets": | |
for target in graph.targets() or (""): | |
print("{}\t{}".format(graph.id, target), file = arguments.output); | |
elif arguments.write == "txt": | |
print("{}\t{}".format(graph.id, graph.input), file = arguments.output); | |
elif arguments.write == "ucca": | |
# Prints everything to one long file. To split to separate XML files, use, e.g., | |
# csplit -zk output.xml '/^<root/' -f '' -b '%02d.xml' {99} | |
codec.ucca.write(graph, graph.input, file = arguments.output) | |
if arguments.write == "norec": | |
norec_graphs = [codec.norec.write(graph, graph.input, node_centric = arguments.node_centric) for graph in graphs] | |
json.dump(norec_graphs, arguments.output, indent=None) | |
if arguments.overlay: | |
for graph in overlays: | |
if graph: | |
json.dump(graph.encode(arguments.version), arguments.overlay, | |
indent = None, ensure_ascii = False); | |
print(file = arguments.overlay); | |
if __name__ == "__main__": | |
main(); | |