massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / coc-python-data / languageServer.0.5.59 / inspector.py
1 # Python Tools for Visual Studio\r
2 # Copyright(c) Microsoft Corporation\r
3 # All rights reserved.\r
4 #\r
5 # Licensed under the Apache License, Version 2.0 (the License); you may not use\r
6 # this file except in compliance with the License. You may obtain a copy of the\r
7 # License at http://www.apache.org/licenses/LICENSE-2.0\r
8 #\r
9 # THIS CODE IS PROVIDED ON AN  *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS\r
10 # OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY\r
11 # IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r
12 # MERCHANTABILITY OR NON-INFRINGEMENT.\r
13 #\r
14 # See the Apache Version 2.0 License for specific language governing\r
15 # permissions and limitations under the License.\r
16 \r
17 # Supported Python versions: 2.7, 3.5+\r
18 # https://devguide.python.org/#status-of-python-branches\r
19 \r
20 from __future__ import print_function\r
21 \r
22 import json\r
23 import sys\r
24 import time\r
25 import inspect\r
26 import importlib\r
27 import os.path\r
28 \r
29 # Uncomment to send stderr somewhere readable.\r
30 # sys.stderr = open(os.path.join(os.path.expanduser("~"), "log.txt"), "a")\r
31 \r
32 if sys.version_info >= (3,):\r
33     stdout = sys.stdout.buffer\r
34     stdin = sys.stdin.buffer\r
35 else:\r
36     stdout = sys.stdout\r
37     stdin = sys.stdin\r
38 \r
39     if sys.platform == "win32":\r
40         import os, msvcrt\r
41 \r
42         msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\r
43         msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)\r
44 \r
45 \r
46 def read_line():\r
47     line = b""\r
48     while True:\r
49         try:\r
50             line += stdin.readline()\r
51         except:\r
52             raise EOFError\r
53         if not line:\r
54             raise EOFError\r
55         if line.endswith(b"\r\n"):\r
56             return line[:-2]\r
57 \r
58 \r
59 def write_stdout(data):\r
60     if not isinstance(data, bytes):\r
61         data = data.encode("utf-8")\r
62 \r
63     stdout.write(data)\r
64     stdout.flush()\r
65 \r
66 \r
67 METHOD_NOT_FOUND = -32601\r
68 INTERNAL_ERROR = -32603\r
69 \r
70 \r
71 def read_request():\r
72     headers = dict()\r
73 \r
74     while True:\r
75         line = read_line()\r
76 \r
77         if line == b"":\r
78             break\r
79 \r
80         key, _, value = line.partition(b": ")\r
81         headers[key] = value\r
82 \r
83     length = int(headers[b"Content-Length"])\r
84 \r
85     body = b""\r
86     while length > 0:\r
87         chunk = stdin.read(length)\r
88         body += chunk\r
89         length -= len(chunk)\r
90 \r
91     request = json.loads(body)\r
92     return Request(request)\r
93 \r
94 \r
95 def requests():\r
96     while True:\r
97         try:\r
98             request = read_request()\r
99         except EOFError:\r
100             return\r
101 \r
102         yield request\r
103 \r
104 \r
105 def write_response(id, d):\r
106     response = {"jsonrpc": "2.0", "id": id}\r
107     response.update(d)\r
108 \r
109     s = json.dumps(response)\r
110 \r
111     data = "Content-Length: {}\r\n\r\n".format(len(s)) + s\r
112     write_stdout(data)\r
113 \r
114 \r
115 class Request(object):\r
116     def __init__(self, request):\r
117         self.id = request["id"]\r
118         self.method = request["method"]\r
119         self.params = request.get("params", None)\r
120 \r
121     def write_result(self, result):\r
122         write_response(self.id, {"result": result})\r
123 \r
124     def write_error(self, code, message):\r
125         write_response(self.id, {"error": {"code": code, "message": message}})\r
126 \r
127 \r
128 class Mux(object):\r
129     def __init__(self):\r
130         self.handlers = {}\r
131 \r
132     def handler(self, method):\r
133         def decorator(func):\r
134             self.handlers[method] = func\r
135             return func\r
136 \r
137         return decorator\r
138 \r
139     def handle(self, request):\r
140         handler = self.handlers.get(request.method, None)\r
141 \r
142         if not handler:\r
143             request.write_error(\r
144                 METHOD_NOT_FOUND, "method {} not found".format(request.method)\r
145             )\r
146             return\r
147 \r
148         try:\r
149             result = handler(*request.params)\r
150         except Exception as e:\r
151             request.write_error(INTERNAL_ERROR, str(e))\r
152         else:\r
153             request.write_result(result)\r
154 \r
155 \r
156 mux = Mux()\r
157 \r
158 \r
159 def do_not_inspect(v):\r
160     # https://github.com/Microsoft/python-language-server/issues/740\r
161     # https://github.com/cython/cython/issues/1470\r
162     if type(v).__name__ != "fused_cython_function":\r
163         return False\r
164 \r
165     # If a fused function has __defaults__, then attempting to access\r
166     # __kwdefaults__ will fail if generated before cython 0.29.6.\r
167     return bool(getattr(v, "__defaults__", False))\r
168 \r
169 \r
170 KNOWN_DIST_PREFIXES = {"PyQt5": ["PyQt5"], "PyQt5-sip": ["PyQt5.sip"]}\r
171 \r
172 \r
173 def build_dist_prefixes():\r
174     import pkg_resources\r
175 \r
176     prefixes = dict()\r
177 \r
178     # This iterates in the order things were added; no need to reverse.\r
179     for d in pkg_resources.WorkingSet():\r
180         top_level = None\r
181 \r
182         try:\r
183             top_level = d.get_metadata("top_level.txt")\r
184         except:\r
185             pass\r
186 \r
187         module_prefixes = None\r
188         if top_level:\r
189             module_prefixes = top_level.splitlines()\r
190         elif d.project_name:\r
191             module_prefixes = KNOWN_DIST_PREFIXES.get(d.project_name, None)\r
192 \r
193         if not module_prefixes:\r
194             continue\r
195 \r
196         for prefix in module_prefixes:\r
197             prefix = prefix.strip()\r
198             if prefix:\r
199                 prefixes[prefix] = d\r
200 \r
201     return prefixes\r
202 \r
203 \r
204 DIST_PREFIXES = None\r
205 \r
206 \r
207 def find_dist(module_name):\r
208     global DIST_PREFIXES\r
209     if DIST_PREFIXES is None:\r
210         DIST_PREFIXES = build_dist_prefixes()\r
211 \r
212     while True:\r
213         if not module_name:\r
214             return None\r
215 \r
216         d = DIST_PREFIXES.get(module_name, None)\r
217         if d:\r
218             return d\r
219 \r
220         split = module_name.split(".")\r
221         if len(split) < 2:\r
222             return None\r
223 \r
224         module_name = ".".join(split[:-1])\r
225 \r
226 \r
227 @mux.handler("$/cancelRequest")\r
228 def cancel_request(params):\r
229     return None\r
230 \r
231 \r
232 @mux.handler("moduleMemberNames")\r
233 def module_member_names(module_name):\r
234     try:\r
235         module = importlib.import_module(module_name)\r
236     except:\r
237         return None\r
238 \r
239     members = inspect.getmembers(module)\r
240 \r
241     return {\r
242         "members": [name for name, _ in members],\r
243         "all": getattr(module, "__all__", None),\r
244     }\r
245 \r
246 \r
247 @mux.handler("moduleVersion")\r
248 def module_version(module_name):\r
249     version = None\r
250     try:\r
251         # TODO: iterate as in find_dist and import looking for __version__?\r
252         module = importlib.import_module(module_name)\r
253         version = getattr(module, "__version__", None)\r
254     except:\r
255         pass\r
256 \r
257     if not version:\r
258         try:\r
259             d = find_dist(module_name)\r
260             if d:\r
261                 version = d.version\r
262         except:\r
263             pass\r
264 \r
265     return version\r
266 \r
267 \r
268 def main():\r
269     for request in requests():\r
270         mux.handle(request)\r
271 \r
272 \r
273 if __name__ == "__main__":\r
274     main()\r