cosmochains 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. cosmochains-0.0.2/PKG-INFO +47 -0
  2. cosmochains-0.0.2/README.md +0 -0
  3. cosmochains-0.0.2/pyproject.toml +73 -0
  4. cosmochains-0.0.2/pyproject.toml.orig +59 -0
  5. cosmochains-0.0.2/src/cosmochains/__init__.py +152 -0
  6. cosmochains-0.0.2/src/cosmochains/backends/__init__.py +12 -0
  7. cosmochains-0.0.2/src/cosmochains/backends/base.py +108 -0
  8. cosmochains-0.0.2/src/cosmochains/backends/cache.py +24 -0
  9. cosmochains-0.0.2/src/cosmochains/backends/dropbox.py +268 -0
  10. cosmochains-0.0.2/src/cosmochains/backends/github.py +333 -0
  11. cosmochains-0.0.2/src/cosmochains/backends/googledrive.py +319 -0
  12. cosmochains-0.0.2/src/cosmochains/backends/huggingface.py +281 -0
  13. cosmochains-0.0.2/src/cosmochains/backends/huggingface_bucket.py +203 -0
  14. cosmochains-0.0.2/src/cosmochains/catalog.py +125 -0
  15. cosmochains-0.0.2/src/cosmochains/cli/__init__.py +0 -0
  16. cosmochains-0.0.2/src/cosmochains/cli/app.py +21 -0
  17. cosmochains-0.0.2/src/cosmochains/cli/commands/__init__.py +2 -0
  18. cosmochains-0.0.2/src/cosmochains/cli/commands/delete.py +41 -0
  19. cosmochains-0.0.2/src/cosmochains/cli/commands/download.py +42 -0
  20. cosmochains-0.0.2/src/cosmochains/cli/commands/info.py +60 -0
  21. cosmochains-0.0.2/src/cosmochains/cli/commands/initialize.py +46 -0
  22. cosmochains-0.0.2/src/cosmochains/cli/commands/list.py +56 -0
  23. cosmochains-0.0.2/src/cosmochains/cli/commands/upload.py +337 -0
  24. cosmochains-0.0.2/src/cosmochains/cli/options.py +30 -0
  25. cosmochains-0.0.2/src/cosmochains/config.py +105 -0
  26. cosmochains-0.0.2/src/cosmochains/local.py +93 -0
  27. cosmochains-0.0.2/src/cosmochains/models.py +266 -0
  28. cosmochains-0.0.2/src/cosmochains/py.typed +0 -0
  29. cosmochains-0.0.2/src/cosmochains/readers/__init__.py +200 -0
  30. cosmochains-0.0.2/src/cosmochains/readers/base.py +100 -0
  31. cosmochains-0.0.2/src/cosmochains/readers/cobaya_.py +78 -0
  32. cosmochains-0.0.2/src/cosmochains/readers/getdist_.py +90 -0
  33. cosmochains-0.0.2/src/cosmochains/readers/hdf5.py +110 -0
  34. cosmochains-0.0.2/src/cosmochains/readers/montepython_.py +58 -0
  35. cosmochains-0.0.2/src/cosmochains/readers/numpy_.py +68 -0
  36. cosmochains-0.0.2/src/cosmochains/readers/zarr_.py +52 -0
  37. cosmochains-0.0.2/src/cosmochains/stats.py +70 -0
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.3
2
+ Name: cosmochains
3
+ Version: 0.0.2
4
+ Summary: Cloud-based MCMC chain repository manager
5
+ Author: Rodrigo Calderon
6
+ Author-email: Rodrigo Calderon <calderon.cosmology@gmail.com>
7
+ Requires-Dist: numpy>=1.26
8
+ Requires-Dist: pydantic>=2.7
9
+ Requires-Dist: pyyaml>=6.0
10
+ Requires-Dist: typer>=0.12
11
+ Requires-Dist: rich>=13.0
12
+ Requires-Dist: shellingham>=1.5
13
+ Requires-Dist: tomli-w>=1.0
14
+ Requires-Dist: huggingface-hub>=0.23 ; extra == 'all-backends'
15
+ Requires-Dist: python-dotenv>=1.0 ; extra == 'all-backends'
16
+ Requires-Dist: h5py>=3.10 ; extra == 'all-backends'
17
+ Requires-Dist: pygithub>=2.1 ; extra == 'all-backends'
18
+ Requires-Dist: google-api-python-client>=2.100 ; extra == 'all-backends'
19
+ Requires-Dist: google-auth>=2.20 ; extra == 'all-backends'
20
+ Requires-Dist: dropbox>=12.0 ; extra == 'all-backends'
21
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
22
+ Requires-Dist: pytest-mock>=3.14 ; extra == 'dev'
23
+ Requires-Dist: mkdocs-material>=9.5 ; extra == 'docs'
24
+ Requires-Dist: mkdocstrings[python]>=0.25 ; extra == 'docs'
25
+ Requires-Dist: dropbox>=12.0 ; extra == 'dropbox'
26
+ Requires-Dist: getdist>=1.4 ; extra == 'getdist'
27
+ Requires-Dist: pygithub>=2.1 ; extra == 'github'
28
+ Requires-Dist: google-api-python-client>=2.100 ; extra == 'google-drive'
29
+ Requires-Dist: google-auth>=2.20 ; extra == 'google-drive'
30
+ Requires-Dist: h5py>=3.10 ; extra == 'hdf5'
31
+ Requires-Dist: huggingface-hub>=0.23 ; extra == 'hf'
32
+ Requires-Dist: python-dotenv>=1.0 ; extra == 'hf'
33
+ Requires-Dist: h5py>=3.10 ; extra == 'hf'
34
+ Requires-Dist: zarr>=2.18 ; extra == 'zarr'
35
+ Requires-Python: >=3.12
36
+ Provides-Extra: all-backends
37
+ Provides-Extra: dev
38
+ Provides-Extra: docs
39
+ Provides-Extra: dropbox
40
+ Provides-Extra: getdist
41
+ Provides-Extra: github
42
+ Provides-Extra: google-drive
43
+ Provides-Extra: hdf5
44
+ Provides-Extra: hf
45
+ Provides-Extra: zarr
46
+ Description-Content-Type: text/markdown
47
+
File without changes
@@ -0,0 +1,73 @@
1
+ [project]
2
+ name = "cosmochains"
3
+ version = "0.0.2"
4
+ description = "Cloud-based MCMC chain repository manager"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "numpy>=1.26",
9
+ "pydantic>=2.7",
10
+ "pyyaml>=6.0",
11
+ "typer>=0.12",
12
+ "rich>=13.0",
13
+ "shellingham>=1.5",
14
+ "tomli-w>=1.0",
15
+ ]
16
+
17
+ [[project.authors]]
18
+ name = "Rodrigo Calderon"
19
+ email = "calderon.cosmology@gmail.com"
20
+
21
+ [project.optional-dependencies]
22
+ hf = [
23
+ "huggingface-hub>=0.23",
24
+ "python-dotenv>=1.0",
25
+ "h5py>=3.10",
26
+ ]
27
+ hdf5 = ["h5py>=3.10"]
28
+ zarr = ["zarr>=2.18"]
29
+ getdist = ["getdist>=1.4"]
30
+ github = ["PyGithub>=2.1"]
31
+ google-drive = [
32
+ "google-api-python-client>=2.100",
33
+ "google-auth>=2.20",
34
+ ]
35
+ dropbox = ["dropbox>=12.0"]
36
+ all-backends = [
37
+ "huggingface-hub>=0.23",
38
+ "python-dotenv>=1.0",
39
+ "h5py>=3.10",
40
+ "PyGithub>=2.1",
41
+ "google-api-python-client>=2.100",
42
+ "google-auth>=2.20",
43
+ "dropbox>=12.0",
44
+ ]
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "pytest-mock>=3.14",
48
+ ]
49
+ docs = [
50
+ "mkdocs-material>=9.5",
51
+ "mkdocstrings[python]>=0.25",
52
+ ]
53
+
54
+ [project.scripts]
55
+ cosmochains = "cosmochains.cli.app:main"
56
+
57
+ [build-system]
58
+ requires = ["uv_build>=0.9.9,<0.10.0"]
59
+ build-backend = "uv_build"
60
+
61
+ [tool.pytest.ini_options]
62
+ markers = [
63
+ "integration: requires credentials and network access",
64
+ "integration_github: GitHub integration tests",
65
+ "integration_googledrive: Google Drive integration tests",
66
+ "integration_dropbox: Dropbox integration tests",
67
+ ]
68
+
69
+ [dependency-groups]
70
+ dev = [
71
+ "jupyter>=1.1.1",
72
+ "nbconvert>=7.17.1",
73
+ ]
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "cosmochains"
3
+ version = "0.0.2"
4
+ description = "Cloud-based MCMC chain repository manager"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Rodrigo Calderon", email = "calderon.cosmology@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "numpy>=1.26",
12
+ "pydantic>=2.7",
13
+ "pyyaml>=6.0",
14
+ "typer>=0.12",
15
+ "rich>=13.0",
16
+ "shellingham>=1.5",
17
+ "tomli-w>=1.0",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ hf = ["huggingface-hub>=0.23", "python-dotenv>=1.0", "h5py>=3.10"]
22
+ hdf5 = ["h5py>=3.10"]
23
+ zarr = ["zarr>=2.18"]
24
+ getdist = ["getdist>=1.4"]
25
+ github = ["PyGithub>=2.1"]
26
+ google-drive = ["google-api-python-client>=2.100", "google-auth>=2.20"]
27
+ dropbox = ["dropbox>=12.0"]
28
+ all-backends = [
29
+ "huggingface-hub>=0.23",
30
+ "python-dotenv>=1.0",
31
+ "h5py>=3.10",
32
+ "PyGithub>=2.1",
33
+ "google-api-python-client>=2.100",
34
+ "google-auth>=2.20",
35
+ "dropbox>=12.0",
36
+ ]
37
+ dev = ["pytest>=8.0", "pytest-mock>=3.14"]
38
+ docs = ["mkdocs-material>=9.5", "mkdocstrings[python]>=0.25"]
39
+
40
+ [project.scripts]
41
+ cosmochains = "cosmochains.cli.app:main"
42
+
43
+ [build-system]
44
+ requires = ["uv_build>=0.9.9,<0.10.0"]
45
+ build-backend = "uv_build"
46
+
47
+ [tool.pytest.ini_options]
48
+ markers = [
49
+ "integration: requires credentials and network access",
50
+ "integration_github: GitHub integration tests",
51
+ "integration_googledrive: Google Drive integration tests",
52
+ "integration_dropbox: Dropbox integration tests",
53
+ ]
54
+
55
+ [dependency-groups]
56
+ dev = [
57
+ "jupyter>=1.1.1",
58
+ "nbconvert>=7.17.1",
59
+ ]
@@ -0,0 +1,152 @@
1
+ from __future__ import annotations
2
+
3
+ import concurrent.futures
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+
8
+ from .backends.base import ChainBackend
9
+ from .catalog import resolve_name
10
+ from .config import get_default_backend
11
+ from .local import read
12
+ from .models import Chain, ChainArrays, ChainMetadata
13
+ from .readers import get_reader, read_from_zip
14
+ from .stats import Stats, marginal_stats
15
+
16
+
17
+ def _backend(backend: ChainBackend | None) -> ChainBackend:
18
+ return backend if backend is not None else get_default_backend()
19
+
20
+
21
+ def load_chain(
22
+ name: str | list[str],
23
+ backend: ChainBackend | None = None,
24
+ ) -> Chain | dict[str, Chain]:
25
+ """Download and return one or more chains by name.
26
+
27
+ When *name* is a list the chains are fetched concurrently and returned as
28
+ a ``{name: Chain}`` mapping. When *name* is a string a single
29
+ :class:`~cosmochains.models.Chain` is returned.
30
+
31
+ Args:
32
+ name: Chain name or list of chain names as registered in the catalog.
33
+ backend: Backend instance to use. Falls back to
34
+ :func:`~cosmochains.config.get_default_backend` when ``None``.
35
+
36
+ Returns:
37
+ A single :class:`~cosmochains.models.Chain` or a ``dict[str, Chain]``
38
+ when *name* is a list.
39
+
40
+ Raises:
41
+ KeyError: If *name* is not found in the catalog.
42
+ RuntimeError: If no backend is configured and environment variables are
43
+ missing.
44
+
45
+ Example:
46
+ >>> import os
47
+ >>> os.environ["COSMOCHAINS_REPO"] = "myorg/my-chains"
48
+ >>> from cosmochains import load_chain
49
+ >>> chain = load_chain("planck2018_TTTEEE")
50
+ >>> chain.samples.shape
51
+ (5000, 6)
52
+ """
53
+ b = _backend(backend)
54
+ if isinstance(name, list):
55
+ with concurrent.futures.ThreadPoolExecutor() as pool:
56
+ futures = {n: pool.submit(_load_one, n, b) for n in name}
57
+ return {n: f.result() for n, f in futures.items()}
58
+ return _load_one(name, b)
59
+
60
+
61
+ def _load_one(name: str, backend: ChainBackend) -> Chain:
62
+ path = backend.download(name)
63
+ catalog = backend.get_catalog()
64
+ entry = resolve_name(name, catalog)
65
+ fmt = entry.get("format", "hdf5")
66
+ if fmt in ("hdf5", "h5"):
67
+ from .readers.hdf5 import read_hdf5
68
+
69
+ return read_hdf5(path)
70
+ arrays = _read_arrays(path, fmt)
71
+ metadata = ChainMetadata.model_validate(backend.download_metadata(name))
72
+ return arrays.to_chain(metadata)
73
+
74
+
75
+ def _read_arrays(path: Path, fmt: str) -> ChainArrays:
76
+ if fmt in ("getdist", "zarr"):
77
+ return read_from_zip(path, fmt)
78
+ return get_reader(path).read(path)
79
+
80
+
81
+ def list_chains(backend: ChainBackend | None = None) -> list[str]:
82
+ """Return the names of all chains registered in the catalog.
83
+
84
+ Args:
85
+ backend: Backend instance to use. Falls back to
86
+ :func:`~cosmochains.config.get_default_backend` when ``None``.
87
+
88
+ Returns:
89
+ Sorted list of chain name strings.
90
+ """
91
+ return _backend(backend).list_chains()
92
+
93
+
94
+ def get_metadata(name: str, backend: ChainBackend | None = None) -> ChainMetadata:
95
+ """Fetch and return the metadata for a single chain without loading its arrays.
96
+
97
+ Uses the lightweight :meth:`~cosmochains.backends.base.ChainBackend.download_metadata`
98
+ path — no chain file is downloaded.
99
+
100
+ Args:
101
+ name: Chain name as registered in the catalog.
102
+ backend: Backend instance to use. Falls back to
103
+ :func:`~cosmochains.config.get_default_backend` when ``None``.
104
+
105
+ Returns:
106
+ :class:`~cosmochains.models.ChainMetadata` for the requested chain.
107
+ """
108
+ b = _backend(backend)
109
+ return ChainMetadata.model_validate(b.download_metadata(name))
110
+
111
+
112
+ def load_samples(name: str, backend: ChainBackend | None = None) -> np.ndarray:
113
+ """Download a chain and return its sample array.
114
+
115
+ Args:
116
+ name: Chain name as registered in the catalog.
117
+ backend: Backend instance to use. Falls back to
118
+ :func:`~cosmochains.config.get_default_backend` when ``None``.
119
+
120
+ Returns:
121
+ NumPy array of shape ``(n_samples, n_params)``.
122
+ """
123
+ return load_chain(name, backend=backend).samples
124
+
125
+
126
+ def load_weights(name: str, backend: ChainBackend | None = None) -> np.ndarray:
127
+ """Download a chain and return its importance weights.
128
+
129
+ Args:
130
+ name: Chain name as registered in the catalog.
131
+ backend: Backend instance to use. Falls back to
132
+ :func:`~cosmochains.config.get_default_backend` when ``None``.
133
+
134
+ Returns:
135
+ NumPy array of shape ``(n_samples,)``.
136
+ """
137
+ return load_chain(name, backend=backend).weights
138
+
139
+
140
+ __all__ = [
141
+ "read",
142
+ "marginal_stats",
143
+ "Stats",
144
+ "load_chain",
145
+ "list_chains",
146
+ "get_metadata",
147
+ "load_samples",
148
+ "load_weights",
149
+ "Chain",
150
+ "ChainMetadata",
151
+ "ChainBackend",
152
+ ]
@@ -0,0 +1,12 @@
1
+ from .base import ChainBackend
2
+ from .huggingface import HuggingFaceBackend
3
+ from .huggingface_bucket import HuggingFaceBucketBackend
4
+
5
+ __all__ = ["ChainBackend", "HuggingFaceBackend", "HuggingFaceBucketBackend"]
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .dropbox import DropboxBackend
11
+ from .github import GitHubBackend
12
+ from .googledrive import GoogleDriveBackend
@@ -0,0 +1,108 @@
1
+ from pathlib import Path
2
+ from typing import Protocol, runtime_checkable
3
+
4
+
5
+ @runtime_checkable
6
+ class ChainBackend(Protocol):
7
+ """Structural protocol that all storage backends must satisfy.
8
+
9
+ Implementations are duck-typed: any class that provides all methods below
10
+ is a valid backend regardless of inheritance.
11
+
12
+ Built-in implementations:
13
+
14
+ - :class:`~cosmochains.backends.huggingface.HuggingFaceBackend` —
15
+ HuggingFace dataset repository (Git-backed, atomic uploads).
16
+ - :class:`~cosmochains.backends.huggingface_bucket.HuggingFaceBucketBackend`
17
+ — HuggingFace S3-compatible bucket (non-atomic, no versioning).
18
+ - :class:`~cosmochains.backends.github.GitHubBackend` —
19
+ dedicated branch of a GitHub repository (atomic uploads via Git Data API;
20
+ 100 MB per-file limit). Requires ``cosmochains[github]``.
21
+ - :class:`~cosmochains.backends.googledrive.GoogleDriveBackend` —
22
+ Google Drive folder (non-atomic; deleted files go to trash). Requires
23
+ ``cosmochains[google-drive]``.
24
+ - :class:`~cosmochains.backends.dropbox.DropboxBackend` —
25
+ Dropbox folder path (non-atomic; chunked upload for files > 150 MB).
26
+ Requires ``cosmochains[dropbox]``.
27
+
28
+ Select a backend at runtime via the ``COSMOCHAINS_BACKEND`` environment
29
+ variable or the ``--backend`` CLI flag. Auth tokens can be set with the
30
+ generic ``COSMOCHAINS_TOKEN`` variable or backend-specific variables
31
+ (``HF_TOKEN``, ``GITHUB_TOKEN``, ``GOOGLE_APPLICATION_CREDENTIALS``,
32
+ ``DROPBOX_REFRESH_TOKEN`` + key/secret).
33
+ """
34
+
35
+ def download(self, name: str) -> Path:
36
+ """Download a chain file and return its local cache path.
37
+
38
+ Args:
39
+ name: Chain name as registered in the catalog.
40
+
41
+ Returns:
42
+ :class:`pathlib.Path` to the cached local file.
43
+ """
44
+ ...
45
+
46
+ def upload(self, name: str, local_path: Path, metadata: dict) -> None:
47
+ """Upload a chain file and its metadata YAML to the remote store.
48
+
49
+ Args:
50
+ name: Chain name to register in the remote catalog.
51
+ local_path: Local file to upload.
52
+ metadata: Metadata dict that will be serialized as YAML alongside
53
+ the chain file.
54
+ """
55
+ ...
56
+
57
+ def download_metadata(self, name: str) -> dict:
58
+ """Fetch and return the metadata dict for *name*.
59
+
60
+ Args:
61
+ name: Chain name as registered in the catalog.
62
+
63
+ Returns:
64
+ Raw metadata dict loaded from the YAML sidecar file.
65
+ """
66
+ ...
67
+
68
+ def list_chains(self) -> list[str]:
69
+ """Return the names of all concrete chains in the remote catalog.
70
+
71
+ Returns:
72
+ List of chain name strings (aliases excluded).
73
+ """
74
+ ...
75
+
76
+ def get_catalog(self) -> dict:
77
+ """Fetch and return the full catalog from the remote store.
78
+
79
+ Returns:
80
+ Catalog dict; empty dict when the catalog does not yet exist.
81
+ """
82
+ ...
83
+
84
+ def update_catalog(self, catalog: dict) -> None:
85
+ """Write *catalog* back to the remote store, replacing the current version.
86
+
87
+ Args:
88
+ catalog: Complete catalog dict to persist.
89
+ """
90
+ ...
91
+
92
+ def delete(self, name: str) -> None:
93
+ """Delete a chain's files and metadata from the remote store.
94
+
95
+ Also removes any catalog aliases that point to *name*.
96
+
97
+ Args:
98
+ name: Chain name as registered in the catalog.
99
+ """
100
+ ...
101
+
102
+ def create_repo(self, *, private: bool = False) -> None:
103
+ """Create the remote repository or bucket if it does not already exist.
104
+
105
+ Args:
106
+ private: If ``True``, create a private repository/bucket.
107
+ """
108
+ ...
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ from pathlib import Path
5
+
6
+
7
+ def cache_path(backend_key: str, repo_id: str, relative: str) -> Path:
8
+ """Return a local cache path for a remote file, creating parent dirs as needed.
9
+
10
+ Args:
11
+ backend_key: Short identifier for the backend (e.g. ``"github"``).
12
+ repo_id: Repository / folder identifier used to namespace the cache.
13
+ relative: Relative file path within the remote store
14
+ (e.g. ``"chains/run1/chain.hdf5"``).
15
+
16
+ Returns:
17
+ Absolute :class:`pathlib.Path` inside
18
+ ``~/.cache/cosmochains/<backend_key>/<hash>/``.
19
+ Parent directories are created automatically.
20
+ """
21
+ safe = hashlib.sha256(repo_id.encode()).hexdigest()[:16]
22
+ p = Path.home() / ".cache" / "cosmochains" / backend_key / safe / relative
23
+ p.parent.mkdir(parents=True, exist_ok=True)
24
+ return p