--- /dev/null
+# Python Tools for Visual Studio\r
+# Copyright(c) Microsoft Corporation\r
+# All rights reserved.\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the License); you may not use\r
+# this file except in compliance with the License. You may obtain a copy of the\r
+# License at http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS\r
+# OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY\r
+# IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r
+# MERCHANTABILITY OR NON-INFRINGEMENT.\r
+#\r
+# See the Apache Version 2.0 License for specific language governing\r
+# permissions and limitations under the License.\r
+\r
+# Supported Python versions: 2.7, 3.5+\r
+# https://devguide.python.org/#status-of-python-branches\r
+\r
+from __future__ import print_function\r
+\r
+import json\r
+import sys\r
+import time\r
+import inspect\r
+import importlib\r
+import os.path\r
+\r
+# Uncomment to send stderr somewhere readable.\r
+# sys.stderr = open(os.path.join(os.path.expanduser("~"), "log.txt"), "a")\r
+\r
+if sys.version_info >= (3,):\r
+ stdout = sys.stdout.buffer\r
+ stdin = sys.stdin.buffer\r
+else:\r
+ stdout = sys.stdout\r
+ stdin = sys.stdin\r
+\r
+ if sys.platform == "win32":\r
+ import os, msvcrt\r
+\r
+ msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\r
+ msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)\r
+\r
+\r
+def read_line():\r
+ line = b""\r
+ while True:\r
+ try:\r
+ line += stdin.readline()\r
+ except:\r
+ raise EOFError\r
+ if not line:\r
+ raise EOFError\r
+ if line.endswith(b"\r\n"):\r
+ return line[:-2]\r
+\r
+\r
+def write_stdout(data):\r
+ if not isinstance(data, bytes):\r
+ data = data.encode("utf-8")\r
+\r
+ stdout.write(data)\r
+ stdout.flush()\r
+\r
+\r
+METHOD_NOT_FOUND = -32601\r
+INTERNAL_ERROR = -32603\r
+\r
+\r
+def read_request():\r
+ headers = dict()\r
+\r
+ while True:\r
+ line = read_line()\r
+\r
+ if line == b"":\r
+ break\r
+\r
+ key, _, value = line.partition(b": ")\r
+ headers[key] = value\r
+\r
+ length = int(headers[b"Content-Length"])\r
+\r
+ body = b""\r
+ while length > 0:\r
+ chunk = stdin.read(length)\r
+ body += chunk\r
+ length -= len(chunk)\r
+\r
+ request = json.loads(body)\r
+ return Request(request)\r
+\r
+\r
+def requests():\r
+ while True:\r
+ try:\r
+ request = read_request()\r
+ except EOFError:\r
+ return\r
+\r
+ yield request\r
+\r
+\r
+def write_response(id, d):\r
+ response = {"jsonrpc": "2.0", "id": id}\r
+ response.update(d)\r
+\r
+ s = json.dumps(response)\r
+\r
+ data = "Content-Length: {}\r\n\r\n".format(len(s)) + s\r
+ write_stdout(data)\r
+\r
+\r
+class Request(object):\r
+ def __init__(self, request):\r
+ self.id = request["id"]\r
+ self.method = request["method"]\r
+ self.params = request.get("params", None)\r
+\r
+ def write_result(self, result):\r
+ write_response(self.id, {"result": result})\r
+\r
+ def write_error(self, code, message):\r
+ write_response(self.id, {"error": {"code": code, "message": message}})\r
+\r
+\r
+class Mux(object):\r
+ def __init__(self):\r
+ self.handlers = {}\r
+\r
+ def handler(self, method):\r
+ def decorator(func):\r
+ self.handlers[method] = func\r
+ return func\r
+\r
+ return decorator\r
+\r
+ def handle(self, request):\r
+ handler = self.handlers.get(request.method, None)\r
+\r
+ if not handler:\r
+ request.write_error(\r
+ METHOD_NOT_FOUND, "method {} not found".format(request.method)\r
+ )\r
+ return\r
+\r
+ try:\r
+ result = handler(*request.params)\r
+ except Exception as e:\r
+ request.write_error(INTERNAL_ERROR, str(e))\r
+ else:\r
+ request.write_result(result)\r
+\r
+\r
+mux = Mux()\r
+\r
+\r
+def do_not_inspect(v):\r
+ # https://github.com/Microsoft/python-language-server/issues/740\r
+ # https://github.com/cython/cython/issues/1470\r
+ if type(v).__name__ != "fused_cython_function":\r
+ return False\r
+\r
+ # If a fused function has __defaults__, then attempting to access\r
+ # __kwdefaults__ will fail if generated before cython 0.29.6.\r
+ return bool(getattr(v, "__defaults__", False))\r
+\r
+\r
+KNOWN_DIST_PREFIXES = {"PyQt5": ["PyQt5"], "PyQt5-sip": ["PyQt5.sip"]}\r
+\r
+\r
+def build_dist_prefixes():\r
+ import pkg_resources\r
+\r
+ prefixes = dict()\r
+\r
+ # This iterates in the order things were added; no need to reverse.\r
+ for d in pkg_resources.WorkingSet():\r
+ top_level = None\r
+\r
+ try:\r
+ top_level = d.get_metadata("top_level.txt")\r
+ except:\r
+ pass\r
+\r
+ module_prefixes = None\r
+ if top_level:\r
+ module_prefixes = top_level.splitlines()\r
+ elif d.project_name:\r
+ module_prefixes = KNOWN_DIST_PREFIXES.get(d.project_name, None)\r
+\r
+ if not module_prefixes:\r
+ continue\r
+\r
+ for prefix in module_prefixes:\r
+ prefix = prefix.strip()\r
+ if prefix:\r
+ prefixes[prefix] = d\r
+\r
+ return prefixes\r
+\r
+\r
+DIST_PREFIXES = None\r
+\r
+\r
+def find_dist(module_name):\r
+ global DIST_PREFIXES\r
+ if DIST_PREFIXES is None:\r
+ DIST_PREFIXES = build_dist_prefixes()\r
+\r
+ while True:\r
+ if not module_name:\r
+ return None\r
+\r
+ d = DIST_PREFIXES.get(module_name, None)\r
+ if d:\r
+ return d\r
+\r
+ split = module_name.split(".")\r
+ if len(split) < 2:\r
+ return None\r
+\r
+ module_name = ".".join(split[:-1])\r
+\r
+\r
+@mux.handler("$/cancelRequest")\r
+def cancel_request(params):\r
+ return None\r
+\r
+\r
+@mux.handler("moduleMemberNames")\r
+def module_member_names(module_name):\r
+ try:\r
+ module = importlib.import_module(module_name)\r
+ except:\r
+ return None\r
+\r
+ members = inspect.getmembers(module)\r
+\r
+ return {\r
+ "members": [name for name, _ in members],\r
+ "all": getattr(module, "__all__", None),\r
+ }\r
+\r
+\r
+@mux.handler("moduleVersion")\r
+def module_version(module_name):\r
+ version = None\r
+ try:\r
+ # TODO: iterate as in find_dist and import looking for __version__?\r
+ module = importlib.import_module(module_name)\r
+ version = getattr(module, "__version__", None)\r
+ except:\r
+ pass\r
+\r
+ if not version:\r
+ try:\r
+ d = find_dist(module_name)\r
+ if d:\r
+ version = d.version\r
+ except:\r
+ pass\r
+\r
+ return version\r
+\r
+\r
+def main():\r
+ for request in requests():\r
+ mux.handle(request)\r
+\r
+\r
+if __name__ == "__main__":\r
+ main()\r