Repository: cherrypy/cheroot (language: python)

Issue:
Add type hints
❓ **I'm submitting a ...**
- [ ] 🐞 bug report
- [X] 🐣 feature request
- [ ] ❓ question about the decisions made in the repository

🐣 **Describe the solution you'd like**
It would be great to see the python 3.5+ type hints in the repository, especially for the ``Server`` api.

📋 **Describe alternatives you've considered**
Adding type stubs to typeshed (https://github.com/python/typeshed/tree/master/stubs).
If you think this makes more sense, that's possible. But I first wanted to ask here.



Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 71d7cef7a7..0d7c0d7676 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,4 +1,9 @@
 ---
+
+ci:
+  skip:
+  - stubtest
+
 repos:
 - repo: https://github.com/asottile/add-trailing-comma.git
   rev: v2.2.1
@@ -124,6 +129,32 @@ repos:
     - cheroot/
     pass_filenames: false
 
+- repo: local
+  hooks:
+  - id: stubtest
+    name: MyPy stubtest
+    language: system
+    entry: stubtest
+    args:
+    - cheroot
+    - --mypy-config-file=mypy.ini
+    - --allowlist=stubtest_allowlist.txt
+    pass_filenames: false
+    types_or:
+    - python
+    - text
+    files: >-
+      (?x)
+      ^
+        (
+          .*\.pyi?
+          |
+          cheroot/py\.typed
+          |
+          stubtest_allowlist\.txt
+        )
+      $
+
 - repo: https://github.com/PyCQA/pylint.git
   rev: v2.12.2
   hooks:
diff --git a/cheroot/errors.pyi b/cheroot/errors.pyi
index e78a758523..186695682f 100644
--- a/cheroot/errors.pyi
+++ b/cheroot/errors.pyi
@@ -1,4 +1,4 @@
-from typing import Any, List, Set, Tuple
+from typing import List, Set, Tuple, Type
 
 class MaxSizeExceeded(Exception): ...
 class NoSSLError(Exception): ...
@@ -10,4 +10,4 @@ socket_error_eintr: List[int]
 socket_errors_to_ignore: List[int]
 socket_errors_nonblocking: List[int]
 acceptable_sock_shutdown_error_codes: Set[int]
-acceptable_sock_shutdown_exceptions: Tuple[Exception]
+acceptable_sock_shutdown_exceptions: Tuple[Type[Exception], ...]
diff --git a/cheroot/makefile.pyi b/cheroot/makefile.pyi
index 1174850597..139e0a2e8d 100644
--- a/cheroot/makefile.pyi
+++ b/cheroot/makefile.pyi
@@ -13,7 +13,6 @@ class MakeFile_PY2:
     def send(self, data): ...
     def flush(self) -> None: ...
     def recv(self, size): ...
-    class FauxSocket: ...
     def read(self, size: int = ...): ...
     def readline(self, size: int = ...): ...
     def has_data(self): ...
diff --git a/cheroot/ssl/builtin.pyi b/cheroot/ssl/builtin.pyi
index fdc656e03d..72e4500179 100644
--- a/cheroot/ssl/builtin.pyi
+++ b/cheroot/ssl/builtin.pyi
@@ -1,7 +1,6 @@
 from typing import Any
 from . import Adapter
 
-generic_socket_error: OSError
 DEFAULT_BUFFER_SIZE: int
 
 class BuiltinSSLAdapter(Adapter):
@@ -14,5 +13,5 @@ class BuiltinSSLAdapter(Adapter):
     def context(self, context) -> None: ...
     def bind(self, sock): ...
     def wrap(self, sock): ...
-    def get_environ(self): ...
+    def get_environ(self, sock): ...
     def makefile(self, sock, mode: str = ..., bufsize: int = ...): ...
diff --git a/cheroot/ssl/pyopenssl.pyi b/cheroot/ssl/pyopenssl.pyi
index d5b93471ab..f138506c95 100644
--- a/cheroot/ssl/pyopenssl.pyi
+++ b/cheroot/ssl/pyopenssl.pyi
@@ -1,9 +1,9 @@
 from . import Adapter
 from ..makefile import StreamReader, StreamWriter
 from OpenSSL import SSL
-from typing import Any
+from typing import Any, Type
 
-ssl_conn_type: SSL.Connection
+ssl_conn_type: Type[SSL.Connection]
 
 class SSLFileobjectMixin:
     ssl_timeout: int
@@ -19,7 +19,7 @@ class SSLFileobjectStreamWriter(SSLFileobjectMixin, StreamWriter): ...  # type:i
 class SSLConnectionProxyMeta:
     def __new__(mcl, name, bases, nmspc): ...
 
-class SSLConnection():
+class SSLConnection:
     def __init__(self, *args) -> None: ...
 
 class pyOpenSSLAdapter(Adapter):
@@ -28,3 +28,4 @@ class pyOpenSSLAdapter(Adapter):
     def wrap(self, sock): ...
     def get_environ(self): ...
     def makefile(self, sock, mode: str = ..., bufsize: int = ...): ...
+    def get_context(self) -> SSL.Context: ...
diff --git a/cheroot/wsgi.pyi b/cheroot/wsgi.pyi
index b4851a3d4d..9607563347 100644
--- a/cheroot/wsgi.pyi
+++ b/cheroot/wsgi.pyi
@@ -40,3 +40,10 @@ class PathInfoDispatcher:
     apps: Any
     def __init__(self, apps): ...
     def __call__(self, environ, start_response): ...
+
+
+WSGIServer = Server
+WSGIGateway = Gateway
+WSGIGateway_u0 = Gateway_u0
+WSGIGateway_10 = Gateway_10
+WSGIPathInfoDispatcher = PathInfoDispatcher
diff --git a/requirements/tox-pre-commit-cp310-linux-x86_64.txt b/requirements/tox-pre-commit-cp310-linux-x86_64.txt
index 6bda9d67c8..f023d2d73e 100644
--- a/requirements/tox-pre-commit-cp310-linux-x86_64.txt
+++ b/requirements/tox-pre-commit-cp310-linux-x86_64.txt
@@ -2,27 +2,67 @@
 # This file is autogenerated by pip-compile with python 3.10
 # To update, run:
 #
-#    pip-compile --allow-unsafe --output-file=requirements/tox-pre-commit-cp310-linux-x86_64.txt --strip-extras requirements/tox-pre-commit-cp310-linux-x86_64.in
+#    pip-compile --allow-unsafe --output-file=requirements/tox-pre-commit-cp310-linux-x86_64.txt --strip-extras requirements/tox-pre-commit-cp310-linux-x86_64.in setup.cfg
 #
+cffi==1.15.0
+    # via cryptography
 cfgv==3.3.1
     # via pre-commit
+cryptography==36.0.1
+    # via pyopenssl
 distlib==0.3.4
     # via virtualenv
 filelock==3.4.2
     # via virtualenv
 identify==2.4.1
     # via pre-commit
+jaraco.functools==3.5.0
+    # via cheroot (setup.cfg)
+more-itertools==8.12.0 ; python_version >= "3.6"
+    # via
+    #   cheroot (setup.cfg)
+    #   jaraco.functools
+mypy==0.931
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+mypy-extensions==0.4.3
+    # via mypy
 nodeenv==1.6.0
     # via pre-commit
 platformdirs==2.4.1
     # via virtualenv
 pre-commit==2.16.0
     # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+pycparser==2.21
+    # via cffi
+pyopenssl==22.0.0
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
 pyyaml==6.0
     # via pre-commit
 six==1.16.0
-    # via virtualenv
+    # via
+    #   cheroot (setup.cfg)
+    #   virtualenv
 toml==0.10.2
     # via pre-commit
+tomli==2.0.0
+    # via mypy
+types-backports==0.1.3
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+types-cryptography==3.3.14
+    # via types-pyopenssl
+types-enum34==1.1.8
+    # via types-cryptography
+types-ipaddress==1.0.7
+    # via types-cryptography
+types-pyopenssl==21.0.3
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+types-requests==2.27.7
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+types-six==1.16.10
+    # via -r requirements/tox-pre-commit-cp310-linux-x86_64.in
+types-urllib3==1.26.7
+    # via types-requests
+typing-extensions==4.0.1
+    # via mypy
 virtualenv==20.13.0
     # via pre-commit
diff --git a/requirements/tox-pre-commit-cp39-linux-x86_64.txt b/requirements/tox-pre-commit-cp39-linux-x86_64.txt
index ed804a9efa..09095262ac 100644
--- a/requirements/tox-pre-commit-cp39-linux-x86_64.txt
+++ b/requirements/tox-pre-commit-cp39-linux-x86_64.txt
@@ -2,27 +2,67 @@
 # This file is autogenerated by pip-compile with python 3.9
 # To update, run:
 #
-#    pip-compile --allow-unsafe --output-file=requirements/tox-pre-commit-cp39-linux-x86_64.txt --strip-extras requirements/tox-pre-commit-cp39-linux-x86_64.in
+#    pip-compile --allow-unsafe --output-file=requirements/tox-pre-commit-cp39-linux-x86_64.txt --strip-extras requirements/tox-pre-commit-cp39-linux-x86_64.in setup.cfg
 #
+cffi==1.15.0
+    # via cryptography
 cfgv==3.3.1
     # via pre-commit
+cryptography==36.0.1
+    # via pyopenssl
 distlib==0.3.4
     # via virtualenv
 filelock==3.4.2
     # via virtualenv
 identify==2.4.1
     # via pre-commit
+jaraco.functools==3.5.0
+    # via cheroot (setup.cfg)
+more-itertools==8.12.0 ; python_version >= "3.6"
+    # via
+    #   cheroot (setup.cfg)
+    #   jaraco.functools
+mypy==0.931
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+mypy-extensions==0.4.3
+    # via mypy
 nodeenv==1.6.0
     # via pre-commit
 platformdirs==2.4.1
     # via virtualenv
 pre-commit==2.16.0
     # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+pycparser==2.21
+    # via cffi
+pyopenssl==22.0.0
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
 pyyaml==6.0
     # via pre-commit
 six==1.16.0
-    # via virtualenv
+    # via
+    #   cheroot (setup.cfg)
+    #   virtualenv
 toml==0.10.2
     # via pre-commit
+tomli==2.0.0
+    # via mypy
+types-backports==0.1.3
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+types-cryptography==3.3.14
+    # via types-pyopenssl
+types-enum34==1.1.8
+    # via types-cryptography
+types-ipaddress==1.0.7
+    # via types-cryptography
+types-pyopenssl==21.0.3
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+types-requests==2.27.7
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+types-six==1.16.10
+    # via -r requirements/tox-pre-commit-cp39-linux-x86_64.in
+types-urllib3==1.26.7
+    # via types-requests
+typing-extensions==4.0.1
+    # via mypy
 virtualenv==20.13.0
     # via pre-commit
diff --git a/requirements/tox-pre-commit.in b/requirements/tox-pre-commit.in
index 416634f528..ba4b2af864 100644
--- a/requirements/tox-pre-commit.in
+++ b/requirements/tox-pre-commit.in
@@ -1,1 +1,15 @@
-pre-commit
+pre-commit  # main unified linting framework
+
+# Dependencies for the local `stubtest` hook
+mypy  # provides the main `stubtest` executable
+
+# Extra runtime dependencies for `stubtest` to see runtime variables
+# that are also declared in `*.pyi` files the same way they are
+# declared:
+pyOpenSSL
+
+# Extra dependencies providing external type definitions:
+types-backports
+types-pyOpenSSL
+types-requests
+types-six


Test patch:
diff --git a/stubtest_allowlist.txt b/stubtest_allowlist.txt
new file mode 100644
index 0000000000..d628fb2620
--- /dev/null
+++ b/stubtest_allowlist.txt
@@ -0,0 +1,40 @@
+# generated members by metaclass
+cheroot.ssl.pyopenssl.SSLConnection.accept
+cheroot.ssl.pyopenssl.SSLConnection.bind
+cheroot.ssl.pyopenssl.SSLConnection.close
+cheroot.ssl.pyopenssl.SSLConnection.connect
+cheroot.ssl.pyopenssl.SSLConnection.connect_ex
+cheroot.ssl.pyopenssl.SSLConnection.family
+cheroot.ssl.pyopenssl.SSLConnection.fileno
+cheroot.ssl.pyopenssl.SSLConnection.get_app_data
+cheroot.ssl.pyopenssl.SSLConnection.get_cipher_list
+cheroot.ssl.pyopenssl.SSLConnection.get_context
+cheroot.ssl.pyopenssl.SSLConnection.get_peer_certificate
+cheroot.ssl.pyopenssl.SSLConnection.getpeername
+cheroot.ssl.pyopenssl.SSLConnection.getsockname
+cheroot.ssl.pyopenssl.SSLConnection.getsockopt
+cheroot.ssl.pyopenssl.SSLConnection.gettimeout
+cheroot.ssl.pyopenssl.SSLConnection.listen
+cheroot.ssl.pyopenssl.SSLConnection.makefile
+cheroot.ssl.pyopenssl.SSLConnection.pending
+cheroot.ssl.pyopenssl.SSLConnection.read
+cheroot.ssl.pyopenssl.SSLConnection.recv
+cheroot.ssl.pyopenssl.SSLConnection.renegotiate
+cheroot.ssl.pyopenssl.SSLConnection.send
+cheroot.ssl.pyopenssl.SSLConnection.sendall
+cheroot.ssl.pyopenssl.SSLConnection.set_accept_state
+cheroot.ssl.pyopenssl.SSLConnection.set_app_data
+cheroot.ssl.pyopenssl.SSLConnection.set_connect_state
+cheroot.ssl.pyopenssl.SSLConnection.setblocking
+cheroot.ssl.pyopenssl.SSLConnection.setsockopt
+cheroot.ssl.pyopenssl.SSLConnection.settimeout
+cheroot.ssl.pyopenssl.SSLConnection.shutdown
+cheroot.ssl.pyopenssl.SSLConnection.sock_shutdown
+cheroot.ssl.pyopenssl.SSLConnection.state_string
+cheroot.ssl.pyopenssl.SSLConnection.want_read
+cheroot.ssl.pyopenssl.SSLConnection.want_write
+cheroot.ssl.pyopenssl.SSLConnection.write
+
+# false positives (https://github.com/python/mypy/issues/11843)
+cheroot.connections.IS_WINDOWS
+cheroot.ssl.builtin.IS_ABOVE_OPENSSL10