|
1 | 1 | #!/usr/bin/env python3 |
2 | | -import os |
3 | | -import gc |
| 2 | +"""Thin wrapper for backward compatibility. Calls udapi.cli.main().""" |
4 | 3 | import sys |
5 | | -import atexit |
6 | | -import logging |
7 | | -import argparse |
| 4 | +from udapi.cli import main |
8 | 5 |
|
9 | | -from udapi.core.run import Run |
10 | | - |
11 | | -# Parse command line arguments. |
12 | | -argparser = argparse.ArgumentParser( |
13 | | - formatter_class=argparse.RawTextHelpFormatter, |
14 | | - usage="udapy [optional_arguments] scenario", |
15 | | - epilog="See http://udapi.github.io", |
16 | | - description="udapy - Python interface to Udapi - API for Universal Dependencies\n\n" |
17 | | - "Examples of usage:\n" |
18 | | - " udapy -s read.Sentences udpipe.En < in.txt > out.conllu\n" |
19 | | - " udapy -T < sample.conllu | less -R\n" |
20 | | - " udapy -HAM ud.MarkBugs < sample.conllu > bugs.html\n") |
21 | | -argparser.add_argument( |
22 | | - "-q", "--quiet", action="store_true", |
23 | | - help="Warning, info and debug messages are suppressed. Only fatal errors are reported.") |
24 | | -argparser.add_argument( |
25 | | - "-v", "--verbose", action="store_true", |
26 | | - help="Warning, info and debug messages are printed to the STDERR.") |
27 | | -argparser.add_argument( |
28 | | - "-s", "--save", action="store_true", |
29 | | - help="Add write.Conllu to the end of the scenario") |
30 | | -argparser.add_argument( |
31 | | - "-T", "--save_text_mode_trees", action="store_true", |
32 | | - help="Add write.TextModeTrees color=1 to the end of the scenario") |
33 | | -argparser.add_argument( |
34 | | - "-H", "--save_html", action="store_true", |
35 | | - help="Add write.TextModeTreesHtml color=1 to the end of the scenario") |
36 | | -argparser.add_argument( |
37 | | - "-A", "--save_all_attributes", action="store_true", |
38 | | - help="Add attributes=form,lemma,upos,xpos,feats,deprel,misc (to be used after -T and -H)") |
39 | | -argparser.add_argument( |
40 | | - "-C", "--save_comments", action="store_true", |
41 | | - help="Add print_comments=1 (to be used after -T and -H)") |
42 | | -argparser.add_argument( |
43 | | - "-M", "--marked_only", action="store_true", |
44 | | - help="Add marked_only=1 to the end of the scenario (to be used after -T and -H)") |
45 | | -argparser.add_argument( |
46 | | - "-N", "--no_color", action="store_true", |
47 | | - help="Add color=0 to the end of the scenario, this overrides color=1 of -T and -H") |
48 | | -argparser.add_argument( |
49 | | - "-X", "--extra", action="append", |
50 | | - help="Add a specified parameter (or a block name) to the end of the scenario\n" |
51 | | - "For example 'udapy -TNX attributes=form,misc -X layout=align < my.conllu'") |
52 | | -argparser.add_argument( |
53 | | - "--gc", action="store_true", |
54 | | - help="By default, udapy disables Python garbage collection and at-exit cleanup\n" |
55 | | - "to speed up everything (especially reading CoNLL-U files). In edge cases,\n" |
56 | | - "when processing many files and running out of memory, you can disable this\n" |
57 | | - "optimization (i.e. enable garbage collection) with 'udapy --gc'.") |
58 | | -argparser.add_argument( |
59 | | - 'scenario', nargs=argparse.REMAINDER, help="A sequence of blocks and their parameters.") |
60 | | - |
61 | | -args = argparser.parse_args() |
62 | | - |
63 | | -# Set the level of logs according to parameters. |
64 | | -if args.verbose: |
65 | | - level = logging.DEBUG |
66 | | -elif args.quiet: |
67 | | - level = logging.CRITICAL |
68 | | -else: |
69 | | - level = logging.INFO |
70 | | - |
71 | | -logging.basicConfig(format='%(asctime)-15s [%(levelname)7s] %(funcName)s - %(message)s', |
72 | | - level=level) |
73 | | - |
74 | | -# Global flag to track if an unhandled exception occurred |
75 | | -_unhandled_exception_occurred = False |
76 | | - |
77 | | -def _custom_excepthook(exc_type, exc_value, traceback): |
78 | | - global _unhandled_exception_occurred |
79 | | - _unhandled_exception_occurred = True |
80 | | - |
81 | | - # Call the default excepthook to allow normal error reporting |
82 | | - sys.__excepthook__(exc_type, exc_value, traceback) |
83 | | - |
84 | | -# Override the default excepthook |
85 | | -sys.excepthook = _custom_excepthook |
86 | | - |
87 | | - |
88 | | -# Process and provide the scenario. |
89 | 6 | if __name__ == "__main__": |
90 | | - |
91 | | - # Disabling garbage collections makes the whole processing much faster. |
92 | | - # Similarly, we can save several seconds by partially disabling the at-exit Python cleanup |
93 | | - # (atexit hooks are called in reversed order of their registration, |
94 | | - # so flushing stdio buffers etc. will be still done before the os._exit(0) call). |
95 | | - # See https://instagram-engineering.com/dismissing-python-garbage-collection-at-instagram-4dca40b29172 |
96 | | - # Is it safe to disable GC? |
97 | | - # OS will free the memory allocated by this process after it ends anyway. |
98 | | - # The udapy wrapper is aimed for one-time tasks, not a long-running server, |
99 | | - # so in a typical case a document is loaded and almost no memory is freed before the end. |
100 | | - # Udapi documents have a many cyclic references, so running GC is quite slow. |
101 | | - if not args.gc: |
102 | | - gc.disable() |
103 | | - # When an exception/error has happened, udapy should exit with a non-zero exit code, |
104 | | - # so that users can use `udapy ... || echo "Error detected"` (or Makefile reports errors). |
105 | | - # However, we cannot use `atexit.register(lambda: os._exit(1 if sys.exc_info()[0] else 0))` |
106 | | - # because the Python has already exited the exception-handling block |
107 | | - # (the exception/error has been already reported and sys.exc_info()[0] is None). |
108 | | - # We thus keep record whether _unhandled_exception_occurred. |
109 | | - atexit.register(lambda: os._exit(1 if _unhandled_exception_occurred else 0)) |
110 | | - atexit.register(sys.stderr.flush) |
111 | | - if args.save: |
112 | | - args.scenario = args.scenario + ['write.Conllu'] |
113 | | - if args.save_text_mode_trees: |
114 | | - args.scenario = args.scenario + ['write.TextModeTrees', 'color=1'] |
115 | | - if args.save_html: |
116 | | - args.scenario = args.scenario + ['write.TextModeTreesHtml', 'color=1'] |
117 | | - if args.save_all_attributes: |
118 | | - args.scenario = args.scenario + ['attributes=form,lemma,upos,xpos,feats,deprel,misc'] |
119 | | - if args.save_comments: |
120 | | - args.scenario = args.scenario + ['print_comments=1'] |
121 | | - if args.marked_only: |
122 | | - args.scenario = args.scenario + ['marked_only=1'] |
123 | | - if args.no_color: |
124 | | - args.scenario = args.scenario + ['color=0'] |
125 | | - if args.extra: |
126 | | - args.scenario += args.extra |
127 | | - |
128 | | - runner = Run(args) |
129 | | - # udapy is often piped to head etc., e.g. |
130 | | - # `seq 1000 | udapy -s read.Sentences | head` |
131 | | - # Let's prevent Python from reporting (with distracting stacktrace) |
132 | | - # "BrokenPipeError: [Errno 32] Broken pipe" |
133 | | - try: |
134 | | - runner.execute() |
135 | | - except BrokenPipeError: |
136 | | - pass |
| 7 | + sys.exit(main()) |
0 commit comments