From de6dcdcb9bbc280ef3c861bde2f4ae63452d716e Mon Sep 17 00:00:00 2001 From: Armin Berres Date: Tue, 30 Apr 2019 13:37:54 +0200 Subject: [PATCH] Add package to provide gevent compatibiliy --- README.rst | 2 + contrib/opencensus-ext-gevent/CHANGELOG.md | 5 + contrib/opencensus-ext-gevent/README.rst | 41 ++++++++ .../opencensus/__init__.py | 1 + .../opencensus/ext/__init__.py | 1 + .../opencensus/ext/gevent/__init__.py | 1 + .../ext/gevent/geventcompatibility.py | 21 ++++ contrib/opencensus-ext-gevent/setup.cfg | 2 + contrib/opencensus-ext-gevent/setup.py | 56 +++++++++++ .../tests/test_patching.py | 96 +++++++++++++++++++ contrib/opencensus-ext-gevent/version.py | 15 +++ nox.py | 1 + 12 files changed, 242 insertions(+) create mode 100644 contrib/opencensus-ext-gevent/CHANGELOG.md create mode 100644 contrib/opencensus-ext-gevent/README.rst create mode 100644 contrib/opencensus-ext-gevent/opencensus/__init__.py create mode 100644 contrib/opencensus-ext-gevent/opencensus/ext/__init__.py create mode 100644 contrib/opencensus-ext-gevent/opencensus/ext/gevent/__init__.py create mode 100644 contrib/opencensus-ext-gevent/opencensus/ext/gevent/geventcompatibility.py create mode 100644 contrib/opencensus-ext-gevent/setup.cfg create mode 100644 contrib/opencensus-ext-gevent/setup.py create mode 100644 contrib/opencensus-ext-gevent/tests/test_patching.py create mode 100644 contrib/opencensus-ext-gevent/version.py diff --git a/README.rst b/README.rst index 4dcffac4d..06705386a 100644 --- a/README.rst +++ b/README.rst @@ -185,6 +185,7 @@ OpenCensus supports integration with popular web frameworks, client libraries an - `Django`_ - `Flask`_ +- `gevent`_ - `Google Cloud Client Libraries`_ - `gRPC`_ - `httplib`_ @@ -216,6 +217,7 @@ Stats Exporter .. _Azure: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-azure .. _Django: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-django .. _Flask: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-flask +.. _gevent: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-gevent .. _Google Cloud Client Libraries: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-google-cloud-clientlibs .. _gRPC: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-grpc .. _httplib: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-httplib diff --git a/contrib/opencensus-ext-gevent/CHANGELOG.md b/contrib/opencensus-ext-gevent/CHANGELOG.md new file mode 100644 index 000000000..49e4f9e5e --- /dev/null +++ b/contrib/opencensus-ext-gevent/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +- Initial version diff --git a/contrib/opencensus-ext-gevent/README.rst b/contrib/opencensus-ext-gevent/README.rst new file mode 100644 index 000000000..f6bf0202a --- /dev/null +++ b/contrib/opencensus-ext-gevent/README.rst @@ -0,0 +1,41 @@ +OpenCensus gevent helper +============================================================================ + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opencensus-ext-gevent.svg + :target: https://pypi.org/project/opencensus-ext-gevent/ + +Installation +------------ + +:: + + pip install opencensus-ext-gevent + +Usage +----- + +As gevent is to date `incompatible `_ with +the new context variables the **OpenCensus gevent helper** configures OpenCensus to use +a compatible thread based runtime context implementation. + +No action apart from installing the package is needed as it listens to events +`emitted by gevent `_ once +patching via ``patch_all`` is complete. + + +Warning +------- + +OpenCensus itself is completely compatible with gevent. Be aware though that not all +available OpenCensus integrations are compatible. + +You should check this on a case by case basis. + + +References +---------- + +* `OpenCensus Project `_ +* `gevent `_ diff --git a/contrib/opencensus-ext-gevent/opencensus/__init__.py b/contrib/opencensus-ext-gevent/opencensus/__init__.py new file mode 100644 index 000000000..69e3be50d --- /dev/null +++ b/contrib/opencensus-ext-gevent/opencensus/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/contrib/opencensus-ext-gevent/opencensus/ext/__init__.py b/contrib/opencensus-ext-gevent/opencensus/ext/__init__.py new file mode 100644 index 000000000..69e3be50d --- /dev/null +++ b/contrib/opencensus-ext-gevent/opencensus/ext/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/contrib/opencensus-ext-gevent/opencensus/ext/gevent/__init__.py b/contrib/opencensus-ext-gevent/opencensus/ext/gevent/__init__.py new file mode 100644 index 000000000..69e3be50d --- /dev/null +++ b/contrib/opencensus-ext-gevent/opencensus/ext/gevent/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/contrib/opencensus-ext-gevent/opencensus/ext/gevent/geventcompatibility.py b/contrib/opencensus-ext-gevent/opencensus/ext/gevent/geventcompatibility.py new file mode 100644 index 000000000..f1f662c48 --- /dev/null +++ b/contrib/opencensus-ext-gevent/opencensus/ext/gevent/geventcompatibility.py @@ -0,0 +1,21 @@ +import logging +import gevent.monkey + + +def patch_opencensus(event): + # Switch from the default runtime context using ContextVar to one + # using thread locals. Needed until gevent supports ContextVar. + # See https://github.com/gevent/gevent/issues/1407 + import opencensus.common.runtime_context as runtime_context + + if not gevent.monkey.is_module_patched("contextvars"): + runtime_context.RuntimeContext = ( + runtime_context._ThreadLocalRuntimeContext() + ) + + logging.warning("OpenCensus patched for gevent compatibility") + else: + logging.warning( + "OpenCensus is already compatible with your gevent version. " + "Feel free to uninstall the opencensus-ext-gevent package." + ) diff --git a/contrib/opencensus-ext-gevent/setup.cfg b/contrib/opencensus-ext-gevent/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/contrib/opencensus-ext-gevent/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/contrib/opencensus-ext-gevent/setup.py b/contrib/opencensus-ext-gevent/setup.py new file mode 100644 index 000000000..7e3dea1f9 --- /dev/null +++ b/contrib/opencensus-ext-gevent/setup.py @@ -0,0 +1,56 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from setuptools import find_packages +from setuptools import setup +from version import __version__ + +setup( + name='opencensus-ext-gevent', + version=__version__, # noqa + author='OpenCensus Authors', + author_email='census-developers@googlegroups.com', + classifiers=[ + 'Intended Audience :: Developers', + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ], + description='OpenCensus gevent compatibility helper', + include_package_data=True, + long_description=open('README.rst').read(), + install_requires=[ + 'opencensus >= 0.6.dev0, < 1.0.0', + 'gevent >= 1.3' + ], + extras_require={}, + license='Apache-2.0', + packages=find_packages(exclude=('tests',)), + namespace_packages=[], + url='https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-gevent', # noqa: E501 + zip_safe=False, + entry_points={ + "gevent.plugins.monkey.did_patch_builtins": [ + "opencensus = opencensus.ext.gevent.geventcompatibility:patch_opencensus" # noqa: E501 + ] + }, +) diff --git a/contrib/opencensus-ext-gevent/tests/test_patching.py b/contrib/opencensus-ext-gevent/tests/test_patching.py new file mode 100644 index 000000000..08955fdd8 --- /dev/null +++ b/contrib/opencensus-ext-gevent/tests/test_patching.py @@ -0,0 +1,96 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import opencensus.common.runtime_context as runtime_context +import gevent.monkey + +import mock + + +class TestPatching(unittest.TestCase): + def setUp(self): + self.original_context = runtime_context.RuntimeContext + + def tearDown(self): + runtime_context.RuntimeContext = self.original_context + + @mock.patch("gevent.monkey.is_module_patched", return_value=False) + def test_context_is_switched_without_contextvar_support( + self, patched_is_module_patched + ): + # patched_is_module_patched.return_value = False + + # Trick gevent into thinking it is run for the first time. + # Allows to run multiple tests. + gevent.monkey.saved = {} + + # All module patching is disabled to avoid the need of "unpatching". + # The needed events are emitted nevertheless. + gevent.monkey.patch_all( + contextvar=False, + socket=False, + dns=False, + time=False, + select=False, + thread=False, + os=False, + ssl=False, + httplib=False, + subprocess=False, + sys=False, + aggressive=False, + Event=False, + builtins=False, + signal=False, + queue=False + ) + + assert isinstance( + runtime_context.RuntimeContext, + runtime_context._ThreadLocalRuntimeContext, + ) + + @mock.patch("gevent.monkey.is_module_patched", return_value=True) + def test_context_is_switched_with_contextvar_support( + self, patched_is_module_patched + ): + + # Trick gevent into thinking it is run for the first time. + # Allows to run multiple tests. + gevent.monkey.saved = {} + + # All module patching is disabled to avoid the need of "unpatching". + # The needed events are emitted nevertheless. + gevent.monkey.patch_all( + contextvar=False, + socket=False, + dns=False, + time=False, + select=False, + thread=False, + os=False, + ssl=False, + httplib=False, + subprocess=False, + sys=False, + aggressive=False, + Event=False, + builtins=False, + signal=False, + queue=False + ) + + assert runtime_context.RuntimeContext is self.original_context diff --git a/contrib/opencensus-ext-gevent/version.py b/contrib/opencensus-ext-gevent/version.py new file mode 100644 index 000000000..f3a64a892 --- /dev/null +++ b/contrib/opencensus-ext-gevent/version.py @@ -0,0 +1,15 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = '0.1.dev0' diff --git a/nox.py b/nox.py index dc18a4d20..3f2cc1ab0 100644 --- a/nox.py +++ b/nox.py @@ -27,6 +27,7 @@ def _install_dev_packages(session): session.install('-e', 'contrib/opencensus-ext-dbapi') session.install('-e', 'contrib/opencensus-ext-django') session.install('-e', 'contrib/opencensus-ext-flask') + session.install('-e', 'contrib/opencensus-ext-gevent') session.install('-e', 'contrib/opencensus-ext-grpc') session.install('-e', 'contrib/opencensus-ext-httplib') session.install('-e', 'contrib/opencensus-ext-jaeger')