diff options
author | Josh Marshall <catchjosh@gmail.com> | 2021-03-30 12:32:11 +0900 |
---|---|---|
committer | Josh Marshall <catchjosh@gmail.com> | 2021-03-30 12:32:11 +0900 |
commit | 0cf6f80b517bc0e7f0539cd21516fcb7118e2220 (patch) | |
tree | cf9150928bce61a05beb4066d8cd546cc4c82f4d | |
parent | 73f252aaceab9bb28a62d1d5ba2967cc455c4068 (diff) | |
download | jsonrpclib-0cf6f80b517bc0e7f0539cd21516fcb7118e2220.tar.gz |
Removing Python 2 fallbacks, tweaking test runners, fixing flake8 errors.
-rw-r--r-- | dev-requirements.txt | 14 | ||||
-rw-r--r-- | jsonrpclib/SimpleJSONRPCServer.py | 62 | ||||
-rw-r--r-- | jsonrpclib/__init__.py | 10 | ||||
-rw-r--r-- | jsonrpclib/jsonclass.py | 11 | ||||
-rw-r--r-- | jsonrpclib/jsonrpc.py | 106 | ||||
-rw-r--r-- | tests.py | 71 |
6 files changed, 115 insertions, 159 deletions
diff --git a/dev-requirements.txt b/dev-requirements.txt index c1c28fa..a75fca4 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,11 +1,3 @@ -coverage==4.4 -linecache2==1.0.0 -nose==1.3.7 -pluggy==0.4.0 -py==1.4.33 -six==1.10.0 -tox==2.7.0 -traceback2==1.4.0 -unittest2==1.1.0 -virtualenv==15.1.0 -wheel==0.29.0 +coverage==5.5 +flake8==3.9.0 +pytest==6.2.2 diff --git a/jsonrpclib/SimpleJSONRPCServer.py b/jsonrpclib/SimpleJSONRPCServer.py index 708c5d2..5addcf0 100644 --- a/jsonrpclib/SimpleJSONRPCServer.py +++ b/jsonrpclib/SimpleJSONRPCServer.py @@ -1,27 +1,21 @@ -import jsonrpclib -from jsonrpclib import Fault -from jsonrpclib.jsonrpc import USE_UNIX_SOCKETS -try: - import xmlrpc.server as SimpleXMLRPCServer # Python 3.x - import socketserver as SocketServer -except ImportError: - import SimpleXMLRPCServer # Python 2.7 - import SocketServer -import socket import logging import os -import traceback +import socket +import socketserver import sys +import traceback +import xmlrpc.server + try: import fcntl except ImportError: # For Windows fcntl = None -try: - basestring # Python 2.7 -except NameError: - basestring = str # Python 3.x +import jsonrpclib +from jsonrpclib import Fault +from jsonrpclib.jsonrpc import USE_UNIX_SOCKETS + def get_version(request): # must be a dict @@ -46,7 +40,7 @@ def validate_request(request): request.setdefault('params', []) method = request.get('method', None) params = request.get('params') - if not method or not isinstance(method, basestring) or \ + if not method or not isinstance(method, str) or \ not isinstance(params, (list, dict, tuple)): fault = Fault( -32600, 'Invalid request parameters or method.', rpcid=rpcid @@ -55,10 +49,10 @@ def validate_request(request): return True -class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): +class SimpleJSONRPCDispatcher(xmlrpc.server.SimpleXMLRPCDispatcher): def __init__(self, encoding=None): - SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__( + xmlrpc.server.SimpleXMLRPCDispatcher.__init__( self, allow_none=True, encoding=encoding) def _marshaled_dispatch(self, data, dispatch_method=None): @@ -103,7 +97,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): params = request.get('params') try: response = self._dispatch(method, params) - except: + except Exception: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response() @@ -116,7 +110,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): rpcid=request['id'] ) return response - except: + except Exception: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response() @@ -131,7 +125,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): return self.instance._dispatch(method, params) else: try: - func = SimpleXMLRPCServer.resolve_dotted_attribute( + func = xmlrpc.server.resolve_dotted_attribute( self.instance, method, True @@ -147,7 +141,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): return response # except TypeError: # return Fault(-32602, 'Invalid parameters.') - except: + except Exception: err_lines = traceback.format_exc().splitlines() trace_string = '%s | %s' % (err_lines[-3], err_lines[-1]) fault = jsonrpclib.Fault(-32603, 'Server error: %s' % @@ -158,7 +152,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): class SimpleJSONRPCRequestHandler( - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): + xmlrpc.server.SimpleXMLRPCRequestHandler): def do_POST(self): if not self.is_rpc_path_valid(): @@ -176,7 +170,7 @@ class SimpleJSONRPCRequestHandler( data = ''.join(L) response = self.server._marshaled_dispatch(data) self.send_response(200) - except Exception as ex: + except Exception: self.send_response(500) err_lines = traceback.format_exc().splitlines() trace_string = '%s | %s' % (err_lines[-3], err_lines[-1]) @@ -195,7 +189,7 @@ class SimpleJSONRPCRequestHandler( self.connection.shutdown(1) -class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher): +class SimpleJSONRPCServer(socketserver.TCPServer, SimpleJSONRPCDispatcher): allow_reuse_address = True @@ -204,10 +198,8 @@ class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher): address_family=socket.AF_INET): self.logRequests = logRequests SimpleJSONRPCDispatcher.__init__(self, encoding) - # TCPServer.__init__ has an extra parameter on 2.6+, so - # check Python version and decide on how to call it - vi = sys.version_info self.address_family = address_family + if USE_UNIX_SOCKETS and address_family == socket.AF_UNIX: # Unix sockets can't be bound if they already exist in the # filesystem. The convention of e.g. X11 is to unlink @@ -217,17 +209,15 @@ class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher): os.unlink(addr) except OSError: logging.warning("Could not unlink socket %s", addr) - # if python 2.5 and lower - if vi[0] < 3 and vi[1] < 6: - SocketServer.TCPServer.__init__(self, addr, requestHandler) - else: - SocketServer.TCPServer.__init__( - self, addr, requestHandler, bind_and_activate) + + socketserver.TCPServer.__init__( + self, addr, requestHandler, bind_and_activate) if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) flags |= fcntl.FD_CLOEXEC fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) + class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher): def __init__(self, encoding=None): @@ -235,8 +225,8 @@ class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher): def handle_jsonrpc(self, request_text): response = self._marshaled_dispatch(request_text) - print( 'Content-Type: application/json-rpc' ) - print( 'Content-Length: %d' % len(response) ) + print('Content-Type: application/json-rpc') + print('Content-Length: %d' % len(response)) print() sys.stdout.write(response) diff --git a/jsonrpclib/__init__.py b/jsonrpclib/__init__.py index 6e884b8..91dd5f9 100644 --- a/jsonrpclib/__init__.py +++ b/jsonrpclib/__init__.py @@ -1,6 +1,12 @@ from jsonrpclib.config import Config -config = Config.instance() from jsonrpclib.history import History -history = History.instance() from jsonrpclib.jsonrpc import Server, MultiCall, Fault from jsonrpclib.jsonrpc import ProtocolError, loads, dumps + +config = Config.instance() +history = History.instance() + +__all__ = [ + "config", "Config", "history", "History", "Server", "MultiCall", + "Fault", "ProtocolError", "loads", "dumps" +] diff --git a/jsonrpclib/jsonclass.py b/jsonrpclib/jsonclass.py index 31f829d..17e3653 100644 --- a/jsonrpclib/jsonclass.py +++ b/jsonrpclib/jsonclass.py @@ -5,12 +5,8 @@ from jsonrpclib import config iter_types = (dict, list, tuple) value_types = (bool, ) -try: - string_types = (basestring, ) # Python 2.7 - numeric_types = (int, long, float) -except NameError: - string_types = (str, ) # Python 3.x - numeric_types = (int, float) +string_types = (str, ) +numeric_types = (int, float) supported_types = iter_types+string_types+numeric_types+value_types invalid_module_chars = r'[^a-zA-Z0-9\_\.]' @@ -77,7 +73,8 @@ def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]): def load(obj): - if obj is None or isinstance(obj, string_types + numeric_types + value_types): + if obj is None or isinstance( + obj, string_types + numeric_types + value_types): return obj if isinstance(obj, list): diff --git a/jsonrpclib/jsonrpc.py b/jsonrpclib/jsonrpc.py index 4861e4a..9eeadcb 100644 --- a/jsonrpclib/jsonrpc.py +++ b/jsonrpclib/jsonrpc.py @@ -46,48 +46,28 @@ appropriately. See http://code.google.com/p/jsonrpclib/ for more info. """ -try: - # Python 3.x - from xmlrpc.client import Transport as XMLTransport - from xmlrpc.client import SafeTransport as XMLSafeTransport - from xmlrpc.client import ServerProxy as XMLServerProxy - from xmlrpc.client import _Method as XML_Method -except ImportError: - # Python 2.7 - from xmlrpclib import Transport as XMLTransport - from xmlrpclib import SafeTransport as XMLSafeTransport - from xmlrpclib import ServerProxy as XMLServerProxy - from xmlrpclib import _Method as XML_Method - +from xmlrpc.client import Transport as XMLTransport +from xmlrpc.client import SafeTransport as XMLSafeTransport +from xmlrpc.client import ServerProxy as XMLServerProxy +from xmlrpc.client import _Method as XML_Method +import json import string import random -try: - basestring # Python 2.7 -except NameError: - basestring = str # Python 3.x +from jsonrpclib import Config +from jsonrpclib import History +from http.client import HTTPConnection +from socket import socket -# Library includes -from jsonrpclib import config -from jsonrpclib import history +USE_UNIX_SOCKETS = False -# JSON library importing -cjson = None -json = None try: - import cjson + from socket import AF_UNIX, SOCK_STREAM + USE_UNIX_SOCKETS = True except ImportError: - try: - import json - except ImportError: - try: - import simplejson as json - except ImportError: - raise ImportError( - 'You must have the cjson, json, or simplejson ' + - 'module(s) available.' - ) + pass + IDCHARS = string.ascii_lowercase+string.digits @@ -105,20 +85,11 @@ class UnixSocketMissing(Exception): def jdumps(obj, encoding='utf-8'): # Do 'serialize' test at some point for other classes - global cjson - if cjson: - return cjson.encode(obj) - else: -# return json.dumps(obj, encoding=encoding) - return json.dumps(obj) + return json.dumps(obj) def jloads(json_string): - global cjson - if cjson: - return cjson.decode(json_string) - else: - return json.loads(json_string) + return json.loads(json_string) # XMLRPClib re-implementations @@ -130,16 +101,15 @@ class ProtocolError(Exception): class TransportMixIn(object): """ Just extends the XMLRPC transport where necessary. """ - user_agent = config.user_agent - # for Python 2.7 support - _connection = (None, None) - _extra_headers = [] + user_agent = Config.instance().user_agent def send_content(self, connection, request_body): connection.putheader("Content-Type", "application/json-rpc") connection.putheader("Content-Length", str(len(request_body))) connection.endheaders() if request_body: + if type(request_body) == str: + request_body = request_body.encode("utf8") connection.send(request_body) def getparser(self): @@ -180,19 +150,6 @@ class SafeTransport(TransportMixIn, XMLSafeTransport): TransportMixIn.__init__(self) XMLSafeTransport.__init__(self) -try: - from httplib import HTTPConnection -except ImportError: - from http.client import HTTPConnection -from socket import socket - -USE_UNIX_SOCKETS = False - -try: - from socket import AF_UNIX, SOCK_STREAM - USE_UNIX_SOCKETS = True -except ImportError: - pass if (USE_UNIX_SOCKETS): @@ -219,9 +176,9 @@ class ServerProxy(XMLServerProxy): try: from urllib.parse import splittype, splithost # python 3.x except ImportError: - from urllib import splittype, splithost + from urllib.parse import splittype, splithost if not version: - version = config.version + version = Config.instance().version self.__version = version schema, uri = splittype(uri) if schema not in ('http', 'https', 'unix'): @@ -264,7 +221,7 @@ class ServerProxy(XMLServerProxy): return def _run_request(self, request, notify=None): - history.add_request(request) + History.instance().add_request(request) response = self.__transport.request( self.__host, @@ -279,7 +236,7 @@ class ServerProxy(XMLServerProxy): # the response object, or expect the Server to be # outputting the response appropriately? - history.add_response(response) + History.instance().add_response(response) if not response: return None return_obj = loads(response) @@ -379,7 +336,6 @@ class MultiCallIterator(object): def __iter__(self): for i in range(0, len(self.results)): yield self[i] - raise StopIteration def __getitem__(self, i): item = self.results[i] @@ -419,6 +375,7 @@ class MultiCall(object): __call__ = _request + # These lines conform to xmlrpclib's "compatibility" line. # Not really sure if we should include these, but oh well. Server = ServerProxy @@ -437,7 +394,7 @@ class Fault(object): def response(self, rpcid=None, version=None): if not version: - version = config.version + version = Config.instance().version if rpcid: self.rpcid = rpcid return dumps( @@ -458,12 +415,12 @@ def random_id(length=8): class Payload(dict): def __init__(self, rpcid=None, version=None): if not version: - version = config.version + version = Config.instance().version self.id = rpcid self.version = float(version) def request(self, method, params=[]): - if not isinstance(method, basestring): + if not isinstance(method, str): raise ValueError('Method name must be a string.') if not self.id: self.id = random_id() @@ -508,7 +465,7 @@ def dumps( the rpcid argument since the 2.0 spec requires it for responses. """ if not version: - version = config.version + version = Config.instance().version if isinstance(methodname, str) and \ not isinstance(params, (tuple, list, dict)) and \ not isinstance(params, Fault): @@ -532,7 +489,7 @@ def dumps( 'Method name must be a string, or methodresponse must ' 'be set to True.') - if config.use_jsonclass is True: + if Config.instance().use_jsonclass is True: from jsonrpclib import jsonclass params = jsonclass.dump(params) if methodresponse is True: @@ -561,7 +518,7 @@ def loads(data): # if the above raises an error, the implementing server code # should return something like the following: # { 'jsonrpc':'2.0', 'error': fault.error(), id: None } - if config.use_jsonclass is True: + if Config.instance().use_jsonclass is True: from jsonrpclib import jsonclass result = jsonclass.load(result) return result @@ -576,7 +533,8 @@ def check_for_errors(result): raise TypeError('Response is not a dict.') if 'jsonrpc' in result.keys() and float(result['jsonrpc']) > 2.0: raise NotImplementedError('JSON-RPC version not yet supported.') - if 'result' not in result.keys() and 'error' not in result.keys(): + if 'result' not in result.keys() and \ + 'error' not in result.keys(): raise ValueError('Response does not have a result or error key.') if 'error' in result.keys() and result['error'] is not None: code = result['error']['code'] @@ -56,6 +56,10 @@ class TestCompatibility(unittest.TestCase): self.server = server_set_up(addr=('', self.port)) self.client = Server('http://localhost:%d' % self.port) + def tearDown(self): + self.server.stop() + self.server.join() + # v1 tests forthcoming # Version 2.0 Tests @@ -287,6 +291,8 @@ class InternalTests(unittest.TestCase): self.addCleanup(self.cleanup) def cleanup(self): + self.server.stop() + self.server.join() history.size = ORIGINAL_HISTORY_SIZE history.clear() @@ -429,37 +435,40 @@ class InternalTests(unittest.TestCase): result = sub_service_proxy.add(21, 21) self.assertTrue(result == 42) -if jsonrpc.USE_UNIX_SOCKETS: - # We won't do these tests unless Unix Sockets are supported - - @unittest.skip("Skipping Unix socket tests right now.") - class UnixSocketInternalTests(InternalTests): - """ - These tests run the same internal communication tests, - but over a Unix socket instead of a TCP socket. - """ - def setUp(self): - suffix = "%d.sock" % get_port() - - # Open to safer, alternative processes - # for getting a temp file name... - temp = tempfile.NamedTemporaryFile( - suffix=suffix - ) - self.port = temp.name - temp.close() - self.server = server_set_up( - addr=self.port, - address_family=socket.AF_UNIX - ) +@unittest.skipIf( + not jsonrpc.USE_UNIX_SOCKETS or "SKIP_UNIX_SOCKET_TESTS" in os.environ, + "Skipping Unix socket tests -- unsupported in this environment.") +class UnixSocketInternalTests(InternalTests): + """ + These tests run the same internal communication tests, + but over a Unix socket instead of a TCP socket. + """ + def setUp(self): + suffix = "%d.sock" % get_port() - def get_client(self): - return Server('unix:/%s' % self.port) + # Open to safer, alternative processes + # for getting a temp file name... + temp = tempfile.NamedTemporaryFile( + suffix=suffix + ) + self.port = temp.name + temp.close() - def tearDown(self): - """ Removes the tempory socket file """ - os.unlink(self.port) + self.server = server_set_up( + addr=self.port, + address_family=socket.AF_UNIX + ) + + def get_client(self): + print(f"Serving on {self.port}") + return Server('unix:/%s' % self.port) + + def tearDown(self): + """ Removes the tempory socket file """ + self.server.stop() + self.server.join() + os.unlink(self.port) class UnixSocketErrorTests(unittest.TestCase): @@ -537,7 +546,11 @@ def server_set_up(addr, address_family=socket.AF_INET): server.register_function(service.summation, 'sum') server.register_function(service.summation, 'notify_sum') server.register_function(service.summation, 'namespace.sum') + + def stop(): + server.shutdown() + server_proc = Thread(target=server.serve_forever) - server_proc.daemon = True + server_proc.stop = stop server_proc.start() return server_proc |