diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 627be54..1adb421 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -10,7 +10,7 @@ on: jobs: python-format-black: - name: Python lint [black] + name: Python format [black] runs-on: ubuntu-latest steps: - name: Checkout the code @@ -27,3 +27,22 @@ jobs: - name: Lint run: black --check . + + python-lint-ruff: + name: Python lint [ruff] + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.9" + + - name: Install ruff + run: | + pip install ruff + + - name: Lint + run: ruff . diff --git a/docs/developers/contribute.md b/docs/developers/contribute.md index 5d7dfc6..56a7a74 100644 --- a/docs/developers/contribute.md +++ b/docs/developers/contribute.md @@ -10,14 +10,54 @@ The rest of this page details the development lifecycle of molfeat. ## Setup a dev environment -First you'll need to fork and clone the repository. Once you have a local copy, install the dependencies. -It is strongly recommended that you do so in a new conda environment. +To contribute, you will first need to setup a dev environment. Follow the steps below: -```bash -mamba env create -n molfeat -f env.yml -mamba activate molfeat -pip install -e . -``` +1. Fork the [repository](https://github.com/datamol-io/molfeat) by + clicking on the **[Fork](https://github.com/datamol-io/molfeat/fork)** button on the repository's page. This creates a copy of the code under your GitHub user account. + +2. Clone your fork to your local disk, and add the base repository as a remote: + + ```bash + git clone git@github.com:/molfeat.git + cd molfeat + git remote add upstream https://github.com/datamol-io/molfeat.git + ``` + +3. Create a new branch to hold your development changes: + + ```bash + git checkout -b useful-branch-name + ``` + + **Do not** work on the `main` branch! + +4. Once you have a local copy, setup a development environment and install the dependencies. + + It is strongly recommended that you do so in a new **conda environment**. + + ```bash + mamba env create -n molfeat -f env.yml + conda activate molfeat + pip install -e . --no-deps + ``` + + If you absolutely cannot use `conda/mamba`, please use the following pip install command in your virtual environment: + + ```bash + pip install -e ".[dev]" + ``` + + If molfeat was already installed in the virtual environment, remove it with `pip uninstall molfeat` first, before reinstalling it in editable mode with the `-e` flag. + +5. Make your changes and modifications on your branch. + + As you work on your code, you should make sure the test suite passes. Run the tests impacted by your changes like this: + + ```bash + pytest tests/.py + ``` + +6. Commit your code, push it to your forked repository and open a pull request with a detailed description of your changes and why they are valuable. ## Continuous Integration @@ -27,7 +67,7 @@ molfeat uses Github Actions to: - **Check code formating** the code: `black`. - **Documentation**: build and deploy the documentation on `main` and for every new git tag. -## Run tests +## Run tests globally ```bash pytest diff --git a/env.yml b/env.yml index a057c87..7a9c761 100644 --- a/env.yml +++ b/env.yml @@ -57,6 +57,7 @@ dependencies: - pytest-timeout - pytest-xdist - black >=22 + - ruff - jupyterlab - nbconvert diff --git a/molfeat/calc/base.py b/molfeat/calc/base.py index 1735e34..60b7df6 100644 --- a/molfeat/calc/base.py +++ b/molfeat/calc/base.py @@ -6,7 +6,6 @@ import joblib import yaml import fsspec -import importlib from loguru import logger from molfeat._version import __version__ as MOLFEAT_VERSION diff --git a/molfeat/calc/bond.py b/molfeat/calc/bond.py index aac6409..92157d6 100644 --- a/molfeat/calc/bond.py +++ b/molfeat/calc/bond.py @@ -19,7 +19,6 @@ from molfeat.calc._atom_bond_features import bond_direction_one_hot from molfeat.calc._atom_bond_features import bond_stereo_one_hot from molfeat.calc._atom_bond_features import pairwise_ring_membership -from molfeat.calc._atom_bond_features import pairwise_3D_dist from molfeat.calc._atom_bond_features import pairwise_2D_dist from molfeat.calc._atom_bond_features import pairwise_bond_indicator from molfeat.calc._atom_bond_features import pairwise_dist_indicator diff --git a/molfeat/calc/descriptors.py b/molfeat/calc/descriptors.py index bd21958..742a640 100644 --- a/molfeat/calc/descriptors.py +++ b/molfeat/calc/descriptors.py @@ -55,6 +55,13 @@ class RDKitDescriptors2D(SerializableCalculator): r""" Compute a list of available rdkit 2D descriptors for a molecule. The descriptor calculator does not mask errors in featurization and will propagate them + + !!! note + Due to recent RDKit changes, this calculator could hang for a while for large molecules (>900). + The main culprit is the `Ipc` descriptor which requires some computation on the full adjacency matrix. + You may also consider using `ignore_descrs=['AvgIpc', 'Ipc']` as a workaround when you have large molecules. + Alternatively, You may wish to use an alternative featurizer + """ DESCRIPTORS_FN = {name: fn for (name, fn) in Descriptors.descList} @@ -66,6 +73,7 @@ def __init__( descrs: List = None, avg_ipc: Optional[bool] = True, do_not_standardize: Optional[bool] = False, + ignore_descrs: Optional[List] = None, **kwargs, ): """RDKit descriptor computation @@ -75,6 +83,8 @@ def __init__( augment: Whether to augment the descriptors with some additional custom features descrs: Subset of available features to consider if not None avg_ipc: Whether to average IPC values or to use rdkit original + ignore_descrs: optional list of descriptors to ignore. You can get the full list of default descriptors + by calling `calculator.columns`. do_not_standardize: Whether to force standardization of molecule before computation of the descriptor. Set to True if you want molfeat<=0.5.3 behaviour """ @@ -84,6 +94,7 @@ def __init__( self.avg_ipc = avg_ipc self.do_not_standardize = do_not_standardize all_features = [d[0] for d in Descriptors.descList] + self.ignore_descrs = ignore_descrs or [] if self.augment: all_features += [ "NumAtomStereoCenters", @@ -101,6 +112,8 @@ def __init__( else: self._columns = all_features + self._columns = [x for x in self._columns if x not in self.ignore_descrs] + def __getstate__(self): """Serialize the class for pickling.""" state = {} @@ -266,7 +279,7 @@ def __call__(self, mol: Union[dm.Mol, str], conformer_id: Optional[int] = -1) -> if desc not in self.ignore_descrs: try: val = getattr(Descriptors3D.rdMolDescriptors, desc)(mol, confId=conformer_id) - except: + except Exception: pass desc_val.append(val) for i, desc in enumerate(self._vec_descr): @@ -274,7 +287,7 @@ def __call__(self, mol: Union[dm.Mol, str], conformer_id: Optional[int] = -1) -> if desc not in self.ignore_descrs: try: val = getattr(Descriptors3D.rdMolDescriptors, desc)(mol, confId=conformer_id) - except: + except Exception: pass desc_val.extend(val) diff --git a/molfeat/calc/fingerprints.py b/molfeat/calc/fingerprints.py index 1204464..7144436 100644 --- a/molfeat/calc/fingerprints.py +++ b/molfeat/calc/fingerprints.py @@ -221,7 +221,7 @@ def __init__( """ self.method = method.lower() self.counting = counting or "-count" in self.method - if self.counting and not "-count" in self.method: + if self.counting and "-count" not in self.method: self.method = self.method + "-count" self.input_length = length if self.method not in FP_FUNCS: diff --git a/molfeat/calc/tree.py b/molfeat/calc/tree.py index 51df1d1..a619fe4 100644 --- a/molfeat/calc/tree.py +++ b/molfeat/calc/tree.py @@ -1,7 +1,5 @@ -from typing import Iterable from typing import List from typing import Union -from typing import Tuple from typing import Optional from collections import defaultdict diff --git a/molfeat/store/loader.py b/molfeat/store/loader.py index 4397839..2e00eda 100644 --- a/molfeat/store/loader.py +++ b/molfeat/store/loader.py @@ -98,7 +98,7 @@ def _load_or_raise( try: modelcard = store.search(name=name)[0] artifact_dir = store.download(modelcard, download_path, **kwargs) - except Exception as e: + except Exception: mess = f"Can't retrieve model {name} from the store !" raise ModelStoreError(mess) return artifact_dir diff --git a/molfeat/store/modelcard.py b/molfeat/store/modelcard.py index 97f4bc9..735afdf 100644 --- a/molfeat/store/modelcard.py +++ b/molfeat/store/modelcard.py @@ -15,7 +15,7 @@ def get_model_init(card): import_statement = "from molfeat.trans import MoleculeTransformer" loader_statement = f"MoleculeTransformer(featurizer='{card.name}', dtype=float)" elif card.group in ["rdkit", "fp", "shape"]: - import_statement = f"from molfeat.trans.fp import FPVecTransformer" + import_statement = "from molfeat.trans.fp import FPVecTransformer" loader_statement = f"FPVecTransformer(kind='{card.name}', dtype=float)" elif card.group == "dgllife": import_statement = "from molfeat.trans.pretrained import PretrainedDGLTransformer" @@ -25,7 +25,7 @@ def get_model_init(card): loader_statement = f"GraphormerTransformer(kind='{card.name}', dtype=float)" elif card.group == "fcd": import_statement = "from molfeat.trans.pretrained import FCDTransformer" - loader_statement = f"FCDTransformer()" + loader_statement = "FCDTransformer()" elif card.group == "pharmacophore": name = card.name.split("-")[-1] if card.require_3D: diff --git a/molfeat/store/modelstore.py b/molfeat/store/modelstore.py index 516ed73..aba0830 100644 --- a/molfeat/store/modelstore.py +++ b/molfeat/store/modelstore.py @@ -123,7 +123,7 @@ def register( if save_fn is None: if not isinstance(model, (pathlib.Path, os.PathLike)): local_model_path = tempfile.NamedTemporaryFile(delete=False) - with local_model_path as f: + with local_model_path: joblib.dump(model, local_model_path) model_path = local_model_path.name # Upload the artifact to the bucket @@ -152,10 +152,10 @@ def _filelock(self, lock_name: str): lock_path = dm.fs.join( str(platformdirs.user_cache_dir("molfeat")), "_lock_files", lock_name ) - mapper = dm.fs.get_mapper(lock_path) + dm.fs.get_mapper(lock_path) # ensure file is created # out = mapper.fs.touch(lock_path) # does not work -_- - with fsspec.open(lock_path, "w", auto_mkdir=True) as f: + with fsspec.open(lock_path, "w", auto_mkdir=True): pass return filelock.FileLock(lock_path) diff --git a/molfeat/trans/base.py b/molfeat/trans/base.py index 464476b..b57e72c 100644 --- a/molfeat/trans/base.py +++ b/molfeat/trans/base.py @@ -36,8 +36,6 @@ from molfeat.utils.parsing import get_input_args from molfeat.utils.parsing import import_from_string from molfeat.utils.state import map_dtype -from molfeat.utils.state import ATOM_FEATURIZER_MAPPING -from molfeat.utils.state import BOND_FEATURIZER_MAPPING from molfeat.utils.state import ATOM_FEATURIZER_MAPPING_REVERSE from molfeat.utils.state import BOND_FEATURIZER_MAPPING_REVERSE diff --git a/molfeat/trans/graph/adj.py b/molfeat/trans/graph/adj.py index a409a34..226e3e1 100644 --- a/molfeat/trans/graph/adj.py +++ b/molfeat/trans/graph/adj.py @@ -19,7 +19,6 @@ from molfeat.utils.commons import requires_conformer from molfeat.utils.commons import pack_graph from molfeat.calc.atom import AtomCalculator -from molfeat.calc.bond import BondCalculator from molfeat.calc.bond import EdgeMatCalculator if requires.check("dgl"): @@ -103,7 +102,7 @@ def preprocess(self, inputs, labels=None): mol = dm.to_mol( m, add_hs=self.explicit_hydrogens, ordered=self.canonical_atom_order ) - except: + except Exception: mol = None new_inputs.append(mol) @@ -126,7 +125,7 @@ def atom_dim(self): if self._atom_dim is None: try: self._atom_dim = len(self.atom_featurizer) - except: + except Exception: _toy_mol = dm.to_mol("C") out = self.atom_featurizer(_toy_mol) self._atom_dim = sum([x.shape[-1] for x in out.values()]) @@ -145,7 +144,7 @@ def bond_dim(self): if self._bond_dim is None: try: self._bond_dim = len(self.bond_featurizer) - except: + except Exception: _toy_mol = dm.to_mol("CO") out = self.bond_featurizer(_toy_mol) self._bond_dim = sum([x.shape[-1] for x in out.values()]) diff --git a/molfeat/trans/pretrained/base.py b/molfeat/trans/pretrained/base.py index 0e684c3..0f607b5 100644 --- a/molfeat/trans/pretrained/base.py +++ b/molfeat/trans/pretrained/base.py @@ -3,7 +3,6 @@ from typing import Callable from typing import List from collections.abc import Iterable -from functools import lru_cache import copy import datamol as dm @@ -131,7 +130,7 @@ def preprocess(self, inputs: list, labels: Optional[list] = None): if self.precompute_cache not in [False, None]: try: self.transform(inputs) - except: + except Exception: pass return out diff --git a/molfeat/trans/struct/esm.py b/molfeat/trans/struct/esm.py index 3b67dac..7ad57a1 100644 --- a/molfeat/trans/struct/esm.py +++ b/molfeat/trans/struct/esm.py @@ -57,7 +57,7 @@ def __init__( self._max_layers = int(max_layer_pattern.match(featurizer).group(1)) if layers is None: self.repr_layers = [self._max_layers] - if any(l > self._max_layers for l in self.repr_layers): + if any(lay > self._max_layers for lay in self.repr_layers): raise ValueError( "You are requesting more layers than available for this pretrained model" ) diff --git a/molfeat/utils/cache.py b/molfeat/utils/cache.py index 4e9c2d7..fb6e8fd 100644 --- a/molfeat/utils/cache.py +++ b/molfeat/utils/cache.py @@ -260,7 +260,7 @@ def fetch( try: cacher = copy.deepcopy(self) n_jobs = self.n_jobs - except: # noqa + except Exception: # noqa # cannot parallelize process, ensure n_jobs is 0 cacher = self n_jobs = 0 @@ -357,7 +357,7 @@ def clear(self, delete: bool = False): for path in glob.glob(str(self.cache_file) + "*"): try: os.unlink(path) - except: # noqa + except Exception: # noqa pass else: self._initialize_cache() diff --git a/molfeat/utils/converters.py b/molfeat/utils/converters.py index 9e1c71e..92d8d14 100644 --- a/molfeat/utils/converters.py +++ b/molfeat/utils/converters.py @@ -45,7 +45,7 @@ def decode(self, inp: str): try: decoded = self.converter.decode(inp) return decoded.strip() - except: # (deepsmiles.DecodeError, ValueError, AttributeError, IndexError): + except Exception: # (deepsmiles.DecodeError, ValueError, AttributeError, IndexError): return None def encode(self, smiles: str): @@ -60,5 +60,5 @@ def encode(self, smiles: str): try: encoded = self.converter.encode(smiles) return encoded.strip() - except: + except Exception: return None diff --git a/molfeat/utils/datatype.py b/molfeat/utils/datatype.py index 9eaea70..adad54f 100644 --- a/molfeat/utils/datatype.py +++ b/molfeat/utils/datatype.py @@ -72,7 +72,7 @@ def to_tensor(x, gpu=False, dtype=None): try: if torch.is_tensor(x[0]): x = torch.stack(x) - except: + except Exception: pass x = torch.as_tensor(x) if dtype is not None: @@ -205,11 +205,11 @@ def is_null(obj): try: tmp = to_numpy(obj) array_nan = np.all(np.isnan(tmp)) - except: + except Exception: pass try: all_none = all(x is None for x in obj) - except: + except Exception: pass return obj is None or all_none or array_nan diff --git a/molfeat/utils/requires.py b/molfeat/utils/requires.py index 9f918d3..2b5b812 100644 --- a/molfeat/utils/requires.py +++ b/molfeat/utils/requires.py @@ -21,12 +21,12 @@ def check(module: str, min_version: Optional[str] = None, max_version: Optional[ try: imported_module = importlib.import_module(module) version = getattr(imported_module, "__version__", None) - except ImportError as _: + except ImportError: return False if version is not None: try: version = pkg_version.parse(version) - except pkg_version.InvalidVersion as _: + except pkg_version.InvalidVersion: # EN: packaging v22 removed LegacyVersion which has consequences version = None return version is None or ( diff --git a/pyproject.toml b/pyproject.toml index 24474b6..0590c45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,10 @@ all = [ "ipywidgets", ] +test = ["pytest >=6.0","pytest-dotenv", "pytest-cov", "pytest-xdist", "black >=22", "ruff"] +docs = ["mkdocs", "mike", "mdx_truly_sane_lists", "mkdocs-material >=7.1.1", "mkdocs-jupyter", "mkdocstrings", "mkdocstrings-python", "markdown-include"] +dev = ["molfeat[test]", "molfeat[all]", "molfeat[docs]"] + [project.urls] Website = "https://molfeat.datamol.io" "Source Code" = "https://github.com/datamol-io/molfeat" @@ -104,6 +108,23 @@ line-length = 100 target-version = ['py39', 'py310'] include = '\.pyi?$' +[tool.ruff] +ignore = [ + "E501", # Never enforce `E501` (line length violations). + "E731", # Do not assign a lambda expression, use a def +] +line-length = 110 +target-version = "py311" + +[tool.ruff.per-file-ignores] +"__init__.py" = [ + "F401", # imported but unused + "E402", # Module level import not at top of file +] + +[tool.ruff.pycodestyle] +max-doc-length = 150 + [tool.pytest.ini_options] minversion = "6.0" addopts = "--verbose --cov-report xml --cov-report term --color yes" diff --git a/tests/test_atom_bond_calculator.py b/tests/test_atom_bond_calculator.py index f619784..12f6aa6 100644 --- a/tests/test_atom_bond_calculator.py +++ b/tests/test_atom_bond_calculator.py @@ -86,7 +86,7 @@ def test_atom_calculator(self): dgl_atom_calc = DGLCanonicalAtomCalculator() true_dgl_atom_calc = CanonicalAtomFeaturizer(atom_data_field="hv") for mol in self.mols: - a1 = atom_calc(mol) + atom_calc(mol) a2 = dgl_atom_calc(mol) a3 = true_dgl_atom_calc(mol) np.testing.assert_allclose(a2["hv"], a3["hv"]) @@ -96,7 +96,7 @@ def test_bond_calculator(self): dgl_bond_calc = DGLCanonicalBondCalculator() true_dgl_bond_calc = CanonicalBondFeaturizer(bond_data_field="he") for mol in self.mols: - b1 = bond_calc(mol) + bond_calc(mol) b2 = dgl_bond_calc(mol) b3 = true_dgl_bond_calc(mol) if "he" in b3: diff --git a/tests/test_descriptors.py b/tests/test_descriptors.py index ec84a32..878a63c 100644 --- a/tests/test_descriptors.py +++ b/tests/test_descriptors.py @@ -46,7 +46,6 @@ def test_rdkit2d(self): fps = calc(sm) fps2 = calc(sm_disconnected) np.testing.assert_allclose(fps, fps2) - # with the fix we should not have any value that is nan after sanitization # neither for Charge related or BCut2D properties self.assertFalse(np.isnan(fps).any()) @@ -58,10 +57,11 @@ def test_rdkit2d(self): bcut_cols = [i for i, x in enumerate(calc.columns) if "bcut" in x.lower()] self.assertTrue(np.isnan(fps[bcut_cols]).all()) - # sanity check for large molecules - ipc_colums = calc.columns.index("Ipc") + def test_rdkit2d_large_mol_no_ipc(self): + # sanity check for large molecules that will hang forerever + calc = RDKitDescriptors2D(ignore_descrs=["Ipc", "AvgIpc"]) fps = calc(self.EXTRA_LARGE_MOL) - self.assertLessEqual(fps[ipc_colums], 1e3) + self.assertEqual(len(fps), len(calc)) def test_rdkit3d(self): calc = RDKitDescriptors3D() @@ -118,7 +118,7 @@ def test_cats_pickle(self): def test_shape_descriptors(self): calc = USRDescriptors("usrcat") - with self.assertRaises(ValueError) as context: + with self.assertRaises(ValueError): calc(self.smiles[0]) mol_with_conf = dm.conformers.generate(dm.to_mol(self.smiles[0])) out = calc(mol_with_conf) diff --git a/tests/test_fp.py b/tests/test_fp.py index 21431c0..d432fd1 100644 --- a/tests/test_fp.py +++ b/tests/test_fp.py @@ -11,7 +11,7 @@ import torch from rdkit.DataStructs.cDataStructs import ExplicitBitVect -from molfeat.calc import RDKitDescriptors2D, SerializableCalculator +from molfeat.calc import SerializableCalculator from molfeat.calc.fingerprints import FPCalculator from molfeat.calc.pharmacophore import Pharmacophore2D from molfeat.calc.descriptors import MordredDescriptors @@ -95,11 +95,11 @@ def test_transformer_parallel_mol(self): transf1 = MoleculeTransformer(featurizer=fpkind, n_jobs=1) transf2 = MoleculeTransformer(featurizer=fpkind, n_jobs=-1) smiles = dm.freesolv()["smiles"].sample(n=2000, replace=True).values - t0 = time.time() + time.time() out1 = transf1.transform(smiles) - t1 = time.time() + time.time() out2 = transf2.transform(smiles) - t2 = time.time() + time.time() np.testing.assert_allclose(out1, out2) # we should be technically saving time with parallelization # self.assertLessEqual(t2 - t1, t1 - t0) @@ -132,13 +132,13 @@ def test_fp_parallel(self): def test_unknown_kind_exception(self): with self.assertRaises(ValueError) as context: - transf = FPVecTransformer(kind="notakind", length=300) + FPVecTransformer(kind="notakind", length=300) self.assertTrue("is not a valid" in str(context.exception)) def test_none_mol_exception(self): transf = MoleculeTransformer("rdkit") with self.assertRaises(ValueError) as context: - fps = transf.transform([None]) + transf.transform([None]) self.assertTrue("transform molecule at" in str(context.exception)) feat = transf.transform([None], ignore_errors=True) self.assertEqual(feat[0], None) @@ -167,7 +167,7 @@ def test_dtype_casting(self): def test_3d_exception(self): with self.assertRaises(ValueError) as context: transf = MoleculeTransformer("desc3D", verbose=True) - fp = transf.transform(self.smiles, ignore_errors=False) + transf.transform(self.smiles, ignore_errors=False) self.assertTrue("molecule with conformer" in str(context.exception)) def test_fp_filtering(self): @@ -310,7 +310,7 @@ def test_cached_featurizer_pickling(self): try: os.unlink(parquet_out) - except: + except Exception: shutil.rmtree(parquet_out) diff --git a/tests/test_pharmacophore.py b/tests/test_pharmacophore.py index effa25b..1049745 100644 --- a/tests/test_pharmacophore.py +++ b/tests/test_pharmacophore.py @@ -63,15 +63,15 @@ def test_pharmacophore_2d_override(): includeBondOrder=True, ) - assert featurizer.useCounts == True + assert featurizer.useCounts is True assert featurizer.minPointCount == 3 assert featurizer.maxPointCount == 4 - assert featurizer.trianglePruneBins == True - assert featurizer.shortestPathsOnly == False + assert featurizer.trianglePruneBins is True + assert featurizer.shortestPathsOnly is False assert featurizer.skipFeats == [ "A", ] - assert featurizer.includeBondOrder == True + assert featurizer.includeBondOrder is True assert featurizer.useCounts == featurizer.sig_factory.useCounts assert featurizer.minPointCount == featurizer.sig_factory.minPointCount diff --git a/tests/test_state.py b/tests/test_state.py index 050cedc..95a2458 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -157,7 +157,7 @@ def test_state_atom_bond_pickle(featurizer_builder, tmp_path): featurizer2 = MoleculeTransformer.from_state_dict(state2) to_remove = [] for key in state["args"]: - if key.endswith("is_pickled") and state["args"][key] == True: + if key.endswith("is_pickled") and state["args"][key] is True: to_remove.append(key.replace("is_pickled", "").strip("_")) for key_val in to_remove: state["args"].pop(key_val, None) @@ -169,7 +169,7 @@ def test_state_atom_bond_pickle(featurizer_builder, tmp_path): assert len(out) == len(out2) == len(mols) for o1, o2 in zip(out, out2): if isinstance(o1, (list, tuple)): - assert np.all([np.allclose(i, j, atol=2) for i, j in zip(o1, o2)]) == True + assert bool(np.all([np.allclose(i, j, atol=2) for i, j in zip(o1, o2)])) is True else: np.testing.assert_array_equal(o1, o2) @@ -182,7 +182,7 @@ def test_PrecomputedMolTransformer_state(tmp_path): cache = FileCache(cache_file=cache_path, file_type="parquet", n_jobs=-1) featurizer_cache = PrecomputedMolTransformer(cache=cache, featurizer=featurizer) - fps = featurizer_cache(smiles_list) + featurizer_cache(smiles_list) # sanity check assert len(featurizer_cache.cache.cache) == 2 @@ -214,7 +214,7 @@ def test_PrecomputedMolTransformer_init_from_state_file(tmp_path): cache = FileCache(cache_file=cache_path, file_type="parquet", n_jobs=-1) featurizer_cache = PrecomputedMolTransformer(cache=cache, featurizer=featurizer) - fps = featurizer_cache(smiles_list) + featurizer_cache(smiles_list) # sanity check assert len(featurizer_cache.cache.cache) == 2 diff --git a/tests/test_utils.py b/tests/test_utils.py index 1941dc1..09de0d4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -112,7 +112,7 @@ def test_datacache(self): np.testing.assert_array_equal(new_cache[first_smiles], first_smiles_val) try: os.unlink(save_file) - except: + except Exception: pass def test_filecache(self): @@ -196,7 +196,7 @@ def test_filecache(self): for path in [parquet_out, csv_out]: try: os.unlink(path) - except: + except Exception: shutil.rmtree(path) def test_cache_list(self):