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

Await future for keepalive thread after creation #57

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion mode/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ def human_tracebacks(self) -> str:
)

def _on_future_done(self, fut: asyncio.Future) -> None:
self._futures.discard(fut)
return self._futures.discard(fut)

def __post_init__(self) -> None:
"""Additional user initialization."""
Expand Down
3 changes: 2 additions & 1 deletion mode/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ async def start(self) -> None:
assert not self._thread_started.is_set()
self._thread_started.set()
self._thread_running = asyncio.Future(loop=self.parent_loop)
self.add_future(self._keepalive2())
fut = self.add_future(self._keepalive2())
try:
self._thread = self.Worker(self)
self._thread.start()
Expand All @@ -183,6 +183,7 @@ async def start(self) -> None:

# wait for thread to be fully started
await self._thread_running
await fut
finally:
self._thread_running = None

Expand Down
63 changes: 61 additions & 2 deletions mode/utils/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,25 @@
Tuple,
Type,
TypeVar,
_eval_type,
cast,
)

try:
from typing import _eval_type # type: ignore
except ImportError:

def _eval_type(t, globalns, localns, recursive_guard=frozenset()): # type: ignore
return t


try:
from typing import _type_check # type: ignore
except ImportError:

def _type_check(arg, msg, is_argument=True, module=None): # type: ignore
return arg


try:
from typing import _ClassVar # type: ignore
except ImportError: # pragma: no cover
Expand All @@ -48,6 +63,25 @@ def _is_class_var(x: Any) -> bool:
return type(x) is _ClassVar


if typing.TYPE_CHECKING:

class ForwardRef:
__forward_arg__: str
__forward_evaluated__: bool
__forward_value__: Type
__forward_code__: Any

def __init__(self, arg: str, is_argument: bool = True) -> None: ...

else:
try:
# CPython 3.7
from typing import ForwardRef
except ImportError: # pragma: no cover
# CPython 3.6
from typing import _ForwardRef as ForwardRef


__all__ = [
"FieldMapping",
"DefaultsMapping",
Expand Down Expand Up @@ -374,7 +408,9 @@ def eval_type(
if isinstance(typ, str):
typ = ForwardRef(typ)
if isinstance(typ, ForwardRef):
if sys.version_info < (3, 9):
if not typ.__forward_evaluated__:
typ = _ForwardRef_safe_eval(typ, globalns, localns)
elif sys.version_info < (3, 9):
typ = typ._evaluate(globalns, localns)
else:
typ = typ._evaluate(globalns, localns, frozenset())
Expand All @@ -384,6 +420,29 @@ def eval_type(
return alias_types.get(typ, typ)


def _ForwardRef_safe_eval(
ref: ForwardRef,
globalns: Optional[Dict[str, Any]] = None,
localns: Optional[Dict[str, Any]] = None,
) -> Type:
# On 3.6/3.7 ForwardRef._evaluate crashes if str references ClassVar
if not ref.__forward_evaluated__:
if globalns is None and localns is None:
globalns = localns = {}
elif globalns is None:
globalns = localns
elif localns is None:
localns = globalns
val = eval(ref.__forward_code__, globalns, localns) # noqa: S307
if not _is_class_var(val):
val = _type_check(
val, "Forward references must evaluate to types."
)
ref.__forward_value__ = val
ref.__forward_evaluated__ = True
return ref.__forward_value__


def _get_globalns(typ: Type) -> Dict[str, Any]:
return sys.modules[typ.__module__].__dict__

Expand Down
Loading