We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AttributeError in jedi_plugin.py:102
I am getting the following error with the current master:
```
Response error:Traceback (most recent call last):
  File "plugin_server.py", line 95, in run
    response['result'] = func(*args, **kwargs)
  File "/home/jitse/misc/spyder/spyder/utils/introspection/jedi_plugin.py", line 102, in get_info
    elif call_def.doc and '(' in call_def.doc.splitlines()[0]:
AttributeError: 'Definition' object has no attribute 'doc'
```
This is almost certainly due to a mistake on my part in #6497 that was not found by the automatic tests, so I will take care of it shortly.

To trigger, press `Ctrl+I` when the cursor is on `end_callbacks` on line 119 of spyder/widgets/editor.py .

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `spyder/utils/introspection/jedi_plugin.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © Spyder Project Contributors
4 # Licensed under the terms of the MIT License
5 # (see spyder/__init__.py for details)
6 
7 """
8 Jedi Introspection Plugin
9 """
10 import re
11 import os.path as osp
12 import sys
13 import time
14 
15 from spyder.config.base import debug_print
16 from spyder.utils import programs
17 from spyder.utils.debug import log_last_error, log_dt
18 from spyder.utils.dochelpers import getsignaturefromtext
19 from spyder.utils.introspection.manager import (
20     DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin)
21 from spyder.utils.introspection.utils import (default_info_response,
22                                               get_parent_until)
23 from spyder.utils.introspection.manager import JEDI_REQVER
24 
25 try:
26     import jedi
27 except ImportError:
28     jedi = None
29 
30 
31 class JediPlugin(IntrospectionPlugin):
32     """
33     Jedi based introspection plugin for jedi
34 
35     Experimental Editor's code completion, go-to-definition and help
36     """
37 
38     # ---- IntrospectionPlugin API --------------------------------------------
39     name = 'jedi'
40 
41     def load_plugin(self):
42         """Load the Jedi introspection plugin"""
43         if not programs.is_module_installed('jedi', JEDI_REQVER):
44             raise ImportError('Requires Jedi %s' % JEDI_REQVER)
45         jedi.settings.case_insensitive_completion = False
46         for lib in ['numpy', 'matplotlib']:
47             jedi.preload_module(lib)
48 
49     def get_completions(self, info):
50         """Return a list of (completion, type) tuples"""
51         completions = self.get_jedi_object('completions', info)
52         if DEBUG_EDITOR:
53             log_last_error(LOG_FILENAME, str("comp: " + str(completions)[:100]))
54         if completions is not None:
55             completions = [(c.name, c.type) for c in completions]
56         else:
57             completions = []
58         debug_print(str(completions)[:100])
59         return completions
60 
61     def get_info(self, info):
62         """
63         Find the calltip and docs
64 
65         Returns a dict like the following:
66            {'note': 'Function of numpy.core.numeric...',
67             'argspec': "(shape, dtype=None, order='C')'
68             'docstring': 'Return an array of given...'
69             'name': 'ones',
70             'calltip': 'ones(shape, dtype=None, order='C')'}
71         """
72         call_def = self.get_jedi_object('goto_definitions', info)
73 
74         for cd in call_def:
75             docstring = cd.docstring()
76             if docstring and not docstring.rstrip().endswith(')'):
77                 call_def = cd
78                 break
79         else:
80             try:
81                 call_def = call_def[0]
82                 docstring = call_def.docstring()
83             except IndexError:
84                 return default_info_response()
85 
86         name = call_def.name
87         if name is None:
88             return default_info_response()
89 
90         if call_def.module_path:
91             mod_name = get_parent_until(call_def.module_path)
92         else:
93             mod_name = None
94 
95         if not mod_name:
96             mod_name = call_def.module_name
97 
98         if docstring.startswith(name + '('):
99             calltip = getsignaturefromtext(docstring, name)
100             argspec = calltip[calltip.find('('):]
101             docstring = docstring[docstring.find(')') + 3:]
102         elif call_def.doc and '(' in call_def.doc.splitlines()[0]:
103             calltip = docstring.splitlines()[0]
104             name = docstring.split('(')[0]
105             docstring = docstring[docstring.find(')') + 3:]
106             argspec = calltip[calltip.find('('):]
107         else:
108             calltip = name + '(...)'
109             argspec = '()'
110 
111         if call_def.type == 'module':
112             note = 'Module %s' % mod_name
113             argspec = ''
114             calltip = name
115         elif call_def.type == 'class':
116             note = 'Class in %s module' % mod_name
117         elif docstring.startswith('%s(self' % name):
118             class_name = call_def.full_name.split('.')[-2]
119             note = 'Method of %s class in %s module' % (
120                 class_name.capitalize(), mod_name)
121         else:
122             note = '%s in %s module' % (call_def.type.capitalize(),
123                                         mod_name)
124 
125         argspec = argspec.replace(' = ', '=')
126         calltip = calltip.replace(' = ', '=')
127         debug_print(call_def.name)
128 
129         doc_info = dict(name=name, argspec=argspec,
130                         note=note, docstring=docstring, calltip=calltip)
131         return doc_info
132 
133     def get_definition(self, info):
134         """
135         Find a definition location using Jedi
136 
137         Follows gotos until a definition is found, or it reaches a builtin
138         module.  Falls back on token lookup if it is in an enaml file or does
139         not find a match
140         """
141         line, filename = info['line_num'], info['filename']
142         def_info, module_path, line_nr = None, None, None
143         gotos = self.get_jedi_object('goto_assignments', info)
144 
145         if gotos:
146             def_info = self.get_definition_info(gotos[0])
147         if def_info and def_info['goto_next']:
148             defns = self.get_jedi_object('goto_definitions', info)
149             new_info = None
150             if defns:
151                 new_info = self.get_definition_info(defns[0])
152             if new_info and not new_info['in_builtin']:
153                 def_info = new_info
154         elif not def_info:
155             return
156 
157         # handle builtins -> try and find the module
158         if def_info and def_info['in_builtin']:
159             module_path, line_nr = self.find_in_builtin(def_info)
160         elif def_info:
161             module_path = def_info['module_path']
162             line_nr = def_info['line_nr']
163 
164         # Handle failures to find module_path and line_nr
165         if module_path == filename and line_nr == line:
166             return
167         elif module_path is None:
168             return
169 
170         return module_path, line_nr
171 
172     # ---- Private API -------------------------------------------------------
173 
174     def get_jedi_object(self, func_name, info, use_filename=True):
175         """Call a desired function on a Jedi Script and return the result"""
176         if not jedi:
177             return
178         if DEBUG_EDITOR:
179             t0 = time.time()
180         # override IPython qt_loaders ImportDenier behavior
181         metas = sys.meta_path
182         for meta in metas:
183             if (meta.__class__.__name__ == 'ImportDenier'
184                     and hasattr(meta, 'forbid')):
185                 sys.meta_path.remove(meta)
186 
187         if use_filename:
188             filename = info['filename']
189         else:
190             filename = None
191 
192         try:
193             script = jedi.api.Script(info['source_code'], info['line_num'],
194                                      info['column'], filename,
195                                      sys_path=info['sys_path'])
196             func = getattr(script, func_name)
197             val = func()
198         except Exception as e:
199             val = None
200             debug_print('Jedi error (%s)' % func_name)
201             debug_print(str(e))
202             if DEBUG_EDITOR:
203                 log_last_error(LOG_FILENAME, str(e))
204         if DEBUG_EDITOR:
205             log_dt(LOG_FILENAME, func_name, t0)
206         if not val and filename:
207             return self.get_jedi_object(func_name, info, False)
208         else:
209             return val
210 
211     @staticmethod
212     def get_definition_info(defn):
213         """Extract definition information from the Jedi definition object"""
214         try:
215             module_path = defn.module_path
216             name = defn.name
217             if hasattr(defn, 'line_nr'):
218                 line_nr = defn.line_nr
219             else:
220                 line_nr = defn.line
221             description = defn.description
222             in_builtin = defn.in_builtin_module()
223         except Exception as e:
224             if DEBUG_EDITOR:
225                 log_last_error(LOG_FILENAME, 'Get Defintion: %s' % e)
226             return None
227         pattern = r'class\s+{0}|def\s+{0}|self.{0}\s*=|{0}\s*='.format(name)
228         if not re.match(pattern, description):
229             goto_next = True
230         else:
231             goto_next = False
232         return dict(module_path=module_path, line_nr=line_nr,
233                     description=description, name=name, in_builtin=in_builtin,
234                     goto_next=goto_next)
235 
236     def find_in_builtin(self, info):
237         """Find a definition in a builtin file"""
238         module_path = info['module_path']
239         line_nr = info['line_nr']
240 
241         if module_path is None:
242             return None, None
243 
244         ext = osp.splitext(info['module_path'])[1]
245         desc = info['description']
246         name = info['name']
247         if ext in self.python_like_exts() and (
248                 desc.startswith('import ') or desc.startswith('from ')):
249             path = self.python_like_mod_finder(desc,
250                                           osp.dirname(module_path), name)
251             if path:
252                 info['module_path'] = module_path = path
253                 info['line_nr'] = line_nr = 1
254 
255         if ext in self.all_editable_exts():
256             pattern = r'from.*\W{0}\W?.*c?import|import.*\W{0}'
257             if not re.match(pattern.format(info['name']), desc):
258                 line_nr = self.get_definition_from_file(module_path, name,
259                                                         line_nr)
260                 if not line_nr:
261                     module_path = None
262 
263         if not ext in self.all_editable_exts():
264             line_nr = None
265 
266         return module_path, line_nr
267
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/spyder/utils/introspection/jedi_plugin.py b/spyder/utils/introspection/jedi_plugin.py
--- a/spyder/utils/introspection/jedi_plugin.py
+++ b/spyder/utils/introspection/jedi_plugin.py
@@ -99,7 +99,7 @@
             calltip = getsignaturefromtext(docstring, name)
             argspec = calltip[calltip.find('('):]
             docstring = docstring[docstring.find(')') + 3:]
-        elif call_def.doc and '(' in call_def.doc.splitlines()[0]:
+        elif docstring and '(' in docstring.splitlines()[0]:
             calltip = docstring.splitlines()[0]
             name = docstring.split('(')[0]
             docstring = docstring[docstring.find(')') + 3:]

{"golden_diff": "diff --git a/spyder/utils/introspection/jedi_plugin.py b/spyder/utils/introspection/jedi_plugin.py\n--- a/spyder/utils/introspection/jedi_plugin.py\n+++ b/spyder/utils/introspection/jedi_plugin.py\n@@ -99,7 +99,7 @@\n             calltip = getsignaturefromtext(docstring, name)\n             argspec = calltip[calltip.find('('):]\n             docstring = docstring[docstring.find(')') + 3:]\n-        elif call_def.doc and '(' in call_def.doc.splitlines()[0]:\n+        elif docstring and '(' in docstring.splitlines()[0]:\n             calltip = docstring.splitlines()[0]\n             name = docstring.split('(')[0]\n             docstring = docstring[docstring.find(')') + 3:]\n", "issue": "AttributeError in jedi_plugin.py:102\nI am getting the following error with the current master:\r\n```\r\nResponse error:Traceback (most recent call last):\r\n  File \"plugin_server.py\", line 95, in run\r\n    response['result'] = func(*args, **kwargs)\r\n  File \"/home/jitse/misc/spyder/spyder/utils/introspection/jedi_plugin.py\", line 102, in get_info\r\n    elif call_def.doc and '(' in call_def.doc.splitlines()[0]:\r\nAttributeError: 'Definition' object has no attribute 'doc'\r\n```\r\nThis is almost certainly due to a mistake on my part in #6497 that was not found by the automatic tests, so I will take care of it shortly.\r\n\r\nTo trigger, press `Ctrl+I` when the cursor is on `end_callbacks` on line 119 of spyder/widgets/editor.py .\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright \u00a9 Spyder Project Contributors\n# Licensed under the terms of the MIT License\n# (see spyder/__init__.py for details)\n\n\"\"\"\nJedi Introspection Plugin\n\"\"\"\nimport re\nimport os.path as osp\nimport sys\nimport time\n\nfrom spyder.config.base import debug_print\nfrom spyder.utils import programs\nfrom spyder.utils.debug import log_last_error, log_dt\nfrom spyder.utils.dochelpers import getsignaturefromtext\nfrom spyder.utils.introspection.manager import (\n    DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin)\nfrom spyder.utils.introspection.utils import (default_info_response,\n                                              get_parent_until)\nfrom spyder.utils.introspection.manager import JEDI_REQVER\n\ntry:\n    import jedi\nexcept ImportError:\n    jedi = None\n\n\nclass JediPlugin(IntrospectionPlugin):\n    \"\"\"\n    Jedi based introspection plugin for jedi\n\n    Experimental Editor's code completion, go-to-definition and help\n    \"\"\"\n\n    # ---- IntrospectionPlugin API --------------------------------------------\n    name = 'jedi'\n\n    def load_plugin(self):\n        \"\"\"Load the Jedi introspection plugin\"\"\"\n        if not programs.is_module_installed('jedi', JEDI_REQVER):\n            raise ImportError('Requires Jedi %s' % JEDI_REQVER)\n        jedi.settings.case_insensitive_completion = False\n        for lib in ['numpy', 'matplotlib']:\n            jedi.preload_module(lib)\n\n    def get_completions(self, info):\n        \"\"\"Return a list of (completion, type) tuples\"\"\"\n        completions = self.get_jedi_object('completions', info)\n        if DEBUG_EDITOR:\n            log_last_error(LOG_FILENAME, str(\"comp: \" + str(completions)[:100]))\n        if completions is not None:\n            completions = [(c.name, c.type) for c in completions]\n        else:\n            completions = []\n        debug_print(str(completions)[:100])\n        return completions\n\n    def get_info(self, info):\n        \"\"\"\n        Find the calltip and docs\n\n        Returns a dict like the following:\n           {'note': 'Function of numpy.core.numeric...',\n            'argspec': \"(shape, dtype=None, order='C')'\n            'docstring': 'Return an array of given...'\n            'name': 'ones',\n            'calltip': 'ones(shape, dtype=None, order='C')'}\n        \"\"\"\n        call_def = self.get_jedi_object('goto_definitions', info)\n\n        for cd in call_def:\n            docstring = cd.docstring()\n            if docstring and not docstring.rstrip().endswith(')'):\n                call_def = cd\n                break\n        else:\n            try:\n                call_def = call_def[0]\n                docstring = call_def.docstring()\n            except IndexError:\n                return default_info_response()\n\n        name = call_def.name\n        if name is None:\n            return default_info_response()\n\n        if call_def.module_path:\n            mod_name = get_parent_until(call_def.module_path)\n        else:\n            mod_name = None\n\n        if not mod_name:\n            mod_name = call_def.module_name\n\n        if docstring.startswith(name + '('):\n            calltip = getsignaturefromtext(docstring, name)\n            argspec = calltip[calltip.find('('):]\n            docstring = docstring[docstring.find(')') + 3:]\n        elif call_def.doc and '(' in call_def.doc.splitlines()[0]:\n            calltip = docstring.splitlines()[0]\n            name = docstring.split('(')[0]\n            docstring = docstring[docstring.find(')') + 3:]\n            argspec = calltip[calltip.find('('):]\n        else:\n            calltip = name + '(...)'\n            argspec = '()'\n\n        if call_def.type == 'module':\n            note = 'Module %s' % mod_name\n            argspec = ''\n            calltip = name\n        elif call_def.type == 'class':\n            note = 'Class in %s module' % mod_name\n        elif docstring.startswith('%s(self' % name):\n            class_name = call_def.full_name.split('.')[-2]\n            note = 'Method of %s class in %s module' % (\n                class_name.capitalize(), mod_name)\n        else:\n            note = '%s in %s module' % (call_def.type.capitalize(),\n                                        mod_name)\n\n        argspec = argspec.replace(' = ', '=')\n        calltip = calltip.replace(' = ', '=')\n        debug_print(call_def.name)\n\n        doc_info = dict(name=name, argspec=argspec,\n                        note=note, docstring=docstring, calltip=calltip)\n        return doc_info\n\n    def get_definition(self, info):\n        \"\"\"\n        Find a definition location using Jedi\n\n        Follows gotos until a definition is found, or it reaches a builtin\n        module.  Falls back on token lookup if it is in an enaml file or does\n        not find a match\n        \"\"\"\n        line, filename = info['line_num'], info['filename']\n        def_info, module_path, line_nr = None, None, None\n        gotos = self.get_jedi_object('goto_assignments', info)\n\n        if gotos:\n            def_info = self.get_definition_info(gotos[0])\n        if def_info and def_info['goto_next']:\n            defns = self.get_jedi_object('goto_definitions', info)\n            new_info = None\n            if defns:\n                new_info = self.get_definition_info(defns[0])\n            if new_info and not new_info['in_builtin']:\n                def_info = new_info\n        elif not def_info:\n            return\n\n        # handle builtins -> try and find the module\n        if def_info and def_info['in_builtin']:\n            module_path, line_nr = self.find_in_builtin(def_info)\n        elif def_info:\n            module_path = def_info['module_path']\n            line_nr = def_info['line_nr']\n\n        # Handle failures to find module_path and line_nr\n        if module_path == filename and line_nr == line:\n            return\n        elif module_path is None:\n            return\n\n        return module_path, line_nr\n\n    # ---- Private API -------------------------------------------------------\n\n    def get_jedi_object(self, func_name, info, use_filename=True):\n        \"\"\"Call a desired function on a Jedi Script and return the result\"\"\"\n        if not jedi:\n            return\n        if DEBUG_EDITOR:\n            t0 = time.time()\n        # override IPython qt_loaders ImportDenier behavior\n        metas = sys.meta_path\n        for meta in metas:\n            if (meta.__class__.__name__ == 'ImportDenier'\n                    and hasattr(meta, 'forbid')):\n                sys.meta_path.remove(meta)\n\n        if use_filename:\n            filename = info['filename']\n        else:\n            filename = None\n\n        try:\n            script = jedi.api.Script(info['source_code'], info['line_num'],\n                                     info['column'], filename,\n                                     sys_path=info['sys_path'])\n            func = getattr(script, func_name)\n            val = func()\n        except Exception as e:\n            val = None\n            debug_print('Jedi error (%s)' % func_name)\n            debug_print(str(e))\n            if DEBUG_EDITOR:\n                log_last_error(LOG_FILENAME, str(e))\n        if DEBUG_EDITOR:\n            log_dt(LOG_FILENAME, func_name, t0)\n        if not val and filename:\n            return self.get_jedi_object(func_name, info, False)\n        else:\n            return val\n\n    @staticmethod\n    def get_definition_info(defn):\n        \"\"\"Extract definition information from the Jedi definition object\"\"\"\n        try:\n            module_path = defn.module_path\n            name = defn.name\n            if hasattr(defn, 'line_nr'):\n                line_nr = defn.line_nr\n            else:\n                line_nr = defn.line\n            description = defn.description\n            in_builtin = defn.in_builtin_module()\n        except Exception as e:\n            if DEBUG_EDITOR:\n                log_last_error(LOG_FILENAME, 'Get Defintion: %s' % e)\n            return None\n        pattern = r'class\\s+{0}|def\\s+{0}|self.{0}\\s*=|{0}\\s*='.format(name)\n        if not re.match(pattern, description):\n            goto_next = True\n        else:\n            goto_next = False\n        return dict(module_path=module_path, line_nr=line_nr,\n                    description=description, name=name, in_builtin=in_builtin,\n                    goto_next=goto_next)\n\n    def find_in_builtin(self, info):\n        \"\"\"Find a definition in a builtin file\"\"\"\n        module_path = info['module_path']\n        line_nr = info['line_nr']\n\n        if module_path is None:\n            return None, None\n\n        ext = osp.splitext(info['module_path'])[1]\n        desc = info['description']\n        name = info['name']\n        if ext in self.python_like_exts() and (\n                desc.startswith('import ') or desc.startswith('from ')):\n            path = self.python_like_mod_finder(desc,\n                                          osp.dirname(module_path), name)\n            if path:\n                info['module_path'] = module_path = path\n                info['line_nr'] = line_nr = 1\n\n        if ext in self.all_editable_exts():\n            pattern = r'from.*\\W{0}\\W?.*c?import|import.*\\W{0}'\n            if not re.match(pattern.format(info['name']), desc):\n                line_nr = self.get_definition_from_file(module_path, name,\n                                                        line_nr)\n                if not line_nr:\n                    module_path = None\n\n        if not ext in self.all_editable_exts():\n            line_nr = None\n\n        return module_path, line_nr\n", "path": "spyder/utils/introspection/jedi_plugin.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright \u00a9 Spyder Project Contributors\n# Licensed under the terms of the MIT License\n# (see spyder/__init__.py for details)\n\n\"\"\"\nJedi Introspection Plugin\n\"\"\"\nimport re\nimport os.path as osp\nimport sys\nimport time\n\nfrom spyder.config.base import debug_print\nfrom spyder.utils import programs\nfrom spyder.utils.debug import log_last_error, log_dt\nfrom spyder.utils.dochelpers import getsignaturefromtext\nfrom spyder.utils.introspection.manager import (\n    DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin)\nfrom spyder.utils.introspection.utils import (default_info_response,\n                                              get_parent_until)\nfrom spyder.utils.introspection.manager import JEDI_REQVER\n\ntry:\n    import jedi\nexcept ImportError:\n    jedi = None\n\n\nclass JediPlugin(IntrospectionPlugin):\n    \"\"\"\n    Jedi based introspection plugin for jedi\n\n    Experimental Editor's code completion, go-to-definition and help\n    \"\"\"\n\n    # ---- IntrospectionPlugin API --------------------------------------------\n    name = 'jedi'\n\n    def load_plugin(self):\n        \"\"\"Load the Jedi introspection plugin\"\"\"\n        if not programs.is_module_installed('jedi', JEDI_REQVER):\n            raise ImportError('Requires Jedi %s' % JEDI_REQVER)\n        jedi.settings.case_insensitive_completion = False\n        for lib in ['numpy', 'matplotlib']:\n            jedi.preload_module(lib)\n\n    def get_completions(self, info):\n        \"\"\"Return a list of (completion, type) tuples\"\"\"\n        completions = self.get_jedi_object('completions', info)\n        if DEBUG_EDITOR:\n            log_last_error(LOG_FILENAME, str(\"comp: \" + str(completions)[:100]))\n        if completions is not None:\n            completions = [(c.name, c.type) for c in completions]\n        else:\n            completions = []\n        debug_print(str(completions)[:100])\n        return completions\n\n    def get_info(self, info):\n        \"\"\"\n        Find the calltip and docs\n\n        Returns a dict like the following:\n           {'note': 'Function of numpy.core.numeric...',\n            'argspec': \"(shape, dtype=None, order='C')'\n            'docstring': 'Return an array of given...'\n            'name': 'ones',\n            'calltip': 'ones(shape, dtype=None, order='C')'}\n        \"\"\"\n        call_def = self.get_jedi_object('goto_definitions', info)\n\n        for cd in call_def:\n            docstring = cd.docstring()\n            if docstring and not docstring.rstrip().endswith(')'):\n                call_def = cd\n                break\n        else:\n            try:\n                call_def = call_def[0]\n                docstring = call_def.docstring()\n            except IndexError:\n                return default_info_response()\n\n        name = call_def.name\n        if name is None:\n            return default_info_response()\n\n        if call_def.module_path:\n            mod_name = get_parent_until(call_def.module_path)\n        else:\n            mod_name = None\n\n        if not mod_name:\n            mod_name = call_def.module_name\n\n        if docstring.startswith(name + '('):\n            calltip = getsignaturefromtext(docstring, name)\n            argspec = calltip[calltip.find('('):]\n            docstring = docstring[docstring.find(')') + 3:]\n        elif docstring and '(' in docstring.splitlines()[0]:\n            calltip = docstring.splitlines()[0]\n            name = docstring.split('(')[0]\n            docstring = docstring[docstring.find(')') + 3:]\n            argspec = calltip[calltip.find('('):]\n        else:\n            calltip = name + '(...)'\n            argspec = '()'\n\n        if call_def.type == 'module':\n            note = 'Module %s' % mod_name\n            argspec = ''\n            calltip = name\n        elif call_def.type == 'class':\n            note = 'Class in %s module' % mod_name\n        elif docstring.startswith('%s(self' % name):\n            class_name = call_def.full_name.split('.')[-2]\n            note = 'Method of %s class in %s module' % (\n                class_name.capitalize(), mod_name)\n        else:\n            note = '%s in %s module' % (call_def.type.capitalize(),\n                                        mod_name)\n\n        argspec = argspec.replace(' = ', '=')\n        calltip = calltip.replace(' = ', '=')\n        debug_print(call_def.name)\n\n        doc_info = dict(name=name, argspec=argspec,\n                        note=note, docstring=docstring, calltip=calltip)\n        return doc_info\n\n    def get_definition(self, info):\n        \"\"\"\n        Find a definition location using Jedi\n\n        Follows gotos until a definition is found, or it reaches a builtin\n        module.  Falls back on token lookup if it is in an enaml file or does\n        not find a match\n        \"\"\"\n        line, filename = info['line_num'], info['filename']\n        def_info, module_path, line_nr = None, None, None\n        gotos = self.get_jedi_object('goto_assignments', info)\n\n        if gotos:\n            def_info = self.get_definition_info(gotos[0])\n        if def_info and def_info['goto_next']:\n            defns = self.get_jedi_object('goto_definitions', info)\n            new_info = None\n            if defns:\n                new_info = self.get_definition_info(defns[0])\n            if new_info and not new_info['in_builtin']:\n                def_info = new_info\n        elif not def_info:\n            return\n\n        # handle builtins -> try and find the module\n        if def_info and def_info['in_builtin']:\n            module_path, line_nr = self.find_in_builtin(def_info)\n        elif def_info:\n            module_path = def_info['module_path']\n            line_nr = def_info['line_nr']\n\n        # Handle failures to find module_path and line_nr\n        if module_path == filename and line_nr == line:\n            return\n        elif module_path is None:\n            return\n\n        return module_path, line_nr\n\n    # ---- Private API -------------------------------------------------------\n\n    def get_jedi_object(self, func_name, info, use_filename=True):\n        \"\"\"Call a desired function on a Jedi Script and return the result\"\"\"\n        if not jedi:\n            return\n        if DEBUG_EDITOR:\n            t0 = time.time()\n        # override IPython qt_loaders ImportDenier behavior\n        metas = sys.meta_path\n        for meta in metas:\n            if (meta.__class__.__name__ == 'ImportDenier'\n                    and hasattr(meta, 'forbid')):\n                sys.meta_path.remove(meta)\n\n        if use_filename:\n            filename = info['filename']\n        else:\n            filename = None\n\n        try:\n            script = jedi.api.Script(info['source_code'], info['line_num'],\n                                     info['column'], filename,\n                                     sys_path=info['sys_path'])\n            func = getattr(script, func_name)\n            val = func()\n        except Exception as e:\n            val = None\n            debug_print('Jedi error (%s)' % func_name)\n            debug_print(str(e))\n            if DEBUG_EDITOR:\n                log_last_error(LOG_FILENAME, str(e))\n        if DEBUG_EDITOR:\n            log_dt(LOG_FILENAME, func_name, t0)\n        if not val and filename:\n            return self.get_jedi_object(func_name, info, False)\n        else:\n            return val\n\n    @staticmethod\n    def get_definition_info(defn):\n        \"\"\"Extract definition information from the Jedi definition object\"\"\"\n        try:\n            module_path = defn.module_path\n            name = defn.name\n            if hasattr(defn, 'line_nr'):\n                line_nr = defn.line_nr\n            else:\n                line_nr = defn.line\n            description = defn.description\n            in_builtin = defn.in_builtin_module()\n        except Exception as e:\n            if DEBUG_EDITOR:\n                log_last_error(LOG_FILENAME, 'Get Defintion: %s' % e)\n            return None\n        pattern = r'class\\s+{0}|def\\s+{0}|self.{0}\\s*=|{0}\\s*='.format(name)\n        if not re.match(pattern, description):\n            goto_next = True\n        else:\n            goto_next = False\n        return dict(module_path=module_path, line_nr=line_nr,\n                    description=description, name=name, in_builtin=in_builtin,\n                    goto_next=goto_next)\n\n    def find_in_builtin(self, info):\n        \"\"\"Find a definition in a builtin file\"\"\"\n        module_path = info['module_path']\n        line_nr = info['line_nr']\n\n        if module_path is None:\n            return None, None\n\n        ext = osp.splitext(info['module_path'])[1]\n        desc = info['description']\n        name = info['name']\n        if ext in self.python_like_exts() and (\n                desc.startswith('import ') or desc.startswith('from ')):\n            path = self.python_like_mod_finder(desc,\n                                          osp.dirname(module_path), name)\n            if path:\n                info['module_path'] = module_path = path\n                info['line_nr'] = line_nr = 1\n\n        if ext in self.all_editable_exts():\n            pattern = r'from.*\\W{0}\\W?.*c?import|import.*\\W{0}'\n            if not re.match(pattern.format(info['name']), desc):\n                line_nr = self.get_definition_from_file(module_path, name,\n                                                        line_nr)\n                if not line_nr:\n                    module_path = None\n\n        if not ext in self.all_editable_exts():\n            line_nr = None\n\n        return module_path, line_nr\n", "path": "spyder/utils/introspection/jedi_plugin.py"}]}