Skip to content
Open
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 stubs/paramiko/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "4.0.*"
version = "5.0.*"
upstream-repository = "https://github.com/paramiko/paramiko"
# Requires a version of cryptography where cryptography.hazmat.primitives.ciphers.Cipher is generic
dependencies = ["cryptography>=37.0.0"]
Expand Down
15 changes: 0 additions & 15 deletions stubs/paramiko/paramiko/auth_handler.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ from typing import TypeAlias

from paramiko.message import Message
from paramiko.pkey import PKey
from paramiko.ssh_gss import _SSH_GSSAuth
from paramiko.transport import Transport

_InteractiveCallback: TypeAlias = Callable[[str, str, list[tuple[str, bool]]], list[str]]
Expand All @@ -31,23 +30,9 @@ class AuthHandler:
def auth_publickey(self, username: str, key: PKey, event: Event) -> None: ...
def auth_password(self, username: str, password: str, event: Event) -> None: ...
def auth_interactive(self, username: str, handler: _InteractiveCallback, event: Event, submethods: str = "") -> None: ...
def auth_gssapi_with_mic(self, username: str, gss_host: str, gss_deleg_creds: bool, event: Event) -> None: ...
def auth_gssapi_keyex(self, username: str, event: Event) -> None: ...
def abort(self) -> None: ...
def wait_for_response(self, event: Event) -> list[str]: ...

class GssapiWithMicAuthHandler:
method: str
sshgss: _SSH_GSSAuth
def __init__(self, delegate: AuthHandler, sshgss: _SSH_GSSAuth) -> None: ...
def abort(self) -> None: ...
@property
def transport(self) -> Transport: ...
@property
def auth_username(self) -> str: ...
@property
def gss_host(self) -> str: ...

class AuthOnlyHandler(AuthHandler):
def send_auth_request(
self, username: str, method: str, finish_message: Callable[[Message], None] | None = None
Expand Down
5 changes: 0 additions & 5 deletions stubs/paramiko/paramiko/client.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,9 @@ class SSHClient(ClosingContextManager):
look_for_keys: bool = True,
compress: bool = False,
sock: _SocketLike | None = None,
gss_auth: bool = False,
gss_kex: bool = False,
gss_deleg_creds: bool = True,
gss_host: str | None = None,
banner_timeout: float | None = None,
auth_timeout: float | None = None,
channel_timeout: float | None = None,
gss_trust_dns: bool = True,
passphrase: str | None = None,
disabled_algorithms: Mapping[str, Iterable[str]] | None = None,
transport_factory: _TransportFactory | None = None,
Expand Down
5 changes: 2 additions & 3 deletions stubs/paramiko/paramiko/config.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from _typeshed import FileDescriptorOrPath
from collections.abc import Iterable
from re import Pattern
from typing import IO
from typing_extensions import Self

from paramiko.ssh_exception import ConfigParseError as ConfigParseError, CouldNotCanonicalize as CouldNotCanonicalize
Expand All @@ -18,8 +17,8 @@ class SSHConfig:
@classmethod
def from_path(cls, path: FileDescriptorOrPath) -> Self: ...
@classmethod
def from_file(cls, flo: IO[str]) -> Self: ...
def parse(self, file_obj: IO[str]) -> None: ...
def from_file(cls, flo: Iterable[str]) -> Self: ...
def parse(self, file_obj: Iterable[str]) -> None: ...
Comment on lines +20 to +21

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while it's true that any iterable of strings will work at runtime, the docstrings say that a file-like object is expected, and IO[str] does express that it should be an iterable of lines (rather than an iterable of words or characters).

I know you're generally opposed to using IO, though, and I share your philosophy there... not sure if there's a protocol that could also express "this needs to be an iterable of lines of source code" in the same way...

@srittau srittau Jun 13, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Iterable[str] is explicitly named for situations like this by the Python docs, but then that section was also written be me IIRC, so make of it what you will.

That said, maybe for now we could add a type alias to _typeshed and at some point this might become something more.

def lookup(self, hostname: str) -> SSHConfigDict: ...
def canonicalize(self, hostname: str, options: SSHConfigDict, domains: Iterable[str]) -> str: ...
def get_hostnames(self) -> set[str]: ...
Expand Down
12 changes: 7 additions & 5 deletions stubs/paramiko/paramiko/ecdsakey.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from _typeshed import FileDescriptorOrPath, ReadableBuffer
from collections.abc import Callable, Sequence
from typing import IO, Any
from typing import Any

from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve, EllipticCurvePrivateKey, EllipticCurvePublicKey
from cryptography.hazmat.primitives.hashes import HashAlgorithm
from paramiko.message import Message
from paramiko.pkey import PKey
from paramiko.pkey import PKey, _HasReadlines

class _ECDSACurve:
nist_name: str
Expand Down Expand Up @@ -35,10 +35,12 @@ class ECDSAKey(PKey):
filename: FileDescriptorOrPath | None = None,
password: str | None = None,
vals: tuple[EllipticCurvePrivateKey, EllipticCurvePublicKey] | None = None,
file_obj: IO[str] | None = None,
file_obj: _HasReadlines | None = None,
validate_point: bool = True,
) -> None: ...
@classmethod
def identifiers(cls) -> list[str]: ...
@classmethod
def supported_key_format_identifiers(cls: Any) -> list[str]: ...
def asbytes(self) -> bytes: ...
def __hash__(self) -> int: ...
Expand All @@ -47,8 +49,8 @@ class ECDSAKey(PKey):
def can_sign(self) -> bool: ...
def sign_ssh_data(self, data: bytes, algorithm: str | None = None) -> Message: ...
def verify_ssh_sig(self, data: bytes, msg: Message) -> bool: ...
def write_private_key_file(self, filename: FileDescriptorOrPath, password: str | None = None) -> None: ...
def write_private_key(self, file_obj: IO[str], password: str | None = None) -> None: ...
@property
def private_key(self) -> EllipticCurvePrivateKey | None: ...
@classmethod
def generate(
cls, curve: EllipticCurve = ..., progress_func: Callable[..., object] | None = None, bits: int | None = None
Expand Down
15 changes: 11 additions & 4 deletions stubs/paramiko/paramiko/ed25519key.pyi
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
from _typeshed import FileDescriptorOrPath, ReadableBuffer
from typing import IO
from typing import Any, Final, TypeAlias

from paramiko.message import Message
from paramiko.pkey import PKey
from paramiko.pkey import PKey, _HasReadlines

_VerifyKey: TypeAlias = Any # actually nacl.signing.VerifyKey

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pynacl looks to be fully typed and has a py.typed file. We could consider adding it to the stub_uploader allowlist and then adding it as a dependency of these stubs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just confirmed that paramiko depends on pynacl so that is viable, and probably wouldn't have much of a negative impact in practice. That said, considering that this looks to be the only place where the stubs pynacl and this is more of an internal module, an optional dependency like I suggested #15549 would be a better fit here, in my opinion.


class Ed25519Key(PKey):
name: Final = "ssh-ed25519"

public_blob: None

def __init__(
self,
msg: Message | None = None,
data: ReadableBuffer | None = None,
filename: FileDescriptorOrPath | None = None,
password: str | None = None,
file_obj: IO[str] | None = None,
file_obj: _HasReadlines | None = None,
) -> None: ...
def asbytes(self) -> bytes: ...
def __hash__(self) -> int: ...
def get_name(self) -> str: ...
def get_bits(self) -> int: ...
def can_sign(self) -> bool: ...
def can_verify(self) -> bool: ...
@property
def verifying_key(self) -> _VerifyKey | None: ...
def sign_ssh_data(self, data: bytes, algorithm: str | None = None) -> Message: ...
def verify_ssh_sig(self, data: bytes, msg: Message) -> bool: ...
31 changes: 15 additions & 16 deletions stubs/paramiko/paramiko/kex_gex.pyi
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
from _hashlib import HASH
from _typeshed import ReadableBuffer
from collections.abc import Callable
from hashlib import _Hash
from typing import ClassVar, Final

from paramiko.message import Message
from paramiko.transport import Transport

c_MSG_KEXDH_GEX_REQUEST_OLD: bytes
c_MSG_KEXDH_GEX_GROUP: bytes
c_MSG_KEXDH_GEX_INIT: bytes
c_MSG_KEXDH_GEX_REPLY: bytes
c_MSG_KEXDH_GEX_REQUEST: bytes
c_MSG_KEXDH_GEX_REQUEST_OLD: Final[bytes]
c_MSG_KEXDH_GEX_GROUP: Final[bytes]
c_MSG_KEXDH_GEX_INIT: Final[bytes]
c_MSG_KEXDH_GEX_REPLY: Final[bytes]
c_MSG_KEXDH_GEX_REQUEST: Final[bytes]

class KexGexSHA256:
name: ClassVar[str]
min_bits: ClassVar[int]
max_bits: ClassVar[int]
preferred_bits: ClassVar[int]
hash_algo: ClassVar[Callable[[ReadableBuffer], HASH]]

class KexGex:
name: str
min_bits: int
max_bits: int
preferred_bits: int
hash_algo: Callable[[ReadableBuffer], _Hash]
transport: Transport
p: int | None
q: int | None
Expand All @@ -25,10 +27,7 @@ class KexGex:
e: int | None
f: int | None
old_style: bool

def __init__(self, transport: Transport) -> None: ...
def start_kex(self, _test_old_style: bool = False) -> None: ...
def parse_next(self, ptype: int, m: Message) -> None: ...

class KexGexSHA256(KexGex):
name: str
hash_algo: Callable[[ReadableBuffer], _Hash]
24 changes: 0 additions & 24 deletions stubs/paramiko/paramiko/kex_group1.pyi

This file was deleted.

33 changes: 23 additions & 10 deletions stubs/paramiko/paramiko/kex_group14.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
from _hashlib import HASH
from _typeshed import ReadableBuffer
from collections.abc import Callable
from hashlib import _Hash
from typing import ClassVar, Final

from paramiko.kex_group1 import KexGroup1 as KexGroup1
from paramiko.message import Message
from paramiko.transport import Transport

class KexGroup14(KexGroup1):
P: int
G: int
name: str
hash_algo: Callable[[ReadableBuffer], _Hash]
c_MSG_KEXDH_INIT: Final[bytes]
c_MSG_KEXDH_REPLY: Final[bytes]
b7fffffffffffffff: Final[bytes]
b0000000000000000: Final[bytes]

class KexGroup14SHA256(KexGroup14):
name: str
hash_algo: Callable[[ReadableBuffer], _Hash]
class KexGroup14SHA256:
P: ClassVar[int]
G: ClassVar[int]

name: ClassVar[str]
hash_algo: ClassVar[Callable[[ReadableBuffer], HASH]]

transport: Transport
x: int
e: int
f: int

def __init__(self, transport: Transport) -> None: ...
def start_kex(self) -> None: ...
def parse_next(self, ptype: int, m: Message) -> None: ...
12 changes: 2 additions & 10 deletions stubs/paramiko/paramiko/kex_group16.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
from _typeshed import ReadableBuffer
from collections.abc import Callable
from hashlib import _Hash
from paramiko.kex_group14 import KexGroup14SHA256

from paramiko.kex_group1 import KexGroup1 as KexGroup1

class KexGroup16SHA512(KexGroup1):
name: str
P: int
G: int
hash_algo: Callable[[ReadableBuffer], _Hash]
class KexGroup16SHA512(KexGroup14SHA256): ...
64 changes: 0 additions & 64 deletions stubs/paramiko/paramiko/kex_gss.pyi

This file was deleted.

Loading
Loading