[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-45]
yeah! figured out the last things that were causing GC cycles and got rid of them: Channels, Transports, SFTPClients, and SFTPFiles can all have __del__ methods now, which auto-close themselves :)
This commit is contained in:
parent
f27e625926
commit
e9ccd7ea20
|
@ -88,6 +88,9 @@ class Channel (object):
|
||||||
self.combine_stderr = False
|
self.combine_stderr = False
|
||||||
self.exit_status = -1
|
self.exit_status = -1
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
Return a string representation of this object, for debugging.
|
Return a string representation of this object, for debugging.
|
||||||
|
@ -455,11 +458,7 @@ class Channel (object):
|
||||||
Close the channel. All future read/write operations on the channel
|
Close the channel. All future read/write operations on the channel
|
||||||
will fail. The remote end will receive no more data (after queued data
|
will fail. The remote end will receive no more data (after queued data
|
||||||
is flushed). Channels are automatically closed when their L{Transport}
|
is flushed). Channels are automatically closed when their L{Transport}
|
||||||
is closed.
|
is closed or when they are garbage collected.
|
||||||
|
|
||||||
Note that because of peculiarities with the way python's garbage
|
|
||||||
collection works on cycles, channels will B{not} be automatically
|
|
||||||
closed by the garbage collector.
|
|
||||||
"""
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -64,6 +64,9 @@ class SFTPClient (BaseSFTP):
|
||||||
self.ultra_debug = transport.get_hexdump()
|
self.ultra_debug = transport.get_hexdump()
|
||||||
self._send_version()
|
self._send_version()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
def from_transport(selfclass, t):
|
def from_transport(selfclass, t):
|
||||||
"""
|
"""
|
||||||
Create an SFTP client channel from an open L{Transport}.
|
Create an SFTP client channel from an open L{Transport}.
|
||||||
|
|
|
@ -41,6 +41,9 @@ class SFTPFile (BufferedFile):
|
||||||
self.handle = handle
|
self.handle = handle
|
||||||
BufferedFile._set_mode(self, mode, bufsize)
|
BufferedFile._set_mode(self, mode, bufsize)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
BufferedFile.close(self)
|
BufferedFile.close(self)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -21,6 +21,7 @@ L{BaseTransport} handles the core SSH2 protocol.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, os, string, threading, socket, struct, time
|
import sys, os, string, threading, socket, struct, time
|
||||||
|
import weakref
|
||||||
|
|
||||||
from common import *
|
from common import *
|
||||||
from ssh_exception import SSHException
|
from ssh_exception import SSHException
|
||||||
|
@ -205,7 +206,7 @@ class BaseTransport (threading.Thread):
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.connect((hostname, port))
|
sock.connect((hostname, port))
|
||||||
# okay, normal socket-ish flow here...
|
# okay, normal socket-ish flow here...
|
||||||
threading.Thread.__init__(self, target=self._run)
|
threading.Thread.__init__(self)
|
||||||
self.randpool = randpool
|
self.randpool = randpool
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
# Python < 2.3 doesn't have the settimeout method - RogerB
|
# Python < 2.3 doesn't have the settimeout method - RogerB
|
||||||
|
@ -229,7 +230,7 @@ class BaseTransport (threading.Thread):
|
||||||
self.initial_kex_done = False
|
self.initial_kex_done = False
|
||||||
self.in_kex = False
|
self.in_kex = False
|
||||||
self.lock = threading.Lock() # synchronization (always higher level than write_lock)
|
self.lock = threading.Lock() # synchronization (always higher level than write_lock)
|
||||||
self.channels = { } # (id -> Channel)
|
self.channels = weakref.WeakValueDictionary() # (id -> Channel)
|
||||||
self.channel_events = { } # (id -> Event)
|
self.channel_events = { } # (id -> Event)
|
||||||
self.channel_counter = 1
|
self.channel_counter = 1
|
||||||
self.window_size = 65536
|
self.window_size = 65536
|
||||||
|
@ -249,6 +250,9 @@ class BaseTransport (threading.Thread):
|
||||||
self.server_accept_cv = threading.Condition(self.lock)
|
self.server_accept_cv = threading.Condition(self.lock)
|
||||||
self.subsystem_table = { }
|
self.subsystem_table = { }
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
@ -969,7 +973,12 @@ class BaseTransport (threading.Thread):
|
||||||
raise SSHException('Unknown client cipher ' + name)
|
raise SSHException('Unknown client cipher ' + name)
|
||||||
return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
|
return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
|
||||||
|
|
||||||
def _run(self):
|
def run(self):
|
||||||
|
# (use the exposed "run" method, because if we specify a thread target
|
||||||
|
# of a private method, threading.Thread will keep a reference to it
|
||||||
|
# indefinitely, creating a GC cycle and not letting Transport ever be
|
||||||
|
# GC'd. it's a bug in Thread.)
|
||||||
|
|
||||||
# active=True occurs before the thread is launched, to avoid a race
|
# active=True occurs before the thread is launched, to avoid a race
|
||||||
_active_threads.append(self)
|
_active_threads.append(self)
|
||||||
if self.server_mode:
|
if self.server_mode:
|
||||||
|
@ -1281,6 +1290,7 @@ class BaseTransport (threading.Thread):
|
||||||
# can also free a bunch of stuff here
|
# can also free a bunch of stuff here
|
||||||
self.local_kex_init = self.remote_kex_init = None
|
self.local_kex_init = self.remote_kex_init = None
|
||||||
self.K = None
|
self.K = None
|
||||||
|
self.kex_engine = None
|
||||||
if not self.initial_kex_done:
|
if not self.initial_kex_done:
|
||||||
# this was the first key exchange
|
# this was the first key exchange
|
||||||
self.initial_kex_done = True
|
self.initial_kex_done = True
|
||||||
|
|
Loading…
Reference in New Issue