deepmedchem 0.2.0b1__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 (34) hide show
  1. deepmedchem-0.2.0b1/.github/workflows/ci.yml +39 -0
  2. deepmedchem-0.2.0b1/.github/workflows/release.yml +48 -0
  3. deepmedchem-0.2.0b1/.gitignore +218 -0
  4. deepmedchem-0.2.0b1/LICENSE +21 -0
  5. deepmedchem-0.2.0b1/PKG-INFO +192 -0
  6. deepmedchem-0.2.0b1/README.md +154 -0
  7. deepmedchem-0.2.0b1/RELEASING.md +41 -0
  8. deepmedchem-0.2.0b1/examples/docs/durable_runs.py +22 -0
  9. deepmedchem-0.2.0b1/examples/docs/python_quickstart.py +13 -0
  10. deepmedchem-0.2.0b1/examples/docs/selection_builder.py +23 -0
  11. deepmedchem-0.2.0b1/examples/docs/substructure_search.py +32 -0
  12. deepmedchem-0.2.0b1/examples/live/README.md +49 -0
  13. deepmedchem-0.2.0b1/examples/live/enamine_known_product.py +33 -0
  14. deepmedchem-0.2.0b1/examples/live/enamine_named_drugs.py +28 -0
  15. deepmedchem-0.2.0b1/examples/notebooks/README.md +17 -0
  16. deepmedchem-0.2.0b1/examples/notebooks/enamine_search.ipynb +274 -0
  17. deepmedchem-0.2.0b1/pyproject.toml +69 -0
  18. deepmedchem-0.2.0b1/scripts/verify_backend_contract.py +97 -0
  19. deepmedchem-0.2.0b1/src/deepmedchem/__init__.py +42 -0
  20. deepmedchem-0.2.0b1/src/deepmedchem/aio.py +37 -0
  21. deepmedchem-0.2.0b1/src/deepmedchem/auth.py +71 -0
  22. deepmedchem-0.2.0b1/src/deepmedchem/cli.py +140 -0
  23. deepmedchem-0.2.0b1/src/deepmedchem/client.py +670 -0
  24. deepmedchem-0.2.0b1/src/deepmedchem/config.py +260 -0
  25. deepmedchem-0.2.0b1/src/deepmedchem/facade.py +159 -0
  26. deepmedchem-0.2.0b1/src/deepmedchem/models.py +258 -0
  27. deepmedchem-0.2.0b1/src/deepmedchem/py.typed +1 -0
  28. deepmedchem-0.2.0b1/src/deepmedchem/selection.py +315 -0
  29. deepmedchem-0.2.0b1/tests/test_auth.py +44 -0
  30. deepmedchem-0.2.0b1/tests/test_cli.py +20 -0
  31. deepmedchem-0.2.0b1/tests/test_client.py +312 -0
  32. deepmedchem-0.2.0b1/tests/test_config.py +76 -0
  33. deepmedchem-0.2.0b1/tests/test_documented_examples.py +127 -0
  34. deepmedchem-0.2.0b1/tests/test_facade.py +70 -0
@@ -0,0 +1,39 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ release:
7
+ types: [published]
8
+
9
+ concurrency:
10
+ group: deepmedchem-python-ci-${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test-and-package:
15
+ name: Python 3.10 test and package
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 10
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.10"
23
+ - run: python -m pip install -e ".[test]"
24
+ - run: ruff check .
25
+ - run: pytest
26
+ - run: python -m build
27
+ - run: python -m twine check dist/*
28
+ - name: Wheel installation smoke test
29
+ run: |
30
+ python -m venv /tmp/deepmedchem-wheel-smoke
31
+ /tmp/deepmedchem-wheel-smoke/bin/python -m pip install dist/*.whl
32
+ /tmp/deepmedchem-wheel-smoke/bin/python - <<'PY'
33
+ from deepmedchem import AsyncClient, Client, Run, Selection
34
+
35
+ selection = Selection.from_database("example").sample(seed=42).limit(10)
36
+ assert Run.selection(selection).to_dict()["schema_version"] == "run/1"
37
+ assert Client and AsyncClient
38
+ print("wheel import and builder smoke passed")
39
+ PY
@@ -0,0 +1,48 @@
1
+ name: release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ publish_pypi:
7
+ description: Publish this build with the configured PyPI Trusted Publisher
8
+ type: boolean
9
+ required: true
10
+ default: false
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 10
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.10"
24
+ - run: python -m pip install build twine
25
+ - run: python -m build
26
+ - run: python -m twine check dist/*
27
+ - uses: actions/upload-artifact@v4
28
+ with:
29
+ name: python-dist
30
+ path: dist/*
31
+ retention-days: 1
32
+
33
+ publish-pypi:
34
+ if: inputs.publish_pypi
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 10
38
+ environment: pypi
39
+ permissions:
40
+ id-token: write
41
+ steps:
42
+ - uses: actions/download-artifact@v4
43
+ with:
44
+ name: python-dist
45
+ path: dist
46
+ - uses: pypa/gh-action-pypi-publish@release/v1
47
+ with:
48
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,218 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deep MedChem
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepmedchem
3
+ Version: 0.2.0b1
4
+ Summary: Official Python client for the DeepMedChem chemical-space platform
5
+ Project-URL: Homepage, https://deepmedchem.com
6
+ Project-URL: Documentation, https://docs.deepmedchem.com/docs/python/quickstart
7
+ Project-URL: Repository, https://github.com/Deep-MedChem/deepmedchem-python
8
+ Project-URL: Issues, https://github.com/Deep-MedChem/deepmedchem-python/issues
9
+ Author: Deep MedChem
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: api-client,chemical-space,cheminformatics,drug-discovery
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx<1,>=0.27
25
+ Requires-Dist: keyring<27,>=25.3
26
+ Requires-Dist: platformdirs<5,>=4.2
27
+ Requires-Dist: pydantic<3,>=2.8
28
+ Requires-Dist: pyyaml<7,>=6
29
+ Requires-Dist: tomli<3,>=2; python_version < '3.11'
30
+ Provides-Extra: auth
31
+ Provides-Extra: test
32
+ Requires-Dist: build<2,>=1.2; extra == 'test'
33
+ Requires-Dist: keyring<27,>=25.3; extra == 'test'
34
+ Requires-Dist: pytest<10,>=8.2; extra == 'test'
35
+ Requires-Dist: ruff>=0.6; extra == 'test'
36
+ Requires-Dist: twine<7,>=5; extra == 'test'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # DeepMedChem Python SDK
40
+
41
+ The official, chemistry-thin Python client for the DeepMedChem hosted chemical-space platform.
42
+ It contains no RDKit, models, databases, or proprietary search implementation.
43
+
44
+ > **Beta:** `deepmedchem` 0.2 is available for early use. APIs may still change before the
45
+ > stable release.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install deepmedchem
51
+ ```
52
+
53
+ Authenticate once with the OS credential store, or set `DEEPMEDCHEM_API_KEY` in automation:
54
+
55
+ ```bash
56
+ deepmedchem login
57
+ deepmedchem status
58
+ ```
59
+
60
+ ## Quickstart
61
+
62
+ ```python
63
+ import deepmedchem as dmc
64
+
65
+ result = dmc.search(
66
+ "CC(=O)OC1=CC=CC=C1C(=O)O", # Aspirin
67
+ database="enamine-real-v5a",
68
+ method="shape",
69
+ limit=3,
70
+ )
71
+
72
+ print(repr(result))
73
+ for hit in result.hits:
74
+ price = f"${hit.price}" if hit.price is not None else "unavailable"
75
+ print(f"{hit.rank} score={hit.score:.4f} price={price} {hit.smiles}")
76
+ ```
77
+
78
+ Example output (the database release and search results can change):
79
+
80
+ ```text
81
+ SearchResult(3 molecules, method='shape', database='enamine-real-v5a')
82
+ 1 score=0.9726 price=$245 O=C(O)Oc1ccccc1C(=O)O
83
+ 2 score=0.9719 price=$163 COC(=O)Oc1ccccc1C(=O)O
84
+ 3 score=0.8713 price=$245 O=C(O)COc1ccccc1C(=O)O
85
+ ```
86
+
87
+ Prices are whole US dollars for delivery to the United States and default to 1 mg where the
88
+ vendor uses pack sizes. They are returned in the original search response, so both `hit.price`
89
+ and the aligned `result.prices` list are available without another API request. An unavailable
90
+ price is `None`.
91
+
92
+ | Database | Price available | Basis |
93
+ | --- | --- | --- |
94
+ | Freedom Space 5 | Yes | $250 at 1 mg |
95
+ | Enamine REAL | Yes | $163 or $245, selected by the trained factorized model |
96
+ | eMolecules Synple | Yes | Building-block prices plus reaction price |
97
+ | eMolecules eXplore | Yes | Building-block prices plus reaction price |
98
+ | VAST | No | — |
99
+ | d2b / molecule.one | No | — |
100
+ | ChemInfinita | No | — |
101
+
102
+ ## SMILES and SMARTS substructure search
103
+
104
+ Use `format="smiles"` for a concrete molecular graph, including the existing
105
+ junction-spanning examples. Use `format="smarts"` for atom lists, ring constraints,
106
+ recursive expressions, and other SMARTS query features:
107
+
108
+ ```python
109
+ junction = dmc.substructure(
110
+ "CNC(=O)N1CCC1", format="smiles", database="enamine-real-v5a", limit=10
111
+ )
112
+ hydrazides = dmc.substructure(
113
+ "[N;R0][N;R0]C(=O)", format="smarts", database="enamine-real-v5a", limit=10
114
+ )
115
+ ```
116
+
117
+ See the runnable [substructure example](examples/docs/substructure_search.py) for several
118
+ SQC-derived SMARTS queries. Complex recursive SMARTS can require a longer timeout.
119
+
120
+ Module-level `search`, `substructure`, `sample`, and `catalog` operations create and close a small
121
+ internal client. The explicit `Client` remains available for connection reuse and advanced
122
+ selections/runs. Search results behave as ordered SMILES sequences (`result[0]`, `result[:3]`,
123
+ `list(result)`) while retaining typed hits, scores, prices, metadata, warnings, and the complete raw
124
+ response locally.
125
+
126
+ Credentials resolve from an explicit `api_key`, `DEEPMEDCHEM_API_KEY`, compatibility environment
127
+ variables, a custom credential provider, or the selected profile's OS-keyring entry. Use
128
+ `deepmedchem login --profile dev` for the development service; profiles never share credentials.
129
+
130
+ Every request identifies its source with `X-DMC-Client`, `X-DMC-Client-Version`, and
131
+ `X-DMC-SDK-Version`. The default values attribute direct SDK use to `deepmedchem-python`; an
132
+ application such as Navigator can override `application` and `application_version` while retaining
133
+ the installed SDK version separately.
134
+
135
+ ## Selections and durable runs
136
+
137
+ `Selection` and `Run` are immutable, chemistry-thin builders. They produce the public
138
+ `molecule-selection/1` and `run/1` documents; all chemistry and capability validation remains on
139
+ the API.
140
+
141
+ ```python
142
+ from deepmedchem import Client, Run, Selection
143
+
144
+ template = (
145
+ Selection.from_database("enamine-real-v5a")
146
+ .ranked()
147
+ .maximize_similarity("rdkit.ecfp4_tanimoto", reference="query")
148
+ .limit(10)
149
+ )
150
+
151
+ run_spec = Run.selection_batch(
152
+ template=template,
153
+ items={
154
+ "lead-001": {"query": "CCO"},
155
+ "lead-002": {"query": "CCN"},
156
+ },
157
+ )
158
+
159
+ with Client() as dmc:
160
+ run = dmc.runs.create(run_spec, idempotency_key="lead-set-v1")
161
+ terminal = dmc.runs.wait(run.id)
162
+ results = list(dmc.runs.iter_results(terminal.id))
163
+ ```
164
+
165
+ `AsyncClient` offers matching asynchronous operations and iterators. `DMCClient` and
166
+ `AsyncDMCClient` are compatibility aliases for code written against the pre-split Navigator SDK.
167
+
168
+ ## Navigator
169
+
170
+ The `navigator` terminal application is distributed separately as `dmc-navigator`. It depends on
171
+ this SDK and adds file handling, login commands, terminal presentation, and Navigator-specific
172
+ workflows.
173
+
174
+ ## Development
175
+
176
+ ```bash
177
+ python -m venv .venv
178
+ . .venv/bin/activate
179
+ python -m pip install -e ".[test]"
180
+ ruff check .
181
+ pytest
182
+ python -m build
183
+ twine check dist/*
184
+ ```
185
+
186
+ API documentation: <https://docs.deepmedchem.com/docs/python/quickstart>
187
+
188
+ Runnable authenticated examples using the established Enamine query panels are in
189
+ [`examples/live`](examples/live/README.md).
190
+
191
+ For interactive RDKit visualization of similarity and SMARTS substructure queries, open the
192
+ [`Enamine search notebook`](examples/notebooks/enamine_search.ipynb).
@@ -0,0 +1,154 @@
1
+ # DeepMedChem Python SDK
2
+
3
+ The official, chemistry-thin Python client for the DeepMedChem hosted chemical-space platform.
4
+ It contains no RDKit, models, databases, or proprietary search implementation.
5
+
6
+ > **Beta:** `deepmedchem` 0.2 is available for early use. APIs may still change before the
7
+ > stable release.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install deepmedchem
13
+ ```
14
+
15
+ Authenticate once with the OS credential store, or set `DEEPMEDCHEM_API_KEY` in automation:
16
+
17
+ ```bash
18
+ deepmedchem login
19
+ deepmedchem status
20
+ ```
21
+
22
+ ## Quickstart
23
+
24
+ ```python
25
+ import deepmedchem as dmc
26
+
27
+ result = dmc.search(
28
+ "CC(=O)OC1=CC=CC=C1C(=O)O", # Aspirin
29
+ database="enamine-real-v5a",
30
+ method="shape",
31
+ limit=3,
32
+ )
33
+
34
+ print(repr(result))
35
+ for hit in result.hits:
36
+ price = f"${hit.price}" if hit.price is not None else "unavailable"
37
+ print(f"{hit.rank} score={hit.score:.4f} price={price} {hit.smiles}")
38
+ ```
39
+
40
+ Example output (the database release and search results can change):
41
+
42
+ ```text
43
+ SearchResult(3 molecules, method='shape', database='enamine-real-v5a')
44
+ 1 score=0.9726 price=$245 O=C(O)Oc1ccccc1C(=O)O
45
+ 2 score=0.9719 price=$163 COC(=O)Oc1ccccc1C(=O)O
46
+ 3 score=0.8713 price=$245 O=C(O)COc1ccccc1C(=O)O
47
+ ```
48
+
49
+ Prices are whole US dollars for delivery to the United States and default to 1 mg where the
50
+ vendor uses pack sizes. They are returned in the original search response, so both `hit.price`
51
+ and the aligned `result.prices` list are available without another API request. An unavailable
52
+ price is `None`.
53
+
54
+ | Database | Price available | Basis |
55
+ | --- | --- | --- |
56
+ | Freedom Space 5 | Yes | $250 at 1 mg |
57
+ | Enamine REAL | Yes | $163 or $245, selected by the trained factorized model |
58
+ | eMolecules Synple | Yes | Building-block prices plus reaction price |
59
+ | eMolecules eXplore | Yes | Building-block prices plus reaction price |
60
+ | VAST | No | — |
61
+ | d2b / molecule.one | No | — |
62
+ | ChemInfinita | No | — |
63
+
64
+ ## SMILES and SMARTS substructure search
65
+
66
+ Use `format="smiles"` for a concrete molecular graph, including the existing
67
+ junction-spanning examples. Use `format="smarts"` for atom lists, ring constraints,
68
+ recursive expressions, and other SMARTS query features:
69
+
70
+ ```python
71
+ junction = dmc.substructure(
72
+ "CNC(=O)N1CCC1", format="smiles", database="enamine-real-v5a", limit=10
73
+ )
74
+ hydrazides = dmc.substructure(
75
+ "[N;R0][N;R0]C(=O)", format="smarts", database="enamine-real-v5a", limit=10
76
+ )
77
+ ```
78
+
79
+ See the runnable [substructure example](examples/docs/substructure_search.py) for several
80
+ SQC-derived SMARTS queries. Complex recursive SMARTS can require a longer timeout.
81
+
82
+ Module-level `search`, `substructure`, `sample`, and `catalog` operations create and close a small
83
+ internal client. The explicit `Client` remains available for connection reuse and advanced
84
+ selections/runs. Search results behave as ordered SMILES sequences (`result[0]`, `result[:3]`,
85
+ `list(result)`) while retaining typed hits, scores, prices, metadata, warnings, and the complete raw
86
+ response locally.
87
+
88
+ Credentials resolve from an explicit `api_key`, `DEEPMEDCHEM_API_KEY`, compatibility environment
89
+ variables, a custom credential provider, or the selected profile's OS-keyring entry. Use
90
+ `deepmedchem login --profile dev` for the development service; profiles never share credentials.
91
+
92
+ Every request identifies its source with `X-DMC-Client`, `X-DMC-Client-Version`, and
93
+ `X-DMC-SDK-Version`. The default values attribute direct SDK use to `deepmedchem-python`; an
94
+ application such as Navigator can override `application` and `application_version` while retaining
95
+ the installed SDK version separately.
96
+
97
+ ## Selections and durable runs
98
+
99
+ `Selection` and `Run` are immutable, chemistry-thin builders. They produce the public
100
+ `molecule-selection/1` and `run/1` documents; all chemistry and capability validation remains on
101
+ the API.
102
+
103
+ ```python
104
+ from deepmedchem import Client, Run, Selection
105
+
106
+ template = (
107
+ Selection.from_database("enamine-real-v5a")
108
+ .ranked()
109
+ .maximize_similarity("rdkit.ecfp4_tanimoto", reference="query")
110
+ .limit(10)
111
+ )
112
+
113
+ run_spec = Run.selection_batch(
114
+ template=template,
115
+ items={
116
+ "lead-001": {"query": "CCO"},
117
+ "lead-002": {"query": "CCN"},
118
+ },
119
+ )
120
+
121
+ with Client() as dmc:
122
+ run = dmc.runs.create(run_spec, idempotency_key="lead-set-v1")
123
+ terminal = dmc.runs.wait(run.id)
124
+ results = list(dmc.runs.iter_results(terminal.id))
125
+ ```
126
+
127
+ `AsyncClient` offers matching asynchronous operations and iterators. `DMCClient` and
128
+ `AsyncDMCClient` are compatibility aliases for code written against the pre-split Navigator SDK.
129
+
130
+ ## Navigator
131
+
132
+ The `navigator` terminal application is distributed separately as `dmc-navigator`. It depends on
133
+ this SDK and adds file handling, login commands, terminal presentation, and Navigator-specific
134
+ workflows.
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ python -m venv .venv
140
+ . .venv/bin/activate
141
+ python -m pip install -e ".[test]"
142
+ ruff check .
143
+ pytest
144
+ python -m build
145
+ twine check dist/*
146
+ ```
147
+
148
+ API documentation: <https://docs.deepmedchem.com/docs/python/quickstart>
149
+
150
+ Runnable authenticated examples using the established Enamine query panels are in
151
+ [`examples/live`](examples/live/README.md).
152
+
153
+ For interactive RDKit visualization of similarity and SMARTS substructure queries, open the
154
+ [`Enamine search notebook`](examples/notebooks/enamine_search.ipynb).