forked from chython/chython
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpyproject.toml
More file actions
196 lines (187 loc) · 12.3 KB
/
Copy pathpyproject.toml
File metadata and controls
196 lines (187 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
[build-system]
requires = ['setuptools>=77', 'cython>=3.1']
build-backend = 'setuptools.build_meta'
[project]
name = 'chython'
version = '3.2'
description = 'Library for processing molecules and reactions in python way'
authors = [{name = 'Ramil Nugmanov', email = 'nougmanoff@protonmail.com'}]
license = 'LGPL-3.0-or-later'
license-files = ['LICENSE']
readme = 'README.md'
requires-python = '>=3.10'
dependencies = [
# numpy is NOT here -- it is `chython[ml]`, below. The budget is a serverless bundle, uploaded
# whole on every deploy: numpy 1.26.4 is 44 MB installed against 7.4 MB of chython runtime files.
# A drop and not a trim, since pruning everything a caller demonstrably does not import still
# leaves ~40 MB.
#
# Nothing on the representation path needs it -- parsing, writing, standardization, kekulization,
# stereo, canonical form, the reactor and depiction are pure Python and C over the arena. What
# needs it is the surface that answers a numpy array by contract; `chython[ml]` enumerates that
# surface and `chython/test/test_optional_numpy.py` is the gate. Nor is it a BUILD dependency:
# there is no `cimport numpy` and no `numpy.get_include()`, because the two matrix builders reach
# their output through typed memoryviews and the PEP 3118 buffer protocol, so `_core.so` carries
# no numpy symbol and `python -m build --wheel` succeeds under default isolation.
#
# JS engine for `clean2d_engine = 'smilesdrawer'`, the default 2D layout backend. Required and
# not an extra: a default that works only once the user guesses an extra name is not a default.
# Affordable because the engine is QuickJS -- 0.5 MB on macOS/Windows, 2.4 MB on manylinux, and a
# 0.8 MB JS heap.
#
# `quickjs-ng` rather than `quickjs` for the wheel matrix: `cp310-abi3` means one wheel per
# platform serves every CPython from 3.10 onwards, over macOS arm64, manylinux and musllinux on
# x86_64/aarch64/i686, and win32/win_amd64.
'quickjs-ng>=0.16',
]
classifiers = [
'Environment :: Plugins',
'Intended Audience :: Science/Research',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3 :: Only',
# One line per interpreter the release workflow builds a wheel for, and `requires-python` above is
# the enforced statement -- these are what PyPI's filters read, so a missing line hides the project
# from a search for the version it actually supports.
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Topic :: Scientific/Engineering',
'Topic :: Scientific/Engineering :: Chemistry',
'Topic :: Scientific/Engineering :: Information Analysis',
'Topic :: Software Development',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
]
[project.urls]
homepage = 'https://github.com/chython/chython'
documentation = 'https://chython.readthedocs.io'
[project.optional-dependencies]
# The public names that need numpy, and nothing else in the library does. Without this extra each
# raises ImportError naming it AT THE CALL -- `import chython` is unaffected. The list is the count;
# `chython/test/test_optional_numpy.py` holds the executable copy.
#
# every `morgan_*` and `linear_*` method, `atom_invariants`, `adjacency_matrix`,
# `distance_matrix`, `eccentricities`, `wiener_index`, `graph_radius`, `graph_diameter`,
# `balaban_j`, `pharmacophore_invariants`, `maccs_keys`, `maccs_bit_set`.
#
# TWO GROUPS ARE NOT OBVIOUS FROM THEIR RETURN TYPES. `morgan_bit_set`, `morgan_hash_set`,
# `morgan_hash_counts`, their `linear_` counterparts and `maccs_bit_set` answer a plain set or dict and
# still need numpy, every one of them building an array first. And only the
# DISTANCE-DERIVED descriptors are listed: `tpsa`, `crippen_logp`, `crippen_mr`, `bertz_ct`,
# `randic_index`, `zagreb_index` and the atom and ring counts are numpy-free and stay available.
#
# Named `ml` for the consumer rather than the package: what a caller knows about themselves is that
# they are feeding a model, not that the array library underneath is numpy.
ml = ['numpy>=1.21.0']
# numpy is named directly and NOT left to onnxruntime, which requires it: an extra that works only
# because a sibling dependency happens to pull its requirement in is one that breaks when that sibling
# changes. NO SCIPY -- the shortest-path distances the encoding needs come from `state_view`, which is
# C in the core, so the one dependency the mapper adds over `ml` is the runtime and the weights.
mapping = ['numpy>=1.21.0', 'onnxruntime>=1.16', 'chython-rxnmap>=2.0']
rdkit = ['rdkit>=2025.9']
extra-clean2d = ['jpype1>=1.6.0', 'openbabel-wheel>=3.1.1.22', 'epam.indigo>=1.45']
extra-clean3d = ['cdpkit>=1.2.3']
iupac = ["openclatura>=0.2; python_version >= '3.11'"]
# PEP 735. `uv sync --group dev` or `pip install --group dev`, replacing `poetry install --with dev`.
# Not extras: a consumer of the library never wants these, and an extra is installable by a consumer.
[dependency-groups]
# `coverage[toml]` and not bare `coverage`: the settings are in `[tool.coverage]` below, and reading them
# needs tomli before 3.11, which the extra is.
dev = ['pytest>=7.4.3', 'coverage[toml]>=7.0']
docs = ['sphinx>=7.0', 'furo>=2024.1']
[tool.setuptools]
# Explicit, because at its default of `true` a wheel's data files are whatever `MANIFEST.in` swept
# into the sdist, so the two distributions disagree about their contents for reasons neither file
# states. `false` means the wheel holds exactly what `[tool.setuptools.package-data]` below names and
# `MANIFEST.in` describes only the sdist -- one file, one question.
include-package-data = false
[tool.setuptools.packages.find]
# `chython*` and nothing else: the repo root also holds `scripts/`, `mapping/`, `java/`, `pach/` and a
# top-level `test/` of data files, none importable and all of which auto-discovery would otherwise
# offer to package. No `exclude` is needed -- every `chython*` package is a shipped one.
include = ['chython*']
# Everything the installed package opens at runtime and cannot regenerate. Nothing is globbed, so a
# file omitted here is a file the wheel does not have.
#
# clean2d.js `depict/layout/molecule.py` reads it through `importlib.resources`; without it the
# default 2D engine raises on first use rather than at import.
#
# tables/*.tsv `chemistry/_tables.py` opens each through `importlib.resources` the first time
# anyone calls the pass that reads it -- `standardize()`, `fix_resonance()`, a salt
# pass, a featurizer. READ AT RUNTIME, so omitting one yields a wheel that imports
# cleanly and raises on the first molecule; `_tables.py`'s FileNotFoundError names
# this section. No count is written here: the list below is the count.
#
# THE `tables/` PREFIX IS PART OF THE PATTERN, not a directory setuptools infers.
# A package-data name is matched against the path relative to the package, so
# `resonance.tsv` alone ships nothing when the file is a level down -- which is why
# `test_packaging.py` compares relative paths and not bare filenames.
#
# libinchi IS DELIBERATELY ABSENT, the one runtime file that is. `package_data` can only name files in
# the SOURCE tree, so an entry here would oblige the build to write a binary into `chython/core/` and
# make a wheel's contents a function of what lies in the checkout. `setup.py` builds it into
# `build/inchi/` and stages it into the wheel from there; `test_libinchi_staging.py` is its gate, since
# this block's own gate (`test_packaging.py`) cannot be.
#
# `core`'s TSVs and RULES.md are not read at runtime. They ship because `core/test/` ships and each
# generator's `compile` verb is re-run against its TSV to prove the checked-in C tables are still that
# file's output -- without the TSV that gate has no input. RULES.md is what those tests cite.
#
# The pach corpora ship because they cannot be regenerated from this tree at all: every record was
# written by an installed chython 2 (2.24 for v2, 1.42 for native v0), so they are the only surviving
# specification of the legacy wire format `pack`/`unpack` read. Without them `test_pach.py` is a
# codec's acceptance test that cannot run and cannot be rebuilt. `reaction_pach_v2_corpus.bin.gz` is
# the same artefact one layer up, pinning the four header bytes `ReactionContainer.pack` prepends.
#
# `pach_v3_corpus.bin.gz` and `pach_v4_corpus.bin.gz` ship for the opposite reason: this tree CAN write
# them, which is why they are frozen. A fixture the test run regenerates pins no layout, so the
# committed bytes are the artefact and `gen_pach3_corpus.py` is run by hand.
# `pach_bond_group_corpus.bin.gz` is the same artefact for the bond enhanced-stereo block, written by the
# same generator and pinning the header field the earlier fifty records all leave zero.
#
# `arena_v4_corpus.bin.gz` has the strongest reason of the three: it is arena `STRUCT_VERSION` 4, which
# this tree no longer writes and no later build ever will, so `gen_v4_fixtures.py` cannot rerun. It is
# the only input to the migration gate that reads a pre-`SEG_PARITY` buffer.
#
# `modeling_view_corpus.json.gz` is a pin that CANNOT be rebuilt from this tree: it was recorded from
# the dict-of-dicts union `modeling_view()` had before the array kernel, so regenerating it would
# replace the pin with the thing being pinned.
#
# `golden_subset.smi` ships because the agreement floor in `reactions/test/test_attention.py` is a
# number about a corpus, and a floor whose corpus the wheel lacks is a claim that cannot be rechecked
# where it matters. 25 records of the public Golden benchmark, 10 kB; the full 1851 stay outside the
# package and are run by `bench/bench_attention_mapping.py`.
#
# ONE ENTRY PER LINE, however long the line gets. The reader of this block is a regex that needs it
# flat: a wrapped list makes it take the continuation for a package name and fail on a path assembled
# out of half a filename.
[tool.setuptools.package-data]
'chython.depict.layout' = ['clean2d.js']
'chython.depict.metrics' = ['helvetica.tsv', 'times.tsv']
'chython.core' = ['valence_rules.tsv', 'elements.tsv', 'isotopes.tsv', 'RULES.md']
'chython.chemistry' = ['tables/abbreviations.tsv', 'tables/standardize_groups.tsv', 'tables/standardize_metals.tsv', 'tables/resonance.tsv', 'tables/salts.tsv', 'tables/acids.tsv', 'tables/sybyl_types.tsv', 'tables/residues.tsv', 'tables/rotatable.tsv', 'tables/hbond.tsv', 'tables/pharmacophore.tsv', 'tables/tpsa.tsv', 'tables/crippen.tsv', 'tables/covalent_radii.tsv', 'tables/maccs.tsv', 'tables/maccs_corpus.tsv', 'tables/qed_alerts.tsv']
'chython.reactions' = ['tables/functional.tsv', 'tables/protective.tsv', 'tables/reactions.tsv', 'tables/roles.tsv']
'chython.core.test' = ['pach_v2_corpus.bin.gz', 'pach_v0_corpus.bin.gz', 'pach_v0_native_corpus.bin.gz', 'reaction_pach_v2_corpus.bin.gz', 'arena_v4_corpus.bin.gz', 'pach_v3_corpus.bin.gz', 'pach_v4_corpus.bin.gz', 'pach_bond_group_corpus.bin.gz', 'modeling_view_corpus.json.gz']
'chython.reactions.test' = ['golden_subset.smi']
'chython.formats.ctfile' = ['CTFILE.md']
# THE HARNESS BEHIND THE README'S COVERAGE BADGE. The `oracles` job in `.github/workflows/test.yml` runs
# the suite under these settings and uploads the report; the same two commands reproduce the number:
#
# python -m coverage run -m pytest chython/ -q --deselect chython/test/test_performance.py
# python -m coverage report
#
# IT MEASURES THE PYTHON LAYERS ONLY. `chython.core._core` is one compiled translation unit and
# coverage.py sees no lines in it, so `core/*.pxi` is in neither the numerator nor the denominator and the
# badge is labelled `python coverage`. What covers the core is the differential suite against chython 2
# and RDKit, which reports agreement rather than lines.
[tool.coverage.run]
source = ['chython']
# a test file's own lines are not a claim about the library, and every test in the tree lives in a `test/`
omit = ['*/test/*']
# paths as the repo writes them, so a reader outside the checkout -- the badge service -- can resolve one
relative_files = true
[tool.coverage.report]
precision = 2