ansys-bdm-api 0.5.dev0__py3-none-any.whl
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.
- ansys/bdm/api/__init__.py +62 -0
- ansys/bdm/api/entity_handle.py +98 -0
- ansys/bdm/api/iasync_entity_writer.py +71 -0
- ansys/bdm/api/iasync_storage_scope.py +605 -0
- ansys/bdm/api/ientity_writer.py +70 -0
- ansys/bdm/api/istorage_scope.py +602 -0
- ansys/bdm/api/istorage_scope_factory.py +135 -0
- ansys/bdm/api/py.typed +0 -0
- ansys/bdm/api/recursive_dictionary.py +132 -0
- ansys/bdm/api/storage_exceptions.py +45 -0
- ansys/bdm/base/__init__.py +15 -0
- ansys/bdm/base/base_async_storage_scope.py +92 -0
- ansys/bdm/base/base_storage_scope.py +80 -0
- ansys/bdm/base/encoder.py +89 -0
- ansys/bdm/base/py.typed +0 -0
- ansys_bdm_api-0.5.dev0.dist-info/METADATA +171 -0
- ansys_bdm_api-0.5.dev0.dist-info/RECORD +20 -0
- ansys_bdm_api-0.5.dev0.dist-info/WHEEL +4 -0
- ansys_bdm_api-0.5.dev0.dist-info/licenses/AUTHORS +12 -0
- ansys_bdm_api-0.5.dev0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
# Copyright (C) 2026 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
from io import BufferedIOBase, RawIOBase
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import shutil
|
|
20
|
+
from types import TracebackType
|
|
21
|
+
from typing import TYPE_CHECKING, Protocol
|
|
22
|
+
|
|
23
|
+
from ansys.bdm.api.entity_handle import EntityHandle
|
|
24
|
+
from ansys.bdm.api.ientity_writer import IEntityWriter
|
|
25
|
+
from ansys.bdm.api.recursive_dictionary import (
|
|
26
|
+
RecursiveDictionaryOfEntityHandles,
|
|
27
|
+
get_and_create_nested_dict,
|
|
28
|
+
validate_path_component,
|
|
29
|
+
)
|
|
30
|
+
from ansys.bdm.api.storage_exceptions import NotFoundInLocalStorageRootError
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from os import PathLike
|
|
34
|
+
|
|
35
|
+
import ansys.bdm.api.iasync_storage_scope
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class IReadStorageScope(Protocol):
|
|
39
|
+
"""
|
|
40
|
+
Represents access to a read only Blob Data Management system.
|
|
41
|
+
Allows consumers to produce and consume :class:`EntityHandle` instances.
|
|
42
|
+
|
|
43
|
+
This is the synchronous version of the :class:`IAsyncReadStorageScope` interface.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __enter__(self) -> "IReadStorageScope":
|
|
47
|
+
"""
|
|
48
|
+
Track the set of files that are stored centrally.
|
|
49
|
+
"""
|
|
50
|
+
# deliberately not implemented
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def __exit__(
|
|
54
|
+
self,
|
|
55
|
+
__exc_type: type[BaseException] | None, # noqa: PYI063
|
|
56
|
+
__exc_value: BaseException | None,
|
|
57
|
+
__traceback: TracebackType | None,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Remove all files from local storage that are not stored centrally.
|
|
61
|
+
"""
|
|
62
|
+
# deliberately not implemented
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
def get_cached(self, entity: EntityHandle) -> Path:
|
|
66
|
+
"""
|
|
67
|
+
Realize the data to a local filesystem if needed and returns a path to the cached or original file.
|
|
68
|
+
|
|
69
|
+
The :class:`EntityHandle` is intended to represent an immutable value. The file
|
|
70
|
+
returned by this call may point to a cached or even the original file. Callers
|
|
71
|
+
must not modify the file on disk or undefined behavior, including class 3 errors,
|
|
72
|
+
may occur. If the caller needs to modify the file, consider using
|
|
73
|
+
:func:`get_copy()`, or copying the file before modifying it.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
entity: EntityHandle
|
|
78
|
+
The handle to the data to realize
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
Path
|
|
83
|
+
The path to the contents of the EntityHandle as realized locally. The caller
|
|
84
|
+
MUST NOT modify the returned path.
|
|
85
|
+
"""
|
|
86
|
+
# deliberately not implemented
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
def get_copy(self, entity: EntityHandle, destination: Path) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Realize the data to a local filesystem by writing to a specified file.
|
|
92
|
+
|
|
93
|
+
The caller is free to modify the written file. The caller is responsible
|
|
94
|
+
for deleting the generated file. If the destination path is within the storage root
|
|
95
|
+
then the copy at that location will be deleted when the storage scope context is closed.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
entity: EntityHandle
|
|
100
|
+
The handle to the data to realize
|
|
101
|
+
destination: Path
|
|
102
|
+
The path to the file to write
|
|
103
|
+
"""
|
|
104
|
+
# deliberately not implemented
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
def get_stream(self, entity: EntityHandle) -> RawIOBase:
|
|
108
|
+
"""
|
|
109
|
+
Open the EntityHandle contents for reading as a stream.
|
|
110
|
+
|
|
111
|
+
The returned stream MUST NOT be writable. The returned stream MAY be seekable.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
entity: EntityHandle
|
|
116
|
+
The handle to the data to realize
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
RawIOBase
|
|
121
|
+
The stream which, when read, will return the contents of the EntityHandle.
|
|
122
|
+
|
|
123
|
+
Raises
|
|
124
|
+
------
|
|
125
|
+
|
|
126
|
+
CannotGenerateStreamForDirectoryError
|
|
127
|
+
If the entity requested is a collection
|
|
128
|
+
"""
|
|
129
|
+
# deliberately not implemented
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
def get_bytes(self, entity: EntityHandle) -> bytes:
|
|
133
|
+
"""
|
|
134
|
+
Return the content of the referenced blob.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
entity: EntityHandle
|
|
139
|
+
The handle to the data to realize
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
bytes
|
|
144
|
+
the contents of the EntityHandle.
|
|
145
|
+
|
|
146
|
+
Raises
|
|
147
|
+
------
|
|
148
|
+
|
|
149
|
+
CannotGenerateStreamForDirectoryError
|
|
150
|
+
If the entity requested is a collection
|
|
151
|
+
"""
|
|
152
|
+
# deliberately not implemented
|
|
153
|
+
...
|
|
154
|
+
|
|
155
|
+
def get_text(self, entity: EntityHandle, encoding: str | None = None) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Return the content of the referenced blob.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
entity: EntityHandle
|
|
162
|
+
The handle to the data to realize
|
|
163
|
+
encoding: Optional[str]
|
|
164
|
+
The name of an encoding. When this argument is not None the bytes
|
|
165
|
+
of ``entity`` will be read using that encoding.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
str
|
|
170
|
+
the contents of the EntityHandle in text form with the encoding determined
|
|
171
|
+
in order of preference by:
|
|
172
|
+
- the encoding argument if not None
|
|
173
|
+
- the encoding field of the entity argument if set
|
|
174
|
+
- the BOM of the referenced entity if it contains one; or
|
|
175
|
+
- UTF-8
|
|
176
|
+
|
|
177
|
+
Raises
|
|
178
|
+
------
|
|
179
|
+
|
|
180
|
+
CannotGenerateStreamForDirectoryError
|
|
181
|
+
If the entity requested is a collection
|
|
182
|
+
"""
|
|
183
|
+
# deliberately not implemented
|
|
184
|
+
...
|
|
185
|
+
|
|
186
|
+
def get_children(self, entity: EntityHandle) -> list[EntityHandle]:
|
|
187
|
+
"""
|
|
188
|
+
Return the entities contained in this entity.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
|
|
193
|
+
entity: EntityHandle
|
|
194
|
+
The entity to query
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
|
|
200
|
+
List[EntityHandle]
|
|
201
|
+
The list of entities that are children of the provided entity
|
|
202
|
+
|
|
203
|
+
Raises
|
|
204
|
+
------
|
|
205
|
+
|
|
206
|
+
NotADirectoryError
|
|
207
|
+
If the requested entity is not a directory
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
# deliberately not implemented
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
def get_child(self, entity: EntityHandle, child_name: str) -> EntityHandle:
|
|
214
|
+
"""
|
|
215
|
+
Return the entity contained in this entity with a given name.
|
|
216
|
+
|
|
217
|
+
Parameters
|
|
218
|
+
----------
|
|
219
|
+
|
|
220
|
+
entity: EntityHandle
|
|
221
|
+
The entity to query
|
|
222
|
+
child_name: str
|
|
223
|
+
The child to search for
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
|
|
228
|
+
EntityHandle
|
|
229
|
+
The :class:`EntityHandle` requested
|
|
230
|
+
|
|
231
|
+
Raises
|
|
232
|
+
------
|
|
233
|
+
|
|
234
|
+
NotADirectoryError
|
|
235
|
+
If the requested entity is not a directory
|
|
236
|
+
EntityNotFoundInBlobStorageError
|
|
237
|
+
If the requested entity is not found
|
|
238
|
+
"""
|
|
239
|
+
# deliberately not implemented
|
|
240
|
+
...
|
|
241
|
+
|
|
242
|
+
def get_parent(self, entity: EntityHandle) -> EntityHandle | None:
|
|
243
|
+
"""
|
|
244
|
+
Return the entity containing in this entity.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
|
|
249
|
+
entity: EntityHandle
|
|
250
|
+
The entity to query
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
|
|
255
|
+
Optional[EntityHandle]
|
|
256
|
+
The :class:`EntityHandle` requested, or None if the entity is stored at the root of its scope.
|
|
257
|
+
|
|
258
|
+
Raises
|
|
259
|
+
------
|
|
260
|
+
|
|
261
|
+
EntityNotFoundInBlobStorageError
|
|
262
|
+
If the requested entity is not found.
|
|
263
|
+
"""
|
|
264
|
+
# deliberately not implemented
|
|
265
|
+
...
|
|
266
|
+
|
|
267
|
+
def get_unreferenced_entities(
|
|
268
|
+
self,
|
|
269
|
+
context: str,
|
|
270
|
+
live_handles: list[EntityHandle],
|
|
271
|
+
) -> list[EntityHandle]:
|
|
272
|
+
"""
|
|
273
|
+
Return a list of all entities within the context that are not referenced by the given live entity handles.
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
|
|
278
|
+
context: str
|
|
279
|
+
A label for storage scopes that enables them to be categorized and configured given
|
|
280
|
+
a common system wide configuration.
|
|
281
|
+
It defines the bounds over which methods supporting garbage collection are constrained.
|
|
282
|
+
|
|
283
|
+
live_handles: list[EntityHandle]
|
|
284
|
+
A list of handles that are expected to refer to blobs.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
|
|
289
|
+
list[EntityHandle]
|
|
290
|
+
The list of all entities within the context that are not referenced by the given live entity handles.
|
|
291
|
+
|
|
292
|
+
Raises
|
|
293
|
+
------
|
|
294
|
+
|
|
295
|
+
ValueError
|
|
296
|
+
If the live_handles do not belong to the provided context.
|
|
297
|
+
"""
|
|
298
|
+
# deliberately not implemented
|
|
299
|
+
...
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def asynchronous(
|
|
303
|
+
self,
|
|
304
|
+
) -> "ansys.bdm.api.iasync_storage_scope.IAsyncReadStorageScope":
|
|
305
|
+
"""
|
|
306
|
+
Return a object with an asynchronous interface to the same scope.
|
|
307
|
+
"""
|
|
308
|
+
# deliberately not implemented
|
|
309
|
+
...
|
|
310
|
+
|
|
311
|
+
def _copy_nested_dictionary(
|
|
312
|
+
self,
|
|
313
|
+
nested_dict: RecursiveDictionaryOfEntityHandles,
|
|
314
|
+
current_path: Path,
|
|
315
|
+
glob: str | None,
|
|
316
|
+
) -> None:
|
|
317
|
+
for key, value in nested_dict.items():
|
|
318
|
+
validate_path_component(key)
|
|
319
|
+
dest_path = current_path / key
|
|
320
|
+
if isinstance(value, EntityHandle):
|
|
321
|
+
if not glob or dest_path.match(glob):
|
|
322
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
323
|
+
self.get_copy(value, dest_path)
|
|
324
|
+
else:
|
|
325
|
+
if not glob or dest_path.match(glob):
|
|
326
|
+
dest_path.mkdir(parents=True, exist_ok=True)
|
|
327
|
+
# we cannot stop iterating. even if the directory does not match glob,
|
|
328
|
+
# there may be files within it that do match glob.
|
|
329
|
+
# Don't create the directory if it doesn't match, though, to avoid orphaned directories that would need
|
|
330
|
+
# to be cleaned up later.
|
|
331
|
+
self._copy_nested_dictionary(value, dest_path, glob)
|
|
332
|
+
|
|
333
|
+
def get_copy_from_dictionary(
|
|
334
|
+
self,
|
|
335
|
+
root: "PathLike[str]",
|
|
336
|
+
source: RecursiveDictionaryOfEntityHandles,
|
|
337
|
+
glob: str | None = None,
|
|
338
|
+
) -> None:
|
|
339
|
+
"""
|
|
340
|
+
Create a recursive directory structure containing copies of entities from source.
|
|
341
|
+
|
|
342
|
+
This method creates a directory structure on the file system at root containing
|
|
343
|
+
copies of the entities in source. If glob is None, all files at or within source
|
|
344
|
+
are copied to root. Note that file and directory names used when creating the
|
|
345
|
+
directory structure at root are always taken from the keys in source, not from
|
|
346
|
+
the original_name attribute of the EntityHandle objects.
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
root : PathLike[str]
|
|
351
|
+
The root directory to create the directory structure in.
|
|
352
|
+
source : RecursiveDictionaryOfEntityHandles
|
|
353
|
+
A recursive nested dictionary of EntityHandle objects.
|
|
354
|
+
glob : str | None, optional
|
|
355
|
+
A glob pattern to filter which files and directories to copy. Behaves
|
|
356
|
+
identically to Path.match() and is interpreted as relative to root using
|
|
357
|
+
the keys in source as the path components. Default is None.
|
|
358
|
+
|
|
359
|
+
Raises
|
|
360
|
+
------
|
|
361
|
+
NotADirectoryError
|
|
362
|
+
If root points to an existing file.
|
|
363
|
+
"""
|
|
364
|
+
# deliberately not implemented
|
|
365
|
+
abs_dest_path = Path(root).resolve()
|
|
366
|
+
if abs_dest_path.is_file():
|
|
367
|
+
raise NotADirectoryError("destination path must be a directory")
|
|
368
|
+
if abs_dest_path.is_dir():
|
|
369
|
+
shutil.rmtree(abs_dest_path)
|
|
370
|
+
abs_dest_path.mkdir(parents=True)
|
|
371
|
+
# An implementation must perform matching against source not the file system. Callers expect the minimum number
|
|
372
|
+
# of copies between BDM and root. An implementation which copied the whole of source to root then deleted
|
|
373
|
+
# non-matching entities is not acceptable.
|
|
374
|
+
self._copy_nested_dictionary(source, abs_dest_path, glob)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class IStorageScope(IReadStorageScope, Protocol):
|
|
378
|
+
"""
|
|
379
|
+
Represents access to a Blob Data Management system. Allows consumers
|
|
380
|
+
to produce and consume :class:`EntityHandle` instances.
|
|
381
|
+
|
|
382
|
+
This is the synchronous version of the :class:`IAsyncStorageScope` interface.
|
|
383
|
+
|
|
384
|
+
On dispose, files within the :func:`storage_root` that have not been passed to
|
|
385
|
+
:func:`store()` will be deleted.
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
def __enter__(self) -> "IStorageScope":
|
|
389
|
+
"""
|
|
390
|
+
Track the set of files that are stored centrally.
|
|
391
|
+
"""
|
|
392
|
+
# deliberately not implemented
|
|
393
|
+
...
|
|
394
|
+
|
|
395
|
+
def store(
|
|
396
|
+
self,
|
|
397
|
+
from_: "PathLike[str]",
|
|
398
|
+
mime_type: str | None = None,
|
|
399
|
+
encoding: str | None = None,
|
|
400
|
+
) -> EntityHandle:
|
|
401
|
+
"""
|
|
402
|
+
Create an :class:`EntityHandle` from a file on disk.
|
|
403
|
+
|
|
404
|
+
Many blob handler implementations will try to optimize performance and the file contents
|
|
405
|
+
may not be immediately read from disk. To avoid class 3 type errors, the file MUST NOT be
|
|
406
|
+
externally modified after calling this method.
|
|
407
|
+
|
|
408
|
+
Parameters
|
|
409
|
+
----------
|
|
410
|
+
|
|
411
|
+
from_: PathLike[str]
|
|
412
|
+
The local file on disk which contains the contents for the generated :class:`EntityHandle`
|
|
413
|
+
mime_type: Optional[str]
|
|
414
|
+
Mime type of this file, if known. If None is passed in, the IAsyncStorageScope SHOULD
|
|
415
|
+
use file extension to determine the mime type.
|
|
416
|
+
encoding: Optional[str]
|
|
417
|
+
The Internet Assigned Numbers Authority (IANA) registered encoding name used for textual data.
|
|
418
|
+
This MAY be None if not known and SHOULD NOT be set for binary mime types.
|
|
419
|
+
|
|
420
|
+
Returns
|
|
421
|
+
-------
|
|
422
|
+
An :class:`EntityHandle` that rerpesents the contents of the read file at the moment
|
|
423
|
+
this method is invoked
|
|
424
|
+
"""
|
|
425
|
+
# deliberately not implemented
|
|
426
|
+
...
|
|
427
|
+
|
|
428
|
+
def store_stream(
|
|
429
|
+
self,
|
|
430
|
+
from_: RawIOBase | BufferedIOBase | bytes,
|
|
431
|
+
relative_location: Path | None = None,
|
|
432
|
+
mime_type: str | None = None,
|
|
433
|
+
encoding: str | None = None,
|
|
434
|
+
) -> EntityHandle:
|
|
435
|
+
"""
|
|
436
|
+
Fully reads a passed in stream and returns a handle for the given content.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
|
|
441
|
+
from_ : Union[RawIOBase, BufferedIOBase, bytes]
|
|
442
|
+
The stream or in-memory bytes from which the new entity will be created
|
|
443
|
+
relative_location : Optional[Path]
|
|
444
|
+
The nominal relative path of the entity. The path is relative to the storage_root of
|
|
445
|
+
the scope. No data is expected at this location.
|
|
446
|
+
The filename from this path will be used as the original name of the entity.
|
|
447
|
+
If this parameter is not set then the implementation will generate a unique location.
|
|
448
|
+
mime_type: Optional[str]
|
|
449
|
+
Mime type of this file, if known. If None is passed in, the IAsyncStorageScope SHOULD
|
|
450
|
+
use file extension to determine the mime type.
|
|
451
|
+
encoding: Optional[str]
|
|
452
|
+
The Internet Assigned Numbers Authority (IANA) registered encoding name used for textual data.
|
|
453
|
+
This MAY be None if not known and SHOULD NOT be set for binary mime types.
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
|
|
458
|
+
EntityHandle
|
|
459
|
+
The :class:`EntityHandle` that represents the passed in data.
|
|
460
|
+
"""
|
|
461
|
+
# deliberately not implemented
|
|
462
|
+
...
|
|
463
|
+
|
|
464
|
+
def begin_store(
|
|
465
|
+
self,
|
|
466
|
+
relative_location: Path | None = None,
|
|
467
|
+
mime_type: str | None = None,
|
|
468
|
+
encoding: str | None = None,
|
|
469
|
+
) -> IEntityWriter:
|
|
470
|
+
"""
|
|
471
|
+
Create an :class:`IEntityWriter` by writing to a stream object.
|
|
472
|
+
|
|
473
|
+
Parameters
|
|
474
|
+
----------
|
|
475
|
+
|
|
476
|
+
relative_location : Optional[Path]
|
|
477
|
+
The nominal relative path of the entity. The path is relative to the storage_root of
|
|
478
|
+
the scope. No data is expected at this location.
|
|
479
|
+
The filename from this path will be used as the original name of the entity.
|
|
480
|
+
If this parameter is not set then the implementation will generate a unique location.
|
|
481
|
+
mime_type: Optional[str]
|
|
482
|
+
Mime type of this file, if known. If None is passed in, the IAsyncStorageScope SHOULD
|
|
483
|
+
use file extension to determine the mime type.
|
|
484
|
+
encoding: Optional[str]
|
|
485
|
+
The Internet Assigned Numbers Authority (IANA) registered encoding name used for textual data.
|
|
486
|
+
This MAY be None if not known and SHOULD NOT be set for binary mime types.
|
|
487
|
+
|
|
488
|
+
Returns
|
|
489
|
+
-------
|
|
490
|
+
|
|
491
|
+
IEntityWriter
|
|
492
|
+
An object which allows you to write to a stream and retrieve the handle.
|
|
493
|
+
"""
|
|
494
|
+
# deliberately not implemented
|
|
495
|
+
...
|
|
496
|
+
|
|
497
|
+
def get_storage_root(self) -> Path:
|
|
498
|
+
"""
|
|
499
|
+
A local filesystem directory that can be used to stage files.
|
|
500
|
+
|
|
501
|
+
Since this folder is managed by the BDM implementation, there may be performance
|
|
502
|
+
benefits to storing files here.
|
|
503
|
+
|
|
504
|
+
This directory usually is lazy instantiated on the first call to this function.
|
|
505
|
+
"""
|
|
506
|
+
# deliberately not implemented
|
|
507
|
+
...
|
|
508
|
+
|
|
509
|
+
@property
|
|
510
|
+
def stored_entities(self) -> list[EntityHandle]:
|
|
511
|
+
"""
|
|
512
|
+
The set of :class:`EntityHandle` instances that have been stored via this scope.
|
|
513
|
+
"""
|
|
514
|
+
# deliberately not implemented
|
|
515
|
+
...
|
|
516
|
+
|
|
517
|
+
def destroy(self, *entities: EntityHandle) -> None:
|
|
518
|
+
"""
|
|
519
|
+
Delete the entities referenced by the handle.
|
|
520
|
+
|
|
521
|
+
As :class:`EntityHandle` instances are intended to be simple immutable structures
|
|
522
|
+
that can be passed across process and service boundaries, their lifespan must be managed
|
|
523
|
+
by an external system. This call allows the resources associated with a BlobHandle
|
|
524
|
+
to be removed.
|
|
525
|
+
|
|
526
|
+
Parameters
|
|
527
|
+
----------
|
|
528
|
+
|
|
529
|
+
*entities: EntityHandle
|
|
530
|
+
The entities to delete
|
|
531
|
+
"""
|
|
532
|
+
# deliberately not implemented
|
|
533
|
+
...
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def asynchronous(self) -> "ansys.bdm.api.iasync_storage_scope.IAsyncStorageScope":
|
|
537
|
+
"""
|
|
538
|
+
Return an object with an asynchronous interface to the same scope.
|
|
539
|
+
"""
|
|
540
|
+
# deliberately not implemented
|
|
541
|
+
...
|
|
542
|
+
|
|
543
|
+
def store_to_dictionary(
|
|
544
|
+
self,
|
|
545
|
+
root: "PathLike[str]",
|
|
546
|
+
glob: str | None = None,
|
|
547
|
+
) -> RecursiveDictionaryOfEntityHandles:
|
|
548
|
+
"""
|
|
549
|
+
Create a recursive nested dictionary of EntityHandles from a directory.
|
|
550
|
+
|
|
551
|
+
This method creates a recursive nested dictionary containing EntityHandle objects for files and nested
|
|
552
|
+
dictionaries for directories. If glob is None, all files at or within root are included in the result.
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
root : PathLike[str]
|
|
557
|
+
The root directory to store to a nested dictionary.
|
|
558
|
+
glob : str | None, optional
|
|
559
|
+
A glob pattern to filter which files and directories to include in the result. Behaves identically to
|
|
560
|
+
Path.rglob() and is interpreted as relative to root. Default is None.
|
|
561
|
+
|
|
562
|
+
Returns
|
|
563
|
+
-------
|
|
564
|
+
RecursiveDictionaryOfEntityHandles
|
|
565
|
+
A recursive nested dictionary of EntityHandle objects.
|
|
566
|
+
|
|
567
|
+
Raises
|
|
568
|
+
------
|
|
569
|
+
FileNotFoundError
|
|
570
|
+
If root does not exist.
|
|
571
|
+
NotADirectoryError
|
|
572
|
+
If root is a file.
|
|
573
|
+
NotFoundInLocalStorageRootError
|
|
574
|
+
If root is not within the storage root of storage scope.
|
|
575
|
+
"""
|
|
576
|
+
abs_root_path = Path(root).resolve()
|
|
577
|
+
|
|
578
|
+
storage_root = self.get_storage_root()
|
|
579
|
+
try:
|
|
580
|
+
abs_root_path.relative_to(storage_root)
|
|
581
|
+
except ValueError as e:
|
|
582
|
+
raise NotFoundInLocalStorageRootError(
|
|
583
|
+
f"root path is not within the storage root. storage_root={storage_root}, root={root}",
|
|
584
|
+
) from e
|
|
585
|
+
|
|
586
|
+
if not abs_root_path.exists():
|
|
587
|
+
raise FileNotFoundError(f"root path does not exist: {root}")
|
|
588
|
+
|
|
589
|
+
if abs_root_path.is_file():
|
|
590
|
+
raise NotADirectoryError(f"root path is a file: {root}")
|
|
591
|
+
|
|
592
|
+
result_dict = RecursiveDictionaryOfEntityHandles()
|
|
593
|
+
for path in abs_root_path.rglob(glob or "*"):
|
|
594
|
+
subdirs = path.parent.relative_to(abs_root_path).parts
|
|
595
|
+
subresult_dict = get_and_create_nested_dict(subdirs, result_dict)
|
|
596
|
+
if path.is_file():
|
|
597
|
+
subresult_dict[path.name] = self.store(path)
|
|
598
|
+
else:
|
|
599
|
+
# needed, otherwise empty directories are not created in _get_and_create_nested_dict
|
|
600
|
+
subresult_dict[path.name] = RecursiveDictionaryOfEntityHandles()
|
|
601
|
+
|
|
602
|
+
return result_dict
|