Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support coroutine functions as story step definitions
You can use async def syntax to define story steps. Story objects become available, so you can apply in your aiohttp views. All story steps should be either coroutines or functions. Most part of the work in this PR was done by @supadrupa and @thedrow
- Loading branch information
dry-python-bot
authored and
dry-python-bot
committed
Mar 6, 2020
1 parent
a4da2e3
commit 55cbfda
Showing
14 changed files
with
271 additions
and
37 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,3 @@ | ||
from typing import Callable | ||
|
||
def iscoroutinefunction(func: Callable) -> bool: ... |
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,40 @@ | ||
# -*- coding: utf-8 -*- | ||
from _stories.compat import iscoroutinefunction | ||
from _stories.exceptions import StoryDefinitionError | ||
from _stories.execute import function | ||
|
||
try: | ||
from _stories.execute import coroutine | ||
except SyntaxError: | ||
pass | ||
|
||
|
||
def get_executor(method, previous, cls_name, story_name): | ||
if iscoroutinefunction(method): | ||
executor = coroutine.execute | ||
other_kind = "function" | ||
else: | ||
executor = function.execute | ||
other_kind = "coroutine" | ||
|
||
if previous is not executor and previous is not None: | ||
message = mixed_method_template.format( | ||
other_kind=other_kind, | ||
cls=cls_name, | ||
method=method.__name__, | ||
story_name=story_name, | ||
) | ||
raise StoryDefinitionError(message) | ||
return executor | ||
|
||
|
||
# Messages. | ||
|
||
|
||
mixed_method_template = """ | ||
Coroutines and functions can not be used together in story definition. | ||
This method should be a {other_kind}: {cls}.{method} | ||
Story method: {cls}.{story_name} | ||
""".strip() |
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,15 @@ | ||
from typing import Callable | ||
from typing import Optional | ||
from typing import Union | ||
|
||
from _stories.returned import Failure | ||
from _stories.returned import Result | ||
from _stories.returned import Skip | ||
from _stories.returned import Success | ||
|
||
def get_executor( | ||
method: Callable[[object], Union[Result, Success, Failure, Skip]], | ||
previous: Optional[Callable], | ||
cls_name: str, | ||
story_name: str, | ||
) -> Callable: ... |
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,78 @@ | ||
# -*- coding: utf-8 -*- | ||
from _stories.context import assign_namespace | ||
from _stories.marker import BeginningOfStory | ||
from _stories.marker import EndOfStory | ||
from _stories.returned import Failure | ||
from _stories.returned import Result | ||
from _stories.returned import Skip | ||
from _stories.returned import Success | ||
|
||
|
||
async def execute(runner, ctx, ns, lines, history, methods): | ||
__tracebackhide__ = True | ||
|
||
skipped = 0 | ||
|
||
for method, contract, protocol in methods: | ||
|
||
method_type = type(method) | ||
|
||
if skipped > 0: | ||
if method_type is EndOfStory: | ||
skipped -= 1 | ||
elif method_type is BeginningOfStory: | ||
skipped += 1 | ||
continue | ||
|
||
if method_type is BeginningOfStory: | ||
history.on_substory_start(method.story_name) | ||
try: | ||
contract.check_substory_call(ctx, ns) | ||
except Exception as error: | ||
history.on_error(error.__class__.__name__) | ||
raise | ||
continue | ||
|
||
if method_type is EndOfStory: | ||
history.on_substory_end() | ||
continue | ||
|
||
history.before_call(method.__name__) | ||
|
||
try: | ||
result = await method(ctx) | ||
except Exception as error: | ||
history.on_error(error.__class__.__name__) | ||
raise | ||
|
||
restype = type(result) | ||
if restype not in (Result, Success, Failure, Skip): | ||
raise AssertionError | ||
|
||
if restype is Failure: | ||
try: | ||
protocol.check_return_statement(method, result.reason) | ||
except Exception as error: | ||
history.on_error(error.__class__.__name__) | ||
raise | ||
history.on_failure(result.reason) | ||
return runner.got_failure(ctx, method.__name__, result.reason) | ||
|
||
if restype is Result: | ||
history.on_result(result.value) | ||
return runner.got_result(result.value) | ||
|
||
if restype is Skip: | ||
history.on_skip() | ||
skipped = 1 | ||
continue | ||
|
||
try: | ||
kwargs = contract.check_success_statement(method, ctx, ns, result.kwargs) | ||
except Exception as error: | ||
history.on_error(error.__class__.__name__) | ||
raise | ||
|
||
assign_namespace(ns, lines, method, kwargs) | ||
|
||
return runner.finished() |
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,55 @@ | ||
from typing import Any | ||
from typing import Callable | ||
from typing import List | ||
from typing import overload | ||
from typing import Tuple | ||
from typing import Union | ||
|
||
from _stories.contract import NullContract | ||
from _stories.contract import SpecContract | ||
from _stories.failures import DisabledNullExecProtocol | ||
from _stories.failures import NotNullExecProtocol | ||
from _stories.failures import NullExecProtocol | ||
from _stories.history import History | ||
from _stories.marker import BeginningOfStory | ||
from _stories.marker import EndOfStory | ||
from _stories.returned import Failure | ||
from _stories.returned import Result | ||
from _stories.returned import Skip | ||
from _stories.returned import Success | ||
from _stories.run import Call | ||
from _stories.run import Run | ||
@overload | ||
async def execute( | ||
runner: Call, | ||
ctx: object, | ||
history: History, | ||
methods: List[ | ||
Tuple[ | ||
Union[ | ||
BeginningOfStory, | ||
Callable[[object], Union[Result, Success, Failure, Skip]], | ||
EndOfStory, | ||
], | ||
Union[NullContract, SpecContract], | ||
Union[NullExecProtocol, DisabledNullExecProtocol, NotNullExecProtocol], | ||
], | ||
], | ||
) -> Any: ... | ||
@overload | ||
async def execute( | ||
runner: Run, | ||
ctx: object, | ||
history: History, | ||
methods: List[ | ||
Tuple[ | ||
Union[ | ||
BeginningOfStory, | ||
Callable[[object], Union[Result, Success, Failure, Skip]], | ||
EndOfStory, | ||
], | ||
Union[NullContract, SpecContract], | ||
Union[NullExecProtocol, DisabledNullExecProtocol, NotNullExecProtocol], | ||
], | ||
], | ||
) -> object: ... |
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
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
Oops, something went wrong.