Skip to content

Commit 562db4c

Browse files
author
Pan
committed
Added error code exception handling to session class, tests. Updated SFTP error code handling, tests.
Documentation updates, changelog. Resolves #23
1 parent 3000570 commit 562db4c

12 files changed

+2159
-1752
lines changed

Changelog.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
Change Log
22
=============
33

4-
0.12.0
4+
0.11.0
55
++++++++
66

77
Changes
88
---------
99

1010
* Session functions now raise exceptions.
1111
* Channel functions now raise specific exceptions.
12-
* Added exceptions for all known libssh2 error codes.
12+
* SCP errors now raise exceptions.
13+
* SFTP open handle errors now raise exceptions.
14+
* Added raising exceptions for all known libssh2 error codes.
1315
* Added ``ssh2.utils.handle_error_codes`` function for raising appropriate exception from error code.
16+
* Added file types to ``ssh2.sftp``.
1417

15-
0.11.0
16-
+++++++
18+
Fixes
19+
------
1720

18-
Changes
19-
--------
21+
* Double de-allocation crash on objects being garbage collected in some rare cases.
2022

21-
* SCP errors now raise exceptions.
22-
* SFTP open handle errors now raise exceptions.
2323

2424
0.10.0
2525
++++++++

ssh2/channel.c

Lines changed: 511 additions & 469 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ssh2/channel.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ cdef class Channel:
4545

4646
@property
4747
def session(self):
48+
"""Originating session."""
4849
return self._session
4950

5051
def pty(self, term="vt100"):

ssh2/session.c

Lines changed: 489 additions & 381 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ssh2/session.pyx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,8 @@ cdef class Session:
347347
channel = c_ssh2.libssh2_channel_open_session(
348348
self._session)
349349
if channel is NULL:
350-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
350+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
351351
self._session))
352-
return
353352
return PyChannel(channel, self)
354353

355354
def direct_tcpip_ex(self, host not None, int port,
@@ -363,9 +362,8 @@ cdef class Session:
363362
channel = c_ssh2.libssh2_channel_direct_tcpip_ex(
364363
self._session, _host, port, _shost, sport)
365364
if channel is NULL:
366-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
365+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
367366
self._session))
368-
return
369367
return PyChannel(channel, self)
370368

371369
def direct_tcpip(self, host not None, int port):
@@ -381,9 +379,8 @@ cdef class Session:
381379
channel = c_ssh2.libssh2_channel_direct_tcpip(
382380
self._session, _host, port)
383381
if channel is NULL:
384-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
382+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
385383
self._session))
386-
return
387384
return PyChannel(channel, self)
388385

389386
def block_directions(self):
@@ -425,9 +422,8 @@ cdef class Session:
425422
listener = c_ssh2.libssh2_channel_forward_listen(
426423
self._session, port)
427424
if listener is NULL:
428-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
425+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
429426
self._session))
430-
return
431427
return PyListener(listener, self)
432428

433429
def forward_listen_ex(self, host not None, int port,
@@ -439,9 +435,8 @@ cdef class Session:
439435
listener = c_ssh2.libssh2_channel_forward_listen_ex(
440436
self._session, _host, port, &bound_port, queue_maxsize)
441437
if listener is NULL:
442-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
438+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
443439
self._session))
444-
return
445440
return PyListener(listener, self)
446441

447442
def sftp_init(self):
@@ -453,9 +448,8 @@ cdef class Session:
453448
with nogil:
454449
_sftp = c_sftp.libssh2_sftp_init(self._session)
455450
if _sftp is NULL:
456-
handle_error_codes(c_ssh2.libssh2_session_last_errno(
451+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
457452
self._session))
458-
return
459453
return PySFTP(_sftp, self)
460454

461455
def last_error(self):

ssh2/sftp.c

Lines changed: 986 additions & 866 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ssh2/sftp.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ cdef object PySFTP(c_sftp.LIBSSH2_SFTP *sftp, Session session)
2626
cdef class SFTP:
2727
cdef c_sftp.LIBSSH2_SFTP *_sftp
2828
cdef Session _session
29+
cdef int _handle_error(self, int errcode, filename) except -1

ssh2/sftp.pyx

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
"""
1919
SFTP channel class and related SFTP flags.
2020
21+
File types
22+
------------
23+
:var LIBSSH2_SFTP_S_IFMT: Type of file mask
24+
:var LIBSSH2_SFTP_S_IFIFO: Named pipe (fifo)
25+
:var LIBSSH2_SFTP_S_IFCHR: Character special (character device)
26+
:var LIBSSH2_SFTP_S_IFDIR: Directory
27+
:var LIBSSH2_SFTP_S_IFBLK: Block special (block device)
28+
:var LIBSSH2_SFTP_S_IFREG: Regular file
29+
:var LIBSSH2_SFTP_S_IFLNK: Symbolic link
30+
:var LIBSSH2_SFTP_S_IFSOCK: Socket
31+
2132
File transfer flags
2233
--------------------
2334
:var LIBSSH2_FXF_READ: File read flag
@@ -53,14 +64,20 @@ ____________
5364
:var LIBSSH2_SFTP_S_IROTH: Read
5465
:var LIBSSH2_SFTP_S_IWOTH: Write
5566
:var LIBSSH2_SFTP_S_IXOTH: Execute
67+
68+
Generic mode masks
69+
___________________
70+
71+
:var LIBSSH2_SFTP_ST_RDONLY: Read only
72+
:var LIBSSH2_SFTP_ST_NOSUID: No suid
5673
"""
5774

5875
from libc.stdlib cimport malloc, free
5976

6077
from session cimport Session
6178
from error_codes cimport _LIBSSH2_ERROR_BUFFER_TOO_SMALL
6279
from channel cimport Channel, PyChannel
63-
from utils cimport to_bytes, to_str_len
80+
from utils cimport to_bytes, to_str_len, handle_error_codes
6481
from sftp_handle cimport SFTPHandle, PySFTPHandle, SFTPAttributes, SFTPStatVFS
6582

6683
from exceptions import SFTPHandleError, SFTPBufferTooSmall, SFTPIOError
@@ -70,7 +87,23 @@ cimport c_sftp
7087

7188

7289
# File types
73-
# TODO
90+
91+
# Type of file mask
92+
LIBSSH2_SFTP_S_IFMT = c_sftp.LIBSSH2_SFTP_S_IFMT
93+
# named pipe (fifo)
94+
LIBSSH2_SFTP_S_IFIFO = c_sftp.LIBSSH2_SFTP_S_IFIFO
95+
# character special
96+
LIBSSH2_SFTP_S_IFCHR = c_sftp.LIBSSH2_SFTP_S_IFCHR
97+
# directory
98+
LIBSSH2_SFTP_S_IFDIR = c_sftp.LIBSSH2_SFTP_S_IFDIR
99+
# block special (block device)
100+
LIBSSH2_SFTP_S_IFBLK = c_sftp.LIBSSH2_SFTP_S_IFBLK
101+
# regular
102+
LIBSSH2_SFTP_S_IFREG = c_sftp.LIBSSH2_SFTP_S_IFREG
103+
# symbolic link
104+
LIBSSH2_SFTP_S_IFLNK = c_sftp.LIBSSH2_SFTP_S_IFLNK
105+
# socket
106+
LIBSSH2_SFTP_S_IFSOCK = c_sftp.LIBSSH2_SFTP_S_IFSOCK
74107

75108

76109
# File Transfer Flags
@@ -84,6 +117,7 @@ LIBSSH2_FXF_EXCL = c_sftp.LIBSSH2_FXF_EXCL
84117

85118

86119
# File mode masks
120+
87121
# Read, write, execute/search by owner
88122
LIBSSH2_SFTP_S_IRWXU = c_sftp.LIBSSH2_SFTP_S_IRWXU
89123
LIBSSH2_SFTP_S_IRUSR = c_sftp.LIBSSH2_SFTP_S_IRUSR
@@ -100,7 +134,9 @@ LIBSSH2_SFTP_S_IROTH = c_sftp.LIBSSH2_SFTP_S_IROTH
100134
LIBSSH2_SFTP_S_IWOTH = c_sftp.LIBSSH2_SFTP_S_IWOTH
101135
LIBSSH2_SFTP_S_IXOTH = c_sftp.LIBSSH2_SFTP_S_IXOTH
102136

137+
# Read only
103138
LIBSSH2_SFTP_ST_RDONLY = c_sftp.LIBSSH2_SFTP_ST_RDONLY
139+
# No suid
104140
LIBSSH2_SFTP_ST_NOSUID = c_sftp.LIBSSH2_SFTP_ST_NOSUID
105141

106142

@@ -124,13 +160,26 @@ cdef class SFTP:
124160
with nogil:
125161
c_sftp.libssh2_sftp_shutdown(self._sftp)
126162

163+
@property
164+
def session(self):
165+
"""Originating session."""
166+
return self._session
167+
168+
cdef int _handle_error(self, int errcode, path) except -1:
169+
if errcode == c_ssh2.LIBSSH2_ERROR_EAGAIN:
170+
return errcode
171+
raise SFTPHandleError(
172+
"Could not open file or directory %s - error code %s - %s", path,
173+
errcode, self._session.last_error())
174+
127175
def get_channel(self):
128176
"""Get new channel from the SFTP session"""
129177
cdef c_ssh2.LIBSSH2_CHANNEL *_channel
130178
with nogil:
131179
_channel = c_sftp.libssh2_sftp_get_channel(self._sftp)
132180
if _channel is NULL:
133-
return
181+
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
182+
self._session._session))
134183
return PyChannel(_channel, self._session)
135184

136185
def open_ex(self, const char *filename,
@@ -144,9 +193,8 @@ cdef class SFTP:
144193
self._sftp, filename, filename_len, flags,
145194
mode, open_type)
146195
if _handle is NULL:
147-
raise SFTPHandleError(
148-
"Could not open file %s - error code %s - %s", filename,
149-
self._session.last_errno(), self._session.last_error())
196+
return self._handle_error(c_ssh2.libssh2_session_last_errno(
197+
self._session._session), filename)
150198
handle = PySFTPHandle(_handle, self)
151199
return handle
152200

@@ -191,9 +239,8 @@ cdef class SFTP:
191239
_handle = c_sftp.libssh2_sftp_open(
192240
self._sftp, _filename, flags, mode)
193241
if _handle is NULL:
194-
raise SFTPHandleError(
195-
"Could not open file %s - error code %s - %s", filename,
196-
self._session.last_errno(), self._session.last_error())
242+
return self._handle_error(c_ssh2.libssh2_session_last_errno(
243+
self._session._session), filename)
197244
return PySFTPHandle(_handle, self)
198245

199246
def opendir(self, path not None):
@@ -213,9 +260,8 @@ cdef class SFTP:
213260
with nogil:
214261
_handle = c_sftp.libssh2_sftp_opendir(self._sftp, _path)
215262
if _handle is NULL:
216-
raise SFTPHandleError(
217-
"Could not open directory %s - error code %s - %s", path,
218-
self._session.last_errno(), self._session.last_error())
263+
return self._handle_error(c_ssh2.libssh2_session_last_errno(
264+
self._session._session), path)
219265
return PySFTPHandle(_handle, self)
220266

221267
def rename_ex(self, const char *source_filename,

ssh2/sftp_handle.c

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_channel.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
from .base_test import SSH2TestCase
2+
23
from ssh2.exceptions import SocketSendError
4+
from ssh2.session import Session
5+
from ssh2.channel import Channel
36

47

58
class ChannelTestCase(SSH2TestCase):
69

10+
def test_init(self):
11+
self.assertEqual(self._auth(), 0)
12+
chan = self.session.open_session()
13+
self.assertIsInstance(chan, Channel)
14+
self.assertIsInstance(chan.session, Session)
15+
self.assertEqual(chan.session, self.session)
16+
717
def test_execute(self):
818
self.assertEqual(self._auth(), 0)
919
chan = self.session.open_session()

0 commit comments

Comments
 (0)