Skip to content

Commit

Permalink
Merge pull request #196 from ImperialCollegeLondon/review-dev-depende…
Browse files Browse the repository at this point in the history
…ncies

Review dev and optional dependencies
  • Loading branch information
tomjholland authored Jan 3, 2025
2 parents 9c36949 + 5e0ce2d commit 1e17cef
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
python-version-file: "pyproject.toml"

- name: Install the project
run: uv sync --all-extras --group format --group test --group docs --group external-integration
run: uv sync --all-extras

- name: Run tests
run: uv run pytest tests --benchmark-json output.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:

- name: Install project
run: |
uv sync --all-extras --group format --group test --group docs --group external-integration
uv sync --all-extras
- name: Bump version
id: bump
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
python-version-file: "pyproject.toml"

- name: Install the project
run: uv sync --all-extras --group format --group test --group docs --group external-integration
run: uv sync --all-extras

- name: Build
run: uv build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

- name: Install the project
run: |
uv sync --all-extras --group format --group test --group docs --group external-integration
uv sync --all-extras
uv pip install ipykernel
sudo apt-get install pandoc
Expand Down
33 changes: 4 additions & 29 deletions docs/source/developer_guide/developer_installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ virtual environment:

.. code-block:: bash
uv sync --all-extras --all-groups
uv sync --all-extras
The virtual environment is stored in the :code:`PyProBE/.venv` directory inside your and
can be activated with :code:`source .venv/bin/activate`.
Expand All @@ -58,38 +58,13 @@ This will create a kernel that you can select within VSCode in the usual way.
this method results in dependency conflicts:

1. Create and activate a virtual environment.

.. tabs::
.. tab:: venv

In your working directory:

.. code-block:: bash
python -m venv venv
source .venv/bin/activate
.. tab:: conda

In any directory:

.. code-block:: bash
conda create -n pyprobe python=3.12
conda activate pyprobe
2. Install the developer dependencies:

.. code-block:: bash
cd /path/to/your/directory/PyProBE
pip install -r requirements-dev.txt
3. Install PyProBE as a package into your virtual environment:
2. Install PyProBE as a package into your virtual environment with the developer
dependencies:

.. code-block:: bash
pip install -e .
pip install -e '.[dev, docs]'
The :code:`-e` flag installs in "editable" mode, which means changes that you
make to the code will be automatically reflected in the package inside your
Expand Down
10 changes: 10 additions & 0 deletions docs/source/user_guide/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ The steps to install PyProBE are as follows:
pip install PyProBE-Data
Optional dependencies can be added to the installation as follows:

.. code-block:: bash
pip install 'PyProBE-Data[hvplot]'
If a method uses an optional dependency, it will be detailed in the
:doc:`api documentation <pyprobe>`. If these methods is run without their dependencies
installed, they will return an error.

3. You can create a new python script or jupyter notebook to
process your data. You can import PyProBE into your script as follows:

Expand Down
9 changes: 8 additions & 1 deletion pyprobe/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def __getattr__(self, _: Any) -> None:
"""Raise an ImportError if seaborn is not installed."""
raise ImportError(
"Optional dependency 'seaborn' is not installed. Please install by "
"running 'pip install seaborn'."
"running 'pip install seaborn' or installing PyProBE with seaborn "
"as an optional dependency: `pip install 'PyProBE-Data[seaborn]'."
)

return SeabornWrapper()
Expand Down Expand Up @@ -124,11 +125,17 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
seaborn = _create_seaborn_wrapper()
"""A wrapped version of the seaborn package.
Requires the seaborn package to be installed as an optional dependency. You can install
it with PyProBE by running :code:`pip install 'PyProBE-Data[seaborn]'`, or install it
seperately with :code:`pip install seaborn`.
This version of seaborn is modified to work with PyProBE Result objects. All functions
from the original seaborn package are available in this version. Where seaborn functions
accept a 'data' argument, a PyProBE Result object can be passed instead of a pandas
DataFrame. For example:
.. code-block:: python
from pyprobe.plot import seaborn as sns
result = cell.procedure['Sample']
Expand Down
20 changes: 13 additions & 7 deletions pyprobe/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,27 @@ def hvplot(self, *args: Any, **kwargs: Any) -> None:
data_to_plot = _retrieve_relevant_columns(self, args, kwargs)
return data_to_plot.hvplot(*args, **kwargs)

hvplot.__doc__ = (
"HvPlot is a library for creating fast and interactive plots.\n\n"
"The default backend is bokeh, which can be changed by setting the backend "
"with :code:`hvplot.extension('matplotlib')` or "
":code:`hvplot.extension('plotly')`.\n\n" + (hvplot.__doc__ or "")
)
else:

def hvplot(self, *args: Any, **kwargs: Any) -> None:
"""Wrapper for plotting using the hvplot library."""
raise ImportError(
"Optional dependency hvplot is not installed. Please install it via "
"'pip install hvplot'."
"'pip install hvplot' or by installing PyProBE with hvplot as an "
"optional dependency: pip install 'PyProBE-Data[hvplot]'."
)

hvplot.__doc__ = (
"HvPlot is a library for creating fast and interactive plots.\n\n"
"This method requires the hvplot library to be installed as an optional "
"dependency. You can install it with PyProBE by running "
":code:`pip install 'PyProBE-Data[hvplot]'`, or install it seperately with "
":code:`pip install hvplot`.\n\n"
"The default backend is bokeh, which can be changed by setting the backend "
"with :code:`hvplot.extension('matplotlib')` or "
":code:`hvplot.extension('plotly')`.\n\n" + (hvplot.__doc__ or "")
)

def _get_data_subset(self, *column_names: str) -> pl.DataFrame:
"""Return a subset of the data with the specified columns.
Expand Down
10 changes: 3 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,14 @@ hvplot = [
seaborn = [
"seaborn>=0.13.2",
]

[dependency-groups]
format = [
dev = [
"black>=24.10.0",
"flake8>=7.1.1",
"flake8-docstrings>=1.7.0",
"isort>=5.13.2",
"mypy>=1.14.1",
"pre-commit>=4.0.1",
"python-semantic-release>=9.15.2",
]
test = [
"nbmake>=1.5.5",
"pytest>=8.3.4",
"pytest-benchmark>=5.1.0",
Expand All @@ -52,6 +48,7 @@ test = [
"types-deprecated>=1.2.15.20241117",
"types-pyyaml>=6.0.12.20241230",
"types-toml>=0.10.8.20240310",
"xlsxwriter>=3.2.0",
]
docs = [
"autodoc-pydantic>=2.2.0",
Expand All @@ -61,9 +58,8 @@ docs = [
"sphinx-design>=0.6.1",
"sphinx-tabs>=3.4.7",
"sphinxcontrib-bibtex>=2.6.3",
"xlsxwriter>=3.2.0",
]
external-integration = [
pybamm = [
"pybamm>=24.1",
]

Expand Down
12 changes: 9 additions & 3 deletions tests/test_cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import shutil

import polars as pl
import pybamm
import pytest
from numpy.testing import assert_array_equal
from polars.testing import assert_frame_equal
Expand Down Expand Up @@ -146,18 +145,25 @@ def add_procedure():
return cell_instance.add_procedure(title, input_path, file_name)

benchmark(add_procedure)
assert_frame_equal(cell_instance.procedure[title].data, procedure_fixture.data)
assert_frame_equal(
cell_instance.procedure[title].data,
procedure_fixture.data,
check_column_order=False,
)

cell_instance.add_procedure(
"Test_custom", input_path, file_name, readme_name="README_total_steps.yaml"
)
assert_frame_equal(
cell_instance.procedure["Test_custom"].data, procedure_fixture.data
cell_instance.procedure["Test_custom"].data,
procedure_fixture.data,
check_column_order=False,
)


def test_import_pybamm_solution(benchmark):
"""Test the import_pybamm_solution method."""
pybamm = pytest.importorskip("pybamm")
parameter_values = pybamm.ParameterValues("Chen2020")
spm = pybamm.lithium_ion.SPM()
experiment = pybamm.Experiment(
Expand Down
18 changes: 11 additions & 7 deletions tests/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import polars as pl
import polars.testing as pl_testing
import pytest
import seaborn as _sns
from plotly.express.colors import sample_colorscale
from sklearn.preprocessing import minmax_scale

Expand Down Expand Up @@ -122,44 +121,48 @@ def test_retrieve_relevant_columns_with_unit_conversion():

def test_seaborn_wrapper_creation():
"""Test basic seaborn wrapper creation."""
pytest.importorskip("seaborn")
wrapper = plot._create_seaborn_wrapper()
assert wrapper is not None
assert isinstance(wrapper, object)


def test_seaborn_wrapper_data_conversion(mocker):
"""Test that wrapped functions convert data correctly."""
sns = pytest.importorskip("seaborn")
result = Result(
base_dataframe=pl.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}),
info={},
column_definitions={"x": "int", "y": "int"},
)
data = result.data.to_pandas()
pyprobe_seaborn_plot = plot.seaborn.lineplot(data=result, x="x", y="y")
seaborn_lineplot = _sns.lineplot(data=data, x="x", y="y")
seaborn_lineplot = sns.lineplot(data=data, x="x", y="y")
assert pyprobe_seaborn_plot == seaborn_lineplot


def test_seaborn_wrapper_function_call():
"""Test that wrapped functions produce same output."""
sns = pytest.importorskip("seaborn")
wrapper = plot._create_seaborn_wrapper()

assert wrapper.set_theme() == _sns.set_theme()
assert wrapper.set_theme() == sns.set_theme()

colors1 = wrapper.color_palette()
colors2 = _sns.color_palette()
colors2 = sns.color_palette()
assert colors1 == colors2

# Test with specific parameters
palette1 = wrapper.color_palette("husl", 8)
palette2 = _sns.color_palette("husl", 8)
palette2 = sns.color_palette("husl", 8)
assert palette1 == palette2


def test_seaborn_wrapper_function_properties():
"""Test that wrapped functions maintain original properties."""
sns = pytest.importorskip("seaborn")
wrapper = plot._create_seaborn_wrapper()
original_func = _sns.lineplot
original_func = sns.lineplot
wrapped_func = wrapper.lineplot

assert wrapped_func.__name__ == original_func.__name__
Expand All @@ -168,8 +171,9 @@ def test_seaborn_wrapper_function_properties():

def test_seaborn_wrapper_complete_coverage():
"""Test that all public seaborn attributes are wrapped."""
sns = pytest.importorskip("seaborn")
wrapper = plot._create_seaborn_wrapper()
sns_attrs = {attr for attr in dir(_sns) if not attr.startswith("_")}
sns_attrs = {attr for attr in dir(sns) if not attr.startswith("_")}
wrapper_attrs = {attr for attr in dir(wrapper) if not attr.startswith("_")}
assert sns_attrs == wrapper_attrs

Expand Down
Loading

0 comments on commit 1e17cef

Please sign in to comment.