psdi-data-conversion 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ from argparse import ArgumentParser
10
10
  from psdi_data_conversion import constants as const
11
11
  from psdi_data_conversion.gui.env import update_env
12
12
  from psdi_data_conversion.gui.setup import get_app, limit_upload_size, start_app
13
- from psdi_data_conversion.main import print_wrap
13
+ from psdi_data_conversion.utils import print_wrap
14
14
 
15
15
  app = get_app()
16
16
 
@@ -20,6 +20,7 @@ from typing import Any
20
20
  from psdi_data_conversion import constants as const
21
21
  from psdi_data_conversion import log_utility
22
22
  from psdi_data_conversion.dist import bin_exists, get_bin_path, get_dist
23
+ from psdi_data_conversion.file_io import get_package_path
23
24
  from psdi_data_conversion.security import SAFE_STRING_RE, string_is_safe
24
25
 
25
26
  try:
@@ -744,7 +745,9 @@ class ScriptFileConverter(FileConverter):
744
745
  if self.required_bin is not None:
745
746
  env["BIN_PATH"] = get_bin_path(self.required_bin)
746
747
 
747
- process = subprocess.run(['sh', f'psdi_data_conversion/scripts/{self.script}', *self._get_script_args()],
748
+ script_abs_path = os.path.join(get_package_path(), "scripts", self.script)
749
+
750
+ process = subprocess.run(['sh', script_abs_path, *self._get_script_args()],
748
751
  env=env, capture_output=True, text=True)
749
752
 
750
753
  self.out = process.stdout
@@ -12,6 +12,7 @@ from openbabel import openbabel
12
12
 
13
13
  from psdi_data_conversion.converters.base import FileConverter, FileConverterInputException
14
14
  from psdi_data_conversion.security import SAFE_STRING_RE, string_is_safe
15
+ from psdi_data_conversion.utils import print_wrap
15
16
 
16
17
  CONVERTER_OB = 'Open Babel'
17
18
 
@@ -83,30 +84,34 @@ class OBFileConverter(FileConverter):
83
84
  `run_converter()` through providing a dict to the `data` kwarg. The supported keys and values are:
84
85
 
85
86
  "from_flags": str
86
- String of concatenated one-letter flags for how to read the input file. To list the flags supported for a given
87
- input format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look for the
88
- "Allowed input flags" section, if one exists, or alternatively call the library function
87
+ String of concatenated one-letter flags for how to write the output file, e.g. ``"from_flags": "xyz"`` will set
88
+ flags x, y and z. To list the flags supported for a given output format, call
89
+ ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look for the "Allowed input flags"
90
+ section, if one exists, or alternatively call the library function
89
91
  ``psdi_data_conversion.database.get_in_format_args("Open Babel", <format>)`` from within Python code.
90
92
 
91
93
  "to_flags": str
92
- String of concatenated one-letter flags for how to write the output file. To list the flags supported for a
93
- given output format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look for
94
- the "Allowed output flags" section, if one exists, or alternatively call the library function
94
+ String of concatenated one-letter flags for how to write the output file, e.g. ``"to_flags": "xyz"`` will set
95
+ flags x, y and z. To list the flags supported for a given output format, call
96
+ ``psdi-data-convert -l -t <format> -w Open Babel`` at the command-line and look for the "Allowed output flags"
97
+ section, if one exists, or alternatively call the library function
95
98
  ``psdi_data_conversion.database.get_out_format_args("Open Babel", <format>)`` from within Python code.
96
99
 
97
100
  "from_options": str
98
101
  String of space-separated options for how to read the input file. Each option "word" in this string should start
99
- with the letter indicating which option is being used, followed by the value for that option. To list the
102
+ with the letter indicating which option is being used, followed by the value for that option. E.g.
103
+ ``"from_options": "a1 b2"`` will set the value 1 for option a and the value 2 for option b. To list the
100
104
  options supported for a given input format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the
101
105
  command-line and look for the "Allowed input options" section, if one exists, or alternatively call the library
102
106
  function ``psdi_data_conversion.database.get_in_format_args("Open Babel", <format>)`` from within Python code.
103
107
 
104
108
  "to_options": str
105
109
  String of space-separated options for how to write the output file. Each option "word" in this string should
106
- start with the letter indicating which option is being used, followed by the value for that option. To list the
110
+ start with the letter indicating which option is being used, followed by the value for that option. E.g.
111
+ ``"to_options": "a1 b2"`` will set the value 1 for option a and the value 2 for option b. To list the
107
112
  options supported for a given output format, call ``psdi-data-convert -l -t <format> -w Open Babel`` at the
108
113
  command-line and look for the "Allowed output options" section, if one exists, or alternatively call the library
109
- function ``psdi_data_conversion.database.get_out_format_args("Open Babel", <format>)`` from within Python code.
114
+ function ``psdi_data_conversion.database.get_in_format_args("Open Babel", <format>)`` from within Python code.
110
115
 
111
116
  "coordinates": str
112
117
  One of "Gen2D", "Gen3D", or "neither", specifying how positional coordinates should be generated in the output
@@ -159,13 +164,28 @@ class OBFileConverter(FileConverter):
159
164
  from_args = self.data.get("from_args", "")
160
165
  to_args = self.data.get("to_args", "")
161
166
 
167
+ from psdi_data_conversion.database import (FileConverterDatabaseException, get_in_format_args,
168
+ get_out_format_args)
169
+
162
170
  # Add option flags and arguments as appropriate
163
171
  for char in from_flags:
164
172
  check_string_security(char)
173
+ # Check that the flag is valid
174
+ try:
175
+ get_in_format_args(self.name, self.from_format_info, char)
176
+ except FileConverterDatabaseException:
177
+ print_wrap(f"WARNING: Input format flag '{char}' not recognised for conversion with {self.name}. "
178
+ "If this is valid, the database should be updated to indicate this.", err=True)
165
179
  ob_conversion.AddOption(char, ob_conversion.INOPTIONS)
166
180
 
167
181
  for char in to_flags:
168
182
  check_string_security(char)
183
+ # Check that the flag is valid
184
+ try:
185
+ get_out_format_args(self.name, self.from_format_info, char)
186
+ except FileConverterDatabaseException:
187
+ print_wrap(f"WARNING: Output format flag '{char}' not recognised for conversion with {self.name}. "
188
+ "If this is valid, the database should be updated to indicate this", err=True)
169
189
  ob_conversion.AddOption(char, ob_conversion.OUTOPTIONS)
170
190
 
171
191
  self.data["read_flags_args"] = []
@@ -176,37 +196,82 @@ class OBFileConverter(FileConverter):
176
196
  if "from_options" in self.data:
177
197
  # From options were provided by the command-line script or library
178
198
  l_from_options = self.data["from_options"].split()
199
+
179
200
  for opt in l_from_options:
180
201
  option, value = get_option_and_value(opt)
202
+
203
+ # Check that the option is valid
204
+ try:
205
+ get_in_format_args(self.name, self.from_format_info, option)
206
+ except FileConverterDatabaseException:
207
+ print_wrap(f"WARNING: Input format option '{option}' not recognised for conversion with "
208
+ f"{self.name}. If this is valid, the database should be updated to indicate "
209
+ "this", err=True)
210
+
181
211
  ob_conversion.AddOption(option, ob_conversion.INOPTIONS, value)
212
+
182
213
  self.logger.debug(f"Set Open Babel read flags arguments to: {self.data['from_options']}")
183
214
  # Store the options in the "read_flags_args" entry for the later logging
184
215
  self.data["read_flags_args"] = l_from_options
216
+
185
217
  else:
186
218
  # From options were provided by the command-line script or library
187
219
  for char in from_arg_flags:
220
+
188
221
  index = from_args.find('£')
189
222
  arg, from_args = from_args[0:index], from_args[index + 1:len(from_args)]
190
223
  check_string_security(char), check_string_security(arg)
224
+
225
+ # Check that the option is valid
226
+ try:
227
+ get_in_format_args(self.name, self.from_format_info, arg)
228
+ except FileConverterDatabaseException:
229
+ print_wrap(f"WARNING: Input format option '{arg}' not recognised for conversion with "
230
+ f"{self.name}. If this is valid, the database should be updated to indicate "
231
+ "this.", err=True)
232
+
191
233
  ob_conversion.AddOption(char, ob_conversion.INOPTIONS, arg)
192
234
  self.data["read_flags_args"].append(char + " " + arg)
235
+
193
236
  self.logger.debug(f"Set Open Babel read flags arguments to: {self.data['read_flags_args']}")
194
237
 
195
238
  if "to_options" in self.data:
196
239
  # From options were provided by the command-line script or library
197
240
  l_to_options = self.data["to_options"].split()
241
+
198
242
  for opt in l_to_options:
199
243
  option, value = get_option_and_value(opt)
244
+
245
+ # Check that the option is valid
246
+ try:
247
+ get_in_format_args(self.name, self.to_format_info, option)
248
+ except FileConverterDatabaseException:
249
+ print_wrap(f"WARNING: Output format option '{option}' not recognised for conversion with "
250
+ f"{self.name}. If this is valid, the database should be updated to indicate "
251
+ "this.", err=True)
252
+
200
253
  ob_conversion.AddOption(option, ob_conversion.OUTOPTIONS, value)
254
+
201
255
  self.logger.debug(f"Set Open Babel write flags arguments to: {self.data['to_options']}")
202
256
  # Store the options in the "write_flags_args" entry for the later logging
203
257
  self.data["write_flags_args"] = l_to_options
258
+
204
259
  else:
205
- # From options were provided by the command-line script or library
260
+ # To options were provided by the command-line script or library
206
261
  for char in to_arg_flags:
262
+
207
263
  index = to_args.find('£')
208
264
  arg, to_args = to_args[0:index], to_args[index + 1:len(to_args)]
209
265
  check_string_security(char), check_string_security(arg)
266
+
267
+ # Check that the option is valid
268
+ try:
269
+ get_out_format_args(self.name, self.to_format_info, arg)
270
+ except FileConverterDatabaseException:
271
+ print_wrap(f"WARNING: Output format option '{arg}' not recognised for conversion with "
272
+ f"{self.name}. If this is valid, the database should be updated to indicate "
273
+ "this.", err=True)
274
+
210
275
  ob_conversion.AddOption(char, ob_conversion.OUTOPTIONS, arg)
211
276
  self.data["write_flags_args"].append(char + " " + arg)
212
277
  self.logger.debug(f"Set Open Babel write flags arguments to: {self.data['read_flags_args']}")
@@ -239,6 +304,12 @@ class OBFileConverter(FileConverter):
239
304
  if "Open Babel Error" in self.err:
240
305
  self._abort_from_err()
241
306
 
307
+ # Check for any non-critical errors and print them out
308
+ l_err_blocks = self.err.split("\n\n")
309
+ for err_block in l_err_blocks:
310
+ if err_block.startswith("ERROR:") or err_block.startswith("WARNING:"):
311
+ print_wrap(err_block, err=True)
312
+
242
313
  def _create_message(self) -> str:
243
314
  """Overload method to create a log of options passed to the converter
244
315
  """
@@ -248,7 +319,7 @@ class OBFileConverter(FileConverter):
248
319
  label_length = 19
249
320
 
250
321
  for (label, key, multi) in (("Coord. gen.:", COORD_GEN_KEY, False),
251
- ("Coord. option:", "coord_option", False),
322
+ ("Coord. option:", COORD_GEN_QUAL_KEY, False),
252
323
  ("Read options:", "from_flags", False),
253
324
  ("Write options:", "to_flags", False),
254
325
  ("Read opts + args:", "read_flags_args", True),
@@ -24,6 +24,7 @@ from psdi_data_conversion import constants as const
24
24
  from psdi_data_conversion.converter import (L_REGISTERED_CONVERTERS, L_SUPPORTED_CONVERTERS,
25
25
  get_registered_converter_class)
26
26
  from psdi_data_conversion.converters.base import FileConverter, FileConverterException
27
+ from psdi_data_conversion.file_io import get_package_path
27
28
  from psdi_data_conversion.utils import regularize_name
28
29
 
29
30
  # Keys for top-level and general items in the database
@@ -1206,10 +1207,7 @@ def get_database_path() -> str:
1206
1207
  str
1207
1208
  """
1208
1209
 
1209
- # For an interactive shell, __file__ won't be defined for this module, so use the constants module instead
1210
- reference_file = os.path.realpath(const.__file__)
1211
-
1212
- qualified_database_filename = os.path.join(os.path.dirname(reference_file), const.DATABASE_FILENAME)
1210
+ qualified_database_filename = os.path.join(get_package_path(), const.DATABASE_FILENAME)
1213
1211
 
1214
1212
  return qualified_database_filename
1215
1213
 
@@ -1432,11 +1430,14 @@ def disambiguate_formats(converter_name: str,
1432
1430
  raise FileConverterDatabaseException(f"Conversion from {in_format} to {out_format} with converter "
1433
1431
  f"{converter_name} is not supported", help=True)
1434
1432
  else:
1435
- msg = (f"Conversion from {in_format} to {out_format} with converter {converter_name} is ambiguous.\n"
1436
- "Possible matching conversions are:\n")
1433
+ msg = (f"Conversion from {in_format} to {out_format} with converter {converter_name} is ambiguous. Please "
1434
+ "Use the ID or disambiguated name (listed below) of the desired conversion. Possible matching "
1435
+ "conversions are:\n")
1437
1436
  for _, possible_in_format, possible_out_format in l_possible_conversions:
1438
- msg += (f"{possible_in_format.disambiguated_name} ({possible_in_format.note}) to "
1439
- f"{possible_out_format.disambiguated_name} ({possible_out_format.note})\n")
1437
+ msg += (f" {possible_in_format.id}: {possible_in_format.disambiguated_name} "
1438
+ f"({possible_in_format.note}) to "
1439
+ f"{possible_out_format.id}: {possible_out_format.disambiguated_name} "
1440
+ f"({possible_out_format.note})\n")
1440
1441
  # Trim the final newline from the message
1441
1442
  msg = msg[:-1]
1442
1443
  raise FileConverterDatabaseException(msg, help=True)
@@ -9,7 +9,7 @@ import os
9
9
  import shutil
10
10
  import sys
11
11
 
12
- import psdi_data_conversion
12
+ from psdi_data_conversion.file_io import get_package_path
13
13
 
14
14
  # Labels for each platform (which we use for the folder in this project), and the head of the name each platform will
15
15
  # have in `sys.platform`
@@ -29,7 +29,7 @@ D_DIST_NAME_HEADS = {LINUX_LABEL: LINUX_NAME_HEAD,
29
29
 
30
30
 
31
31
  # Determine the fully-qualified binary directory when this module is first imported
32
- BIN_DIR: str = os.path.join(psdi_data_conversion.__path__[0], "bin")
32
+ BIN_DIR: str = os.path.join(get_package_path(), "bin")
33
33
 
34
34
 
35
35
  def get_dist():
@@ -7,12 +7,30 @@ Functions and classes related to general filesystem input/output
7
7
 
8
8
  import glob
9
9
  import os
10
+ from functools import lru_cache
10
11
  from shutil import copyfile, make_archive, unpack_archive
11
12
  from tempfile import TemporaryDirectory
12
13
 
13
14
  from psdi_data_conversion import constants as const
14
15
 
15
16
 
17
+ @lru_cache(maxsize=1)
18
+ def get_package_path() -> str:
19
+ """Gets the absolute path to where the `psdi_data_conversion` package is on disk
20
+
21
+ Returns
22
+ -------
23
+ str
24
+ """
25
+
26
+ # For an interactive shell, __file__ won't be defined for this module, so use the constants module instead
27
+ reference_file = os.path.realpath(const.__file__)
28
+
29
+ package_path = os.path.dirname(reference_file)
30
+
31
+ return package_path
32
+
33
+
16
34
  def is_archive(filename: str) -> bool:
17
35
  """Uses a file's extension to check if it's an archive or not
18
36
  """
@@ -26,20 +26,7 @@ from psdi_data_conversion.database import (FormatInfo, get_conversion_pathway, g
26
26
  get_out_format_args, get_possible_conversions, get_possible_formats)
27
27
  from psdi_data_conversion.file_io import split_archive_ext
28
28
  from psdi_data_conversion.log_utility import get_log_level_from_str
29
- from psdi_data_conversion.utils import regularize_name
30
-
31
-
32
- def print_wrap(s: str, newline=False, err=False, **kwargs):
33
- """Print a string wrapped to the terminal width
34
- """
35
- if err:
36
- file = sys.stderr
37
- else:
38
- file = sys.stdout
39
- for line in s.split("\n"):
40
- print(textwrap.fill(line, width=TERM_WIDTH, **kwargs), file=file)
41
- if newline:
42
- print("")
29
+ from psdi_data_conversion.utils import print_wrap, regularize_name
43
30
 
44
31
 
45
32
  class ConvertArgs:
@@ -260,29 +247,29 @@ def get_argument_parser():
260
247
  parser.add_argument("--delete-input", action="store_true",
261
248
  help="If set, input files will be deleted after conversion, default they will be kept")
262
249
  parser.add_argument("--from-flags", type=str, default="",
263
- help="Any command-line flags to be provided to the converter for reading in the input file(s). "
264
- "For information on the flags accepted by a converter and its required format for them, "
265
- "call this script with '-l <converter name>'. If the set of flags includes any spaces, it "
266
- "must be quoted, and if hyphens are used, the first preceding hyphen for each flag must "
267
- "be backslash-escaped, e.g. '--from-flags \"\\-a \\-bc \\--example\"'")
250
+ help="String of concatenated one-letter flags for how to read the input file, e.g. "
251
+ "``--from-flags xyz`` will set flags x, y, and z. To list the flags supported for a given "
252
+ "input format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line "
253
+ "and look for the \"Allowed input flags\" section, if one exists.")
268
254
  parser.add_argument("--to-flags", type=str, default="",
269
- help="Any command-line flags to be provided to the converter for writing the output file(s). "
270
- "For information on the flags accepted by a converter and its required format for them, "
271
- "call this script with '-l <converter name>'. If the set of flags includes any spaces, it "
272
- "must be quoted, and if hyphens are used, the first preceding hyphen for each flag must "
273
- "be backslash-escaped, e.g. '--to-flags \"\\-a \\-bc \\--example\"'")
255
+ help="String of concatenated one-letter flags for how to write the output file, e.g. "
256
+ "``--from-flags xyz`` will set flags x, y, and z. To list the flags supported for a given "
257
+ "output format, call ``psdi-data-convert -l -t <format> -w Open Babel`` at the command-line "
258
+ "and look for the \"Allowed output flags\" section, if one exists.")
274
259
  parser.add_argument("--from-options", type=str, default="",
275
- help="Any command-line options to be provided to the converter for reading in the input "
276
- "file(s). For information on the options accepted by a converter and its required format "
277
- "for them, call this script with '-l <converter name>'. If the set of options includes "
278
- "any spaces, it must be quoted, and the first preceding hyphen for each option must be "
279
- "backslash-escaped, e.g. '--from-options \"\\-x xval --opt optval\"'")
260
+ help="String of space-separated options for how to read the input file. Each option \"word\" "
261
+ "in this string should start with the letter indicating which option is being used, followed "
262
+ "by the value for that option. E.g. ``--from_options a1 b2`` will set the value 1 for "
263
+ "option a and the value 2 for option b. To list the options supported for a given input "
264
+ "format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look "
265
+ "for the \"Allowed input options\" section, if one exists.")
280
266
  parser.add_argument("--to-options", type=str, default="",
281
- help="Any command-line options to be provided to the converter for writing the output "
282
- "file(s). For information on the options accepted by a converter and its required format "
283
- "for them, call this script with '-l <converter name>'. If the set of options includes "
284
- "any spaces, it must be quoted, and the first preceding hyphen for each option must be "
285
- "backslash-escaped, e.g. '--to-options \"\\-x xval --opt optval\"'")
267
+ help="String of space-separated options for how to write the output file. Each option \"word\" "
268
+ "in this string should start with the letter indicating which option is being used, followed "
269
+ "by the value for that option. E.g. ``--to_options a1 b2`` will set the value 1 for "
270
+ "option a and the value 2 for option b. To list the options supported for a given output "
271
+ "format, call ``psdi-data-convert -l -t <format> -w Open Babel`` at the command-line and look "
272
+ "for the \"Allowed input options\" section, if one exists.")
286
273
  parser.add_argument("-s", "--strict", action="store_true",
287
274
  help="If set, will fail if one of the input files has the wrong extension (including those "
288
275
  "contained in archives, but not the archive files themselves). Otherwise, will only print a "
@@ -399,13 +386,17 @@ def detail_converter_use(args: ConvertArgs):
399
386
 
400
387
  print_wrap(f"File formats supported by {converter_name}:", newline=True)
401
388
  max_format_length = max([len(x.disambiguated_name) for x in l_all_formats])
402
- print(" "*(max_format_length+4) + " INPUT OUTPUT")
403
- print(" "*(max_format_length+4) + " ----- ------")
389
+ print(" "*(max_format_length+4) + " INPUT OUTPUT DESCRIPTION")
390
+ print(" "*(max_format_length+4) + " ----- ------ -----------")
404
391
  for file_format in l_all_formats:
405
392
  in_yes_or_no = "yes" if file_format in l_input_formats else "no"
406
393
  out_yes_or_no = "yes" if file_format in l_output_formats else "no"
407
- print(f" {file_format.disambiguated_name:>{max_format_length}}{in_yes_or_no:>8}{out_yes_or_no:>8}")
408
- print("")
394
+ print(f" {file_format.disambiguated_name:>{max_format_length}} {in_yes_or_no:<9}{out_yes_or_no:<10}"
395
+ f"{file_format.note}")
396
+ print_wrap("\nFor more information on a format, including its ID (which can be used to specify it uniquely in "
397
+ "case of ambiguity, and is resilient to database changes affecting the disambiguated names listed "
398
+ "above), call:\n"
399
+ f"{CL_SCRIPT_NAME} -l -f <format>", newline=True)
409
400
 
410
401
  if converter_class.allowed_flags is None:
411
402
  print_wrap("Information has not been provided about general flags accepted by this converter.", newline=True)
@@ -9,7 +9,7 @@ import os
9
9
  # Locations relative to the root directory of the project - to ensure the files are found, tests should chdir to this
10
10
  # directory before searching for files
11
11
 
12
- TEST_DATA_LOC_IN_PROJECT = "./test_data"
12
+ TEST_DATA_LOC_IN_PROJECT = "test_data"
13
13
 
14
14
  INPUT_TEST_DATA_LOC_IN_PROJECT = TEST_DATA_LOC_IN_PROJECT
15
15
  OUTPUT_TEST_DATA_LOC_IN_PROJECT = os.path.join(TEST_DATA_LOC_IN_PROJECT, "output")
@@ -11,6 +11,7 @@ import shlex
11
11
  import sys
12
12
  from collections.abc import Callable, Iterable
13
13
  from dataclasses import dataclass, field
14
+ from functools import lru_cache
14
15
  from math import isclose
15
16
  from tempfile import TemporaryDirectory
16
17
  from typing import Any
@@ -19,32 +20,43 @@ from unittest.mock import patch
19
20
  import py
20
21
  import pytest
21
22
 
22
- import psdi_data_conversion
23
23
  from psdi_data_conversion.constants import CONVERTER_DEFAULT, GLOBAL_LOG_FILENAME, LOG_NONE, OUTPUT_LOG_EXT
24
24
  from psdi_data_conversion.converter import run_converter
25
25
  from psdi_data_conversion.converters.openbabel import COORD_GEN_KEY, COORD_GEN_QUAL_KEY
26
26
  from psdi_data_conversion.database import get_format_info
27
27
  from psdi_data_conversion.dist import LINUX_LABEL, get_dist
28
- from psdi_data_conversion.file_io import is_archive, split_archive_ext
28
+ from psdi_data_conversion.file_io import get_package_path, is_archive, split_archive_ext
29
29
  from psdi_data_conversion.main import main as data_convert_main
30
30
  from psdi_data_conversion.testing.constants import (INPUT_TEST_DATA_LOC_IN_PROJECT, OUTPUT_TEST_DATA_LOC_IN_PROJECT,
31
31
  TEST_DATA_LOC_IN_PROJECT)
32
32
 
33
33
 
34
- def get_path_in_project(filename):
35
- """Get the realpath to a file contained within the project, given its project-relative path"""
34
+ @lru_cache(maxsize=1)
35
+ def get_project_path() -> str:
36
+ """Gets the absolute path to where the project is on disk, using the package path to find it and checking that it
37
+ contains the expected files
38
+
39
+ Returns
40
+ -------
41
+ str
42
+ """
36
43
 
37
- old_cwd = os.getcwd()
44
+ project_path = os.path.abspath(os.path.join(get_package_path(), ".."))
38
45
 
39
- try:
40
- os.chdir(os.path.join(psdi_data_conversion.__path__[0], ".."))
41
- realpath = os.path.realpath(filename)
46
+ # Check that the project path contains the expected test_data folder
47
+ if not os.path.isdir(os.path.join(project_path, TEST_DATA_LOC_IN_PROJECT)):
48
+ raise FileNotFoundError(f"Project path was expected to be '{project_path}', but this does not contain the "
49
+ f"expected directory '{TEST_DATA_LOC_IN_PROJECT}'")
42
50
 
43
- finally:
44
- # Change back to the previous directory
45
- os.chdir(old_cwd)
51
+ return project_path
52
+
53
+
54
+ def get_path_in_project(filename):
55
+ """Get the realpath to a file contained within the project, given its project-relative path"""
56
+
57
+ abs_path = os.path.abspath(os.path.join(get_project_path(), filename))
46
58
 
47
- return realpath
59
+ return abs_path
48
60
 
49
61
 
50
62
  def get_test_data_loc():
@@ -567,7 +579,7 @@ def check_file_match(filename: str, ex_filename: str) -> str:
567
579
 
568
580
  # Read in both files
569
581
  text = open(filename, "r").read()
570
- ex_text = open(ex_filename, "r").read()
582
+ ex_text = open(get_path_in_project(ex_filename), "r").read()
571
583
 
572
584
  # We want to check they're the same without worrying about whitespace (which doesn't matter for this format),
573
585
  # so we accomplish this by using the string's `split` method, which splits on whitespace by default
@@ -5,6 +5,25 @@ Miscellaneous utility functions used by this project
5
5
  """
6
6
 
7
7
 
8
+ import sys
9
+ import textwrap
10
+
11
+ from psdi_data_conversion.constants import TERM_WIDTH
12
+
13
+
14
+ def print_wrap(s: str, newline=False, err=False, **kwargs):
15
+ """Print a string wrapped to the terminal width
16
+ """
17
+ if err:
18
+ file = sys.stderr
19
+ else:
20
+ file = sys.stdout
21
+ for line in s.split("\n"):
22
+ print(textwrap.fill(line, width=TERM_WIDTH, **kwargs), file=file)
23
+ if newline:
24
+ print("")
25
+
26
+
8
27
  def regularize_name(name: str):
9
28
  """Regularizes a name for comparisons, making it lowercase and stripping spaces
10
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: psdi_data_conversion
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Chemistry file format conversion service, provided by PSDI
5
5
  Project-URL: Homepage, https://data-conversion.psdi.ac.uk/
6
6
  Project-URL: Documentation, https://psdi-uk.github.io/psdi-data-conversion/
@@ -293,6 +293,7 @@ This is the repository for the PSDI PF2 Chemistry File Format Conversion project
293
293
  - [Input file is malformatted or corrupt](#input-file-is-malformatted-or-corrupt)
294
294
  - [Input file's format is misidentified](#input-files-format-is-misidentified)
295
295
  - [Other known issues](#other-known-issues)
296
+ - [Feedback](#feedback)
296
297
  - [Licensing](#licensing)
297
298
  - [Contributors](#contributors)
298
299
  - [Funding](#funding)
@@ -484,12 +485,17 @@ options] [-s/--strict] [--nc/--no-check] [-q/--quiet] [-g/--log-file <log file n
484
485
 
485
486
  Call `psdi-data-convert -h` for details on each of these options.
486
487
 
487
- Note that some requested conversions may involve ambiguous formats which share the same extension. In this case, the application will print a warning and list possible matching formats, with a disambiguating name that can be used to specify which one. For instance, the `c2x` converter can convert into two variants of the `pdb` format, and if you ask it to convert to `pdb` without specifying which one, you'll see:
488
+ Note that some requested conversions may involve ambiguous formats which share the same extension. In this case, the application will print a warning and list possible matching formats, with IDs and disambiguating names that can be used to specify which one. For instance, the `c2x` converter can convert into two variants of the `pdb` format, and if you ask it to convert to `pdb` without specifying which one, you'll see:
488
489
 
489
490
  ```
490
- ERROR: Extension 'pdb' is ambiguous and must be defined by ID. Possible formats and their IDs are:
491
+ WARNING: Format 'pdb' is ambiguous and could refer to multiple formats. It may be necessary to explicitly specify which
492
+ you want to use when calling this script, e.g. with '-f pdb-0' - see the disambiguated names in the list below:
493
+
491
494
  9: pdb-0 (Protein Data Bank)
495
+ ...
496
+
492
497
  259: pdb-1 (Protein Data Bank with atoms numbered)
498
+ ...
493
499
  ```
494
500
 
495
501
  This provides the IDs ("9" and "259") and disambiguating names ("pdb-0" and "pdb-1") for the matching formats. Either can be used in the call to the converter, e.g.:
@@ -745,38 +751,69 @@ To remedy this, try explicitly specifying the format, rather than letting the ap
745
751
  `<format>` here can be the standard extension of the format (in the case of unambiguous extensions), its ID, or its disambiguated name. To give an example which explains what each of these are, let's say you have an MDL MOL file you wish to convert to XYZ, so you get information about it and possible converters with `psdi-data-convert -l -f mol -t xyz`:
746
752
 
747
753
  ```base
748
- $ psdi-data-convert -l -f mol
754
+ $ psdi-data-convert -l -f mol -t xyz
749
755
  WARNING: Format 'mol' is ambiguous and could refer to multiple formats. It may be necessary to explicitly specify which
750
756
  you want to use when calling this script, e.g. with '-f mol-0' - see the disambiguated names in the list below:
751
757
 
752
758
  18: mol-0 (MDL MOL)
759
+ - Atomic composition is supported
760
+ - Atomic connections are supported
761
+ - 2D atomic coordinates are supported
762
+ - 3D atomic coordinates are supported
763
+
753
764
  216: mol-1 (MOLDY)
765
+ - Atomic composition is unknown whether or not to be supported
766
+ - Atomic connections are unknown whether or not to be supported
767
+ - 2D atomic coordinates are unknown whether or not to be supported
768
+ - 3D atomic coordinates are unknown whether or not to be supported
769
+
770
+ WARNING: Format 'xyz' is ambiguous and could refer to multiple formats. It may be necessary to explicitly specify which
771
+ you want to use when calling this script, e.g. with '-f xyz-0' - see the disambiguated names in the list below:
754
772
 
755
- 20: xyz (XYZ cartesian coordinates)
773
+ 20: xyz-0 (XYZ cartesian coordinates)
774
+ - Atomic composition is supported
775
+ - Atomic connections are not supported
776
+ - 2D atomic coordinates are supported
777
+ - 3D atomic coordinates are supported
756
778
 
757
- The following registered converters can convert from mol-0 to xyz:
779
+ 284: xyz-1 (Extended XYZ (adds lattice vectors))
780
+ - Atomic composition is unknown whether or not to be supported
781
+ - Atomic connections are unknown whether or not to be supported
782
+ - 2D atomic coordinates are unknown whether or not to be supported
783
+ - 3D atomic coordinates are unknown whether or not to be supported
784
+
785
+ The following registered converters can convert from mol-0 to xyz-0:
758
786
 
759
787
  Open Babel
760
788
  c2x
761
789
 
762
790
  For details on input/output flags and options allowed by a converter for this conversion, call:
763
- psdi-data-convert -l <converter name> -f mol-0 -t xyz
791
+ psdi-data-convert -l <converter name> -f mol-0 -t xyz-0
792
+
793
+ The following registered converters can convert from mol-0 to xyz-1:
764
794
 
765
- The following registered converters can convert from mol-1 to xyz:
795
+ c2x
796
+
797
+ For details on input/output flags and options allowed by a converter for this conversion, call:
798
+ psdi-data-convert -l <converter name> -f mol-0 -t xyz-1
799
+
800
+ The following registered converters can convert from mol-1 to xyz-0:
766
801
 
767
802
  Atomsk
768
803
 
769
804
  For details on input/output flags and options allowed by a converter for this conversion, call:
770
- psdi-data-convert -l <converter name> -f mol-1 -t xyz
805
+ psdi-data-convert -l <converter name> -f mol-1 -t xyz-0
806
+
807
+ No converters are available which can perform a conversion from mol-1 to xyz-1
771
808
  ```
772
809
 
773
- This output indicates that the application is aware of two formats which share the `mol` extension: MDL MOL and MOLDY. It lists the ID, disambiguated name, and description of each: ID `18` and disambiguated name `mol-0` for MDL MOL, and ID `216` and disambiguated name `mol-1` for MOLDY. The XYZ format, on the other hand, is unambiguous, and only lists the standard extension for it as its disambiguated name (although `xyz-0` will be accepted without error as well).
810
+ This output indicates that the application is aware of two formats which share the `mol` extension: MDL MOL and MOLDY. It lists the ID, disambiguated name, and description of each: ID `18` and disambiguated name `mol-0` for MDL MOL, and ID `216` and disambiguated name `mol-1` for MOLDY. The XYZ format similarly has two variants which can be converted to.
774
811
 
775
812
  The program then lists converters which can handle the requested conversion, revealing a potential pitfall: The Open Babel and c2x converters can convert from MDL MOL to XYZ, which the Atomsk converter can convert from MOLDY to XYZ. If you don't specify which format you're converting from, the script might assume you meant to use the other one, if that's the only one compatible with the converter you've requested (or with the default converter, Open Babel, if you didn't explicitly request one). So to be careful here, it's best to specify this input format unambiguously.
776
813
 
777
814
  Since in this example you have an MDL MOL file, you would use `-f 18` or `-f mol-0` to explicitly specify it in the command-line, or similarly provide one of these to the `from_format` argument of `run_converter` within Python. The application will then properly handle it, including alerting you if you request a conversion that isn't supported by your requested converter (e.g. if you request a conversion of this MDL MOL file to XYZ with Atomsk).
778
815
 
779
- Important note: The disambiguated name is generated dynamically and isn't stored in the database, and in rare cases may change for some formats in future versions of this application which expand support to more formats and conversions. For uses which require forward-compatibility with future versions of this application, the ID should be used instead.
816
+ Important note: The disambiguated name is generated dynamically and isn't stored in the database, and in rare cases may change for some formats in future versions of this application which expand support to more formats and conversions. For uses which require forward-compatibility with future versions of this application, the ID should be used instead. You can obtain the ID for any format via the command: `psdi-data-convert -l -f <format-name>`.
780
817
 
781
818
  #### Other known issues
782
819
 
@@ -784,6 +821,10 @@ Through testing, we've identified some other conversion issues, which we list he
784
821
 
785
822
  - Open Babel will indefinitely hang when attempting to convert large files (more than ~1 MB) of certain types (such as `mmcif`). This is an issue with the converter itself and not our application, which we hope will be fixed in a future version. If this occurs, the job will have to be forcibly terminated. CTRL+C will fail to terminate it, but it can be stopped with CTRL+Z, then terminated with `kill %N`, where N is the number listed beside the job when it is stopped (usually 1). The conversion should then be attempted with another supported converter.
786
823
 
824
+ ## Feedback
825
+
826
+ To report a missing format or conversion, please use [the form on the public web service](https://data-conversion.psdi.ac.uk/report.htm). Other feedback can be submitted on [the feedback page](https://data-conversion.psdi.ac.uk/static/content/feedback.htm).
827
+
787
828
  ## Licensing
788
829
 
789
830
  This project is provided under the Apache License version 2.0, the terms of which can be found in the file `LICENSE`.
@@ -1,14 +1,14 @@
1
1
  psdi_data_conversion/__init__.py,sha256=urMsTqsTHTch1q4rMT9dgGnrvdPFMP9B8r-6Kr8H5sE,404
2
- psdi_data_conversion/app.py,sha256=VmZlIAZ_J9W6p_WUcTA_2UNmZxssmq1aT72I2d1ObDc,3606
2
+ psdi_data_conversion/app.py,sha256=u26a6taxmS3Yn9o78hL1avpO-50BnbcMnJAuVn5XlEc,3607
3
3
  psdi_data_conversion/constants.py,sha256=eXE43_9YFahoehiUV7FtqN9MKb-UpSfY2win9ic9W8I,7348
4
4
  psdi_data_conversion/converter.py,sha256=OlwGKxI8nj76wrlVzP5S7fpXb6L-Ql8-mYLYGhW81Fk,27816
5
- psdi_data_conversion/database.py,sha256=7Xm5e8Wi3b2wGUh2soBZ4DdJ0PhVSXAvv1dVCXDhNQg,65685
6
- psdi_data_conversion/dist.py,sha256=LOcKEP7H7JA9teX1m-5awuBi69gmdhtUit7yxtCTOZ8,2293
7
- psdi_data_conversion/file_io.py,sha256=LvdPmnYL_7Xlcr-7LJjUbbky4gKiqTTvPRzdbtvQaJo,8794
5
+ psdi_data_conversion/database.py,sha256=uE2s9RelTmcsr8u42nKdTJfmobysO2PY-B_7rl7TecQ,65766
6
+ psdi_data_conversion/dist.py,sha256=Db7mbLLANfugaptrcWCoVddwHT2rdP4X4HuP6uxr9AI,2309
7
+ psdi_data_conversion/file_io.py,sha256=SE7cXVedm5ZLdxDQ7W7L2R9LXTqJz2Tak9-YTvc7_js,9250
8
8
  psdi_data_conversion/log_utility.py,sha256=CHAq-JvBnTKaE0SHK5hM5j2dTbfSli4iUc3hsf6dBhc,8789
9
- psdi_data_conversion/main.py,sha256=LWiYiELF_LY1GghQLt37eZS3ZNojmwHWuSnAhe3qbDI,44768
9
+ psdi_data_conversion/main.py,sha256=_HmlDicG7TD7-taLNPazZS4rflXUU7ijHv3Tzx5kyLM,44770
10
10
  psdi_data_conversion/security.py,sha256=wjdrMre29TpkF2NqrsXJ5sschSAnDzqLYTLUcNR21Qw,902
11
- psdi_data_conversion/utils.py,sha256=iTjNfrD4n_hU9h20ldYrX2Bmp5KhCBIeMSUMLtPZ_8k,402
11
+ psdi_data_conversion/utils.py,sha256=1DaTxoaUkoiImBYyXIiPToV2ae2eZEDPxbNWUvEqwDg,821
12
12
  psdi_data_conversion/bin/LICENSE_ATOMSK,sha256=-Ay6SFTAf9x-OaRAiOgMNoutfUMLHx5jQQA1HqZ6p7I,34886
13
13
  psdi_data_conversion/bin/LICENSE_C2X,sha256=-Ay6SFTAf9x-OaRAiOgMNoutfUMLHx5jQQA1HqZ6p7I,34886
14
14
  psdi_data_conversion/bin/linux/atomsk,sha256=GDsG1MlEvmk_XPspadzEzuil6N775iewDvNZS6rWJWk,34104032
@@ -17,9 +17,9 @@ psdi_data_conversion/bin/mac/atomsk,sha256=pqExRdkR8NSqSasHZjE74R_CiM6Dkr_yvfyak
17
17
  psdi_data_conversion/bin/mac/c2x,sha256=dI-bBoQ6uqc6KMYKJaq0x7ejJgOf_wysTxQA5BrF8AY,2581960
18
18
  psdi_data_conversion/converters/__init__.py,sha256=15Ldt06eyZ0bgNPB4qg419U0Zcjt6TUCTzjCBo8EIzM,210
19
19
  psdi_data_conversion/converters/atomsk.py,sha256=_V33me1e4HW0-YXvdE-z6PdwtSK2gYV6QZf5d8aPqH4,1523
20
- psdi_data_conversion/converters/base.py,sha256=0hK9IeC306zvQ1qAWb19eI_m_e6CA8TfuEoWgypimeo,36403
20
+ psdi_data_conversion/converters/base.py,sha256=yGlwQmYcLSKP9DH2reTBGvJ-HhfxjO_dYo6qVX501Zw,36515
21
21
  psdi_data_conversion/converters/c2x.py,sha256=jDA84H8Jpz--ajTWNWX6K6oMfOMMbMxkA31-VnGi0gU,2019
22
- psdi_data_conversion/converters/openbabel.py,sha256=OYppOMfnvxmVgRY5vUkcVokWn-bQSSeG8MOFqN1MCIY,13224
22
+ psdi_data_conversion/converters/openbabel.py,sha256=L1R9gc1oU2w_OUqU5Fduz_xT_l8ZPRVG915jOYnKCdE,16956
23
23
  psdi_data_conversion/gui/__init__.py,sha256=Q52GAaJWfNWGDOAEM_fIfAoivLIgM3krhVTiHFTRixM,125
24
24
  psdi_data_conversion/gui/accessibility.py,sha256=XQyAAJZQdIyRftfidhhVgsZXVtA9b9qZhwwFm899kbE,1302
25
25
  psdi_data_conversion/gui/env.py,sha256=nVuKzuE6z9ItH09ZRkooGRUTVZSHtPzSR9XrZVDHC-0,9235
@@ -82,13 +82,13 @@ psdi_data_conversion/templates/documentation.htm,sha256=BeEPgklJOB9tA6jna0Qp4lwR
82
82
  psdi_data_conversion/templates/index.htm,sha256=zY9zihO6adJwLU8bB-iaz0AAsRvg24CNx2gVXJFqNLw,7858
83
83
  psdi_data_conversion/templates/report.htm,sha256=bru_5EtKqHtKnQ3JB9HpTdK047zCwd5akTLQr2pGd5c,5612
84
84
  psdi_data_conversion/testing/__init__.py,sha256=Xku7drtLTYLLPsd403eC0LIEa_iohVifyeyAITy2w7U,135
85
- psdi_data_conversion/testing/constants.py,sha256=BtIafruSobZ9cFY0VW5Bu209eiftnN8b3ObouZBrFQU,521
85
+ psdi_data_conversion/testing/constants.py,sha256=uYUp3FIwoXG_mDFgitpv0bD5M3xK5ThVm9fUHooons0,519
86
86
  psdi_data_conversion/testing/conversion_callbacks.py,sha256=ATR-_BsYCUN8KyOyUjfdWCELzySxLN5jOI0JyrQnmHQ,18858
87
87
  psdi_data_conversion/testing/conversion_test_specs.py,sha256=PFzTZYonQ0PsZBKjcn0Hp-UPbwphT3zRH5v-VdNImmQ,27585
88
88
  psdi_data_conversion/testing/gui.py,sha256=ul7ixYANIzmOG2ZNOZmQO6wsHmGHdiBGAlw-KuoN0j8,19085
89
- psdi_data_conversion/testing/utils.py,sha256=A2CDiGr4Gah1eRvGwFmHt1m0F7RdXOxzCuTyYhcAIR4,26122
90
- psdi_data_conversion-0.2.1.dist-info/METADATA,sha256=jv8iuV3VSQz_4NzFcG5bvTSzO5Ib7WysLqEWFYQ04NU,48538
91
- psdi_data_conversion-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- psdi_data_conversion-0.2.1.dist-info/entry_points.txt,sha256=xL7XTzaPRr2E67WhOD1M1Q-76hB8ausQlnNiHzuZQPA,123
93
- psdi_data_conversion-0.2.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
94
- psdi_data_conversion-0.2.1.dist-info/RECORD,,
89
+ psdi_data_conversion/testing/utils.py,sha256=uhJQUceaNmpQd3SioFLZVMExMVzO3Ds8V9DD2hqP-mQ,26700
90
+ psdi_data_conversion-0.2.3.dist-info/METADATA,sha256=zCwJY99aohLJ_Cekj0rPP0mz2ieo8WI9qBu7ZuSM-Z8,50396
91
+ psdi_data_conversion-0.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ psdi_data_conversion-0.2.3.dist-info/entry_points.txt,sha256=xL7XTzaPRr2E67WhOD1M1Q-76hB8ausQlnNiHzuZQPA,123
93
+ psdi_data_conversion-0.2.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
94
+ psdi_data_conversion-0.2.3.dist-info/RECORD,,