diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6c3647aa..341d72a2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.19.0 +current_version = 0.20.0 commit = True tag = True diff --git a/README.rst b/README.rst index 97388afa..72ec4d57 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,10 @@ a common operational platform for your python microservices. It integrates with many standard python libraries to give you out-of-the-box logging, metrics, error reporting, status urls and more. +Python version support +---------------------- + +This release of talisker (0.20.0) will be the last to support python 2.7 Quick Start ----------- diff --git a/docs/conf.py b/docs/conf.py index 7cfc02e1..8d2cb00c 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -73,7 +73,7 @@ # for |version| and |release|, also used in various other places throughout # the built documents. # The short X.Y version. -version = '0.19.0' +version = '0.20.0' # The full version, including alpha/beta/rc tags. release = version diff --git a/setup.cfg b/setup.cfg index 79344faf..8137fe04 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ filterwarnings = ignore [metadata] name = talisker -version = 0.19.0 +version = 0.20.0 description = A common WSGI stack long_description = file: README.rst author = Simon Davy diff --git a/setup.py b/setup.py index 9ecf710f..2412ca0c 100644 --- a/setup.py +++ b/setup.py @@ -203,6 +203,6 @@ ], test_suite='tests', url='https://github.com/canonical-ols/talisker', - version='0.19.0', + version='0.20.0', zip_safe=False, ) diff --git a/talisker/__init__.py b/talisker/__init__.py index f543c7c0..494d3cba 100644 --- a/talisker/__init__.py +++ b/talisker/__init__.py @@ -48,7 +48,7 @@ request_timeout, ) -__version__ = '0.19.0' +__version__ = '0.20.0' __all__ = [ 'initialise', 'get_config', diff --git a/talisker/util.py b/talisker/util.py index d756b985..607f1429 100644 --- a/talisker/util.py +++ b/talisker/util.py @@ -276,7 +276,11 @@ def __init__(self): def _check(self): pid = os.getpid() - if self._local._pid != pid: + if not hasattr(self._local, '_pid'): + # In a new thread - just add the missing _pid attr. + self._local._pid = pid + elif self._local._pid != pid: + # In a new process - discard the thread local. self._local = threading.local() self._local._pid = pid diff --git a/tests/test_gunicorn.py b/tests/test_gunicorn.py index 27726cbe..c674cd37 100644 --- a/tests/test_gunicorn.py +++ b/tests/test_gunicorn.py @@ -220,7 +220,7 @@ def stats(): sleep_factor = 1 if os.environ.get('CI') == 'true': # travis is slow - sleep_factor = 10 + sleep_factor = 20 with server: # forking can be really slow on travis, so make sure *all* the workers diff --git a/tests/test_requests.py b/tests/test_requests.py index b98e9802..a4b2e89d 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -38,6 +38,7 @@ import requests import responses import socket +import threading import time from future.moves.urllib.parse import urlunsplit @@ -767,6 +768,21 @@ def test_adapter_callsite_retries(mock_urllib3, backends): ] +def test_threadlocal_threading(): + session = talisker.requests.get_session() + thread_results = {'ok': False} + + def f(results): + new_session = talisker.requests.get_session() + results['ok'] = new_session is not session + + thread = threading.Thread(target=f, args=(thread_results,)) + thread.start() + thread.join(timeout=1.1) + + assert thread_results['ok'] + + def test_threadlocal_forking(): session = talisker.requests.get_session() if os.fork() == 0: diff --git a/tests/test_util.py b/tests/test_util.py index faa5fb92..6cf7c9db 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -32,6 +32,7 @@ import os import sys +import threading import time from freezegun import freeze_time @@ -122,10 +123,36 @@ def test_get_errno_fields_dns(): } -def test_local(): +def test_local_forking(): local = util.Local() local.test = 1 if os.fork() == 0: assert not hasattr(local, 'test') + local.test = 2 + assert local.test == 2 os._exit(0) + + +def test_local_threading(): + local = util.Local() + local.test = 1 + + thread_results = {} + + def f(results): + results['no_attr'] = not hasattr(local, 'test') + try: + local.test = 2 + except Exception: + pass + else: + results['new_attr'] = local.test + + thread = threading.Thread(target=f, args=(thread_results,)) + thread.start() + thread.join(timeout=1.1) + + assert local.test == 1 + assert thread_results['no_attr'] + assert thread_results['new_attr'] == 2