Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: |
pixi run -e py-phylo2vec install-python
pixi run -e py-phylo2vec pip install nbconvert
# optimisation.ipynb is skipped as it needs the [opt] extras, RAxML-NG and R
# optimisation.ipynb is skipped as it needs the [gradme] extras, RAxML-NG and R
- name: Execute notebooks
run: |
cd docs/notebooks
Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ The easiest way to install the standard Python package is using pip:
pip install phylo2vec
```

Several optimization schemes based on Phylo2Vec are also available, but require
extra dependencies. (See this
Several optimization schemes based on Phylo2Vec are also available (see this
[notebook](https://phylo2vec.readthedocs.io/en/latest/notebooks/optimisation.html)
for a demo). To avoid bloating the standard package, these dependencies must be
installed separately. To do so, run:
for a demo). GradME requires extra dependencies, which are not installed by
default to avoid bloating the standard package. To install them, run:

```bash
pip install "phylo2vec[opt]"
pip install "phylo2vec[gradme]"
```

#### Manual installation
Expand Down
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ sphinx:
config:
add_module_names: True
autosummary_generate: True
# phylo2vec.opt dependencies (the [opt] extras) are not installed in the docs env
# The [gradme] extras and numba (for phylo2vec.opt) are not installed in the docs env
autodoc_mock_imports: ['jax', 'numba', 'optax', 'rpy2', 'tqdm']
3 changes: 2 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ Datasets
Optimisation
----------------

Requires the optional dependencies: ``pip install phylo2vec[opt]``
``opt.GradME`` and ``opt.gradme_loss`` require the optional dependencies:
``pip install phylo2vec[gradme]``

.. autosummary::
:nosignatures:
Expand Down
9 changes: 4 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ The easiest way to install the standard Python package is using pip:
pip install phylo2vec
```

Several optimization schemes based on Phylo2Vec are also available, but require
extra dependencies. (See this
Several optimization schemes based on Phylo2Vec are also available (see this
[notebook](https://phylo2vec.readthedocs.io/en/latest/notebooks/optimisation.html)
for a demo). To avoid bloating the standard package, these dependencies must be
installed separately. To do so, run:
for a demo). GradME requires extra dependencies, which are not installed by
default to avoid bloating the standard package. To install them, run:

```bash
pip install phylo2vec[opt]
pip install phylo2vec[gradme]
```

### Manual installation
Expand Down
2 changes: 1 addition & 1 deletion docs/notebooks/optimisation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"* A hill-climbing scheme using Phylo2Vec vectors, with branch length optimisation using [RAxML-NG](https://github.com/amkozlov/raxml-ng)\n",
"* [GradME](https://doi.org/10.1093/gbe/evad213), a continuous optimisation scheme based on a probabilistic version of Phylo2Vec.\n",
"\n",
"Running this notebook requires the optional dependencies (`pip install phylo2vec[opt]`), [RAxML-NG](https://github.com/amkozlov/raxml-ng) for hill-climbing, and R with the [ape](https://cran.r-project.org/package=ape) package for GradME."
"Running this notebook requires [RAxML-NG](https://github.com/amkozlov/raxml-ng) for hill-climbing. GradME needs the optional dependencies (`pip install phylo2vec[gradme]`) and R with the [ape](https://cran.r-project.org/package=ape) package."
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ libxcrypt = ">=4.4.36,<5"
cmd = "maturin develop --release --manifest-path ./py-phylo2vec/Cargo.toml"

[feature.python.tasks.test]
# Also run docstring examples; phylo2vec.opt is skipped as it needs the [opt] extras
cmd = "pytest ./py-phylo2vec/tests ./py-phylo2vec/phylo2vec --doctest-modules --ignore=./py-phylo2vec/phylo2vec/opt"
# Also run docstring examples, except in the phylo2vec.opt modules that need the [gradme] extras or numba
cmd = "pytest ./py-phylo2vec/tests ./py-phylo2vec/phylo2vec --doctest-modules --ignore=./py-phylo2vec/phylo2vec/opt/_gradme.py --ignore=./py-phylo2vec/phylo2vec/opt/_gradme_losses.py --ignore=./py-phylo2vec/phylo2vec/opt/utils.py --ignore=./py-phylo2vec/phylo2vec/opt/_beagle_losses.py"
depends-on = ["install-python"]

[feature.python.tasks.benchmark]
Expand Down
27 changes: 23 additions & 4 deletions py-phylo2vec/phylo2vec/opt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
"""Phylo2Vec-based optimisation methods."""
"""Phylo2Vec-based optimisation methods.

GradME needs the optional dependencies: ``pip install phylo2vec[gradme]``.
"""

from ._api import list_methods
from ._gradme import GradME
from ._gradme_losses import gradme_loss
from ._hc import HillClimbing

__all__ = ["GradME", "gradme_loss", "HillClimbing", "list_methods"]
__all__ = ["HillClimbing", "list_methods"]

try:
from ._gradme import GradME
from ._gradme_losses import gradme_loss

__all__ += ["GradME", "gradme_loss"]
except ImportError as err:
# Without the [gradme] extras, the rest of phylo2vec.opt still imports.
# Python calls a module's __getattr__ only for names it cannot find,
# so asking for GradME then raises an error that says what to install.
_GRADME_IMPORT_ERROR = err

def __getattr__(name):
if name in ("GradME", "gradme_loss"):
raise ImportError(
f"{name} needs the optional dependencies: pip install phylo2vec[gradme]"
) from _GRADME_IMPORT_ERROR
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
3 changes: 1 addition & 2 deletions py-phylo2vec/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ dependencies = [
]

[project.optional-dependencies]
opt = [
gradme = [
"jax",
"joblib",
"optax",
"rpy2",
"tqdm"
Expand Down
42 changes: 42 additions & 0 deletions py-phylo2vec/tests/test_opt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Tests for phylo2vec.opt"""

import subprocess
import sys

# Hide jax, so that the [gradme] extras look missing even if they are installed
HIDE_JAX = "import sys; sys.modules['jax'] = None; "


def run_without_gradme_extras(code):
"""Run code in a fresh interpreter, leaving the test session's imports as is."""
return subprocess.run(
[sys.executable, "-c", HIDE_JAX + code],
capture_output=True,
text=True,
check=False,
)


def test_opt_imports_without_gradme_extras():
"""Without the [gradme] extras, phylo2vec.opt imports and leaves GradME out."""
result = run_without_gradme_extras(
"import phylo2vec.opt as opt; print(opt.list_methods(), opt.__all__)"
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "['HillClimbing'] ['HillClimbing', 'list_methods']"


def test_gradme_without_extras_says_what_to_install():
"""Asking for GradME without the [gradme] extras names the extras to install."""
result = run_without_gradme_extras("from phylo2vec.opt import GradME")
assert result.returncode != 0
assert "ImportError: GradME needs the optional dependencies" in result.stderr
assert "pip install phylo2vec[gradme]" in result.stderr


def test_unknown_name_without_gradme_extras():
"""Other missing names still raise Python's usual AttributeError."""
result = run_without_gradme_extras("import phylo2vec.opt as opt; opt.missing")
assert result.returncode != 0
expected = "AttributeError: module 'phylo2vec.opt' has no attribute 'missing'"
assert expected in result.stderr