Skip to content

Commit e6cbb83

Browse files
authored
Merge pull request #1 from malmeloo/flexible-libs
Be more flexible when saving / loading prov and libs data
2 parents 44a8cef + ce48251 commit e6cbb83

File tree

4 files changed

+52
-20
lines changed

4 files changed

+52
-20
lines changed

src/anisette/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from importlib.metadata import version
44

55
from ._device import AnisetteDeviceConfig
6-
from .anisette import Anisette
6+
from .anisette import Anisette, AnisetteHeaders
77

88
__version__ = version("anisette")
99

10-
__all__ = ("Anisette", "AnisetteDeviceConfig")
10+
__all__ = ("Anisette", "AnisetteDeviceConfig", "AnisetteHeaders")

src/anisette/_ani_provider.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
from __future__ import annotations
22

33
import logging
4-
from typing import BinaryIO
4+
from typing import BinaryIO, Callable
55

66
from typing_extensions import Self
77

88
from ._adi import ADI
99
from ._device import AnisetteDeviceConfig, Device
10-
from ._fs import FSCollection
10+
from ._fs import FSCollection, VirtualFileSystem
1111
from ._library import LibraryStore
1212
from ._session import ProvisioningSession
1313

1414

1515
class AnisetteProvider:
16-
def __init__(self, fs_collection: FSCollection, default_device_config: AnisetteDeviceConfig | None) -> None:
16+
def __init__(
17+
self,
18+
fs_collection: FSCollection,
19+
fs_fallback: Callable[[], VirtualFileSystem],
20+
default_device_config: AnisetteDeviceConfig | None,
21+
) -> None:
1722
self._fs_collection = fs_collection
23+
self._fs_fallback = fs_fallback
1824
self._default_device_config = default_device_config or AnisetteDeviceConfig.default()
1925

2026
self._lib_store: LibraryStore | None = None
@@ -23,8 +29,13 @@ def __init__(self, fs_collection: FSCollection, default_device_config: AnisetteD
2329
self._provisioning_session: ProvisioningSession | None = None
2430

2531
@classmethod
26-
def load(cls, *files: BinaryIO, default_device_config: AnisetteDeviceConfig | None = None) -> Self:
27-
provider = cls(FSCollection.load(*files), default_device_config)
32+
def load(
33+
cls,
34+
*files: BinaryIO,
35+
fs_fallback: Callable[[], VirtualFileSystem],
36+
default_device_config: AnisetteDeviceConfig | None = None,
37+
) -> Self:
38+
provider = cls(FSCollection.load(*files), fs_fallback, default_device_config)
2839
assert provider.library_store is not None # verify that library store exists
2940
return provider
3041

@@ -34,10 +45,10 @@ def save(self, file: BinaryIO, include: list[str] | None = None, exclude: list[s
3445
@property
3546
def library_store(self) -> LibraryStore:
3647
if self._lib_store is None:
37-
lib_fs = self._fs_collection.get("libs", False)
48+
lib_fs = self._fs_collection.get("libs", create_if_missing=False)
3849
if lib_fs is None:
39-
msg = "Library filesystem missing"
40-
raise RuntimeError(msg)
50+
lib_fs = self._fs_fallback()
51+
self._fs_collection.add("libs", lib_fs)
4152
self._lib_store = LibraryStore.from_virtfs(lib_fs)
4253
return self._lib_store
4354

src/anisette/_fs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ def load(cls, *files: BinaryIO) -> Self:
168168
filesystems[name] = VirtualFileSystem(fs)
169169
return cls(**filesystems)
170170

171+
def add(self, name: str, fs: VirtualFileSystem) -> None:
172+
self._filesystems[name] = fs
173+
171174
def save(self, file: BinaryIO, include: list[str] | None = None, exclude: list[str] | None = None) -> None:
172175
to_save = set(self._filesystems.keys()) if include is None else set(include)
173176
if exclude is not None:

src/anisette/anisette.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@
4343
)
4444

4545

46+
def _get_libs(file: BinaryIO | str | Path | None = None) -> LibraryStore:
47+
file = file or DEFAULT_LIBS_URL
48+
49+
with open_file(file, "rb") as f:
50+
return LibraryStore.from_file(f)
51+
52+
4653
class Anisette:
4754
"""
4855
The main Anisette provider class.
@@ -64,6 +71,11 @@ def __init__(self, ani_provider: AnisetteProvider) -> None:
6471

6572
self._ds_id = c_ulonglong(-2).value
6673

74+
@property
75+
def is_provisioned(self) -> bool:
76+
"""Whether this Anisette session has been provisioned yet or not."""
77+
return self._ani_provider.adi.is_machine_provisioned(self._ds_id)
78+
6779
@classmethod
6880
def init(
6981
cls,
@@ -81,14 +93,11 @@ def init(
8193
:return: An instance of :class:`Anisette`.
8294
:rtype: :class:`Anisette`
8395
"""
84-
file = file or DEFAULT_LIBS_URL
85-
86-
with open_file(file, "rb") as f:
87-
library_store = LibraryStore.from_file(f)
88-
89-
fs_collection = FSCollection(libs=library_store)
90-
ani_provider = AnisetteProvider(fs_collection, default_device_config)
91-
96+
ani_provider = AnisetteProvider(
97+
FSCollection(),
98+
lambda: _get_libs(file),
99+
default_device_config,
100+
)
92101
return cls(ani_provider)
93102

94103
@classmethod
@@ -106,7 +115,11 @@ def load(cls, *files: BinaryIO | str | Path, default_device_config: AnisetteDevi
106115
"""
107116
with ExitStack() as stack:
108117
file_objs = [stack.enter_context(open_file(f, "rb")) for f in files]
109-
ani_provider = AnisetteProvider.load(*file_objs, default_device_config=default_device_config)
118+
ani_provider = AnisetteProvider.load(
119+
*file_objs,
120+
fs_fallback=lambda: _get_libs(),
121+
default_device_config=default_device_config,
122+
)
110123

111124
return cls(ani_provider)
112125

@@ -126,6 +139,8 @@ def save_provisioning(self, file: BinaryIO | str | Path) -> None:
126139
:param file: The file or path to save provisioning data to.
127140
:type file: BinaryIO, str, Path
128141
"""
142+
self.provision()
143+
129144
with open_file(file, "wb+") as f:
130145
self._ani_provider.save(f, exclude=["libs"])
131146

@@ -144,6 +159,9 @@ def save_libs(self, file: BinaryIO | str | Path) -> None:
144159
:param file: The file or path to save library data to.
145160
:type file: BinaryIO, str, Path
146161
"""
162+
# force fetch of library store to make sure it exists when saving
163+
_ = self._ani_provider.library_store
164+
147165
with open_file(file, "wb+") as f:
148166
self._ani_provider.save(f, include=["libs"])
149167

@@ -173,7 +191,7 @@ def provision(self) -> None:
173191
In most cases it is not necessary to manually use this method, since :meth:`Anisette.get_data`
174192
will call it implicitly.
175193
"""
176-
if not self._ani_provider.adi.is_machine_provisioned(self._ds_id):
194+
if not self.is_provisioned:
177195
logging.info("Provisioning...")
178196
self._ani_provider.provisioning_session.provision(self._ds_id)
179197

0 commit comments

Comments
 (0)