def _store_python_exe(self, python_exe): """Postprocess --python_exe.""" if python_exe is None and utils.can_compile_bytecode_natively( self.output_options.python_version): # The user has not requested a custom exe and pytype does not need an exe # for bytecode compilation. Abort early to avoid extracting a large unused # exe into /tmp. self.output_options.python_exe = (None, None) return if python_exe is None: python_exe, flags = utils.get_python_exe( self.output_options.python_version) user_provided_exe = False else: if isinstance(python_exe, tuple): python_exe, flags = python_exe else: flags = [] user_provided_exe = True python_exe_version = utils.get_python_exe_version(python_exe) if python_exe_version != self.output_options.python_version: if not user_provided_exe: err = ("Need a valid python%d.%d executable in $PATH" % self.output_options.python_version) elif python_exe_version: err = ("--python_exe version %d.%d does not match " "--python_version %d.%d" % ( python_exe_version + self.output_options.python_version)) else: err = "Bad flag --python_exe: could not run %s" % python_exe self.error(err) self.output_options.python_exe = (python_exe, flags)
def _store_python_version(self, python_version): """Configure the python version.""" if python_version: if isinstance(python_version, str): version = utils.version_from_string(python_version) else: version = python_version else: version = sys.version_info[:2] if len(version) != 2: self.error("--python_version must be <major>.<minor>: %r" % python_version) # Check that we have a version supported by pytype. utils.validate_version(version) self.output_options.python_version = version if utils.can_compile_bytecode_natively( self.output_options.python_version): # pytype does not need an exe for bytecode compilation. Abort early to # avoid extracting a large unused exe into /tmp. self.output_options.python_exe = None return for exe in utils.get_python_exes(self.output_options.python_version): exe_version = utils.get_python_exe_version(exe) if exe_version == self.output_options.python_version: self.output_options.python_exe = exe break else: self.error("Need a valid python%d.%d executable in $PATH" % self.output_options.python_version)
def compile_src_string_to_pyc_string(src, filename, python_version, python_exe: Tuple[List[str], List[str]], mode="exec"): """Compile Python source code to pyc data. This may use py_compile if the src is for the same version as we're running, or else it spawns an external process to produce a .pyc file. The generated bytecode (.pyc file) is read and both it and any temporary files are deleted. Args: src: Python sourcecode filename: Name of the source file. For error messages. python_version: Python version, (major, minor). python_exe: Tuple of a path to a Python interpreter and command-line flags. mode: Same as __builtin__.compile: "exec" if source consists of a sequence of statements, "eval" if it consists of a single expression, or "single" if it consists of a single interactive statement. Returns: The compiled pyc file as a binary string. Raises: CompileError: If we find a syntax error in the file. IOError: If our compile script failed. """ if utils.can_compile_bytecode_natively(python_version): output = six.BytesIO() compile_bytecode.compile_src_to_pyc(src, filename or "<>", output, mode) bytecode = output.getvalue() else: tempfile_options = {"mode": "w", "suffix": ".py", "delete": False} if six.PY3: tempfile_options.update({"encoding": "utf-8"}) else: tempfile_options.update({"mode": "wb"}) fi = tempfile.NamedTemporaryFile(**tempfile_options) try: if six.PY3: fi.write(src) else: fi.write(src.encode("utf-8")) fi.close() # In order to be able to compile pyc files for a different Python version # from the one we're running under, we spawn an external process. # We pass -E to ignore the environment so that PYTHONPATH and # sitecustomize on some people's systems don't mess with the interpreter. exe, flags = python_exe cmd = exe + flags + ["-E", "-", fi.name, filename or fi.name, mode] compile_script_src = pytype_source_utils.load_pytype_file( COMPILE_SCRIPT) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) bytecode, _ = p.communicate(compile_script_src) assert p.poll() == 0, "Child process failed" finally: os.unlink(fi.name) first_byte = six.indexbytes(bytecode, 0) if first_byte == 0: # compile OK return bytecode[1:] elif first_byte == 1: # compile error code = bytecode[1:] # type: bytes raise CompileError(compat.native_str(code)) else: raise IOError("_compile.py produced invalid result")