bagofholding 0.1.3__tar.gz → 0.1.5__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 (35) hide show
  1. {bagofholding-0.1.3 → bagofholding-0.1.5}/PKG-INFO +6 -6
  2. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/_version.py +16 -3
  3. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/bag.py +3 -3
  4. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/content.py +11 -9
  5. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/_version.py +16 -3
  6. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/bag.py +3 -3
  7. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/content.py +11 -9
  8. {bagofholding-0.1.3 → bagofholding-0.1.5}/docs/README.md +2 -2
  9. {bagofholding-0.1.3 → bagofholding-0.1.5}/pyproject.toml +3 -3
  10. bagofholding-0.1.3/bagofholding/retrieve.py +0 -52
  11. bagofholding-0.1.3/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/retrieve.py +0 -52
  12. {bagofholding-0.1.3 → bagofholding-0.1.5}/.gitignore +0 -0
  13. {bagofholding-0.1.3 → bagofholding-0.1.5}/LICENSE +0 -0
  14. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/__init__.py +0 -0
  15. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/exceptions.py +0 -0
  16. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/__init__.py +0 -0
  17. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/bag.py +0 -0
  18. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/content.py +0 -0
  19. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/context.py +0 -0
  20. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/dtypes.py +0 -0
  21. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/h5/triebag.py +0 -0
  22. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/metadata.py +0 -0
  23. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/trie.py +0 -0
  24. {bagofholding-0.1.3 → bagofholding-0.1.5}/bagofholding/widget.py +0 -0
  25. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/__init__.py +0 -0
  26. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/exceptions.py +0 -0
  27. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/__init__.py +0 -0
  28. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/bag.py +0 -0
  29. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/content.py +0 -0
  30. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/context.py +0 -0
  31. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/dtypes.py +0 -0
  32. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/h5/triebag.py +0 -0
  33. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/metadata.py +0 -0
  34. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/trie.py +0 -0
  35. {bagofholding-0.1.3 → bagofholding-0.1.5}/cached-miniforge/my-env/lib/python3.1/site-packages/bagofholding/widget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bagofholding
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: bagofholding - browsable, partially-reloadable serialization for pickleable python objects.
5
5
  Project-URL: Homepage, https://pyiron.org/
6
6
  Project-URL: Documentation, https://bagofholding.readthedocs.io
@@ -43,14 +43,14 @@ Classifier: Operating System :: OS Independent
43
43
  Classifier: Programming Language :: Python :: 3.11
44
44
  Classifier: Programming Language :: Python :: 3.12
45
45
  Classifier: Programming Language :: Python :: 3.13
46
+ Classifier: Programming Language :: Python :: 3.14
46
47
  Classifier: Topic :: Scientific/Engineering
47
- Requires-Python: <3.14,>=3.11
48
+ Requires-Python: <3.15,>=3.11
48
49
  Requires-Dist: bidict==0.23.1
49
50
  Requires-Dist: h5py<3.15.0,>=3.12.1
50
- Requires-Dist: mpi4py<4.2.0,>=4.0.1
51
51
  Requires-Dist: numpy<2.4.0,>=1.26.4
52
52
  Requires-Dist: pygtrie<2.6.0,>=2.5.0
53
- Requires-Dist: pyiron-snippets<=0.2.0,>=0.1.4
53
+ Requires-Dist: pyiron-snippets<2.0.0,>=1.0.0
54
54
  Provides-Extra: widget
55
55
  Requires-Dist: ipytree==0.2.2; extra == 'widget'
56
56
  Requires-Dist: traitlets==5.14.3; extra == 'widget'
@@ -191,7 +191,7 @@ For a more in-depth look at the above features and to explore other aspects of `
191
191
  ## Object requirements
192
192
 
193
193
  Under-the-hood, we follow the same patterns as `pickle` by explicitly invoking many of the same method (`__reduce__`, `__setstate__`, etc).
194
- _Almost_ and object which can be pickled can be stored using `bagofholding`.
194
+ _Almost_ any object which can be pickled can be stored using `bagofholding`.
195
195
  Our requirements are that the object...
196
196
 
197
197
  - Must be pickleable
@@ -204,4 +204,4 @@ Our requirements are that the object...
204
204
  - `extend`
205
205
  - Must have a valid boolean response to `hasattr` for `__metadata__`, and this attribute must be castable to a string if present
206
206
 
207
- If your object satisfies these conditions and fails to "bag", please raise a bug report on the issues page!
207
+ If your object satisfies these conditions and fails to "bag", please raise a bug report on the issues page!
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.1.3'
21
- __version_tuple__ = version_tuple = (0, 1, 3)
31
+ __version__ = version = '0.1.5'
32
+ __version_tuple__ = version_tuple = (0, 1, 5)
33
+
34
+ __commit_id__ = commit_id = None
@@ -27,7 +27,7 @@ from typing import (
27
27
  import bidict
28
28
  from pyiron_snippets import import_alarm
29
29
 
30
- from bagofholding.content import BespokeItem, Packer, pack, unpack
30
+ from bagofholding.content import MAX_PICKLE_PROTOCOL, BespokeItem, Packer, pack, unpack
31
31
  from bagofholding.exceptions import BagMismatchError, InvalidMetadataError
32
32
  from bagofholding.metadata import (
33
33
  HasFieldIterator,
@@ -47,7 +47,7 @@ except (ImportError, ModuleNotFoundError):
47
47
  "The browsing widget relies on ipytree and traitlets, but this was "
48
48
  "unavailable. You can get a text-representation of all available paths with "
49
49
  ":meth:`bagofholding.bag.Bag.list_paths`.",
50
- _fail_on_warning=True,
50
+ raise_exception=True,
51
51
  )
52
52
 
53
53
  PATH_DELIMITER = "/"
@@ -87,7 +87,7 @@ class Bag(Packer, Mapping[str, Metadata | None], abc.ABC):
87
87
  require_versions: bool = False,
88
88
  forbidden_modules: list[str] | tuple[str, ...] = (),
89
89
  version_scraping: VersionScrapingMap | None = None,
90
- _pickle_protocol: SupportsIndex = pickle.DEFAULT_PROTOCOL,
90
+ _pickle_protocol: SupportsIndex = MAX_PICKLE_PROTOCOL,
91
91
  overwrite_existing: bool = True,
92
92
  ) -> None:
93
93
  """
@@ -13,7 +13,6 @@ import abc
13
13
  import collections.abc
14
14
  import dataclasses
15
15
  import operator
16
- import pickle
17
16
  import types
18
17
  from collections.abc import Callable, Iterable, Iterator, Sized
19
18
  from typing import (
@@ -30,6 +29,7 @@ from typing import (
30
29
 
31
30
  import bidict
32
31
  import h5py
32
+ from pyiron_snippets import retrieve
33
33
 
34
34
  from bagofholding.exceptions import (
35
35
  ModuleForbiddenError,
@@ -46,10 +46,6 @@ from bagofholding.metadata import (
46
46
  get_version,
47
47
  validate_version,
48
48
  )
49
- from bagofholding.retrieve import (
50
- get_importable_string_from_string_reduction,
51
- import_from_string,
52
- )
53
49
 
54
50
  PackingMemoAlias: TypeAlias = bidict.bidict[int, str]
55
51
  ReferencesAlias: TypeAlias = list[object]
@@ -60,6 +56,12 @@ PackingType = TypeVar("PackingType", bound=Any)
60
56
  UnpackingType = TypeVar("UnpackingType", bound=Any)
61
57
 
62
58
 
59
+ MAX_PICKLE_PROTOCOL = 4
60
+ # Although many of the same patterns as `pickle` are exploited to decompose data,
61
+ # `bagofholding` does not actually _execute_ `pickle`.
62
+ # To this end, the highest protocol value exploiting out-of-band data is not supported
63
+
64
+
63
65
  class HasContents(Sized, Iterable[str], Protocol): ...
64
66
 
65
67
 
@@ -252,7 +254,7 @@ class Global(Item[GlobalType, Any, Packer]):
252
254
  @classmethod
253
255
  def unpack(cls, packer: Packer, path: str, unpacking: UnpackingArguments) -> Any:
254
256
  import_string = packer.unpack_string(path)
255
- return import_from_string(import_string)
257
+ return retrieve.import_from_string(import_string)
256
258
 
257
259
 
258
260
  class NoneItem(Item[type[None], None, Packer]):
@@ -782,7 +784,7 @@ def pack(
782
784
  require_versions: bool,
783
785
  forbidden_modules: list[str] | tuple[str, ...],
784
786
  version_scraping: VersionScrapingMap | None,
785
- _pickle_protocol: SupportsIndex = pickle.DEFAULT_PROTOCOL,
787
+ _pickle_protocol: SupportsIndex = MAX_PICKLE_PROTOCOL,
786
788
  ) -> None:
787
789
  if _pickle_protocol not in (4, 3, 2, 1, 0):
788
790
  raise PickleProtocolError(
@@ -831,7 +833,7 @@ def pack(
831
833
  rv = obj.__reduce_ex__(_pickle_protocol)
832
834
  if isinstance(rv, str):
833
835
  Global.pack(
834
- get_importable_string_from_string_reduction(rv, obj),
836
+ retrieve.get_importable_string_from_string_reduction(rv, obj),
835
837
  packer,
836
838
  path,
837
839
  packing_args,
@@ -887,7 +889,7 @@ def unpack(
887
889
  memo_value = memo.get(path, NotData)
888
890
  if memo_value is NotData:
889
891
  metadata = packer.unpack_metadata(path)
890
- content_class = import_from_string(metadata.content_type)
892
+ content_class = retrieve.import_from_string(metadata.content_type)
891
893
  if metadata is not None:
892
894
  validate_version(
893
895
  metadata, validator=version_validator, version_scraping=version_scraping
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.1.3'
21
- __version_tuple__ = version_tuple = (0, 1, 3)
31
+ __version__ = version = '0.1.5'
32
+ __version_tuple__ = version_tuple = (0, 1, 5)
33
+
34
+ __commit_id__ = commit_id = None
@@ -27,7 +27,7 @@ from typing import (
27
27
  import bidict
28
28
  from pyiron_snippets import import_alarm
29
29
 
30
- from bagofholding.content import BespokeItem, Packer, pack, unpack
30
+ from bagofholding.content import MAX_PICKLE_PROTOCOL, BespokeItem, Packer, pack, unpack
31
31
  from bagofholding.exceptions import BagMismatchError, InvalidMetadataError
32
32
  from bagofholding.metadata import (
33
33
  HasFieldIterator,
@@ -47,7 +47,7 @@ except (ImportError, ModuleNotFoundError):
47
47
  "The browsing widget relies on ipytree and traitlets, but this was "
48
48
  "unavailable. You can get a text-representation of all available paths with "
49
49
  ":meth:`bagofholding.bag.Bag.list_paths`.",
50
- _fail_on_warning=True,
50
+ raise_exception=True,
51
51
  )
52
52
 
53
53
  PATH_DELIMITER = "/"
@@ -87,7 +87,7 @@ class Bag(Packer, Mapping[str, Metadata | None], abc.ABC):
87
87
  require_versions: bool = False,
88
88
  forbidden_modules: list[str] | tuple[str, ...] = (),
89
89
  version_scraping: VersionScrapingMap | None = None,
90
- _pickle_protocol: SupportsIndex = pickle.DEFAULT_PROTOCOL,
90
+ _pickle_protocol: SupportsIndex = MAX_PICKLE_PROTOCOL,
91
91
  overwrite_existing: bool = True,
92
92
  ) -> None:
93
93
  """
@@ -13,7 +13,6 @@ import abc
13
13
  import collections.abc
14
14
  import dataclasses
15
15
  import operator
16
- import pickle
17
16
  import types
18
17
  from collections.abc import Callable, Iterable, Iterator, Sized
19
18
  from typing import (
@@ -30,6 +29,7 @@ from typing import (
30
29
 
31
30
  import bidict
32
31
  import h5py
32
+ from pyiron_snippets import retrieve
33
33
 
34
34
  from bagofholding.exceptions import (
35
35
  ModuleForbiddenError,
@@ -46,10 +46,6 @@ from bagofholding.metadata import (
46
46
  get_version,
47
47
  validate_version,
48
48
  )
49
- from bagofholding.retrieve import (
50
- get_importable_string_from_string_reduction,
51
- import_from_string,
52
- )
53
49
 
54
50
  PackingMemoAlias: TypeAlias = bidict.bidict[int, str]
55
51
  ReferencesAlias: TypeAlias = list[object]
@@ -60,6 +56,12 @@ PackingType = TypeVar("PackingType", bound=Any)
60
56
  UnpackingType = TypeVar("UnpackingType", bound=Any)
61
57
 
62
58
 
59
+ MAX_PICKLE_PROTOCOL = 4
60
+ # Although many of the same patterns as `pickle` are exploited to decompose data,
61
+ # `bagofholding` does not actually _execute_ `pickle`.
62
+ # To this end, the highest protocol value exploiting out-of-band data is not supported
63
+
64
+
63
65
  class HasContents(Sized, Iterable[str], Protocol): ...
64
66
 
65
67
 
@@ -252,7 +254,7 @@ class Global(Item[GlobalType, Any, Packer]):
252
254
  @classmethod
253
255
  def unpack(cls, packer: Packer, path: str, unpacking: UnpackingArguments) -> Any:
254
256
  import_string = packer.unpack_string(path)
255
- return import_from_string(import_string)
257
+ return retrieve.import_from_string(import_string)
256
258
 
257
259
 
258
260
  class NoneItem(Item[type[None], None, Packer]):
@@ -782,7 +784,7 @@ def pack(
782
784
  require_versions: bool,
783
785
  forbidden_modules: list[str] | tuple[str, ...],
784
786
  version_scraping: VersionScrapingMap | None,
785
- _pickle_protocol: SupportsIndex = pickle.DEFAULT_PROTOCOL,
787
+ _pickle_protocol: SupportsIndex = MAX_PICKLE_PROTOCOL,
786
788
  ) -> None:
787
789
  if _pickle_protocol not in (4, 3, 2, 1, 0):
788
790
  raise PickleProtocolError(
@@ -831,7 +833,7 @@ def pack(
831
833
  rv = obj.__reduce_ex__(_pickle_protocol)
832
834
  if isinstance(rv, str):
833
835
  Global.pack(
834
- get_importable_string_from_string_reduction(rv, obj),
836
+ retrieve.get_importable_string_from_string_reduction(rv, obj),
835
837
  packer,
836
838
  path,
837
839
  packing_args,
@@ -887,7 +889,7 @@ def unpack(
887
889
  memo_value = memo.get(path, NotData)
888
890
  if memo_value is NotData:
889
891
  metadata = packer.unpack_metadata(path)
890
- content_class = import_from_string(metadata.content_type)
892
+ content_class = retrieve.import_from_string(metadata.content_type)
891
893
  if metadata is not None:
892
894
  validate_version(
893
895
  metadata, validator=version_validator, version_scraping=version_scraping
@@ -133,7 +133,7 @@ For a more in-depth look at the above features and to explore other aspects of `
133
133
  ## Object requirements
134
134
 
135
135
  Under-the-hood, we follow the same patterns as `pickle` by explicitly invoking many of the same method (`__reduce__`, `__setstate__`, etc).
136
- _Almost_ and object which can be pickled can be stored using `bagofholding`.
136
+ _Almost_ any object which can be pickled can be stored using `bagofholding`.
137
137
  Our requirements are that the object...
138
138
 
139
139
  - Must be pickleable
@@ -146,4 +146,4 @@ Our requirements are that the object...
146
146
  - `extend`
147
147
  - Must have a valid boolean response to `hasattr` for `__metadata__`, and this attribute must be castable to a string if present
148
148
 
149
- If your object satisfies these conditions and fails to "bag", please raise a bug report on the issues page!
149
+ If your object satisfies these conditions and fails to "bag", please raise a bug report on the issues page!
@@ -7,9 +7,9 @@ name = "bagofholding"
7
7
  description = "bagofholding - browsable, partially-reloadable serialization for pickleable python objects."
8
8
  readme = "docs/README.md"
9
9
  keywords = [ "pyiron",]
10
- requires-python = ">=3.11, <3.14"
11
- classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering", "License :: OSI Approved :: BSD License", "Intended Audience :: Science/Research", "Operating System :: OS Independent", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13",]
12
- dependencies = [ "bidict==0.23.1", "h5py>=3.12.1,<3.15.0", "mpi4py>=4.0.1,<4.2.0", "numpy>=1.26.4,<2.4.0", "pygtrie>=2.5.0,<2.6.0", "pyiron_snippets>=0.1.4,<=0.2.0",]
10
+ requires-python = ">=3.11, <3.15"
11
+ classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering", "License :: OSI Approved :: BSD License", "Intended Audience :: Science/Research", "Operating System :: OS Independent", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14",]
12
+ dependencies = [ "bidict==0.23.1", "h5py>=3.12.1,<3.15.0", "numpy>=1.26.4,<2.4.0", "pygtrie>=2.5.0,<2.6.0", "pyiron_snippets>=1.0.0,<2.0.0",]
13
13
  dynamic = [ "version",]
14
14
  [[project.authors]]
15
15
  name = "Liam Huber"
@@ -1,52 +0,0 @@
1
- """
2
- Helper functions for managing the relationship between strings and imports.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from importlib import import_module
8
- from typing import Any
9
-
10
- from bagofholding.exceptions import StringNotImportableError
11
-
12
-
13
- def import_from_string(library_path: str) -> Any:
14
- split_path = library_path.split(".", 1)
15
- if len(split_path) == 1:
16
- module_name, path = split_path[0], ""
17
- else:
18
- module_name, path = split_path
19
- obj = import_module(module_name)
20
- for k in path.split("."):
21
- obj = getattr(obj, k)
22
- return obj
23
-
24
-
25
- def get_importable_string_from_string_reduction(
26
- string_reduction: str, reduced_object: object
27
- ) -> str:
28
- """
29
- Per the pickle docs:
30
-
31
- > If a string is returned, the string should be interpreted as the name of a global
32
- variable. It should be the object’s local name relative to its module; the pickle
33
- module searches the module namespace to determine the object’s module. This
34
- behaviour is typically useful for singletons.
35
-
36
- To then import such an object from a non-local caller, we try scoping the string
37
- with the module of the object which returned it.
38
- """
39
- try:
40
- import_from_string(string_reduction)
41
- importable = string_reduction
42
- except ModuleNotFoundError:
43
- importable = reduced_object.__module__ + "." + string_reduction
44
- try:
45
- import_from_string(importable)
46
- except (ModuleNotFoundError, AttributeError) as e:
47
- raise StringNotImportableError(
48
- f"Couldn't import {string_reduction} after scoping it as {importable}. "
49
- f"Please contact the developers so we can figure out how to handle "
50
- f"this edge case."
51
- ) from e
52
- return importable
@@ -1,52 +0,0 @@
1
- """
2
- Helper functions for managing the relationship between strings and imports.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from importlib import import_module
8
- from typing import Any
9
-
10
- from bagofholding.exceptions import StringNotImportableError
11
-
12
-
13
- def import_from_string(library_path: str) -> Any:
14
- split_path = library_path.split(".", 1)
15
- if len(split_path) == 1:
16
- module_name, path = split_path[0], ""
17
- else:
18
- module_name, path = split_path
19
- obj = import_module(module_name)
20
- for k in path.split("."):
21
- obj = getattr(obj, k)
22
- return obj
23
-
24
-
25
- def get_importable_string_from_string_reduction(
26
- string_reduction: str, reduced_object: object
27
- ) -> str:
28
- """
29
- Per the pickle docs:
30
-
31
- > If a string is returned, the string should be interpreted as the name of a global
32
- variable. It should be the object’s local name relative to its module; the pickle
33
- module searches the module namespace to determine the object’s module. This
34
- behaviour is typically useful for singletons.
35
-
36
- To then import such an object from a non-local caller, we try scoping the string
37
- with the module of the object which returned it.
38
- """
39
- try:
40
- import_from_string(string_reduction)
41
- importable = string_reduction
42
- except ModuleNotFoundError:
43
- importable = reduced_object.__module__ + "." + string_reduction
44
- try:
45
- import_from_string(importable)
46
- except (ModuleNotFoundError, AttributeError) as e:
47
- raise StringNotImportableError(
48
- f"Couldn't import {string_reduction} after scoping it as {importable}. "
49
- f"Please contact the developers so we can figure out how to handle "
50
- f"this edge case."
51
- ) from e
52
- return importable
File without changes
File without changes