From ba0ce89f1f5da0f115277301858f478145359ace Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 16 Nov 2022 19:39:18 -0500 Subject: [PATCH 01/13] Fix potential usage of None and Unbound variables issues - Misnamed `src_cmap` - Uninitialized attributes and missing methods due to usage of similar classes that don't inherit the same bases - Better defaults than `None` for variables and attributes when possible and `None` has no special meaning. - Potentially unbound variables - Additional Non checks or type coersion --- Xlib/ext/xinput.py | 7 ++++--- Xlib/protocol/display.py | 33 ++++++++++++++++++--------------- Xlib/protocol/rq.py | 28 +++++++++++++++++++++++----- Xlib/xobject/colormap.py | 2 +- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Xlib/ext/xinput.py b/Xlib/ext/xinput.py index f9218064..235124b0 100644 --- a/Xlib/ext/xinput.py +++ b/Xlib/ext/xinput.py @@ -226,8 +226,9 @@ def pack_value(self, val): # bytes we build a longer array, being careful to maintain native # byte order across the entire set of values. if sys.byteorder == 'little': - def fun(val): - mask_seq.insert(0, val) + def fun(__v): + # type: (int) -> None + mask_seq.insert(0, __v) elif sys.byteorder == 'big': fun = mask_seq.append else: @@ -378,7 +379,7 @@ def parse_binary_value(self, data, display, length, fmt): } class ClassInfoClass(object): - + check_value = None structcode = None def parse_binary(self, data, display): diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index 56623c35..7b116edb 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -369,11 +369,11 @@ def send_request(self, request, wait_for_response): def close_internal(self, whom): # Clear out data structures - self.request_queue = None - self.sent_requests = None - self.event_queue = None - self.data_send = None - self.data_recv = None + self.request_queue = [] + self.sent_requests = [] + self.event_queue = [] + self.data_send = b'' + self.data_recv = b'' # Close the connection self.socket.close() @@ -583,7 +583,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) i = self.socket.send(self.data_send) except socket.error as err: self.close_internal('server: %s' % err) - raise self.socket_error + raise self.socket_error or Exception() self.data_send = self.data_send[i:] self.data_sent_bytes = self.data_sent_bytes + i @@ -601,12 +601,12 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) bytes_recv = self.socket.recv(count) except socket.error as err: self.close_internal('server: %s' % err) - raise self.socket_error + raise self.socket_error or Exception() if not bytes_recv: # Clear up, set a connection closed indicator and raise it self.close_internal('server') - raise self.socket_error + raise self.socket_error or Exception() self.data_recv = bytes(self.data_recv) + bytes_recv gotreq = self.parse_response(request) @@ -690,6 +690,7 @@ def parse_response(self, request): # Parse ordinary server response gotreq = 0 + rtype = None while 1: if self.data_recv: # Check the first byte to find out what kind of response it is @@ -703,7 +704,7 @@ def parse_response(self, request): if rtype == 1: gotreq = self.parse_request_response(request) or gotreq continue - elif rtype & 0x7f == ge.GenericEventCode: + elif rtype and (rtype & 0x7f == ge.GenericEventCode): self.parse_event_response(rtype) continue else: @@ -711,7 +712,7 @@ def parse_response(self, request): # Every response is at least 32 bytes long, so don't bother # until we have received that much - if len(self.data_recv) < 32: + if len(self.data_recv) < 32 or rtype is None: return gotreq # Error response @@ -719,7 +720,7 @@ def parse_response(self, request): gotreq = self.parse_error_response(request) or gotreq # Request response or generic event. - elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode: + elif rtype and rtype == 1 or rtype and (rtype & 0x7f == ge.GenericEventCode): # Set reply length, and loop around to see if # we have got the full response rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) @@ -812,9 +813,10 @@ def parse_request_response(self, request): def parse_event_response(self, etype): # Skip bit 8, that is set if this event came from an SendEvent - etype = etype & 0x7f + etype = etype and (etype & 0x7f) + is_generic_event_code = etype == ge.GenericEventCode - if etype == ge.GenericEventCode: + if is_generic_event_code: length = self.recv_packet_len else: length = 32 @@ -832,7 +834,7 @@ def parse_event_response(self, etype): e = estruct(display = self, binarydata = self.data_recv[:length]) - if etype == ge.GenericEventCode: + if is_generic_event_code: self.recv_packet_len = 0 self.data_recv = bytesview(self.data_recv, length) @@ -1022,6 +1024,7 @@ def parse_connection_setup(self): class ConnectionSetupRequest(rq.GetAttrData): + _serial = None # type: int | None _request = rq.Struct( rq.Set('byte_order', 1, (0x42, 0x6c)), rq.Pad(1), rq.Card16('protocol_major'), @@ -1061,7 +1064,7 @@ class ConnectionSetupRequest(rq.GetAttrData): def __init__(self, display, *args, **keys): self._binary = self._request.to_binary(*args, **keys) - self._data = None + self._data = {} # Don't bother about locking, since no other threads have # access to the display yet diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 86cb2def..05fe509d 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -110,8 +110,9 @@ class Field(object): f.parse_binary_value() instead. See its documentation string for details. """ - name = None + name = "" default = None + pack_value = None structcode = None structvalues = 0 @@ -185,6 +186,10 @@ class LengthField(Field): structvalues = 1 other_fields = None + def parse_binary_value(self, data = None, display = None, length = None, format = None): + # type: (object, object, object, object) -> tuple[Any, _SliceableBuffer] + return b'', b'' + def calc_length(self, length): """newlen = lf.calc_length(length) @@ -654,10 +659,9 @@ def parse_binary_value(self, data, display, length, format): else: length = int(length) - if format == 0: - ret = None + ret = None - elif format == 8: + if format == 8: ret = (8, data[:length]) data = data[length + ((4 - length % 4) % 4):] @@ -938,6 +942,9 @@ class Field. The fields of a structure are given as arguments object to make conversion as fast as possible. They are generated the first time the methods are called. """ + name = "" + check_value = None # type: Callable[[Any], Any] | None + keyword_args = False def __init__(self, *fields): self.fields = fields @@ -1015,6 +1022,9 @@ def to_binary(self, *varargs, **keys): formats = {} for f in self.var_fields: + if not f.pack_value: + continue + if f.keyword_args: v, l, fm = f.pack_value(field_args[f.name], keys) else: @@ -1273,7 +1283,7 @@ def parse_binary_value(self, data, display, length, format): # string with delta else: - v, data = self.string_textitem.parse_binary(data, display) + v, _ = self.string_textitem.parse_binary(data, display) values.append(v) return values, '' @@ -1288,6 +1298,9 @@ class TextElements16(TextElements8): class GetAttrData(object): + _data = {} # type: dict[str, object] + # GetAttrData classes get their attributes dynamically + # TODO: Complete all classes inheriting from GetAttrData def __getattr__(self, attr): try: if self._data: @@ -1342,6 +1355,7 @@ def __eq__(self, other): class Request(object): + _request = None # type: Struct def __init__(self, display, onerror = None, *args, **keys): self._errorhandler = onerror self._binary = self._request.to_binary(*args, **keys) @@ -1355,6 +1369,7 @@ def _set_error(self, error): return 0 class ReplyRequest(GetAttrData): + _request = None # type: Struct def __init__(self, display, defer = 0, *args, **keys): self._display = display self._binary = self._request.to_binary(*args, **keys) @@ -1372,6 +1387,8 @@ def reply(self): # Send request and wait for reply if we hasn't # already got one. This means that reply() can safely # be called more than one time. + if self._display is None: + raise TypeError self._response_lock.acquire() while self._data is None and self._error is None: @@ -1404,6 +1421,7 @@ def __repr__(self): class Event(GetAttrData): + _fields = None # type: Struct def __init__(self, binarydata = None, display = None, **keys): if binarydata: diff --git a/Xlib/xobject/colormap.py b/Xlib/xobject/colormap.py index 033fb493..a1508d9e 100644 --- a/Xlib/xobject/colormap.py +++ b/Xlib/xobject/colormap.py @@ -44,7 +44,7 @@ def free(self, onerror = None): self.display.free_resource_id(self.id) - def copy_colormap_and_free(self, scr_cmap): + def copy_colormap_and_free(self, src_cmap): mid = self.display.allocate_resource_id() request.CopyColormapAndFree(display = self.display, mid = mid, From d3af1f8e7a935b1f3ced706fac586134d14acbf6 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 16 Nov 2022 19:43:59 -0500 Subject: [PATCH 02/13] Update Xlib/protocol/rq.py --- Xlib/protocol/rq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 05fe509d..15174679 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -1283,7 +1283,7 @@ def parse_binary_value(self, data, display, length, format): # string with delta else: - v, _ = self.string_textitem.parse_binary(data, display) + v, data = self.string_textitem.parse_binary(data, display) values.append(v) return values, '' From 4db982b36400452f197611fde7fb1b71c91a53a5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 16 Nov 2022 19:47:29 -0500 Subject: [PATCH 03/13] Default to 0 length and 0 format --- Xlib/protocol/rq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 15174679..972f59f6 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -1211,8 +1211,8 @@ def parse_binary(self, data, display, rawdict = 0): for f in self.var_fields: ret[f.name], data = f.parse_binary_value(data, display, - lengths.get(f.name), - formats.get(f.name), + lengths.get(f.name, 0), + formats.get(f.name, 0), ) if not rawdict: From 18a3f6e9ef372e16711893da3676d0ccf46642b3 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 15:46:07 -0500 Subject: [PATCH 04/13] Data not nullable --- Xlib/protocol/display.py | 2 +- Xlib/protocol/rq.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index 7b116edb..e812fedb 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -1064,7 +1064,7 @@ class ConnectionSetupRequest(rq.GetAttrData): def __init__(self, display, *args, **keys): self._binary = self._request.to_binary(*args, **keys) - self._data = {} + self._data = {} # type: dict[str, object] # Don't bother about locking, since no other threads have # access to the display yet diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 05fe509d..07c12949 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -1374,7 +1374,7 @@ def __init__(self, display, defer = 0, *args, **keys): self._display = display self._binary = self._request.to_binary(*args, **keys) self._serial = None - self._data = None + self._data = {} # type: dict[str, object] self._error = None self._response_lock = lock.allocate_lock() @@ -1391,7 +1391,7 @@ def reply(self): raise TypeError self._response_lock.acquire() - while self._data is None and self._error is None: + while not self._data and self._error is None: self._display.send_recv_lock.acquire() self._response_lock.release() From fdcc2104d652232855f7a3691f810e687e8a4e0b Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 16:07:46 -0500 Subject: [PATCH 05/13] nonable display in parse_value --- Xlib/protocol/rq.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 66a3befd..36527541 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -307,8 +307,8 @@ def check_value(self, value): return value def parse_value(self, value, display): - # if not display: - # return value + if not display: + return value if value in self.codes: return value @@ -897,8 +897,8 @@ def __init__(self, class_name): self.check_value = None def parse_value(self, value, display): - # if not display: - # return value + if not display: + return value c = display.get_resource_class(self.class_name) if c: return c(display, value) From abec40bc9218cb3955d47e952d4e4be5a044b273 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 17:01:51 -0500 Subject: [PATCH 06/13] XK --- Xlib/XK.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Xlib/XK.py b/Xlib/XK.py index 7603ccd0..4749b542 100644 --- a/Xlib/XK.py +++ b/Xlib/XK.py @@ -24,6 +24,10 @@ # definition modules in the Xlib/keysymdef directory. from Xlib.X import NoSymbol +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False def string_to_keysym(keysym): '''Return the (16 bit) numeric code of keysym. @@ -64,7 +68,11 @@ def _load_keysyms_into_XK(mod): # Always import miscellany and latin1 keysyms load_keysym_group('miscellany') +if TYPE_CHECKING: + from Xlib.keysymdef.miscellany import * load_keysym_group('latin1') +if TYPE_CHECKING: + from Xlib.keysymdef.latin1 import * def keysym_to_string(keysym): From e1ecc736eb22b3d11ebdee071f0ef00a045b6da6 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 18:17:27 -0500 Subject: [PATCH 07/13] Update Xlib/protocol/rq.py --- Xlib/protocol/rq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 36527541..d639d39e 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -112,7 +112,7 @@ class Field(object): """ name = "" default = None - pack_value = None + pack_value = None # type: Callable[[Any], tuple[Any, int | None, int | None]] | None structcode = None structvalues = 0 From b4f44528d9a2352bf60a333d03e71d6bfb26e8a0 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 20:57:05 -0500 Subject: [PATCH 08/13] redundant _data def --- Xlib/protocol/rq.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 36527541..dda517c4 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -1374,7 +1374,6 @@ def __init__(self, display, defer = 0, *args, **keys): self._display = display self._binary = self._request.to_binary(*args, **keys) self._serial = None - self._data = {} # type: dict[str, object] self._error = None self._response_lock = lock.allocate_lock() From 2161afd7a0d751b3f91d88272d49a71e8e5e4882 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 21:04:17 -0500 Subject: [PATCH 09/13] resource_classes init --- Xlib/protocol/display.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index e812fedb..c061cc52 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -79,6 +79,7 @@ class Display(object): extension_major_opcodes = {} error_classes = error.xerror_class.copy() event_classes = event.event_class.copy() + resource_classes = {} # type: _BaseClasses def __init__(self, display = None): name, protocol, host, displayno, screenno = connect.get_display(display) From 2a2a76ee5975172ceb3998a760d1a16f6b6a2e7c Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 21:42:16 -0500 Subject: [PATCH 10/13] test --- Xlib/protocol/rq.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 73796704..e3700544 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -186,9 +186,9 @@ class LengthField(Field): structvalues = 1 other_fields = None - def parse_binary_value(self, data = None, display = None, length = None, format = None): - # type: (object, object, object, object) -> tuple[Any, _SliceableBuffer] - return b'', b'' + # def parse_binary_value(self, data = None, display = None, length = None, format = None): + # # type: (object, object, object, object) -> tuple[Any, _SliceableBuffer] + # return b'', b'' def calc_length(self, length): """newlen = lf.calc_length(length) @@ -1303,11 +1303,8 @@ class GetAttrData(object): # TODO: Complete all classes inheriting from GetAttrData def __getattr__(self, attr): try: - if self._data: - return self._data[attr] - else: - raise AttributeError(attr) - except KeyError: + return self._data[attr] + except (KeyError, AttributeError): raise AttributeError(attr) class DictWrapper(GetAttrData): From 4081dd0b6ed65fe605692685d36bc63888fa0b90 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 21:51:28 -0500 Subject: [PATCH 11/13] debug --- Xlib/display.py | 12 +++++++++--- Xlib/protocol/display.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Xlib/display.py b/Xlib/display.py index e0f7b5c8..f36ed6a0 100644 --- a/Xlib/display.py +++ b/Xlib/display.py @@ -379,9 +379,15 @@ def keysym_to_keycode(self, keysym): lowest code is returned. If keysym is not bound to any key, 0 is returned.""" try: - return self._keymap_syms[keysym][0][1] - except (KeyError, IndexError): - return 0 + return (1, self._keymap_syms[keysym][0][1]) + except: + try: + return (2, self._keymap_syms[keysym][0]) + except: + try: + return (3, self._keymap_syms[keysym]) + except: + return (4, self._keymap_syms) def keysym_to_keycodes(self, keysym): """Look up all the keycodes that is bound to keysym. A list of diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index c061cc52..df5ffb07 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -721,7 +721,7 @@ def parse_response(self, request): gotreq = self.parse_error_response(request) or gotreq # Request response or generic event. - elif rtype and rtype == 1 or rtype and (rtype & 0x7f == ge.GenericEventCode): + elif rtype == 1 or (rtype & 0x7f == ge.GenericEventCode): # Set reply length, and loop around to see if # we have got the full response rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) From 81da35c26e711e28e9cbc064dfae90aad62c46cb Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 22:33:56 -0500 Subject: [PATCH 12/13] Nonable length --- Xlib/ext/xinput.py | 2 ++ Xlib/protocol/display.py | 6 +++--- Xlib/protocol/rq.py | 14 ++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Xlib/ext/xinput.py b/Xlib/ext/xinput.py index 235124b0..1ad98d41 100644 --- a/Xlib/ext/xinput.py +++ b/Xlib/ext/xinput.py @@ -309,6 +309,8 @@ def __init__(self, name): rq.ValueField.__init__(self, name) def parse_binary_value(self, data, display, length, fmt): + if length is None: + length = len(data) # Mask: bitfield of button states. mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) mask_data = data[:mask_len] diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index df5ffb07..ef47c6b0 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -361,12 +361,12 @@ def send_request(self, request, wait_for_response): self.request_serial = (self.request_serial + 1) % 65536 self.request_queue.append((request, wait_for_response)) - qlen = len(self.request_queue) + # qlen = len(self.request_queue) self.request_queue_lock.release() -# if qlen > 10: -# self.flush() + # if qlen > 10: + # self.flush() def close_internal(self, whom): # Clear out data structures diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index e3700544..fb668292 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -186,9 +186,9 @@ class LengthField(Field): structvalues = 1 other_fields = None - # def parse_binary_value(self, data = None, display = None, length = None, format = None): - # # type: (object, object, object, object) -> tuple[Any, _SliceableBuffer] - # return b'', b'' + def parse_binary_value(self, data = None, display = None, length = None, format = None): + # type: (object, object, object, object) -> tuple[Any, _SliceableBuffer] + return b'', b'' def calc_length(self, length): """newlen = lf.calc_length(length) @@ -474,7 +474,9 @@ def pack_value(self, val): return struct.pack('>' + 'H' * slen, *val) + pad, slen, None def parse_binary_value(self, data, display, length, format): - if length == 'odd': + if length is None: + length = len(data) + elif length == 'odd': length = len(data) // 2 - 1 elif length == 'even': length = len(data) // 2 @@ -1211,8 +1213,8 @@ def parse_binary(self, data, display, rawdict = 0): for f in self.var_fields: ret[f.name], data = f.parse_binary_value(data, display, - lengths.get(f.name, 0), - formats.get(f.name, 0), + lengths.get(f.name), + formats.get(f.name), ) if not rawdict: From d3bd0985684dad1b8c3c9d034890ee09543aa108 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Nov 2022 22:47:56 -0500 Subject: [PATCH 13/13] undo debug --- Xlib/display.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Xlib/display.py b/Xlib/display.py index f36ed6a0..e0f7b5c8 100644 --- a/Xlib/display.py +++ b/Xlib/display.py @@ -379,15 +379,9 @@ def keysym_to_keycode(self, keysym): lowest code is returned. If keysym is not bound to any key, 0 is returned.""" try: - return (1, self._keymap_syms[keysym][0][1]) - except: - try: - return (2, self._keymap_syms[keysym][0]) - except: - try: - return (3, self._keymap_syms[keysym]) - except: - return (4, self._keymap_syms) + return self._keymap_syms[keysym][0][1] + except (KeyError, IndexError): + return 0 def keysym_to_keycodes(self, keysym): """Look up all the keycodes that is bound to keysym. A list of