From b7b357189d7a7a2b740c89c30621f920c83d4c9c Mon Sep 17 00:00:00 2001 From: Sergey Shepelev Date: Fri, 30 Dec 2016 06:54:11 +0300 Subject: [PATCH] test_import_patched_defaults bended to play with pyopenssl>=16.1.0 Basically this patch replaces urllib with custom module that is guaranteed not imported before patching. https://github.com/eventlet/eventlet/issues/362 More general issue here https://github.com/eventlet/eventlet/issues/368 --- tests/__init__.py | 46 +++++++++++++------ tests/env_test.py | 4 +- .../patcher_import_patched_defaults.py | 23 ++++++---- tests/patcher/__init__.py | 0 tests/patcher/shared1.py | 11 +++++ tests/patcher/shared_import_socket.py | 7 +++ tests/patcher_test.py | 12 +---- tests/socket_test.py | 5 +- tox.ini | 6 +-- 9 files changed, 70 insertions(+), 44 deletions(-) create mode 100644 tests/patcher/__init__.py create mode 100644 tests/patcher/shared1.py create mode 100644 tests/patcher/shared_import_socket.py diff --git a/tests/__init__.py b/tests/__init__.py index 6ff6d2a..1c7a5b3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -302,16 +302,23 @@ def get_database_auth(): return retval -def run_python(path, env=None, args=None, timeout=None): +def run_python(path, env=None, args=None, timeout=None, pythonpath_extend=None, expect_pass=False): new_argv = [sys.executable] new_env = os.environ.copy() + new_env.setdefault('eventlet_test_in_progress', 'yes') + src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if path: path = os.path.abspath(path) new_argv.append(path) - src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) new_env['PYTHONPATH'] = os.pathsep.join(sys.path + [src_dir]) if env: new_env.update(env) + if pythonpath_extend: + new_path = [p for p in new_env.get('PYTHONPATH', '').split(os.pathsep) if p] + new_path.extend( + p if os.path.isabs(p) else os.path.join(src_dir, p) for p in pythonpath_extend + ) + new_env['PYTHONPATH'] = os.pathsep.join(new_path) if args: new_argv.extend(args) p = subprocess.Popen( @@ -329,21 +336,25 @@ def run_python(path, env=None, args=None, timeout=None): p.kill() output, _ = p.communicate(timeout=timeout) return '{0}\nFAIL - timed out'.format(output).encode() + + if expect_pass: + if output.startswith(b'skip'): + parts = output.rstrip().split(b':', 1) + skip_args = [] + if len(parts) > 1: + skip_args.append(parts[1]) + raise SkipTest(*skip_args) + ok = output.rstrip() == b'pass' + if not ok: + sys.stderr.write('Program {0} output:\n---\n{1}\n---\n'.format(path, output.decode())) + assert ok, 'Expected single line "pass" in stdout' + return output -def run_isolated(path, prefix='tests/isolated/', env=None, args=None, timeout=None): - output = run_python(prefix + path, env=env, args=args, timeout=timeout).rstrip() - if output.startswith(b'skip'): - parts = output.split(b':', 1) - skip_args = [] - if len(parts) > 1: - skip_args.append(parts[1]) - raise SkipTest(*skip_args) - ok = output == b'pass' - if not ok: - sys.stderr.write('Isolated test {0} output:\n---\n{1}\n---\n'.format(path, output.decode())) - assert ok, 'Expected single line "pass" in stdout' +def run_isolated(path, prefix='tests/isolated/', **kwargs): + kwargs.setdefault('expect_pass', True) + run_python(prefix + path, **kwargs) certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') @@ -353,3 +364,10 @@ private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') def test_run_python_timeout(): output = run_python('', args=('-c', 'import time; time.sleep(0.5)'), timeout=0.1) assert output.endswith(b'FAIL - timed out') + + +def test_run_python_pythonpath_extend(): + code = '''import os, sys ; print('\\n'.join(sys.path))''' + output = run_python('', args=('-c', code), pythonpath_extend=('dira', 'dirb')) + assert b'/dira\n' in output + assert b'/dirb\n' in output diff --git a/tests/env_test.py b/tests/env_test.py index fb7a58b..20a694d 100644 --- a/tests/env_test.py +++ b/tests/env_test.py @@ -19,12 +19,12 @@ socket.gethostbyname('localhost') socket.getaddrinfo('localhost', 80) print('pass') ''' - output = tests.run_python( + tests.run_python( path=None, env={'EVENTLET_TPOOL_DNS': 'yes'}, args=['-c', code], + expect_pass=True, ) - assert output.rstrip() == b'pass' @tests.skip_with_pyevent diff --git a/tests/isolated/patcher_import_patched_defaults.py b/tests/isolated/patcher_import_patched_defaults.py index 62b0807..69ea1b5 100644 --- a/tests/isolated/patcher_import_patched_defaults.py +++ b/tests/isolated/patcher_import_patched_defaults.py @@ -1,15 +1,18 @@ -import os __test__ = False +if __name__ == '__main__': + import sys + # On eventlet<=0.20.0 uncommenting this unpatched import fails test + # because import_patched did not agressively repatch sub-imported modules cached in sys.modules + # to be fixed in https://github.com/eventlet/eventlet/issues/368 + # import tests.patcher.shared_import_socket -if os.environ.get('eventlet_test_import_patched_defaults') == '1': - try: - import urllib.request as target - except ImportError: - import urllib as target + import eventlet + target = eventlet.import_patched('tests.patcher.shared1').shared t = target.socket.socket - import eventlet.green.socket - if issubclass(t, eventlet.green.socket.socket): - print('pass') - else: + import eventlet.green.socket as g + if not issubclass(t, g.socket): print('Fail. Target socket not green: {0} bases {1}'.format(t, t.__bases__)) + sys.exit(1) + + print('pass') diff --git a/tests/patcher/__init__.py b/tests/patcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/patcher/shared1.py b/tests/patcher/shared1.py new file mode 100644 index 0000000..adc7eb9 --- /dev/null +++ b/tests/patcher/shared1.py @@ -0,0 +1,11 @@ +import os +__test__ = False +shared = None + + +if os.environ.get('eventlet_test_in_progress') == 'yes': + # pyopenssl imported urllib before we could patch it + # we can ensure this shared module was not imported + # https://github.com/eventlet/eventlet/issues/362 + import tests.patcher.shared_import_socket as shared + _ = shared # mask unused import error diff --git a/tests/patcher/shared_import_socket.py b/tests/patcher/shared_import_socket.py new file mode 100644 index 0000000..8096ba8 --- /dev/null +++ b/tests/patcher/shared_import_socket.py @@ -0,0 +1,7 @@ +import os +import socket +__test__ = False +_ = socket # mask unused import error + +# prevent accidental imports +assert os.environ.get('eventlet_test_in_progress') == 'yes' diff --git a/tests/patcher_test.py b/tests/patcher_test.py index 933644f..eb8e100 100644 --- a/tests/patcher_test.py +++ b/tests/patcher_test.py @@ -84,17 +84,7 @@ class ImportPatched(ProcessBase): def test_import_patched_defaults(): - code = '''\ -import eventlet -eventlet.import_patched('patcher_import_patched_defaults') -''' - isolated_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/tests/isolated' - env = { - 'eventlet_test_import_patched_defaults': '1', - 'PYTHONPATH': os.pathsep.join(sys.path + [isolated_path]), - } - output = tests.run_python(path=None, env=env, args=['-c', code]) - assert output.rstrip() == b'pass', repr(output) + tests.run_isolated('patcher_import_patched_defaults.py') class MonkeyPatch(ProcessBase): diff --git a/tests/socket_test.py b/tests/socket_test.py index 954eba3..7b61a7f 100644 --- a/tests/socket_test.py +++ b/tests/socket_test.py @@ -72,10 +72,7 @@ def test_dns_methods_are_green(): with open(mock_sys_pkg_dir + '/dns.py', 'wb') as f: f.write(b'raise Exception("Your IP address string is so illegal ' + b'it prevents installing packages.")\n') - tests.run_isolated( - 'socket_resolve_green.py', - env={'PYTHONPATH': os.pathsep.join(sys.path + [mock_sys_pkg_dir])}, - ) + tests.run_isolated('socket_resolve_green.py', pythonpath_extend=[mock_sys_pkg_dir]) finally: shutil.rmtree(mock_sys_pkg_dir) diff --git a/tox.ini b/tox.ini index 9847fc3..0ad4722 100644 --- a/tox.ini +++ b/tox.ini @@ -42,12 +42,12 @@ basepython = py35: python3.5 pypy: pypy deps = - nose==1.3.1 - setuptools==5.4.1 + nose==1.3.7 + setuptools==32.3.1 py{26,27}: subprocess32==3.2.7 py{26,27}-{selects,poll,epolls}: MySQL-python==1.2.5 py26-{selects,poll,epolls}: pyopenssl==0.13 - py{27,33,34}-{selects,poll,epolls}: pyopenssl==16.0.0 + py{27,33,34}-{selects,poll,epolls}: pyopenssl==16.2.0 {selects,poll,epolls}: psycopg2cffi-compat==1.1 {selects,poll,epolls}: pyzmq==13.1.0 commands =