{"id":46996077,"url":"https://github.com/pyiron/bagofholding","last_synced_at":"2026-03-11T15:17:24.958Z","repository":{"id":300306586,"uuid":"932344567","full_name":"pyiron/bagofholding","owner":"pyiron","description":"Pickle-like storage for python objects with browsing, version control, and partial loading","archived":false,"fork":false,"pushed_at":"2026-03-09T14:21:48.000Z","size":687,"stargazers_count":3,"open_issues_count":10,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-03-09T18:58:01.256Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyiron.png","metadata":{"files":{"readme":"docs/README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-13T19:02:31.000Z","updated_at":"2026-03-05T14:52:52.000Z","dependencies_parsed_at":"2025-06-21T00:28:17.684Z","dependency_job_id":"c227969d-ffec-41f7-8cd4-6951fb13ab2d","html_url":"https://github.com/pyiron/bagofholding","commit_stats":null,"previous_names":["pyiron/bagofholding"],"tags_count":13,"template":false,"template_full_name":"pyiron/pyiron_module_template","purl":"pkg:github/pyiron/bagofholding","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fbagofholding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fbagofholding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fbagofholding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fbagofholding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyiron","download_url":"https://codeload.github.com/pyiron/bagofholding/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fbagofholding/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30385497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T14:10:17.325Z","status":"ssl_error","status_checked_at":"2026-03-11T14:09:37.934Z","response_time":84,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-11T15:17:20.498Z","updated_at":"2026-03-11T15:17:24.953Z","avatar_url":"https://github.com/pyiron.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bagofholding\n\n\u003cimg src=\"_static/bagofholding_logo.png\" alt=\"Logo\" width=\"190\"/\u003e  \n\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pyiron/bagofholding/HEAD)\n[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n[![Coverage](https://codecov.io/gh/pyiron/bagofholding/graph/badge.svg)](https://codecov.io/gh/pyiron/bagofholding)\n[![Documentation](https://readthedocs.org/projects/bagofholding/badge/?version=latest)](https://bagofholding.readthedocs.io/en/latest/?badge=latest)\n\n[![Anaconda](https://anaconda.org/conda-forge/bagofholding/badges/version.svg)](https://anaconda.org/conda-forge/bagofholding)\n[![Last Updated](https://anaconda.org/conda-forge/bagofholding/badges/latest_release_date.svg)](https://anaconda.org/conda-forge/bagofholding)\n[![Platform](https://anaconda.org/conda-forge/bagofholding/badges/platforms.svg)](https://anaconda.org/conda-forge/bagofholding)\n[![Downloads](https://anaconda.org/conda-forge/bagofholding/badges/downloads.svg)](https://anaconda.org/conda-forge/bagofholding)\n\n`bagofholding` is designed to be an easy stand-in for `pickle` serialization for python objects that is transparent, flexible, and suitable for long-term storage.\n\n## Advantages\n### Drop-in replacement\n\n`bagofholding` stores `pickle`-able python objects, and can be easily used as a drop-in replacement for `pickle` serialization:\n\n```python\n\u003e\u003e\u003e import bagofholding as boh\n\u003e\u003e\u003e\n\u003e\u003e\u003e boh.H5Bag.save(42, \"file.h5\")\n\u003e\u003e\u003e print(boh.H5Bag(\"file.h5\").load())\n42\n\n```\n\n\n### Browseable\n\nThe contents of stored objects can be browsed without re-instantiating any of the stored data.\nIn the example above, we saw that saving is a class-method, while loading is an instance method.\nWe can grab the \"bag\" instance and use it to peek at what's inside!\n\nLet's use a slightly more complex object.\nReaders familiar with `pickle` will be able to see that the \"reduced\" structure of the object is captured in the structure of the storage itself:\n\n```python\n\u003e\u003e\u003e class MyThing:\n...     def __init__(self, answer: int, question: str):\n...         self.answer = answer\n...         self.question = question\n\u003e\u003e\u003e\n\u003e\u003e\u003e something = MyThing(42, \"still computing...\")\n\u003e\u003e\u003e boh.H5Bag.save(something, \"something.h5\")\n\u003e\u003e\u003e bag = boh.H5Bag(\"something.h5\")\n\u003e\u003e\u003e bag.list_paths()\n['object', 'object/args', 'object/args/i0', 'object/constructor', 'object/item_iterator', 'object/kv_iterator', 'object/state', 'object/state/answer', 'object/state/question']\n\n```\n\nItem-access on the bag object gives access to metadata stored alongside the actual serialized information:\n\n```python\n\u003e\u003e\u003e bag[\"object\"]\nMetadata(content_type='bagofholding.content.Reducible', qualname='MyThing', module='__main__', version=None, meta=None)\n\n```\n\nFor Jupyter users, we power-up browsing capabilities with a widget under `bag.browse()` which lets you navigate the tree and see both metadata values and stored types:\n\n![](_static/widget_snapshot.png)\n\n\n### Partial-loading\n\nStored objects can also be re-instantiated _in part_ by leveraging their storage path:\n\n```python\n\u003e\u003e\u003e bag.load(\"object/state/answer\")\n42\n\n```\n\nNote that we didn't re-instantiate any part of the object other than this one integer!\n\nThis feature is incredibly useful for long-term storage and data transferability, as the loading environment does not need to fully match the saving environment -- only the environment required to load the actual piece of data desired matches.\nConsider some complex object which, ultimately, contains important or expensive-to-calculate numeric data, e.g. in the form of numpy array.\nWith `bagofholding`, you can pass this data to a colleague running a different python environment, or come back to it years later.\nWith only `bagofholding` and `numpy` installed, the end user can browse through the stored object, access, and load only the valuable numeric data without re-installing the entire original environment.\n\n\n### Version control\n\nIn the examples above, we saw that version (and of course package) information is part of the automatically-scraped and stored metadata.\nThis is useful post-facto for knowing what packages need to be installed to properly load your serialized data, and allows us to fail in clean and helpful ways if the loading environment does not match the saving environment.\nYou can also specify at load-time how strict or relaxed `bagofholding` should be in re-instantiating data if a stored version does not match the currently installed version, giving flexible protection from flawed re-instantiations.\n\n`bagofholding` also provides tools to act on this data a-priori.\nTo increase the likelihood that stored data will be accessible in the future, you can outlaw any (sub)objects coming from particular modules:\n\n```python\nimport bagofholding as boh\n\u003e\u003e\u003e try:\n...     boh.H5Bag.save(something, \"will_fail.h5\", forbidden_modules=(\"__main__\",))\n... except boh.ModuleForbiddenError as e:\n...     print(e)\nModule '__main__' is forbidden as a source of stored objects. Change the `forbidden_modules` or move this object to an allowed module.\n\n```\n\nAnd/or demand that all objects have an identifiable version:\n\n```python\nimport bagofholding as boh\n\u003e\u003e\u003e try:\n...     boh.H5Bag.save(something, \"will_fail.h5\", require_versions=True)\n... except boh.NoVersionError as e:\n...     print(e)\nCould not find a version for __main__. Either disable `require_versions`, use `version_scraping` to find an existing version for this package, or add versioning to the unversioned package.\n\n```\n\nOf course, metadata for the bag itself is also stored.\nWe saw this in the GUI snapshot above, but it can also be accessed directly by code:\n\n```python\n\u003e\u003e\u003e boh.H5Bag.get_bag_info()\nH5Info(qualname='H5Bag', module='bagofholding.h5.bag', version='...', libver_str='latest')\n\n```\n\n(In reality you will see a version code, it is omitted here because this example is executed automatically in the test suite.)\n\n## Going further\n\nFor a more in-depth look at the above features and to explore other aspects of `bagofholding`, check out [the tutorial notebook](../notebooks/tutorial.ipynb).\n\n\n## Object requirements\n\nUnder-the-hood, we follow the same patterns as `pickle` by explicitly invoking many of the same method (`__reduce__`, `__setstate__`, etc).\n_Almost_ any object which can be pickled can be stored using `bagofholding`.\nOur requirements are that the object...\n\n- Must be pickleable\n  - You can use the `pickle_check` method on bag classes to quickly assess this\n- Must not depend on `pickle` protocol \u003e4\n- Must have a valid boolean response to `hasattr` for each of the following, and they must conform to python and `abc.collections` norms if present:\n  - `__setstate__`\n  - `__setitem__`\n  - `append`\n  - `extend`\n- Must have a valid boolean response to `hasattr` for `__metadata__`, and this attribute must be castable to a string if present\n\nIf your object satisfies these conditions and fails to \"bag\", please raise a bug report on the issues page!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyiron%2Fbagofholding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyiron%2Fbagofholding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyiron%2Fbagofholding/lists"}