summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--paramiko/channel.py9
-rw-r--r--paramiko/sftp_client.py3
-rw-r--r--paramiko/sftp_file.py3
-rw-r--r--paramiko/transport.py16
4 files changed, 23 insertions, 8 deletions
diff --git a/paramiko/channel.py b/paramiko/channel.py
index 3c9fa35e..db85aacc 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -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:
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index a7afdb03..84e32fec 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -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):
"""
diff --git a/paramiko/sftp_file.py b/paramiko/sftp_file.py
index 51c7d078..a359510a 100644
--- a/paramiko/sftp_file.py
+++ b/paramiko/sftp_file.py
@@ -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:
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 86e33ed4..9ad8b452 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -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