Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: third_party/grpc/src/python/grpcio/commands.py

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015-2016, Google Inc.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Provides distutils command classes for the GRPC Python setup process."""
31
32 import distutils
33 import glob
34 import os
35 import os.path
36 import platform
37 import re
38 import shutil
39 import subprocess
40 import sys
41 import traceback
42
43 import setuptools
44 from setuptools.command import build_ext
45 from setuptools.command import build_py
46 from setuptools.command import easy_install
47 from setuptools.command import install
48 from setuptools.command import test
49
50 import support
51
52 PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
53
54 CONF_PY_ADDENDUM = """
55 extensions.append('sphinx.ext.napoleon')
56 napoleon_google_docstring = True
57 napoleon_numpy_docstring = True
58
59 html_theme = 'sphinx_rtd_theme'
60 """
61
62
63 class CommandError(Exception):
64 """Simple exception class for GRPC custom commands."""
65
66
67 # TODO(atash): Remove this once PyPI has better Linux bdist support. See
68 # https://bitbucket.org/pypa/pypi/issues/120/binary-wheels-for-linux-are-not-sup ported
69 def _get_grpc_custom_bdist(decorated_basename, target_bdist_basename):
70 """Returns a string path to a bdist file for Linux to install.
71
72 If we can retrieve a pre-compiled bdist from online, uses it. Else, emits a
73 warning and builds from source.
74 """
75 # TODO(atash): somehow the name that's returned from `wheel` is different
76 # between different versions of 'wheel' (but from a compatibility standpoint,
77 # the names are compatible); we should have some way of determining name
78 # compatibility in the same way `wheel` does to avoid having to rename all of
79 # the custom wheels that we build/upload to GCS.
80
81 # Break import style to ensure that setup.py has had a chance to install the
82 # relevant package.
83 from six.moves.urllib import request
84 decorated_path = decorated_basename + GRPC_CUSTOM_BDIST_EXT
85 try:
86 url = BINARIES_REPOSITORY + '/{target}'.format(target=decorated_path)
87 bdist_data = request.urlopen(url).read()
88 except IOError as error:
89 raise CommandError(
90 '{}\n\nCould not find the bdist {}: {}'
91 .format(traceback.format_exc(), decorated_path, error.message))
92 # Our chosen local bdist path.
93 bdist_path = target_bdist_basename + GRPC_CUSTOM_BDIST_EXT
94 try:
95 with open(bdist_path, 'w') as bdist_file:
96 bdist_file.write(bdist_data)
97 except IOError as error:
98 raise CommandError(
99 '{}\n\nCould not write grpcio bdist: {}'
100 .format(traceback.format_exc(), error.message))
101 return bdist_path
102
103
104 class SphinxDocumentation(setuptools.Command):
105 """Command to generate documentation via sphinx."""
106
107 description = 'generate sphinx documentation'
108 user_options = []
109
110 def initialize_options(self):
111 pass
112
113 def finalize_options(self):
114 pass
115
116 def run(self):
117 # We import here to ensure that setup.py has had a chance to install the
118 # relevant package eggs first.
119 import sphinx
120 import sphinx.apidoc
121 metadata = self.distribution.metadata
122 src_dir = os.path.join(PYTHON_STEM, 'grpc')
123 sys.path.append(src_dir)
124 sphinx.apidoc.main([
125 '', '--force', '--full', '-H', metadata.name, '-A', metadata.author,
126 '-V', metadata.version, '-R', metadata.version,
127 '-o', os.path.join('doc', 'src'), src_dir])
128 conf_filepath = os.path.join('doc', 'src', 'conf.py')
129 with open(conf_filepath, 'a') as conf_file:
130 conf_file.write(CONF_PY_ADDENDUM)
131 sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')])
132
133
134 class BuildProtoModules(setuptools.Command):
135 """Command to generate project *_pb2.py modules from proto files."""
136
137 description = 'build protobuf modules'
138 user_options = [
139 ('include=', None, 'path patterns to include in protobuf generation'),
140 ('exclude=', None, 'path patterns to exclude from protobuf generation')
141 ]
142
143 def initialize_options(self):
144 self.exclude = None
145 self.include = r'.*\.proto$'
146 self.protoc_command = None
147 self.grpc_python_plugin_command = None
148
149 def finalize_options(self):
150 self.protoc_command = distutils.spawn.find_executable('protoc')
151 self.grpc_python_plugin_command = distutils.spawn.find_executable(
152 'grpc_python_plugin')
153
154 def run(self):
155 if not self.protoc_command:
156 raise CommandError('could not find protoc')
157 if not self.grpc_python_plugin_command:
158 raise CommandError('could not find grpc_python_plugin '
159 '(protoc plugin for GRPC Python)')
160 include_regex = re.compile(self.include)
161 exclude_regex = re.compile(self.exclude) if self.exclude else None
162 paths = []
163 root_directory = PYTHON_STEM
164 for walk_root, directories, filenames in os.walk(root_directory):
165 for filename in filenames:
166 path = os.path.join(walk_root, filename)
167 if include_regex.match(path) and not (
168 exclude_regex and exclude_regex.match(path)):
169 paths.append(path)
170 command = [
171 self.protoc_command,
172 '--plugin=protoc-gen-python-grpc={}'.format(
173 self.grpc_python_plugin_command),
174 '-I {}'.format(root_directory),
175 '--python_out={}'.format(root_directory),
176 '--python-grpc_out={}'.format(root_directory),
177 ] + paths
178 try:
179 subprocess.check_output(' '.join(command), cwd=root_directory, shell=True,
180 stderr=subprocess.STDOUT)
181 except subprocess.CalledProcessError as e:
182 raise CommandError('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
183 command, e.message, e.output))
184
185
186 class BuildProjectMetadata(setuptools.Command):
187 """Command to generate project metadata in a module."""
188
189 description = 'build grpcio project metadata files'
190 user_options = []
191
192 def initialize_options(self):
193 pass
194
195 def finalize_options(self):
196 pass
197
198 def run(self):
199 with open(os.path.join(PYTHON_STEM, 'grpc/_grpcio_metadata.py'), 'w') as mod ule_file:
200 module_file.write('__version__ = """{}"""'.format(
201 self.distribution.get_version()))
202
203
204 class BuildPy(build_py.build_py):
205 """Custom project build command."""
206
207 def run(self):
208 try:
209 self.run_command('build_proto_modules')
210 except CommandError as error:
211 sys.stderr.write('warning: %s\n' % error.message)
212 self.run_command('build_project_metadata')
213 build_py.build_py.run(self)
214
215
216 class BuildExt(build_ext.build_ext):
217 """Custom build_ext command to enable compiler-specific flags."""
218
219 C_OPTIONS = {
220 'unix': ('-pthread', '-std=gnu99'),
221 'msvc': (),
222 }
223 LINK_OPTIONS = {}
224
225 def build_extensions(self):
226 compiler = self.compiler.compiler_type
227 if compiler in BuildExt.C_OPTIONS:
228 for extension in self.extensions:
229 extension.extra_compile_args += list(BuildExt.C_OPTIONS[compiler])
230 if compiler in BuildExt.LINK_OPTIONS:
231 for extension in self.extensions:
232 extension.extra_link_args += list(BuildExt.LINK_OPTIONS[compiler])
233 try:
234 build_ext.build_ext.build_extensions(self)
235 except Exception as error:
236 formatted_exception = traceback.format_exc()
237 support.diagnose_build_ext_error(self, error, formatted_exception)
238 raise CommandError(
239 "Failed `build_ext` step:\n{}".format(formatted_exception))
240
241
242 class Gather(setuptools.Command):
243 """Command to gather project dependencies."""
244
245 description = 'gather dependencies for grpcio'
246 user_options = [
247 ('test', 't', 'flag indicating to gather test dependencies'),
248 ('install', 'i', 'flag indicating to gather install dependencies')
249 ]
250
251 def initialize_options(self):
252 self.test = False
253 self.install = False
254
255 def finalize_options(self):
256 # distutils requires this override.
257 pass
258
259 def run(self):
260 if self.install and self.distribution.install_requires:
261 self.distribution.fetch_build_eggs(self.distribution.install_requires)
262 if self.test and self.distribution.tests_require:
263 self.distribution.fetch_build_eggs(self.distribution.tests_require)
264
265
266 class TestLite(setuptools.Command):
267 """Command to run tests without fetching or building anything."""
268
269 description = 'run tests without fetching or building anything.'
270 user_options = []
271
272 def initialize_options(self):
273 pass
274
275 def finalize_options(self):
276 # distutils requires this override.
277 pass
278
279 def run(self):
280 self._add_eggs_to_path()
281
282 import tests
283 loader = tests.Loader()
284 loader.loadTestsFromNames(['tests'])
285 runner = tests.Runner()
286 result = runner.run(loader.suite)
287 if not result.wasSuccessful():
288 sys.exit('Test failure')
289
290 def _add_eggs_to_path(self):
291 """Adds all egg files under .eggs to sys.path"""
292 # TODO(jtattemusch): there has to be a cleaner way to do this
293 import pkg_resources
294 eggs_dir = os.path.join(PYTHON_STEM, '../../../.eggs')
295 eggs = [os.path.join(eggs_dir, filename)
296 for filename in os.listdir(eggs_dir)
297 if filename.endswith('.egg')]
298 for egg in eggs:
299 sys.path.insert(0, pkg_resources.normalize_path(egg))
300
301
302 class RunInterop(test.test):
303
304 description = 'run interop test client/server'
305 user_options = [
306 ('args=', 'a', 'pass-thru arguments for the client/server'),
307 ('client', 'c', 'flag indicating to run the client'),
308 ('server', 's', 'flag indicating to run the server')
309 ]
310
311 def initialize_options(self):
312 self.args = ''
313 self.client = False
314 self.server = False
315
316 def finalize_options(self):
317 if self.client and self.server:
318 raise DistutilsOptionError('you may only specify one of client or server')
319
320 def run(self):
321 if self.distribution.install_requires:
322 self.distribution.fetch_build_eggs(self.distribution.install_requires)
323 if self.distribution.tests_require:
324 self.distribution.fetch_build_eggs(self.distribution.tests_require)
325 if self.client:
326 self.run_client()
327 elif self.server:
328 self.run_server()
329
330 def run_server(self):
331 # We import here to ensure that our setuptools parent has had a chance to
332 # edit the Python system path.
333 from tests.interop import server
334 sys.argv[1:] = self.args.split()
335 server.serve()
336
337 def run_client(self):
338 # We import here to ensure that our setuptools parent has had a chance to
339 # edit the Python system path.
340 from tests.interop import client
341 sys.argv[1:] = self.args.split()
342 client.test_interoperability()
OLDNEW
« no previous file with comments | « third_party/grpc/src/python/grpcio/README.rst ('k') | third_party/grpc/src/python/grpcio/grpc/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698