Source code for vimtk.jedi_monkeypatch

"""
Because support for google-style docstrings wont be merged into jedi, we can do
a simple monkey patch to get the functionality here.
"""


[docs] def apply_monkey_patch_jedi(): """ To use, modify your vimrc to execute this code: Usage: python3 << EOF from vimtk import jedi_monkeypatch jedi_monkeypatch.apply_monkey_patch_jedi() EOF """ DEBUG = 0 if DEBUG: print('Applying jedi monkey patch') # import os # ret = os.system(sys.executable + ' -m pip install xdoctest -U --user') # print('ret = {!r}'.format(ret)) try: import jedi except ImportError: print('cannot monkey patch jedi, jedi module not found') return import re import sys import vimtk import ubelt as ub try: import xdoctest if 0: print('sys.executable = {!r}'.format(sys.executable)) print('sys.prefix = {!r}'.format(sys.prefix)) print('xdoctest = {!r}'.format(xdoctest)) print('xdoctest.__version__ = {!r}'.format(xdoctest.__version__)) print('xdoctest.__file__ = {!r}'.format(xdoctest.__file__)) from xdoctest.docstr import docscrape_google except ImportError as ex: print('vimtk = {!r}'.format(vimtk)) print('ub = {!r}'.format(ub)) print('jedi = {!r}'.format(jedi)) print('sys.prefix = {!r}'.format(sys.prefix)) print('ERROR ex = {!r}'.format(ex)) raise from distutils.version import LooseVersion acceptable_versions = [ LooseVersion('0.10.2'), LooseVersion('0.11.1'), LooseVersion('0.12.0'), LooseVersion('0.13.3'), LooseVersion('0.14.1'), ] # min_ver = min(acceptable_versions) # max_ver = max(acceptable_versions) jedi_version = LooseVersion(jedi.__version__) if DEBUG: print('jedi.__file__ = {!r}'.format(jedi.__file__)) if jedi_version not in acceptable_versions: import warnings warnings.warn('Monkey patching an unknown version of jedi={}. ' 'Possible this may not work'.format(jedi_version)) # This is the module we are going to monkey patch module = jedi.evaluate.docstrings # These are the functions we will inject into the module def _search_param_in_googledocstr(docstr, param_str): r""" >>> docstr = '\n'.join([ ... 'Args:', ... ' x ( ndarray ):', ... ' y (int or str or list):', ... ' z ({"foo", "bar", 100500}):', ... ]) >>> sorted(set(_search_param_in_googledocstr(docstr, 'x'))) ['ndarray'] >>> sorted(set(_search_param_in_googledocstr(docstr, 'y'))) ['int', 'list', 'str'] >>> sorted(set(_search_param_in_googledocstr(docstr, 'z'))) ['int', 'str'] """ if DEBUG: open(ub.expandpath('~/jedi-test.txt'), 'a').write('search google param\n') for garg in docscrape_google.parse_google_args(docstr): if garg['name'] == param_str: typestr = garg['type'] for type_ in module._expand_typestr(typestr): yield type_ break def _search_return_in_googledocstr(docstr): r""" >>> docstr = '\n'.join([ ... 'Returns:', ... ' ndarray:', ... ' int:', ... ]) >>> sorted(_search_return_in_googledocstr(docstr)) ['int', 'ndarray'] """ if DEBUG: open(ub.expandpath('~/jedi-test.txt'), 'a').write('search google ret\n') google_rets = list(docscrape_google.parse_google_returns(docstr)) found = set() for retdict in google_rets: p_type = retdict['type'] found.update(module._expand_typestr(p_type)) return found def _search_param_in_docstr(docstr, param_str): """ Search `docstr` for type(-s) of `param_str`. >>> _search_param_in_docstr(':type param: int', 'param') ['int'] >>> _search_param_in_docstr('@type param: int', 'param') ['int'] >>> _search_param_in_docstr( ... ':type param: :class:`threading.Thread`', 'param') ['threading.Thread'] >>> bool(_search_param_in_docstr('no document', 'param')) False >>> _search_param_in_docstr(':param int param: some description', 'param') ['int'] """ if DEBUG: open(ub.expandpath('~/jedi-test.txt'), 'a').write('search param\n') # look at #40 to see definitions of those params patterns = [re.compile(p % re.escape(param_str)) for p in module.DOCSTRING_PARAM_PATTERNS] for pattern in patterns: match = pattern.search(docstr) if match: return [module._strip_rst_role(match.group(1))] return (module._search_param_in_numpydocstr(docstr, param_str) or list(_search_param_in_googledocstr(docstr, param_str))) # NOTE: WE ARE INJECTING OUR GOOGLE RETURN DOCSTRING IN THE NUMPY PARSER # BECAUSE infer_return_types IS AN INTERNAL FUNCTION :( def _search_return_in_numpydocstr(docstr): """ Search `docstr` (in numpydoc format) for type(-s) of function returns. """ if DEBUG: open(ub.expandpath('~/jedi-test.txt'), 'a').write('search return\n') try: doc = module._get_numpy_doc_string_cls()(docstr) except ImportError: return try: # This is a non-public API. If it ever changes we should be # prepared and return gracefully. returns = doc._parsed_data['Returns'] returns += doc._parsed_data['Yields'] except (KeyError, AttributeError): return for r_name, r_type, r_descr in returns: # Return names are optional and if so the type is in the name if not r_type: r_type = r_name for type_ in module._expand_typestr(r_type): yield type_ # Injected code: # Check for google style return hint for type_ in _search_return_in_googledocstr(docstr): yield type_ # def infer_return_types(function_context): # def search_return_in_docstr(code): # for p in module.DOCSTRING_RETURN_PATTERNS: # match = p.search(code) # if match: # yield module._strip_rst_role(match.group(1)) # # Check for numpy style return hint # for type_ in module._search_return_in_numpydocstr(code): # yield type_ # # Check for google style return hint # for type_ in _search_return_in_googledocstr(code): # yield type_ # for type_str in search_return_in_docstr(function_context.py__doc__()): # for type_eval in module._evaluate_for_statement_string(function_context.get_root_context(), type_str): # yield type_eval module._search_param_in_docstr = _search_param_in_docstr module._search_return_in_numpydocstr = _search_return_in_numpydocstr