Ticket #392: xpath_engine_mem_opt.patch
| File xpath_engine_mem_opt.patch, 9.4 KB (added by Carsten Klein <carsten.klein@…>, 13 years ago) |
|---|
-
path.py
82 82 83 83 class GenericStrategy(object): 84 84 85 @classmethod 86 def supports(cls, path): 85 def supports(self, path): 87 86 return True 88 87 89 def __init__(self, path): 90 self.path = path 91 92 def test(self, ignore_context): 93 p = self.path 88 def test(self, path, ignore_context): 89 p = path 94 90 if ignore_context: 95 91 if p[0][0] is ATTRIBUTE: 96 92 steps = [_DOTSLASHSLASH] + p … … 236 232 class SimplePathStrategy(object): 237 233 """Strategy for path with only local names, attributes and text nodes.""" 238 234 239 @classmethod 240 def supports(cls, path): 235 def supports(self, path): 241 236 if path[0][0] is ATTRIBUTE: 242 237 return False 243 238 allowed_tests = (LocalNameTest, CommentNodeTest, TextNodeTest) … … 248 243 return False 249 244 return True 250 245 251 def __init__(self, path): 246 def __init__(self): 247 self._path_fragments = {} 248 249 def _init_or_get_path_fragments(self, path): 252 250 # fragments is list of tuples (fragment, pi, attr, self_beginning) 253 251 # fragment is list of nodetests for fragment of path with only 254 252 # child:: axes between … … 256 254 # attr is attribute nodetest if fragment ends with @ and None otherwise 257 255 # self_beginning is True if axis for first fragment element 258 256 # was self (first fragment) or descendant-or-self (farther fragment) 259 self.fragments = []260 257 258 prepr = repr(path) 259 if prepr in self._path_fragments: 260 return self._path_fragments[prepr] 261 261 262 self_beginning = False 263 fragments = [] 262 264 fragment = [] 263 265 264 266 def nodes_equal(node1, node2): … … 293 295 # the same as previous one 294 296 # for example child::a/self::b is always wrong 295 297 if axis[1] != fragment[-1][1]: 296 self.fragments = None298 fragments = None 297 299 return 298 300 else: 299 301 self_beginning = True … … 302 304 fragment.append(axis[1]) 303 305 elif axis[0] is ATTRIBUTE: 304 306 pi = calculate_pi(fragment) 305 self.fragments.append((fragment, pi, axis[1], self_beginning)) 307 fragments.append((fragment, pi, axis[1], self_beginning)) 308 self._path_fragments[prepr] = fragments 306 309 # attribute has always to be at the end, so we can jump out 307 return 310 return fragments 308 311 else: 309 312 pi = calculate_pi(fragment) 310 self.fragments.append((fragment, pi, None, self_beginning))313 fragments.append((fragment, pi, None, self_beginning)) 311 314 fragment = [axis[1]] 312 315 if axis[0] is DESCENDANT: 313 316 self_beginning = False 314 317 else: # DESCENDANT_OR_SELF 315 318 self_beginning = True 316 319 pi = calculate_pi(fragment) 317 self.fragments.append((fragment, pi, None, self_beginning)) 320 fragments.append((fragment, pi, None, self_beginning)) 321 self._path_fragments[prepr] = fragments 322 return fragments 318 323 319 def test(self, ignore_context):324 def test(self, path, ignore_context): 320 325 # stack of triples (fid, p, ic) 321 326 # fid is index of current fragment 322 327 # p is position in this fragment 323 328 # ic is if we ignore context in this fragment 329 324 330 stack = [] 325 331 stack_push = stack.append 326 332 stack_pop = stack.pop 327 frags = self. fragments333 frags = self._init_or_get_path_fragments(path) 328 334 frags_len = len(frags) 329 335 330 336 def _test(event, namespaces, variables, updateonly=False): 331 337 # expression found impossible during init 332 338 if frags is None: 333 return None339 return False 334 340 335 341 kind, data, pos = event[:3] 336 342 … … 338 344 if kind is END: 339 345 if stack: 340 346 stack_pop() 341 return None347 return False 342 348 if kind is START_NS or kind is END_NS \ 343 349 or kind is START_CDATA or kind is END_CDATA: 344 return None350 return False 345 351 346 352 if not stack: 347 353 # root node, nothing on stack, special case … … 359 365 if not frags[fid][3] and (not ignore_context or fid > 0): 360 366 # axis is not self-beggining, we have to skip this node 361 367 stack_push((fid, p, ic)) 362 return None368 return False 363 369 else: 364 370 # take position of parent 365 371 fid, p, ic = stack[-1] … … 389 395 # there was no match in fragment not ignoring context 390 396 if kind is START: 391 397 stack_push((fid, p, ic)) 392 return None398 return False 393 399 394 400 if ic: 395 401 # we are in fragment ignoring context … … 435 441 return attrib(kind, data, pos, namespaces, variables) 436 442 return True 437 443 438 return None444 return False 439 445 440 446 return _test 441 447 442 448 443 449 class SingleStepStrategy(object): 444 450 445 @classmethod 446 def supports(cls, path): 451 def supports(self, path): 447 452 return len(path) == 1 448 453 449 def __init__(self, path): 450 self.path = path 451 452 def test(self, ignore_context): 453 steps = self.path 454 def test(self, path, ignore_context): 455 steps = path 454 456 if steps[0][0] is ATTRIBUTE: 455 457 steps = [_DOTSLASH] + steps 456 select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or None458 select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or False 457 459 458 460 # for every position in expression stores counters' list 459 461 # it is used for position based predicates … … 467 469 if kind is END: 468 470 if not ignore_context: 469 471 depth[0] -= 1 470 return None472 return False 471 473 elif kind is START_NS or kind is END_NS \ 472 474 or kind is START_CDATA or kind is END_CDATA: 473 475 # should we make namespaces work? 474 return None476 return False 475 477 476 478 if not ignore_context: 477 479 outside = (steps[0][0] is SELF and depth[0] != 0) \ … … 480 482 if kind is START: 481 483 depth[0] += 1 482 484 if outside: 483 return None485 return False 484 486 485 487 axis, nodetest, predicates = steps[0] 486 488 if not nodetest(kind, data, pos, namespaces, variables): 487 return None489 return False 488 490 489 491 if predicates: 490 492 cnum = 0 … … 500 502 pretval = False 501 503 cnum += 1 502 504 if not pretval: 503 return None505 return False 504 506 505 507 if select_attr: 506 508 return select_attr(kind, data, pos, namespaces, variables) … … 509 511 510 512 return _test 511 513 514 STRATEGY_INSTANCES = [] 515 STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy) 512 516 517 for strategy_class in STRATEGIES: 518 STRATEGY_INSTANCES.append(strategy_class()) 519 513 520 class Path(object): 514 521 """Implements basic XPath support on streams. 515 522 … … 518 525 extracting a substream matching that path. 519 526 """ 520 527 521 STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy)522 523 528 def __init__(self, text, filename=None, lineno=-1): 524 529 """Create the path object from a string. 525 530 … … 530 535 """ 531 536 self.source = text 532 537 self.paths = PathParser(text, filename, lineno).parse() 533 self. strategies = []538 self.path_strategies = [] 534 539 for path in self.paths: 535 for strategy _class in self.STRATEGIES:536 if strategy _class.supports(path):537 self. strategies.append(strategy_class(path))540 for strategy in STRATEGY_INSTANCES: 541 if strategy.supports(path): 542 self.path_strategies.append((path, strategy)) 538 543 break 539 544 else: 540 raise NotImplemented('No strategy found for path ')545 raise NotImplemented('No strategy found for path "%s" in line "%s" of template "%s"' % (path, lineno, filename)) 541 546 542 547 def __repr__(self): 543 548 paths = [] … … 628 633 stream against the path 629 634 :rtype: ``function`` 630 635 """ 631 tests = [s .test(ignore_context) for s in self.strategies]636 tests = [strategy.test(path, ignore_context) for path, strategy in self.path_strategies] 632 637 if len(tests) == 1: 633 638 return tests[0] 634 639 635 640 def _multi(event, namespaces, variables, updateonly=False): 636 641 retval = None 637 642 for test in tests: 638 val = test(event, namespaces, variables, updateonly=updateonly) 639 if retval is None: 640 retval = val 641 return retval 643 if test(event, namespaces, variables, updateonly=updateonly): 644 return True 645 return False 642 646 return _multi 643 647
