[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:
Robey Pointer 2005-07-17 20:06:29 +00:00
parent f27e625926
commit e9ccd7ea20
4 changed files with 23 additions and 8 deletions

View File

@ -88,6 +88,9 @@ class Channel (object):
self.combine_stderr = False
self.exit_status = -1
def __del__(self):
self.close()
def __repr__(self):
"""
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
will fail. The remote end will receive no more data (after queued data
is flushed). Channels are automatically closed when their L{Transport}
is closed.
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.
is closed or when they are garbage collected.
"""
self.lock.acquire()
try:

View File

@ -63,6 +63,9 @@ class SFTPClient (BaseSFTP):
self.sock.get_name() + '.sftp')
self.ultra_debug = transport.get_hexdump()
self._send_version()
def __del__(self):
self.close()
def from_transport(selfclass, t):
"""

View File

@ -41,6 +41,9 @@ class SFTPFile (BufferedFile):
self.handle = handle
BufferedFile._set_mode(self, mode, bufsize)
def __del__(self):
self.close()
def close(self):
BufferedFile.close(self)
try:

View File

@ -21,6 +21,7 @@ L{BaseTransport} handles the core SSH2 protocol.
"""
import sys, os, string, threading, socket, struct, time
import weakref
from common import *
from ssh_exception import SSHException
@ -205,7 +206,7 @@ class BaseTransport (threading.Thread):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((hostname, port))
# okay, normal socket-ish flow here...
threading.Thread.__init__(self, target=self._run)
threading.Thread.__init__(self)
self.randpool = randpool
self.sock = sock
# Python < 2.3 doesn't have the settimeout method - RogerB
@ -229,7 +230,7 @@ class BaseTransport (threading.Thread):
self.initial_kex_done = False
self.in_kex = False
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_counter = 1
self.window_size = 65536
@ -249,6 +250,9 @@ class BaseTransport (threading.Thread):
self.server_accept_cv = threading.Condition(self.lock)
self.subsystem_table = { }
def __del__(self):
self.close()
def __repr__(self):
"""
Returns a string representation of this object, for debugging.
@ -969,7 +973,12 @@ class BaseTransport (threading.Thread):
raise SSHException('Unknown client cipher ' + name)
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_threads.append(self)
if self.server_mode:
@ -1281,6 +1290,7 @@ class BaseTransport (threading.Thread):
# can also free a bunch of stuff here
self.local_kex_init = self.remote_kex_init = None
self.K = None
self.kex_engine = None
if not self.initial_kex_done:
# this was the first key exchange
self.initial_kex_done = True