commands.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. # Copyright 2015 gRPC authors.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Provides distutils command classes for the gRPC Python setup process."""
  15. from distutils import errors as _errors
  16. import glob
  17. import os
  18. import os.path
  19. import platform
  20. import re
  21. import shutil
  22. import subprocess
  23. import sys
  24. import setuptools
  25. from setuptools.command import build_ext
  26. from setuptools.command import build_py
  27. from setuptools.command import easy_install
  28. from setuptools.command import install
  29. from setuptools.command import test
  30. PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
  31. GRPC_STEM = os.path.abspath(PYTHON_STEM + '../../../../')
  32. GRPC_PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto')
  33. PROTO_STEM = os.path.join(PYTHON_STEM, 'src', 'proto')
  34. PYTHON_PROTO_TOP_LEVEL = os.path.join(PYTHON_STEM, 'src')
  35. class CommandError(object):
  36. pass
  37. class GatherProto(setuptools.Command):
  38. description = 'gather proto dependencies'
  39. user_options = []
  40. def initialize_options(self):
  41. pass
  42. def finalize_options(self):
  43. pass
  44. def run(self):
  45. # TODO(atash) ensure that we're running from the repository directory when
  46. # this command is used
  47. try:
  48. shutil.rmtree(PROTO_STEM)
  49. except Exception as error:
  50. # We don't care if this command fails
  51. pass
  52. shutil.copytree(GRPC_PROTO_STEM, PROTO_STEM)
  53. for root, _, _ in os.walk(PYTHON_PROTO_TOP_LEVEL):
  54. path = os.path.join(root, '__init__.py')
  55. open(path, 'a').close()
  56. class BuildPy(build_py.build_py):
  57. """Custom project build command."""
  58. def run(self):
  59. try:
  60. self.run_command('build_package_protos')
  61. except CommandError as error:
  62. sys.stderr.write('warning: %s\n' % error.message)
  63. build_py.build_py.run(self)
  64. class TestLite(setuptools.Command):
  65. """Command to run tests without fetching or building anything."""
  66. description = 'run tests without fetching or building anything.'
  67. user_options = []
  68. def initialize_options(self):
  69. pass
  70. def finalize_options(self):
  71. # distutils requires this override.
  72. pass
  73. def run(self):
  74. self._add_eggs_to_path()
  75. import tests
  76. loader = tests.Loader()
  77. loader.loadTestsFromNames(['tests'])
  78. runner = tests.Runner()
  79. result = runner.run(loader.suite)
  80. if not result.wasSuccessful():
  81. sys.exit('Test failure')
  82. def _add_eggs_to_path(self):
  83. """Fetch install and test requirements"""
  84. self.distribution.fetch_build_eggs(self.distribution.install_requires)
  85. self.distribution.fetch_build_eggs(self.distribution.tests_require)
  86. class TestAio(setuptools.Command):
  87. """Command to run aio tests without fetching or building anything."""
  88. description = 'run aio tests without fetching or building anything.'
  89. user_options = []
  90. def initialize_options(self):
  91. pass
  92. def finalize_options(self):
  93. pass
  94. def run(self):
  95. self._add_eggs_to_path()
  96. import tests
  97. loader = tests.Loader()
  98. loader.loadTestsFromNames(['tests_aio'])
  99. runner = tests.Runner()
  100. result = runner.run(loader.suite)
  101. if not result.wasSuccessful():
  102. sys.exit('Test failure')
  103. def _add_eggs_to_path(self):
  104. """Fetch install and test requirements"""
  105. self.distribution.fetch_build_eggs(self.distribution.install_requires)
  106. self.distribution.fetch_build_eggs(self.distribution.tests_require)
  107. class TestGevent(setuptools.Command):
  108. """Command to run tests w/gevent."""
  109. BANNED_TESTS = (
  110. # Fork support is not compatible with gevent
  111. 'fork._fork_interop_test.ForkInteropTest',
  112. # These tests send a lot of RPCs and are really slow on gevent. They will
  113. # eventually succeed, but need to dig into performance issues.
  114. 'unit._cython._no_messages_server_completion_queue_per_call_test.Test.test_rpcs',
  115. 'unit._cython._no_messages_single_server_completion_queue_test.Test.test_rpcs',
  116. 'unit._compression_test',
  117. # TODO(https://github.com/grpc/grpc/issues/16890) enable this test
  118. 'unit._cython._channel_test.ChannelTest.test_multiple_channels_lonely_connectivity',
  119. # I have no idea why this doesn't work in gevent, but it shouldn't even be
  120. # using the c-core
  121. 'testing._client_test.ClientTest.test_infinite_request_stream_real_time',
  122. # TODO(https://github.com/grpc/grpc/issues/15743) enable this test
  123. 'unit._session_cache_test.SSLSessionCacheTest.testSSLSessionCacheLRU',
  124. # TODO(https://github.com/grpc/grpc/issues/14789) enable this test
  125. 'unit._server_ssl_cert_config_test',
  126. # TODO(https://github.com/grpc/grpc/issues/14901) enable this test
  127. 'protoc_plugin._python_plugin_test.PythonPluginTest',
  128. # Beta API is unsupported for gevent
  129. 'protoc_plugin.beta_python_plugin_test',
  130. 'unit.beta._beta_features_test',
  131. # TODO(https://github.com/grpc/grpc/issues/15411) unpin gevent version
  132. # This test will stuck while running higher version of gevent
  133. 'unit._auth_context_test.AuthContextTest.testSessionResumption',
  134. # TODO(https://github.com/grpc/grpc/issues/15411) enable these tests
  135. 'unit._channel_ready_future_test.ChannelReadyFutureTest.test_immediately_connectable_channel_connectivity',
  136. "unit._cython._channel_test.ChannelTest.test_single_channel_lonely_connectivity",
  137. 'unit._exit_test.ExitTest.test_in_flight_unary_unary_call',
  138. 'unit._exit_test.ExitTest.test_in_flight_unary_stream_call',
  139. 'unit._exit_test.ExitTest.test_in_flight_stream_unary_call',
  140. 'unit._exit_test.ExitTest.test_in_flight_stream_stream_call',
  141. 'unit._exit_test.ExitTest.test_in_flight_partial_unary_stream_call',
  142. 'unit._exit_test.ExitTest.test_in_flight_partial_stream_unary_call',
  143. 'unit._exit_test.ExitTest.test_in_flight_partial_stream_stream_call',
  144. # TODO(https://github.com/grpc/grpc/issues/18980): Reenable.
  145. 'unit._signal_handling_test.SignalHandlingTest',
  146. 'unit._metadata_flags_test',
  147. 'health_check._health_servicer_test.HealthServicerTest.test_cancelled_watch_removed_from_watch_list',
  148. # TODO(https://github.com/grpc/grpc/issues/17330) enable these three tests
  149. 'channelz._channelz_servicer_test.ChannelzServicerTest.test_many_subchannels',
  150. 'channelz._channelz_servicer_test.ChannelzServicerTest.test_many_subchannels_and_sockets',
  151. 'channelz._channelz_servicer_test.ChannelzServicerTest.test_streaming_rpc',
  152. # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
  153. 'unit._cython._channel_test.ChannelTest.test_negative_deadline_connectivity',
  154. # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
  155. 'unit._local_credentials_test.LocalCredentialsTest',
  156. )
  157. BANNED_WINDOWS_TESTS = (
  158. # TODO(https://github.com/grpc/grpc/pull/15411) enable this test
  159. 'unit._dns_resolver_test.DNSResolverTest.test_connect_loopback',)
  160. description = 'run tests with gevent. Assumes grpc/gevent are installed'
  161. user_options = []
  162. def initialize_options(self):
  163. pass
  164. def finalize_options(self):
  165. # distutils requires this override.
  166. pass
  167. def run(self):
  168. from gevent import monkey
  169. monkey.patch_all()
  170. import tests
  171. import grpc.experimental.gevent
  172. grpc.experimental.gevent.init_gevent()
  173. import gevent
  174. import tests
  175. loader = tests.Loader()
  176. loader.loadTestsFromNames(['tests'])
  177. runner = tests.Runner()
  178. if sys.platform == 'win32':
  179. runner.skip_tests(self.BANNED_TESTS + self.BANNED_WINDOWS_TESTS)
  180. else:
  181. runner.skip_tests(self.BANNED_TESTS)
  182. result = gevent.spawn(runner.run, loader.suite)
  183. result.join()
  184. if not result.value.wasSuccessful():
  185. sys.exit('Test failure')
  186. class RunInterop(test.test):
  187. description = 'run interop test client/server'
  188. user_options = [('args=', 'a', 'pass-thru arguments for the client/server'),
  189. ('client', 'c', 'flag indicating to run the client'),
  190. ('server', 's', 'flag indicating to run the server')]
  191. def initialize_options(self):
  192. self.args = ''
  193. self.client = False
  194. self.server = False
  195. def finalize_options(self):
  196. if self.client and self.server:
  197. raise _errors.DistutilsOptionError(
  198. 'you may only specify one of client or server')
  199. def run(self):
  200. if self.distribution.install_requires:
  201. self.distribution.fetch_build_eggs(
  202. self.distribution.install_requires)
  203. if self.distribution.tests_require:
  204. self.distribution.fetch_build_eggs(self.distribution.tests_require)
  205. if self.client:
  206. self.run_client()
  207. elif self.server:
  208. self.run_server()
  209. def run_server(self):
  210. # We import here to ensure that our setuptools parent has had a chance to
  211. # edit the Python system path.
  212. from tests.interop import server
  213. sys.argv[1:] = self.args.split()
  214. server.serve()
  215. def run_client(self):
  216. # We import here to ensure that our setuptools parent has had a chance to
  217. # edit the Python system path.
  218. from tests.interop import client
  219. sys.argv[1:] = self.args.split()
  220. client.test_interoperability()
  221. class RunFork(test.test):
  222. description = 'run fork test client'
  223. user_options = [('args=', 'a', 'pass-thru arguments for the client')]
  224. def initialize_options(self):
  225. self.args = ''
  226. def finalize_options(self):
  227. # distutils requires this override.
  228. pass
  229. def run(self):
  230. if self.distribution.install_requires:
  231. self.distribution.fetch_build_eggs(
  232. self.distribution.install_requires)
  233. if self.distribution.tests_require:
  234. self.distribution.fetch_build_eggs(self.distribution.tests_require)
  235. # We import here to ensure that our setuptools parent has had a chance to
  236. # edit the Python system path.
  237. from tests.fork import client
  238. sys.argv[1:] = self.args.split()
  239. client.test_fork()