diff options
| author | Christopher Larson <kergoth@gmail.com> | 2011-10-27 22:23:05 -0700 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-11-10 11:44:28 +0000 |
| commit | 002451b3a27661517073bbe8917dcac04b3bf12a (patch) | |
| tree | 6b03e8fcc8bbddc96f4ec108c26bc2278303c8dd | |
| parent | 9e06f31a3008dabb3d517d415a552cca9e47d2e7 (diff) | |
| download | poky-002451b3a27661517073bbe8917dcac04b3bf12a.tar.gz | |
codeparser: merge the nested python parsing classes
The split is even less necessary now that we use ast.walk rather than an
actual NodeVisitor subclass.
(Bitbake rev: d6c44fac184abae8395bfa7078f06675218aa534)
Signed-off-by: Christopher Larson <kergoth@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | bitbake/lib/bb/codeparser.py | 189 |
1 files changed, 87 insertions, 102 deletions
diff --git a/bitbake/lib/bb/codeparser.py b/bitbake/lib/bb/codeparser.py index 0dd9a365ee..0f3d646cc4 100644 --- a/bitbake/lib/bb/codeparser.py +++ b/bitbake/lib/bb/codeparser.py | |||
| @@ -151,116 +151,103 @@ def parser_cache_savemerge(d): | |||
| 151 | 151 | ||
| 152 | 152 | ||
| 153 | class PythonParser(): | 153 | class PythonParser(): |
| 154 | class ValueVisitor(): | 154 | getvars = ("d.getVar", "bb.data.getVar", "data.getVar") |
| 155 | """Visitor to traverse a python abstract syntax tree and obtain | 155 | expands = ("d.expand", "bb.data.expand", "data.expand") |
| 156 | the variables referenced via bitbake metadata APIs, and the external | 156 | execfuncs = ("bb.build.exec_func", "bb.build.exec_task") |
| 157 | functions called. | 157 | |
| 158 | @classmethod | ||
| 159 | def _compare_name(cls, strparts, node): | ||
| 160 | """Given a sequence of strings representing a python name, | ||
| 161 | where the last component is the actual Name and the prior | ||
| 162 | elements are Attribute nodes, determine if the supplied node | ||
| 163 | matches. | ||
| 158 | """ | 164 | """ |
| 159 | 165 | ||
| 160 | getvars = ("d.getVar", "bb.data.getVar", "data.getVar") | 166 | if not strparts: |
| 161 | expands = ("d.expand", "bb.data.expand", "data.expand") | 167 | return True |
| 162 | execs = ("bb.build.exec_func", "bb.build.exec_task") | ||
| 163 | 168 | ||
| 164 | @classmethod | 169 | current, rest = strparts[0], strparts[1:] |
| 165 | def _compare_name(cls, strparts, node): | 170 | if isinstance(node, ast.Attribute): |
| 166 | """Given a sequence of strings representing a python name, | 171 | if current == node.attr: |
| 167 | where the last component is the actual Name and the prior | 172 | return cls._compare_name(rest, node.value) |
| 168 | elements are Attribute nodes, determine if the supplied node | 173 | elif isinstance(node, ast.Name): |
| 169 | matches. | 174 | if current == node.id: |
| 170 | """ | ||
| 171 | |||
| 172 | if not strparts: | ||
| 173 | return True | 175 | return True |
| 176 | return False | ||
| 177 | |||
| 178 | @classmethod | ||
| 179 | def compare_name(cls, value, node): | ||
| 180 | """Convenience function for the _compare_node method, which | ||
| 181 | can accept a string (which is split by '.' for you), or an | ||
| 182 | iterable of strings, in which case it checks to see if any of | ||
| 183 | them match, similar to isinstance. | ||
| 184 | """ | ||
| 174 | 185 | ||
| 175 | current, rest = strparts[0], strparts[1:] | 186 | if isinstance(value, basestring): |
| 176 | if isinstance(node, ast.Attribute): | 187 | return cls._compare_name(tuple(reversed(value.split("."))), |
| 177 | if current == node.attr: | 188 | node) |
| 178 | return cls._compare_name(rest, node.value) | 189 | else: |
| 179 | elif isinstance(node, ast.Name): | 190 | return any(cls.compare_name(item, node) for item in value) |
| 180 | if current == node.id: | ||
| 181 | return True | ||
| 182 | return False | ||
| 183 | |||
| 184 | @classmethod | ||
| 185 | def compare_name(cls, value, node): | ||
| 186 | """Convenience function for the _compare_node method, which | ||
| 187 | can accept a string (which is split by '.' for you), or an | ||
| 188 | iterable of strings, in which case it checks to see if any of | ||
| 189 | them match, similar to isinstance. | ||
| 190 | """ | ||
| 191 | |||
| 192 | if isinstance(value, basestring): | ||
| 193 | return cls._compare_name(tuple(reversed(value.split("."))), | ||
| 194 | node) | ||
| 195 | else: | ||
| 196 | return any(cls.compare_name(item, node) for item in value) | ||
| 197 | |||
| 198 | def __init__(self, value): | ||
| 199 | self.var_references = set() | ||
| 200 | self.var_execs = set() | ||
| 201 | self.direct_func_calls = set() | ||
| 202 | self.var_expands = set() | ||
| 203 | self.value = value | ||
| 204 | |||
| 205 | @classmethod | ||
| 206 | def warn(cls, func, arg): | ||
| 207 | """Warn about calls of bitbake APIs which pass a non-literal | ||
| 208 | argument for the variable name, as we're not able to track such | ||
| 209 | a reference. | ||
| 210 | """ | ||
| 211 | 191 | ||
| 212 | try: | 192 | @classmethod |
| 213 | funcstr = codegen.to_source(func) | 193 | def warn(cls, func, arg): |
| 214 | argstr = codegen.to_source(arg) | 194 | """Warn about calls of bitbake APIs which pass a non-literal |
| 215 | except TypeError: | 195 | argument for the variable name, as we're not able to track such |
| 216 | logger.debug(2, 'Failed to convert function and argument to source form') | 196 | a reference. |
| 217 | else: | 197 | """ |
| 218 | logger.debug(1, "Warning: in call to '%s', argument '%s' is " | ||
| 219 | "not a literal", funcstr, argstr) | ||
| 220 | 198 | ||
| 221 | def visit_Call(self, node): | 199 | try: |
| 222 | if self.compare_name(self.getvars, node.func): | 200 | funcstr = codegen.to_source(func) |
| 223 | if isinstance(node.args[0], ast.Str): | 201 | argstr = codegen.to_source(arg) |
| 224 | self.var_references.add(node.args[0].s) | 202 | except TypeError: |
| 225 | else: | 203 | logger.debug(2, 'Failed to convert function and argument to source form') |
| 226 | self.warn(node.func, node.args[0]) | 204 | else: |
| 227 | elif self.compare_name(self.expands, node.func): | 205 | logger.debug(1, "Warning: in call to '%s', argument '%s' is " |
| 228 | if isinstance(node.args[0], ast.Str): | 206 | "not a literal", funcstr, argstr) |
| 229 | self.warn(node.func, node.args[0]) | 207 | |
| 230 | self.var_expands.add(node.args[0].s) | 208 | def visit_Call(self, node): |
| 231 | elif isinstance(node.args[0], ast.Call) and \ | 209 | if self.compare_name(self.getvars, node.func): |
| 232 | self.compare_name(self.getvars, node.args[0].func): | 210 | if isinstance(node.args[0], ast.Str): |
| 233 | pass | 211 | self.var_references.add(node.args[0].s) |
| 234 | else: | 212 | else: |
| 235 | self.warn(node.func, node.args[0]) | 213 | self.warn(node.func, node.args[0]) |
| 236 | elif self.compare_name(self.execs, node.func): | 214 | elif self.compare_name(self.expands, node.func): |
| 237 | if isinstance(node.args[0], ast.Str): | 215 | if isinstance(node.args[0], ast.Str): |
| 238 | self.var_execs.add(node.args[0].s) | 216 | self.warn(node.func, node.args[0]) |
| 239 | else: | 217 | self.var_expands.add(node.args[0].s) |
| 240 | self.warn(node.func, node.args[0]) | 218 | elif isinstance(node.args[0], ast.Call) and \ |
| 241 | elif isinstance(node.func, ast.Name): | 219 | self.compare_name(self.getvars, node.args[0].func): |
| 242 | self.direct_func_calls.add(node.func.id) | 220 | pass |
| 243 | elif isinstance(node.func, ast.Attribute): | 221 | else: |
| 244 | # We must have a qualified name. Therefore we need | 222 | self.warn(node.func, node.args[0]) |
| 245 | # to walk the chain of 'Attribute' nodes to determine | 223 | elif self.compare_name(self.execfuncs, node.func): |
| 246 | # the qualification. | 224 | if isinstance(node.args[0], ast.Str): |
| 247 | attr_node = node.func.value | 225 | self.var_execs.add(node.args[0].s) |
| 248 | identifier = node.func.attr | 226 | else: |
| 249 | while isinstance(attr_node, ast.Attribute): | 227 | self.warn(node.func, node.args[0]) |
| 250 | identifier = attr_node.attr + "." + identifier | 228 | elif isinstance(node.func, ast.Name): |
| 251 | attr_node = attr_node.value | 229 | self.execs.add(node.func.id) |
| 252 | if isinstance(attr_node, ast.Name): | 230 | elif isinstance(node.func, ast.Attribute): |
| 253 | identifier = attr_node.id + "." + identifier | 231 | # We must have a qualified name. Therefore we need |
| 254 | self.direct_func_calls.add(identifier) | 232 | # to walk the chain of 'Attribute' nodes to determine |
| 233 | # the qualification. | ||
| 234 | attr_node = node.func.value | ||
| 235 | identifier = node.func.attr | ||
| 236 | while isinstance(attr_node, ast.Attribute): | ||
| 237 | identifier = attr_node.attr + "." + identifier | ||
| 238 | attr_node = attr_node.value | ||
| 239 | if isinstance(attr_node, ast.Name): | ||
| 240 | identifier = attr_node.id + "." + identifier | ||
| 241 | self.execs.add(identifier) | ||
| 255 | 242 | ||
| 256 | def __init__(self): | 243 | def __init__(self): |
| 257 | #self.funcdefs = set() | 244 | self.var_references = set() |
| 245 | self.var_execs = set() | ||
| 258 | self.execs = set() | 246 | self.execs = set() |
| 259 | #self.external_cmds = set() | 247 | self.var_expands = set() |
| 260 | self.references = set() | 248 | self.references = set() |
| 261 | 249 | ||
| 262 | def parse_python(self, node): | 250 | def parse_python(self, node): |
| 263 | |||
| 264 | h = hash(str(node)) | 251 | h = hash(str(node)) |
| 265 | 252 | ||
| 266 | if h in pythonparsecache: | 253 | if h in pythonparsecache: |
| @@ -271,14 +258,12 @@ class PythonParser(): | |||
| 271 | code = compile(check_indent(str(node)), "<string>", "exec", | 258 | code = compile(check_indent(str(node)), "<string>", "exec", |
| 272 | ast.PyCF_ONLY_AST) | 259 | ast.PyCF_ONLY_AST) |
| 273 | 260 | ||
| 274 | visitor = self.ValueVisitor(code) | ||
| 275 | for n in ast.walk(code): | 261 | for n in ast.walk(code): |
| 276 | if n.__class__.__name__ == "Call": | 262 | if n.__class__.__name__ == "Call": |
| 277 | visitor.visit_Call(n) | 263 | self.visit_Call(n) |
| 278 | 264 | ||
| 279 | self.references.update(visitor.var_references) | 265 | self.references.update(self.var_references) |
| 280 | self.references.update(visitor.var_execs) | 266 | self.references.update(self.var_execs) |
| 281 | self.execs = visitor.direct_func_calls | ||
| 282 | 267 | ||
| 283 | pythonparsecache[h] = {} | 268 | pythonparsecache[h] = {} |
| 284 | pythonparsecache[h]["refs"] = self.references | 269 | pythonparsecache[h]["refs"] = self.references |
