From fa2b4b113fd219b5042fb959ee0730413ccf954b Mon Sep 17 00:00:00 2001 From: manoflearning <77jwk0724@gmail.com> Date: Thu, 11 Sep 2025 04:12:11 +0900 Subject: [PATCH 1/3] build: separate core and optional deps --- README.md | 6 ++++++ cranberry/features/datasets.py | 21 +++++++++++++++++++-- cranberry/features/visualize.py | 14 +++++++++++++- pyproject.toml | 11 ++++++++++- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 104dec9..35c5e7c 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,12 @@ pip install -e . Note: Depending on your platform, a local build may be required. +Optional features (extras) +- Core install includes only `numpy`. +- Visualization utils require `graphviz` and the Graphviz system binary: `uv pip install -e .[viz]`. +- Dataset download progress uses `tqdm`: `uv pip install -e .[datasets]`. +- Install everything optional: `uv pip install -e .[all]`. + ## Quickstart Minimal example training MNIST with mini‑batches. Tensor slicing is not available yet, so batching uses NumPy views. See `examples/mnist.py` for a complete script. diff --git a/cranberry/features/datasets.py b/cranberry/features/datasets.py index f9fcef7..e9c6586 100644 --- a/cranberry/features/datasets.py +++ b/cranberry/features/datasets.py @@ -6,12 +6,29 @@ import tempfile from typing import Optional, Union import urllib.request +import sys import numpy as np -from psutil import OSX -from tqdm import tqdm from cranberry import Tensor +# Platform detection without psutil +OSX = sys.platform == "darwin" + +# Optional tqdm progress bar +try: + from tqdm import tqdm as _tqdm # type: ignore + def tqdm(*a, **kw): # noqa: N802 - keep name compatibility + return _tqdm(*a, **kw) +except Exception: # pragma: no cover - fallback path + class _NoopTqdm: + def __init__(self, *a, **kw): + pass + def update(self, n): + pass + + def tqdm(*a, **kw): # noqa: N802 - keep name compatibility + return _NoopTqdm() + _cache_dir: str = getenv("XDG_CACHE_HOME", os.path.expanduser("~/Library/Caches" if OSX else "~/.cache")) diff --git a/cranberry/features/visualize.py b/cranberry/features/visualize.py index 4499bbd..fedfe97 100644 --- a/cranberry/features/visualize.py +++ b/cranberry/features/visualize.py @@ -1,6 +1,12 @@ -from graphviz import Digraph from cranberry import Tensor +# Defer graphviz requirement until used, with a helpful error +try: + from graphviz import Digraph # type: ignore +except ImportError as _gv_err: # pragma: no cover - informative path + Digraph = None # type: ignore + _GRAPHVIZ_IMPORT_ERROR = _gv_err + def trace(root: Tensor): nodes, edges = set(), set() @@ -27,6 +33,12 @@ def plot_graph(root: Tensor, fmt="svg", rankdir="LR"): rankdir: TB (top to bottom graph) | LR (left to right) """ assert rankdir in ["LR", "TB"] + if Digraph is None: + raise ImportError( + "graphviz is required for visualization. Install the extra with '\n" + "pip install 'cranberry[viz]'\n" + "and ensure the Graphviz system binary is installed (brew/apt/choco)." + ) from _GRAPHVIZ_IMPORT_ERROR nodes, edges = trace(root) dot = Digraph(format=fmt, graph_attr={"rankdir": rankdir}) diff --git a/pyproject.toml b/pyproject.toml index 9de3719..39651ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,17 @@ classifiers = [ ] dependencies = [ "numpy~=2.0", +] + +[project.optional-dependencies] +viz = [ + "graphviz>=0.20.3,<0.21", +] +datasets = [ + "tqdm>=4.66.4,<5", +] +all = [ "graphviz>=0.20.3,<0.21", - "psutil>=6.0.0,<7", "tqdm>=4.66.4,<5", ] From 9842463eb15475dc2ddafef247710e1a8c0ec2aa Mon Sep 17 00:00:00 2001 From: manoflearning <77jwk0724@gmail.com> Date: Thu, 11 Sep 2025 04:14:13 +0900 Subject: [PATCH 2/3] fix: update uv.lock --- uv.lock | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/uv.lock b/uv.lock index b9fe4b4..fb1135d 100644 --- a/uv.lock +++ b/uv.lock @@ -102,11 +102,20 @@ name = "cranberry" version = "0.1.3" source = { editable = "." } dependencies = [ - { name = "graphviz" }, { name = "numpy" }, - { name = "psutil" }, +] + +[package.optional-dependencies] +all = [ + { name = "graphviz" }, { name = "tqdm" }, ] +datasets = [ + { name = "tqdm" }, +] +viz = [ + { name = "graphviz" }, +] [package.dev-dependencies] dev = [ @@ -121,11 +130,13 @@ dev = [ [package.metadata] requires-dist = [ - { name = "graphviz", specifier = ">=0.20.3,<0.21" }, + { name = "graphviz", marker = "extra == 'all'", specifier = ">=0.20.3,<0.21" }, + { name = "graphviz", marker = "extra == 'viz'", specifier = ">=0.20.3,<0.21" }, { name = "numpy", specifier = "~=2.0" }, - { name = "psutil", specifier = ">=6.0.0,<7" }, - { name = "tqdm", specifier = ">=4.66.4,<5" }, + { name = "tqdm", marker = "extra == 'all'", specifier = ">=4.66.4,<5" }, + { name = "tqdm", marker = "extra == 'datasets'", specifier = ">=4.66.4,<5" }, ] +provides-extras = ["viz", "datasets", "all"] [package.metadata.requires-dev] dev = [ @@ -762,21 +773,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] -[[package]] -name = "psutil" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565, upload-time = "2024-10-17T21:31:45.68Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762, upload-time = "2024-10-17T21:32:05.991Z" }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777, upload-time = "2024-10-17T21:32:07.872Z" }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259, upload-time = "2024-10-17T21:32:10.177Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255, upload-time = "2024-10-17T21:32:11.964Z" }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804, upload-time = "2024-10-17T21:32:13.785Z" }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386, upload-time = "2024-10-17T21:32:21.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228, upload-time = "2024-10-17T21:32:23.88Z" }, -] - [[package]] name = "pyparsing" version = "3.2.3" From 18c196045597d3910aec8ca7b4f4bcb642d92a95 Mon Sep 17 00:00:00 2001 From: manoflearning <77jwk0724@gmail.com> Date: Thu, 11 Sep 2025 04:19:23 +0900 Subject: [PATCH 3/3] try --- cranberry/features/datasets.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cranberry/features/datasets.py b/cranberry/features/datasets.py index e9c6586..013bae3 100644 --- a/cranberry/features/datasets.py +++ b/cranberry/features/datasets.py @@ -17,17 +17,16 @@ # Optional tqdm progress bar try: from tqdm import tqdm as _tqdm # type: ignore - def tqdm(*a, **kw): # noqa: N802 - keep name compatibility - return _tqdm(*a, **kw) + tqdm = _tqdm # noqa: N802 - keep name compatibility except Exception: # pragma: no cover - fallback path class _NoopTqdm: def __init__(self, *a, **kw): pass - def update(self, n): + def update(self, n: int): pass - def tqdm(*a, **kw): # noqa: N802 - keep name compatibility - return _NoopTqdm() + # Keep the same callable interface as tqdm + tqdm = _NoopTqdm # noqa: N802 - keep name compatibility _cache_dir: str = getenv("XDG_CACHE_HOME", os.path.expanduser("~/Library/Caches" if OSX else "~/.cache"))