Changeset 391 for python/trunk/Lib/lib2to3/refactor.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/lib2to3/refactor.py
r2 r391 8 8 provides infrastructure to write your own refactoring tool. 9 9 """ 10 11 from __future__ import with_statement 10 12 11 13 __author__ = "Guido van Rossum <guido@python.org>" … … 23 25 # Local imports 24 26 from .pgen2 import driver, tokenize, token 27 from .fixer_util import find_root 25 28 from . import pytree, pygram 29 from . import btm_utils as bu 30 from . import btm_matcher as bm 26 31 27 32 … … 123 128 124 129 125 def _detect_future_ print(source):130 def _detect_future_features(source): 126 131 have_docstring = False 127 132 gen = tokenize.generate_tokens(StringIO.StringIO(source).readline) 128 133 def advance(): 129 tok = next(gen)134 tok = gen.next() 130 135 return tok[0], tok[1] 131 136 ignore = frozenset((token.NEWLINE, tokenize.NL, token.COMMENT)) 137 features = set() 132 138 try: 133 139 while True: … … 141 147 elif tp == token.NAME and value == u"from": 142 148 tp, value = advance() 143 if tp != token.NAME andvalue != u"__future__":149 if tp != token.NAME or value != u"__future__": 144 150 break 145 151 tp, value = advance() 146 if tp != token.NAME andvalue != u"import":152 if tp != token.NAME or value != u"import": 147 153 break 148 154 tp, value = advance() … … 150 156 tp, value = advance() 151 157 while tp == token.NAME: 152 if value == u"print_function": 153 return True 158 features.add(value) 154 159 tp, value = advance() 155 if tp != token.OP andvalue != u",":160 if tp != token.OP or value != u",": 156 161 break 157 162 tp, value = advance() … … 160 165 except StopIteration: 161 166 pass 162 return False167 return frozenset(features) 163 168 164 169 … … 169 174 class RefactoringTool(object): 170 175 171 _default_options = {"print_function" : False} 176 _default_options = {"print_function" : False, 177 "write_unchanged_files" : False} 172 178 173 179 CLASS_PREFIX = "Fix" # The prefix for fixer classes … … 191 197 else: 192 198 self.grammar = pygram.python_grammar 199 # When this is True, the refactor*() methods will call write_file() for 200 # files processed even if they were not changed during refactoring. If 201 # and only if the refactor method's write parameter was True. 202 self.write_unchanged_files = self.options.get("write_unchanged_files") 193 203 self.errors = [] 194 204 self.logger = logging.getLogger("RefactoringTool") … … 200 210 self.pre_order, self.post_order = self.get_fixers() 201 211 202 self.pre_order_heads = _get_headnode_dict(self.pre_order)203 self.post_order_heads = _get_headnode_dict(self.post_order)204 212 205 213 self.files = [] # List of files that were or should be modified 214 215 self.BM = bm.BottomMatcher() 216 self.bmi_pre_order = [] # Bottom Matcher incompatible fixers 217 self.bmi_post_order = [] 218 219 for fixer in chain(self.post_order, self.pre_order): 220 if fixer.BM_compatible: 221 self.BM.add_fixer(fixer) 222 # remove fixers that will be handled by the bottom-up 223 # matcher 224 elif fixer in self.pre_order: 225 self.bmi_pre_order.append(fixer) 226 elif fixer in self.post_order: 227 self.bmi_post_order.append(fixer) 228 229 self.bmi_pre_order_heads = _get_headnode_dict(self.bmi_pre_order) 230 self.bmi_post_order_heads = _get_headnode_dict(self.bmi_post_order) 231 232 206 233 207 234 def get_fixers(self): … … 267 294 def refactor(self, items, write=False, doctests_only=False): 268 295 """Refactor a list of files and directories.""" 296 269 297 for dir_or_file in items: 270 298 if os.path.isdir(dir_or_file): … … 280 308 Files and subdirectories starting with '.' are skipped. 281 309 """ 310 py_ext = os.extsep + "py" 282 311 for dirpath, dirnames, filenames in os.walk(dir_name): 283 312 self.log_debug("Descending into %s", dirpath) … … 285 314 filenames.sort() 286 315 for name in filenames: 287 if not name.startswith(".") and \288 os.path.splitext(name)[1].endswith("py"):316 if (not name.startswith(".") and 317 os.path.splitext(name)[1] == py_ext): 289 318 fullname = os.path.join(dirpath, name) 290 319 self.refactor_file(fullname, write, doctests_only) … … 298 327 try: 299 328 f = open(filename, "rb") 300 except IOError ,err:329 except IOError as err: 301 330 self.log_error("Can't open %s: %s", filename, err) 302 331 return None, None … … 318 347 self.log_debug("Refactoring doctests in %s", filename) 319 348 output = self.refactor_docstring(input, filename) 320 if output != input:349 if self.write_unchanged_files or output != input: 321 350 self.processed_file(output, filename, input, write, encoding) 322 351 else: … … 324 353 else: 325 354 tree = self.refactor_string(input, filename) 326 if tree and tree.was_changed:355 if self.write_unchanged_files or (tree and tree.was_changed): 327 356 # The [:-1] is to take off the \n we added earlier 328 357 self.processed_file(unicode(tree)[:-1], filename, … … 342 371 there were errors during the parse. 343 372 """ 344 if _detect_future_print(data): 373 features = _detect_future_features(data) 374 if "print_function" in features: 345 375 self.driver.grammar = pygram.python_grammar_no_print_statement 346 376 try: 347 377 tree = self.driver.parse_string(data) 348 except Exception ,err:378 except Exception as err: 349 379 self.log_error("Can't parse %s: %s: %s", 350 380 name, err.__class__.__name__, err) … … 352 382 finally: 353 383 self.driver.grammar = self.grammar 384 tree.future_features = features 354 385 self.log_debug("Refactoring %s", name) 355 386 self.refactor_tree(tree, name) … … 361 392 self.log_debug("Refactoring doctests in stdin") 362 393 output = self.refactor_docstring(input, "<stdin>") 363 if output != input:394 if self.write_unchanged_files or output != input: 364 395 self.processed_file(output, "<stdin>", input) 365 396 else: … … 367 398 else: 368 399 tree = self.refactor_string(input, "<stdin>") 369 if tree and tree.was_changed:400 if self.write_unchanged_files or (tree and tree.was_changed): 370 401 self.processed_file(unicode(tree), "<stdin>", input) 371 402 else: … … 374 405 def refactor_tree(self, tree, name): 375 406 """Refactors a parse tree (modifying the tree in place). 407 408 For compatible patterns the bottom matcher module is 409 used. Otherwise the tree is traversed node-to-node for 410 matches. 376 411 377 412 Args: … … 383 418 True if the tree was modified, False otherwise. 384 419 """ 420 385 421 for fixer in chain(self.pre_order, self.post_order): 386 422 fixer.start_tree(tree, name) 387 423 388 self.traverse_by(self.pre_order_heads, tree.pre_order()) 389 self.traverse_by(self.post_order_heads, tree.post_order()) 424 #use traditional matching for the incompatible fixers 425 self.traverse_by(self.bmi_pre_order_heads, tree.pre_order()) 426 self.traverse_by(self.bmi_post_order_heads, tree.post_order()) 427 428 # obtain a set of candidate nodes 429 match_set = self.BM.run(tree.leaves()) 430 431 while any(match_set.values()): 432 for fixer in self.BM.fixers: 433 if fixer in match_set and match_set[fixer]: 434 #sort by depth; apply fixers from bottom(of the AST) to top 435 match_set[fixer].sort(key=pytree.Base.depth, reverse=True) 436 437 if fixer.keep_line_order: 438 #some fixers(eg fix_imports) must be applied 439 #with the original file's line order 440 match_set[fixer].sort(key=pytree.Base.get_lineno) 441 442 for node in list(match_set[fixer]): 443 if node in match_set[fixer]: 444 match_set[fixer].remove(node) 445 446 try: 447 find_root(node) 448 except ValueError: 449 # this node has been cut off from a 450 # previous transformation ; skip 451 continue 452 453 if node.fixers_applied and fixer in node.fixers_applied: 454 # do not apply the same fixer again 455 continue 456 457 results = fixer.match(node) 458 459 if results: 460 new = fixer.transform(node, results) 461 if new is not None: 462 node.replace(new) 463 #new.fixers_applied.append(fixer) 464 for node in new.post_order(): 465 # do not apply the fixer again to 466 # this or any subnode 467 if not node.fixers_applied: 468 node.fixers_applied = [] 469 node.fixers_applied.append(fixer) 470 471 # update the original match set for 472 # the added code 473 new_matches = self.BM.run(new.leaves()) 474 for fxr in new_matches: 475 if not fxr in match_set: 476 match_set[fxr]=[] 477 478 match_set[fxr].extend(new_matches[fxr]) 390 479 391 480 for fixer in chain(self.pre_order, self.post_order): … … 419 508 encoding=None): 420 509 """ 421 Called when a file has been refactored , and there are changes.510 Called when a file has been refactored and there may be changes. 422 511 """ 423 512 self.files.append(filename) … … 430 519 if equal: 431 520 self.log_debug("No changes to %s", filename) 432 return 521 if not self.write_unchanged_files: 522 return 433 523 if write: 434 524 self.write_file(new_text, filename, old_text, encoding) … … 445 535 try: 446 536 f = _open_with_encoding(filename, "w", encoding=encoding) 447 except os.error ,err:537 except os.error as err: 448 538 self.log_error("Can't create %s: %s", filename, err) 449 539 return 450 540 try: 451 541 f.write(_to_system_newlines(new_text)) 452 except os.error ,err:542 except os.error as err: 453 543 self.log_error("Can't write %s: %s", filename, err) 454 544 finally: … … 513 603 try: 514 604 tree = self.parse_block(block, lineno, indent) 515 except Exception ,err:516 if self.log .isEnabledFor(logging.DEBUG):605 except Exception as err: 606 if self.logger.isEnabledFor(logging.DEBUG): 517 607 for line in block: 518 608 self.log_debug("Source: %s", line.rstrip(u"\n")) … … 561 651 in the parser diagnostics and embedded into the parse tree. 562 652 """ 563 return self.driver.parse_tokens(self.wrap_toks(block, lineno, indent)) 653 tree = self.driver.parse_tokens(self.wrap_toks(block, lineno, indent)) 654 tree.future_features = frozenset() 655 return tree 564 656 565 657 def wrap_toks(self, block, lineno, indent): … … 606 698 super(MultiprocessRefactoringTool, self).__init__(*args, **kwargs) 607 699 self.queue = None 700 self.output_lock = None 608 701 609 702 def refactor(self, items, write=False, doctests_only=False, … … 619 712 raise RuntimeError("already doing multiple processes") 620 713 self.queue = multiprocessing.JoinableQueue() 714 self.output_lock = multiprocessing.Lock() 621 715 processes = [multiprocessing.Process(target=self._child) 622 716 for i in xrange(num_processes)]
Note:
See TracChangeset
for help on using the changeset viewer.