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,135 @@
|
|
|
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 typing import Protocol
|
|
18
|
+
|
|
19
|
+
from ansys.bdm.api.iasync_storage_scope import (
|
|
20
|
+
IAsyncReadStorageScope,
|
|
21
|
+
IAsyncStorageScope,
|
|
22
|
+
)
|
|
23
|
+
from ansys.bdm.api.istorage_scope import IReadStorageScope, IStorageScope
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class IReadStorageScopeFactory(Protocol):
|
|
27
|
+
"""
|
|
28
|
+
Factory pattern for creating instances of IReadStorageScope and IAsyncReadStorageScope.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def create_storage_scope(
|
|
32
|
+
self,
|
|
33
|
+
context: str,
|
|
34
|
+
template_vars: dict[str, str],
|
|
35
|
+
) -> IReadStorageScope:
|
|
36
|
+
"""
|
|
37
|
+
Create a new read only storage scope.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
context: str
|
|
42
|
+
TODO: How to word what this genericaly does.
|
|
43
|
+
template_vars: Dict[str, str]
|
|
44
|
+
Template variables that may be used by the configuration system \
|
|
45
|
+
to return an appropriate :class:`IStorageScope`. TODO: Define whether certain fields \
|
|
46
|
+
need to be passed in?
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
IReadStorageScope
|
|
51
|
+
A configured and ready to use :class:`IReadStorageScope`
|
|
52
|
+
"""
|
|
53
|
+
# deliberately not implemented
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
async def create_async_storage_scope(
|
|
57
|
+
self,
|
|
58
|
+
context: str,
|
|
59
|
+
template_vars: dict[str, str],
|
|
60
|
+
) -> IAsyncReadStorageScope:
|
|
61
|
+
"""
|
|
62
|
+
Create a new asynchronous read only storage scope.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
context: str
|
|
67
|
+
TODO: How to word what this genericaly does.
|
|
68
|
+
template_vars: Dict[str, str]
|
|
69
|
+
Template variables that may be used by the configuration system \
|
|
70
|
+
to return an appropriate :class:`IAsyncStorageScope`. TODO: Define whether certain fields \
|
|
71
|
+
need to be passed in?
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
IAsyncReadStorageScope
|
|
76
|
+
A configured and ready to use :class:`IAsyncReadStorageScope`
|
|
77
|
+
"""
|
|
78
|
+
# deliberately not implemented
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class IStorageScopeFactory(IReadStorageScopeFactory, Protocol):
|
|
83
|
+
"""
|
|
84
|
+
Factory pattern for creating instances of IStorageScope and IAsyncStorageScope.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def create_storage_scope(
|
|
88
|
+
self,
|
|
89
|
+
context: str,
|
|
90
|
+
template_vars: dict[str, str],
|
|
91
|
+
) -> IStorageScope:
|
|
92
|
+
"""
|
|
93
|
+
Create a new storage scope.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
context: str
|
|
98
|
+
TODO: How to word what this genericaly does.
|
|
99
|
+
template_vars: Dict[str, str]
|
|
100
|
+
Template variables that may be used by the configuration system \
|
|
101
|
+
to return an appropriate :class:`IStorageScope`. TODO: Define whether certain fields \
|
|
102
|
+
need to be passed in?
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
IStorageScope
|
|
107
|
+
A configured and ready to use :class:`IStorageScope`
|
|
108
|
+
"""
|
|
109
|
+
# deliberately not implemented
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
async def create_async_storage_scope(
|
|
113
|
+
self,
|
|
114
|
+
context: str,
|
|
115
|
+
template_vars: dict[str, str],
|
|
116
|
+
) -> IAsyncStorageScope:
|
|
117
|
+
"""
|
|
118
|
+
Create a new asynchronous storage scope.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
context: str
|
|
123
|
+
TODO: How to word what this genericaly does.
|
|
124
|
+
template_vars: Dict[str, str]
|
|
125
|
+
Template variables that may be used by the configuration system \
|
|
126
|
+
to return an appropriate :class:`IAsyncStorageScope`. TODO: Define whether certain fields \
|
|
127
|
+
need to be passed in?
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
IAsyncStorageScope
|
|
132
|
+
A configured and ready to use :class:`IAsyncStorageScope`
|
|
133
|
+
"""
|
|
134
|
+
# deliberately not implemented
|
|
135
|
+
...
|
ansys/bdm/api/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
import re
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from pydantic import GetCoreSchemaHandler
|
|
21
|
+
from pydantic_core import CoreSchema, core_schema
|
|
22
|
+
|
|
23
|
+
from ansys.bdm.api.entity_handle import EntityHandle
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RecursiveDictionaryOfEntityHandles(
|
|
27
|
+
dict[str, "EntityHandle | RecursiveDictionaryOfEntityHandles"],
|
|
28
|
+
):
|
|
29
|
+
"""A recursive dictionary structure for storing entity handles in a nested hierarchy that maps a filesystem-like
|
|
30
|
+
directory structure."""
|
|
31
|
+
|
|
32
|
+
# Defining it as a class instead of a type, in case in the future we want to add methods that operate on the
|
|
33
|
+
# recursive dict (e.g., filter) and don't require the storage scope.
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def __get_pydantic_core_schema__(
|
|
37
|
+
cls,
|
|
38
|
+
source_type: Any,
|
|
39
|
+
handler: GetCoreSchemaHandler,
|
|
40
|
+
) -> CoreSchema:
|
|
41
|
+
"""Generate Pydantic core schema to be able to serialize/deserialize this structure with Pydantic.
|
|
42
|
+
|
|
43
|
+
We cannot use a simple:
|
|
44
|
+
> return core_schema.no_info_after_validator_function(cls, handler(dict))
|
|
45
|
+
because it would not instantiate the nested EntityHandles, leaving them as plain dictionaries.
|
|
46
|
+
"""
|
|
47
|
+
entity_handle_schema = handler.generate_schema(EntityHandle)
|
|
48
|
+
|
|
49
|
+
dict_schema = core_schema.dict_schema(
|
|
50
|
+
keys_schema=core_schema.str_schema(),
|
|
51
|
+
values_schema=core_schema.union_schema(
|
|
52
|
+
[
|
|
53
|
+
entity_handle_schema,
|
|
54
|
+
core_schema.definition_reference_schema(
|
|
55
|
+
"RecursiveDictionaryOfEntityHandles",
|
|
56
|
+
),
|
|
57
|
+
],
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
validated_schema = core_schema.no_info_after_validator_function(
|
|
62
|
+
cls,
|
|
63
|
+
dict_schema,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return core_schema.definitions_schema(
|
|
67
|
+
schema=validated_schema,
|
|
68
|
+
definitions=[
|
|
69
|
+
core_schema.dict_schema(
|
|
70
|
+
keys_schema=core_schema.str_schema(),
|
|
71
|
+
values_schema=core_schema.union_schema(
|
|
72
|
+
[
|
|
73
|
+
entity_handle_schema,
|
|
74
|
+
core_schema.definition_reference_schema(
|
|
75
|
+
"RecursiveDictionaryOfEntityHandles",
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
ref="RecursiveDictionaryOfEntityHandles",
|
|
80
|
+
),
|
|
81
|
+
],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_and_create_nested_dict(
|
|
86
|
+
parts: tuple[str, ...],
|
|
87
|
+
value: RecursiveDictionaryOfEntityHandles,
|
|
88
|
+
) -> RecursiveDictionaryOfEntityHandles:
|
|
89
|
+
if not parts:
|
|
90
|
+
return value
|
|
91
|
+
if parts[0] not in value:
|
|
92
|
+
value[parts[0]] = RecursiveDictionaryOfEntityHandles()
|
|
93
|
+
|
|
94
|
+
# Ensure the value at parts[0] is a RecursiveDictionaryOfEntityHandles
|
|
95
|
+
nested_value = value[parts[0]]
|
|
96
|
+
if not isinstance(nested_value, RecursiveDictionaryOfEntityHandles):
|
|
97
|
+
# If it's an EntityHandle, we need to convert it to a nested dict
|
|
98
|
+
# This shouldn't normally happen in correct usage, but we handle it for type safety
|
|
99
|
+
nested_value = RecursiveDictionaryOfEntityHandles()
|
|
100
|
+
value[parts[0]] = nested_value
|
|
101
|
+
|
|
102
|
+
return get_and_create_nested_dict(
|
|
103
|
+
parts[1:],
|
|
104
|
+
nested_value,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def validate_path_component(name: str) -> None:
|
|
109
|
+
"""Validate that a string is suitable for use as a file or directory name."""
|
|
110
|
+
# Would be great if we could validate at assign or construction time, e.g.,:
|
|
111
|
+
# > my_dict['invalid/string'] = my_entity
|
|
112
|
+
# or
|
|
113
|
+
# > my_dict = RecursiveDictionaryOfEntityHandles({'invalid/string': my_entity})
|
|
114
|
+
# but given the definition of RecursiveDictionaryOfEntityHandles, we are left with validation only within
|
|
115
|
+
# get_copy_from_dictionary()
|
|
116
|
+
|
|
117
|
+
if not name or not name.strip():
|
|
118
|
+
raise ValueError("Path component cannot be empty")
|
|
119
|
+
if name in (".", ".."): # special names for current and parent directory
|
|
120
|
+
raise ValueError(f"Path component cannot be '{name}'")
|
|
121
|
+
if name.endswith((".", " ")): # Windows rule
|
|
122
|
+
raise ValueError(f"Path component '{name}' cannot end with a dot or space")
|
|
123
|
+
|
|
124
|
+
# Check for invalid characters
|
|
125
|
+
# - Windows: < > : " / \\ | ? * and control characters (0-31)
|
|
126
|
+
# - Linux: / and null character
|
|
127
|
+
invalid_chars = r'[<>:"/\\|?*\x00-\x1f]'
|
|
128
|
+
if re.search(invalid_chars, name):
|
|
129
|
+
raise ValueError(
|
|
130
|
+
f"Path component '{name}' contains invalid characters. "
|
|
131
|
+
f'The following characters are not allowed: < > : " / \\ | ? * and control characters',
|
|
132
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
|
|
18
|
+
class NotFoundInLocalStorageRootError(Exception):
|
|
19
|
+
"""Exception raised when attempting to access object that doesn't exist within the local storage root."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EntityNotFoundInBlobStorageError(Exception):
|
|
23
|
+
"""Exception raised when attempting to access entity using handle where the referenced entity does not exist."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CannotGenerateStreamForDirectoryError(Exception):
|
|
27
|
+
"""Exception raised when attempting to generate a stream for a directory entity."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EntityWriterHasNotCompletedWritingDataError(Exception):
|
|
31
|
+
"""Exception raised when attempting to get handle from EntityWriter before the writing context is closed."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EntityWriterIsNotWritingDataError(Exception):
|
|
35
|
+
"""Exception raised when attempting to access stream from EntityWriter
|
|
36
|
+
after the writing context is closed or before it is opened."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class EntityWriterHasWrittenDataError(Exception):
|
|
40
|
+
"""Exception raised when attempting to write to stream from EntityWriter
|
|
41
|
+
after the writing context is closed."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class InvalidContextError(Exception):
|
|
45
|
+
"""Exception raised when unknown context when creating a scope."""
|
|
@@ -0,0 +1,15 @@
|
|
|
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.
|
|
@@ -0,0 +1,92 @@
|
|
|
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 anyio import EndOfStream
|
|
18
|
+
|
|
19
|
+
from ansys.bdm.api.entity_handle import EntityHandle
|
|
20
|
+
from ansys.bdm.api.iasync_storage_scope import IAsyncStorageScope
|
|
21
|
+
from ansys.bdm.base.encoder import encode_text
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AsyncStorageScopeBase(IAsyncStorageScope):
|
|
25
|
+
"""
|
|
26
|
+
A partial implementation of ``IAsyncStorageScope`` that provides implementations
|
|
27
|
+
of some methods based on other more fundamental methods. ``AsyncStorageScopeBase``
|
|
28
|
+
can be used as a base class for implementations of ``IAsyncStorageScope``.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
async def get_bytes(self, entity: EntityHandle) -> bytes:
|
|
33
|
+
"""
|
|
34
|
+
Return the content of the referenced blob.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
entity: EntityHandle
|
|
39
|
+
The handle to the data to realize
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
bytes
|
|
44
|
+
the contents of the EntityHandle.
|
|
45
|
+
|
|
46
|
+
Raises
|
|
47
|
+
------
|
|
48
|
+
|
|
49
|
+
CannotGenerateStreamForDirectoryError
|
|
50
|
+
If the entity requested is a collection
|
|
51
|
+
"""
|
|
52
|
+
stream = await self.get_stream(entity)
|
|
53
|
+
result = bytearray()
|
|
54
|
+
while True:
|
|
55
|
+
try:
|
|
56
|
+
data = await stream.receive()
|
|
57
|
+
result.extend(data)
|
|
58
|
+
except EndOfStream:
|
|
59
|
+
break
|
|
60
|
+
return bytes(result)
|
|
61
|
+
|
|
62
|
+
async def get_text(self, entity: EntityHandle, encoding: str | None = None) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Return the content of the referenced blob.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
entity: EntityHandle
|
|
69
|
+
The handle to the data to realize
|
|
70
|
+
encoding: Optional[str]
|
|
71
|
+
The name of an encoding. When this argument is not None the bytes
|
|
72
|
+
of ``entity`` will be read using that encoding.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
str
|
|
77
|
+
the contents of the blob referenced by ``entity`` in text form with the encoding determined
|
|
78
|
+
in order of preference:
|
|
79
|
+
- the encoding argument if not None
|
|
80
|
+
- the encoding field of the entity argument if set
|
|
81
|
+
- the BOM of the referenced entity if it contains one; or
|
|
82
|
+
- UTF-8
|
|
83
|
+
|
|
84
|
+
Raises
|
|
85
|
+
------
|
|
86
|
+
|
|
87
|
+
CannotGenerateStreamForDirectoryError
|
|
88
|
+
If the entity requested is a collection
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
data = await self.get_bytes(entity)
|
|
92
|
+
return encode_text(data, entity, encoding)
|
|
@@ -0,0 +1,80 @@
|
|
|
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 ansys.bdm.api.entity_handle import EntityHandle
|
|
18
|
+
from ansys.bdm.api.istorage_scope import IStorageScope
|
|
19
|
+
from ansys.bdm.base.encoder import encode_text
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StorageScopeBase(IStorageScope):
|
|
23
|
+
"""
|
|
24
|
+
A partial implementation of ``IStorageScope`` that provides implementations
|
|
25
|
+
of some methods based on other more fundamental methods. ``StorageScopeBase``
|
|
26
|
+
can be used as a base class for implementations of ``IStorageScope``.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def get_bytes(self, entity: EntityHandle) -> bytes:
|
|
31
|
+
"""
|
|
32
|
+
Return the content of the referenced blob.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
entity: EntityHandle
|
|
37
|
+
The handle to the data to realize
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
bytes
|
|
42
|
+
the contents of the EntityHandle.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
|
|
47
|
+
CannotGenerateStreamForDirectoryError
|
|
48
|
+
If the entity requested is a collection
|
|
49
|
+
"""
|
|
50
|
+
return self.get_stream(entity).readall()
|
|
51
|
+
|
|
52
|
+
def get_text(self, entity: EntityHandle, encoding: str | None = None) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Return the content of the referenced blob.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
entity: EntityHandle
|
|
59
|
+
The handle to the data to realize
|
|
60
|
+
encoding: Optional[str]
|
|
61
|
+
The name of an encoding. When this argument is not None the bytes
|
|
62
|
+
of ``entity`` will be read using that encoding.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
str
|
|
67
|
+
the contents of the EntityHandle in text form with the encoding determined
|
|
68
|
+
in order of preference:
|
|
69
|
+
- the encoding argument if not None
|
|
70
|
+
- the encoding field of the entity argument if set
|
|
71
|
+
- the BOM of the referenced entity if it contains one; or
|
|
72
|
+
- UTF-8
|
|
73
|
+
|
|
74
|
+
Raises
|
|
75
|
+
------
|
|
76
|
+
|
|
77
|
+
CannotGenerateStreamForDirectoryError
|
|
78
|
+
If the entity requested is a collection
|
|
79
|
+
"""
|
|
80
|
+
return encode_text(self.get_bytes(entity), entity, encoding)
|
|
@@ -0,0 +1,89 @@
|
|
|
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 ansys.bdm.api.entity_handle import EntityHandle
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def decode_bom(bom: bytes) -> tuple[str | None, int]:
|
|
21
|
+
"""
|
|
22
|
+
Return a tuple consisting of
|
|
23
|
+
1) the name of the encoding stored in the BOM prefix of the given byte data; and
|
|
24
|
+
2) the length of BOM in bytes.
|
|
25
|
+
|
|
26
|
+
If the byte data does not contain a BOM then a tuple ``(None, 0)`` is returned.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
bom: bytes
|
|
31
|
+
byte data that may or may not be prefixed with a BOM
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
Tuple[Optional[str], int]
|
|
36
|
+
a tuple consisting of
|
|
37
|
+
1) the name of the encoding stored in the BOM prefix of the given byte data; and
|
|
38
|
+
2) the length of BOM in bytes.
|
|
39
|
+
|
|
40
|
+
If the byte data does not contain a BOM then a tuple ``(None, 0)`` is returned.
|
|
41
|
+
"""
|
|
42
|
+
if bom[0] == 0xEF and bom[1] == 0xBB and bom[2] == 0xBF:
|
|
43
|
+
return ("utf-8", 3)
|
|
44
|
+
if bom[0] == 0xFF and bom[1] == 0xFE and bom[2] == 0 and bom[3] == 0:
|
|
45
|
+
return ("utf-32-le", 4)
|
|
46
|
+
if bom[0] == 0xFF and bom[1] == 0xFE:
|
|
47
|
+
return ("utf-16-le", 2)
|
|
48
|
+
if bom[0] == 0xFE and bom[1] == 0xFF:
|
|
49
|
+
return ("utf-16-be", 2)
|
|
50
|
+
if bom[0] == 0 and bom[1] == 0 and bom[2] == 0xFE and bom[3] == 0xFF:
|
|
51
|
+
return ("utf-32-be", 4)
|
|
52
|
+
return (None, 0)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def encode_text(data: bytes, handle: EntityHandle, encoding: str | None = None) -> str:
|
|
56
|
+
"""
|
|
57
|
+
Return a string which is derived from the given byte data
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
data: bytes
|
|
62
|
+
byte data that may or may not be prefixed with a BOM that provides the data for the returned string
|
|
63
|
+
handle: EntityHandle
|
|
64
|
+
the entity handle that refers to the object that contains the given data
|
|
65
|
+
encoding: Optional[str]
|
|
66
|
+
The name of an encoding. When this argument is not None the returned
|
|
67
|
+
string will be decoded using the given encoding.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
str
|
|
72
|
+
a string containing the byte data with the encoding determined
|
|
73
|
+
in order of preference by:
|
|
74
|
+
- the encoding argument if not None
|
|
75
|
+
- the encoding field of the entity argument if set
|
|
76
|
+
- the BOM of the referenced entity if it contains one; or
|
|
77
|
+
- UTF-8
|
|
78
|
+
"""
|
|
79
|
+
if encoding is None:
|
|
80
|
+
if handle.encoding is not None:
|
|
81
|
+
encoding = handle.encoding
|
|
82
|
+
else:
|
|
83
|
+
encoding, bom_length = decode_bom(data)
|
|
84
|
+
|
|
85
|
+
if encoding is not None:
|
|
86
|
+
data = data[bom_length:]
|
|
87
|
+
else:
|
|
88
|
+
encoding = "utf-8"
|
|
89
|
+
return str(data, encoding=encoding)
|
ansys/bdm/base/py.typed
ADDED
|
File without changes
|