nerdd-module 0.3.3__tar.gz → 0.3.7__tar.gz

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.
Files changed (115) hide show
  1. nerdd_module-0.3.7/PKG-INFO +105 -0
  2. nerdd_module-0.3.7/README.md +18 -0
  3. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/__init__.py +0 -2
  4. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/cli.py +63 -44
  5. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/configuration.py +31 -12
  6. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/default_configuration.py +3 -1
  7. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/merged_configuration.py +1 -1
  8. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/package_configuration.py +6 -7
  9. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/search_yaml_configuration.py +2 -5
  10. nerdd_module-0.3.7/nerdd_module/config/yaml_configuration.py +90 -0
  11. nerdd_module-0.3.7/nerdd_module/converters/__init__.py +2 -0
  12. nerdd_module-0.3.7/nerdd_module/converters/converter.py +62 -0
  13. nerdd_module-0.3.7/nerdd_module/converters/identity_converter.py +8 -0
  14. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/__init__.py +0 -1
  15. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/depth_first_explorer.py +12 -10
  16. nerdd_module-0.3.7/nerdd_module/input/explorer.py +16 -0
  17. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/file_reader.py +7 -9
  18. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/gzip_reader.py +4 -6
  19. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/inchi_reader.py +5 -7
  20. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/list_reader.py +4 -6
  21. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/mol_reader.py +4 -6
  22. nerdd_module-0.3.7/nerdd_module/input/reader.py +58 -0
  23. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/sdf_reader.py +5 -8
  24. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/smiles_reader.py +6 -10
  25. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/string_reader.py +4 -6
  26. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/tar_reader.py +4 -6
  27. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/input/zip_reader.py +4 -6
  28. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/__init__.py +2 -1
  29. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/assign_mol_id_step.py +4 -2
  30. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/assign_name_step.py +2 -4
  31. nerdd_module-0.3.7/nerdd_module/model/convert_representations_step.py +18 -0
  32. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/enforce_schema_step.py +3 -4
  33. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/model.py +43 -63
  34. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/read_input_step.py +4 -4
  35. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/simple_model.py +32 -21
  36. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/model/write_output_step.py +3 -3
  37. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/output/__init__.py +1 -1
  38. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/output/csv_writer.py +2 -4
  39. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/output/file_writer.py +5 -5
  40. nerdd_module-0.3.7/nerdd_module/output/iterator_writer.py +13 -0
  41. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/output/pandas_writer.py +4 -4
  42. nerdd_module-0.3.7/nerdd_module/output/record_list_writer.py +13 -0
  43. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/output/sdf_writer.py +1 -3
  44. nerdd_module-0.3.7/nerdd_module/output/writer.py +54 -0
  45. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/polyfills/__init__.py +1 -0
  46. nerdd_module-0.3.7/nerdd_module/polyfills/files.py +13 -0
  47. nerdd_module-0.3.7/nerdd_module/polyfills/get_entry_points.py +27 -0
  48. nerdd_module-0.3.7/nerdd_module/polyfills/types.py +14 -0
  49. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/check_valid_smiles.py +1 -1
  50. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/chembl_structure_pipeline.py +2 -2
  51. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/filter_by_element.py +1 -1
  52. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/filter_by_weight.py +6 -1
  53. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/preprocessing_step.py +5 -5
  54. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/remove_stereochemistry.py +2 -4
  55. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/sanitize.py +4 -3
  56. nerdd_module-0.3.7/nerdd_module/problem.py +16 -0
  57. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/steps/map_step.py +6 -6
  58. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/steps/step.py +1 -1
  59. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/__init__.py +0 -1
  60. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/checks.py +31 -54
  61. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/models/AtomicMassModel.py +19 -5
  62. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/models/MolWeightModel.py +18 -12
  63. nerdd_module-0.3.7/nerdd_module/tests/predictions.py +56 -0
  64. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/representations.py +1 -3
  65. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/util/__init__.py +0 -1
  66. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/util/call_with_mappings.py +1 -3
  67. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/util/package.py +2 -1
  68. nerdd_module-0.3.7/nerdd_module.egg-info/PKG-INFO +105 -0
  69. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module.egg-info/SOURCES.txt +5 -14
  70. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module.egg-info/requires.txt +8 -4
  71. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module.egg-info/top_level.txt +0 -1
  72. nerdd_module-0.3.7/pyproject.toml +134 -0
  73. nerdd_module-0.3.3/PKG-INFO +0 -77
  74. nerdd_module-0.3.3/README.md +0 -18
  75. nerdd_module-0.3.3/nerdd_module/config/yaml_configuration.py +0 -60
  76. nerdd_module-0.3.3/nerdd_module/converters/__init__.py +0 -2
  77. nerdd_module-0.3.3/nerdd_module/converters/converter.py +0 -16
  78. nerdd_module-0.3.3/nerdd_module/converters/converter_registry.py +0 -61
  79. nerdd_module-0.3.3/nerdd_module/converters/identity_converter.py +0 -5
  80. nerdd_module-0.3.3/nerdd_module/input/explorer.py +0 -16
  81. nerdd_module-0.3.3/nerdd_module/input/reader.py +0 -25
  82. nerdd_module-0.3.3/nerdd_module/input/reader_registry.py +0 -41
  83. nerdd_module-0.3.3/nerdd_module/model/add_smiles_step.py +0 -25
  84. nerdd_module-0.3.3/nerdd_module/output/iterator_writer.py +0 -13
  85. nerdd_module-0.3.3/nerdd_module/output/record_list_writer.py +0 -13
  86. nerdd_module-0.3.3/nerdd_module/output/writer.py +0 -18
  87. nerdd_module-0.3.3/nerdd_module/output/writer_registry.py +0 -50
  88. nerdd_module-0.3.3/nerdd_module/polyfills/files.py +0 -8
  89. nerdd_module-0.3.3/nerdd_module/polyfills/get_entry_points.py +0 -18
  90. nerdd_module-0.3.3/nerdd_module/problem.py +0 -17
  91. nerdd_module-0.3.3/nerdd_module/tests/predictions.py +0 -10
  92. nerdd_module-0.3.3/nerdd_module/tests/predictors.py +0 -42
  93. nerdd_module-0.3.3/nerdd_module/util/class_decorator.py +0 -29
  94. nerdd_module-0.3.3/nerdd_module.egg-info/PKG-INFO +0 -77
  95. nerdd_module-0.3.3/setup.py +0 -92
  96. nerdd_module-0.3.3/tests/conftest.py +0 -7
  97. nerdd_module-0.3.3/tests/steps/__init__.py +0 -3
  98. nerdd_module-0.3.3/tests/steps/checks.py +0 -77
  99. nerdd_module-0.3.3/tests/steps/input.py +0 -114
  100. nerdd_module-0.3.3/tests/steps/preprocessing.py +0 -111
  101. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/LICENSE +0 -0
  102. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/__init__.py +0 -0
  103. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/config/dict_configuration.py +0 -0
  104. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/polyfills/version.py +0 -0
  105. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/preprocessing/__init__.py +0 -0
  106. /nerdd_module-0.3.3/tests/__init__.py → /nerdd_module-0.3.7/nerdd_module/py.typed +0 -0
  107. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/steps/__init__.py +0 -0
  108. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/steps/output_step.py +0 -0
  109. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/models/__init__.py +0 -0
  110. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/preprocessing/DummyPreprocessingStep.py +0 -0
  111. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/tests/preprocessing/__init__.py +0 -0
  112. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module/version.py +0 -0
  113. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/nerdd_module.egg-info/dependency_links.txt +0 -0
  114. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/setup.cfg +0 -0
  115. {nerdd_module-0.3.3 → nerdd_module-0.3.7}/tests/test_features.py +0 -0
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.1
2
+ Name: nerdd-module
3
+ Version: 0.3.7
4
+ Summary: Base package to create NERDD modules
5
+ Author-email: Steffen Hirte <steffen.hirte@univie.ac.at>
6
+ Maintainer-email: Steffen Hirte <steffen.hirte@univie.ac.at>
7
+ License: BSD 3-Clause License
8
+
9
+ Copyright (c) 2023 - present, The Computational Drug Discovery and Design Group (COMP3D)
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ 2. Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ 3. Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+ Project-URL: Repository, https://github.com/molinfo-vienna/nerdd-module
36
+ Keywords: science,research,development,nerdd
37
+ Classifier: Intended Audience :: Science/Research
38
+ Classifier: Intended Audience :: Developers
39
+ Classifier: License :: OSI Approved :: BSD License
40
+ Classifier: Programming Language :: Python
41
+ Classifier: Topic :: Software Development
42
+ Classifier: Topic :: Scientific/Engineering
43
+ Classifier: Operating System :: Microsoft :: Windows
44
+ Classifier: Operating System :: POSIX
45
+ Classifier: Operating System :: Unix
46
+ Classifier: Operating System :: MacOS
47
+ Classifier: Programming Language :: Python :: 3
48
+ Classifier: Programming Language :: Python :: 3.9
49
+ Classifier: Programming Language :: Python :: 3.10
50
+ Classifier: Programming Language :: Python :: 3.11
51
+ Classifier: Programming Language :: Python :: 3.12
52
+ Description-Content-Type: text/markdown
53
+ License-File: LICENSE
54
+ Requires-Dist: pandas>=1.2.1
55
+ Requires-Dist: pyyaml>=6.0
56
+ Requires-Dist: filetype~=1.2.0
57
+ Requires-Dist: rich-click>=1.7.1
58
+ Requires-Dist: stringcase>=1.2.0
59
+ Requires-Dist: decorator>=5.1.1
60
+ Requires-Dist: importlib-resources>=5; python_version < "3.9"
61
+ Requires-Dist: importlib-metadata>=4.6; python_version < "3.10"
62
+ Provides-Extra: dev
63
+ Requires-Dist: mypy; extra == "dev"
64
+ Requires-Dist: ruff; extra == "dev"
65
+ Requires-Dist: pandas-stubs; extra == "dev"
66
+ Requires-Dist: types-PyYAML; extra == "dev"
67
+ Requires-Dist: types-decorator; extra == "dev"
68
+ Requires-Dist: types-setuptools; extra == "dev"
69
+ Provides-Extra: rdkit
70
+ Requires-Dist: rdkit>=2022.3.3; extra == "rdkit"
71
+ Provides-Extra: csp
72
+ Requires-Dist: chembl_structure_pipeline>=1.0.0; extra == "csp"
73
+ Provides-Extra: test
74
+ Requires-Dist: pytest; extra == "test"
75
+ Requires-Dist: pytest-sugar; extra == "test"
76
+ Requires-Dist: pytest-cov; extra == "test"
77
+ Requires-Dist: pytest-asyncio; extra == "test"
78
+ Requires-Dist: pytest-bdd; extra == "test"
79
+ Requires-Dist: pytest-mock; extra == "test"
80
+ Requires-Dist: pytest-watcher; extra == "test"
81
+ Requires-Dist: hypothesis; extra == "test"
82
+ Requires-Dist: hypothesis-rdkit; extra == "test"
83
+ Provides-Extra: docs
84
+ Requires-Dist: mkdocs; extra == "docs"
85
+ Requires-Dist: mkdocs-material; extra == "docs"
86
+ Requires-Dist: mkdocstrings; extra == "docs"
87
+
88
+ # NERDD Module
89
+
90
+ This package provides the basis to implement molecular prediction modules in the
91
+ NERDD ecosystem.
92
+
93
+ ## Installation
94
+
95
+ ``` bash
96
+ pip install -U nerdd-module
97
+ ```
98
+
99
+
100
+ ## Contribute
101
+
102
+ 1. Fork and clone the code
103
+ 2. Install test dependencies with ` pip install -e .[test,dev,csp]`
104
+ 3. Run tests via `pytest` or `pytest-watch` (short: `ptw`)
105
+ 4. Build docs via ` pip install -e .[docs]` and ` mkdocs serve`
@@ -0,0 +1,18 @@
1
+ # NERDD Module
2
+
3
+ This package provides the basis to implement molecular prediction modules in the
4
+ NERDD ecosystem.
5
+
6
+ ## Installation
7
+
8
+ ``` bash
9
+ pip install -U nerdd-module
10
+ ```
11
+
12
+
13
+ ## Contribute
14
+
15
+ 1. Fork and clone the code
16
+ 2. Install test dependencies with ` pip install -e .[test,dev,csp]`
17
+ 3. Run tests via `pytest` or `pytest-watch` (short: `ptw`)
18
+ 4. Build docs via ` pip install -e .[docs]` and ` mkdocs serve`
@@ -1,7 +1,5 @@
1
1
  from .cli import *
2
- from .input import ReaderRegistry
3
2
  from .model import *
4
- from .output import WriterRegistry
5
3
  from .polyfills import get_entry_points
6
4
  from .problem import *
7
5
  from .version import *
@@ -1,11 +1,13 @@
1
1
  import logging
2
- import os
3
2
  import sys
3
+ from typing import Any, Callable
4
4
 
5
5
  import rich_click as click
6
6
  from decorator import decorator
7
7
  from stringcase import spinalcase # type: ignore
8
8
 
9
+ from .model import Model
10
+
9
11
  __all__ = ["auto_cli"]
10
12
 
11
13
  input_description = """{description}
@@ -19,74 +21,70 @@ Note that input formats shouldn't be mixed.
19
21
  """
20
22
 
21
23
 
22
- def infer_click_type(param):
24
+ def infer_click_type(param: dict) -> click.ParamType:
23
25
  if "choices" in param:
24
26
  choices = [c["value"] for c in param["choices"]]
25
27
  return click.Choice(choices)
26
28
 
27
29
  type_map = {
28
- "float": float,
29
- "int": int,
30
- "str": str,
31
- "bool": bool,
30
+ "float": click.FLOAT,
31
+ "int": click.INT,
32
+ "str": click.STRING,
33
+ "bool": click.BOOL,
32
34
  }
33
35
 
34
- return type_map[param.get("type")]
36
+ if "type" not in param:
37
+ raise ValueError(f"Parameter {param['name']} does not have a type")
38
+
39
+ t = param["type"]
40
+
41
+ if t not in type_map:
42
+ raise ValueError(f"Unknown type {t} for parameter {param['name']}")
43
+
44
+ return type_map[t]
35
45
 
36
46
 
37
47
  @decorator
38
- def auto_cli(f, *args, **kwargs):
48
+ def auto_cli(f: Callable[..., Model], *args: Any, **kwargs: Any) -> None:
39
49
  # infer the command name
40
- command_name = os.path.basename(sys.argv[0])
50
+ # command_name = os.path.basename(sys.argv[0])
41
51
 
42
52
  # get the model
43
53
  model = f()
44
54
 
45
- config = model.get_config()
46
-
47
55
  # compose cli description
48
- description = config.get("description", "")
49
-
50
56
  input_format_list = "\n".join([f"* {fmt}" for fmt in ["smiles", "sdf", "inchi"]])
51
57
 
52
58
  help_text = input_description.format(
53
- description=description, input_format_list=input_format_list
59
+ description=model.description, input_format_list=input_format_list
54
60
  )
55
61
 
56
62
  output_format_list = ["sdf", "csv"]
57
63
 
58
64
  # compose footer with examples
59
- examples = []
60
- if "example_smiles" in config:
61
- examples.append(config["example_smiles"])
62
-
63
- if len(examples) > 0:
64
- footer = "Examples:\n"
65
- for example in examples:
66
- footer += f'* {command_name} "{example}"\n'
67
- else:
68
- footer = ""
65
+ # TODO: add examples
66
+ # examples = []
67
+ # if "example_smiles" in config:
68
+ # examples.append(config["example_smiles"])
69
+
70
+ # if len(examples) > 0:
71
+ # footer = "Examples:\n"
72
+ # for example in examples:
73
+ # footer += f'* {command_name} "{example}"\n'
74
+ # else:
75
+ # footer = ""
76
+ footer = ""
69
77
 
70
- # show_default=True: default values are shown in the help text
71
- # show_metavars_column=False: the column types are not in a separate column
72
- # append_metavars_help=True: the column types are shown below the help text
73
- @click.command(context_settings={"show_default": True}, help=help_text)
74
- @click.rich_config(
75
- help_config=click.RichHelpConfiguration(
76
- use_markdown=True,
77
- show_metavars_column=False,
78
- append_metavars_help=True,
79
- footer_text=footer,
80
- )
81
- )
82
- @click.argument("input", type=click.Path(), nargs=-1, required=True)
78
+ #
79
+ # Define the CLI entry point
80
+ #
83
81
  def main(
84
- input,
82
+ input: Any,
85
83
  format: str,
86
84
  output: click.Path,
87
85
  log_level: str,
88
- **kwargs,
89
- ):
86
+ **kwargs: Any,
87
+ ) -> None:
90
88
  logging.basicConfig(level=log_level.upper())
91
89
 
92
90
  # write results
@@ -99,10 +97,15 @@ def auto_cli(f, *args, **kwargs):
99
97
 
100
98
  model.predict(input, output_format=format, output_file=output_handle, **kwargs)
101
99
 
100
+ #
101
+ # Add required input parameter
102
+ #
103
+ main = click.argument("input", type=click.Path(), nargs=-1, required=True)(main)
104
+
102
105
  #
103
106
  # Add job parameters
104
107
  #
105
- for param in config.get("job_parameters", []):
108
+ for param in model.job_parameters:
106
109
  # convert parameter name to spinal case (e.g. "max_confs" -> "max-confs")
107
110
  param_name = spinalcase(param["name"])
108
111
  main = click.option(
@@ -132,10 +135,26 @@ def auto_cli(f, *args, **kwargs):
132
135
  main = click.option(
133
136
  "--log-level",
134
137
  default="warning",
135
- type=click.Choice(
136
- ["debug", "info", "warning", "error", "critical"], case_sensitive=False
137
- ),
138
+ type=click.Choice(["debug", "info", "warning", "error", "critical"], case_sensitive=False),
138
139
  help="The logging level.",
139
140
  )(main)
140
141
 
142
+ #
143
+ # Create Rich command
144
+ #
145
+
146
+ # show_metavars_column=False: the column types are not in a separate column
147
+ # append_metavars_help=True: the column types are shown below the help text
148
+ main = click.rich_config(
149
+ help_config=click.RichHelpConfiguration(
150
+ use_markdown=True,
151
+ show_metavars_column=False,
152
+ append_metavars_help=True,
153
+ footer_text=footer,
154
+ )
155
+ )(main)
156
+
157
+ # show_default=True: default values are shown in the help text
158
+ main = click.command(context_settings={"show_default": True}, help=help_text)(main)
159
+
141
160
  return main()
@@ -5,15 +5,34 @@ from typing import List
5
5
  __all__ = ["Configuration"]
6
6
 
7
7
 
8
- def get_property_columns_of_type(config, t) -> List[dict]:
8
+ def get_property_columns_of_type(config: dict, t: str) -> List[dict]:
9
9
  return [c for c in config["result_properties"] if c.get("level", "molecule") == t]
10
10
 
11
11
 
12
+ def is_visible(result_property: dict, output_format: str) -> bool:
13
+ formats = result_property.get("formats", {})
14
+
15
+ if isinstance(formats, list):
16
+ return output_format in formats
17
+ elif isinstance(formats, dict):
18
+ include = formats.get("include", "*")
19
+ exclude = formats.get("exclude", [])
20
+ assert include == "*" or isinstance(
21
+ include, list
22
+ ), f"Expected include to be a list or '*', got {include}"
23
+ assert isinstance(exclude, list), f"Expected exclude to be a list, got {exclude}"
24
+ return (include == "*" or output_format in include) and output_format not in exclude
25
+ else:
26
+ raise ValueError(
27
+ f"Invalid formats declaration {formats} in result property " f"{result_property}"
28
+ )
29
+
30
+
12
31
  class Configuration(ABC):
13
- def __init__(self):
32
+ def __init__(self) -> None:
14
33
  pass
15
34
 
16
- @lru_cache
35
+ @lru_cache(1)
17
36
  def get_dict(self) -> dict:
18
37
  config = self._get_dict()
19
38
 
@@ -22,9 +41,7 @@ class Configuration(ABC):
22
41
 
23
42
  # check that a module can only predict atom or derivative properties, not both
24
43
  num_atom_properties = len(get_property_columns_of_type(config, "atom"))
25
- num_derivative_properties = len(
26
- get_property_columns_of_type(config, "derivative")
27
- )
44
+ num_derivative_properties = len(get_property_columns_of_type(config, "derivative"))
28
45
  assert (
29
46
  num_atom_properties == 0 or num_derivative_properties == 0
30
47
  ), "A module can only predict atom or derivative properties, not both."
@@ -39,13 +56,13 @@ class Configuration(ABC):
39
56
  return self.get_dict() == {}
40
57
 
41
58
  def molecular_property_columns(self) -> List[dict]:
42
- return get_property_columns_of_type(self, "molecule")
59
+ return get_property_columns_of_type(self.get_dict(), "molecule")
43
60
 
44
61
  def atom_property_columns(self) -> List[dict]:
45
- return get_property_columns_of_type(self, "atom")
62
+ return get_property_columns_of_type(self.get_dict(), "atom")
46
63
 
47
64
  def derivative_property_columns(self) -> List[dict]:
48
- return get_property_columns_of_type(self, "derivative")
65
+ return get_property_columns_of_type(self.get_dict(), "derivative")
49
66
 
50
67
  def get_task(self) -> str:
51
68
  # if task is specified in the config, use that
@@ -64,8 +81,10 @@ class Configuration(ABC):
64
81
  else:
65
82
  return "molecular_property_prediction"
66
83
 
67
- def __getitem__(self, key):
68
- return self.get_dict()[key]
84
+ def get_visible_properties(self, output_format: str) -> List[dict]:
85
+ return [
86
+ p for p in self.get_dict().get("result_properties", []) if is_visible(p, output_format)
87
+ ]
69
88
 
70
- def __repr__(self):
89
+ def __repr__(self) -> str:
71
90
  return f"{self.__class__.__name__}({self._get_dict()})"
@@ -1,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  from stringcase import snakecase # type: ignore
2
4
 
3
5
  from ..polyfills import version
@@ -7,7 +9,7 @@ __all__ = ["DefaultConfiguration"]
7
9
 
8
10
 
9
11
  class DefaultConfiguration(DictConfiguration):
10
- def __init__(self, nerdd_module):
12
+ def __init__(self, nerdd_module: Any) -> None:
11
13
  # generate a name from the module name
12
14
  class_name = nerdd_module.__class__.__name__
13
15
  if class_name.endswith("Model"):
@@ -6,7 +6,7 @@ from .dict_configuration import DictConfiguration
6
6
  __all__ = ["MergedConfiguration"]
7
7
 
8
8
 
9
- def merge(*args):
9
+ def merge(*args: dict) -> dict:
10
10
  assert len(args) > 0
11
11
 
12
12
  first_entry = args[0]
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
11
11
 
12
12
 
13
13
  class PackageConfiguration(Configuration):
14
- def __init__(self, package):
14
+ def __init__(self, package: str) -> None:
15
15
  super().__init__()
16
16
 
17
17
  # get the resource directory
@@ -20,17 +20,16 @@ class PackageConfiguration(Configuration):
20
20
  except ModuleNotFoundError:
21
21
  root_dir = None
22
22
 
23
- if root_dir is None:
24
- self.config = DictConfiguration({})
25
- else:
23
+ self.config: Configuration = DictConfiguration({})
24
+ if root_dir is not None:
26
25
  # navigate to the config file
27
26
  config_file = root_dir / "nerdd.yml"
28
27
 
29
- if config_file is not None and config_file.exists():
28
+ if config_file is not None and config_file.is_file():
30
29
  logger.info(f"Found configuration file in package: {config_file}")
31
- self.config = YamlConfiguration(config_file, base_path=root_dir)
30
+ self.config = YamlConfiguration(config_file.open(), base_path=root_dir)
32
31
  else:
33
32
  self.config = DictConfiguration({})
34
33
 
35
- def _get_dict(self):
34
+ def _get_dict(self) -> dict:
36
35
  return self.config.get_dict()
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  import os
3
- import sys
4
- from typing import Any, Optional
3
+ from typing import Optional
5
4
 
6
5
  from .configuration import Configuration
7
6
  from .dict_configuration import DictConfiguration
@@ -32,9 +31,7 @@ class SearchYamlConfiguration(DictConfiguration):
32
31
  leaf = os.path.dirname(leaf)
33
32
 
34
33
  if default_config_file is not None:
35
- logger.info(
36
- f"Found configuration file in project directory: {default_config_file}"
37
- )
34
+ logger.info(f"Found configuration file in project directory: {default_config_file}")
38
35
  config = YamlConfiguration(default_config_file, base_path)
39
36
 
40
37
  super().__init__(config.get_dict())
@@ -0,0 +1,90 @@
1
+ import base64
2
+ import os
3
+ from pathlib import Path
4
+ from typing import IO, Any, Union
5
+
6
+ import filetype # type: ignore
7
+ import yaml
8
+ from typing_extensions import Protocol
9
+
10
+ from ..polyfills import PathLikeStr, Traversable, as_file
11
+ from .configuration import Configuration
12
+
13
+ __all__ = ["YamlConfiguration"]
14
+
15
+
16
+ class CustomLoaderLike(Protocol):
17
+ base_path: Union[str, PathLikeStr, Traversable]
18
+
19
+ def construct_scalar(self, node: yaml.ScalarNode) -> str: ...
20
+
21
+
22
+ def image_constructor(loader: CustomLoaderLike, node: yaml.Node) -> str:
23
+ assert isinstance(node, yaml.ScalarNode)
24
+
25
+ base_path = loader.base_path
26
+ if isinstance(base_path, Traversable):
27
+ pass
28
+ else:
29
+ base_path = Path(base_path)
30
+
31
+ # obtain the actual file path from the scalar string node
32
+ filepath = loader.construct_scalar(node)
33
+
34
+ p = base_path / filepath
35
+
36
+ assert p.is_file(), f"File {p} does not exist"
37
+
38
+ # load the image from the provided logo path and convert it to base64
39
+ with as_file(p) as path:
40
+ with open(path, "rb") as f:
41
+ encoded = base64.b64encode(f.read()).decode("ascii")
42
+
43
+ # determine the file type from the file extension
44
+ kind = filetype.guess(f)
45
+ assert kind is not None
46
+
47
+ return f"data:{kind.mime};base64,{encoded}"
48
+
49
+
50
+ class YamlConfiguration(Configuration):
51
+ def __init__(
52
+ self,
53
+ path_or_handle: Union[str, PathLikeStr, IO[str]],
54
+ base_path: Union[str, PathLikeStr, Traversable, None] = None,
55
+ ) -> None:
56
+ super().__init__()
57
+
58
+ if isinstance(path_or_handle, str):
59
+ path_or_handle = Path(path_or_handle)
60
+
61
+ if base_path is None:
62
+ assert isinstance(path_or_handle, Path) and os.path.isfile(
63
+ path_or_handle
64
+ ), f"File {path_or_handle} does not exist"
65
+ base_path = os.path.dirname(path_or_handle)
66
+
67
+ handle: IO[str]
68
+ if isinstance(path_or_handle, Path):
69
+ handle = path_or_handle.open()
70
+ elif hasattr(path_or_handle, "__fspath__"):
71
+ handle = open(path_or_handle)
72
+ else:
73
+ handle = path_or_handle
74
+
75
+ # we want to parse and process special tags (e.g. !image) in yaml files
76
+ # when loading a file with !image, the specified path should be relative to
77
+ # the yaml file itself
78
+ # --> need a custom loader that knows the path to the yaml file
79
+ class CustomLoader(yaml.SafeLoader, CustomLoaderLike):
80
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
81
+ super().__init__(*args, **kwargs)
82
+ assert base_path is not None, "base_path is None"
83
+ self.base_path = base_path
84
+
85
+ yaml.add_constructor("!image", image_constructor, CustomLoader)
86
+
87
+ self.yaml = yaml.load(handle, Loader=CustomLoader)
88
+
89
+ def _get_dict(self) -> dict:
90
+ return self.yaml["module"]
@@ -0,0 +1,2 @@
1
+ from .converter import *
2
+ from .identity_converter import *
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from functools import partial
5
+ from typing import Any, Callable, Dict, Optional, Tuple
6
+
7
+ from ..util import call_with_mappings
8
+
9
+ __all__ = ["Converter"]
10
+
11
+
12
+ _factories: Dict[Tuple[Optional[str], Optional[str]], Callable[[dict], Converter]] = {}
13
+
14
+
15
+ class Converter(ABC):
16
+ def __init__(self) -> None:
17
+ super().__init__()
18
+
19
+ @classmethod
20
+ def __init_subclass__(
21
+ cls,
22
+ output_format: Optional[str] = None,
23
+ data_type: Optional[str] = None,
24
+ is_abstract: bool = False,
25
+ **kwargs: Any,
26
+ ) -> None:
27
+ super().__init_subclass__(**kwargs)
28
+ if not is_abstract:
29
+ _factories[(data_type, output_format)] = partial(call_with_mappings, cls)
30
+
31
+ @abstractmethod
32
+ def _convert(self, input: Any, context: dict, **kwargs: Any) -> Any:
33
+ pass
34
+
35
+ def convert(self, input: Any, context: dict, **kwargs: Any) -> Any:
36
+ return self._convert(input, context, **kwargs)
37
+
38
+ @classmethod
39
+ def get_converter(
40
+ cls,
41
+ data_type: str,
42
+ output_format: str,
43
+ return_default: bool = True,
44
+ **kwargs: Any,
45
+ ) -> Converter:
46
+ if (data_type, output_format) not in _factories:
47
+ default = None
48
+ if return_default:
49
+ if (data_type, None) in _factories:
50
+ default = _factories[(data_type, None)]
51
+ elif (None, output_format) in _factories:
52
+ default = _factories[(None, output_format)]
53
+ elif (None, None) in _factories:
54
+ default = _factories[(None, None)]
55
+
56
+ if default is None:
57
+ raise ValueError(
58
+ f"Unknown data type '{data_type}' or output format '{output_format}'"
59
+ )
60
+ return default(kwargs)
61
+
62
+ return _factories[(data_type, output_format)](kwargs)
@@ -0,0 +1,8 @@
1
+ from typing import Any
2
+
3
+ from .converter import Converter
4
+
5
+
6
+ class IdentityConverter(Converter, data_type=None, output_format=None):
7
+ def _convert(self, input: Any, context: dict, **kwargs: Any) -> dict:
8
+ return input
@@ -6,7 +6,6 @@ from .inchi_reader import *
6
6
  from .list_reader import *
7
7
  from .mol_reader import *
8
8
  from .reader import *
9
- from .reader_registry import *
10
9
  from .sdf_reader import *
11
10
  from .smiles_reader import *
12
11
  from .string_reader import *