Commit f2028450 authored by xa's avatar xa

run func in executor

parent a2a8eeb8
from __future__ import annotations
import asyncio
import concurrent.futures
import logging
from abc import ABCMeta
from collections import defaultdict, OrderedDict
from itertools import chain
from weakref import WeakKeyDictionary
from collections import OrderedDict, defaultdict
from functools import wraps
from inspect import signature
from itertools import chain
from weakref import WeakKeyDictionary
from cached_property import cached_property
logger = logging.getLogger("knighted")
......@@ -19,7 +22,7 @@ class Factory:
def __call__(self, note, func=None):
def decorate(func):
self.target.factories[note] = asyncio.coroutine(func)
self.target.factories[note] = func
return func
if func:
return decorate(func)
......@@ -96,6 +99,10 @@ class Injector(metaclass=ABCMeta):
self.reactions = defaultdict(WeakKeyDictionary)
self.close = CloseHandler(self)
@cached_property
def executor(self):
return concurrent.futures.ThreadPoolExecutor(max_workers=10)
@asyncio.coroutine
def get(self, note):
if note in self.services:
......@@ -103,7 +110,13 @@ class Injector(metaclass=ABCMeta):
for fact, args in note_loop(note):
if fact in self.factories:
instance = yield from self.factories[fact](*args)
func = self.factories[fact]
print("!", fact, func, asyncio.iscoroutinefunction(func))
if asyncio.iscoroutinefunction(func):
instance = yield from func(*args)
else:
loop = asyncio.get_running_loop()
instance = yield from loop.run_in_executor(self.executor, func, *args)
logger.info('loaded service %s' % note)
self.services[note] = instance
return instance
......
......@@ -13,7 +13,6 @@ setup(
"cached_property"
],
extras_require={
':python_version=="3.3"': ['asyncio'],
},
classifiers=[
"Development Status :: 5 - Production/Stable",
......@@ -21,9 +20,7 @@ setup(
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.7",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules"
],
......
import pytest
from knighted import Injector, annotate
import asyncio
from contextlib import contextmanager
from time import time_ns, sleep
class Timer:
started_at = None
stoped_at = None
duration = None
@contextmanager
def timed():
timer = Timer()
timer.started_at = time_ns()
yield timer
timer.stoped_at = time_ns()
timer.duration = timer.stoped_at - timer.started_at
@pytest.mark.asyncio
......@@ -84,6 +102,40 @@ async def test_fill_the_gaps():
'bar': 'I feel'}
@pytest.mark.slow
@pytest.mark.asyncio
async def test_load_in_parallel():
class MyInjector(Injector):
pass
services = MyInjector()
@services.factory('foo')
def foo_factory():
sleep(1)
return 'I am foo'
@services.factory('bar')
async def bar_factory():
await asyncio.sleep(1)
return 'I am bar'
@services.factory('baz')
async def baz_factory():
await asyncio.sleep(1)
return 'I am baz'
@annotate('foo', 'bar', 'baz')
def fun(foo, bar, baz):
return {'foo': foo,
'bar': bar,
'baz': baz}
with timed() as timer:
await services.apply(fun)
assert timer.duration == pytest.approx(1000000000, rel=1e-2)
@pytest.mark.asyncio
async def test_partial():
class MyInjector(Injector):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment