1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/common.py Tue Aug 30 16:51:10 2016 +0200
1.3 @@ -0,0 +1,1067 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Common functions.
1.8 +
1.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
1.10 + 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
1.11 +
1.12 +This program is free software; you can redistribute it and/or modify it under
1.13 +the terms of the GNU General Public License as published by the Free Software
1.14 +Foundation; either version 3 of the License, or (at your option) any later
1.15 +version.
1.16 +
1.17 +This program is distributed in the hope that it will be useful, but WITHOUT
1.18 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.19 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.20 +details.
1.21 +
1.22 +You should have received a copy of the GNU General Public License along with
1.23 +this program. If not, see <http://www.gnu.org/licenses/>.
1.24 +"""
1.25 +
1.26 +from errors import *
1.27 +from os import listdir, makedirs, remove
1.28 +from os.path import exists, isdir, join, split
1.29 +import compiler
1.30 +
1.31 +class CommonOutput:
1.32 +
1.33 + "Common output functionality."
1.34 +
1.35 + def check_output(self):
1.36 +
1.37 + "Check the existing output and remove it if irrelevant."
1.38 +
1.39 + if not exists(self.output):
1.40 + makedirs(self.output)
1.41 +
1.42 + details = self.importer.get_cache_details()
1.43 + recorded_details = self.get_output_details()
1.44 +
1.45 + if recorded_details != details:
1.46 + self.remove_output()
1.47 +
1.48 + writefile(self.get_output_details_filename(), details)
1.49 +
1.50 + def get_output_details_filename(self):
1.51 +
1.52 + "Return the output details filename."
1.53 +
1.54 + return join(self.output, "$details")
1.55 +
1.56 + def get_output_details(self):
1.57 +
1.58 + "Return details of the existing output."
1.59 +
1.60 + details_filename = self.get_output_details_filename()
1.61 +
1.62 + if not exists(details_filename):
1.63 + return None
1.64 + else:
1.65 + return readfile(details_filename)
1.66 +
1.67 + def remove_output(self, dirname=None):
1.68 +
1.69 + "Remove the output."
1.70 +
1.71 + dirname = dirname or self.output
1.72 +
1.73 + for filename in listdir(dirname):
1.74 + path = join(dirname, filename)
1.75 + if isdir(path):
1.76 + self.remove_output(path)
1.77 + else:
1.78 + remove(path)
1.79 +
1.80 +class CommonModule:
1.81 +
1.82 + "A common module representation."
1.83 +
1.84 + def __init__(self, name, importer):
1.85 +
1.86 + """
1.87 + Initialise this module with the given 'name' and an 'importer' which is
1.88 + used to provide access to other modules when required.
1.89 + """
1.90 +
1.91 + self.name = name
1.92 + self.importer = importer
1.93 + self.filename = None
1.94 +
1.95 + # Inspection-related attributes.
1.96 +
1.97 + self.astnode = None
1.98 + self.iterators = {}
1.99 + self.temp = {}
1.100 + self.lambdas = {}
1.101 +
1.102 + # Constants, literals and values.
1.103 +
1.104 + self.constants = {}
1.105 + self.constant_values = {}
1.106 + self.literals = {}
1.107 + self.literal_types = {}
1.108 +
1.109 + # Nested namespaces.
1.110 +
1.111 + self.namespace_path = []
1.112 + self.in_function = False
1.113 +
1.114 + # Attribute chains.
1.115 +
1.116 + self.attrs = []
1.117 +
1.118 + def __repr__(self):
1.119 + return "CommonModule(%r, %r)" % (self.name, self.importer)
1.120 +
1.121 + def parse_file(self, filename):
1.122 +
1.123 + "Parse the file with the given 'filename', initialising attributes."
1.124 +
1.125 + self.filename = filename
1.126 + self.astnode = compiler.parseFile(filename)
1.127 +
1.128 + # Module-relative naming.
1.129 +
1.130 + def get_global_path(self, name):
1.131 + return "%s.%s" % (self.name, name)
1.132 +
1.133 + def get_namespace_path(self):
1.134 + return ".".join([self.name] + self.namespace_path)
1.135 +
1.136 + def get_object_path(self, name):
1.137 + return ".".join([self.name] + self.namespace_path + [name])
1.138 +
1.139 + def get_parent_path(self):
1.140 + return ".".join([self.name] + self.namespace_path[:-1])
1.141 +
1.142 + # Namespace management.
1.143 +
1.144 + def enter_namespace(self, name):
1.145 +
1.146 + "Enter the namespace having the given 'name'."
1.147 +
1.148 + self.namespace_path.append(name)
1.149 +
1.150 + def exit_namespace(self):
1.151 +
1.152 + "Exit the current namespace."
1.153 +
1.154 + self.namespace_path.pop()
1.155 +
1.156 + # Constant reference naming.
1.157 +
1.158 + def get_constant_name(self, value):
1.159 +
1.160 + "Add a new constant to the current namespace for 'value'."
1.161 +
1.162 + path = self.get_namespace_path()
1.163 + init_item(self.constants, path, dict)
1.164 + return "$c%d" % add_counter_item(self.constants[path], value)
1.165 +
1.166 + # Literal reference naming.
1.167 +
1.168 + def get_literal_name(self):
1.169 +
1.170 + "Add a new literal to the current namespace."
1.171 +
1.172 + path = self.get_namespace_path()
1.173 + init_item(self.literals, path, lambda: 0)
1.174 + return "$C%d" % self.literals[path]
1.175 +
1.176 + def next_literal(self):
1.177 + self.literals[self.get_namespace_path()] += 1
1.178 +
1.179 + # Temporary iterator naming.
1.180 +
1.181 + def get_iterator_path(self):
1.182 + return self.in_function and self.get_namespace_path() or self.name
1.183 +
1.184 + def get_iterator_name(self):
1.185 + path = self.get_iterator_path()
1.186 + init_item(self.iterators, path, lambda: 0)
1.187 + return "$i%d" % self.iterators[path]
1.188 +
1.189 + def next_iterator(self):
1.190 + self.iterators[self.get_iterator_path()] += 1
1.191 +
1.192 + # Temporary variable naming.
1.193 +
1.194 + def get_temporary_name(self):
1.195 + path = self.get_namespace_path()
1.196 + init_item(self.temp, path, lambda: 0)
1.197 + return "$t%d" % self.temp[path]
1.198 +
1.199 + def next_temporary(self):
1.200 + self.temp[self.get_namespace_path()] += 1
1.201 +
1.202 + # Arbitrary function naming.
1.203 +
1.204 + def get_lambda_name(self):
1.205 + path = self.get_namespace_path()
1.206 + init_item(self.lambdas, path, lambda: 0)
1.207 + name = "$l%d" % self.lambdas[path]
1.208 + self.lambdas[path] += 1
1.209 + return name
1.210 +
1.211 + def reset_lambdas(self):
1.212 + self.lambdas = {}
1.213 +
1.214 + # Constant and literal recording.
1.215 +
1.216 + def get_constant_reference(self, ref, value):
1.217 +
1.218 + "Return a constant reference for the given 'ref' type and 'value'."
1.219 +
1.220 + constant_name = self.get_constant_name(value)
1.221 +
1.222 + # Return a reference for the constant.
1.223 +
1.224 + objpath = self.get_object_path(constant_name)
1.225 + name_ref = ConstantValueRef(constant_name, ref.instance_of(), value)
1.226 +
1.227 + # Record the value and type for the constant.
1.228 +
1.229 + self.constant_values[objpath] = name_ref.value, name_ref.get_origin()
1.230 + return name_ref
1.231 +
1.232 + def get_literal_reference(self, name, ref, items, cls):
1.233 +
1.234 + # Construct an invocation using the items as arguments.
1.235 +
1.236 + typename = "$L%s" % name
1.237 +
1.238 + invocation = compiler.ast.CallFunc(
1.239 + compiler.ast.Name(typename),
1.240 + items
1.241 + )
1.242 +
1.243 + # Get a name for the actual literal.
1.244 +
1.245 + instname = self.get_literal_name()
1.246 + self.next_literal()
1.247 +
1.248 + # Record the type for the literal.
1.249 +
1.250 + objpath = self.get_object_path(instname)
1.251 + self.literal_types[objpath] = ref.get_origin()
1.252 +
1.253 + # Return a wrapper for the invocation exposing the items.
1.254 +
1.255 + return cls(
1.256 + instname,
1.257 + ref.instance_of(),
1.258 + self.process_structure_node(invocation),
1.259 + invocation.args
1.260 + )
1.261 +
1.262 + # Node handling.
1.263 +
1.264 + def process_structure(self, node):
1.265 +
1.266 + """
1.267 + Within the given 'node', process the program structure.
1.268 +
1.269 + During inspection, this will process global declarations, adjusting the
1.270 + module namespace, and import statements, building a module dependency
1.271 + hierarchy.
1.272 +
1.273 + During translation, this will consult deduced program information and
1.274 + output translated code.
1.275 + """
1.276 +
1.277 + l = []
1.278 + for n in node.getChildNodes():
1.279 + l.append(self.process_structure_node(n))
1.280 + return l
1.281 +
1.282 + def process_augassign_node(self, n):
1.283 +
1.284 + "Process the given augmented assignment node 'n'."
1.285 +
1.286 + op = operator_functions[n.op]
1.287 +
1.288 + if isinstance(n.node, compiler.ast.Getattr):
1.289 + target = compiler.ast.AssAttr(n.node.expr, n.node.attrname, "OP_ASSIGN")
1.290 + elif isinstance(n.node, compiler.ast.Name):
1.291 + target = compiler.ast.AssName(n.node.name, "OP_ASSIGN")
1.292 + else:
1.293 + target = n.node
1.294 +
1.295 + assignment = compiler.ast.Assign(
1.296 + [target],
1.297 + compiler.ast.CallFunc(
1.298 + compiler.ast.Name("$op%s" % op),
1.299 + [n.node, n.expr]))
1.300 +
1.301 + return self.process_structure_node(assignment)
1.302 +
1.303 + def process_assignment_for_function(self, original_name, name):
1.304 +
1.305 + """
1.306 + Return an assignment operation making 'original_name' refer to the given
1.307 + 'name'.
1.308 + """
1.309 +
1.310 + assignment = compiler.ast.Assign(
1.311 + [compiler.ast.AssName(original_name, "OP_ASSIGN")],
1.312 + compiler.ast.Name(name)
1.313 + )
1.314 +
1.315 + return self.process_structure_node(assignment)
1.316 +
1.317 + def process_assignment_node_items(self, n, expr):
1.318 +
1.319 + """
1.320 + Process the given assignment node 'n' whose children are to be assigned
1.321 + items of 'expr'.
1.322 + """
1.323 +
1.324 + name_ref = self.process_structure_node(expr)
1.325 +
1.326 + # Either unpack the items and present them directly to each assignment
1.327 + # node.
1.328 +
1.329 + if isinstance(name_ref, LiteralSequenceRef):
1.330 + self.process_literal_sequence_items(n, name_ref)
1.331 +
1.332 + # Or have the assignment nodes access each item via the sequence API.
1.333 +
1.334 + else:
1.335 + self.process_assignment_node_items_by_position(n, expr, name_ref)
1.336 +
1.337 + def process_assignment_node_items_by_position(self, n, expr, name_ref):
1.338 +
1.339 + """
1.340 + Process the given sequence assignment node 'n', converting the node to
1.341 + the separate assignment of each target using positional access on a
1.342 + temporary variable representing the sequence. Use 'expr' as the assigned
1.343 + value and 'name_ref' as the reference providing any existing temporary
1.344 + variable.
1.345 + """
1.346 +
1.347 + assignments = []
1.348 +
1.349 + if isinstance(name_ref, NameRef):
1.350 + temp = name_ref.name
1.351 + else:
1.352 + temp = self.get_temporary_name()
1.353 + self.next_temporary()
1.354 +
1.355 + assignments.append(
1.356 + compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], expr)
1.357 + )
1.358 +
1.359 + for i, node in enumerate(n.nodes):
1.360 + assignments.append(
1.361 + compiler.ast.Assign([node], compiler.ast.Subscript(
1.362 + compiler.ast.Name(temp), "OP_APPLY", [compiler.ast.Const(i)]))
1.363 + )
1.364 +
1.365 + return self.process_structure_node(compiler.ast.Stmt(assignments))
1.366 +
1.367 + def process_literal_sequence_items(self, n, name_ref):
1.368 +
1.369 + """
1.370 + Process the given assignment node 'n', obtaining from the given
1.371 + 'name_ref' the items to be assigned to the assignment targets.
1.372 + """
1.373 +
1.374 + if len(n.nodes) == len(name_ref.items):
1.375 + for node, item in zip(n.nodes, name_ref.items):
1.376 + self.process_assignment_node(node, item)
1.377 + else:
1.378 + raise InspectError("In %s, item assignment needing %d items is given %d items." % (
1.379 + self.get_namespace_path(), len(n.nodes), len(name_ref.items)))
1.380 +
1.381 + def process_compare_node(self, n):
1.382 +
1.383 + """
1.384 + Process the given comparison node 'n', converting an operator sequence
1.385 + from...
1.386 +
1.387 + <expr1> <op1> <expr2> <op2> <expr3>
1.388 +
1.389 + ...to...
1.390 +
1.391 + <op1>(<expr1>, <expr2>) and <op2>(<expr2>, <expr3>)
1.392 + """
1.393 +
1.394 + invocations = []
1.395 + last = n.expr
1.396 +
1.397 + for op, op_node in n.ops:
1.398 + op = operator_functions.get(op)
1.399 +
1.400 + invocations.append(compiler.ast.CallFunc(
1.401 + compiler.ast.Name("$op%s" % op),
1.402 + [last, op_node]))
1.403 +
1.404 + last = op_node
1.405 +
1.406 + if len(invocations) > 1:
1.407 + result = compiler.ast.And(invocations)
1.408 + else:
1.409 + result = invocations[0]
1.410 +
1.411 + return self.process_structure_node(result)
1.412 +
1.413 + def process_dict_node(self, node):
1.414 +
1.415 + """
1.416 + Process the given dictionary 'node', returning a list of (key, value)
1.417 + tuples.
1.418 + """
1.419 +
1.420 + l = []
1.421 + for key, value in node.items:
1.422 + l.append((
1.423 + self.process_structure_node(key),
1.424 + self.process_structure_node(value)))
1.425 + return l
1.426 +
1.427 + def process_for_node(self, n):
1.428 +
1.429 + """
1.430 + Generate attribute accesses for {n.list}.__iter__ and the next method on
1.431 + the iterator, producing a replacement node for the original.
1.432 + """
1.433 +
1.434 + node = compiler.ast.Stmt([
1.435 +
1.436 + # <iterator> = {n.list}.__iter__
1.437 +
1.438 + compiler.ast.Assign(
1.439 + [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")],
1.440 + compiler.ast.CallFunc(
1.441 + compiler.ast.Getattr(n.list, "__iter__"),
1.442 + []
1.443 + )),
1.444 +
1.445 + # try:
1.446 + # while True:
1.447 + # <var>... = <iterator>.next()
1.448 + # ...
1.449 + # except StopIteration:
1.450 + # pass
1.451 +
1.452 + compiler.ast.TryExcept(
1.453 + compiler.ast.While(
1.454 + compiler.ast.Name("True"),
1.455 + compiler.ast.Stmt([
1.456 + compiler.ast.Assign(
1.457 + [n.assign],
1.458 + compiler.ast.CallFunc(
1.459 + compiler.ast.Getattr(compiler.ast.Name(self.get_iterator_name()), "next"),
1.460 + []
1.461 + )),
1.462 + n.body]),
1.463 + None),
1.464 + [(compiler.ast.Name("StopIteration"), None, compiler.ast.Stmt([compiler.ast.Pass()]))],
1.465 + None)
1.466 + ])
1.467 +
1.468 + self.next_iterator()
1.469 + self.process_structure_node(node)
1.470 +
1.471 + def convert_ifexp_node(self, n):
1.472 +
1.473 + """
1.474 + Convert the given if expression node 'n'. An if expression is considered
1.475 + as mapping to a function body containing an if statement as follows:
1.476 +
1.477 + <expr> if <test> else <altexpr>
1.478 +
1.479 + lambda <argnames>:
1.480 + if <test>:
1.481 + return <expr>
1.482 + else:
1.483 + return <altexpr>
1.484 +
1.485 + The <argnames> are populated with defaults after the node has been
1.486 + processed.
1.487 + """
1.488 +
1.489 + node = compiler.ast.Lambda(
1.490 + [], [], 0,
1.491 + compiler.ast.If([
1.492 + (n.test, compiler.ast.Return(n.then))
1.493 + ],
1.494 + compiler.ast.Return(n.else_)
1.495 + ))
1.496 +
1.497 + return node
1.498 +
1.499 + def convert_listcomp_node(self, n):
1.500 +
1.501 + """
1.502 + Convert the given list comprehension node 'n'. A list comprehension is
1.503 + considered as mapping to a function body containing a for loop as
1.504 + follows:
1.505 +
1.506 + [<expr> for <varexpr1> in <list1> if <ifexpr1> for <varexpr2> in <list2> if <ifexpr2> if <ifexpr3>]
1.507 +
1.508 + lambda <argnames>:
1.509 + <result> = []
1.510 + for <varexpr1> in <list1>:
1.511 + if <ifexpr1>:
1.512 + for <varexpr2> in <list2>:
1.513 + if <ifexpr2>:
1.514 + if <ifexpr3>:
1.515 + <result>.append(<expr>)
1.516 + return <result>
1.517 +
1.518 + The <argnames> are populated with defaults after the node has been
1.519 + processed.
1.520 + """
1.521 +
1.522 + temp = "$tr"
1.523 +
1.524 + node = compiler.ast.Lambda(
1.525 + [], [], 0,
1.526 + compiler.ast.Stmt([
1.527 +
1.528 + # <result> = []
1.529 +
1.530 + compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")],
1.531 + compiler.ast.List([])
1.532 + ),
1.533 +
1.534 + # for ...
1.535 +
1.536 + self.convert_listcomp_for_node(n.quals[0], n.quals[1:], n.expr, temp),
1.537 +
1.538 + # return <result>
1.539 +
1.540 + compiler.ast.Return(compiler.ast.Name(temp))
1.541 + ]))
1.542 +
1.543 + return node
1.544 +
1.545 + def convert_listcomp_for_node(self, loop, following_loops, expr, temp):
1.546 +
1.547 + """
1.548 + Return a node representing 'loop', encapsulating 'following_loops' and
1.549 + employing 'expr' in the innermost loop body appending to 'temp'.
1.550 + """
1.551 +
1.552 + if loop.ifs:
1.553 + body = self.convert_listcomp_if_node(loop.ifs[0], loop.ifs[1:], following_loops, expr, temp)
1.554 + elif following_loops:
1.555 + body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp)
1.556 + else:
1.557 + body = self.convert_listcomp_body_node(expr, temp)
1.558 +
1.559 + return compiler.ast.For(loop.assign, loop.list, compiler.ast.Stmt([body]), None)
1.560 +
1.561 + def convert_listcomp_if_node(self, if_, following_ifs, following_loops, expr, temp):
1.562 +
1.563 + """
1.564 + Return a node representing 'if_', encapsulating the 'following_ifs' and
1.565 + 'following_loops' and employing 'expr' in the innermost loop body
1.566 + appending to 'temp'.
1.567 + """
1.568 +
1.569 + if following_ifs:
1.570 + body = self.convert_listcomp_if_node(following_ifs[0], following_ifs[1:], following_loops, expr, temp)
1.571 + elif following_loops:
1.572 + body = self.convert_listcomp_for_node(following_loops[0], following_loops[1:], expr, temp)
1.573 + else:
1.574 + body = self.convert_listcomp_body_node(expr, temp)
1.575 +
1.576 + return compiler.ast.If([(if_.test, compiler.ast.Stmt([body]))], None)
1.577 +
1.578 + def convert_listcomp_body_node(self, expr, temp):
1.579 +
1.580 + "Return a node appending 'expr' to 'temp'."
1.581 +
1.582 + return compiler.ast.Discard(
1.583 + compiler.ast.CallFunc(
1.584 + compiler.ast.Getattr(compiler.ast.Name(temp), "append"), [expr]))
1.585 +
1.586 + def process_literal_sequence_node(self, n, name, ref, cls):
1.587 +
1.588 + """
1.589 + Process the given literal sequence node 'n' as a function invocation,
1.590 + with 'name' indicating the type of the sequence, and 'ref' being a
1.591 + reference to the type. The 'cls' is used to instantiate a suitable name
1.592 + reference.
1.593 + """
1.594 +
1.595 + if name == "dict":
1.596 + items = []
1.597 + for key, value in n.items:
1.598 + items.append(compiler.ast.Tuple([key, value]))
1.599 + else: # name in ("list", "tuple"):
1.600 + items = n.nodes
1.601 +
1.602 + return self.get_literal_reference(name, ref, items, cls)
1.603 +
1.604 + def process_operator_node(self, n):
1.605 +
1.606 + """
1.607 + Process the given operator node 'n' as an operator function invocation.
1.608 + """
1.609 +
1.610 + op = operator_functions[n.__class__.__name__]
1.611 + invocation = compiler.ast.CallFunc(
1.612 + compiler.ast.Name("$op%s" % op),
1.613 + list(n.getChildNodes())
1.614 + )
1.615 + return self.process_structure_node(invocation)
1.616 +
1.617 + def process_slice_node(self, n, expr=None):
1.618 +
1.619 + """
1.620 + Process the given slice node 'n' as an operator function invocation.
1.621 + """
1.622 +
1.623 + op = n.flags == "OP_ASSIGN" and "setslice" or "getslice"
1.624 + invocation = compiler.ast.CallFunc(
1.625 + compiler.ast.Name("$op%s" % op),
1.626 + [n.expr, n.lower or compiler.ast.Name("None"), n.upper or compiler.ast.Name("None")] +
1.627 + (expr and [expr] or [])
1.628 + )
1.629 + return self.process_structure_node(invocation)
1.630 +
1.631 + def process_sliceobj_node(self, n):
1.632 +
1.633 + """
1.634 + Process the given slice object node 'n' as a slice constructor.
1.635 + """
1.636 +
1.637 + op = "slice"
1.638 + invocation = compiler.ast.CallFunc(
1.639 + compiler.ast.Name("$op%s" % op),
1.640 + n.nodes
1.641 + )
1.642 + return self.process_structure_node(invocation)
1.643 +
1.644 + def process_subscript_node(self, n, expr=None):
1.645 +
1.646 + """
1.647 + Process the given subscript node 'n' as an operator function invocation.
1.648 + """
1.649 +
1.650 + op = n.flags == "OP_ASSIGN" and "setitem" or "getitem"
1.651 + invocation = compiler.ast.CallFunc(
1.652 + compiler.ast.Name("$op%s" % op),
1.653 + [n.expr] + list(n.subs) + (expr and [expr] or [])
1.654 + )
1.655 + return self.process_structure_node(invocation)
1.656 +
1.657 + def process_attribute_chain(self, n):
1.658 +
1.659 + """
1.660 + Process the given attribute access node 'n'. Return a reference
1.661 + describing the expression.
1.662 + """
1.663 +
1.664 + # AssAttr/Getattr are nested with the outermost access being the last
1.665 + # access in any chain.
1.666 +
1.667 + self.attrs.insert(0, n.attrname)
1.668 + attrs = self.attrs
1.669 +
1.670 + # Break attribute chains where non-access nodes are found.
1.671 +
1.672 + if not self.have_access_expression(n):
1.673 + self.attrs = []
1.674 +
1.675 + # Descend into the expression, extending backwards any existing chain,
1.676 + # or building another for the expression.
1.677 +
1.678 + name_ref = self.process_structure_node(n.expr)
1.679 +
1.680 + # Restore chain information applying to this node.
1.681 +
1.682 + self.attrs = attrs
1.683 +
1.684 + # Return immediately if the expression was another access and thus a
1.685 + # continuation backwards along the chain. The above processing will
1.686 + # have followed the chain all the way to its conclusion.
1.687 +
1.688 + if self.have_access_expression(n):
1.689 + del self.attrs[0]
1.690 +
1.691 + return name_ref
1.692 +
1.693 + def have_access_expression(self, node):
1.694 +
1.695 + "Return whether the expression associated with 'node' is Getattr."
1.696 +
1.697 + return isinstance(node.expr, compiler.ast.Getattr)
1.698 +
1.699 + def get_name_for_tracking(self, name, path=None):
1.700 +
1.701 + """
1.702 + Return the name to be used for attribute usage observations involving
1.703 + the given 'name' in the current namespace. If 'path' is indicated and
1.704 + the name is being used outside a function, return the path value;
1.705 + otherwise, return a path computed using the current namespace and the
1.706 + given name.
1.707 +
1.708 + The intention of this method is to provide a suitably-qualified name
1.709 + that can be tracked across namespaces. Where globals are being
1.710 + referenced in class namespaces, they should be referenced using their
1.711 + path within the module, not using a path within each class.
1.712 +
1.713 + It may not be possible to identify a global within a function at the
1.714 + time of inspection (since a global may appear later in a file).
1.715 + Consequently, globals are identified by their local name rather than
1.716 + their module-qualified path.
1.717 + """
1.718 +
1.719 + # For functions, use the appropriate local names.
1.720 +
1.721 + if self.in_function:
1.722 + return name
1.723 +
1.724 + # For static namespaces, use the given qualified name.
1.725 +
1.726 + elif path:
1.727 + return path
1.728 +
1.729 + # Otherwise, establish a name in the current (module) namespace.
1.730 +
1.731 + else:
1.732 + return self.get_object_path(name)
1.733 +
1.734 + def get_path_for_access(self):
1.735 +
1.736 + "Outside functions, register accesses at the module level."
1.737 +
1.738 + if not self.in_function:
1.739 + return self.name
1.740 + else:
1.741 + return self.get_namespace_path()
1.742 +
1.743 + def get_module_name(self, node):
1.744 +
1.745 + """
1.746 + Using the given From 'node' in this module, calculate any relative import
1.747 + information, returning a tuple containing a module to import along with any
1.748 + names to import based on the node's name information.
1.749 +
1.750 + Where the returned module is given as None, whole module imports should
1.751 + be performed for the returned modules using the returned names.
1.752 + """
1.753 +
1.754 + # Absolute import.
1.755 +
1.756 + if node.level == 0:
1.757 + return node.modname, node.names
1.758 +
1.759 + # Relative to an ancestor of this module.
1.760 +
1.761 + else:
1.762 + path = self.name.split(".")
1.763 + level = node.level
1.764 +
1.765 + # Relative imports treat package roots as submodules.
1.766 +
1.767 + if split(self.filename)[-1] == "__init__.py":
1.768 + level -= 1
1.769 +
1.770 + if level > len(path):
1.771 + raise InspectError("Relative import %r involves too many levels up from module %r" % (
1.772 + ("%s%s" % ("." * node.level, node.modname or "")), self.name))
1.773 +
1.774 + basename = ".".join(path[:len(path)-level])
1.775 +
1.776 + # Name imports from a module.
1.777 +
1.778 + if node.modname:
1.779 + return "%s.%s" % (basename, node.modname), node.names
1.780 +
1.781 + # Relative whole module imports.
1.782 +
1.783 + else:
1.784 + return basename, node.names
1.785 +
1.786 +def get_argnames(args):
1.787 +
1.788 + """
1.789 + Return a list of all names provided by 'args'. Since tuples may be
1.790 + employed, the arguments are traversed depth-first.
1.791 + """
1.792 +
1.793 + l = []
1.794 + for arg in args:
1.795 + if isinstance(arg, tuple):
1.796 + l += get_argnames(arg)
1.797 + else:
1.798 + l.append(arg)
1.799 + return l
1.800 +
1.801 +# Classes representing inspection and translation observations.
1.802 +
1.803 +class Result:
1.804 +
1.805 + "An abstract expression result."
1.806 +
1.807 + def is_name(self):
1.808 + return False
1.809 + def get_origin(self):
1.810 + return None
1.811 +
1.812 +class NameRef(Result):
1.813 +
1.814 + "A reference to a name."
1.815 +
1.816 + def __init__(self, name, expr=None):
1.817 + self.name = name
1.818 + self.expr = expr
1.819 +
1.820 + def is_name(self):
1.821 + return True
1.822 +
1.823 + def reference(self):
1.824 + return None
1.825 +
1.826 + def final(self):
1.827 + return None
1.828 +
1.829 + def __repr__(self):
1.830 + return "NameRef(%r, %r)" % (self.name, self.expr)
1.831 +
1.832 +class LocalNameRef(NameRef):
1.833 +
1.834 + "A reference to a local name."
1.835 +
1.836 + def __init__(self, name, number):
1.837 + NameRef.__init__(self, name)
1.838 + self.number = number
1.839 +
1.840 + def __repr__(self):
1.841 + return "LocalNameRef(%r, %r)" % (self.name, self.number)
1.842 +
1.843 +class ResolvedNameRef(NameRef):
1.844 +
1.845 + "A resolved name-based reference."
1.846 +
1.847 + def __init__(self, name, ref, expr=None):
1.848 + NameRef.__init__(self, name, expr)
1.849 + self.ref = ref
1.850 +
1.851 + def reference(self):
1.852 + return self.ref
1.853 +
1.854 + def get_name(self):
1.855 + return self.ref and self.ref.get_name() or None
1.856 +
1.857 + def get_origin(self):
1.858 + return self.ref and self.ref.get_origin() or None
1.859 +
1.860 + def static(self):
1.861 + return self.ref and self.ref.static() or None
1.862 +
1.863 + def final(self):
1.864 + return self.ref and self.ref.final() or None
1.865 +
1.866 + def has_kind(self, kinds):
1.867 + return self.ref and self.ref.has_kind(kinds)
1.868 +
1.869 + def __repr__(self):
1.870 + return "ResolvedNameRef(%r, %r, %r)" % (self.name, self.ref, self.expr)
1.871 +
1.872 +class ConstantValueRef(ResolvedNameRef):
1.873 +
1.874 + "A constant reference representing a single literal value."
1.875 +
1.876 + def __init__(self, name, ref, value, number=None):
1.877 + ResolvedNameRef.__init__(self, name, ref)
1.878 + self.value = value
1.879 + self.number = number
1.880 +
1.881 + def __repr__(self):
1.882 + return "ConstantValueRef(%r, %r, %r, %r)" % (self.name, self.ref, self.value, self.number)
1.883 +
1.884 +class InstanceRef(Result):
1.885 +
1.886 + "An instance reference."
1.887 +
1.888 + def __init__(self, ref):
1.889 + self.ref = ref
1.890 +
1.891 + def reference(self):
1.892 + return self.ref
1.893 +
1.894 + def __repr__(self):
1.895 + return "InstanceRef(%r)" % self.ref
1.896 +
1.897 +class LiteralSequenceRef(ResolvedNameRef):
1.898 +
1.899 + "A reference representing a sequence of values."
1.900 +
1.901 + def __init__(self, name, ref, node, items=None):
1.902 + ResolvedNameRef.__init__(self, name, ref)
1.903 + self.node = node
1.904 + self.items = items
1.905 +
1.906 + def __repr__(self):
1.907 + return "LiteralSequenceRef(%r, %r, %r, %r)" % (self.name, self.ref, self.node, self.items)
1.908 +
1.909 +# Dictionary utilities.
1.910 +
1.911 +def init_item(d, key, fn):
1.912 +
1.913 + """
1.914 + Add to 'd' an entry for 'key' using the callable 'fn' to make an initial
1.915 + value where no entry already exists.
1.916 + """
1.917 +
1.918 + if not d.has_key(key):
1.919 + d[key] = fn()
1.920 + return d[key]
1.921 +
1.922 +def dict_for_keys(d, keys):
1.923 +
1.924 + "Return a new dictionary containing entries from 'd' for the given 'keys'."
1.925 +
1.926 + nd = {}
1.927 + for key in keys:
1.928 + if d.has_key(key):
1.929 + nd[key] = d[key]
1.930 + return nd
1.931 +
1.932 +def make_key(s):
1.933 +
1.934 + "Make sequence 's' into a tuple-based key, first sorting its contents."
1.935 +
1.936 + l = list(s)
1.937 + l.sort()
1.938 + return tuple(l)
1.939 +
1.940 +def add_counter_item(d, key):
1.941 +
1.942 + """
1.943 + Make a mapping in 'd' for 'key' to the number of keys added before it, thus
1.944 + maintaining a mapping of keys to their order of insertion.
1.945 + """
1.946 +
1.947 + if not d.has_key(key):
1.948 + d[key] = len(d.keys())
1.949 + return d[key]
1.950 +
1.951 +def remove_items(d1, d2):
1.952 +
1.953 + "Remove from 'd1' all items from 'd2'."
1.954 +
1.955 + for key in d2.keys():
1.956 + if d1.has_key(key):
1.957 + del d1[key]
1.958 +
1.959 +# Set utilities.
1.960 +
1.961 +def first(s):
1.962 + return list(s)[0]
1.963 +
1.964 +def same(s1, s2):
1.965 + return set(s1) == set(s2)
1.966 +
1.967 +# General input/output.
1.968 +
1.969 +def readfile(filename):
1.970 +
1.971 + "Return the contents of 'filename'."
1.972 +
1.973 + f = open(filename)
1.974 + try:
1.975 + return f.read()
1.976 + finally:
1.977 + f.close()
1.978 +
1.979 +def writefile(filename, s):
1.980 +
1.981 + "Write to 'filename' the string 's'."
1.982 +
1.983 + f = open(filename, "w")
1.984 + try:
1.985 + f.write(s)
1.986 + finally:
1.987 + f.close()
1.988 +
1.989 +# General encoding.
1.990 +
1.991 +def sorted_output(x):
1.992 +
1.993 + "Sort sequence 'x' and return a string with commas separating the values."
1.994 +
1.995 + x = map(str, x)
1.996 + x.sort()
1.997 + return ", ".join(x)
1.998 +
1.999 +# Attribute chain decoding.
1.1000 +
1.1001 +def get_attrnames(attrnames):
1.1002 + if attrnames.startswith("#"):
1.1003 + return [attrnames]
1.1004 + else:
1.1005 + return attrnames.split(".")
1.1006 +
1.1007 +def get_attrname_from_location(location):
1.1008 + path, name, attrnames, access = location
1.1009 + return get_attrnames(attrnames)[0]
1.1010 +
1.1011 +# Useful data.
1.1012 +
1.1013 +predefined_constants = "Ellipsis", "False", "None", "NotImplemented", "True"
1.1014 +
1.1015 +operator_functions = {
1.1016 +
1.1017 + # Fundamental operations.
1.1018 +
1.1019 + "is" : "is_",
1.1020 + "is not" : "is_not",
1.1021 +
1.1022 + # Binary operations.
1.1023 +
1.1024 + "in" : "in_",
1.1025 + "not in" : "not_in",
1.1026 + "Add" : "add",
1.1027 + "Bitand" : "and_",
1.1028 + "Bitor" : "or_",
1.1029 + "Bitxor" : "xor",
1.1030 + "Div" : "div",
1.1031 + "FloorDiv" : "floordiv",
1.1032 + "LeftShift" : "lshift",
1.1033 + "Mod" : "mod",
1.1034 + "Mul" : "mul",
1.1035 + "Power" : "pow",
1.1036 + "RightShift" : "rshift",
1.1037 + "Sub" : "sub",
1.1038 +
1.1039 + # Unary operations.
1.1040 +
1.1041 + "Invert" : "invert",
1.1042 + "UnaryAdd" : "pos",
1.1043 + "UnarySub" : "neg",
1.1044 +
1.1045 + # Augmented assignment.
1.1046 +
1.1047 + "+=" : "iadd",
1.1048 + "-=" : "isub",
1.1049 + "*=" : "imul",
1.1050 + "/=" : "idiv",
1.1051 + "//=" : "ifloordiv",
1.1052 + "%=" : "imod",
1.1053 + "**=" : "ipow",
1.1054 + "<<=" : "ilshift",
1.1055 + ">>=" : "irshift",
1.1056 + "&=" : "iand",
1.1057 + "^=" : "ixor",
1.1058 + "|=" : "ior",
1.1059 +
1.1060 + # Comparisons.
1.1061 +
1.1062 + "==" : "eq",
1.1063 + "!=" : "ne",
1.1064 + "<" : "lt",
1.1065 + "<=" : "le",
1.1066 + ">=" : "ge",
1.1067 + ">" : "gt",
1.1068 + }
1.1069 +
1.1070 +# vim: tabstop=4 expandtab shiftwidth=4