diff options
Diffstat (limited to 'bitbake/lib/bb/data_smart.py')
| -rw-r--r-- | bitbake/lib/bb/data_smart.py | 256 |
1 files changed, 219 insertions, 37 deletions
diff --git a/bitbake/lib/bb/data_smart.py b/bitbake/lib/bb/data_smart.py index 5fdfeee2c7..ddf98e6a2e 100644 --- a/bitbake/lib/bb/data_smart.py +++ b/bitbake/lib/bb/data_smart.py | |||
| @@ -28,7 +28,7 @@ BitBake build tools. | |||
| 28 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 28 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 29 | # Based on functions from the base bb module, Copyright 2003 Holger Schurig | 29 | # Based on functions from the base bb module, Copyright 2003 Holger Schurig |
| 30 | 30 | ||
| 31 | import copy, re | 31 | import copy, re, sys, traceback |
| 32 | from collections import MutableMapping | 32 | from collections import MutableMapping |
| 33 | import logging | 33 | import logging |
| 34 | import hashlib | 34 | import hashlib |
| @@ -43,6 +43,42 @@ __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P< | |||
| 43 | __expand_var_regexp__ = re.compile(r"\${[^{}]+}") | 43 | __expand_var_regexp__ = re.compile(r"\${[^{}]+}") |
| 44 | __expand_python_regexp__ = re.compile(r"\${@.+?}") | 44 | __expand_python_regexp__ = re.compile(r"\${@.+?}") |
| 45 | 45 | ||
| 46 | def infer_caller_details(loginfo, parent = False, varval = True): | ||
| 47 | """Save the caller the trouble of specifying everything.""" | ||
| 48 | # Save effort. | ||
| 49 | if 'ignore' in loginfo and loginfo['ignore']: | ||
| 50 | return | ||
| 51 | # If nothing was provided, mark this as possibly unneeded. | ||
| 52 | if not loginfo: | ||
| 53 | loginfo['ignore'] = True | ||
| 54 | return | ||
| 55 | # Infer caller's likely values for variable (var) and value (value), | ||
| 56 | # to reduce clutter in the rest of the code. | ||
| 57 | if varval and ('variable' not in loginfo or 'detail' not in loginfo): | ||
| 58 | try: | ||
| 59 | raise Exception | ||
| 60 | except Exception: | ||
| 61 | tb = sys.exc_info()[2] | ||
| 62 | if parent: | ||
| 63 | above = tb.tb_frame.f_back.f_back | ||
| 64 | else: | ||
| 65 | above = tb.tb_frame.f_back | ||
| 66 | lcls = above.f_locals.items() | ||
| 67 | for k, v in lcls: | ||
| 68 | if k == 'value' and 'detail' not in loginfo: | ||
| 69 | loginfo['detail'] = v | ||
| 70 | if k == 'var' and 'variable' not in loginfo: | ||
| 71 | loginfo['variable'] = v | ||
| 72 | # Infer file/line/function from traceback | ||
| 73 | if 'file' not in loginfo: | ||
| 74 | depth = 3 | ||
| 75 | if parent: | ||
| 76 | depth = 4 | ||
| 77 | file, line, func, text = traceback.extract_stack(limit = depth)[0] | ||
| 78 | loginfo['file'] = file | ||
| 79 | loginfo['line'] = line | ||
| 80 | if func not in loginfo: | ||
| 81 | loginfo['func'] = func | ||
| 46 | 82 | ||
| 47 | class VariableParse: | 83 | class VariableParse: |
| 48 | def __init__(self, varname, d, val = None): | 84 | def __init__(self, varname, d, val = None): |
| @@ -157,11 +193,80 @@ class IncludeHistory(object): | |||
| 157 | o.write("\n") | 193 | o.write("\n") |
| 158 | child.emit(o, level) | 194 | child.emit(o, level) |
| 159 | 195 | ||
| 196 | class VariableHistory(object): | ||
| 197 | def __init__(self, dataroot): | ||
| 198 | self.dataroot = dataroot | ||
| 199 | self.variables = COWDictBase.copy() | ||
| 200 | |||
| 201 | def copy(self): | ||
| 202 | new = VariableHistory(self.dataroot) | ||
| 203 | new.variables = self.variables.copy() | ||
| 204 | return new | ||
| 205 | |||
| 206 | def record(self, *kwonly, **loginfo): | ||
| 207 | if not self.dataroot._tracking: | ||
| 208 | return | ||
| 209 | if len(kwonly) > 0: | ||
| 210 | raise TypeError | ||
| 211 | infer_caller_details(loginfo, parent = True) | ||
| 212 | if 'ignore' in loginfo and loginfo['ignore']: | ||
| 213 | return | ||
| 214 | if 'op' not in loginfo or not loginfo['op']: | ||
| 215 | loginfo['op'] = 'set' | ||
| 216 | if 'detail' in loginfo: | ||
| 217 | loginfo['detail'] = str(loginfo['detail']) | ||
| 218 | if 'variable' not in loginfo or 'file' not in loginfo: | ||
| 219 | raise ValueError("record() missing variable or file.") | ||
| 220 | var = loginfo['variable'] | ||
| 221 | |||
| 222 | if var not in self.variables: | ||
| 223 | self.variables[var] = [] | ||
| 224 | self.variables[var].append(loginfo.copy()) | ||
| 225 | |||
| 226 | def variable(self, var): | ||
| 227 | if var in self.variables: | ||
| 228 | return self.variables[var] | ||
| 229 | else: | ||
| 230 | return [] | ||
| 231 | |||
| 232 | def emit(self, var, oval, val, o): | ||
| 233 | history = self.variable(var) | ||
| 234 | commentVal = re.sub('\n', '\n#', str(oval)) | ||
| 235 | if history: | ||
| 236 | if len(history) == 1: | ||
| 237 | o.write("#\n# $%s\n" % var) | ||
| 238 | else: | ||
| 239 | o.write("#\n# $%s [%d operations]\n" % (var, len(history))) | ||
| 240 | for event in history: | ||
| 241 | # o.write("# %s\n" % str(event)) | ||
| 242 | if 'func' in event: | ||
| 243 | # If we have a function listed, this is internal | ||
| 244 | # code, not an operation in a config file, and the | ||
| 245 | # full path is distracting. | ||
| 246 | event['file'] = re.sub('.*/', '', event['file']) | ||
| 247 | display_func = ' [%s]' % event['func'] | ||
| 248 | else: | ||
| 249 | display_func = '' | ||
| 250 | if 'flag' in event: | ||
| 251 | flag = '[%s] ' % (event['flag']) | ||
| 252 | else: | ||
| 253 | flag = '' | ||
| 254 | o.write("# %s %s:%s%s\n# %s\"%s\"\n" % (event['op'], event['file'], event['line'], display_func, flag, re.sub('\n', '\n# ', event['detail']))) | ||
| 255 | if len(history) > 1: | ||
| 256 | o.write("# computed:\n") | ||
| 257 | o.write('# "%s"\n' % (commentVal)) | ||
| 258 | else: | ||
| 259 | o.write("#\n# $%s\n# [no history recorded]\n#\n" % var) | ||
| 260 | o.write('# "%s"\n' % (commentVal)) | ||
| 261 | |||
| 262 | |||
| 160 | class DataSmart(MutableMapping): | 263 | class DataSmart(MutableMapping): |
| 161 | def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ): | 264 | def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ): |
| 162 | self.dict = {} | 265 | self.dict = {} |
| 163 | 266 | ||
| 164 | self.inchistory = IncludeHistory() | 267 | self.inchistory = IncludeHistory() |
| 268 | self.varhistory = VariableHistory(self) | ||
| 269 | self._tracking = False | ||
| 165 | 270 | ||
| 166 | # cookie monster tribute | 271 | # cookie monster tribute |
| 167 | self._special_values = special | 272 | self._special_values = special |
| @@ -169,6 +274,12 @@ class DataSmart(MutableMapping): | |||
| 169 | 274 | ||
| 170 | self.expand_cache = {} | 275 | self.expand_cache = {} |
| 171 | 276 | ||
| 277 | def enableTracking(self): | ||
| 278 | self._tracking = True | ||
| 279 | |||
| 280 | def disableTracking(self): | ||
| 281 | self._tracking = False | ||
| 282 | |||
| 172 | def expandWithRefs(self, s, varname): | 283 | def expandWithRefs(self, s, varname): |
| 173 | 284 | ||
| 174 | if not isinstance(s, basestring): # sanity check | 285 | if not isinstance(s, basestring): # sanity check |
| @@ -204,10 +315,14 @@ class DataSmart(MutableMapping): | |||
| 204 | return self.expandWithRefs(s, varname).value | 315 | return self.expandWithRefs(s, varname).value |
| 205 | 316 | ||
| 206 | 317 | ||
| 207 | def finalize(self): | 318 | def finalize(self, parent = False): |
| 208 | """Performs final steps upon the datastore, including application of overrides""" | 319 | """Performs final steps upon the datastore, including application of overrides""" |
| 209 | 320 | ||
| 210 | overrides = (self.getVar("OVERRIDES", True) or "").split(":") or [] | 321 | overrides = (self.getVar("OVERRIDES", True) or "").split(":") or [] |
| 322 | finalize_caller = { | ||
| 323 | 'op': 'finalize', | ||
| 324 | } | ||
| 325 | infer_caller_details(finalize_caller, parent = parent, varval = False) | ||
| 211 | 326 | ||
| 212 | # | 327 | # |
| 213 | # Well let us see what breaks here. We used to iterate | 328 | # Well let us see what breaks here. We used to iterate |
| @@ -224,6 +339,9 @@ class DataSmart(MutableMapping): | |||
| 224 | # Then we will handle _append and _prepend | 339 | # Then we will handle _append and _prepend |
| 225 | # | 340 | # |
| 226 | 341 | ||
| 342 | # We only want to report finalization once per variable overridden. | ||
| 343 | finalizes_reported = {} | ||
| 344 | |||
| 227 | for o in overrides: | 345 | for o in overrides: |
| 228 | # calculate '_'+override | 346 | # calculate '_'+override |
| 229 | l = len(o) + 1 | 347 | l = len(o) + 1 |
| @@ -236,7 +354,19 @@ class DataSmart(MutableMapping): | |||
| 236 | for var in vars: | 354 | for var in vars: |
| 237 | name = var[:-l] | 355 | name = var[:-l] |
| 238 | try: | 356 | try: |
| 239 | self.setVar(name, self.getVar(var, False)) | 357 | # Report only once, even if multiple changes. |
| 358 | if name not in finalizes_reported: | ||
| 359 | finalizes_reported[name] = True | ||
| 360 | finalize_caller['variable'] = name | ||
| 361 | finalize_caller['detail'] = 'was: ' + str(self.getVar(name, False)) | ||
| 362 | self.varhistory.record(**finalize_caller) | ||
| 363 | # Copy history of the override over. | ||
| 364 | for event in self.varhistory.variable(var): | ||
| 365 | loginfo = event.copy() | ||
| 366 | loginfo['variable'] = name | ||
| 367 | loginfo['op'] = 'override[%s]:%s' % (o, loginfo['op']) | ||
| 368 | self.varhistory.record(**loginfo) | ||
| 369 | self.setVar(name, self.getVar(var, False), op = 'finalize', file = 'override[%s]' % o, line = '') | ||
| 240 | self.delVar(var) | 370 | self.delVar(var) |
| 241 | except Exception: | 371 | except Exception: |
| 242 | logger.info("Untracked delVar") | 372 | logger.info("Untracked delVar") |
| @@ -267,9 +397,9 @@ class DataSmart(MutableMapping): | |||
| 267 | 397 | ||
| 268 | # We save overrides that may be applied at some later stage | 398 | # We save overrides that may be applied at some later stage |
| 269 | if keep: | 399 | if keep: |
| 270 | self.setVarFlag(append, op, keep) | 400 | self.setVarFlag(append, op, keep, ignore=True) |
| 271 | else: | 401 | else: |
| 272 | self.delVarFlag(append, op) | 402 | self.delVarFlag(append, op, ignore=True) |
| 273 | 403 | ||
| 274 | def initVar(self, var): | 404 | def initVar(self, var): |
| 275 | self.expand_cache = {} | 405 | self.expand_cache = {} |
| @@ -297,7 +427,10 @@ class DataSmart(MutableMapping): | |||
| 297 | else: | 427 | else: |
| 298 | self.initVar(var) | 428 | self.initVar(var) |
| 299 | 429 | ||
| 300 | def setVar(self, var, value): | 430 | |
| 431 | def setVar(self, var, value, **loginfo): | ||
| 432 | if 'op' not in loginfo: | ||
| 433 | loginfo['op'] = "set" | ||
| 301 | self.expand_cache = {} | 434 | self.expand_cache = {} |
| 302 | match = __setvar_regexp__.match(var) | 435 | match = __setvar_regexp__.match(var) |
| 303 | if match and match.group("keyword") in __setvar_keyword__: | 436 | if match and match.group("keyword") in __setvar_keyword__: |
| @@ -306,15 +439,22 @@ class DataSmart(MutableMapping): | |||
| 306 | override = match.group('add') | 439 | override = match.group('add') |
| 307 | l = self.getVarFlag(base, keyword) or [] | 440 | l = self.getVarFlag(base, keyword) or [] |
| 308 | l.append([value, override]) | 441 | l.append([value, override]) |
| 309 | self.setVarFlag(base, keyword, l) | 442 | self.setVarFlag(base, keyword, l, ignore=True) |
| 310 | 443 | # And cause that to be recorded: | |
| 444 | loginfo['detail'] = value | ||
| 445 | loginfo['variable'] = base | ||
| 446 | if override: | ||
| 447 | loginfo['op'] = '%s[%s]' % (keyword, override) | ||
| 448 | else: | ||
| 449 | loginfo['op'] = keyword | ||
| 450 | self.varhistory.record(**loginfo) | ||
| 311 | # todo make sure keyword is not __doc__ or __module__ | 451 | # todo make sure keyword is not __doc__ or __module__ |
| 312 | # pay the cookie monster | 452 | # pay the cookie monster |
| 313 | try: | 453 | try: |
| 314 | self._special_values[keyword].add( base ) | 454 | self._special_values[keyword].add(base) |
| 315 | except KeyError: | 455 | except KeyError: |
| 316 | self._special_values[keyword] = set() | 456 | self._special_values[keyword] = set() |
| 317 | self._special_values[keyword].add( base ) | 457 | self._special_values[keyword].add(base) |
| 318 | 458 | ||
| 319 | return | 459 | return |
| 320 | 460 | ||
| @@ -331,6 +471,7 @@ class DataSmart(MutableMapping): | |||
| 331 | 471 | ||
| 332 | # setting var | 472 | # setting var |
| 333 | self.dict[var]["_content"] = value | 473 | self.dict[var]["_content"] = value |
| 474 | self.varhistory.record(**loginfo) | ||
| 334 | 475 | ||
| 335 | def getVar(self, var, expand=False, noweakdefault=False): | 476 | def getVar(self, var, expand=False, noweakdefault=False): |
| 336 | value = self.getVarFlag(var, "_content", False, noweakdefault) | 477 | value = self.getVarFlag(var, "_content", False, noweakdefault) |
| @@ -340,13 +481,17 @@ class DataSmart(MutableMapping): | |||
| 340 | return self.expand(value, var) | 481 | return self.expand(value, var) |
| 341 | return value | 482 | return value |
| 342 | 483 | ||
| 343 | def renameVar(self, key, newkey): | 484 | def renameVar(self, key, newkey, **loginfo): |
| 344 | """ | 485 | """ |
| 345 | Rename the variable key to newkey | 486 | Rename the variable key to newkey |
| 346 | """ | 487 | """ |
| 347 | val = self.getVar(key, 0) | 488 | val = self.getVar(key, 0) |
| 348 | if val is not None: | 489 | if val is not None: |
| 349 | self.setVar(newkey, val) | 490 | loginfo['variable'] = newkey |
| 491 | loginfo['op'] = 'rename from %s' % key | ||
| 492 | loginfo['detail'] = val | ||
| 493 | self.varhistory.record(**loginfo) | ||
| 494 | self.setVar(newkey, val, ignore=True) | ||
| 350 | 495 | ||
| 351 | for i in ('_append', '_prepend'): | 496 | for i in ('_append', '_prepend'): |
| 352 | src = self.getVarFlag(key, i) | 497 | src = self.getVarFlag(key, i) |
| @@ -355,23 +500,34 @@ class DataSmart(MutableMapping): | |||
| 355 | 500 | ||
| 356 | dest = self.getVarFlag(newkey, i) or [] | 501 | dest = self.getVarFlag(newkey, i) or [] |
| 357 | dest.extend(src) | 502 | dest.extend(src) |
| 358 | self.setVarFlag(newkey, i, dest) | 503 | self.setVarFlag(newkey, i, dest, ignore=True) |
| 359 | 504 | ||
| 360 | if i in self._special_values and key in self._special_values[i]: | 505 | if i in self._special_values and key in self._special_values[i]: |
| 361 | self._special_values[i].remove(key) | 506 | self._special_values[i].remove(key) |
| 362 | self._special_values[i].add(newkey) | 507 | self._special_values[i].add(newkey) |
| 363 | 508 | ||
| 364 | self.delVar(key) | 509 | loginfo['variable'] = key |
| 365 | 510 | loginfo['op'] = 'rename (to)' | |
| 366 | def appendVar(self, key, value): | 511 | loginfo['detail'] = newkey |
| 367 | value = (self.getVar(key, False) or "") + value | 512 | self.varhistory.record(**loginfo) |
| 368 | self.setVar(key, value) | 513 | self.delVar(key, ignore=True) |
| 369 | 514 | ||
| 370 | def prependVar(self, key, value): | 515 | def appendVar(self, var, value, **loginfo): |
| 371 | value = value + (self.getVar(key, False) or "") | 516 | loginfo['op'] = 'append' |
| 372 | self.setVar(key, value) | 517 | self.varhistory.record(**loginfo) |
| 373 | 518 | newvalue = (self.getVar(var, False) or "") + value | |
| 374 | def delVar(self, var): | 519 | self.setVar(var, newvalue, ignore=True) |
| 520 | |||
| 521 | def prependVar(self, var, value, **loginfo): | ||
| 522 | loginfo['op'] = 'prepend' | ||
| 523 | self.varhistory.record(**loginfo) | ||
| 524 | newvalue = value + (self.getVar(var, False) or "") | ||
| 525 | self.setVar(var, newvalue, ignore=True) | ||
| 526 | |||
| 527 | def delVar(self, var, **loginfo): | ||
| 528 | loginfo['detail'] = "" | ||
| 529 | loginfo['op'] = 'del' | ||
| 530 | self.varhistory.record(**loginfo) | ||
| 375 | self.expand_cache = {} | 531 | self.expand_cache = {} |
| 376 | self.dict[var] = {} | 532 | self.dict[var] = {} |
| 377 | if '_' in var: | 533 | if '_' in var: |
| @@ -379,10 +535,14 @@ class DataSmart(MutableMapping): | |||
| 379 | if override and override in self._seen_overrides and var in self._seen_overrides[override]: | 535 | if override and override in self._seen_overrides and var in self._seen_overrides[override]: |
| 380 | self._seen_overrides[override].remove(var) | 536 | self._seen_overrides[override].remove(var) |
| 381 | 537 | ||
| 382 | def setVarFlag(self, var, flag, flagvalue): | 538 | def setVarFlag(self, var, flag, value, **loginfo): |
| 539 | if 'op' not in loginfo: | ||
| 540 | loginfo['op'] = "set" | ||
| 541 | loginfo['flag'] = flag | ||
| 542 | self.varhistory.record(**loginfo) | ||
| 383 | if not var in self.dict: | 543 | if not var in self.dict: |
| 384 | self._makeShadowCopy(var) | 544 | self._makeShadowCopy(var) |
| 385 | self.dict[var][flag] = flagvalue | 545 | self.dict[var][flag] = value |
| 386 | 546 | ||
| 387 | def getVarFlag(self, var, flag, expand=False, noweakdefault=False): | 547 | def getVarFlag(self, var, flag, expand=False, noweakdefault=False): |
| 388 | local_var = self._findVar(var) | 548 | local_var = self._findVar(var) |
| @@ -396,7 +556,7 @@ class DataSmart(MutableMapping): | |||
| 396 | value = self.expand(value, None) | 556 | value = self.expand(value, None) |
| 397 | return value | 557 | return value |
| 398 | 558 | ||
| 399 | def delVarFlag(self, var, flag): | 559 | def delVarFlag(self, var, flag, **loginfo): |
| 400 | local_var = self._findVar(var) | 560 | local_var = self._findVar(var) |
| 401 | if not local_var: | 561 | if not local_var: |
| 402 | return | 562 | return |
| @@ -404,23 +564,38 @@ class DataSmart(MutableMapping): | |||
| 404 | self._makeShadowCopy(var) | 564 | self._makeShadowCopy(var) |
| 405 | 565 | ||
| 406 | if var in self.dict and flag in self.dict[var]: | 566 | if var in self.dict and flag in self.dict[var]: |
| 407 | del self.dict[var][flag] | 567 | loginfo['detail'] = "" |
| 408 | 568 | loginfo['op'] = 'delFlag' | |
| 409 | def appendVarFlag(self, key, flag, value): | 569 | loginfo['flag'] = flag |
| 410 | value = (self.getVarFlag(key, flag, False) or "") + value | 570 | self.varhistory.record(**loginfo) |
| 411 | self.setVarFlag(key, flag, value) | ||
| 412 | 571 | ||
| 413 | def prependVarFlag(self, key, flag, value): | 572 | del self.dict[var][flag] |
| 414 | value = value + (self.getVarFlag(key, flag, False) or "") | ||
| 415 | self.setVarFlag(key, flag, value) | ||
| 416 | 573 | ||
| 417 | def setVarFlags(self, var, flags): | 574 | def appendVarFlag(self, var, flag, value, **loginfo): |
| 575 | loginfo['op'] = 'append' | ||
| 576 | loginfo['flag'] = flag | ||
| 577 | self.varhistory.record(**loginfo) | ||
| 578 | newvalue = (self.getVarFlag(var, flag, False) or "") + value | ||
| 579 | self.setVarFlag(var, flag, newvalue, ignore=True) | ||
| 580 | |||
| 581 | def prependVarFlag(self, var, flag, value, **loginfo): | ||
| 582 | loginfo['op'] = 'prepend' | ||
| 583 | loginfo['flag'] = flag | ||
| 584 | self.varhistory.record(**loginfo) | ||
| 585 | newvalue = value + (self.getVarFlag(var, flag, False) or "") | ||
| 586 | self.setVarFlag(var, flag, newvalue, ignore=True) | ||
| 587 | |||
| 588 | def setVarFlags(self, var, flags, **loginfo): | ||
| 589 | infer_caller_details(loginfo) | ||
| 418 | if not var in self.dict: | 590 | if not var in self.dict: |
| 419 | self._makeShadowCopy(var) | 591 | self._makeShadowCopy(var) |
| 420 | 592 | ||
| 421 | for i in flags: | 593 | for i in flags: |
| 422 | if i == "_content": | 594 | if i == "_content": |
| 423 | continue | 595 | continue |
| 596 | loginfo['flag'] = i | ||
| 597 | loginfo['detail'] = flags[i] | ||
| 598 | self.varhistory.record(**loginfo) | ||
| 424 | self.dict[var][i] = flags[i] | 599 | self.dict[var][i] = flags[i] |
| 425 | 600 | ||
| 426 | def getVarFlags(self, var): | 601 | def getVarFlags(self, var): |
| @@ -438,13 +613,16 @@ class DataSmart(MutableMapping): | |||
| 438 | return flags | 613 | return flags |
| 439 | 614 | ||
| 440 | 615 | ||
| 441 | def delVarFlags(self, var): | 616 | def delVarFlags(self, var, **loginfo): |
| 442 | if not var in self.dict: | 617 | if not var in self.dict: |
| 443 | self._makeShadowCopy(var) | 618 | self._makeShadowCopy(var) |
| 444 | 619 | ||
| 445 | if var in self.dict: | 620 | if var in self.dict: |
| 446 | content = None | 621 | content = None |
| 447 | 622 | ||
| 623 | loginfo['op'] = 'delete flags' | ||
| 624 | self.varhistory.record(**loginfo) | ||
| 625 | |||
| 448 | # try to save the content | 626 | # try to save the content |
| 449 | if "_content" in self.dict[var]: | 627 | if "_content" in self.dict[var]: |
| 450 | content = self.dict[var]["_content"] | 628 | content = self.dict[var]["_content"] |
| @@ -461,8 +639,12 @@ class DataSmart(MutableMapping): | |||
| 461 | # we really want this to be a DataSmart... | 639 | # we really want this to be a DataSmart... |
| 462 | data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy()) | 640 | data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy()) |
| 463 | data.dict["_data"] = self.dict | 641 | data.dict["_data"] = self.dict |
| 642 | data.varhistory = self.varhistory.copy() | ||
| 643 | data.varhistory.datasmart = data | ||
| 464 | data.inchistory = self.inchistory.copy() | 644 | data.inchistory = self.inchistory.copy() |
| 465 | 645 | ||
| 646 | data._tracking = self._tracking | ||
| 647 | |||
| 466 | return data | 648 | return data |
| 467 | 649 | ||
| 468 | def expandVarref(self, variable, parents=False): | 650 | def expandVarref(self, variable, parents=False): |
