1# Copyright 2017-2020 Palantir Technologies, Inc. 2# Copyright 2021- Python Language Server Contributors. 3 4import argparse 5import logging 6import logging.config 7import sys 8import time 9 10try: 11 import ujson as json 12except Exception: # pylint: disable=broad-except 13 import json 14 15from .python_lsp import (PythonLSPServer, start_io_lang_server, 16 start_tcp_lang_server) 17from ._version import __version__ 18 19LOG_FORMAT = "%(asctime)s {0} - %(levelname)s - %(name)s - %(message)s".format( 20 time.localtime().tm_zone) 21 22 23def add_arguments(parser): 24 parser.description = "Python Language Server" 25 26 parser.add_argument( 27 "--tcp", action="store_true", 28 help="Use TCP server instead of stdio" 29 ) 30 parser.add_argument( 31 "--host", default="127.0.0.1", 32 help="Bind to this address" 33 ) 34 parser.add_argument( 35 "--port", type=int, default=2087, 36 help="Bind to this port" 37 ) 38 parser.add_argument( 39 '--check-parent-process', action="store_true", 40 help="Check whether parent process is still alive using os.kill(ppid, 0) " 41 "and auto shut down language server process when parent process is not alive." 42 "Note that this may not work on a Windows machine." 43 ) 44 45 log_group = parser.add_mutually_exclusive_group() 46 log_group.add_argument( 47 "--log-config", 48 help="Path to a JSON file containing Python logging config." 49 ) 50 log_group.add_argument( 51 "--log-file", 52 help="Redirect logs to the given file instead of writing to stderr." 53 "Has no effect if used with --log-config." 54 ) 55 56 parser.add_argument( 57 '-v', '--verbose', action='count', default=0, 58 help="Increase verbosity of log output, overrides log config file" 59 ) 60 61 parser.add_argument( 62 '-V', '--version', action='version', version='%(prog)s v' + __version__ 63 ) 64 65 66def main(): 67 parser = argparse.ArgumentParser() 68 add_arguments(parser) 69 args = parser.parse_args() 70 _configure_logger(args.verbose, args.log_config, args.log_file) 71 72 if args.tcp: 73 start_tcp_lang_server(args.host, args.port, args.check_parent_process, 74 PythonLSPServer) 75 else: 76 stdin, stdout = _binary_stdio() 77 start_io_lang_server(stdin, stdout, args.check_parent_process, 78 PythonLSPServer) 79 80 81def _binary_stdio(): 82 """Construct binary stdio streams (not text mode). 83 84 This seems to be different for Window/Unix Python2/3, so going by: 85 https://stackoverflow.com/questions/2850893/reading-binary-data-from-stdin 86 """ 87 stdin, stdout = sys.stdin.buffer, sys.stdout.buffer 88 return stdin, stdout 89 90 91def _configure_logger(verbose=0, log_config=None, log_file=None): 92 root_logger = logging.root 93 94 if log_config: 95 with open(log_config, 'r', encoding='utf-8') as f: 96 logging.config.dictConfig(json.load(f)) 97 else: 98 formatter = logging.Formatter(LOG_FORMAT) 99 if log_file: 100 log_handler = logging.handlers.RotatingFileHandler( 101 log_file, mode='a', maxBytes=50*1024*1024, 102 backupCount=10, encoding=None, delay=0 103 ) 104 else: 105 log_handler = logging.StreamHandler() 106 log_handler.setFormatter(formatter) 107 root_logger.addHandler(log_handler) 108 109 if verbose == 0: 110 level = logging.WARNING 111 elif verbose == 1: 112 level = logging.INFO 113 elif verbose >= 2: 114 level = logging.DEBUG 115 116 root_logger.setLevel(level) 117 118 119if __name__ == '__main__': 120 main() 121