Repository navigation
Expand file tree
/
Copy pathexpression.py
More file actions
137 lines (86 loc) · 4.67 KB
/
Copy pathexpression.py
File metadata and controls
137 lines (86 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
'''
Copyright (c) 2019 Timothy Savannah under terms of LGPLv3. All Rights Reserved.
See LICENSE (https://gnu.org/licenses/lgpl-3.0.txt) for more information.
See: https://github.com/kata198/AdvancedHTMLParser for full information
xpath.expression.py - Module defines some types and features related to expressions
'''
# vim: set ts=4 sw=4 st=4 expandtab :
import copy
from ..Tags import TagCollection, AdvancedTag
from ._debug import getXPathDebug
from .exceptions import XPathParseError
from .operation import XPathOperation
from .parsing import parseXPathStrIntoOperations
from ._cache import XPathExpressionCache
__all__ = ('XPathExpression', )
class XPathExpression(object):
'''
XPathExpression - The main class for dealing with XPath expressions
'''
def __init__(self, xpathStr):
'''
__init__ - Create this object from a string expression
@param xpathStr <str> - An xpath expression
'''
self.xpathStr = xpathStr
# Check if we've recently compiled this string, and copy the compiled operations, if so.
wasCached = XPathExpressionCache.applyCachedExpressionIfAvailable( xpathStr, self )
if wasCached is False:
# No cached entity found, compile this string
self.orderedOperations = parseXPathStrIntoOperations(self.xpathStr)
# Save compiled expression in the expression cache
XPathExpressionCache.setCachedExpression( xpathStr, self )
def _copyOperationsFromXPathExpressionObj(self, otherXPathExpressionObj):
'''
_copyOperationsFromXPathExpressionObj - Copies the operations from another XPathExpression object onto this one.
This will clear the current set of operations on this object, replacing it with a copy from the provided object.
@param otherXPathExpressionObj <XPathExpression> - Another XPathExpression object
'''
self.orderedOperations = copy.copy( otherXPathExpressionObj.orderedOperations )
def evaluate(self, pathRoot):
'''
evaluate - Run this XPath expression against a tree, and return the results.
@param pathRoot <
curResults = [ pathRoot ]
Tags.AdvancedTag [From a single root tag] -or-
Parser.AdvancedHTMLParser [From the root of a document] -or-
(list/tuple)<Tags.AdvancedTag> [From a list or tuple of tags] -or-
Tags.TagCollecction [From a TagCollection of tags]
> -
Run this XPath expression against this/these given node/nodes/document
@return <TagCollection> - A TagCollection of matched tags
'''
# Late binding import
from ..Parser import AdvancedHTMLParser
pathRootClass = pathRoot.__class__
# TODO: Support starting from a text node (not a tag node) ?
# TODO: Check for "None" ?
if issubclass(pathRootClass, AdvancedTag):
# A single tag
curResults = [ pathRoot ]
elif issubclass(pathRootClass, AdvancedHTMLParser):
# A "document" (AdvancedHTMLParser instance)
curResults = pathRoot.getRootNodes()
# TODO: Test if above is okay,
# e.x. will /html[1] return the <html> as expected, or fail to find because start at <html? ?
# TagCollection is a subclass of list, so check it explicitly, first.
elif issubclass(pathRootClass, TagCollection):
# A TagCollection -- convert to a basic list
# NOTE: Just cast to list instead?
curResults = pathRoot.all()
elif issubclass(pathRootClass, (list, tuple)):
# If a list/tuple, make into a copy list
curResults = list(pathRoot)
# TODO: Check if the elements in #pathRoot are actually Tags.AdvancedTag objects?
else:
raise ValueError('Unknown type < %s > ( %s ) passed to XPathExpression.evaluate! Should be Tags.AdvancedTag or Parser.AdvancedHTMLParser or Tags.TagCollectiojn or list/tuple<Tags,AdvancedTag>.' %( pathRootClass.__name__, str(type(pathRoot)) ) )
# Make a fresh TagCollection, even if we were passed one at start
curCollection = TagCollection(curResults)
for orderedOperation in self.orderedOperations:
thisResultCollection = orderedOperation.applyFunction( curCollection )
if len(thisResultCollection) == 0:
# TODO: Why create fresh?
return TagCollection()
curCollection = thisResultCollection
return curCollection
# vim: set ts=4 sw=4 st=4 expandtab :