1
0
forked from VimPlug/jedi

Fix resource warnings

This commit is contained in:
micbou
2019-02-28 15:40:24 +01:00
committed by Dave Halter
parent 81e7dcf31e
commit 744662d096
3 changed files with 78 additions and 17 deletions

View File

@@ -3,7 +3,9 @@ To ensure compatibility from Python ``2.7`` - ``3.x``, a module has been
created. Clearly there is huge need to use conforming syntax. created. Clearly there is huge need to use conforming syntax.
""" """
from __future__ import print_function from __future__ import print_function
import atexit
import errno import errno
import functools
import sys import sys
import os import os
import re import re
@@ -11,6 +13,7 @@ import pkgutil
import warnings import warnings
import inspect import inspect
import subprocess import subprocess
import weakref
try: try:
import importlib import importlib
except ImportError: except ImportError:
@@ -635,3 +638,49 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
if _access_check(name, mode): if _access_check(name, mode):
return name return name
return None return None
if not is_py3:
# Simplified backport of Python 3 weakref.finalize:
# https://github.com/python/cpython/blob/ded4737989316653469763230036b04513cb62b3/Lib/weakref.py#L502-L662
class finalize(object):
"""Class for finalization of weakrefable objects.
finalize(obj, func, *args, **kwargs) returns a callable finalizer
object which will be called when obj is garbage collected. The
first time the finalizer is called it evaluates func(*arg, **kwargs)
and returns the result. After this the finalizer is dead, and
calling it just returns None.
When the program exits any remaining finalizers will be run.
"""
# Finalizer objects don't have any state of their own.
# This ensures that they cannot be part of a ref-cycle.
__slots__ = ()
_registry = {}
def __init__(self, obj, func, *args, **kwargs):
info = functools.partial(func, *args, **kwargs)
info.weakref = weakref.ref(obj, self)
self._registry[self] = info
def __call__(self):
"""Return func(*args, **kwargs) if alive."""
info = self._registry.pop(self, None)
if info:
return info()
@classmethod
def _exitfunc(cls):
if not cls._registry:
return
for finalizer in list(cls._registry):
try:
finalizer(None)
except Exception:
sys.excepthook(*sys.exc_info())
assert finalizer not in cls._registry
atexit.register(finalize._exitfunc)
weakref.finalize = finalize

View File

@@ -12,7 +12,6 @@ import sys
import subprocess import subprocess
import socket import socket
import errno import errno
import weakref
import traceback import traceback
from functools import partial from functools import partial
from threading import Thread from threading import Thread
@@ -22,7 +21,7 @@ except ImportError:
from Queue import Queue, Empty # python 2.7 from Queue import Queue, Empty # python 2.7
from jedi._compatibility import queue, is_py3, force_unicode, \ from jedi._compatibility import queue, is_py3, force_unicode, \
pickle_dump, pickle_load, GeneralizedPopen pickle_dump, pickle_load, GeneralizedPopen, weakref
from jedi import debug from jedi import debug
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.evaluate.compiled.subprocess import functions from jedi.evaluate.compiled.subprocess import functions
@@ -37,7 +36,6 @@ _MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py')
def _enqueue_output(out, queue): def _enqueue_output(out, queue):
for line in iter(out.readline, b''): for line in iter(out.readline, b''):
queue.put(line) queue.put(line)
out.close()
def _add_stderr_to_debug(stderr_queue): def _add_stderr_to_debug(stderr_queue):
@@ -56,6 +54,22 @@ def _get_function(name):
return getattr(functions, name) return getattr(functions, name)
def _cleanup_process(process, thread):
try:
process.kill()
process.wait()
except OSError:
# Raised if the process is already killed.
pass
thread.join()
for stream in [process.stdin, process.stdout, process.stderr]:
try:
stream.close()
except OSError:
# Raised if the stream is broken.
pass
class _EvaluatorProcess(object): class _EvaluatorProcess(object):
def __init__(self, evaluator): def __init__(self, evaluator):
self._evaluator_weakref = weakref.ref(evaluator) self._evaluator_weakref = weakref.ref(evaluator)
@@ -145,6 +159,7 @@ class CompiledSubprocess(object):
def __init__(self, executable): def __init__(self, executable):
self._executable = executable self._executable = executable
self._evaluator_deletion_queue = queue.deque() self._evaluator_deletion_queue = queue.deque()
self._cleanup_callable = lambda: None
def __repr__(self): def __repr__(self):
pid = os.getpid() pid = os.getpid()
@@ -182,6 +197,12 @@ class CompiledSubprocess(object):
) )
t.daemon = True t.daemon = True
t.start() t.start()
# Ensure the subprocess is properly cleaned up when the object
# is garbage collected.
self._cleanup_callable = weakref.finalize(self,
_cleanup_process,
process,
t)
return process return process
def run(self, evaluator, function, args=(), kwargs={}): def run(self, evaluator, function, args=(), kwargs={}):
@@ -202,18 +223,7 @@ class CompiledSubprocess(object):
def _kill(self): def _kill(self):
self.is_crashed = True self.is_crashed = True
try: self._cleanup_callable()
self._get_process().kill()
self._get_process().wait()
except (AttributeError, TypeError):
# If the Python process is terminating, it will remove some modules
# earlier than others and in general it's unclear how to deal with
# that so we just ignore the exceptions here.
pass
def __del__(self):
if not self.is_crashed:
self._kill()
def _send(self, evaluator_id, function, args=(), kwargs={}): def _send(self, evaluator_id, function, args=(), kwargs={}):
if self.is_crashed: if self.is_crashed:

View File

@@ -357,9 +357,11 @@ def collect_dir_tests(base_dir, test_files, check_thirdparty=False):
path = os.path.join(base_dir, f_name) path = os.path.join(base_dir, f_name)
if is_py3: if is_py3:
source = open(path, encoding='utf-8').read() with open(path, encoding='utf-8') as f:
source = f.read()
else: else:
source = unicode(open(path).read(), 'UTF-8') with open(path) as f:
source = unicode(f.read(), 'UTF-8')
for case in collect_file_tests(path, StringIO(source), for case in collect_file_tests(path, StringIO(source),
lines_to_execute): lines_to_execute):