We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
curious behavior on Ijulia on IPython3.0b1 on windows
Hi,

Trying `Pkg.`+`<Tab>` on a cell of a IJulia notebook, I can select `build` in the dropdown window, but when I validate it, only `build`remains in my cell (instead of `Pkg.build`)

(on Firefox as on Chrome)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `IPython/kernel/adapter.py`
Content:
```
1 """Adapters for IPython msg spec versions."""
2 
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5 
6 import json
7 
8 from IPython.core.release import kernel_protocol_version_info
9 from IPython.utils.tokenutil import token_at_cursor
10 
11 
12 def code_to_line(code, cursor_pos):
13     """Turn a multiline code block and cursor position into a single line
14     and new cursor position.
15     
16     For adapting ``complete_`` and ``object_info_request``.
17     """
18     if not code:
19         return "", 0
20     for line in code.splitlines(True):
21         n = len(line)
22         if cursor_pos > n:
23             cursor_pos -= n
24         else:
25             break
26     return line, cursor_pos
27 
28 
29 class Adapter(object):
30     """Base class for adapting messages
31     
32     Override message_type(msg) methods to create adapters.
33     """
34     
35     msg_type_map = {}
36     
37     def update_header(self, msg):
38         return msg
39     
40     def update_metadata(self, msg):
41         return msg
42     
43     def update_msg_type(self, msg):
44         header = msg['header']
45         msg_type = header['msg_type']
46         if msg_type in self.msg_type_map:
47             msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]
48         return msg
49     
50     def handle_reply_status_error(self, msg):
51         """This will be called *instead of* the regular handler
52         
53         on any reply with status != ok
54         """
55         return msg
56     
57     def __call__(self, msg):
58         msg = self.update_header(msg)
59         msg = self.update_metadata(msg)
60         msg = self.update_msg_type(msg)
61         header = msg['header']
62         
63         handler = getattr(self, header['msg_type'], None)
64         if handler is None:
65             return msg
66         
67         # handle status=error replies separately (no change, at present)
68         if msg['content'].get('status', None) in {'error', 'aborted'}:
69             return self.handle_reply_status_error(msg)
70         return handler(msg)
71 
72 def _version_str_to_list(version):
73     """convert a version string to a list of ints
74     
75     non-int segments are excluded
76     """
77     v = []
78     for part in version.split('.'):
79         try:
80             v.append(int(part))
81         except ValueError:
82             pass
83     return v
84 
85 class V5toV4(Adapter):
86     """Adapt msg protocol v5 to v4"""
87     
88     version = '4.1'
89     
90     msg_type_map = {
91         'execute_result' : 'pyout',
92         'execute_input' : 'pyin',
93         'error' : 'pyerr',
94         'inspect_request' : 'object_info_request',
95         'inspect_reply' : 'object_info_reply',
96     }
97     
98     def update_header(self, msg):
99         msg['header'].pop('version', None)
100         return msg
101     
102     # shell channel
103     
104     def kernel_info_reply(self, msg):
105         v4c = {}
106         content = msg['content']
107         for key in ('language_version', 'protocol_version'):
108             if key in content:
109                 v4c[key] = _version_str_to_list(content[key])
110         if content.get('implementation', '') == 'ipython' \
111             and 'implementation_version' in content:
112             v4c['ipython_version'] = _version_str_to_list(content['implementation_version'])
113         language_info = content.get('language_info', {})
114         language = language_info.get('name', '')
115         v4c.setdefault('language', language)
116         if 'version' in language_info:
117             v4c.setdefault('language_version', _version_str_to_list(language_info['version']))
118         msg['content'] = v4c
119         return msg
120     
121     def execute_request(self, msg):
122         content = msg['content']
123         content.setdefault('user_variables', [])
124         return msg
125     
126     def execute_reply(self, msg):
127         content = msg['content']
128         content.setdefault('user_variables', {})
129         # TODO: handle payloads
130         return msg
131     
132     def complete_request(self, msg):
133         content = msg['content']
134         code = content['code']
135         cursor_pos = content['cursor_pos']
136         line, cursor_pos = code_to_line(code, cursor_pos)
137         
138         new_content = msg['content'] = {}
139         new_content['text'] = ''
140         new_content['line'] = line
141         new_content['block'] = None
142         new_content['cursor_pos'] = cursor_pos
143         return msg
144     
145     def complete_reply(self, msg):
146         content = msg['content']
147         cursor_start = content.pop('cursor_start')
148         cursor_end = content.pop('cursor_end')
149         match_len = cursor_end - cursor_start
150         content['matched_text'] = content['matches'][0][:match_len]
151         content.pop('metadata', None)
152         return msg
153     
154     def object_info_request(self, msg):
155         content = msg['content']
156         code = content['code']
157         cursor_pos = content['cursor_pos']
158         line, _ = code_to_line(code, cursor_pos)
159         
160         new_content = msg['content'] = {}
161         new_content['oname'] = token_at_cursor(code, cursor_pos)
162         new_content['detail_level'] = content['detail_level']
163         return msg
164     
165     def object_info_reply(self, msg):
166         """inspect_reply can't be easily backward compatible"""
167         msg['content'] = {'found' : False, 'oname' : 'unknown'}
168         return msg
169     
170     # iopub channel
171     
172     def stream(self, msg):
173         content = msg['content']
174         content['data'] = content.pop('text')
175         return msg
176 
177     def display_data(self, msg):
178         content = msg['content']
179         content.setdefault("source", "display")
180         data = content['data']
181         if 'application/json' in data:
182             try:
183                 data['application/json'] = json.dumps(data['application/json'])
184             except Exception:
185                 # warn?
186                 pass
187         return msg
188     
189     # stdin channel
190     
191     def input_request(self, msg):
192         msg['content'].pop('password', None)
193         return msg
194 
195 
196 class V4toV5(Adapter):
197     """Convert msg spec V4 to V5"""
198     version = '5.0'
199     
200     # invert message renames above
201     msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}
202     
203     def update_header(self, msg):
204         msg['header']['version'] = self.version
205         return msg
206     
207     # shell channel
208     
209     def kernel_info_reply(self, msg):
210         content = msg['content']
211         for key in ('protocol_version', 'ipython_version'):
212             if key in content:
213                 content[key] = '.'.join(map(str, content[key]))
214         
215         content.setdefault('protocol_version', '4.1')
216         
217         if content['language'].startswith('python') and 'ipython_version' in content:
218             content['implementation'] = 'ipython'
219             content['implementation_version'] = content.pop('ipython_version')
220         
221         language = content.pop('language')
222         language_info = content.setdefault('language_info', {})
223         language_info.setdefault('name', language)
224         if 'language_version' in content:
225             language_version = '.'.join(map(str, content.pop('language_version')))
226             language_info.setdefault('version', language_version)
227         
228         content['banner'] = ''
229         return msg
230     
231     def execute_request(self, msg):
232         content = msg['content']
233         user_variables = content.pop('user_variables', [])
234         user_expressions = content.setdefault('user_expressions', {})
235         for v in user_variables:
236             user_expressions[v] = v
237         return msg
238     
239     def execute_reply(self, msg):
240         content = msg['content']
241         user_expressions = content.setdefault('user_expressions', {})
242         user_variables = content.pop('user_variables', {})
243         if user_variables:
244             user_expressions.update(user_variables)
245 
246         # Pager payloads became a mime bundle
247         for payload in content.get('payload', []):
248             if payload.get('source', None) == 'page' and ('text' in payload):
249                 if 'data' not in payload:
250                     payload['data'] = {}
251                 payload['data']['text/plain'] = payload.pop('text')
252 
253         return msg
254     
255     def complete_request(self, msg):
256         old_content = msg['content']
257         
258         new_content = msg['content'] = {}
259         new_content['code'] = old_content['line']
260         new_content['cursor_pos'] = old_content['cursor_pos']
261         return msg
262     
263     def complete_reply(self, msg):
264         # complete_reply needs more context than we have to get cursor_start and end.
265         # use special value of `-1` to indicate to frontend that it should be at
266         # the current cursor position.
267         content = msg['content']
268         new_content = msg['content'] = {'status' : 'ok'}
269         new_content['matches'] = content['matches']
270         new_content['cursor_start'] = -len(content['matched_text'])
271         new_content['cursor_end'] = None
272         new_content['metadata'] = {}
273         return msg
274     
275     def inspect_request(self, msg):
276         content = msg['content']
277         name = content['oname']
278         
279         new_content = msg['content'] = {}
280         new_content['code'] = name
281         new_content['cursor_pos'] = len(name)
282         new_content['detail_level'] = content['detail_level']
283         return msg
284     
285     def inspect_reply(self, msg):
286         """inspect_reply can't be easily backward compatible"""
287         content = msg['content']
288         new_content = msg['content'] = {'status' : 'ok'}
289         found = new_content['found'] = content['found']
290         new_content['name'] = content['oname']
291         new_content['data'] = data = {}
292         new_content['metadata'] = {}
293         if found:
294             lines = []
295             for key in ('call_def', 'init_definition', 'definition'):
296                 if content.get(key, False):
297                     lines.append(content[key])
298                     break
299             for key in ('call_docstring', 'init_docstring', 'docstring'):
300                 if content.get(key, False):
301                     lines.append(content[key])
302                     break
303             if not lines:
304                 lines.append("<empty docstring>")
305             data['text/plain'] = '\n'.join(lines)
306         return msg
307     
308     # iopub channel
309     
310     def stream(self, msg):
311         content = msg['content']
312         content['text'] = content.pop('data')
313         return msg
314 
315     def display_data(self, msg):
316         content = msg['content']
317         content.pop("source", None)
318         data = content['data']
319         if 'application/json' in data:
320             try:
321                 data['application/json'] = json.loads(data['application/json'])
322             except Exception:
323                 # warn?
324                 pass
325         return msg
326     
327     # stdin channel
328     
329     def input_request(self, msg):
330         msg['content'].setdefault('password', False)
331         return msg
332     
333 
334 
335 def adapt(msg, to_version=kernel_protocol_version_info[0]):
336     """Adapt a single message to a target version
337     
338     Parameters
339     ----------
340     
341     msg : dict
342         An IPython message.
343     to_version : int, optional
344         The target major version.
345         If unspecified, adapt to the current version for IPython.
346     
347     Returns
348     -------
349     
350     msg : dict
351         An IPython message appropriate in the new version.
352     """
353     header = msg['header']
354     if 'version' in header:
355         from_version = int(header['version'].split('.')[0])
356     else:
357         # assume last version before adding the key to the header
358         from_version = 4
359     adapter = adapters.get((from_version, to_version), None)
360     if adapter is None:
361         return msg
362     return adapter(msg)
363 
364 
365 # one adapter per major version from,to
366 adapters = {
367     (5,4) : V5toV4(),
368     (4,5) : V4toV5(),
369 }
370
```
--- 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/IPython/kernel/adapter.py b/IPython/kernel/adapter.py
--- a/IPython/kernel/adapter.py
+++ b/IPython/kernel/adapter.py
@@ -262,12 +262,17 @@
     
     def complete_reply(self, msg):
         # complete_reply needs more context than we have to get cursor_start and end.
-        # use special value of `-1` to indicate to frontend that it should be at
-        # the current cursor position.
+        # use special end=null to indicate current cursor position and negative offset
+        # for start relative to the cursor.
+        # start=None indicates that start == end (accounts for no -0).
         content = msg['content']
         new_content = msg['content'] = {'status' : 'ok'}
         new_content['matches'] = content['matches']
-        new_content['cursor_start'] = -len(content['matched_text'])
+        if content['matched_text']:
+            new_content['cursor_start'] = -len(content['matched_text'])
+        else:
+            # no -0, use None to indicate that start == end
+            new_content['cursor_start'] = None
         new_content['cursor_end'] = None
         new_content['metadata'] = {}
         return msg

{"golden_diff": "diff --git a/IPython/kernel/adapter.py b/IPython/kernel/adapter.py\n--- a/IPython/kernel/adapter.py\n+++ b/IPython/kernel/adapter.py\n@@ -262,12 +262,17 @@\n     \n     def complete_reply(self, msg):\n         # complete_reply needs more context than we have to get cursor_start and end.\n-        # use special value of `-1` to indicate to frontend that it should be at\n-        # the current cursor position.\n+        # use special end=null to indicate current cursor position and negative offset\n+        # for start relative to the cursor.\n+        # start=None indicates that start == end (accounts for no -0).\n         content = msg['content']\n         new_content = msg['content'] = {'status' : 'ok'}\n         new_content['matches'] = content['matches']\n-        new_content['cursor_start'] = -len(content['matched_text'])\n+        if content['matched_text']:\n+            new_content['cursor_start'] = -len(content['matched_text'])\n+        else:\n+            # no -0, use None to indicate that start == end\n+            new_content['cursor_start'] = None\n         new_content['cursor_end'] = None\n         new_content['metadata'] = {}\n         return msg\n", "issue": "curious behavior on Ijulia on IPython3.0b1 on windows\nHi,\n\nTrying `Pkg.`+`<Tab>` on a cell of a IJulia notebook, I can select `build` in the dropdown window, but when I validate it, only `build`remains in my cell (instead of `Pkg.build`)\n\n(on Firefox as on Chrome)\n\n", "before_files": [{"content": "\"\"\"Adapters for IPython msg spec versions.\"\"\"\n\n# Copyright (c) IPython Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport json\n\nfrom IPython.core.release import kernel_protocol_version_info\nfrom IPython.utils.tokenutil import token_at_cursor\n\n\ndef code_to_line(code, cursor_pos):\n    \"\"\"Turn a multiline code block and cursor position into a single line\n    and new cursor position.\n    \n    For adapting ``complete_`` and ``object_info_request``.\n    \"\"\"\n    if not code:\n        return \"\", 0\n    for line in code.splitlines(True):\n        n = len(line)\n        if cursor_pos > n:\n            cursor_pos -= n\n        else:\n            break\n    return line, cursor_pos\n\n\nclass Adapter(object):\n    \"\"\"Base class for adapting messages\n    \n    Override message_type(msg) methods to create adapters.\n    \"\"\"\n    \n    msg_type_map = {}\n    \n    def update_header(self, msg):\n        return msg\n    \n    def update_metadata(self, msg):\n        return msg\n    \n    def update_msg_type(self, msg):\n        header = msg['header']\n        msg_type = header['msg_type']\n        if msg_type in self.msg_type_map:\n            msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]\n        return msg\n    \n    def handle_reply_status_error(self, msg):\n        \"\"\"This will be called *instead of* the regular handler\n        \n        on any reply with status != ok\n        \"\"\"\n        return msg\n    \n    def __call__(self, msg):\n        msg = self.update_header(msg)\n        msg = self.update_metadata(msg)\n        msg = self.update_msg_type(msg)\n        header = msg['header']\n        \n        handler = getattr(self, header['msg_type'], None)\n        if handler is None:\n            return msg\n        \n        # handle status=error replies separately (no change, at present)\n        if msg['content'].get('status', None) in {'error', 'aborted'}:\n            return self.handle_reply_status_error(msg)\n        return handler(msg)\n\ndef _version_str_to_list(version):\n    \"\"\"convert a version string to a list of ints\n    \n    non-int segments are excluded\n    \"\"\"\n    v = []\n    for part in version.split('.'):\n        try:\n            v.append(int(part))\n        except ValueError:\n            pass\n    return v\n\nclass V5toV4(Adapter):\n    \"\"\"Adapt msg protocol v5 to v4\"\"\"\n    \n    version = '4.1'\n    \n    msg_type_map = {\n        'execute_result' : 'pyout',\n        'execute_input' : 'pyin',\n        'error' : 'pyerr',\n        'inspect_request' : 'object_info_request',\n        'inspect_reply' : 'object_info_reply',\n    }\n    \n    def update_header(self, msg):\n        msg['header'].pop('version', None)\n        return msg\n    \n    # shell channel\n    \n    def kernel_info_reply(self, msg):\n        v4c = {}\n        content = msg['content']\n        for key in ('language_version', 'protocol_version'):\n            if key in content:\n                v4c[key] = _version_str_to_list(content[key])\n        if content.get('implementation', '') == 'ipython' \\\n            and 'implementation_version' in content:\n            v4c['ipython_version'] = _version_str_to_list(content['implementation_version'])\n        language_info = content.get('language_info', {})\n        language = language_info.get('name', '')\n        v4c.setdefault('language', language)\n        if 'version' in language_info:\n            v4c.setdefault('language_version', _version_str_to_list(language_info['version']))\n        msg['content'] = v4c\n        return msg\n    \n    def execute_request(self, msg):\n        content = msg['content']\n        content.setdefault('user_variables', [])\n        return msg\n    \n    def execute_reply(self, msg):\n        content = msg['content']\n        content.setdefault('user_variables', {})\n        # TODO: handle payloads\n        return msg\n    \n    def complete_request(self, msg):\n        content = msg['content']\n        code = content['code']\n        cursor_pos = content['cursor_pos']\n        line, cursor_pos = code_to_line(code, cursor_pos)\n        \n        new_content = msg['content'] = {}\n        new_content['text'] = ''\n        new_content['line'] = line\n        new_content['block'] = None\n        new_content['cursor_pos'] = cursor_pos\n        return msg\n    \n    def complete_reply(self, msg):\n        content = msg['content']\n        cursor_start = content.pop('cursor_start')\n        cursor_end = content.pop('cursor_end')\n        match_len = cursor_end - cursor_start\n        content['matched_text'] = content['matches'][0][:match_len]\n        content.pop('metadata', None)\n        return msg\n    \n    def object_info_request(self, msg):\n        content = msg['content']\n        code = content['code']\n        cursor_pos = content['cursor_pos']\n        line, _ = code_to_line(code, cursor_pos)\n        \n        new_content = msg['content'] = {}\n        new_content['oname'] = token_at_cursor(code, cursor_pos)\n        new_content['detail_level'] = content['detail_level']\n        return msg\n    \n    def object_info_reply(self, msg):\n        \"\"\"inspect_reply can't be easily backward compatible\"\"\"\n        msg['content'] = {'found' : False, 'oname' : 'unknown'}\n        return msg\n    \n    # iopub channel\n    \n    def stream(self, msg):\n        content = msg['content']\n        content['data'] = content.pop('text')\n        return msg\n\n    def display_data(self, msg):\n        content = msg['content']\n        content.setdefault(\"source\", \"display\")\n        data = content['data']\n        if 'application/json' in data:\n            try:\n                data['application/json'] = json.dumps(data['application/json'])\n            except Exception:\n                # warn?\n                pass\n        return msg\n    \n    # stdin channel\n    \n    def input_request(self, msg):\n        msg['content'].pop('password', None)\n        return msg\n\n\nclass V4toV5(Adapter):\n    \"\"\"Convert msg spec V4 to V5\"\"\"\n    version = '5.0'\n    \n    # invert message renames above\n    msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}\n    \n    def update_header(self, msg):\n        msg['header']['version'] = self.version\n        return msg\n    \n    # shell channel\n    \n    def kernel_info_reply(self, msg):\n        content = msg['content']\n        for key in ('protocol_version', 'ipython_version'):\n            if key in content:\n                content[key] = '.'.join(map(str, content[key]))\n        \n        content.setdefault('protocol_version', '4.1')\n        \n        if content['language'].startswith('python') and 'ipython_version' in content:\n            content['implementation'] = 'ipython'\n            content['implementation_version'] = content.pop('ipython_version')\n        \n        language = content.pop('language')\n        language_info = content.setdefault('language_info', {})\n        language_info.setdefault('name', language)\n        if 'language_version' in content:\n            language_version = '.'.join(map(str, content.pop('language_version')))\n            language_info.setdefault('version', language_version)\n        \n        content['banner'] = ''\n        return msg\n    \n    def execute_request(self, msg):\n        content = msg['content']\n        user_variables = content.pop('user_variables', [])\n        user_expressions = content.setdefault('user_expressions', {})\n        for v in user_variables:\n            user_expressions[v] = v\n        return msg\n    \n    def execute_reply(self, msg):\n        content = msg['content']\n        user_expressions = content.setdefault('user_expressions', {})\n        user_variables = content.pop('user_variables', {})\n        if user_variables:\n            user_expressions.update(user_variables)\n\n        # Pager payloads became a mime bundle\n        for payload in content.get('payload', []):\n            if payload.get('source', None) == 'page' and ('text' in payload):\n                if 'data' not in payload:\n                    payload['data'] = {}\n                payload['data']['text/plain'] = payload.pop('text')\n\n        return msg\n    \n    def complete_request(self, msg):\n        old_content = msg['content']\n        \n        new_content = msg['content'] = {}\n        new_content['code'] = old_content['line']\n        new_content['cursor_pos'] = old_content['cursor_pos']\n        return msg\n    \n    def complete_reply(self, msg):\n        # complete_reply needs more context than we have to get cursor_start and end.\n        # use special value of `-1` to indicate to frontend that it should be at\n        # the current cursor position.\n        content = msg['content']\n        new_content = msg['content'] = {'status' : 'ok'}\n        new_content['matches'] = content['matches']\n        new_content['cursor_start'] = -len(content['matched_text'])\n        new_content['cursor_end'] = None\n        new_content['metadata'] = {}\n        return msg\n    \n    def inspect_request(self, msg):\n        content = msg['content']\n        name = content['oname']\n        \n        new_content = msg['content'] = {}\n        new_content['code'] = name\n        new_content['cursor_pos'] = len(name)\n        new_content['detail_level'] = content['detail_level']\n        return msg\n    \n    def inspect_reply(self, msg):\n        \"\"\"inspect_reply can't be easily backward compatible\"\"\"\n        content = msg['content']\n        new_content = msg['content'] = {'status' : 'ok'}\n        found = new_content['found'] = content['found']\n        new_content['name'] = content['oname']\n        new_content['data'] = data = {}\n        new_content['metadata'] = {}\n        if found:\n            lines = []\n            for key in ('call_def', 'init_definition', 'definition'):\n                if content.get(key, False):\n                    lines.append(content[key])\n                    break\n            for key in ('call_docstring', 'init_docstring', 'docstring'):\n                if content.get(key, False):\n                    lines.append(content[key])\n                    break\n            if not lines:\n                lines.append(\"<empty docstring>\")\n            data['text/plain'] = '\\n'.join(lines)\n        return msg\n    \n    # iopub channel\n    \n    def stream(self, msg):\n        content = msg['content']\n        content['text'] = content.pop('data')\n        return msg\n\n    def display_data(self, msg):\n        content = msg['content']\n        content.pop(\"source\", None)\n        data = content['data']\n        if 'application/json' in data:\n            try:\n                data['application/json'] = json.loads(data['application/json'])\n            except Exception:\n                # warn?\n                pass\n        return msg\n    \n    # stdin channel\n    \n    def input_request(self, msg):\n        msg['content'].setdefault('password', False)\n        return msg\n    \n\n\ndef adapt(msg, to_version=kernel_protocol_version_info[0]):\n    \"\"\"Adapt a single message to a target version\n    \n    Parameters\n    ----------\n    \n    msg : dict\n        An IPython message.\n    to_version : int, optional\n        The target major version.\n        If unspecified, adapt to the current version for IPython.\n    \n    Returns\n    -------\n    \n    msg : dict\n        An IPython message appropriate in the new version.\n    \"\"\"\n    header = msg['header']\n    if 'version' in header:\n        from_version = int(header['version'].split('.')[0])\n    else:\n        # assume last version before adding the key to the header\n        from_version = 4\n    adapter = adapters.get((from_version, to_version), None)\n    if adapter is None:\n        return msg\n    return adapter(msg)\n\n\n# one adapter per major version from,to\nadapters = {\n    (5,4) : V5toV4(),\n    (4,5) : V4toV5(),\n}\n", "path": "IPython/kernel/adapter.py"}], "after_files": [{"content": "\"\"\"Adapters for IPython msg spec versions.\"\"\"\n\n# Copyright (c) IPython Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport json\n\nfrom IPython.core.release import kernel_protocol_version_info\nfrom IPython.utils.tokenutil import token_at_cursor\n\n\ndef code_to_line(code, cursor_pos):\n    \"\"\"Turn a multiline code block and cursor position into a single line\n    and new cursor position.\n    \n    For adapting ``complete_`` and ``object_info_request``.\n    \"\"\"\n    if not code:\n        return \"\", 0\n    for line in code.splitlines(True):\n        n = len(line)\n        if cursor_pos > n:\n            cursor_pos -= n\n        else:\n            break\n    return line, cursor_pos\n\n\nclass Adapter(object):\n    \"\"\"Base class for adapting messages\n    \n    Override message_type(msg) methods to create adapters.\n    \"\"\"\n    \n    msg_type_map = {}\n    \n    def update_header(self, msg):\n        return msg\n    \n    def update_metadata(self, msg):\n        return msg\n    \n    def update_msg_type(self, msg):\n        header = msg['header']\n        msg_type = header['msg_type']\n        if msg_type in self.msg_type_map:\n            msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]\n        return msg\n    \n    def handle_reply_status_error(self, msg):\n        \"\"\"This will be called *instead of* the regular handler\n        \n        on any reply with status != ok\n        \"\"\"\n        return msg\n    \n    def __call__(self, msg):\n        msg = self.update_header(msg)\n        msg = self.update_metadata(msg)\n        msg = self.update_msg_type(msg)\n        header = msg['header']\n        \n        handler = getattr(self, header['msg_type'], None)\n        if handler is None:\n            return msg\n        \n        # handle status=error replies separately (no change, at present)\n        if msg['content'].get('status', None) in {'error', 'aborted'}:\n            return self.handle_reply_status_error(msg)\n        return handler(msg)\n\ndef _version_str_to_list(version):\n    \"\"\"convert a version string to a list of ints\n    \n    non-int segments are excluded\n    \"\"\"\n    v = []\n    for part in version.split('.'):\n        try:\n            v.append(int(part))\n        except ValueError:\n            pass\n    return v\n\nclass V5toV4(Adapter):\n    \"\"\"Adapt msg protocol v5 to v4\"\"\"\n    \n    version = '4.1'\n    \n    msg_type_map = {\n        'execute_result' : 'pyout',\n        'execute_input' : 'pyin',\n        'error' : 'pyerr',\n        'inspect_request' : 'object_info_request',\n        'inspect_reply' : 'object_info_reply',\n    }\n    \n    def update_header(self, msg):\n        msg['header'].pop('version', None)\n        return msg\n    \n    # shell channel\n    \n    def kernel_info_reply(self, msg):\n        v4c = {}\n        content = msg['content']\n        for key in ('language_version', 'protocol_version'):\n            if key in content:\n                v4c[key] = _version_str_to_list(content[key])\n        if content.get('implementation', '') == 'ipython' \\\n            and 'implementation_version' in content:\n            v4c['ipython_version'] = _version_str_to_list(content['implementation_version'])\n        language_info = content.get('language_info', {})\n        language = language_info.get('name', '')\n        v4c.setdefault('language', language)\n        if 'version' in language_info:\n            v4c.setdefault('language_version', _version_str_to_list(language_info['version']))\n        msg['content'] = v4c\n        return msg\n    \n    def execute_request(self, msg):\n        content = msg['content']\n        content.setdefault('user_variables', [])\n        return msg\n    \n    def execute_reply(self, msg):\n        content = msg['content']\n        content.setdefault('user_variables', {})\n        # TODO: handle payloads\n        return msg\n    \n    def complete_request(self, msg):\n        content = msg['content']\n        code = content['code']\n        cursor_pos = content['cursor_pos']\n        line, cursor_pos = code_to_line(code, cursor_pos)\n        \n        new_content = msg['content'] = {}\n        new_content['text'] = ''\n        new_content['line'] = line\n        new_content['block'] = None\n        new_content['cursor_pos'] = cursor_pos\n        return msg\n    \n    def complete_reply(self, msg):\n        content = msg['content']\n        cursor_start = content.pop('cursor_start')\n        cursor_end = content.pop('cursor_end')\n        match_len = cursor_end - cursor_start\n        content['matched_text'] = content['matches'][0][:match_len]\n        content.pop('metadata', None)\n        return msg\n    \n    def object_info_request(self, msg):\n        content = msg['content']\n        code = content['code']\n        cursor_pos = content['cursor_pos']\n        line, _ = code_to_line(code, cursor_pos)\n        \n        new_content = msg['content'] = {}\n        new_content['oname'] = token_at_cursor(code, cursor_pos)\n        new_content['detail_level'] = content['detail_level']\n        return msg\n    \n    def object_info_reply(self, msg):\n        \"\"\"inspect_reply can't be easily backward compatible\"\"\"\n        msg['content'] = {'found' : False, 'oname' : 'unknown'}\n        return msg\n    \n    # iopub channel\n    \n    def stream(self, msg):\n        content = msg['content']\n        content['data'] = content.pop('text')\n        return msg\n\n    def display_data(self, msg):\n        content = msg['content']\n        content.setdefault(\"source\", \"display\")\n        data = content['data']\n        if 'application/json' in data:\n            try:\n                data['application/json'] = json.dumps(data['application/json'])\n            except Exception:\n                # warn?\n                pass\n        return msg\n    \n    # stdin channel\n    \n    def input_request(self, msg):\n        msg['content'].pop('password', None)\n        return msg\n\n\nclass V4toV5(Adapter):\n    \"\"\"Convert msg spec V4 to V5\"\"\"\n    version = '5.0'\n    \n    # invert message renames above\n    msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}\n    \n    def update_header(self, msg):\n        msg['header']['version'] = self.version\n        return msg\n    \n    # shell channel\n    \n    def kernel_info_reply(self, msg):\n        content = msg['content']\n        for key in ('protocol_version', 'ipython_version'):\n            if key in content:\n                content[key] = '.'.join(map(str, content[key]))\n        \n        content.setdefault('protocol_version', '4.1')\n        \n        if content['language'].startswith('python') and 'ipython_version' in content:\n            content['implementation'] = 'ipython'\n            content['implementation_version'] = content.pop('ipython_version')\n        \n        language = content.pop('language')\n        language_info = content.setdefault('language_info', {})\n        language_info.setdefault('name', language)\n        if 'language_version' in content:\n            language_version = '.'.join(map(str, content.pop('language_version')))\n            language_info.setdefault('version', language_version)\n        \n        content['banner'] = ''\n        return msg\n    \n    def execute_request(self, msg):\n        content = msg['content']\n        user_variables = content.pop('user_variables', [])\n        user_expressions = content.setdefault('user_expressions', {})\n        for v in user_variables:\n            user_expressions[v] = v\n        return msg\n    \n    def execute_reply(self, msg):\n        content = msg['content']\n        user_expressions = content.setdefault('user_expressions', {})\n        user_variables = content.pop('user_variables', {})\n        if user_variables:\n            user_expressions.update(user_variables)\n\n        # Pager payloads became a mime bundle\n        for payload in content.get('payload', []):\n            if payload.get('source', None) == 'page' and ('text' in payload):\n                if 'data' not in payload:\n                    payload['data'] = {}\n                payload['data']['text/plain'] = payload.pop('text')\n\n        return msg\n    \n    def complete_request(self, msg):\n        old_content = msg['content']\n        \n        new_content = msg['content'] = {}\n        new_content['code'] = old_content['line']\n        new_content['cursor_pos'] = old_content['cursor_pos']\n        return msg\n    \n    def complete_reply(self, msg):\n        # complete_reply needs more context than we have to get cursor_start and end.\n        # use special end=null to indicate current cursor position and negative offset\n        # for start relative to the cursor.\n        # start=None indicates that start == end (accounts for no -0).\n        content = msg['content']\n        new_content = msg['content'] = {'status' : 'ok'}\n        new_content['matches'] = content['matches']\n        if content['matched_text']:\n            new_content['cursor_start'] = -len(content['matched_text'])\n        else:\n            # no -0, use None to indicate that start == end\n            new_content['cursor_start'] = None\n        new_content['cursor_end'] = None\n        new_content['metadata'] = {}\n        return msg\n    \n    def inspect_request(self, msg):\n        content = msg['content']\n        name = content['oname']\n        \n        new_content = msg['content'] = {}\n        new_content['code'] = name\n        new_content['cursor_pos'] = len(name)\n        new_content['detail_level'] = content['detail_level']\n        return msg\n    \n    def inspect_reply(self, msg):\n        \"\"\"inspect_reply can't be easily backward compatible\"\"\"\n        content = msg['content']\n        new_content = msg['content'] = {'status' : 'ok'}\n        found = new_content['found'] = content['found']\n        new_content['name'] = content['oname']\n        new_content['data'] = data = {}\n        new_content['metadata'] = {}\n        if found:\n            lines = []\n            for key in ('call_def', 'init_definition', 'definition'):\n                if content.get(key, False):\n                    lines.append(content[key])\n                    break\n            for key in ('call_docstring', 'init_docstring', 'docstring'):\n                if content.get(key, False):\n                    lines.append(content[key])\n                    break\n            if not lines:\n                lines.append(\"<empty docstring>\")\n            data['text/plain'] = '\\n'.join(lines)\n        return msg\n    \n    # iopub channel\n    \n    def stream(self, msg):\n        content = msg['content']\n        content['text'] = content.pop('data')\n        return msg\n\n    def display_data(self, msg):\n        content = msg['content']\n        content.pop(\"source\", None)\n        data = content['data']\n        if 'application/json' in data:\n            try:\n                data['application/json'] = json.loads(data['application/json'])\n            except Exception:\n                # warn?\n                pass\n        return msg\n    \n    # stdin channel\n    \n    def input_request(self, msg):\n        msg['content'].setdefault('password', False)\n        return msg\n    \n\n\ndef adapt(msg, to_version=kernel_protocol_version_info[0]):\n    \"\"\"Adapt a single message to a target version\n    \n    Parameters\n    ----------\n    \n    msg : dict\n        An IPython message.\n    to_version : int, optional\n        The target major version.\n        If unspecified, adapt to the current version for IPython.\n    \n    Returns\n    -------\n    \n    msg : dict\n        An IPython message appropriate in the new version.\n    \"\"\"\n    header = msg['header']\n    if 'version' in header:\n        from_version = int(header['version'].split('.')[0])\n    else:\n        # assume last version before adding the key to the header\n        from_version = 4\n    adapter = adapters.get((from_version, to_version), None)\n    if adapter is None:\n        return msg\n    return adapter(msg)\n\n\n# one adapter per major version from,to\nadapters = {\n    (5,4) : V5toV4(),\n    (4,5) : V4toV5(),\n}\n", "path": "IPython/kernel/adapter.py"}]}