def test_system_pip_command(self): plugin = python.PythonPlugin('test-part', self.options, self.project_options) pip_command = os.path.join(os.path.sep, 'usr', 'bin', 'pip3') self.assertEqual(plugin.system_pip_command, pip_command) # NOTE(SamYaple): python2 pip test self.options.python_version = 'python2' plugin = python.PythonPlugin('test-part', self.options, self.project_options) pip_command = os.path.join(os.path.sep, 'usr', 'bin', 'pip') self.assertEqual(plugin.system_pip_command, pip_command)
def test_build(self, mock_base_build, mock_run, mock_run_output): self.options.requirements = 'requirements.txt' self.options.constraints = 'constraints.txt' self.options.python_packages = ['test', 'packages'] class TempDir(tempfile.TemporaryDirectory): def __enter__(self): project_whl_path = os.path.join(self.name, 'project.whl') open(project_whl_path, 'w').close() return super().__enter__() patcher = mock.patch('tempfile.TemporaryDirectory', new=mock.Mock(wraps=TempDir)) patcher.start() self.addCleanup(patcher.stop) mock_run_output.return_value = 'yaml (1.2)\bextras (1.0)' os.environ = {} plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) def build_side_effect(): open(os.path.join(plugin.builddir, 'setup.py'), 'w').close() mock_base_build.side_effect = build_side_effect requirements_path = os.path.join(plugin.sourcedir, 'requirements.txt') constraints_path = os.path.join(plugin.sourcedir, 'constraints.txt') pip_command = [ os.path.join(plugin.installdir, 'usr', 'bin', 'python3'), '-m', 'pip' ] pip_wheel = [ 'wheel', '--disable-pip-version-check', '--no-index', '--find-links', plugin._python_package_dir, '--constraint', constraints_path, '--wheel-dir', mock.ANY ] pip_install = [ 'install', '--user', '--no-compile', '--disable-pip-version-check', '--no-index', '--find-links', plugin._python_package_dir, '--constraint', constraints_path ] calls = [ mock.call( pip_command + pip_wheel + ['--requirement', requirements_path, '.', 'test', 'packages'], cwd=plugin.builddir, env=mock.ANY), mock.call(tests.ContainsList(pip_command + pip_install + ['project.whl']), env=mock.ANY), ] plugin.build() mock_run.assert_has_calls(calls)
def test_process_dependency_links(self): self.options.process_dependency_links = True plugin = python.PythonPlugin("test-part", self.options, self.project) setup_directories(plugin, self.options.python_version) plugin.pull() plugin.build() pip_download = self.mock_pip.return_value.download self.assertThat(pip_download.call_count, Equals(2)) pip_download.assert_has_calls([ mock.call( [], constraints=set(), process_dependency_links=True, requirements=set(), setup_py_dir=None, ), mock.call( [], constraints=set(), process_dependency_links=True, requirements=set(), setup_py_dir=plugin.sourcedir, ), ]) pip_install = self.mock_pip.return_value.install self.assertThat(pip_install.call_count, Equals(2)) pip_install.assert_has_calls([ mock.call([], upgrade=True, process_dependency_links=True, install_deps=False), mock.call([], upgrade=True, process_dependency_links=True, install_deps=False), ]) pip_wheel = self.mock_pip.return_value.wheel self.assertThat(pip_wheel.call_count, Equals(2)) # Double check to avoid annotating the magic methods. pip_wheel.assert_has_calls([ mock.call( [], constraints=set(), process_dependency_links=True, requirements=set(), setup_py_dir=None, ) ]) pip_wheel.assert_has_calls([ mock.call( [], constraints=set(), process_dependency_links=True, requirements=set(), setup_py_dir=plugin.sourcedir, ) ])
def test_constraints_file_missing(self): setattr(self.options, self.property, self.file_path) plugin = python.PythonPlugin("test-part", self.options, self.project_options) setup_directories(plugin, self.options.python_version) self.assertRaises(python.SnapcraftPluginPythonFileMissing, plugin.pull)
def test_plugin_stage_packages_python3(self): self.options.python_version = "python3" plugin = python.PythonPlugin("test-part", self.options, self.project) self.assertThat( plugin.plugin_stage_packages, Equals(["python3", "python3-distutils"]) )
def test_process_dependency_links(self): self.options.process_dependency_links = True plugin = python.PythonPlugin("test-part", self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() plugin.build() pip_download = self.mock_pip.return_value.download pip_download.assert_called_once_with( [], constraints=None, process_dependency_links=True, requirements=None, setup_py_dir=plugin.sourcedir, ) pip_install = self.mock_pip.return_value.install pip_install.assert_called_once_with( [], upgrade=True, process_dependency_links=True, install_deps=False ) pip_wheel = self.mock_pip.return_value.wheel pip_wheel.assert_called_once_with( [], constraints=None, process_dependency_links=True, requirements=None, setup_py_dir=plugin.sourcedir, )
def test_pull_with_nothing(self, mock_run, mock_run_output): plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() mock_run.assert_has_calls([])
def test_process_dependency_links(self, run_mock, mock_run_output): self.options.process_dependency_links = True plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() self.assertIn('--process-dependency-links', run_mock.call_args[0][0])
def test_missing_setup_path(self, mock_path_exists, mock_run, mock_run_output): plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() self.assertFalse(mock_run.called)
def test_no_python_packages_does_nothing(self): # This should be an error but given that we default to # 'source: .' and now that pip 10 has been released # we run into the need of fixing this situation. self.mock_pip.return_value.list.return_value = dict() self.useFixture(fixture_setup.CleanEnvironment()) plugin = python.PythonPlugin("test-part", self.options, self.project_options) setup_directories(plugin, self.options.python_version, create_setup_py=False) pip_wheel = self.mock_pip.return_value.wheel pip_wheel.return_value = [] plugin.build() # Pip should not attempt to download again in build (only pull) pip_download = self.mock_pip.return_value.download pip_download.assert_not_called() pip_wheel.assert_called_once_with( [], constraints=None, process_dependency_links=False, requirements=None, setup_py_dir=None, ) pip_install = self.mock_pip.return_value.install pip_install.assert_not_called()
def test_plugin_stage_packages_python2_bionic(self): self.options.python_version = "python2" self.mock_os_release.return_value.version_codename.return_value = "bionic" plugin = python.PythonPlugin("test-part", self.options, self.project_options) self.assertThat(plugin.plugin_stage_packages, Equals(["python"]))
def test_build_creates_correct_sitecustomize(self, mock_base_build, mock_run, mock_run_output): plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.build() expected_sitecustomize = ( 'import site\n' 'import os\n' '\n' 'snap_dir = os.getenv("SNAP")\n' 'snapcraft_stage_dir = os.getenv("SNAPCRAFT_STAGE")\n' 'snapcraft_part_install = os.getenv("SNAPCRAFT_PART_INSTALL")\n' '\n' 'for d in (snap_dir, snapcraft_stage_dir, ' 'snapcraft_part_install):\n' ' if d:\n' ' site_dir = os.path.join(d, ' '"lib/python3.5/site-packages")\n' ' site.addsitedir(site_dir)\n' '\n' 'if snap_dir:\n' ' site.ENABLE_USER_SITE = False') site_path = glob( os.path.join(plugin.installdir, 'usr', 'lib', 'python*', 'sitecustomize.py'))[0] self.assertThat(site_path, FileContains(expected_sitecustomize))
def test_pull(self, mock_run): self.options.requirements = 'requirements.txt' self.options.constraints = 'constraints.txt' self.options.python_packages = ['test', 'packages'] plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) requirements_path = os.path.join(plugin.sourcedir, 'requirements.txt') constraints_path = os.path.join(plugin.sourcedir, 'constraints.txt') pip_download = [ 'pip', 'download', '--disable-pip-version-check', '--dest', plugin._python_package_dir, '--constraint', constraints_path ] calls = [ mock.call(pip_download + ['--requirement', requirements_path], env=mock.ANY), mock.call(pip_download + ['test', 'packages'], env=mock.ANY), mock.call(pip_download + ['.'], cwd=plugin.sourcedir, env=mock.ANY), ] plugin.pull() mock_run.assert_has_calls(calls)
def test_pip_with_url(self): self.options.requirements = 'https://test.com/requirements.txt' self.options.constraints = 'http://test.com/constraints.txt' plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() plugin.build() pip_download = self.mock_pip.return_value.download pip_download.assert_called_once_with( [], constraints={self.options.constraints}, process_dependency_links=False, requirements={self.options.requirements}, setup_py_dir=plugin.sourcedir) pip_install = self.mock_pip.return_value.install pip_install.assert_called_once_with([], upgrade=True, process_dependency_links=False, install_deps=False) pip_wheel = self.mock_pip.return_value.wheel pip_wheel.assert_called_once_with( [], constraints={self.options.constraints}, process_dependency_links=False, requirements={self.options.requirements}, setup_py_dir=plugin.sourcedir)
def test_pull_without_requirements(self, mock_run): self.options.requirements = '' self.options.constraints = 'constraints.txt' self.options.python_packages = ['test', 'packages'] plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) constraints_path = os.path.join(plugin.sourcedir, 'constraints.txt') pip_command = [ os.path.join(plugin.installdir, 'usr', 'bin', 'python3'), '-m', 'pip' ] pip_download = [ 'download', '--disable-pip-version-check', '--dest', plugin._python_package_dir, '--constraint', constraints_path ] calls = [ mock.call(pip_command + pip_download + ['.', 'test', 'packages'], cwd=plugin.sourcedir, env=mock.ANY), ] plugin.pull() mock_run.assert_has_calls(calls)
def test_pip_with_url(self, mock_run, mock_run_output): self.options.requirements = 'https://test.com/requirements.txt' self.options.constraints = 'http://test.com/constraints.txt' plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) pip_command = [ os.path.join(plugin.installdir, 'usr', 'bin', 'python3'), '-m', 'pip' ] pip_download = [ 'download', '--disable-pip-version-check', '--dest', plugin._python_package_dir, '--constraint', 'http://test.com/constraints.txt' ] calls = [ mock.call( pip_command + pip_download + ['--requirement', 'https://test.com/requirements.txt', '.'], cwd=plugin.sourcedir, env=mock.ANY), ] plugin.pull() mock_run.assert_has_calls(calls)
def test_pip_with_url(self): self.options.requirements = 'https://test.com/requirements.txt' self.options.constraints = 'http://test.com/constraints.txt' plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) # Patch requests so we don't hit the network when the requirements # and constraints files are downloaded to save their contents in the # manifest. with mock.patch('requests.get'): plugin.pull() plugin.build() pip_download = self.mock_pip.return_value.download pip_download.assert_called_once_with( [], constraints={self.options.constraints}, process_dependency_links=False, requirements={self.options.requirements}, setup_py_dir=plugin.sourcedir) pip_install = self.mock_pip.return_value.install pip_install.assert_called_once_with( [], upgrade=True, process_dependency_links=False, install_deps=False) pip_wheel = self.mock_pip.return_value.wheel pip_wheel.assert_called_once_with( [], constraints={self.options.constraints}, process_dependency_links=False, requirements={self.options.requirements}, setup_py_dir=plugin.sourcedir)
def test_plugin_stage_packages_python2_bionic(self): self.options.python_version = 'python2' self.mock_os_release.return_value.version_codename.return_value = ( 'bionic') plugin = python.PythonPlugin('test-part', self.options, self.project_options) self.assertThat(plugin.plugin_stage_packages, Equals(['python']))
def test_env(self): plugin = python.PythonPlugin("test-part", self.options, self.project_options) expected_env = [] env = plugin.env("/testpath") self.assertListEqual(expected_env, env) env_missing_path = plugin.env("/testpath") self.assertTrue("PYTHONPATH=/testpath" not in env_missing_path)
def test_get_manifest_with_constraints_url(self): self.options.constraints = [self.source] plugin = python.PythonPlugin("test-part", self.options, self.project) setup_directories(plugin, self.options.python_version) plugin.build() self.assertThat(plugin.get_manifest()["constraints-contents"], Equals(["Test fake file"]))
def test_clean_pull(self, mock_run): plugin = python.PythonPlugin('test-part', self.options, self.project_options) # Pretend pip downloaded packages os.makedirs(os.path.join(plugin.partdir, 'packages')) plugin.clean_pull() self.assertFalse( os.path.isdir(os.path.join(plugin.partdir, 'packages')))
def test_fileset_ignores(self): plugin = python.PythonPlugin('test-part', self.options, self.project_options) expected_fileset = [ '-bin/pip*', '-bin/easy_install*', '-bin/wheel', '-**/__pycache__', '-**/*.pyc', '-lib/python*/site-packages/*/RECORD' ] fileset = plugin.snap_fileset() self.assertListEqual(expected_fileset, fileset)
def test_build(self, mock_base_build): self.options.requirements = "requirements.txt" self.options.constraints = "constraints.txt" self.options.python_packages = ["test", "packages"] packages = collections.OrderedDict() packages["yaml"] = "1.2" packages["extras"] = "1.0" self.mock_pip.return_value.list.return_value = packages self.useFixture(fixture_setup.CleanEnvironment()) plugin = python.PythonPlugin("test-part", self.options, self.project_options) setup_directories(plugin, self.options.python_version) for file_name in (self.options.requirements, self.options.constraints): path = os.path.join(plugin.sourcedir, file_name) open(path, "w").close() requirements_path = os.path.join(plugin.builddir, "requirements.txt") constraints_path = os.path.join(plugin.builddir, "constraints.txt") def build_side_effect(): open(os.path.join(plugin.builddir, "setup.py"), "w").close() os.mkdir(os.path.join(plugin.builddir, "dist")) open(os.path.join(plugin.builddir, "dist", "package.tar"), "w").close() open(requirements_path, "w").close() open(constraints_path, "w").close() mock_base_build.side_effect = build_side_effect pip_wheel = self.mock_pip.return_value.wheel pip_wheel.return_value = ["foo", "bar"] plugin.build() # Pip should not attempt to download again in build (only pull) pip_download = self.mock_pip.return_value.download pip_download.assert_not_called() pip_wheel.assert_called_once_with( ["test", "packages"], constraints={constraints_path}, process_dependency_links=False, requirements={requirements_path}, setup_py_dir=plugin.builddir, ) pip_install = self.mock_pip.return_value.install pip_install.assert_called_once_with( ["foo", "bar"], process_dependency_links=False, upgrade=True, install_deps=False, )
def test_get_manifest_with_constraints_url(self, _, mock_run_output): self.options.constraints = self.source plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.build() self.assertThat(plugin.get_manifest()['constraints-contents'], Equals('Test fake file'))
def test_get_manifest_with_requirements_url(self): self.options.requirements = self.source plugin = python.PythonPlugin("test-part", self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.build() self.assertThat( plugin.get_manifest()["requirements-contents"], Equals("Test fake file") )
def test_build_fixes_python_shebangs(self, run_mock): if self.options.python_version == 'python2': py_version_short = 'python2' py_version_long = 'python2' elif self.options.python_version == 'python3': py_version_short = 'python3' py_version_long = 'python3.5' plugin = python.PythonPlugin('test-part', self.options, self.project_options) os.makedirs(plugin.sourcedir) open(os.path.join(plugin.sourcedir, 'setup.py'), 'w').close() os.makedirs(os.path.join(plugin.installdir, 'bin')) os.makedirs(os.path.join( plugin.installdir, 'usr', 'lib', py_version_long, 'dist-packages')) os.makedirs(os.path.join( plugin.installdir, 'usr', 'include', py_version_long)) # Place a few files with bad shebangs, and some files that shouldn't be # changed. files = [ { 'path': os.path.join(plugin.installdir, 'example.py'), 'contents': '#!/foo/bar/baz/python', 'expected': '#!/usr/bin/env python', }, { 'path': os.path.join(plugin.installdir, 'bin/another_example'), 'contents': '#!/foo/baz/' + py_version_short, 'expected': '#!/usr/bin/env ' + py_version_short, }, { 'path': os.path.join(plugin.installdir, 'foo'), 'contents': 'foo', 'expected': 'foo', }, { 'path': os.path.join(plugin.installdir, 'bar'), 'contents': 'bar\n#!/usr/bin/python3', 'expected': 'bar\n#!/usr/bin/python3', } ] for file_info in files: with open(file_info['path'], 'w') as f: f.write(file_info['contents']) plugin.build() for file_info in files: with open(os.path.join(plugin.installdir, file_info['path']), 'r') as f: self.assertEqual(f.read(), file_info['expected'])
def test_env(self): plugin = python.PythonPlugin('test-part', self.options, self.project_options) expected_env = [ 'PYTHONUSERBASE=/testpath', 'PYTHONHOME=/testpath/usr', ] env = plugin.env('/testpath') self.assertListEqual(expected_env, env) env_missing_path = plugin.env('/testpath') self.assertTrue('PYTHONPATH=/testpath' not in env_missing_path)
def test_build(self, mock_base_build): self.options.requirements = 'requirements.txt' self.options.constraints = 'constraints.txt' self.options.python_packages = ['test', 'packages'] packages = collections.OrderedDict() packages['yaml'] = '1.2' packages['extras'] = '1.0' self.mock_pip.return_value.list.return_value = packages self.useFixture(fixture_setup.CleanEnvironment()) plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) for file_name in (self.options.requirements, self.options.constraints): path = os.path.join(plugin.sourcedir, file_name) open(path, 'w').close() requirements_path = os.path.join(plugin.builddir, 'requirements.txt') constraints_path = os.path.join(plugin.builddir, 'constraints.txt') def build_side_effect(): open(os.path.join(plugin.builddir, 'setup.py'), 'w').close() os.mkdir(os.path.join(plugin.builddir, 'dist')) open(os.path.join(plugin.builddir, 'dist', 'package.tar'), 'w').close() open(requirements_path, 'w').close() open(constraints_path, 'w').close() mock_base_build.side_effect = build_side_effect pip_wheel = self.mock_pip.return_value.wheel pip_wheel.return_value = ['foo', 'bar'] plugin.build() # Pip should not attempt to download again in build (only pull) pip_download = self.mock_pip.return_value.download pip_download.assert_not_called() pip_wheel.assert_called_once_with(['test', 'packages'], constraints={constraints_path}, process_dependency_links=False, requirements={requirements_path}, setup_py_dir=plugin.builddir) pip_install = self.mock_pip.return_value.install pip_install.assert_called_once_with(['foo', 'bar'], process_dependency_links=False, upgrade=True, install_deps=False)
def test_pull_with_setup_py(self): plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) plugin.pull() pip_download = self.mock_pip.return_value.download pip_download.assert_called_once_with( [], constraints=None, process_dependency_links=False, requirements=None, setup_py_dir=plugin.sourcedir) self.mock_pip.return_value.wheel.assert_not_called() self.mock_pip.return_value.install.assert_not_called()
def test_get_manifest_with_local_requirements(self, _, mock_run_output): self.options.requirements = 'requirements.txt' plugin = python.PythonPlugin('test-part', self.options, self.project_options) setup_directories(plugin, self.options.python_version) requirements_path = os.path.join(plugin.sourcedir, 'requirements.txt') with open(requirements_path, 'w') as requirements_file: requirements_file.write('testpackage1==1.0\n') requirements_file.write('testpackage2==1.2') plugin.build() self.assertThat(plugin.get_manifest()['requirements-contents'], Equals('testpackage1==1.0\ntestpackage2==1.2'))