Skip to content

Commit 4fb500e

Browse files
committed
Add asyncio dispatcher_callback
1 parent e801a50 commit 4fb500e

14 files changed

+366
-152
lines changed

.github/workflows/code.yml

+29-17
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,36 @@ jobs:
3232
os: [ubuntu-latest, windows-latest, macos-latest]
3333
python: [cp27, cp37, cp38, cp39]
3434

35-
include:
36-
- os: macos-latest
37-
TEST_REQUIRES: pytest-cov cothread
38-
# Put coverage straight in the dist dir
39-
COV_FILE: '{project}/dist/coverage.xml'
35+
exclude:
36+
- os: windows-latest
37+
# No cothread or asyncio so doesn't work
38+
python: cp27
4039

40+
include:
4141
- os: ubuntu-latest
42-
TEST_REQUIRES: pytest-cov cothread
4342
# Put coverage in the output dir mounted in docker
44-
COV_FILE: '/output/coverage.xml'
43+
cov_file: /output/coverage.xml
44+
test_requires: cothread pytest-asyncio aioca
4545

4646
- os: windows-latest
47+
cov_file: '{project}/dist/coverage.xml'
4748
# cothread doesn't work on windows
48-
TEST_REQUIRES: pytest-cov
49-
# Put coverage straight in the dist dir
50-
COV_FILE: '{project}/dist/coverage.xml'
49+
test_requires: pytest-asyncio aioca
50+
51+
- os: macos-latest
52+
cov_file: '{project}/dist/coverage.xml'
53+
test_requires: cothread pytest-asyncio aioca
54+
55+
- os: ubuntu-latest
56+
python: cp27
57+
# asyncio doesn't work on Python2.7
58+
test_requires: cothread
59+
60+
- os: macos-latest
61+
python: cp27
62+
# asyncio doesn't work on Python2.7
63+
test_requires: cothread
64+
5165

5266
steps:
5367
- name: Checkout Source
@@ -70,15 +84,16 @@ jobs:
7084
run: cibuildwheel --output-dir dist
7185
env:
7286
CIBW_BUILD: ${{ matrix.python }}*64
73-
CIBW_TEST_REQUIRES: ${{ matrix.TEST_REQUIRES }}
74-
CIBW_TEST_COMMAND: pytest --cov=softioc {project}/tests --cov-report xml:${{ matrix.COV_FILE }}
87+
CIBW_TEST_REQUIRES: pytest-cov ${{ matrix.test_requires }}
88+
CIBW_TEST_COMMAND: pytest --cov=softioc {project}/tests --cov-report xml:${{ matrix.cov_file }}
7589
# Disable auditwheel as it isn't compatible with setuptools_dso approach
7690
# https://github.com/mdavidsaver/setuptools_dso/issues/17
7791
CIBW_REPAIR_WHEEL_COMMAND: ''
7892

7993
- name: Upload Wheel and Sdist
8094
uses: actions/upload-artifact@v2
8195
with:
96+
name: dist
8297
path: dist/softioc*
8398

8499
- name: Upload coverage to Codecov
@@ -91,17 +106,14 @@ jobs:
91106
needs: [build]
92107
runs-on: ubuntu-latest
93108
# upload to PyPI on every tag
94-
#if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
109+
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
95110
steps:
96111
- uses: actions/download-artifact@v2
97112
with:
113+
name: dist
98114
path: dist
99115

100-
- name: Display structure of downloaded files
101-
run: ls -R
102-
103116
- name: Publish to PyPI
104-
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
105117
env:
106118
TWINE_USERNAME: __token__
107119
TWINE_PASSWORD: ${{ secrets.pypi_token }}

Pipfile

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ sphinx-rtd-theme = "*"
1111
sphinx-multiversion = {editable = true,git = "https://github.com/dls-controls/sphinx-multiversion.git",ref = "only-arg"}
1212
setuptools-dso = "*"
1313
aioca = "*"
14+
pytest-asyncio = "*"
1415

1516
[packages]
1617
# All other package requirements from setup.py
@@ -20,6 +21,7 @@ epicscorelibs = "*"
2021
# Add some other useful extras
2122
cothread = "*"
2223
scipy = "*"
24+
aioca = "*"
2325

2426
[scripts]
2527
# Put coverage here so we don't interfere with debugging in the IDE

Pipfile.lock

+84-58
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

softioc/asyncio_callback.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
import inspect
3+
import threading
4+
5+
6+
class AsyncioCallback(threading.Thread):
7+
def __init__(self):
8+
super().__init__()
9+
self.loop = asyncio.new_event_loop()
10+
11+
def run(self):
12+
self.loop.run_forever()
13+
14+
def __call__(self, func, *args):
15+
async def async_wrapper():
16+
ret = func(*args)
17+
if inspect.isawaitable(ret):
18+
await ret
19+
asyncio.run_coroutine_threadsafe(async_wrapper(), self.loop)

softioc/device.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
import os
22
import time
3-
import traceback
3+
import inspect
44
from ctypes import *
55
import numpy
66

7-
import cothread
8-
97
from . import alarm
108
from .fields import DbfCodeToNumpy, DbrToDbfCode
119
from .imports import dbLoadDatabase, recGblResetAlarms, db_put_field
1210
from .device_core import DeviceSupportCore, RecordLookup
1311

1412

13+
dispatcher_callback = None
14+
15+
16+
def use_cothread():
17+
import cothread
18+
# Create our own cothread callback queue so that our callbacks
19+
# processing doesn't interfere with other callback processing.
20+
global dispatcher_callback
21+
dispatcher_callback = cothread.cothread._Callback()
22+
23+
24+
def use_asyncio():
25+
from .asyncio_callback import AsyncioCallback
26+
global dispatcher_callback
27+
dispatcher_callback = AsyncioCallback()
28+
dispatcher_callback.start()
29+
30+
1531
class ProcessDeviceSupportCore(DeviceSupportCore, RecordLookup):
1632
'''Implements canonical default processing for records with a _process
1733
method. Processing typically either copies a locally set value into the
@@ -79,10 +95,6 @@ def get(self):
7995
class ProcessDeviceSupportOut(ProcessDeviceSupportCore):
8096
_link_ = 'OUT'
8197

82-
# Create our own cothread callback queue so that our callbacks processing
83-
# doesn't interfere with other callback processing.
84-
__Callback = cothread.cothread._Callback()
85-
8698
def __init__(self, name, **kargs):
8799
on_update = kargs.pop('on_update', None)
88100
on_update_name = kargs.pop('on_update_name', None)
@@ -133,7 +145,7 @@ def _process(self, record):
133145

134146
self._value = value
135147
if self.__on_update and self.__enable_write:
136-
self.__Callback(self.__on_update, value)
148+
dispatcher_callback(self.__on_update, value)
137149
return 0
138150

139151

0 commit comments

Comments
 (0)