Skip to content

Commit c58d485

Browse files
committed
Add initial typing
The typing is still not optimal and not ready to be exposed, but can serve as an initial basis.
1 parent cbcd78c commit c58d485

27 files changed

+1152
-724
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ repos:
2929
rev: 'v1.8.0'
3030
hooks:
3131
- id: mypy
32+
additional_dependencies:
33+
- pytest
34+
- types-pywin32
35+
- types-gevent

doc/example/redirect_remote_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def write(data):
2626
print("received:", repr(data))
2727

2828

29-
outchan.setcallback(write)
29+
outchan.setcallback(write) # type: ignore[attr-defined]
3030

3131
gw.remote_exec(
3232
"""

doc/example/sysinfo.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import execnet
1313

14-
1514
parser = optparse.OptionParser(usage=__doc__)
1615
parser.add_option(
1716
"-f",
@@ -129,7 +128,7 @@ def getinfo(sshname, ssh_config=None, loginfo=sys.stdout):
129128
if ssh_config:
130129
spec = f"ssh=-F {ssh_config} {sshname}"
131130
else:
132-
spec += "ssh=%s" % sshname
131+
spec = "ssh=%s" % sshname
133132
debug("connecting to", repr(spec))
134133
try:
135134
gw = execnet.makegateway(spec)

pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,18 @@ include = [
9696

9797
[tool.mypy]
9898
python_version = "3.8"
99+
mypy_path = ["src"]
100+
files = ["src", "testing"]
101+
strict = true
102+
warn_unreachable = true
103+
warn_unused_ignores = false
104+
disallow_untyped_calls = false
105+
disallow_untyped_defs = false
106+
disallow_incomplete_defs = false
107+
108+
[[tool.mypy.overrides]]
109+
module = [
110+
"eventlet.*",
111+
"gevent.thread.*",
112+
]
113+
ignore_missing_imports = true

src/execnet/gateway.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,57 @@
22
gateway code for initiating popen, socket and ssh connections.
33
(c) 2004-2013, Holger Krekel and others
44
"""
5+
from __future__ import annotations
6+
57
import inspect
68
import linecache
79
import os
810
import textwrap
911
import types
12+
from typing import TYPE_CHECKING
13+
from typing import Any
14+
from typing import Callable
1015

1116
import execnet
1217

1318
from . import gateway_base
19+
from .gateway_base import IO
20+
from .gateway_base import Channel
1421
from .gateway_base import Message
22+
from .multi import Group
23+
from .xspec import XSpec
1524

1625
importdir = os.path.dirname(os.path.dirname(execnet.__file__))
1726

1827

1928
class Gateway(gateway_base.BaseGateway):
2029
"""Gateway to a local or remote Python Interpreter."""
2130

22-
def __init__(self, io, spec):
31+
_group: Group
32+
33+
def __init__(self, io: IO, spec: XSpec) -> None:
2334
super().__init__(io=io, id=spec.id, _startcount=1)
2435
self.spec = spec
2536
self._initreceive()
2637

2738
@property
28-
def remoteaddress(self):
29-
return self._io.remoteaddress
39+
def remoteaddress(self) -> str:
40+
# Only defined for remote IO types.
41+
return self._io.remoteaddress # type: ignore[attr-defined,no-any-return]
3042

31-
def __repr__(self):
43+
def __repr__(self) -> str:
3244
"""return string representing gateway type and status."""
3345
try:
34-
r = self.hasreceiver() and "receive-live" or "not-receiving"
35-
i = len(self._channelfactory.channels())
46+
r: str = self.hasreceiver() and "receive-live" or "not-receiving"
47+
i = str(len(self._channelfactory.channels()))
3648
except AttributeError:
3749
r = "uninitialized"
3850
i = "no"
3951
return "<{} id={!r} {}, {} model, {} active channels>".format(
4052
self.__class__.__name__, self.id, r, self.execmodel.backend, i
4153
)
4254

43-
def exit(self):
55+
def exit(self) -> None:
4456
"""trigger gateway exit. Defer waiting for finishing
4557
of receiver-thread and subprocess activity to when
4658
group.terminate() is called.
@@ -59,7 +71,9 @@ def exit(self):
5971
self._trace("io-error: could not send termination sequence")
6072
self._trace(" exception: %r" % exc)
6173

62-
def reconfigure(self, py2str_as_py3str=True, py3str_as_py2str=False):
74+
def reconfigure(
75+
self, py2str_as_py3str: bool = True, py3str_as_py2str: bool = False
76+
) -> None:
6377
"""
6478
set the string coercion for this gateway
6579
the default is to try to convert py2 str as py3 str,
@@ -69,7 +83,7 @@ def reconfigure(self, py2str_as_py3str=True, py3str_as_py2str=False):
6983
data = gateway_base.dumps_internal(self._strconfig)
7084
self._send(Message.RECONFIGURE, data=data)
7185

72-
def _rinfo(self, update=False):
86+
def _rinfo(self, update: bool = False) -> RInfo:
7387
"""return some sys/env information from remote."""
7488
if update or not hasattr(self, "_cache_rinfo"):
7589
ch = self.remote_exec(rinfo_source)
@@ -79,11 +93,11 @@ def _rinfo(self, update=False):
7993
ch.waitclose()
8094
return self._cache_rinfo
8195

82-
def hasreceiver(self):
96+
def hasreceiver(self) -> bool:
8397
"""return True if gateway is able to receive data."""
8498
return self._receivepool.active_count() > 0
8599

86-
def remote_status(self):
100+
def remote_status(self) -> RemoteStatus:
87101
"""return information object about remote execution status."""
88102
channel = self.newchannel()
89103
self._send(Message.STATUS, channel.id)
@@ -93,7 +107,11 @@ def remote_status(self):
93107
self._channelfactory._local_close(channel.id)
94108
return RemoteStatus(statusdict)
95109

96-
def remote_exec(self, source, **kwargs):
110+
def remote_exec(
111+
self,
112+
source: str | types.FunctionType | Callable[..., object] | types.ModuleType,
113+
**kwargs: object,
114+
) -> Channel:
97115
"""return channel object and connect it to a remote
98116
execution thread where the given ``source`` executes.
99117
@@ -113,7 +131,7 @@ def remote_exec(self, source, **kwargs):
113131
file_name = None
114132
if isinstance(source, types.ModuleType):
115133
file_name = inspect.getsourcefile(source)
116-
linecache.updatecache(file_name)
134+
linecache.updatecache(file_name) # type: ignore[arg-type]
117135
source = inspect.getsource(source)
118136
elif isinstance(source, types.FunctionType):
119137
call_name = source.__name__
@@ -133,24 +151,29 @@ def remote_exec(self, source, **kwargs):
133151
)
134152
return channel
135153

136-
def remote_init_threads(self, num=None):
154+
def remote_init_threads(self, num: int | None = None) -> None:
137155
"""DEPRECATED. Is currently a NO-OPERATION already."""
138156
print("WARNING: remote_init_threads()" " is a no-operation in execnet-1.2")
139157

140158

141159
class RInfo:
142-
def __init__(self, kwargs):
160+
def __init__(self, kwargs) -> None:
143161
self.__dict__.update(kwargs)
144162

145-
def __repr__(self):
163+
def __repr__(self) -> str:
146164
info = ", ".join(f"{k}={v}" for k, v in sorted(self.__dict__.items()))
147165
return "<RInfo %r>" % info
148166

167+
if TYPE_CHECKING:
168+
169+
def __getattr__(self, name: str) -> Any:
170+
...
171+
149172

150173
RemoteStatus = RInfo
151174

152175

153-
def rinfo_source(channel):
176+
def rinfo_source(channel) -> None:
154177
import os
155178
import sys
156179

@@ -165,7 +188,7 @@ def rinfo_source(channel):
165188
)
166189

167190

168-
def _find_non_builtin_globals(source, codeobj):
191+
def _find_non_builtin_globals(source: str, codeobj: types.CodeType) -> list[str]:
169192
import ast
170193
import builtins
171194

@@ -179,7 +202,7 @@ def _find_non_builtin_globals(source, codeobj):
179202
]
180203

181204

182-
def _source_of_function(function):
205+
def _source_of_function(function: types.FunctionType | Callable[..., object]) -> str:
183206
if function.__name__ == "<lambda>":
184207
raise ValueError("can't evaluate lambda functions'")
185208
# XXX: we dont check before remote instantiation

0 commit comments

Comments
 (0)