We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug with multiple source files and data_dir configuration
Sample When you add the export_vocabulary_assets with multiple files
```
model_dir: model

data:
  # (optional) During export save the vocabularies as model assets, otherwise embed
  # them in the graph itself (default: True).
  export_vocabulary_assets: true

  train_features_file:
    - train/src.txt
    - train/ner.txt
```

the crash:
```
  File "/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py", line 200, in main
    config["data"] = _prefix_paths(args.data_dir, config["data"])
  File "/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py", line 42, in _prefix_paths
    paths[key] = _prefix_paths(prefix, path)
  File "/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py", line 50, in _prefix_paths
    new_path = os.path.join(prefix, path)
  File "/usr/lib/python3.6/posixpath.py", line 94, in join
    genericpath._check_arg_types('join', a, *p)
  File "/usr/lib/python3.6/genericpath.py", line 149, in _check_arg_types
    (funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'bool'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opennmt/bin/main.py`
Content:
```
1 """Main script."""
2 
3 import argparse
4 import logging
5 import os
6 import sys
7 
8 import tensorflow as tf
9 
10 from opennmt import __version__
11 from opennmt.models import catalog
12 from opennmt.runner import Runner
13 from opennmt.config import load_model, load_config
14 from opennmt.utils import exporters
15 
16 
17 _PYTHON_TO_TENSORFLOW_LOGGING_LEVEL = {
18     logging.CRITICAL: 3,
19     logging.ERROR: 2,
20     logging.WARNING: 1,
21     logging.INFO: 0,
22     logging.DEBUG: 0,
23     logging.NOTSET: 0,
24 }
25 
26 def _set_log_level(log_level):
27   tf.get_logger().setLevel(log_level)
28   os.environ["TF_CPP_MIN_LOG_LEVEL"] = str(_PYTHON_TO_TENSORFLOW_LOGGING_LEVEL[log_level])
29 
30 def _prefix_paths(prefix, paths):
31   """Recursively prefix paths.
32 
33   Args:
34     prefix: The prefix to apply.
35     data: A dict of relative paths.
36 
37   Returns:
38     The updated dict.
39   """
40   if isinstance(paths, dict):
41     for key, path in paths.items():
42       paths[key] = _prefix_paths(prefix, path)
43     return paths
44   elif isinstance(paths, list):
45     for i, path in enumerate(paths):
46       paths[i] = _prefix_paths(prefix, path)
47     return paths
48   else:
49     path = paths
50     new_path = os.path.join(prefix, path)
51     if tf.io.gfile.exists(new_path):
52       return new_path
53     else:
54       return path
55 
56 def main():
57   parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
58   parser.add_argument("-v", "--version", action="version", version="OpenNMT-tf %s" % __version__)
59   parser.add_argument("--config", required=True, nargs="+",
60                       help="List of configuration files.")
61   parser.add_argument("--auto_config", default=False, action="store_true",
62                       help="Enable automatic configuration values.")
63   parser.add_argument("--model_type", default="",
64                       choices=list(sorted(catalog.list_model_names_from_catalog())),
65                       help="Model type from the catalog.")
66   parser.add_argument("--model", default="",
67                       help="Custom model configuration file.")
68   parser.add_argument("--run_dir", default="",
69                       help="If set, model_dir will be created relative to this location.")
70   parser.add_argument("--data_dir", default="",
71                       help="If set, data files are expected to be relative to this location.")
72   parser.add_argument("--checkpoint_path", default=None,
73                       help=("Specific checkpoint or model directory to load "
74                             "(when a directory is set, the latest checkpoint is used)."))
75   parser.add_argument("--log_level", default="INFO",
76                       choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"],
77                       help="Logs verbosity.")
78   parser.add_argument("--seed", type=int, default=None,
79                       help="Random seed.")
80   parser.add_argument("--gpu_allow_growth", default=False, action="store_true",
81                       help="Allocate GPU memory dynamically.")
82   parser.add_argument("--intra_op_parallelism_threads", type=int, default=0,
83                       help=("Number of intra op threads (0 means the system picks "
84                             "an appropriate number)."))
85   parser.add_argument("--inter_op_parallelism_threads", type=int, default=0,
86                       help=("Number of inter op threads (0 means the system picks "
87                             "an appropriate number)."))
88   parser.add_argument("--mixed_precision", default=False, action="store_true",
89                       help="Enable mixed precision.")
90 
91   subparsers = parser.add_subparsers(help="Run type.", dest="run_type")
92   subparsers.required = True
93   parser_train = subparsers.add_parser("train", help="Training.")
94   parser_train.add_argument(
95       "--with_eval", default=False, action="store_true",
96       help="Enable automatic evaluation.")
97   parser_train.add_argument(
98       "--num_gpus", type=int, default=1,
99       help="Number of GPUs to use for in-graph replication.")
100   parser_train.add_argument(
101       "--horovod", default=False, action="store_true",
102       help="Enable Horovod training mode.")
103 
104   parser_eval = subparsers.add_parser("eval", help="Evaluation.")
105   parser_eval.add_argument(
106       "--features_file", nargs="+", default=None,
107       help="Input features files.")
108   parser_eval.add_argument(
109       "--labels_file", default=None,
110       help="Output labels files.")
111 
112   parser_infer = subparsers.add_parser("infer", help="Inference.")
113   parser_infer.add_argument(
114       "--features_file", nargs="+", required=True,
115       help="Run inference on this file.")
116   parser_infer.add_argument(
117       "--predictions_file", default="",
118       help=("File used to save predictions. If not set, predictions are printed "
119             "on the standard output."))
120   parser_infer.add_argument(
121       "--log_prediction_time", default=False, action="store_true",
122       help="Logs some prediction time metrics.")
123 
124   parser_export = subparsers.add_parser("export", help="Model export.")
125   parser_export.add_argument(
126       "--export_dir", required=True,
127       help="The directory of the exported model.")
128   parser_export.add_argument(
129       "--export_format", choices=exporters.list_exporters(), default="saved_model",
130       help="Format of the exported model.")
131 
132   parser_score = subparsers.add_parser("score", help="Scoring.")
133   parser_score.add_argument("--features_file", nargs="+", required=True,
134                             help="Features file.")
135   parser_score.add_argument("--predictions_file", default=None,
136                             help="Predictions to score.")
137 
138   parser_average_checkpoints = subparsers.add_parser(
139       "average_checkpoints", help="Checkpoint averaging.")
140   parser_average_checkpoints.add_argument(
141       "--output_dir", required=True,
142       help="The output directory for the averaged checkpoint.")
143   parser_average_checkpoints.add_argument(
144       "--max_count", type=int, default=8,
145       help="The maximal number of checkpoints to average.")
146 
147   parser_update_vocab = subparsers.add_parser(
148       "update_vocab", help="Update model vocabularies in checkpoint.")
149   parser_update_vocab.add_argument(
150       "--output_dir", required=True,
151       help="The output directory for the updated checkpoint.")
152   parser_update_vocab.add_argument(
153       "--src_vocab", default=None,
154       help="Path to the new source vocabulary.")
155   parser_update_vocab.add_argument(
156       "--tgt_vocab", default=None,
157       help="Path to the new target vocabulary.")
158 
159   # When using an option that takes multiple values just before the run type,
160   # the run type is treated as a value of this option. To fix this issue, we
161   # inject a placeholder option just before the run type to clearly separate it.
162   parser.add_argument("--placeholder", action="store_true", help=argparse.SUPPRESS)
163   run_types = set(subparsers.choices.keys())
164   args = sys.argv[1:]
165   for i, arg in enumerate(args):
166     if arg in run_types:
167       args.insert(i, "--placeholder")
168       break
169 
170   args = parser.parse_args(args)
171   if hasattr(args, "features_file") and args.features_file and len(args.features_file) == 1:
172     args.features_file = args.features_file[0]
173 
174   _set_log_level(getattr(logging, args.log_level))
175   tf.config.threading.set_intra_op_parallelism_threads(args.intra_op_parallelism_threads)
176   tf.config.threading.set_inter_op_parallelism_threads(args.inter_op_parallelism_threads)
177 
178   gpus = tf.config.list_physical_devices(device_type="GPU")
179   if hasattr(args, "horovod") and args.horovod:
180     import horovod.tensorflow as hvd  # pylint: disable=import-outside-toplevel
181     hvd.init()
182     is_master = hvd.rank() == 0
183     if gpus:
184       local_gpu = gpus[hvd.local_rank()]
185       tf.config.experimental.set_visible_devices(local_gpu, device_type="GPU")
186       gpus = [local_gpu]
187   else:
188     hvd = None
189     is_master = True
190 
191   if args.gpu_allow_growth:
192     for device in gpus:
193       tf.config.experimental.set_memory_growth(device, enable=True)
194 
195   # Load and merge run configurations.
196   config = load_config(args.config)
197   if args.run_dir:
198     config["model_dir"] = os.path.join(args.run_dir, config["model_dir"])
199   if args.data_dir:
200     config["data"] = _prefix_paths(args.data_dir, config["data"])
201 
202   if is_master and not tf.io.gfile.exists(config["model_dir"]):
203     tf.get_logger().info("Creating model directory %s", config["model_dir"])
204     tf.io.gfile.makedirs(config["model_dir"])
205 
206   model = load_model(
207       config["model_dir"],
208       model_file=args.model,
209       model_name=args.model_type,
210       serialize_model=is_master,
211       as_builder=True)
212   runner = Runner(
213       model,
214       config,
215       auto_config=args.auto_config,
216       mixed_precision=args.mixed_precision,
217       seed=args.seed)
218 
219   if args.run_type == "train":
220     runner.train(
221         num_devices=args.num_gpus,
222         with_eval=args.with_eval,
223         checkpoint_path=args.checkpoint_path,
224         hvd=hvd)
225   elif args.run_type == "eval":
226     metrics = runner.evaluate(
227         checkpoint_path=args.checkpoint_path,
228         features_file=args.features_file,
229         labels_file=args.labels_file)
230     print(metrics)
231   elif args.run_type == "infer":
232     runner.infer(
233         args.features_file,
234         predictions_file=args.predictions_file,
235         checkpoint_path=args.checkpoint_path,
236         log_time=args.log_prediction_time)
237   elif args.run_type == "export":
238     runner.export(
239         args.export_dir,
240         checkpoint_path=args.checkpoint_path,
241         exporter=exporters.make_exporter(args.export_format))
242   elif args.run_type == "score":
243     runner.score(
244         args.features_file,
245         args.predictions_file,
246         checkpoint_path=args.checkpoint_path)
247   elif args.run_type == "average_checkpoints":
248     runner.average_checkpoints(args.output_dir, max_count=args.max_count)
249   elif args.run_type == "update_vocab":
250     runner.update_vocab(
251         args.output_dir,
252         src_vocab=args.src_vocab,
253         tgt_vocab=args.tgt_vocab)
254 
255 
256 if __name__ == "__main__":
257   main()
258
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/opennmt/bin/main.py b/opennmt/bin/main.py
--- a/opennmt/bin/main.py
+++ b/opennmt/bin/main.py
@@ -45,13 +45,15 @@
     for i, path in enumerate(paths):
       paths[i] = _prefix_paths(prefix, path)
     return paths
-  else:
+  elif isinstance(paths, str):
     path = paths
     new_path = os.path.join(prefix, path)
     if tf.io.gfile.exists(new_path):
       return new_path
     else:
       return path
+  else:
+    return paths
 
 def main():
   parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)

{"golden_diff": "diff --git a/opennmt/bin/main.py b/opennmt/bin/main.py\n--- a/opennmt/bin/main.py\n+++ b/opennmt/bin/main.py\n@@ -45,13 +45,15 @@\n     for i, path in enumerate(paths):\n       paths[i] = _prefix_paths(prefix, path)\n     return paths\n-  else:\n+  elif isinstance(paths, str):\n     path = paths\n     new_path = os.path.join(prefix, path)\n     if tf.io.gfile.exists(new_path):\n       return new_path\n     else:\n       return path\n+  else:\n+    return paths\n \n def main():\n   parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n", "issue": "Bug with multiple source files and data_dir configuration\nSample When you add the export_vocabulary_assets with multiple files\r\n```\r\nmodel_dir: model\r\n\r\ndata:\r\n  # (optional) During export save the vocabularies as model assets, otherwise embed\r\n  # them in the graph itself (default: True).\r\n  export_vocabulary_assets: true\r\n\r\n  train_features_file:\r\n    - train/src.txt\r\n    - train/ner.txt\r\n```\r\n\r\nthe crash:\r\n```\r\n  File \"/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py\", line 200, in main\r\n    config[\"data\"] = _prefix_paths(args.data_dir, config[\"data\"])\r\n  File \"/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py\", line 42, in _prefix_paths\r\n    paths[key] = _prefix_paths(prefix, path)\r\n  File \"/usr/local/lib/python3.6/dist-packages/opennmt/bin/main.py\", line 50, in _prefix_paths\r\n    new_path = os.path.join(prefix, path)\r\n  File \"/usr/lib/python3.6/posixpath.py\", line 94, in join\r\n    genericpath._check_arg_types('join', a, *p)\r\n  File \"/usr/lib/python3.6/genericpath.py\", line 149, in _check_arg_types\r\n    (funcname, s.__class__.__name__)) from None\r\nTypeError: join() argument must be str or bytes, not 'bool'\r\n```\n", "before_files": [{"content": "\"\"\"Main script.\"\"\"\n\nimport argparse\nimport logging\nimport os\nimport sys\n\nimport tensorflow as tf\n\nfrom opennmt import __version__\nfrom opennmt.models import catalog\nfrom opennmt.runner import Runner\nfrom opennmt.config import load_model, load_config\nfrom opennmt.utils import exporters\n\n\n_PYTHON_TO_TENSORFLOW_LOGGING_LEVEL = {\n    logging.CRITICAL: 3,\n    logging.ERROR: 2,\n    logging.WARNING: 1,\n    logging.INFO: 0,\n    logging.DEBUG: 0,\n    logging.NOTSET: 0,\n}\n\ndef _set_log_level(log_level):\n  tf.get_logger().setLevel(log_level)\n  os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = str(_PYTHON_TO_TENSORFLOW_LOGGING_LEVEL[log_level])\n\ndef _prefix_paths(prefix, paths):\n  \"\"\"Recursively prefix paths.\n\n  Args:\n    prefix: The prefix to apply.\n    data: A dict of relative paths.\n\n  Returns:\n    The updated dict.\n  \"\"\"\n  if isinstance(paths, dict):\n    for key, path in paths.items():\n      paths[key] = _prefix_paths(prefix, path)\n    return paths\n  elif isinstance(paths, list):\n    for i, path in enumerate(paths):\n      paths[i] = _prefix_paths(prefix, path)\n    return paths\n  else:\n    path = paths\n    new_path = os.path.join(prefix, path)\n    if tf.io.gfile.exists(new_path):\n      return new_path\n    else:\n      return path\n\ndef main():\n  parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\"-v\", \"--version\", action=\"version\", version=\"OpenNMT-tf %s\" % __version__)\n  parser.add_argument(\"--config\", required=True, nargs=\"+\",\n                      help=\"List of configuration files.\")\n  parser.add_argument(\"--auto_config\", default=False, action=\"store_true\",\n                      help=\"Enable automatic configuration values.\")\n  parser.add_argument(\"--model_type\", default=\"\",\n                      choices=list(sorted(catalog.list_model_names_from_catalog())),\n                      help=\"Model type from the catalog.\")\n  parser.add_argument(\"--model\", default=\"\",\n                      help=\"Custom model configuration file.\")\n  parser.add_argument(\"--run_dir\", default=\"\",\n                      help=\"If set, model_dir will be created relative to this location.\")\n  parser.add_argument(\"--data_dir\", default=\"\",\n                      help=\"If set, data files are expected to be relative to this location.\")\n  parser.add_argument(\"--checkpoint_path\", default=None,\n                      help=(\"Specific checkpoint or model directory to load \"\n                            \"(when a directory is set, the latest checkpoint is used).\"))\n  parser.add_argument(\"--log_level\", default=\"INFO\",\n                      choices=[\"CRITICAL\", \"ERROR\", \"WARNING\", \"INFO\", \"DEBUG\", \"NOTSET\"],\n                      help=\"Logs verbosity.\")\n  parser.add_argument(\"--seed\", type=int, default=None,\n                      help=\"Random seed.\")\n  parser.add_argument(\"--gpu_allow_growth\", default=False, action=\"store_true\",\n                      help=\"Allocate GPU memory dynamically.\")\n  parser.add_argument(\"--intra_op_parallelism_threads\", type=int, default=0,\n                      help=(\"Number of intra op threads (0 means the system picks \"\n                            \"an appropriate number).\"))\n  parser.add_argument(\"--inter_op_parallelism_threads\", type=int, default=0,\n                      help=(\"Number of inter op threads (0 means the system picks \"\n                            \"an appropriate number).\"))\n  parser.add_argument(\"--mixed_precision\", default=False, action=\"store_true\",\n                      help=\"Enable mixed precision.\")\n\n  subparsers = parser.add_subparsers(help=\"Run type.\", dest=\"run_type\")\n  subparsers.required = True\n  parser_train = subparsers.add_parser(\"train\", help=\"Training.\")\n  parser_train.add_argument(\n      \"--with_eval\", default=False, action=\"store_true\",\n      help=\"Enable automatic evaluation.\")\n  parser_train.add_argument(\n      \"--num_gpus\", type=int, default=1,\n      help=\"Number of GPUs to use for in-graph replication.\")\n  parser_train.add_argument(\n      \"--horovod\", default=False, action=\"store_true\",\n      help=\"Enable Horovod training mode.\")\n\n  parser_eval = subparsers.add_parser(\"eval\", help=\"Evaluation.\")\n  parser_eval.add_argument(\n      \"--features_file\", nargs=\"+\", default=None,\n      help=\"Input features files.\")\n  parser_eval.add_argument(\n      \"--labels_file\", default=None,\n      help=\"Output labels files.\")\n\n  parser_infer = subparsers.add_parser(\"infer\", help=\"Inference.\")\n  parser_infer.add_argument(\n      \"--features_file\", nargs=\"+\", required=True,\n      help=\"Run inference on this file.\")\n  parser_infer.add_argument(\n      \"--predictions_file\", default=\"\",\n      help=(\"File used to save predictions. If not set, predictions are printed \"\n            \"on the standard output.\"))\n  parser_infer.add_argument(\n      \"--log_prediction_time\", default=False, action=\"store_true\",\n      help=\"Logs some prediction time metrics.\")\n\n  parser_export = subparsers.add_parser(\"export\", help=\"Model export.\")\n  parser_export.add_argument(\n      \"--export_dir\", required=True,\n      help=\"The directory of the exported model.\")\n  parser_export.add_argument(\n      \"--export_format\", choices=exporters.list_exporters(), default=\"saved_model\",\n      help=\"Format of the exported model.\")\n\n  parser_score = subparsers.add_parser(\"score\", help=\"Scoring.\")\n  parser_score.add_argument(\"--features_file\", nargs=\"+\", required=True,\n                            help=\"Features file.\")\n  parser_score.add_argument(\"--predictions_file\", default=None,\n                            help=\"Predictions to score.\")\n\n  parser_average_checkpoints = subparsers.add_parser(\n      \"average_checkpoints\", help=\"Checkpoint averaging.\")\n  parser_average_checkpoints.add_argument(\n      \"--output_dir\", required=True,\n      help=\"The output directory for the averaged checkpoint.\")\n  parser_average_checkpoints.add_argument(\n      \"--max_count\", type=int, default=8,\n      help=\"The maximal number of checkpoints to average.\")\n\n  parser_update_vocab = subparsers.add_parser(\n      \"update_vocab\", help=\"Update model vocabularies in checkpoint.\")\n  parser_update_vocab.add_argument(\n      \"--output_dir\", required=True,\n      help=\"The output directory for the updated checkpoint.\")\n  parser_update_vocab.add_argument(\n      \"--src_vocab\", default=None,\n      help=\"Path to the new source vocabulary.\")\n  parser_update_vocab.add_argument(\n      \"--tgt_vocab\", default=None,\n      help=\"Path to the new target vocabulary.\")\n\n  # When using an option that takes multiple values just before the run type,\n  # the run type is treated as a value of this option. To fix this issue, we\n  # inject a placeholder option just before the run type to clearly separate it.\n  parser.add_argument(\"--placeholder\", action=\"store_true\", help=argparse.SUPPRESS)\n  run_types = set(subparsers.choices.keys())\n  args = sys.argv[1:]\n  for i, arg in enumerate(args):\n    if arg in run_types:\n      args.insert(i, \"--placeholder\")\n      break\n\n  args = parser.parse_args(args)\n  if hasattr(args, \"features_file\") and args.features_file and len(args.features_file) == 1:\n    args.features_file = args.features_file[0]\n\n  _set_log_level(getattr(logging, args.log_level))\n  tf.config.threading.set_intra_op_parallelism_threads(args.intra_op_parallelism_threads)\n  tf.config.threading.set_inter_op_parallelism_threads(args.inter_op_parallelism_threads)\n\n  gpus = tf.config.list_physical_devices(device_type=\"GPU\")\n  if hasattr(args, \"horovod\") and args.horovod:\n    import horovod.tensorflow as hvd  # pylint: disable=import-outside-toplevel\n    hvd.init()\n    is_master = hvd.rank() == 0\n    if gpus:\n      local_gpu = gpus[hvd.local_rank()]\n      tf.config.experimental.set_visible_devices(local_gpu, device_type=\"GPU\")\n      gpus = [local_gpu]\n  else:\n    hvd = None\n    is_master = True\n\n  if args.gpu_allow_growth:\n    for device in gpus:\n      tf.config.experimental.set_memory_growth(device, enable=True)\n\n  # Load and merge run configurations.\n  config = load_config(args.config)\n  if args.run_dir:\n    config[\"model_dir\"] = os.path.join(args.run_dir, config[\"model_dir\"])\n  if args.data_dir:\n    config[\"data\"] = _prefix_paths(args.data_dir, config[\"data\"])\n\n  if is_master and not tf.io.gfile.exists(config[\"model_dir\"]):\n    tf.get_logger().info(\"Creating model directory %s\", config[\"model_dir\"])\n    tf.io.gfile.makedirs(config[\"model_dir\"])\n\n  model = load_model(\n      config[\"model_dir\"],\n      model_file=args.model,\n      model_name=args.model_type,\n      serialize_model=is_master,\n      as_builder=True)\n  runner = Runner(\n      model,\n      config,\n      auto_config=args.auto_config,\n      mixed_precision=args.mixed_precision,\n      seed=args.seed)\n\n  if args.run_type == \"train\":\n    runner.train(\n        num_devices=args.num_gpus,\n        with_eval=args.with_eval,\n        checkpoint_path=args.checkpoint_path,\n        hvd=hvd)\n  elif args.run_type == \"eval\":\n    metrics = runner.evaluate(\n        checkpoint_path=args.checkpoint_path,\n        features_file=args.features_file,\n        labels_file=args.labels_file)\n    print(metrics)\n  elif args.run_type == \"infer\":\n    runner.infer(\n        args.features_file,\n        predictions_file=args.predictions_file,\n        checkpoint_path=args.checkpoint_path,\n        log_time=args.log_prediction_time)\n  elif args.run_type == \"export\":\n    runner.export(\n        args.export_dir,\n        checkpoint_path=args.checkpoint_path,\n        exporter=exporters.make_exporter(args.export_format))\n  elif args.run_type == \"score\":\n    runner.score(\n        args.features_file,\n        args.predictions_file,\n        checkpoint_path=args.checkpoint_path)\n  elif args.run_type == \"average_checkpoints\":\n    runner.average_checkpoints(args.output_dir, max_count=args.max_count)\n  elif args.run_type == \"update_vocab\":\n    runner.update_vocab(\n        args.output_dir,\n        src_vocab=args.src_vocab,\n        tgt_vocab=args.tgt_vocab)\n\n\nif __name__ == \"__main__\":\n  main()\n", "path": "opennmt/bin/main.py"}], "after_files": [{"content": "\"\"\"Main script.\"\"\"\n\nimport argparse\nimport logging\nimport os\nimport sys\n\nimport tensorflow as tf\n\nfrom opennmt import __version__\nfrom opennmt.models import catalog\nfrom opennmt.runner import Runner\nfrom opennmt.config import load_model, load_config\nfrom opennmt.utils import exporters\n\n\n_PYTHON_TO_TENSORFLOW_LOGGING_LEVEL = {\n    logging.CRITICAL: 3,\n    logging.ERROR: 2,\n    logging.WARNING: 1,\n    logging.INFO: 0,\n    logging.DEBUG: 0,\n    logging.NOTSET: 0,\n}\n\ndef _set_log_level(log_level):\n  tf.get_logger().setLevel(log_level)\n  os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = str(_PYTHON_TO_TENSORFLOW_LOGGING_LEVEL[log_level])\n\ndef _prefix_paths(prefix, paths):\n  \"\"\"Recursively prefix paths.\n\n  Args:\n    prefix: The prefix to apply.\n    data: A dict of relative paths.\n\n  Returns:\n    The updated dict.\n  \"\"\"\n  if isinstance(paths, dict):\n    for key, path in paths.items():\n      paths[key] = _prefix_paths(prefix, path)\n    return paths\n  elif isinstance(paths, list):\n    for i, path in enumerate(paths):\n      paths[i] = _prefix_paths(prefix, path)\n    return paths\n  elif isinstance(paths, str):\n    path = paths\n    new_path = os.path.join(prefix, path)\n    if tf.io.gfile.exists(new_path):\n      return new_path\n    else:\n      return path\n  else:\n    return paths\n\ndef main():\n  parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\"-v\", \"--version\", action=\"version\", version=\"OpenNMT-tf %s\" % __version__)\n  parser.add_argument(\"--config\", required=True, nargs=\"+\",\n                      help=\"List of configuration files.\")\n  parser.add_argument(\"--auto_config\", default=False, action=\"store_true\",\n                      help=\"Enable automatic configuration values.\")\n  parser.add_argument(\"--model_type\", default=\"\",\n                      choices=list(sorted(catalog.list_model_names_from_catalog())),\n                      help=\"Model type from the catalog.\")\n  parser.add_argument(\"--model\", default=\"\",\n                      help=\"Custom model configuration file.\")\n  parser.add_argument(\"--run_dir\", default=\"\",\n                      help=\"If set, model_dir will be created relative to this location.\")\n  parser.add_argument(\"--data_dir\", default=\"\",\n                      help=\"If set, data files are expected to be relative to this location.\")\n  parser.add_argument(\"--checkpoint_path\", default=None,\n                      help=(\"Specific checkpoint or model directory to load \"\n                            \"(when a directory is set, the latest checkpoint is used).\"))\n  parser.add_argument(\"--log_level\", default=\"INFO\",\n                      choices=[\"CRITICAL\", \"ERROR\", \"WARNING\", \"INFO\", \"DEBUG\", \"NOTSET\"],\n                      help=\"Logs verbosity.\")\n  parser.add_argument(\"--seed\", type=int, default=None,\n                      help=\"Random seed.\")\n  parser.add_argument(\"--gpu_allow_growth\", default=False, action=\"store_true\",\n                      help=\"Allocate GPU memory dynamically.\")\n  parser.add_argument(\"--intra_op_parallelism_threads\", type=int, default=0,\n                      help=(\"Number of intra op threads (0 means the system picks \"\n                            \"an appropriate number).\"))\n  parser.add_argument(\"--inter_op_parallelism_threads\", type=int, default=0,\n                      help=(\"Number of inter op threads (0 means the system picks \"\n                            \"an appropriate number).\"))\n  parser.add_argument(\"--mixed_precision\", default=False, action=\"store_true\",\n                      help=\"Enable mixed precision.\")\n\n  subparsers = parser.add_subparsers(help=\"Run type.\", dest=\"run_type\")\n  subparsers.required = True\n  parser_train = subparsers.add_parser(\"train\", help=\"Training.\")\n  parser_train.add_argument(\n      \"--with_eval\", default=False, action=\"store_true\",\n      help=\"Enable automatic evaluation.\")\n  parser_train.add_argument(\n      \"--num_gpus\", type=int, default=1,\n      help=\"Number of GPUs to use for in-graph replication.\")\n  parser_train.add_argument(\n      \"--horovod\", default=False, action=\"store_true\",\n      help=\"Enable Horovod training mode.\")\n\n  parser_eval = subparsers.add_parser(\"eval\", help=\"Evaluation.\")\n  parser_eval.add_argument(\n      \"--features_file\", nargs=\"+\", default=None,\n      help=\"Input features files.\")\n  parser_eval.add_argument(\n      \"--labels_file\", default=None,\n      help=\"Output labels files.\")\n\n  parser_infer = subparsers.add_parser(\"infer\", help=\"Inference.\")\n  parser_infer.add_argument(\n      \"--features_file\", nargs=\"+\", required=True,\n      help=\"Run inference on this file.\")\n  parser_infer.add_argument(\n      \"--predictions_file\", default=\"\",\n      help=(\"File used to save predictions. If not set, predictions are printed \"\n            \"on the standard output.\"))\n  parser_infer.add_argument(\n      \"--log_prediction_time\", default=False, action=\"store_true\",\n      help=\"Logs some prediction time metrics.\")\n\n  parser_export = subparsers.add_parser(\"export\", help=\"Model export.\")\n  parser_export.add_argument(\n      \"--export_dir\", required=True,\n      help=\"The directory of the exported model.\")\n  parser_export.add_argument(\n      \"--export_format\", choices=exporters.list_exporters(), default=\"saved_model\",\n      help=\"Format of the exported model.\")\n\n  parser_score = subparsers.add_parser(\"score\", help=\"Scoring.\")\n  parser_score.add_argument(\"--features_file\", nargs=\"+\", required=True,\n                            help=\"Features file.\")\n  parser_score.add_argument(\"--predictions_file\", default=None,\n                            help=\"Predictions to score.\")\n\n  parser_average_checkpoints = subparsers.add_parser(\n      \"average_checkpoints\", help=\"Checkpoint averaging.\")\n  parser_average_checkpoints.add_argument(\n      \"--output_dir\", required=True,\n      help=\"The output directory for the averaged checkpoint.\")\n  parser_average_checkpoints.add_argument(\n      \"--max_count\", type=int, default=8,\n      help=\"The maximal number of checkpoints to average.\")\n\n  parser_update_vocab = subparsers.add_parser(\n      \"update_vocab\", help=\"Update model vocabularies in checkpoint.\")\n  parser_update_vocab.add_argument(\n      \"--output_dir\", required=True,\n      help=\"The output directory for the updated checkpoint.\")\n  parser_update_vocab.add_argument(\n      \"--src_vocab\", default=None,\n      help=\"Path to the new source vocabulary.\")\n  parser_update_vocab.add_argument(\n      \"--tgt_vocab\", default=None,\n      help=\"Path to the new target vocabulary.\")\n\n  # When using an option that takes multiple values just before the run type,\n  # the run type is treated as a value of this option. To fix this issue, we\n  # inject a placeholder option just before the run type to clearly separate it.\n  parser.add_argument(\"--placeholder\", action=\"store_true\", help=argparse.SUPPRESS)\n  run_types = set(subparsers.choices.keys())\n  args = sys.argv[1:]\n  for i, arg in enumerate(args):\n    if arg in run_types:\n      args.insert(i, \"--placeholder\")\n      break\n\n  args = parser.parse_args(args)\n  if hasattr(args, \"features_file\") and args.features_file and len(args.features_file) == 1:\n    args.features_file = args.features_file[0]\n\n  _set_log_level(getattr(logging, args.log_level))\n  tf.config.threading.set_intra_op_parallelism_threads(args.intra_op_parallelism_threads)\n  tf.config.threading.set_inter_op_parallelism_threads(args.inter_op_parallelism_threads)\n\n  gpus = tf.config.list_physical_devices(device_type=\"GPU\")\n  if hasattr(args, \"horovod\") and args.horovod:\n    import horovod.tensorflow as hvd  # pylint: disable=import-outside-toplevel\n    hvd.init()\n    is_master = hvd.rank() == 0\n    if gpus:\n      local_gpu = gpus[hvd.local_rank()]\n      tf.config.experimental.set_visible_devices(local_gpu, device_type=\"GPU\")\n      gpus = [local_gpu]\n  else:\n    hvd = None\n    is_master = True\n\n  if args.gpu_allow_growth:\n    for device in gpus:\n      tf.config.experimental.set_memory_growth(device, enable=True)\n\n  # Load and merge run configurations.\n  config = load_config(args.config)\n  if args.run_dir:\n    config[\"model_dir\"] = os.path.join(args.run_dir, config[\"model_dir\"])\n  if args.data_dir:\n    config[\"data\"] = _prefix_paths(args.data_dir, config[\"data\"])\n\n  if is_master and not tf.io.gfile.exists(config[\"model_dir\"]):\n    tf.get_logger().info(\"Creating model directory %s\", config[\"model_dir\"])\n    tf.io.gfile.makedirs(config[\"model_dir\"])\n\n  model = load_model(\n      config[\"model_dir\"],\n      model_file=args.model,\n      model_name=args.model_type,\n      serialize_model=is_master,\n      as_builder=True)\n  runner = Runner(\n      model,\n      config,\n      auto_config=args.auto_config,\n      mixed_precision=args.mixed_precision,\n      seed=args.seed)\n\n  if args.run_type == \"train\":\n    runner.train(\n        num_devices=args.num_gpus,\n        with_eval=args.with_eval,\n        checkpoint_path=args.checkpoint_path,\n        hvd=hvd)\n  elif args.run_type == \"eval\":\n    metrics = runner.evaluate(\n        checkpoint_path=args.checkpoint_path,\n        features_file=args.features_file,\n        labels_file=args.labels_file)\n    print(metrics)\n  elif args.run_type == \"infer\":\n    runner.infer(\n        args.features_file,\n        predictions_file=args.predictions_file,\n        checkpoint_path=args.checkpoint_path,\n        log_time=args.log_prediction_time)\n  elif args.run_type == \"export\":\n    runner.export(\n        args.export_dir,\n        checkpoint_path=args.checkpoint_path,\n        exporter=exporters.make_exporter(args.export_format))\n  elif args.run_type == \"score\":\n    runner.score(\n        args.features_file,\n        args.predictions_file,\n        checkpoint_path=args.checkpoint_path)\n  elif args.run_type == \"average_checkpoints\":\n    runner.average_checkpoints(args.output_dir, max_count=args.max_count)\n  elif args.run_type == \"update_vocab\":\n    runner.update_vocab(\n        args.output_dir,\n        src_vocab=args.src_vocab,\n        tgt_vocab=args.tgt_vocab)\n\n\nif __name__ == \"__main__\":\n  main()\n", "path": "opennmt/bin/main.py"}]}