diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-12-18 16:04:28 +0100 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-12-18 16:04:28 +0100 |
commit | 9d2945293f1369bc834cf4c28fbb7ab7bf603f4d (patch) | |
tree | 89a49926c43abb1fbb8ffc7e49c1390c066b69f8 | |
parent | 8716a4c45cdee7d524cce1bef9906bb09dfa85e5 (diff) | |
parent | 7cbe31d0c9cddac8c2cc8d4fe1c16588761f97d4 (diff) | |
download | trollius-9d2945293f1369bc834cf4c28fbb7ab7bf603f4d.tar.gz |
Merge Tulip into Trollius
-rw-r--r-- | .hgignore | 2 | ||||
-rw-r--r-- | release.py | 266 | ||||
-rw-r--r-- | tests/test_base_events.py | 7 | ||||
-rw-r--r-- | tests/test_events.py | 9 | ||||
-rw-r--r-- | tests/test_futures.py | 5 | ||||
-rw-r--r-- | tests/test_subprocess.py | 10 | ||||
-rw-r--r-- | tests/test_tasks.py | 6 | ||||
-rw-r--r-- | tests/test_windows_utils.py | 8 | ||||
-rw-r--r-- | trollius/base_events.py | 2 | ||||
-rw-r--r-- | trollius/events.py | 6 | ||||
-rw-r--r-- | trollius/test_support.py | 314 | ||||
-rw-r--r-- | trollius/windows_events.py | 2 |
12 files changed, 470 insertions, 167 deletions
@@ -14,4 +14,4 @@ dist$ .*\.egg-info$ # Directory created by the "tox" command (ex: tox -e py27) -.tox +\.tox$ diff --git a/release.py b/release.py new file mode 100644 index 0000000..f39d1e1 --- /dev/null +++ b/release.py @@ -0,0 +1,266 @@ +""" +Script to upload 32 bits and 64 bits wheel packages for Python 3.3 on Windows. + +Usage: "python release.py HG_TAG" where HG_TAG is a Mercurial tag, usually +a version number like "3.4.2". + +Modify manually the dry_run attribute to upload files. + +It requires the Windows SDK 7.1 on Windows 64 bits and the aiotest module. +""" +import contextlib +import os +import re +import shutil +import subprocess +import sys +import tempfile +import textwrap + +PY3 = (sys.version_info >= (3,)) +HG = 'hg' +_PYTHON_VERSIONS = [(3, 3)] +PYTHON_VERSIONS = [] +for pyver in _PYTHON_VERSIONS: + PYTHON_VERSIONS.append((pyver, 32)) + PYTHON_VERSIONS.append((pyver, 64)) +SDK_ROOT = r"C:\Program Files\Microsoft SDKs\Windows" +BATCH_FAIL_ON_ERROR = "@IF %errorlevel% neq 0 exit /b %errorlevel%" + +class Release(object): + def __init__(self): + root = os.path.dirname(__file__) + self.root = os.path.realpath(root) + # Set these attributes to True to run also register sdist upload + self.register = False + self.sdist = False + self.dry_run = True + self.test = True + self.aiotest = True + + @contextlib.contextmanager + def _popen(self, args, **kw): + env2 = kw.pop('env', {}) + env = dict(os.environ) + # Force the POSIX locale + env['LC_ALL'] = 'C' + env.update(env2) + print('+ ' + ' '.join(args)) + if PY3: + kw['universal_newlines'] = True + proc = subprocess.Popen(args, env=env, **kw) + with proc: + yield proc + + def get_output(self, *args, **kw): + with self._popen(args, stdout=subprocess.PIPE, **kw) as proc: + stdout, stderr = proc.communicate() + return stdout + + def run_command(self, *args, **kw): + with self._popen(args, **kw) as proc: + exitcode = proc.wait() + if exitcode: + sys.exit(exitcode) + + def get_local_changes(self): + status = self.get_output(HG, 'status') + return [line for line in status.splitlines() + if not line.startswith("?")] + + def remove_directory(self, name): + path = os.path.join(self.root, name) + if os.path.exists(path): + print("Remove directory: %s" % name) + shutil.rmtree(path) + + def remove_file(self, name): + path = os.path.join(self.root, name) + if os.path.exists(path): + print("Remove file: %s" % name) + os.unlink(path) + + def windows_sdk_setenv(self, pyver, bits): + if pyver >= (3, 3): + sdkver = "v7.1" + else: + sdkver = "v7.0" + setenv = os.path.join(SDK_ROOT, sdkver, 'Bin', 'SetEnv.cmd') + if not os.path.exists(setenv): + print("Unable to find Windows SDK %s for Python %s.%s" + % (sdkver, pyver[0], pyver[1])) + print("Please download and install it") + print("%s does not exists" % setenv) + sys.exit(1) + if bits == 64: + arch = '/x64' + else: + arch = '/x86' + return ["CALL", setenv, "/release", arch] + + def get_python(self, version, bits): + if bits == 32: + python = 'c:\\Python%s%s_32bit\\python.exe' % version + else: + python = 'c:\\Python%s%s\\python.exe' % version + if not os.path.exists(python): + print("Unable to find python%s.%s" % version) + print("%s does not exists" % python) + sys.exit(1) + code = ( + 'import platform, sys; ' + 'print("{ver.major}.{ver.minor} {bits}".format(' + 'ver=sys.version_info, ' + 'bits=platform.architecture()[0]))' + ) + stdout = self.get_output(python, '-c', code) + stdout = stdout.rstrip() + expected = "%s.%s %sbit" % (version[0], version[1], bits) + if stdout != expected: + print("Python version or architecture doesn't match") + print("got %r, expected %r" % (stdout, expected)) + print(python) + sys.exit(1) + return python + + def quote(self, arg): + if not re.search("[ '\"]", arg): + return arg + # FIXME: should we escape "? + return '"%s"' % arg + + def quote_args(self, args): + return ' '.join(self.quote(arg) for arg in args) + + def cleanup(self): + self.remove_directory('build') + self.remove_directory('dist') + self.remove_file('_overlapped.pyd') + self.remove_file(os.path.join('asyncio', '_overlapped.pyd')) + + def sdist_upload(self): + self.cleanup() + self.run_command(sys.executable, 'setup.py', 'sdist', 'upload') + + def runtests(self, pyver, bits): + pythonstr = "%s.%s (%s bits)" % (pyver[0], pyver[1], bits) + python = self.get_python(pyver, bits) + dbg_env = {'PYTHONASYNCIODEBUG': '1'} + + self.build(pyver, bits, 'build') + if bits == 64: + arch = 'win-amd64' + else: + arch = 'win32' + build_dir = 'lib.%s-%s.%s' % (arch, pyver[0], pyver[1]) + src = os.path.join(self.root, 'build', build_dir, 'asyncio', '_overlapped.pyd') + dst = os.path.join(self.root, 'asyncio', '_overlapped.pyd') + shutil.copyfile(src, dst) + + args = (python, 'runtests.py', '-r') + print("Run runtests.py in release mode with %s" % pythonstr) + self.run_command(*args) + + print("Run runtests.py in debug mode with %s" % pythonstr) + self.run_command(*args, env=dbg_env) + + if self.aiotest: + args = (python, 'run_aiotest.py') + print("Run aiotest in release mode with %s" % pythonstr) + self.run_command(*args) + + print("Run aiotest in debug mode with %s" % pythonstr) + self.run_command(*args, env=dbg_env) + + def build(self, pyver, bits, *cmds): + self.cleanup() + + setenv = self.windows_sdk_setenv(pyver, bits) + + python = self.get_python(pyver, bits) + + cmd = [python, 'setup.py'] + list(cmds) + + with tempfile.NamedTemporaryFile(mode="w", suffix=".bat", delete=False) as temp: + print("CD %s" % self.quote(self.root), file=temp) + print(self.quote_args(setenv), file=temp) + print(BATCH_FAIL_ON_ERROR, file=temp) + print("", file=temp) + print("SET DISTUTILS_USE_SDK=1", file=temp) + print("SET MSSDK=1", file=temp) + print(self.quote_args(cmd), file=temp) + print(BATCH_FAIL_ON_ERROR, file=temp) + + try: + self.run_command(temp.name) + finally: + os.unlink(temp.name) + + def test_wheel(self, pyver, bits): + self.build(pyver, bits, 'bdist_wheel') + + def publish_wheel(self, pyver, bits): + self.build(pyver, bits, 'bdist_wheel', 'upload') + + def main(self): + try: + pos = sys.argv[1:].index('--ignore') + except ValueError: + ignore = False + else: + ignore = True + del sys.argv[1+pos] + if len(sys.argv) != 2: + print("usage: %s hg_tag" % sys.argv[0]) + sys.exit(1) + + print("Directory: %s" % self.root) + os.chdir(self.root) + + if not ignore: + lines = self.get_local_changes() + else: + lines = () + if lines: + print("ERROR: Found local changes") + for line in lines: + print(line) + print("") + print("Revert local changes") + print("or use the --ignore command line option") + sys.exit(1) + + hg_tag = sys.argv[1] + self.run_command(HG, 'up', hg_tag) + + if self.test: + for pyver, bits in PYTHON_VERSIONS: + self.runtests(pyver, bits) + + for pyver, bits in PYTHON_VERSIONS: + self.test_wheel(pyver, bits) + + if self.dry_run: + sys.exit(0) + + if self.register: + self.run_command(sys.executable, 'setup.py', 'register') + + if self.sdist: + self.sdist_upload() + + for pyver, bits in PYTHON_VERSIONS: + self.publish_wheel(pyver, bits) + + print("") + if self.register: + print("Publish version %s" % hg_tag) + print("Uploaded:") + if self.sdist: + print("- sdist") + for pyver, bits in PYTHON_VERSIONS: + print("- Windows wheel %s bits package for Python %s.%s" + % (bits, pyver[0], pyver[1])) + +if __name__ == "__main__": + Release().main() diff --git a/tests/test_base_events.py b/tests/test_base_events.py index d5c6ffb..83a1810 100644 --- a/tests/test_base_events.py +++ b/tests/test_base_events.py @@ -15,9 +15,14 @@ from trollius import constants from trollius import test_utils from trollius.py33_exceptions import BlockingIOError from trollius.test_utils import mock -from trollius import test_support as support # IPV6_ENABLED, gc_collect from trollius.time_monotonic import time_monotonic from trollius.test_support import assert_python_ok +try: + from test.script_helper import assert_python_ok + from test import support +except ImportError: + from trollius import test_support as support + from trollius.test_support import assert_python_ok MOCK_ANY = mock.ANY diff --git a/tests/test_events.py b/tests/test_events.py index b2e6750..c834af9 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -40,7 +40,10 @@ from trollius.py33_exceptions import (wrap_error, FileNotFoundError) from trollius.test_utils import mock from trollius.time_monotonic import time_monotonic -from trollius import test_support as support # find_unused_port, IPV6_ENABLED, TEST_HOME_DIR +try: + from test import support # find_unused_port, IPV6_ENABLED, TEST_HOME_DIR +except ImportError: + from trollius import test_support as support def data_file(filename): @@ -2288,14 +2291,14 @@ class PolicyTests(test_utils.TestCase): def test_get_event_loop_after_set_none(self): policy = asyncio.DefaultEventLoopPolicy() policy.set_event_loop(None) - self.assertRaises(AssertionError, policy.get_event_loop) + self.assertRaises(RuntimeError, policy.get_event_loop) @mock.patch('trollius.events.threading.current_thread') def test_get_event_loop_thread(self, m_current_thread): def f(): policy = asyncio.DefaultEventLoopPolicy() - self.assertRaises(AssertionError, policy.get_event_loop) + self.assertRaises(RuntimeError, policy.get_event_loop) th = threading.Thread(target=f) th.start() diff --git a/tests/test_futures.py b/tests/test_futures.py index 643b0a0..387e552 100644 --- a/tests/test_futures.py +++ b/tests/test_futures.py @@ -12,8 +12,11 @@ import unittest import trollius as asyncio from trollius import compat from trollius import test_utils -from trollius import test_support as support # gc_collect from trollius.test_utils import mock +try: + from test import support # gc_collect +except ImportError: + from trollius import test_support as support def get_thread_ident(): diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index db5d08a..c65a313 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -6,11 +6,17 @@ import signal import sys import unittest from trollius import From, Return -from trollius import test_support as support from trollius.test_utils import mock +from trollius.py33_exceptions import BrokenPipeError, ConnectionResetError + if sys.platform != 'win32': from trollius import unix_events -from trollius.py33_exceptions import BrokenPipeError, ConnectionResetError + +try: + from test import support # PIPE_MAX_SIZE +except ImportError: + from trollius import test_support as support + # Program blocking PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)'] diff --git a/tests/test_tasks.py b/tests/test_tasks.py index e1a8a27..8d3f776 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -14,6 +14,12 @@ from trollius import test_support as support from trollius import test_utils from trollius.test_support import assert_python_ok from trollius.test_utils import mock +try: + from test import support # gc_collect + from test.script_helper import assert_python_ok +except ImportError: + from trollius import test_support as support + from trollius.test_support import assert_python_ok PY33 = (sys.version_info >= (3, 3)) diff --git a/tests/test_windows_utils.py b/tests/test_windows_utils.py index ac6d736..f40f863 100644 --- a/tests/test_windows_utils.py +++ b/tests/test_windows_utils.py @@ -14,7 +14,10 @@ from trollius import test_utils from trollius import windows_utils from trollius.test_support import IPV6_ENABLED from trollius.test_utils import mock -import trollius.test_support as support +try: + from test import support # gc_collect, IPV6_ENABLED +except ImportError: + from trollius import test_support as support class WinsocketpairTests(unittest.TestCase): @@ -29,7 +32,8 @@ class WinsocketpairTests(unittest.TestCase): ssock, csock = windows_utils.socketpair() self.check_winsocketpair(ssock, csock) - @test_utils.skipUnless(IPV6_ENABLED, 'IPv6 not supported or enabled') + @test_utils.skipUnless(support.IPV6_ENABLED, + 'IPv6 not supported or enabled') def test_winsocketpair_ipv6(self): ssock, csock = windows_utils.socketpair(family=socket.AF_INET6) self.check_winsocketpair(ssock, csock) diff --git a/trollius/base_events.py b/trollius/base_events.py index 2236196..fe5f0a2 100644 --- a/trollius/base_events.py +++ b/trollius/base_events.py @@ -425,7 +425,7 @@ class BaseEventLoop(events.AbstractEventLoop): """ try: current = events.get_event_loop() - except AssertionError: + except RuntimeError: return if current is not self: raise RuntimeError( diff --git a/trollius/events.py b/trollius/events.py index 66fd49a..fa723e3 100644 --- a/trollius/events.py +++ b/trollius/events.py @@ -553,9 +553,9 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy): not self._local._set_called and isinstance(threading.current_thread(), threading._MainThread)): self.set_event_loop(self.new_event_loop()) - assert self._local._loop is not None, \ - ('There is no current event loop in thread %r.' % - threading.current_thread().name) + if self._local._loop is None: + raise RuntimeError('There is no current event loop in thread %r.' + % threading.current_thread().name) return self._local._loop def set_event_loop(self, loop): diff --git a/trollius/test_support.py b/trollius/test_support.py index 8bb4fba..eff2101 100644 --- a/trollius/test_support.py +++ b/trollius/test_support.py @@ -1,19 +1,118 @@ +# Subset of test.support from CPython 3.5, just what we need to run asyncio +# test suite. The cde is copied from CPython 3.5 to not depend on the test +# module because it is rarely installed. + +# Ignore symbol TEST_HOME_DIR: test_events works without it + from __future__ import absolute_import import functools import gc -import os.path +import os import platform import re import socket import subprocess import sys import time + from trollius import test_utils -# TEST_HOME_DIR refers to the top level directory of the "test" package -# that contains Python's regression test suite -TEST_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__)) -TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR) +# A constant likely larger than the underlying OS pipe buffer size, to +# make writes blocking. +# Windows limit seems to be around 512 B, and many Unix kernels have a +# 64 KiB pipe buffer size or 16 * PAGE_SIZE: take a few megs to be sure. +# (see issue #17835 for a discussion of this number). +PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 + +def strip_python_stderr(stderr): + """Strip the stderr of a Python process from potential debug output + emitted by the interpreter. + + This will typically be run on the result of the communicate() method + of a subprocess.Popen object. + """ + stderr = re.sub(br"\[\d+ refs, \d+ blocks\]\r?\n?", b"", stderr).strip() + return stderr + + +# Executing the interpreter in a subprocess +def _assert_python(expected_success, *args, **env_vars): + if '__isolated' in env_vars: + isolated = env_vars.pop('__isolated') + else: + isolated = not env_vars + cmd_line = [sys.executable] + if sys.version_info >= (3, 3): + cmd_line.extend(('-X', 'faulthandler')) + if isolated and sys.version_info >= (3, 4): + # isolated mode: ignore Python environment variables, ignore user + # site-packages, and don't add the current directory to sys.path + cmd_line.append('-I') + elif not env_vars: + # ignore Python environment variables + cmd_line.append('-E') + # Need to preserve the original environment, for in-place testing of + # shared library builds. + env = os.environ.copy() + # But a special flag that can be set to override -- in this case, the + # caller is responsible to pass the full environment. + if env_vars.pop('__cleanenv', None): + env = {} + env.update(env_vars) + cmd_line.extend(args) + p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + env=env) + try: + out, err = p.communicate() + finally: + subprocess._cleanup() + p.stdout.close() + p.stderr.close() + rc = p.returncode + err = strip_python_stderr(err) + if (rc and expected_success) or (not rc and not expected_success): + raise AssertionError( + "Process return code is %d, " + "stderr follows:\n%s" % (rc, err.decode('ascii', 'ignore'))) + return rc, out, err + + +def assert_python_ok(*args, **env_vars): + """ + Assert that running the interpreter with `args` and optional environment + variables `env_vars` succeeds (rc == 0) and return a (return code, stdout, + stderr) tuple. + + If the __cleanenv keyword is set, env_vars is used a fresh environment. + + Python is started in isolated mode (command line option -I), + except if the __isolated keyword is set to False. + """ + return _assert_python(True, *args, **env_vars) + + +is_jython = sys.platform.startswith('java') + +def gc_collect(): + """Force as many objects as possible to be collected. + + In non-CPython implementations of Python, this is needed because timely + deallocation is not guaranteed by the garbage collector. (Even in CPython + this can be the case in case of reference cycles.) This means that __del__ + methods may be called later than expected and weakrefs may remain alive for + longer than expected. This function tries its best to force all garbage + objects to disappear. + """ + gc.collect() + if is_jython: + time.sleep(0.1) + gc.collect() + gc.collect() + + +HOST = "127.0.0.1" +HOSTv6 = "::1" def _is_ipv6_enabled(): @@ -22,7 +121,7 @@ def _is_ipv6_enabled(): sock = None try: sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(("::1", 0)) + sock.bind((HOSTv6, 0)) return True except OSError: pass @@ -34,56 +133,6 @@ def _is_ipv6_enabled(): IPV6_ENABLED = _is_ipv6_enabled() -# A constant likely larger than the underlying OS pipe buffer size, to -# make writes blocking. -# Windows limit seems to be around 512 B, and many Unix kernels have a -# 64 KiB pipe buffer size or 16 * PAGE_SIZE: take a few megs to be sure. -# (see issue #17835 for a discussion of this number). -PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 - - -class TestFailed(Exception): - """Test failed.""" - - -def bind_port(sock, host="127.0.0.1"): - """Bind the socket to a free port and return the port number. Relies on - ephemeral ports in order to ensure we are using an unbound port. This is - important as many tests may be running simultaneously, especially in a - buildbot environment. This method raises an exception if the sock.family - is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR - or SO_REUSEPORT set on it. Tests should *never* set these socket options - for TCP/IP sockets. The only case for setting these options is testing - multicasting via multiple UDP sockets. - - Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. - on Windows), it will be set on the socket. This will prevent anyone else - from bind()'ing to our host/port for the duration of the test. - """ - - if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: - if hasattr(socket, 'SO_REUSEADDR'): - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: - raise TestFailed("tests should never set the SO_REUSEADDR " \ - "socket option on TCP/IP sockets!") - if hasattr(socket, 'SO_REUSEPORT'): - try: - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: - raise TestFailed("tests should never set the SO_REUSEPORT " \ - "socket option on TCP/IP sockets!") - except EnvironmentError: - # Python's socket module was compiled using modern headers - # thus defining SO_REUSEPORT but this process is running - # under an older kernel that does not support SO_REUSEPORT. - pass - if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): - sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - - sock.bind((host, 0)) - port = sock.getsockname()[1] - return port - - def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): """Returns an unused port that should be suitable for binding. This is achieved by creating a temporary socket with the same family and type as @@ -146,38 +195,55 @@ def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): del tempsock return port +def bind_port(sock, host=HOST): + """Bind the socket to a free port and return the port number. Relies on + ephemeral ports in order to ensure we are using an unbound port. This is + important as many tests may be running simultaneously, especially in a + buildbot environment. This method raises an exception if the sock.family + is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR + or SO_REUSEPORT set on it. Tests should *never* set these socket options + for TCP/IP sockets. The only case for setting these options is testing + multicasting via multiple UDP sockets. -is_jython = sys.platform.startswith('java') - + Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. + on Windows), it will be set on the socket. This will prevent anyone else + from bind()'ing to our host/port for the duration of the test. + """ -def gc_collect(): - """Force as many objects as possible to be collected. + if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: + if hasattr(socket, 'SO_REUSEADDR'): + if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: + raise TestFailed("tests should never set the SO_REUSEADDR " \ + "socket option on TCP/IP sockets!") + if hasattr(socket, 'SO_REUSEPORT'): + try: + if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: + raise TestFailed("tests should never set the SO_REUSEPORT " \ + "socket option on TCP/IP sockets!") + except OSError: + # Python's socket module was compiled using modern headers + # thus defining SO_REUSEPORT but this process is running + # under an older kernel that does not support SO_REUSEPORT. + pass + if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - In non-CPython implementations of Python, this is needed because timely - deallocation is not guaranteed by the garbage collector. (Even in CPython - this can be the case in case of reference cycles.) This means that __del__ - methods may be called later than expected and weakrefs may remain alive for - longer than expected. This function tries its best to force all garbage - objects to disappear. - """ - gc.collect() - if is_jython: - time.sleep(0.1) - gc.collect() - gc.collect() + sock.bind((host, 0)) + port = sock.getsockname()[1] + return port -def _requires_unix_version(sysname, min_version): - """Decorator raising SkipTest if the OS is `sysname` and the version is less - than `min_version`. +def requires_mac_ver(*min_version): + """Decorator raising SkipTest if the OS is Mac OS X and the OS X + version if less than min_version. - For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if - the FreeBSD version is less than 7.2. + For example, @requires_mac_ver(10, 5) raises SkipTest if the OS X version + is lesser than 10.5. """ def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): - if platform.system() == sysname: - version_txt = platform.release().split('-', 1)[0] + if sys.platform == 'darwin': + version_txt = platform.mac_ver()[0] try: version = tuple(map(int, version_txt.split('.'))) except ValueError: @@ -186,34 +252,25 @@ def _requires_unix_version(sysname, min_version): if version < min_version: min_version_txt = '.'.join(map(str, min_version)) raise test_utils.SkipTest( - "%s version %s or higher required, not %s" - % (sysname, min_version_txt, version_txt)) + "Mac OS X %s or higher required, not %s" + % (min_version_txt, version_txt)) return func(*args, **kw) wrapper.min_version = min_version return wrapper return decorator -def requires_freebsd_version(*min_version): - """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is - less than `min_version`. - - For example, @requires_freebsd_version(7, 2) raises SkipTest if the FreeBSD - version is less than 7.2. - """ - return _requires_unix_version('FreeBSD', min_version) - -def requires_mac_ver(*min_version): - """Decorator raising SkipTest if the OS is Mac OS X and the OS X - version if less than min_version. +def _requires_unix_version(sysname, min_version): + """Decorator raising SkipTest if the OS is `sysname` and the version is less + than `min_version`. - For example, @requires_mac_ver(10, 5) raises SkipTest if the OS X version - is lesser than 10.5. + For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if + the FreeBSD version is less than 7.2. """ def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): - if sys.platform == 'darwin': - version_txt = platform.mac_ver()[0] + if platform.system() == sysname: + version_txt = platform.release().split('-', 1)[0] try: version = tuple(map(int, version_txt.split('.'))) except ValueError: @@ -222,65 +279,18 @@ def requires_mac_ver(*min_version): if version < min_version: min_version_txt = '.'.join(map(str, min_version)) raise test_utils.SkipTest( - "Mac OS X %s or higher required, not %s" - % (min_version_txt, version_txt)) + "%s version %s or higher required, not %s" + % (sysname, min_version_txt, version_txt)) return func(*args, **kw) wrapper.min_version = min_version return wrapper return decorator +def requires_freebsd_version(*min_version): + """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is + less than `min_version`. -def strip_python_stderr(stderr): - """Strip the stderr of a Python process from potential debug output - emitted by the interpreter. - - This will typically be run on the result of the communicate() method - of a subprocess.Popen object. - """ - stderr = re.sub(br"\[\d+ refs, \d+ blocks\]\r?\n?", b"", stderr).strip() - return stderr - -# Executing the interpreter in a subprocess -def _assert_python(expected_success, *args, **env_vars): - cmd_line = [sys.executable] - if not env_vars: - # ignore Python environment variables - cmd_line.append('-E') - # Need to preserve the original environment, for in-place testing of - # shared library builds. - env = os.environ.copy() - # But a special flag that can be set to override -- in this case, the - # caller is responsible to pass the full environment. - if env_vars.pop('__cleanenv', None): - env = {} - env.update(env_vars) - cmd_line.extend(args) - p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=env) - try: - out, err = p.communicate() - finally: - subprocess._cleanup() - p.stdout.close() - p.stderr.close() - rc = p.returncode - err = strip_python_stderr(err) - if (rc and expected_success) or (not rc and not expected_success): - raise AssertionError( - "Process return code is %d, " - "stderr follows:\n%s" % (rc, err.decode('ascii', 'ignore'))) - return rc, out, err - -def assert_python_ok(*args, **env_vars): - """ - Assert that running the interpreter with `args` and optional environment - variables `env_vars` succeeds (rc == 0) and return a (return code, stdout, - stderr) tuple. - - If the __cleanenv keyword is set, env_vars is used a fresh environment. - - Python is started in isolated mode (command line option -I), - except if the __isolated keyword is set to False. + For example, @requires_freebsd_version(7, 2) raises SkipTest if the FreeBSD + version is less than 7.2. """ - return _assert_python(True, *args, **env_vars) + return _requires_unix_version('FreeBSD', min_version) diff --git a/trollius/windows_events.py b/trollius/windows_events.py index 7319dff..ef9bf04 100644 --- a/trollius/windows_events.py +++ b/trollius/windows_events.py @@ -390,7 +390,7 @@ class IocpProactor(object): wrap_error(ov.getresult) return pipe - # FIXME: Tulip issue 196: why to we neeed register=False? + # FIXME: Tulip issue 196: why do we need register=False? # See also the comment in the _register() method return self._register(ov, pipe, finish_accept_pipe, register=False) |