dissect.cim 3.10.dev3__tar.gz → 3.11.dev2__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.
- dissect_cim-3.11.dev2/.git-blame-ignore-revs +6 -0
- {dissect_cim-3.10.dev3/dissect.cim.egg-info → dissect_cim-3.11.dev2}/PKG-INFO +2 -2
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/c_cim.py +12 -3
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/cim.py +115 -90
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/classes.py +95 -83
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/index.py +57 -44
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/mappings.py +21 -17
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/objects.py +20 -12
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/utils.py +36 -30
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2/dissect.cim.egg-info}/PKG-INFO +2 -2
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect.cim.egg-info/SOURCES.txt +1 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/pyproject.toml +48 -5
- dissect_cim-3.11.dev2/tests/conftest.py +34 -0
- dissect_cim-3.11.dev2/tests/test_cim.py +10 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tox.ini +4 -10
- dissect_cim-3.10.dev3/tests/conftest.py +0 -28
- dissect_cim-3.10.dev3/tests/test_cim.py +0 -6
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/COPYRIGHT +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/LICENSE +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/MANIFEST.in +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/README.md +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/__init__.py +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect/cim/exceptions.py +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect.cim.egg-info/dependency_links.txt +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect.cim.egg-info/requires.txt +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/dissect.cim.egg-info/top_level.txt +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/setup.cfg +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/__init__.py +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/data/INDEX.BTR.gz +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/data/MAPPING1.MAP.gz +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/data/MAPPING2.MAP.gz +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/data/MAPPING3.MAP.gz +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/data/OBJECTS.DATA.gz +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/docs/Makefile +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/docs/conf.py +0 -0
- {dissect_cim-3.10.dev3 → dissect_cim-3.11.dev2}/tests/docs/index.rst +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: dissect.cim
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.11.dev2
|
|
4
4
|
Summary: A Dissect module implementing a parser for the Windows Common Information Model (CIM) database, used in the Windows operating system
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import NamedTuple, Union
|
|
2
2
|
|
|
3
3
|
from dissect.cstruct import cstruct
|
|
4
4
|
|
|
@@ -240,5 +240,14 @@ CIM_TYPES_MAP = {
|
|
|
240
240
|
CIM_TYPES.CHAR16: c_cim.wchar,
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
CimType = Union[int, float, str, bool]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class ClassDefinitionPropertyState(NamedTuple):
|
|
247
|
+
is_inherited: bool
|
|
248
|
+
has_default_value: bool
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class ClassInstancePropertyState(NamedTuple):
|
|
252
|
+
use_default_value: bool
|
|
253
|
+
is_initialized: bool
|
|
@@ -3,28 +3,45 @@
|
|
|
3
3
|
# Information about e.g. data structures can also be found in:
|
|
4
4
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wmio/b44d0581-5bd3-40fc-95d7-01c1b1239820
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from functools import cached_property
|
|
7
9
|
from io import BytesIO
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING, Any, BinaryIO
|
|
8
12
|
|
|
9
13
|
from dissect.cim.c_cim import (
|
|
10
14
|
ARRAY_STATES,
|
|
11
15
|
NAMESPACE_CLASS_NAME,
|
|
12
16
|
ROOT_NAMESPACE_NAME,
|
|
13
17
|
SYSTEM_NAMESPACE_NAME,
|
|
18
|
+
CimType,
|
|
14
19
|
c_cim,
|
|
15
20
|
)
|
|
16
|
-
from dissect.cim.classes import
|
|
21
|
+
from dissect.cim.classes import (
|
|
22
|
+
ClassDefinition,
|
|
23
|
+
ClassDefinitionProperty,
|
|
24
|
+
ClassInstance,
|
|
25
|
+
InstanceKey,
|
|
26
|
+
PropertyDefaultValues,
|
|
27
|
+
Qualifier,
|
|
28
|
+
)
|
|
17
29
|
from dissect.cim.exceptions import Error, InvalidDatabaseError
|
|
18
30
|
from dissect.cim.index import Index, Key
|
|
19
31
|
from dissect.cim.mappings import Mapping
|
|
20
32
|
from dissect.cim.objects import Objects
|
|
21
33
|
from dissect.cim.utils import find_current_mapping, parse_object_path
|
|
22
34
|
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from collections.abc import Iterator
|
|
37
|
+
|
|
38
|
+
from dissect.cstruct import BaseType
|
|
39
|
+
|
|
23
40
|
|
|
24
41
|
class CIM:
|
|
25
|
-
"""Common Information Model"""
|
|
42
|
+
"""Common Information Model."""
|
|
26
43
|
|
|
27
|
-
def __init__(self, index, objects, mappings):
|
|
44
|
+
def __init__(self, index: BinaryIO, objects: BinaryIO, mappings: list[BinaryIO]):
|
|
28
45
|
self._findex = index
|
|
29
46
|
self._fobjects = objects
|
|
30
47
|
self._fmappings = mappings
|
|
@@ -62,18 +79,24 @@ class CIM:
|
|
|
62
79
|
self.system = self.namespace(SYSTEM_NAMESPACE_NAME)
|
|
63
80
|
|
|
64
81
|
@classmethod
|
|
65
|
-
def from_directory(cls, path):
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
def from_directory(cls, path: Path | str) -> CIM:
|
|
83
|
+
if not isinstance(path, Path):
|
|
84
|
+
path = Path(path)
|
|
85
|
+
|
|
86
|
+
path = path.resolve()
|
|
87
|
+
if not path.is_dir():
|
|
88
|
+
raise ValueError(f"Path {path} is not a directory")
|
|
89
|
+
|
|
90
|
+
findex = path.joinpath("INDEX.BTR").open("rb")
|
|
91
|
+
fobjects = path.joinpath("OBJECTS.DATA").open("rb")
|
|
92
|
+
fmappings = [path.joinpath(f"MAPPING{i}.MAP").open("rb") for i in range(1, 4)]
|
|
70
93
|
|
|
71
94
|
return cls(findex, fobjects, fmappings)
|
|
72
95
|
|
|
73
|
-
def key(self, *args):
|
|
96
|
+
def key(self, *args) -> Key:
|
|
74
97
|
return Key(self, *args)
|
|
75
98
|
|
|
76
|
-
def query(self, path, ns=None):
|
|
99
|
+
def query(self, path: str, ns: Namespace | None = None) -> Namespace | Class | Instance:
|
|
77
100
|
if ns is not None and not isinstance(ns, Namespace):
|
|
78
101
|
raise TypeError("namespace should be an instance of Namespace")
|
|
79
102
|
object_path = parse_object_path(path, ns)
|
|
@@ -94,48 +117,48 @@ class CIM:
|
|
|
94
117
|
|
|
95
118
|
return obj
|
|
96
119
|
|
|
97
|
-
def namespace(self, name):
|
|
120
|
+
def namespace(self, name: str) -> Namespace:
|
|
98
121
|
return Namespace(self, name)
|
|
99
122
|
|
|
100
|
-
def _parse_instance(self, class_, buf):
|
|
123
|
+
def _parse_instance(self, class_: Class, buf: BytesIO) -> ClassInstance:
|
|
101
124
|
return ClassInstance(self, class_, buf)
|
|
102
125
|
|
|
103
|
-
def get_class_definition(self, q):
|
|
126
|
+
def get_class_definition(self, q: Key) -> ClassDefinition:
|
|
104
127
|
if not q.reference():
|
|
105
128
|
q = self.key().NS(SYSTEM_NAMESPACE_NAME).CD(q["CD"])
|
|
106
129
|
return ClassDefinition(self, q.object())
|
|
107
130
|
|
|
108
|
-
def get_class_instance(self, class_, q):
|
|
131
|
+
def get_class_instance(self, class_: Class, q: Key) -> ClassInstance:
|
|
109
132
|
return self._parse_instance(class_, q.object())
|
|
110
133
|
|
|
111
134
|
|
|
112
135
|
class Namespace:
|
|
113
|
-
def __init__(self, cim, name, class_instance=None):
|
|
136
|
+
def __init__(self, cim: CIM, name: str, class_instance: ClassInstance | None = None):
|
|
114
137
|
self.cim = cim
|
|
115
138
|
self.name = name
|
|
116
139
|
self.class_instance = class_instance
|
|
117
140
|
|
|
118
|
-
def __repr__(self):
|
|
141
|
+
def __repr__(self) -> str:
|
|
119
142
|
return f"<Namespace {self.name}>"
|
|
120
143
|
|
|
121
|
-
def query(self, path):
|
|
144
|
+
def query(self, path: str) -> Namespace | Class | Instance:
|
|
122
145
|
return self.cim.query(path, self)
|
|
123
146
|
|
|
124
147
|
@property
|
|
125
|
-
def ci(self):
|
|
148
|
+
def ci(self) -> ClassInstance | None:
|
|
126
149
|
return self.class_instance
|
|
127
150
|
|
|
128
|
-
def parent(self):
|
|
129
|
-
raise NotImplementedError
|
|
151
|
+
def parent(self) -> Namespace:
|
|
152
|
+
raise NotImplementedError
|
|
130
153
|
|
|
131
|
-
def class_(self, class_name):
|
|
154
|
+
def class_(self, class_name: str) -> Class:
|
|
132
155
|
q = self.cim.key().NS(self.name).CD(class_name)
|
|
133
156
|
class_def = self.cim.get_class_definition(q)
|
|
134
157
|
|
|
135
158
|
return Class(self.cim, self, class_def)
|
|
136
159
|
|
|
137
160
|
@property
|
|
138
|
-
def classes(self):
|
|
161
|
+
def classes(self) -> Iterator[Class]:
|
|
139
162
|
yielded = set()
|
|
140
163
|
|
|
141
164
|
if self.name != SYSTEM_NAMESPACE_NAME:
|
|
@@ -154,15 +177,16 @@ class Namespace:
|
|
|
154
177
|
yield class_
|
|
155
178
|
yielded.add(class_.name)
|
|
156
179
|
|
|
157
|
-
def namespace(self, name):
|
|
158
|
-
main_name = "
|
|
159
|
-
for
|
|
160
|
-
if
|
|
161
|
-
return
|
|
162
|
-
|
|
180
|
+
def namespace(self, name: str) -> Namespace:
|
|
181
|
+
main_name = f"{self.name}\\{name}".lower()
|
|
182
|
+
for ns in self.namespaces:
|
|
183
|
+
if ns.name.lower() == main_name:
|
|
184
|
+
return ns
|
|
185
|
+
|
|
186
|
+
raise IndexError
|
|
163
187
|
|
|
164
188
|
@property
|
|
165
|
-
def namespaces(self):
|
|
189
|
+
def namespaces(self) -> Iterator[Namespace]:
|
|
166
190
|
yielded = set()
|
|
167
191
|
|
|
168
192
|
q = self.cim.key().NS(self.name).CI(NAMESPACE_CLASS_NAME).IL()
|
|
@@ -170,7 +194,7 @@ class Namespace:
|
|
|
170
194
|
|
|
171
195
|
for ref in q.references():
|
|
172
196
|
class_instance = self.cim.get_class_instance(class_def, ref)
|
|
173
|
-
ns = Namespace(self.cim, "
|
|
197
|
+
ns = Namespace(self.cim, f"{self.name}\\{class_instance.properties['Name'].value}", class_instance)
|
|
174
198
|
|
|
175
199
|
if ns.name not in yielded:
|
|
176
200
|
yield ns
|
|
@@ -181,36 +205,34 @@ class Namespace:
|
|
|
181
205
|
|
|
182
206
|
|
|
183
207
|
class Class:
|
|
184
|
-
def __init__(self, cim, namespace, class_definition):
|
|
208
|
+
def __init__(self, cim: CIM, namespace: Namespace, class_definition: ClassDefinition):
|
|
185
209
|
self.cim = cim
|
|
186
210
|
self.namespace = namespace
|
|
187
211
|
self.class_definition = class_definition
|
|
188
212
|
|
|
189
213
|
self._properties = None
|
|
190
214
|
|
|
191
|
-
def __getattr__(self, attr):
|
|
215
|
+
def __getattr__(self, attr: str) -> Any:
|
|
192
216
|
try:
|
|
193
217
|
return getattr(self.class_definition, attr)
|
|
194
218
|
except AttributeError:
|
|
195
219
|
return object.__getattribute__(self, attr)
|
|
196
220
|
|
|
197
221
|
@property
|
|
198
|
-
def name(self):
|
|
222
|
+
def name(self) -> str:
|
|
199
223
|
return self.class_definition.class_name
|
|
200
224
|
|
|
201
225
|
@property
|
|
202
|
-
def ns(self):
|
|
226
|
+
def ns(self) -> Namespace:
|
|
203
227
|
return self.namespace
|
|
204
228
|
|
|
205
229
|
@property
|
|
206
|
-
def cd(self):
|
|
230
|
+
def cd(self) -> ClassDefinition:
|
|
207
231
|
return self.class_definition
|
|
208
232
|
|
|
209
233
|
@property
|
|
210
|
-
def derivation(self):
|
|
211
|
-
"""
|
|
212
|
-
list from root to leaf of class layouts
|
|
213
|
-
"""
|
|
234
|
+
def derivation(self) -> list[Class]:
|
|
235
|
+
"""List from root to leaf of class layouts."""
|
|
214
236
|
derivation = []
|
|
215
237
|
|
|
216
238
|
class_ = self
|
|
@@ -225,24 +247,22 @@ class Class:
|
|
|
225
247
|
derivation.reverse()
|
|
226
248
|
return derivation
|
|
227
249
|
|
|
228
|
-
@
|
|
229
|
-
def properties(self):
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
for
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
self._properties = props
|
|
236
|
-
return self._properties
|
|
250
|
+
@cached_property
|
|
251
|
+
def properties(self) -> dict[str, Property]:
|
|
252
|
+
props = {}
|
|
253
|
+
for class_ in self.derivation:
|
|
254
|
+
for prop in class_.class_definition.properties.values():
|
|
255
|
+
props[prop.name] = Property(self, prop)
|
|
256
|
+
return props
|
|
237
257
|
|
|
238
258
|
@property
|
|
239
|
-
def property_default_values(self):
|
|
259
|
+
def property_default_values(self) -> PropertyDefaultValues:
|
|
240
260
|
props = self.properties.values()
|
|
241
261
|
props = sorted(props, key=lambda p: p.index)
|
|
242
262
|
return PropertyDefaultValues(BytesIO(self.class_definition.default_values_data), props)
|
|
243
263
|
|
|
244
264
|
@property
|
|
245
|
-
def properties_length(self):
|
|
265
|
+
def properties_length(self) -> int:
|
|
246
266
|
off = 0
|
|
247
267
|
for prop in self.properties.values():
|
|
248
268
|
if prop.type.array_state == ARRAY_STATES.ARRAY:
|
|
@@ -251,15 +271,15 @@ class Class:
|
|
|
251
271
|
off += len(prop.ctype)
|
|
252
272
|
return off
|
|
253
273
|
|
|
254
|
-
def instance(self, key):
|
|
274
|
+
def instance(self, key: str) -> Instance:
|
|
255
275
|
for instance in self.instances:
|
|
256
276
|
if instance.key == key:
|
|
257
277
|
return instance
|
|
258
278
|
|
|
259
|
-
raise IndexError
|
|
279
|
+
raise IndexError
|
|
260
280
|
|
|
261
281
|
@property
|
|
262
|
-
def instances(self):
|
|
282
|
+
def instances(self) -> Iterator[Instance]:
|
|
263
283
|
yielded = set()
|
|
264
284
|
|
|
265
285
|
q = self.cim.key().NS(self.namespace.name).CI(self.name).IL()
|
|
@@ -275,79 +295,83 @@ class Class:
|
|
|
275
295
|
|
|
276
296
|
|
|
277
297
|
class Instance:
|
|
278
|
-
def __init__(self, cim, namespace, class_, class_instance):
|
|
298
|
+
def __init__(self, cim: CIM, namespace: Namespace, class_: Class, class_instance: ClassInstance):
|
|
279
299
|
self.cim = cim
|
|
280
300
|
self.namespace = namespace
|
|
281
301
|
self.class_ = class_
|
|
282
302
|
self.class_definition = class_.class_definition
|
|
283
303
|
self.class_instance = class_instance
|
|
284
304
|
|
|
285
|
-
def __getattr__(self, attr):
|
|
305
|
+
def __getattr__(self, attr: str) -> Any:
|
|
286
306
|
try:
|
|
287
307
|
return getattr(self.class_instance, attr)
|
|
288
308
|
except AttributeError:
|
|
289
309
|
return object.__getattribute__(self, attr)
|
|
290
310
|
|
|
291
311
|
@property
|
|
292
|
-
def
|
|
312
|
+
def key(self) -> InstanceKey:
|
|
313
|
+
return self.class_instance.key
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def name(self) -> str:
|
|
293
317
|
return self.class_instance.class_name
|
|
294
318
|
|
|
295
319
|
@property
|
|
296
|
-
def ns(self):
|
|
320
|
+
def ns(self) -> Namespace:
|
|
297
321
|
return self.namespace
|
|
298
322
|
|
|
299
323
|
@property
|
|
300
|
-
def cd(self):
|
|
324
|
+
def cd(self) -> ClassDefinition:
|
|
301
325
|
return self.class_definition
|
|
302
326
|
|
|
303
327
|
@property
|
|
304
|
-
def ci(self):
|
|
328
|
+
def ci(self) -> ClassInstance:
|
|
305
329
|
return self.class_instance
|
|
306
330
|
|
|
307
331
|
|
|
308
332
|
class Property:
|
|
309
|
-
def __init__(self, class_, prop):
|
|
333
|
+
def __init__(self, class_: Class, prop: ClassDefinitionProperty):
|
|
310
334
|
self.class_ = class_
|
|
311
335
|
self._prop = prop
|
|
312
336
|
|
|
313
337
|
@property
|
|
314
|
-
def type(self):
|
|
338
|
+
def type(self) -> c_cim.cim_type:
|
|
315
339
|
return self._prop.type
|
|
316
340
|
|
|
317
341
|
@property
|
|
318
|
-
def ctype(self):
|
|
342
|
+
def ctype(self) -> BaseType:
|
|
319
343
|
return self._prop.ctype
|
|
320
344
|
|
|
321
345
|
@property
|
|
322
|
-
def qualifiers(self):
|
|
346
|
+
def qualifiers(self) -> dict[str, Qualifier]:
|
|
323
347
|
return self._prop.qualifiers
|
|
324
348
|
|
|
325
349
|
@property
|
|
326
|
-
def name(self):
|
|
350
|
+
def name(self) -> str:
|
|
327
351
|
return self._prop.name
|
|
328
352
|
|
|
329
353
|
@property
|
|
330
|
-
def index(self):
|
|
354
|
+
def index(self) -> int:
|
|
331
355
|
return self._prop.index
|
|
332
356
|
|
|
333
357
|
@property
|
|
334
|
-
def offset(self):
|
|
358
|
+
def offset(self) -> int:
|
|
335
359
|
return self._prop.offset
|
|
336
360
|
|
|
337
361
|
@property
|
|
338
|
-
def level(self):
|
|
362
|
+
def level(self) -> int:
|
|
339
363
|
return self._prop.level
|
|
340
364
|
|
|
341
365
|
@property
|
|
342
|
-
def is_inherited(self):
|
|
366
|
+
def is_inherited(self) -> bool:
|
|
343
367
|
return self.class_.property_default_values.state[self.index].is_inherited
|
|
344
368
|
|
|
345
369
|
@property
|
|
346
|
-
def has_default_value(self):
|
|
370
|
+
def has_default_value(self) -> bool:
|
|
347
371
|
return self.class_.property_default_values.state[self.index].has_default_value
|
|
348
372
|
|
|
349
373
|
@property
|
|
350
|
-
def default_value(self):
|
|
374
|
+
def default_value(self) -> CimType | list[CimType]:
|
|
351
375
|
if not self.has_default_value:
|
|
352
376
|
raise ValueError("Property has no default value!")
|
|
353
377
|
|
|
@@ -355,22 +379,23 @@ class Property:
|
|
|
355
379
|
# then the data is stored nicely in the CD prop data section
|
|
356
380
|
v = self.class_.property_default_values.default_values[self.index]
|
|
357
381
|
return self.class_.class_definition.property_data.get_value(v, self.type)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
382
|
+
|
|
383
|
+
# we have to walk up the derivation path looking for the default value
|
|
384
|
+
rderivation = self.class_.derivation[:]
|
|
385
|
+
rderivation.reverse()
|
|
386
|
+
|
|
387
|
+
for ancestor_cl in rderivation:
|
|
388
|
+
defaults = ancestor_cl.property_default_values
|
|
389
|
+
state = defaults.state[self.index]
|
|
390
|
+
if not state.has_default_value:
|
|
391
|
+
raise Error("Property with inherited default value has bad ancestor (no default value)")
|
|
392
|
+
|
|
393
|
+
if state.is_inherited:
|
|
394
|
+
# keep trucking! look further up the ancestry tree.
|
|
395
|
+
continue
|
|
396
|
+
|
|
397
|
+
# else, this must be where the default value is defined
|
|
398
|
+
v = defaults.default_values[self.index]
|
|
399
|
+
return ancestor_cl.class_definition.property_data.get_value(v, self.type)
|
|
400
|
+
|
|
401
|
+
raise Error("Unable to find ancestor class with default value")
|