-
-
Notifications
You must be signed in to change notification settings - Fork 756
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use deffered imports and nicer import-from-string behaviors (#134)
* Use defered imports and nicer import-from-string behaviors * Black formatting * importlib related changes for Python 3.5 compat
- Loading branch information
1 parent
c1f1d17
commit 1f922a5
Showing
14 changed files
with
230 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Used by test_importer.py | ||
|
||
myattr = 123 | ||
|
||
import does_not_exist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from uvicorn.protocols.http.httptools import HttpToolsProtocol | ||
from uvicorn.protocols.http.auto import AutoHTTPProtocol | ||
from uvicorn.loops.auto import auto_loop_setup | ||
import asyncio | ||
import uvloop | ||
|
||
# TODO: Add pypy to our testing matrix, and assert we get the correct classes | ||
# dependent on the platform we're running the tests under. | ||
|
||
|
||
def test_http_auto(): | ||
protocol = AutoHTTPProtocol(app=None) | ||
assert isinstance(protocol, HttpToolsProtocol) | ||
|
||
|
||
def test_loop_auto(): | ||
loop = auto_loop_setup() | ||
assert isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from uvicorn.importer import import_from_string, ImportFromStringError | ||
import pytest | ||
import os | ||
import sys | ||
|
||
|
||
def test_invalid_format(): | ||
with pytest.raises(ImportFromStringError) as exc: | ||
import_from_string("example:") | ||
expected = 'Import string "example:" must be in format "<module>:<attribute>".' | ||
assert expected in str(exc) | ||
|
||
|
||
def test_invalid_module(): | ||
with pytest.raises(ImportFromStringError) as exc: | ||
import_from_string("module_does_not_exist:myattr") | ||
expected = 'Could not import module "module_does_not_exist".' | ||
assert expected in str(exc) | ||
|
||
|
||
def test_invalid_attr(): | ||
with pytest.raises(ImportFromStringError) as exc: | ||
import_from_string("tempfile:attr_does_not_exist") | ||
expected = 'Attribute "attr_does_not_exist" not found in module "tempfile".' | ||
assert expected in str(exc) | ||
|
||
|
||
def test_internal_import_error(): | ||
with pytest.raises(ImportError) as exc: | ||
import_from_string("tests.raise_import_error:myattr") | ||
|
||
|
||
def test_valid_import(): | ||
instance = import_from_string("tempfile:TemporaryFile") | ||
from tempfile import TemporaryFile | ||
|
||
assert instance == TemporaryFile | ||
|
||
|
||
def test_no_import_needed(): | ||
from tempfile import TemporaryFile | ||
|
||
instance = import_from_string(TemporaryFile) | ||
assert instance == TemporaryFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import importlib | ||
|
||
|
||
class ImportFromStringError(Exception): | ||
pass | ||
|
||
|
||
def import_from_string(import_str): | ||
if not isinstance(import_str, str): | ||
return import_str | ||
|
||
module_str, _, attrs_str = import_str.partition(":") | ||
if not module_str or not attrs_str: | ||
message = ( | ||
'Import string "{import_str}" must be in format "<module>:<attribute>".' | ||
) | ||
raise ImportFromStringError(message.format(import_str=import_str)) | ||
|
||
try: | ||
module = importlib.import_module(module_str) | ||
except ImportError as exc: | ||
if exc.name != module_str: | ||
raise | ||
message = 'Could not import module "{module_str}".' | ||
raise ImportFromStringError(message.format(module_str=module_str)) | ||
|
||
instance = module | ||
try: | ||
for attr_str in attrs_str.split("."): | ||
instance = getattr(instance, attr_str) | ||
except AttributeError: | ||
message = 'Attribute "{attrs_str}" not found in module "{module_str}".' | ||
raise ImportFromStringError( | ||
message.format(attrs_str=attrs_str, module_str=module_str) | ||
) | ||
|
||
return instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import asyncio | ||
|
||
|
||
def asyncio_setup(): | ||
return asyncio.get_event_loop() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
def auto_loop_setup(): | ||
try: | ||
import uvloop | ||
except ImportError: # pragma: no cover | ||
from uvicorn.loops.asyncio import asyncio_setup | ||
|
||
return asyncio_setup() | ||
else: | ||
from uvicorn.loops.uvloop import uvloop_setup | ||
|
||
return uvloop_setup() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import asyncio | ||
import uvloop | ||
|
||
|
||
def uvloop_setup(): | ||
asyncio.get_event_loop().close() | ||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) | ||
return asyncio.get_event_loop() |
Oops, something went wrong.