Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates and modernization #147

Merged
merged 13 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,14 @@ jobs:
name: "Python ${{ matrix.python-version }} ${{ matrix.platform }} ${{ matrix.architecture }}"
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev", "pypy3"]
architecture: ["x64"]
platform: ["ubuntu-latest"]
include:
- python-version: "3.6"
architecture: "x86"
platform: "windows-latest"
- python-version: "3.6"
architecture: "x64"
platform: "windows-latest"
python-version: ["3.9", "3.10", "3.11", "pypy3.9"]
platform: ["ubuntu-latest", "windows-latest"]

runs-on: "${{ matrix.platform }}"

steps:
- uses: "actions/checkout@v2"
- uses: "actions/setup-python@v2"
- uses: "actions/checkout@v3"
- uses: "actions/setup-python@v4"
with:
python-version: "${{ matrix.python-version }}"
architecture: "${{ matrix.architecture }}"
Expand All @@ -37,7 +29,7 @@ jobs:
python -VV
python -m site
python -m pip install --upgrade pip setuptools wheel
python -m pip install --upgrade virtualenv tox tox-gh-actions
python -m pip install --upgrade virtualenv tox tox-gh-actions

- name: "Run tox targets for ${{ matrix.python-version }}"
run: "python -m tox"
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ Bugs and feature requests should be filed at the project `Github page`_.
API and features
================

Crochet supports Python 3.6, 3.7, 3.8, and 3.9 as well as PyPy3.
Python 2.7 and 3.5 support is available in older releases.
Crochet supports Python 3.8, 3.9, 3.10, and 3.11 as well as PyPy3.

Crochet provides the following basic APIs:

Expand Down
46 changes: 17 additions & 29 deletions crochet/_eventloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
Expose Twisted's event loop to threaded programs.
"""

from __future__ import absolute_import

import select
import threading
import weakref
import warnings
from inspect import iscoroutinefunction
from functools import wraps
from queue import SimpleQueue

from twisted.python import threadable
from twisted.python.runtime import platform
Expand Down Expand Up @@ -227,30 +225,20 @@ def original_failure(self):
return None


_STOP = object()


class ThreadLogObserver(object):
"""
A log observer that wraps another observer, and calls it in a thread.

In particular, used to wrap PythonLoggingObserver, so that blocking
logging.py Handlers don't block the event loop.

Once Python 3.6 support is dropped, this can use a queue.SimpleQueue object
instead of a whole 'nother event loop.
"""

def __init__(self, observer):
self._observer = observer
if getattr(select, "epoll", None):
from twisted.internet.epollreactor import EPollReactor
reactorFactory = EPollReactor
elif getattr(select, "poll", None):
from twisted.internet.pollreactor import PollReactor
reactorFactory = PollReactor
else:
from twisted.internet.selectreactor import SelectReactor
reactorFactory = SelectReactor
self._logWritingReactor = reactorFactory()
self._logWritingReactor._registerAsIOThread = False
self._queue = SimpleQueue()
self._thread = threading.Thread(
target=self._reader, name="CrochetLogWriter")
self._thread.start()
Expand All @@ -260,28 +248,28 @@ def _reader(self):
Runs in a thread, reads messages from a queue and writes them to
the wrapped observer.
"""
self._logWritingReactor.run(installSignalHandlers=False)
while True:
msg = self._queue.get()
if msg is _STOP:
return
try:
self._observer(msg)
except Exception:
# Lower-level logging system blew up, nothing we can do, so
# just drop on the floor.
pass

def stop(self):
"""
Stop the thread.
"""
self._logWritingReactor.callFromThread(self._logWritingReactor.stop)
self._queue.put(_STOP)

def __call__(self, msg):
"""
A log observer that writes to a queue.
"""

def log():
try:
self._observer(msg)
except Exception:
# Lower-level logging system blew up, nothing we can do, so
# just drop on the floor.
pass

self._logWritingReactor.callFromThread(log)
self._queue.put(msg)


class EventLoop(object):
Expand Down
52 changes: 4 additions & 48 deletions crochet/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import weakref
import tempfile
import os
import imp
import inspect
from unittest import SkipTest

Expand All @@ -26,7 +25,7 @@
EventLoop, EventualResult, TimeoutError, ResultRegistry, ReactorStopped)
from .test_setup import FakeReactor
from .. import (
_main, setup, retrieve_result, _store, no_setup,
_main, setup as setup_crochet, retrieve_result, _store, no_setup,
run_in_reactor, wait_for)
from ..tests import crochet_directory

Expand Down Expand Up @@ -207,7 +206,8 @@ def test_timeout(self):
start = time.time()
dr = EventualResult(Deferred(), None)
self.assertRaises(TimeoutError, dr.wait, timeout=0.03)
self.assertTrue(abs(time.time() - start - 0.03) < 0.005)
# be a little lenient for slow computers:
self.assertTrue(abs(time.time() - start) < 0.05)

def test_timeout_twice(self):
"""
Expand Down Expand Up @@ -525,50 +525,6 @@ def test_noWaitingDuringImport(self):
""")
self.assertRaises(RuntimeError, __import__, "shouldbeunimportable")

def test_waiting_during_different_thread_importing(self):
"""
EventualResult.wait() should work if called while a module is
being imported in a different thread. See
EventualResultTests.test_noWaitingDuringImport for the explanation of
what should happen if an import is happening in the current thread.
"""
test_complete = threading.Event()
lock_held = threading.Event()
er = EventualResult(succeed(123), None)

def other_thread():
imp.acquire_lock()
lock_held.set()
test_complete.wait()
imp.release_lock()

t = threading.Thread(target=other_thread)
t.start()
lock_held.wait()

# While the imp lock is held by the other thread, we can't
# allow exceptions/assertions to happen because trial will
# try to do an import causing a deadlock instead of a
# failure. We collect all assertion pairs (result, expected),
# wait for the import lock to be released, and then check our
# assertions at the end of the test.
assertions = []

# we want to run .wait while the other thread has the lock acquired
assertions.append((imp.lock_held(), True))
try:
assertions.append((er.wait(0.1), 123))
finally:
test_complete.set()

assertions.append((imp.lock_held(), True))

test_complete.set()

t.join()

[self.assertEqual(result, expected) for result, expected in assertions]


class RunInReactorTests(TestCase):
"""
Expand Down Expand Up @@ -1100,7 +1056,7 @@ def test_eventloop_api(self):
from twisted.python.log import startLoggingWithObserver
from crochet import _shutdown
self.assertIsInstance(_main, EventLoop)
self.assertEqual(_main.setup, setup)
self.assertEqual(_main.setup, setup_crochet)
self.assertEqual(_main.no_setup, no_setup)
self.assertEqual(_main.run_in_reactor, run_in_reactor)
self.assertEqual(_main.wait_for, wait_for)
Expand Down
6 changes: 6 additions & 0 deletions docs/news.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
What's New
==========

2.1.0
^^^^^

* Various internal modernizations and maintenance.
* Dropped Python 3.6 and 3.7 support.

2.0.0
^^^^^

Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ def read(path):
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
],
name='crochet',
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
description="Use Twisted anywhere!",
python_requires=">=3.6.0",
python_requires=">=3.8.0",
install_requires=[
"Twisted>=16.0",
"wrapt",
Expand Down
13 changes: 6 additions & 7 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
[tox]
envlist = lint3,{py36,py37,pypy3,py38,py39,py310}
envlist = lint3,{pypy3,py38,py39,py310,py311}

[gh-actions]
python =
3.6: py36-twisted-16,py36
3.7: py37-twisted-16,py37
3.8: py38
3.9: py39,lint3
3.10-dev: py310
pypy3: pypy3
3.9: py39
3.10: py310
3.11: py311,lint3
pypy3.9: pypy3

[testenv]
deps =
py36,py37,py38,py39: mypy==0.812
mypy
commands =
{envpython} setup.py --version
pip install .
Expand Down