def test_get_python_executable_from_pyinstaller(): try: setattr(sys, '_MEIPASS', 'mock') with pytest.raises(RuntimeError) as e: utils.get_python_executable('python') assert 'Executables are not supported' in str(e) finally: delattr(sys, '_MEIPASS')
def test_get_python_executable_from_pyinstaller_with_exec_host(): try: setattr(sys, '_MEIPASS', 'mock') python_path = utils.get_python_executable('python', exec_home=sys.exec_prefix) assert os.path.abspath(sys.exec_prefix) in python_path finally: delattr(sys, '_MEIPASS')
def test_set_version(github, temp_dir, runner): expected_version = '0.0.3' result = github.run('set-version --branch release --value {}'.format(expected_version)) setup_py = github.api.repo.get_contents(path='setup.py', ref='heads/release').decoded_content setup_py_path = os.path.join(temp_dir, 'setup.py') with open(setup_py_path, 'wb') as stream: stream.write(setup_py) actual_version = runner.run('{} {} --version'.format(utils.get_python_executable('python'), setup_py_path)).std_out expected_output = 'Version is now {} (was 0.0.1)'.format(expected_version) assert expected_output in result.std_out assert expected_version == actual_version
def test_bump_version(github, temp_dir, runner): result = github.run('bump-version --branch release --semantic patch') expected_version = '0.0.2' expected_output = 'Bumped version from 0.0.1 to {}'.format(expected_version) setup_py = github.api.repo.get_contents(path='setup.py', ref='heads/release').decoded_content setup_py_path = os.path.join(temp_dir, 'setup.py') with open(setup_py_path, 'wb') as stream: stream.write(setup_py) actual_version = runner.run('{} {} --version'.format(utils.get_python_executable('python'), setup_py_path)).std_out assert expected_output in result.std_out assert expected_version == actual_version
def _lookup_interpreter(self): if utils.is_pyinstaller(): self._debug( 'Running inside a frozen package...Looking up python interpreter from PATH' ) interpreter = utils.which('python') else: self._debug( 'Running inside a python environment...Looking up python interpreter locally' ) interpreter = utils.get_python_executable('python') if not interpreter: raise exceptions.PythonNotFoundException() self._debug('Python interpreter: {}'.format(interpreter)) return interpreter
def _create_virtualenv(self, name): temp_dir = tempfile.mkdtemp() virtualenv_path = os.path.join(temp_dir, name) self._debug('Creating virtualenv {}'.format(virtualenv_path)) def _create_virtualenv_dist(): dist_directory = os.path.join(temp_dir, 'virtualenv-dist') support_directory = os.path.join(dist_directory, 'virtualenv_support') os.makedirs(dist_directory) os.makedirs(support_directory) _virtualenv_py = os.path.join(dist_directory, 'virtualenv.py') def _write_support_wheel(_wheel): with open(os.path.join(support_directory, _wheel), 'wb') as _w: _w.write( get_binary_resource( os.path.join('virtualenv_support', _wheel))) with open(_virtualenv_py, 'w') as venv_py: venv_py.write(get_text_resource('virtualenv.py')) _write_support_wheel('pip-19.1.1-py2.py3-none-any.whl') _write_support_wheel('setuptools-41.0.1-py2.py3-none-any.whl') return _virtualenv_py virtualenv_py = _create_virtualenv_dist() create_virtualenv_command = '{} {} --no-wheel {}'.format( self._interpreter, virtualenv_py, virtualenv_path) requirements_file = os.path.join(self._repo_dir, 'requirements.txt') self._runner.run(create_virtualenv_command, cwd=self._repo_dir) pip_path = utils.get_python_executable('pip', exec_home=virtualenv_path) install_command = None if os.path.exists(requirements_file): self._debug( 'Using requirements file: {}'.format(requirements_file)) install_command = '{} -r {}'.format(self._pip_install(pip_path), requirements_file) elif os.path.exists(self._setup_py_path): self._debug('Using install_requires from setup.py: {}'.format( self._setup_py_path)) requires = self._setup_py.get('install_requires') install_command = '{} {}'.format(self._pip_install(pip_path), ' '.join(requires)) if install_command: self._debug('Installing {} requirements...'.format(name)) self._runner.run(install_command, cwd=self._repo_dir) self._debug( 'Successfully created virtualenv {}'.format(virtualenv_path)) try: yield virtualenv_path finally: try: utils.rmf(temp_dir) except BaseException as e: if utils.is_windows(): # The temp_dir was populated with files written by a different process # (pip install) On windows, this causes a [Error 5] Access is denied error. # Eventually I will have to fix this - until then, sorry windows users... self._debug( "Failed cleaning up temporary directory after creating virtualenv " "{}: {} - You might have some leftovers because of this..." .format(temp_dir, str(e))) else: raise
def wheel(self, universal=False, wheel_version=None): """ Create a wheel package. This method will create a wheel package, according the the regular python wheel standards. Under the hood, this uses the bdist_wheel command provided by the wheel project. For more information please visit https://pythonwheels.com/ Args: universal (:bool, optional): True if the created will should be universal, False otherwise. wheel_version (:str, optional): Which wheel version to use. Raises: WheelExistsException: Destination file already exists. DirectoryDoesntExistException: Destination directory does not exist. """ temp_dir = tempfile.mkdtemp() try: dist_dir = os.path.join(temp_dir, 'dist') bdist_dir = os.path.join(temp_dir, 'bdist') if not os.path.exists(self._setup_py_path): raise exceptions.SetupPyNotFoundException( repo=self._repo_location) name = self._name with self._create_virtualenv(name) as virtualenv: self._logger.debug('Installing wheel...') pip_path = utils.get_python_executable('pip', exec_home=virtualenv) self._runner.run('{} wheel=={}'.format( self._pip_install(pip_path), wheel_version or DEFAULT_WHEEL_VERSION), cwd=self._repo_dir) command = '{} {} bdist_wheel --bdist-dir {} --dist-dir {}'.format( utils.get_python_executable('python', exec_home=virtualenv), self._setup_py_path, bdist_dir, dist_dir) if universal: command = '{0} --universal'.format(command) self._debug('Running bdist_wheel...', universal=universal) self._runner.run(command, cwd=self._repo_dir) self._debug('Finished running bdist_wheel.', universal=universal) actual_name = utils.lsf(dist_dir)[0] destination = os.path.join(self._target_dir, actual_name) try: utils.validate_file_does_not_exist(path=destination) except exceptions.FileExistException as e: raise exceptions.WheelExistsException(path=e.path) shutil.copy(os.path.join(dist_dir, actual_name), destination) self._debug('Packaged successfully.', package=destination) return os.path.abspath(destination) finally: utils.rmf(temp_dir)
def binary(self, base_name=None, entrypoint=None, pyinstaller_version=None): """ Create a binary executable. This method will create a self-contained, platform dependent, executable file. Under the hood, this uses the PyInstaller project. For more information please visit https://www.pyinstaller.org/ Args: base_name (:str, optional): The base name of the target file. The final name will be in the form of: <name>-<platform-machine>-<platform-system> (e.g pyci-x86_64-Darwin). Defaults to the 'name' specified in your setup.py file. entrypoint (:str, optional): Path to a script file from which the executable is built. This can either by a .py or a .spec file. By default, the packager will look for the entry point specified in setup.py. pyinstaller_version (:str, optional): Which PyInstaller version to use. Defaults to 3.4. Raises: BinaryExistsException: Destination file already exists. DirectoryDoesntExistException: Destination directory does not exist. DefaultEntrypointNotFoundException: A custom entrypoint is not provided and the default entry-points pyci looks for are also missing. EntrypointNotFoundException: The provided custom entrypoint provided does not exist in the repository. MultipleDefaultEntrypointsFoundException: If setup.py contains multiple entrypoints specification. """ temp_dir = tempfile.mkdtemp() try: base_name = base_name or self._name entrypoint = entrypoint or self._entrypoint destination = os.path.join( self._target_dir, '{0}-{1}-{2}'.format(base_name, platform.machine(), platform.system())) if platform.system().lower() == 'windows': destination = '{0}.exe'.format(destination) try: utils.validate_file_does_not_exist(path=destination) except exceptions.FileExistException as e: raise exceptions.BinaryExistsException(path=e.path) dist_dir = os.path.join(temp_dir, 'dist') build_dir = os.path.join(temp_dir, 'build') script = os.path.join(self._repo_dir, entrypoint) if not os.path.exists(script): raise exceptions.EntrypointNotFoundException( repo=self._repo_location, entrypoint=entrypoint) with self._create_virtualenv(base_name) as virtualenv: self._logger.debug('Installing pyinstaller...') pip_path = utils.get_python_executable('pip', exec_home=virtualenv) self._runner.run('{} pyinstaller=={}'.format( self._pip_install(pip_path), pyinstaller_version or DEFAULT_PY_INSTALLER_VERSION), cwd=self._repo_dir) self._debug('Running pyinstaller...', entrypoint=entrypoint, destination=destination) pyinstaller_path = utils.get_python_executable( 'pyinstaller', exec_home=virtualenv) self._runner.run('{} ' '--onefile ' '--distpath {} ' '--workpath {} ' '--specpath {} {}'.format( self._pyinstaller(pyinstaller_path), dist_dir, build_dir, temp_dir, script)) self._debug('Finished running pyinstaller', entrypoint=entrypoint, destination=destination) actual_name = utils.lsf(dist_dir)[0] package_path = os.path.join(dist_dir, actual_name) self._debug('Copying package to destination...', src=package_path, dst=destination) shutil.copy(package_path, destination) self._debug('Packaged successfully.', package=destination) return os.path.abspath(destination) finally: utils.rmf(temp_dir)
def test_get_python_executable(): python_path = utils.get_python_executable('python') assert os.path.abspath(sys.exec_prefix) in python_path