diff --git a/.tools/requirements/requirements.txt b/.tools/requirements/requirements.txt index 3145007..f69c782 100644 --- a/.tools/requirements/requirements.txt +++ b/.tools/requirements/requirements.txt @@ -1,8 +1,12 @@ # Latest working requirements of your package, should be ahead of pyproject.toml. +IPython[notebook]==8.14.0 +pandas[hdf5,performance]==2.0.2 +pandas-stubs~=2.0.2 ploomber-engine==0.0.30 pydantic==1.10.12 ruamel.yaml==0.17.32 scipy==1.11.1 +sympy==1.12 # Numba only supports numpy<1.25 https://github.com/numba/numba/issues/8698 # ! Unpin numba once it supports numpy>=1.25 numba==0.57.1 diff --git a/pyproject.toml b/pyproject.toml index ab401be..b9cd777 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,10 +17,13 @@ license = { file = "LICENSE" } requires-python = ">=3.11" classifiers = ["License :: OSI Approved :: MIT License"] dependencies = [ + "IPython[notebook]>=8.14.0", + "pandas[hdf5,performance]>=2.0.2", "ploomber-engine>=0.0.30", "pydantic>=1.10.12,<2", "ruamel.yaml>=0.17.32", "scipy>=1.11.1", + "sympy>=1.12", # Numba only supports numpy<1.25 https://github.com/numba/numba/issues/8698 # ! Unpin numba once it supports numpy>=1.25 "numba>=0.57.1", diff --git a/src/boilercore/fits.py b/src/boilercore/fits.py index 3226cf2..3b9df0c 100644 --- a/src/boilercore/fits.py +++ b/src/boilercore/fits.py @@ -1,3 +1,5 @@ +"""Model fits.""" + import warnings from collections.abc import Mapping, Sequence from functools import partial diff --git a/src/boilercore/models.py b/src/boilercore/models.py index 0b752ea..0fe9c61 100644 --- a/src/boilercore/models.py +++ b/src/boilercore/models.py @@ -1,4 +1,4 @@ -"""Basic models for this project.""" +"""Basic models.""" from collections.abc import Mapping, Sequence from pathlib import Path diff --git a/src/boilercore/notebooks.py b/src/boilercore/notebooks.py new file mode 100644 index 0000000..fd6f460 --- /dev/null +++ b/src/boilercore/notebooks.py @@ -0,0 +1,41 @@ +"""Notebook helpers.""" + +from typing import Any + +import pandas as pd +from IPython.core.display import Markdown, Math +from IPython.display import display +from sympy import FiniteSet +from sympy.printing.latex import latex + + +def set_format(): + """Set up formatting for interactive notebook sessions. + The triple curly braces in the f-string allows the format function to be dynamically + specified by a given float specification. The intent is clearer this way, and may be + extended in the future by making `float_spec` a parameter. + """ + float_spec = ":#.4g" + pd.options.display.min_rows = pd.options.display.max_rows = 50 + pd.options.display.float_format = f"{{{float_spec}}}".format + + +def disp_named(*args: tuple[Any, str]): + """Display objects with names above them.""" + for elem, name in args: + display(Markdown(f"##### {name}")) + display(elem) + + +def disp_free(title, eqn, **kwargs): + disp(title, eqn, **kwargs) + disp("Free symbols", FiniteSet(*eqn.rhs.free_symbols), **kwargs) + + +def disp(title, *exprs, **kwargs): + print(f"{title}:") + display(*(math_mod(expr, **kwargs) for expr in exprs)) + + +def math_mod(expr, long_frac_ratio=3, **kwargs): + return Math(latex(expr, long_frac_ratio=long_frac_ratio, **kwargs)) diff --git a/src/boilercore/paths.py b/src/boilercore/paths.py new file mode 100644 index 0000000..f98eee0 --- /dev/null +++ b/src/boilercore/paths.py @@ -0,0 +1,54 @@ +"""Paths and modules.""" + +from collections.abc import Iterable +from pathlib import Path +from re import compile +from types import ModuleType + + +def get_package_dir(package: ModuleType) -> Path: + return Path(next(iter(package.__path__))) + + +def map_stages(stages_dir: Path, package_dir: Path) -> dict[str, Path]: + """Map stage module names to their paths.""" + stages: dict[str, Path] = {} + for path in walk_module_paths(stages_dir, package_dir, glob="[!__]*.[py ipynb]*"): + module = get_module_rel(get_module(path, package_dir), stages_dir.name) + stages[module.replace(".", "_")] = path + return stages + + +def walk_module_paths( + package: Path, top: Path, suffix: str = ".py", glob: str | None = None +) -> Iterable[Path]: + """Walk modules from a given submodule path and the top level library directory.""" + for directory in ( + package, + *[ + path + for path in package.iterdir() + if path.is_dir() and "__" not in str(path.relative_to(top.parent)) + ], + ): + yield from sorted(directory.glob(glob or f"[!__]*{suffix}")) + + +def get_module(module: Path, package: Path) -> str: + """Get module name given the submodule path and the top level library directory.""" + return ( + str(module.relative_to(package.parent).with_suffix("")) + .replace("\\", ".") + .replace("/", ".") + ) + + +def walk_modules(package: Path, top: Path, suffix: str = ".py") -> Iterable[str]: + """Walk modules from a given submodule path and the top level library directory.""" + for module in walk_module_paths(package, top, suffix): + yield get_module(module, top) + + +def get_module_rel(module: str, relative: str) -> str: + """Get module name relative to another module.""" + return compile(rf".*{relative}\.").sub(repl="", string=module) diff --git a/src/boilercore/testing.py b/src/boilercore/testing.py index 526d205..e5f13c0 100644 --- a/src/boilercore/testing.py +++ b/src/boilercore/testing.py @@ -1,9 +1,7 @@ -"""Helper functions for testing boiler code.""" +"""Test helpers.""" -from collections.abc import Iterable from contextlib import contextmanager from pathlib import Path -from re import compile from shutil import copy, copytree import pytest @@ -32,36 +30,3 @@ def before_tmp_workdir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): yield finally: monkeypatch.chdir(tmp_path) - - -def walk_modules(package: Path, top: Path, suffix: str = ".py") -> Iterable[str]: - """Walk modules from a given submodule path and the top level library directory.""" - for module in walk_module_paths(package, top, suffix): - yield get_module(module, top) - - -def walk_module_paths(package: Path, top: Path, suffix: str = ".py") -> Iterable[Path]: - """Walk modules from a given submodule path and the top level library directory.""" - for directory in ( - package, - *[ - path - for path in package.iterdir() - if path.is_dir() and "__" not in str(path.relative_to(top.parent)) - ], - ): - yield from sorted(directory.glob(f"[!__]*{suffix}")) - - -def get_module(submodule: Path, library: Path) -> str: - """Get module name given the submodule path and the top level library directory.""" - return ( - str(submodule.relative_to(library.parent).with_suffix("")) - .replace("\\", ".") - .replace("/", ".") - ) - - -def get_module_rel(module: str, relative: str) -> str: - """Get module name relative to another module.""" - return compile(rf".*{relative}\.").sub(repl="", string=module) diff --git a/src/boilercore/types.py b/src/boilercore/types.py index 9614663..df9c2ef 100644 --- a/src/boilercore/types.py +++ b/src/boilercore/types.py @@ -1,4 +1,4 @@ -"""Types used throughout this package.""" +"""Types.""" from typing import Literal, TypeVar