1
1
import logging
2
2
import os
3
3
from typing import (
4
- Optional
4
+ TYPE_CHECKING ,
5
+ Callable ,
6
+ List ,
7
+ Any ,
8
+ Optional ,
9
+ Tuple ,
10
+ Union ,
11
+ Text
5
12
)
6
13
7
14
import paramiko
15
+ from paramiko .pkey import PKey
16
+ from paramiko .sftp_attr import SFTPAttributes
17
+ from paramiko .sftp_file import SFTPFile
18
+ from typeguard import typechecked
8
19
9
- from ssh_proxy_server .clients .ssh import SSHClient
10
-
20
+ import ssh_proxy_server
21
+ from ssh_proxy_server .clients .ssh import AuthenticationMethod , SSHClient
22
+ if TYPE_CHECKING :
23
+ from ssh_proxy_server .session import Session
11
24
12
25
class SFTPClient (SSHClient ):
13
26
14
- def __init__ (self , host , port , method , password , user , key , session ):
27
+ @typechecked
28
+ def __init__ (
29
+ self ,
30
+ host : Text ,
31
+ port : int ,
32
+ method : AuthenticationMethod ,
33
+ password : Optional [Text ],
34
+ user : Text ,
35
+ key : Optional [PKey ],
36
+ session : 'ssh_proxy_server.session.Session'
37
+ ) -> None :
15
38
super ().__init__ (host , port , method , password , user , key , session )
16
39
self ._sftp : Optional [paramiko .SFTPClient ] = None
17
40
self .subsystem_count = 0
18
41
19
42
@classmethod
20
- def from_client (cls , ssh_client : Optional [SSHClient ]):
43
+ @typechecked
44
+ def from_client (cls , ssh_client : Optional [SSHClient ]) -> Optional ['SFTPClient' ]:
21
45
if ssh_client is None :
22
46
logging .error ('error creating sftp client - no ssh client!' )
23
47
return None
@@ -46,35 +70,47 @@ def from_client(cls, ssh_client: Optional[SSHClient]):
46
70
return None
47
71
48
72
@property
49
- def running (self ):
73
+ def running (self ) -> bool :
50
74
return self .subsystem_count > 0
51
75
52
- def connect (self ):
76
+ @typechecked
77
+ def connect (self ) -> bool :
53
78
ret = super ().connect ()
54
79
if not ret :
55
80
return False
56
81
if self ._sftp is None :
57
- return paramiko . sftp . SFTP_FAILURE
82
+ return False
58
83
try :
84
+ if self .transport is None :
85
+ return False
59
86
self ._sftp = paramiko .SFTPClient .from_transport (self .transport )
60
87
return True
61
88
except Exception :
62
89
logging .exception ('error creating sftp client' )
63
90
return False
64
91
65
- def chmod (self , path , mode ):
92
+ @typechecked
93
+ def open (self , filename : Union [Text , bytes ], mode : Text = 'r' , bufsize : int = - 1 ) -> SFTPFile :
94
+ if self ._sftp is None :
95
+ raise paramiko .SFTPError ("Expected handle" )
96
+ return self ._sftp .open (filename , mode , bufsize )
97
+
98
+ @typechecked
99
+ def chmod (self , path : Union [Text , bytes ], mode : int ) -> int :
66
100
if self ._sftp is None :
67
101
return paramiko .sftp .SFTP_FAILURE
68
102
self ._sftp .chmod (path , mode )
69
103
return paramiko .sftp .SFTP_OK
70
104
71
- def chown (self , path , uid , gid ):
105
+ @typechecked
106
+ def chown (self , path : Union [Text , bytes ], uid : int , gid : int ) -> int :
72
107
if self ._sftp is None :
73
108
return paramiko .sftp .SFTP_FAILURE
74
109
self ._sftp .chown (path , uid , gid )
75
110
return paramiko .sftp .SFTP_OK
76
111
77
- def get (self , remotePath , localPath , callback = None ):
112
+ @typechecked
113
+ def get (self , remotePath : Union [Text , bytes ], localPath : Union [Text , bytes ], callback : Optional [Callable [[int , int ], Any ]] = None ) -> int :
78
114
if self ._sftp is None :
79
115
return paramiko .sftp .SFTP_FAILURE
80
116
try :
@@ -83,70 +119,84 @@ def get(self, remotePath, localPath, callback=None):
83
119
except (IOError , OSError ) as ex :
84
120
logging .error (ex )
85
121
os .remove (localPath )
86
- return paramiko .sftp .SFTP_FAILURE
122
+ return paramiko .sftp .SFTP_FAILURE
87
123
88
- def listdir_attr (self , path = '.' ):
124
+ @typechecked
125
+ def listdir_attr (self , path : Text = '.' ) -> Union [int , List [SFTPAttributes ]]:
89
126
if self ._sftp is None :
90
127
return paramiko .sftp .SFTP_FAILURE
91
128
return self ._sftp .listdir_attr (path )
92
129
93
- def lstat (self , path ):
130
+ @typechecked
131
+ def lstat (self , path : Union [Text , bytes ]) -> Union [int , SFTPAttributes ]:
94
132
if self ._sftp is None :
95
133
return paramiko .sftp .SFTP_FAILURE
96
134
return self ._sftp .lstat (path )
97
135
98
- def mkdir (self , path , mode = 511 ):
136
+ @typechecked
137
+ def mkdir (self , path : Union [Text , bytes ], mode : int = 511 ) -> int :
99
138
if self ._sftp is None :
100
139
return paramiko .sftp .SFTP_FAILURE
101
140
self ._sftp .mkdir (path , mode )
102
141
return paramiko .sftp .SFTP_OK
103
142
104
- def put (self , localPath , remotePath , callback = None , confirm = True ):
143
+ @typechecked
144
+ def put (self , localPath : Union [Text , bytes ], remotePath : Union [Text , bytes ], callback : Any = None , confirm : bool = True ) -> None :
105
145
raise NotImplementedError ('put not implemented' )
106
146
107
- def readlink (self , path ):
147
+ @typechecked
148
+ def readlink (self , path : Union [Text , bytes ]) -> Union [int , Text ]:
108
149
if self ._sftp is None :
109
150
return paramiko .sftp .SFTP_FAILURE
110
- return self ._sftp .readlink (path )
151
+ return self ._sftp .readlink (path ) or paramiko . sftp . SFTP_FAILURE
111
152
112
- def remove (self , path ):
153
+ @typechecked
154
+ def remove (self , path : Union [Text , bytes ]) -> int :
113
155
if self ._sftp is None :
114
156
return paramiko .sftp .SFTP_FAILURE
115
157
self ._sftp .remove (path )
116
158
return paramiko .sftp .SFTP_OK
117
159
118
- def rename (self , oldpath , newpath ):
160
+ @typechecked
161
+ def rename (self , oldpath : Union [Text , bytes ], newpath : Union [Text , bytes ]) -> int :
119
162
if self ._sftp is None :
120
163
return paramiko .sftp .SFTP_FAILURE
121
164
self ._sftp .rename (oldpath , newpath )
122
165
return paramiko .sftp .SFTP_OK
123
166
124
- def rmdir (self , path ):
167
+ @typechecked
168
+ def rmdir (self , path : Union [Text , bytes ]) -> int :
125
169
if self ._sftp is None :
126
170
return paramiko .sftp .SFTP_FAILURE
127
171
self ._sftp .rmdir (path )
128
172
return paramiko .sftp .SFTP_OK
129
173
130
- def stat (self , path ):
174
+ @typechecked
175
+ def stat (self , path : Union [Text , bytes ]) -> Union [int , SFTPAttributes ]:
131
176
if self ._sftp is None :
132
177
return paramiko .sftp .SFTP_FAILURE
133
178
return self ._sftp .stat (path )
134
179
135
- def utime (self , path , times ):
180
+ @typechecked
181
+ def utime (self , path : Union [Text , bytes ], times : Tuple [float , float ]) -> int :
136
182
if self ._sftp is None :
137
183
return paramiko .sftp .SFTP_FAILURE
138
- return self ._sftp .utime (path , times )
184
+ self ._sftp .utime (path , times )
185
+ return paramiko .sftp .SFTP_OK
139
186
140
- def symlink (self , source , dest ):
187
+ @typechecked
188
+ def symlink (self , source : Union [Text , bytes ], dest : Union [Text , bytes ]) -> int :
141
189
if self ._sftp is None :
142
190
return paramiko .sftp .SFTP_FAILURE
143
191
self ._sftp .symlink (source , dest )
144
192
return paramiko .sftp .SFTP_OK
145
193
146
- def close (self ):
194
+ @typechecked
195
+ def close (self ) -> int :
147
196
if self ._sftp is None :
148
197
return paramiko .sftp .SFTP_FAILURE
149
198
if not self .running :
150
199
self ._sftp .close ()
151
- self .session .sftp_channel .close ()
200
+ if self .session .sftp_channel is not None :
201
+ self .session .sftp_channel .close ()
152
202
return paramiko .sftp .SFTP_OK
0 commit comments