diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..62561b9 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,34 @@ +name: Publish + +on: + push: + tags: + - v* + +jobs: + pypi: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Build a source tarball + run: python setup.py sdist + - name: Build wheels + uses: RalfG/python-wheels-manylinux-build@v0.3.4-manylinux1_x86_64 + with: + python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310' + build-requirements: 'cython' + - name: Clean linux_x86_64.whl + run: rm dist/*-linux_x86_64.whl + - name: Check build result + run: ls -lh dist/ + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip_existing: true + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3778b8b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + unittest: + runs-on: ubuntu-latest + strategy: + matrix: + pyver: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10"] + + steps: + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.pyver }} + - name: Install python dependencies + run: | + python -m pip install --upgrade pip + pip install Cython tox + + - name: Run unittest + run: | + tox -e ${{ matrix.pyver }} diff --git a/.travis.yml b/.travis.yml.bak similarity index 100% rename from .travis.yml rename to .travis.yml.bak diff --git a/README.rst b/README.rst index 37eda0d..1f471f2 100644 --- a/README.rst +++ b/README.rst @@ -1,22 +1,17 @@ greenify_ ========= +|build| |status| |pypiv| |pyversions| |wheel| |license| + greenify_ can make Python extension modules having network operations in C code to be compatible with gevent_. -greenify_ uses the Dynamic Function Redirecting technique same as ELF-Hook_ +greenify_ uses the Dynamic Function Redirecting technique same as ELF-Hook_ to patch blocking network operations at runtime, without the need modify the original modules. Currently greenify_ only supports ELF format modules, and is tested on Linux. -Build status ------------- - -- Branch **master** : |travis_master| - -.. |travis_master| image:: https://travis-ci.org/douban/greenify.svg?branch=master - :target: https://travis-ci.org/douban/greenify Install from source ------------------- @@ -64,3 +59,14 @@ greenify_ is written and maintained by `douban`_ and is licensed under New BSD l .. _greenify: https://github.com/douban/greenify .. _douban: http://www.douban.com .. _ELF-Hook: https://github.com/shoumikhin/ELF-Hook + +.. |build| image:: https://github.com/douban/greenify/actions/workflows/test.yml/badge.svg + :target: https://github.com/douban/greenify/actions/workflows/test.yml + +.. |pypiv| image:: https://img.shields.io/pypi/v/greenify + :target: https://pypi.org/project/greenify/ + +.. |status| image:: https://img.shields.io/pypi/status/greenify +.. |pyversions| image:: https://img.shields.io/pypi/pyversions/greenify +.. |wheel| image:: https://img.shields.io/pypi/wheel/greenify +.. |license| image:: https://img.shields.io/pypi/l/greenify?color=blue diff --git a/.travis/build-wheels.sh b/misc/build-wheels.sh similarity index 100% rename from .travis/build-wheels.sh rename to misc/build-wheels.sh diff --git a/misc/test.sh b/misc/test.sh index a3c6a14..194ca2b 100755 --- a/misc/test.sh +++ b/misc/test.sh @@ -6,4 +6,4 @@ python fake_slow_http_server.py& pid=$! pip install . python test.py -kill $pid +kill $pid || true diff --git a/tests/http_head/fake_slow_http_server.py b/tests/http_head/fake_slow_http_server.py index c5a4749..1bce479 100644 --- a/tests/http_head/fake_slow_http_server.py +++ b/tests/http_head/fake_slow_http_server.py @@ -1,6 +1,7 @@ # coding: utf-8 import time + try: from SimpleHTTPServer import SimpleHTTPRequestHandler from SocketServer import TCPServer @@ -18,15 +19,14 @@ class Server(TCPServer): class Handler(SimpleHTTPRequestHandler): - def do_HEAD(self): time.sleep(BLOCKING_SECONDS) return SimpleHTTPRequestHandler.do_HEAD(self) -if __name__ == '__main__': +if __name__ == "__main__": httpd = Server(("", PORT), Handler) try: httpd.serve_forever() - except: + except Exception: httpd.server_close() diff --git a/tests/http_head/setup.py b/tests/http_head/setup.py index b571a70..29eb71c 100644 --- a/tests/http_head/setup.py +++ b/tests/http_head/setup.py @@ -2,24 +2,27 @@ import platform from distutils.core import setup, Extension -virtualenv_path = os.environ.get('VIRTUAL_ENV') +virtualenv_path = os.environ.get("VIRTUAL_ENV") -include_dirs = list(filter(None, [os.environ.get('INCLUDE_DIRS')])) -library_dirs = list(filter(None, [os.environ.get('LIBRARY_DIRS')])) +include_dirs = list(filter(None, [os.environ.get("INCLUDE_DIRS")])) +library_dirs = list(filter(None, [os.environ.get("LIBRARY_DIRS")])) if virtualenv_path: - include_dirs.append('%s/include' % virtualenv_path) - library_dirs.append('%s/lib' % virtualenv_path) - ld_lib_key = ('DYLD_LIBRARY_PATH' if platform.platform() == 'Darwin' - else 'LD_LIBRARY_PATH') - os.environ[ld_lib_key] = '%s/lib' % virtualenv_path + include_dirs.append("%s/include" % virtualenv_path) + library_dirs.append("%s/lib" % virtualenv_path) + ld_lib_key = "DYLD_LIBRARY_PATH" if platform.platform() == "Darwin" else "LD_LIBRARY_PATH" + os.environ[ld_lib_key] = "%s/lib" % virtualenv_path mod_http_head = Extension( - 'mod_http_head', sources=['mod_http_head.c'], + "mod_http_head", + sources=["mod_http_head.c"], include_dirs=include_dirs, library_dirs=library_dirs, ) -setup(name='mod_http_head', version='1.0', - description='A demo package http_head greenified', - ext_modules=[mod_http_head]) +setup( + name="mod_http_head", + version="1.0", + description="A demo package http_head greenified", + ext_modules=[mod_http_head], +) diff --git a/tests/http_head/test.py b/tests/http_head/test.py index 49e7f29..c95f947 100644 --- a/tests/http_head/test.py +++ b/tests/http_head/test.py @@ -1,30 +1,34 @@ # coding: utf-8 from __future__ import print_function +import sys +import time + # greenify import greenify + greenify.greenify() # python patch -import gevent -import gevent.monkey +import gevent # noqa: E402 +import gevent.monkey # noqa: E402 + gevent.monkey.patch_all() -import sys -import time -import mod_http_head +import mod_http_head # noqa: E402 + assert greenify.patch_lib(mod_http_head.__file__) -import fake_slow_http_server +import fake_slow_http_server # noqa: E402 stack = [] def c_http_head_check(addr): - stack.append(('begin', addr, 'c')) - print('%.5f head %s begin' % (time.time(), addr), file=sys.stderr) + stack.append(("begin", addr, "c")) + print("%.5f head %s begin" % (time.time(), addr), file=sys.stderr) ret = mod_http_head.http_head(*addr) - print('%.5f head %s end' % (time.time(), addr), file=sys.stderr) - stack.append(('end', addr, 'c')) + print("%.5f head %s end" % (time.time(), addr), file=sys.stderr) + stack.append(("end", addr, "c")) assert ret == 1 @@ -34,50 +38,51 @@ def python_http_head_check(addr): except ImportError: from http.client import HTTPConnection - stack.append(('begin', addr, 'python')) - print('%.5f head %s begin' % (time.time(), addr), file=sys.stderr) + stack.append(("begin", addr, "python")) + print("%.5f head %s begin" % (time.time(), addr), file=sys.stderr) conn = HTTPConnection(*addr) conn.request("HEAD", "/") resp = conn.getresponse() status_code = resp.status - print('%.5f head %s end' % (time.time(), addr), file=sys.stderr) - stack.append(('end', addr, 'python')) + print("%.5f head %s end" % (time.time(), addr), file=sys.stderr) + stack.append(("end", addr, "python")) assert 200 <= status_code < 400 def sleeper(): - stack.append(('begin', 'sleeper')) - print('%.5f sleeper begin' % time.time(), file=sys.stderr) - time.sleep(fake_slow_http_server.BLOCKING_SECONDS/2) - print('%.5f sleeper end' % time.time(), file=sys.stderr) - stack.append(('end', 'sleeper')) + stack.append(("begin", "sleeper")) + print("%.5f sleeper begin" % time.time(), file=sys.stderr) + time.sleep(fake_slow_http_server.BLOCKING_SECONDS / 2) + print("%.5f sleeper end" % time.time(), file=sys.stderr) + stack.append(("end", "sleeper")) def main(): global stack local_addr = ("localhost", str(fake_slow_http_server.PORT)) - test_sites = (local_addr, ("google.com", "80"), ("twitter.com", "80"), - ("douban.com", "80"), ("github.com", "80")) + test_sites = ( + local_addr, + ("google.com", "80"), + ("twitter.com", "80"), + ("douban.com", "80"), + ("github.com", "80"), + ) for fn in [python_http_head_check, c_http_head_check]: stack = [] - print('test %s' % fn.__name__) + print("test %s" % fn.__name__) t0 = time.time() - workers = [ - gevent.spawn(fn, addr) - for addr in test_sites - ] + workers = [gevent.spawn(fn, addr) for addr in test_sites] workers.append(gevent.spawn(sleeper)) gevent.joinall(workers) time_elapsed = time.time() - t0 - print('done in %.5fs' % (time_elapsed)) + print("done in %.5fs" % (time_elapsed)) print() # HEAD to fake_slow_http_server is expected to # be the slowest worker - assert stack[-1][:2] == ('end', local_addr), \ - "unexpected: %r" % (stack[-1][:2], ) + assert stack[-1][:2] == ("end", local_addr), "unexpected: %r" % (stack[-1][:2],) assert time_elapsed < fake_slow_http_server.BLOCKING_SECONDS * 1.5 -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tox.ini b/tox.ini index 1c94243..496c2d9 100644 --- a/tox.ini +++ b/tox.ini @@ -4,4 +4,4 @@ envlist = py27, py35, py36, py37, py38, py39, py310 [testenv] passenv = CC LD deps = -rreq.txt -commands = {toxinidir}/misc/test.sh +commands = {toxinidir}/misc/test.sh