def exec_transform_with_prettyprint( transform: Codemod, code: str, *, include_generated: bool = False, generated_code_marker: str = _DEFAULT_GENERATED_CODE_MARKER, format_code: bool = False, formatter_args: Sequence[str] = (), python_version: Optional[str] = None, ) -> Optional[str]: """ Given an instantiated codemod and a string representing a module, transform that code by executing the transform, optionally invoking the formatter and finally printing any generated warnings to stderr. If the code includes the generated marker at any spot and ``include_generated`` is not set to ``True``, the code will not be modified. If ``format_code`` is set to ``False`` or the instantiated codemod does not modify the code, the code will not be formatted. If a ``python_version`` is provided, then we will parse the module using this version. Otherwise, we will use the version of the currently executing python binary. In all cases a module will be returned. Whether it is changed depends on the input parameters as well as the codemod itself. """ if not include_generated and generated_code_marker in code: print( "WARNING: Code is generated and we are set to ignore generated code, " + "skipping!", file=sys.stderr, ) return code result = transform_module(transform, code, python_version=python_version) code: Optional[str] = (None if isinstance( result, (TransformFailure, TransformExit, TransformSkip)) else result.code) if code is not None and format_code: try: code = invoke_formatter(formatter_args, code) except Exception as ex: # Failed to format code, treat as a failure and make sure that # we print the exception for debugging. code = None result = TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=result.warning_messages, ) # Finally, print the output, regardless of what happened print_execution_result(result) return code
def exec_transform_with_prettyprint( transform: Codemod, code: str, *, include_generated: bool = False, generated_code_marker: str = _DEFAULT_GENERATED_CODE_MARKER, format_code: bool = False, formatter_args: Sequence[str] = (), ) -> Optional[str]: """ Given an instantiated transform, and a code string, transform that code string by executing the transform, and then print any generated warnings to the screen. """ if not include_generated and generated_code_marker in code: print( "WARNING: Code is generated and we are set to ignore generated code, " + "skipping!", file=sys.stderr, ) return code result = transform_module(transform, code) code: Optional[str] = None if isinstance( result, (TransformFailure, TransformExit, TransformSkip) ) else result.code if code is not None and format_code: try: code = invoke_formatter(formatter_args, code) except Exception as ex: # Failed to format code, treat as a failure and make sure that # we print the exception for debugging. code = None result = TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=result.warning_messages, ) # Finally, print the output, regardless of what happened print_execution_result(result) return code
def _execute_transform( # noqa: C901 transformer: Codemod, filename: str, config: ExecutionConfig, ) -> ExecutionResult: for pattern in config.blacklist_patterns: if re.fullmatch(pattern, filename): return ExecutionResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.BLACKLISTED, skip_description=f"Blacklisted by pattern {pattern}.", ), ) try: with open(filename, "rb") as fp: oldcode = fp.read() # Skip generated files if (not config.include_generated and config.generated_code_marker.encode("utf-8") in oldcode): return ExecutionResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.GENERATED, skip_description="Generated file.", ), ) # Somewhat gross hack to provide the filename in the transform's context. # We do this after the fork so that a context that was initialized with # some defaults before calling parallel_exec_transform_with_prettyprint # will be updated per-file. transformer.context = replace( transformer.context, filename=filename, full_module_name=_calculate_module(config.repo_root, filename), ) # Run the transform, bail if we failed or if we aren't formatting code try: input_tree = parse_module( oldcode, config=(PartialParserConfig( python_version=str(config.python_version)) if config.python_version is not None else PartialParserConfig()), ) output_tree = transformer.transform_module(input_tree) newcode = output_tree.bytes encoding = output_tree.encoding except KeyboardInterrupt: return ExecutionResult(filename=filename, changed=False, transform_result=TransformExit()) except SkipFile as ex: return ExecutionResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.OTHER, skip_description=str(ex), warning_messages=transformer.context.warnings, ), ) except Exception as ex: return ExecutionResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), ) # Call formatter if needed, but only if we actually changed something in this # file if config.format_code and newcode != oldcode: try: newcode = invoke_formatter(config.formatter_args, newcode) except KeyboardInterrupt: return ExecutionResult( filename=filename, changed=False, transform_result=TransformExit(), ) except Exception as ex: return ExecutionResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), ) # Format as unified diff if needed, otherwise save it back changed = oldcode != newcode if config.unified_diff: newcode = diff_code( oldcode.decode(encoding), newcode.decode(encoding), config.unified_diff, filename=filename, ) else: # Write back if we changed if changed: with open(filename, "wb") as fp: fp.write(newcode) # Not strictly necessary, but saves space in pickle since we won't use it newcode = "" # Inform success return ExecutionResult( filename=filename, changed=changed, transform_result=TransformSuccess( warning_messages=transformer.context.warnings, code=newcode), ) except KeyboardInterrupt: return ExecutionResult(filename=filename, changed=False, transform_result=TransformExit()) except Exception as ex: return ExecutionResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), )
def _parallel_exec_process_stub( # noqa: C901 result_queue: "Queue[ParallelExecResult]", transformer: Codemod, filename: str, unified_diff: Optional[int], include_generated: bool, generated_code_marker: str, format_code: bool, formatter_args: Sequence[str], blacklist_patterns: Sequence[str], ) -> None: for pattern in blacklist_patterns: if re.fullmatch(pattern, filename): result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.BLACKLISTED, skip_description=f"Blacklisted by pattern {pattern}.", ), ) ) return try: with open(filename, "rb") as fp: oldcode = fp.read() # Skip generated files if not include_generated and generated_code_marker.encode("utf-8") in oldcode: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.GENERATED, skip_description="Generated file.", ), ) ) return # Somewhat gross hack to provide the filename in the transform's context. # We do this after the fork so that a context that was initialized with # some defaults before calling parallel_exec_transform_with_prettyprint # will be updated per-file. transformer.context = replace(transformer.context, filename=filename) # Run the transform, bail if we failed or if we aren't formatting code try: input_tree = parse_module(oldcode) output_tree = transformer.transform_module(input_tree) newcode = output_tree.bytes encoding = output_tree.encoding except KeyboardInterrupt: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformExit() ) ) return except SkipFile as ex: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformSkip( skip_reason=SkipReason.OTHER, skip_description=str(ex), warning_messages=transformer.context.warnings, ), ) ) return except Exception as ex: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), ) ) return # Call formatter if needed, but only if we actually changed something in this # file if format_code and newcode != oldcode: try: newcode = invoke_formatter(formatter_args, newcode) except KeyboardInterrupt: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformExit(), ) ) return except Exception as ex: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), ) ) return # Format as unified diff if needed, otherwise save it back changed = oldcode != newcode if unified_diff: newcode = diff_code( oldcode.decode(encoding), newcode.decode(encoding), unified_diff, filename=filename, ) else: # Write back if we changed if changed: with open(filename, "wb") as fp: fp.write(newcode) # Not strictly necessary, but saves space in pickle since we won't use it newcode = "" # Inform success result_queue.put( ParallelExecResult( filename=filename, changed=changed, transform_result=TransformSuccess( warning_messages=transformer.context.warnings, code=newcode ), ) ) except KeyboardInterrupt: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformExit() ) ) except Exception as ex: result_queue.put( ParallelExecResult( filename=filename, changed=False, transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), warning_messages=transformer.context.warnings, ), ) )