providence 0.0.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: providence
3
+ Version: 0.0.1
4
+ Summary: Reproducible dataset loading and source tracking.
5
+ Author-email: Eachan Johnson <eachan.johnson@crick.ac.uk>
6
+ License: MIT License
7
+
8
+ Copyright (c) [year] [fullname]
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/scbirlab/providence
28
+ Project-URL: Bug Tracker, https://github.com/scbirlab/providence/issues
29
+ Keywords: ai,data,deep-learning,machine-learning
30
+ Classifier: Development Status :: 3 - Alpha
31
+ Classifier: Intended Audience :: Science/Research
32
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Classifier: Programming Language :: Python :: 3.14
38
+ Classifier: Programming Language :: Python :: 3.15
39
+ Classifier: Programming Language :: Python :: 3 :: Only
40
+ Requires-Python: >=3.11
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: carabiner-tools[mpl,pd]>=0.0.5.post3
44
+ Requires-Dist: datasets>=3
45
+ Requires-Dist: filelock
46
+ Requires-Dist: huggingface_hub
47
+ Requires-Dist: platformdirs>=4.4.0
48
+ Requires-Dist: tqdm
49
+ Provides-Extra: dev
50
+ Requires-Dist: providence; extra == "dev"
51
+ Requires-Dist: pytest>=7.4; extra == "dev"
52
+ Requires-Dist: flake8; extra == "dev"
53
+ Dynamic: license-file
54
+
55
+ # 🦞 providence
56
+
57
+ <img src="docs/source/_static/logo.svg" width="200" alt="Providence logo">
58
+
59
+ **providence** is a Python package for reproducible dataset loading and source tracking. Load a local table or a Hugging Face dataset, record where it came from, and save a checkpoint that can reload the same data.
60
+
61
+ Use Providence to load data before splitting it with [Eluent](https://github.com/scbirlab/eluent) or computing features with [Aspect](https://github.com/scbirlab/aspect). You can also checkpoint a split or feature table you already have.
62
+
63
+ ## Installation
64
+
65
+ Providence requires Python 3.11 or later. To install from GitHub, run:
66
+
67
+ ```bash
68
+ python -m pip install 'git+https://github.com/scbirlab/providence.git'
69
+ ```
70
+
71
+ ## Python
72
+
73
+ `DataResolver` accepts local files such as CSV, TSV, and Parquet, as well as pandas DataFrames, dictionaries of columns, and Hugging Face datasets. The loaded Hugging Face dataset is available as `.data`, and `.data_source` holds its source metadata.
74
+
75
+ ```python
76
+ from providence import DataResolver
77
+
78
+ resolved = DataResolver("measurements.csv")
79
+ resolved.save("measurements.checkpoint")
80
+
81
+ restored = DataResolver.load("measurements.checkpoint")
82
+ print(restored.data.num_rows)
83
+ print(restored.data_source.to_config())
84
+ ```
85
+
86
+ By default, `save()` embeds local or in-memory data as Parquet. The checkpoint stores a SHA-256 checksum and verifies the embedded file on reload. For a local file that will remain available at its original path, use `save_source_data=False` to save a reference and verify the source file when you reload.
87
+
88
+ ## Command line
89
+
90
+ Use `providence get` to write a checkpoint, export a table, or do both:
91
+
92
+ ```bash
93
+ providence get measurements.csv --checkpoint measurements.checkpoint --output measurements.parquet
94
+ ```
95
+
96
+ The CLI saves a source reference by default. Add `--force-write-data` to embed the table in the checkpoint.
97
+
98
+ For Hub datasets, use `hf://datasets/OWNER/DATASET`. Append `@REVISION`, `~CONFIG`, and `:SPLIT` as needed. For example:
99
+
100
+ ```bash
101
+ providence get 'hf://datasets/stanfordnlp/imdb@main:train' --checkpoint imdb-train.checkpoint
102
+ ```
103
+
104
+ Providence resolves the requested Hub revision to a commit SHA and records the pinned reference in the checkpoint. Reloading that checkpoint uses the recorded commit even if the branch has moved.
105
+
106
+ ## Issues, problems, suggestions
107
+
108
+ Please [open an issue on GitHub](https://github.com/scbirlab/providence/issues).
109
+
110
+ ## License
111
+
112
+ [MIT](LICENSE)
@@ -0,0 +1,58 @@
1
+ # 🦞 providence
2
+
3
+ <img src="docs/source/_static/logo.svg" width="200" alt="Providence logo">
4
+
5
+ **providence** is a Python package for reproducible dataset loading and source tracking. Load a local table or a Hugging Face dataset, record where it came from, and save a checkpoint that can reload the same data.
6
+
7
+ Use Providence to load data before splitting it with [Eluent](https://github.com/scbirlab/eluent) or computing features with [Aspect](https://github.com/scbirlab/aspect). You can also checkpoint a split or feature table you already have.
8
+
9
+ ## Installation
10
+
11
+ Providence requires Python 3.11 or later. To install from GitHub, run:
12
+
13
+ ```bash
14
+ python -m pip install 'git+https://github.com/scbirlab/providence.git'
15
+ ```
16
+
17
+ ## Python
18
+
19
+ `DataResolver` accepts local files such as CSV, TSV, and Parquet, as well as pandas DataFrames, dictionaries of columns, and Hugging Face datasets. The loaded Hugging Face dataset is available as `.data`, and `.data_source` holds its source metadata.
20
+
21
+ ```python
22
+ from providence import DataResolver
23
+
24
+ resolved = DataResolver("measurements.csv")
25
+ resolved.save("measurements.checkpoint")
26
+
27
+ restored = DataResolver.load("measurements.checkpoint")
28
+ print(restored.data.num_rows)
29
+ print(restored.data_source.to_config())
30
+ ```
31
+
32
+ By default, `save()` embeds local or in-memory data as Parquet. The checkpoint stores a SHA-256 checksum and verifies the embedded file on reload. For a local file that will remain available at its original path, use `save_source_data=False` to save a reference and verify the source file when you reload.
33
+
34
+ ## Command line
35
+
36
+ Use `providence get` to write a checkpoint, export a table, or do both:
37
+
38
+ ```bash
39
+ providence get measurements.csv --checkpoint measurements.checkpoint --output measurements.parquet
40
+ ```
41
+
42
+ The CLI saves a source reference by default. Add `--force-write-data` to embed the table in the checkpoint.
43
+
44
+ For Hub datasets, use `hf://datasets/OWNER/DATASET`. Append `@REVISION`, `~CONFIG`, and `:SPLIT` as needed. For example:
45
+
46
+ ```bash
47
+ providence get 'hf://datasets/stanfordnlp/imdb@main:train' --checkpoint imdb-train.checkpoint
48
+ ```
49
+
50
+ Providence resolves the requested Hub revision to a commit SHA and records the pinned reference in the checkpoint. Reloading that checkpoint uses the recorded commit even if the branch has moved.
51
+
52
+ ## Issues, problems, suggestions
53
+
54
+ Please [open an issue on GitHub](https://github.com/scbirlab/providence/issues).
55
+
56
+ ## License
57
+
58
+ [MIT](LICENSE)
@@ -0,0 +1,8 @@
1
+ from importlib.metadata import version
2
+
3
+ app_name = "providence"
4
+ __author__ = "Eachan Johnson"
5
+ __version__ = version(app_name)
6
+
7
+ from .data import DataResolver
8
+ from .io import DataSource
@@ -0,0 +1,95 @@
1
+ """"Utilities for loading and saving checkpoints."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union
4
+ from tempfile import TemporaryDirectory
5
+ import os
6
+ import json
7
+
8
+ from carabiner import print_err
9
+
10
+ if TYPE_CHECKING:
11
+ from datasets import Dataset, IterableDataset
12
+ else:
13
+ Dataset, IterableDataset = Any, Any
14
+
15
+
16
+ def _load_json(checkpoint: str, filename: str) -> Dict[str, Any]:
17
+ with open(os.path.join(checkpoint, filename), "r") as f:
18
+ obj = json.load(f)
19
+ return obj
20
+
21
+
22
+ def save_json(obj, filename: str) -> None:
23
+ with open(filename, "w") as f:
24
+ try:
25
+ json.dump(obj, f, sort_keys=True, indent=4)
26
+ except TypeError as e:
27
+ print_err(f"{obj=}")
28
+ raise e
29
+ return None
30
+
31
+
32
+ def _load_hf_dataset(checkpoint, filename) -> Union[Dataset, IterableDataset]:
33
+ from datasets import load_from_disk
34
+ return load_from_disk(os.path.join(checkpoint, filename))
35
+
36
+
37
+ FILE_LOADING_CALLBACKS = {
38
+ "json": _load_json,
39
+ "hf-dataset": _load_hf_dataset,
40
+ }
41
+
42
+
43
+ def load_checkpoint_file(
44
+ checkpoint: str,
45
+ filename: str,
46
+ *args,
47
+ callback: Union[str, Callable] = "json",
48
+ none_on_error: bool = False,
49
+ cache_dir: Optional[str] = None,
50
+ **kwargs
51
+ ) -> Union[Any, None]:
52
+ from huggingface_hub import snapshot_download
53
+
54
+ obj = None
55
+ if isinstance(callback, str):
56
+ try:
57
+ callback = FILE_LOADING_CALLBACKS[callback.casefold()]
58
+ except KeyError:
59
+ raise ValueError(
60
+ """
61
+ File loading callback must be callable or name.
62
+ """
63
+ )
64
+ if os.path.exists(checkpoint):
65
+ obj = callback(checkpoint, filename)
66
+ elif checkpoint.startswith("hf://"):
67
+ checkpoint = checkpoint.split("hf://")[-1]
68
+ if filename.endswith(".hf"):
69
+ filename_pattern = [filename + '/*.arrow', filename + '/*.json']
70
+ else:
71
+ filename_pattern = filename
72
+ with TemporaryDirectory() as tmpdirname:
73
+ try:
74
+ print_err(f"Looking up: {checkpoint} :: {filename}")
75
+ snapshot_download(
76
+ repo_id=checkpoint,
77
+ allow_patterns=filename_pattern,
78
+ local_dir=tmpdirname,
79
+ cache_dir=cache_dir,
80
+ *args, **kwargs
81
+ )
82
+ except Exception as e:
83
+ print_err(e)
84
+ if none_on_error:
85
+ return None
86
+ else:
87
+ raise e
88
+ else:
89
+ obj = callback(tmpdirname, filename)
90
+ if obj is not None:
91
+ return obj
92
+ else:
93
+ raise AttributeError(
94
+ f"Could not load anything from {checkpoint=}, {filename=} with {callback=}."
95
+ )
File without changes
@@ -0,0 +1,92 @@
1
+ """Command-line interface for providence."""
2
+
3
+ from argparse import FileType
4
+ import os
5
+ import sys
6
+
7
+ from carabiner.cliutils import CLIOption, CLICommand, CLIApp
8
+
9
+ from .. import app_name, __version__
10
+ from .fetch import _fetch
11
+
12
+ def main() -> None:
13
+
14
+ input_file = CLIOption(
15
+ 'input_file',
16
+ type=FileType('r'),
17
+ default=sys.stdin,
18
+ nargs='?',
19
+ help='Input file. Default: STDIN',
20
+ )
21
+ input_filename = CLIOption(
22
+ 'input_file',
23
+ type=str,
24
+ help='Input file.',
25
+ )
26
+ force_write = CLIOption(
27
+ '--force-write-data', '-f',
28
+ action='store_true',
29
+ help='Save data to disk from remote repos, instead of simply saving a reference.',
30
+ )
31
+ cache = CLIOption(
32
+ '--cache',
33
+ type=str,
34
+ default=None,
35
+ help='Where to cache data.',
36
+ )
37
+ output_name = CLIOption(
38
+ '--output', '-o',
39
+ type=str,
40
+ default=False,
41
+ help='Output filename for data table.',
42
+ )
43
+ checkpoint = CLIOption(
44
+ '--checkpoint', '-c',
45
+ type=str,
46
+ default=False,
47
+ help='Output filename for Providence checkpoint.',
48
+ )
49
+
50
+ # slice dataset
51
+ slice_start = CLIOption(
52
+ '--start',
53
+ type=int,
54
+ default=None,
55
+ help='First row of dataset to process.',
56
+ )
57
+ slice_end = CLIOption(
58
+ '--end',
59
+ type=int,
60
+ default=None,
61
+ help='Last row of dataset to process. Default: end of dataset.',
62
+ )
63
+
64
+ get_command = CLICommand(
65
+ "get",
66
+ description="Load a local or remote dataset, and save as a reloadable and verifiable providence checkpoint.",
67
+ options=[
68
+ input_filename,
69
+ slice_start,
70
+ slice_end,
71
+ force_write,
72
+ checkpoint,
73
+ output_name,
74
+ cache,
75
+ ],
76
+ main=_fetch,
77
+ )
78
+
79
+ app = CLIApp(
80
+ app_name,
81
+ description="Data ingestion, validation, and provenance.",
82
+ version=__version__,
83
+ commands=[
84
+ get_command,
85
+ ],
86
+ )
87
+ app.run()
88
+ return None
89
+
90
+
91
+ if __name__ == '__main__':
92
+ main()
@@ -0,0 +1,43 @@
1
+ from argparse import Namespace
2
+ import os
3
+
4
+ from carabiner.cliutils import clicommand
5
+
6
+
7
+ @clicommand("Fetching data with the following parameters")
8
+ def _fetch(args: Namespace) -> None:
9
+
10
+ from .io import _save_dataset
11
+ from ..data import DataResolver
12
+
13
+ resolved = DataResolver(
14
+ args.input_file,
15
+ cache_dir=args.cache,
16
+ )
17
+ if args.start is not None or args.end is not None:
18
+ resolved = resolved.select_rows(
19
+ args.start,
20
+ args.end,
21
+ )
22
+
23
+ if not args.checkpoint and not args.output:
24
+ raise ValueError("At least one of --checkpoint and --output should be set.")
25
+
26
+ if args.checkpoint:
27
+ output = args.checkpoint
28
+ out_dir = os.path.dirname(output)
29
+ base = os.path.basename(output)
30
+
31
+ resolved.save_checkpoint(output, save_source_data=args.force_write_data)
32
+
33
+ if args.output:
34
+ output = args.output
35
+ out_dir = os.path.dirname(output)
36
+ base = os.path.basename(output)
37
+ if len(out_dir) > 0:
38
+ os.makedirs(out_dir, exist_ok=True)
39
+ _save_dataset(
40
+ resolved.data,
41
+ output,
42
+ )
43
+ return None
@@ -0,0 +1,51 @@
1
+
2
+ from carabiner import print_err
3
+
4
+
5
+ def _resolve_and_slice_data(
6
+ data: str,
7
+ start: int | None = None,
8
+ end: int | None = None,
9
+ batch_size: int = 1024,
10
+ cache_dir: str = None
11
+ ):
12
+ from ..io import AutoDataset
13
+
14
+ ds = AutoDataset.load(data, cache_dir)
15
+ nrows = ds._dataset.num_rows
16
+ skip = 0 if start is None else start
17
+ take = (nrows if end is None else end) - skip
18
+ if (take - skip) < nrows:
19
+ print_err(f"[INFO] Reading dataset from row {skip} to row {take + skip} / {nrows}.")
20
+ return ds._dataset.skip(skip).take(take)
21
+
22
+
23
+ def _save_dataset(
24
+ dataset,
25
+ output: str
26
+ ) -> None:
27
+ print_err("[INFO] Saving dataset:\n" + str(dataset) + "\n" + f"at {output} as", end=" ")
28
+ if output.removesuffix(".gz").endswith((".csv", ".tsv", ".txt")):
29
+ print_err("CSV.")
30
+ dataset.to_csv(
31
+ output,
32
+ sep="," if output.removesuffix(".gz").endswith(".csv") else "\t",
33
+ compression='gzip' if output.endswith(".gz") else None,
34
+ )
35
+ elif output.endswith(".json"):
36
+ print_err("JSON.")
37
+ dataset.to_json(output)
38
+ elif output.endswith(".parquet"):
39
+ print_err("Parquet.")
40
+ dataset.to_parquet(output)
41
+ elif output.endswith(".sql"):
42
+ print_err("SQL.")
43
+ dataset.to_sql(output)
44
+ elif output.endswith(".hf"):
45
+ print_err("Hugging Face dataset.")
46
+ dataset.save_to_disk(output)
47
+ else:
48
+ print_err("Hugging Face dataset.")
49
+ dataset.save_to_disk(output + ".hf")
50
+ print_err(f"WARNING: Unsure what format to save as for filename {output}. Defaulted to Hugging Face dataset.")
51
+ return None