Merge lp:~sseman/juju-ci-tools/py3-pkg into lp:juju-ci-tools

Proposed by Seman
Status: Merged
Merged at revision: 1837
Proposed branch: lp:~sseman/juju-ci-tools/py3-pkg
Merge into: lp:juju-ci-tools
Diff against target: 240 lines (+120/-9)
4 files modified
Makefile (+1/-1)
pipdeps.py (+59/-7)
requirements_py3.txt (+2/-1)
tests/test_pipdeps.py (+58/-0)
To merge this branch: bzr merge lp:~sseman/juju-ci-tools/py3-pkg
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+314370@code.launchpad.net

Description of the change

This branch updates pipdeps.py so that when installing packages, it also installs packages needed for Python3 from the requirements_py3.txt file.

Currently, the main package needed for Python 3 is https://pypi.python.org/pypi/juju/0.1.2. I have uploaded this package and the needed dependencies to S3 pip archive bucket. Before installing the packages for Python 3, first it checked if the machine have Python 3 (ver 3.5 or above) and pip3 installed. If they are not installed, it skips installing the packages.

Also, if pipdeps.py is running inside virtualenv, the "--user" option for pip causes an issue. I have added a code to detect and remove "--user" option when running within virtualenv.

Finally, I updated Makefile to run pipdeps.py in verbose mode. When I was having issues, I had to manually run the command to see why pip was failing.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you. I wonder how precise will handle this given it does have an old version of py3 installed.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-12-06 04:54:17 +0000
3+++ Makefile 2017-01-09 20:34:36 +0000
4@@ -20,7 +20,7 @@
5 install-deps: juju-ci-tools.common_0.1.4-0_all.deb apt-update
6 sudo dpkg -i juju-ci-tools.common_0.1.4-0_all.deb || true
7 sudo apt-get install -y -f
8- ./pipdeps.py install
9+ ./pipdeps.py -v install
10 name=NAMEHERE
11 assess_file=assess_$(name).py
12 test_assess_file=tests/test_assess_$(name).py
13
14=== modified file 'pipdeps.py'
15--- pipdeps.py 2016-06-23 02:50:43 +0000
16+++ pipdeps.py 2017-01-09 20:34:36 +0000
17@@ -4,6 +4,7 @@
18 from __future__ import print_function
19
20 import argparse
21+from distutils.version import StrictVersion
22 import os
23 import platform
24 import subprocess
25@@ -19,16 +20,23 @@
26 PREFIX = "juju-ci-tools/"
27 REQUIREMENTS = os.path.join(os.path.realpath(os.path.dirname(__file__)),
28 "requirements.txt")
29+REQUIREMENTS_PY3 = os.path.join(os.path.realpath(os.path.dirname(__file__)),
30+ "requirements_py3.txt")
31 MAC_WIN_REQS = os.path.join(os.path.realpath(os.path.dirname(__file__)),
32 "mac-win-requirements.txt")
33 OBSOLETE = os.path.join(os.path.realpath(os.path.dirname(__file__)),
34 "obsolete-requirements.txt")
35
36
37-def get_requirements():
38+def get_requirements(python3=False):
39+ """Return requirements file path."""
40 if platform.dist()[0] in ('Ubuntu', 'debian'):
41+ if python3:
42+ return REQUIREMENTS_PY3
43 return REQUIREMENTS
44 else:
45+ if python3:
46+ return None
47 return MAC_WIN_REQS
48
49
50@@ -50,9 +58,34 @@
51 return boto.s3.connection.S3Connection(access_key, secret_key)
52
53
54-def run_pip_install(extra_args, requirements, verbose=False):
55+def is_py3_supported():
56+ """Determine if Python3 and pip3 are installed."""
57+ try:
58+ version = subprocess.check_output(
59+ ['python3', '--version'], stderr=subprocess.STDOUT)
60+ except OSError:
61+ return False
62+ version = version.strip().split()[-1]
63+ if StrictVersion(version) < StrictVersion('3.5'):
64+ return False
65+ try:
66+ subprocess.check_call(['pip3', '--version'])
67+ except OSError:
68+ return False
69+ return True
70+
71+
72+def run_pip3_install(extra_args, requirements, verbose=False):
73+ if requirements and is_py3_supported():
74+ run_pip_install(extra_args, requirements, verbose=verbose,
75+ cmd=['pip3'])
76+ else:
77+ print('Python 3 is not installed.')
78+
79+
80+def run_pip_install(extra_args, requirements, verbose=False, cmd=None):
81 """Run pip install in a subprocess with given additional arguments."""
82- cmd = ["pip"]
83+ cmd = cmd or ['pip']
84 if not verbose:
85 cmd.append("-q")
86 cmd.extend(["install", "-r", requirements])
87@@ -85,15 +118,29 @@
88 subprocess.check_call(uninstall_cmd)
89
90
91-def command_install(bucket, requirements, verbose=False):
92+def get_pip_args(archives_url):
93+ args = ["--no-index", "--find-links", archives_url]
94+ # --user option is invalid when running inside virtualenv. Check
95+ # sys.real_prefix to determine if it is executing inside virtualenv.
96+ # Inside virtualenv, the sys.prefix points to the virtualenv directory
97+ # and sys.real_prefix points to the real prefix.
98+ # Running outside a virtualenv, sys should not have real_prefix attribute.
99+ if not hasattr(sys, 'real_prefix'):
100+ args.append("--user")
101+ return args
102+
103+
104+def command_install(bucket, requirements, verbose=False,
105+ requirements_py3=None):
106 with utility.temp_dir() as archives_dir:
107 for key in bucket.list(prefix=PREFIX):
108 archive = key.name[len(PREFIX):]
109 key.get_contents_to_filename(os.path.join(archives_dir, archive))
110 archives_url = "file://" + archives_dir
111+ pip_args = get_pip_args(archives_url)
112 run_pip_uninstall(OBSOLETE)
113- run_pip_install(["--user", "--no-index", "--find-links", archives_url],
114- requirements, verbose=verbose)
115+ run_pip_install(pip_args, requirements, verbose=verbose)
116+ run_pip3_install(pip_args, requirements_py3, verbose=verbose)
117
118
119 def command_update(s3, requirements, verbose=False):
120@@ -136,6 +183,10 @@
121 parser.add_argument(
122 "--requirements", default=get_requirements(), type=os.path.expanduser,
123 help="Location requirements file to use.")
124+ parser.add_argument(
125+ "--requirements_py3", default=get_requirements(python3=True),
126+ type=os.path.expanduser,
127+ help="Location requirements file to use for Python 3.")
128 subparsers = parser.add_subparsers(dest="command")
129 subparsers.add_parser("install", help="Download deps from S3 and install.")
130 subparsers.add_parser(
131@@ -157,7 +208,8 @@
132 else:
133 bucket = s3.get_bucket(BUCKET)
134 if args.command == "install":
135- command_install(bucket, args.requirements, args.verbose)
136+ command_install(
137+ bucket, args.requirements, args.verbose, args.requirements_py3)
138 elif args.command == "list":
139 command_list(bucket, args.verbose)
140 elif args.command == "delete":
141
142=== modified file 'requirements_py3.txt'
143--- requirements_py3.txt 2016-12-10 00:47:37 +0000
144+++ requirements_py3.txt 2017-01-09 20:34:36 +0000
145@@ -1,1 +1,2 @@
146-juju>=0.0.1
147\ No newline at end of file
148+juju>=0.1.2
149+
150
151=== modified file 'tests/test_pipdeps.py'
152--- tests/test_pipdeps.py 2016-06-23 02:50:43 +0000
153+++ tests/test_pipdeps.py 2017-01-09 20:34:36 +0000
154@@ -2,10 +2,12 @@
155
156 import mock
157 import os
158+import subprocess
159 import unittest
160
161
162 import pipdeps
163+import tests
164 import utility
165
166
167@@ -69,6 +71,10 @@
168 os.path.realpath(os.path.dirname(pipdeps.__file__)),
169 "requirements.txt")
170
171+ req_path_py3 = os.path.join(
172+ os.path.realpath(os.path.dirname(pipdeps.__file__)),
173+ "requirements_py3.txt")
174+
175 def test_added_args(self):
176 with mock.patch("subprocess.check_call", autospec=True) as cc_mock:
177 pipdeps.run_pip_install(["--user"], self.req_path)
178@@ -82,6 +88,18 @@
179 cc_mock.assert_called_once_with([
180 "pip", "install", "-r", self.req_path, "--download", "/tmp/pip"])
181
182+ def test_pip3_install(self):
183+ with mock.patch("subprocess.check_output", autospec=True,
184+ return_value='3.5.0') as co_mock:
185+ with mock.patch("subprocess.check_call", autospec=True) as cc_mock:
186+ pipdeps.run_pip3_install(["--user"], self.req_path_py3)
187+ co_mock.assert_called_once_with(
188+ ['python3', '--version'], stderr=subprocess.STDOUT)
189+ calls = [mock.call(['pip3', '--version']),
190+ mock.call(['pip3', '-q', 'install', '-r', self.req_path_py3,
191+ '--user'])]
192+ self.assertEqual(cc_mock.call_args_list, calls)
193+
194
195 class TestRunPipUninstall(unittest.TestCase):
196
197@@ -117,3 +135,43 @@
198 self.assertEqual(pipdeps.MAC_WIN_REQS, pipdeps.get_requirements())
199
200 self.assertEqual(pipdeps.MAC_WIN_REQS, pipdeps.get_requirements())
201+
202+
203+class TestIsPy3Supported(tests.TestCase):
204+
205+ def test_is_py3_supported(self):
206+ with mock.patch("subprocess.check_output", autospec=True,
207+ return_value='3.5.0') as co_mock:
208+ with mock.patch("subprocess.check_call", autospec=True) as cc_mock:
209+ self.assertTrue(pipdeps.is_py3_supported())
210+ co_mock.assert_called_once_with(
211+ ['python3', '--version'], stderr=subprocess.STDOUT)
212+ cc_mock.assert_called_once_with(['pip3', '--version'])
213+
214+ def test_is_py3_supported_older_python3_version(self):
215+ with mock.patch("subprocess.check_output", autospec=True,
216+ return_value='3.4') as co_mock:
217+ with mock.patch("subprocess.check_call", autospec=True) as cc_mock:
218+ self.assertFalse(pipdeps.is_py3_supported())
219+ co_mock.assert_called_once_with(
220+ ['python3', '--version'], stderr=subprocess.STDOUT)
221+ self.assertEqual(cc_mock.call_count, 0)
222+
223+ def test_is_pyt3_supported_python3_not_installed(self):
224+ with mock.patch("subprocess.check_output", autospec=True,
225+ side_effect=OSError(2, 'No such file')) as co_mock:
226+ with mock.patch("subprocess.check_call", autospec=True) as cc_mock:
227+ self.assertFalse(pipdeps.is_py3_supported())
228+ co_mock.assert_called_once_with(
229+ ['python3', '--version'], stderr=subprocess.STDOUT)
230+ self.assertEqual(cc_mock.call_count, 0)
231+
232+ def test_is_pyt3_supported_pip3_not_installed(self):
233+ with mock.patch("subprocess.check_output", autospec=True,
234+ return_value='3.5.2') as co_mock:
235+ with mock.patch("subprocess.check_call", autospec=True,
236+ side_effect=OSError(2, 'No such file')) as cc_mock:
237+ self.assertFalse(pipdeps.is_py3_supported())
238+ co_mock.assert_called_once_with(
239+ ['python3', '--version'], stderr=subprocess.STDOUT)
240+ cc_mock.assert_called_once_with(['pip3', '--version'])

Subscribers

People subscribed via source and target branches