Commit 93c6542e authored by Jason Madden's avatar Jason Madden

Run the leakchecks on Python 3.11

parent beb98ce4
...@@ -350,11 +350,10 @@ jobs: ...@@ -350,11 +350,10 @@ jobs:
run: | run: |
python -mgevent.tests --second-chance $G_USE_COV --ignore tests_that_dont_use_resolver.txt python -mgevent.tests --second-chance $G_USE_COV --ignore tests_that_dont_use_resolver.txt
- name: "Tests: leakchecks" - name: "Tests: leakchecks"
# Run the leaktests; this seems to be extremely slow on Python 3.7 # Run the leaktests;
# XXX: Figure out why. Can we reproduce locally?
# This is incredibly important and we MUST have an environment that successfully passes # This is incredibly important and we MUST have an environment that successfully passes
# these tests. # these tests.
if: matrix.python-version == 2.7 && startsWith(runner.os, 'Linux') if: (matrix.python-version == 2.7 || startsWith(matrix.python-version, '3.11')) && startsWith(runner.os, 'Linux')
env: env:
GEVENTTEST_LEAKCHECK: 1 GEVENTTEST_LEAKCHECK: 1
run: | run: |
......
...@@ -135,6 +135,15 @@ class AbstractResolver(object): ...@@ -135,6 +135,15 @@ class AbstractResolver(object):
and k not in ('SOCK_CLOEXEC', 'SOCK_MAX_SIZE') and k not in ('SOCK_CLOEXEC', 'SOCK_MAX_SIZE')
} }
def close(self):
"""
Release resources held by this object.
Subclasses that define resources should override.
.. versionadded:: NEXT
"""
@staticmethod @staticmethod
def fixup_gaierror(func): def fixup_gaierror(func):
import functools import functools
......
...@@ -4,6 +4,7 @@ c-ares based hostname resolver. ...@@ -4,6 +4,7 @@ c-ares based hostname resolver.
""" """
from __future__ import absolute_import, print_function, division from __future__ import absolute_import, print_function, division
import os import os
import warnings
from _socket import gaierror from _socket import gaierror
from _socket import herror from _socket import herror
...@@ -116,12 +117,17 @@ class Resolver(AbstractResolver): ...@@ -116,12 +117,17 @@ class Resolver(AbstractResolver):
Handling of localhost and broadcast names is now more consistent. Handling of localhost and broadcast names is now more consistent.
.. versionchanged:: NEXT
Now has a ``__del__`` method that warns if the object is destroyed
without being properly closed.
.. _c-ares: http://c-ares.haxx.se .. _c-ares: http://c-ares.haxx.se
""" """
cares_class = channel cares_class = channel
def __init__(self, hub=None, use_environ=True, **kwargs): def __init__(self, hub=None, use_environ=True, **kwargs):
AbstractResolver.__init__(self)
if hub is None: if hub is None:
hub = get_hub() hub = get_hub()
self.hub = hub self.hub = hub
...@@ -134,7 +140,7 @@ class Resolver(AbstractResolver): ...@@ -134,7 +140,7 @@ class Resolver(AbstractResolver):
self.cares = self.cares_class(hub.loop, **kwargs) self.cares = self.cares_class(hub.loop, **kwargs)
self.pid = os.getpid() self.pid = os.getpid()
self.params = kwargs self.params = kwargs
self.fork_watcher = hub.loop.fork(ref=False) self.fork_watcher = hub.loop.fork(ref=False) # We shouldn't keep the loop alive
self.fork_watcher.start(self._on_fork) self.fork_watcher.start(self._on_fork)
def __repr__(self): def __repr__(self):
...@@ -149,11 +155,18 @@ class Resolver(AbstractResolver): ...@@ -149,11 +155,18 @@ class Resolver(AbstractResolver):
self.pid = pid self.pid = pid
def close(self): def close(self):
AbstractResolver.close(self)
if self.cares is not None: if self.cares is not None:
self.hub.loop.run_callback(self.cares.destroy) self.hub.loop.run_callback(self.cares.destroy)
self.cares = None self.cares = None
self.fork_watcher.stop() self.fork_watcher.stop()
def __del__(self):
if self.cares is not None:
warnings.warn("cares Resolver destroyed while not closed",
ResourceWarning)
self.close()
def _gethostbyname_ex(self, hostname_bytes, family): def _gethostbyname_ex(self, hostname_bytes, family):
while True: while True:
ares = self.cares ares = self.cares
......
...@@ -412,6 +412,9 @@ cdef class channel: ...@@ -412,6 +412,9 @@ cdef class channel:
return '<%s at 0x%x _timer=%r _watchers[%s]>' % args return '<%s at 0x%x _timer=%r _watchers[%s]>' % args
def destroy(self): def destroy(self):
self.__destroy()
cdef __destroy(self):
if self.channel: if self.channel:
# XXX ares_library_cleanup? # XXX ares_library_cleanup?
cares.ares_destroy(self.channel) cares.ares_destroy(self.channel)
...@@ -421,10 +424,7 @@ cdef class channel: ...@@ -421,10 +424,7 @@ cdef class channel:
self.loop = None self.loop = None
def __dealloc__(self): def __dealloc__(self):
if self.channel: self.__destroy()
# XXX ares_library_cleanup?
cares.ares_destroy(self.channel)
self.channel = NULL
cpdef set_servers(self, servers=None): cpdef set_servers(self, servers=None):
if not self.channel: if not self.channel:
......
...@@ -34,6 +34,7 @@ class TestTimeout(greentest.TestCase): ...@@ -34,6 +34,7 @@ class TestTimeout(greentest.TestCase):
r = Resolver(servers=[address[0]], timeout=0.001, tries=1, r = Resolver(servers=[address[0]], timeout=0.001, tries=1,
udp_port=address[-1]) udp_port=address[-1])
self._close_on_teardown(r)
with self.assertRaisesRegex(socket.herror, "ARES_ETIMEOUT"): with self.assertRaisesRegex(socket.herror, "ARES_ETIMEOUT"):
r.gethostbyname('www.google.com') r.gethostbyname('www.google.com')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment