Skip to content

Commit

Permalink
#3229 convert keys rather than risking having duplicate bytes vs strings
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Aug 6, 2021
1 parent 123efb0 commit 6283443
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 76 deletions.
2 changes: 0 additions & 2 deletions tests/unittests/unit/util_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ def test_typedict(self):
"intpair" : (1, 2),
"strtuple" : ["a", "b"],
})
v = d.rawget("bytekey")
self.assertIsNotNone(v)
#test all accessors:
self.assertEqual(d.strget("strkey"), "strvalue")
self.assertEqual(d.intget(1), 1)
Expand Down
3 changes: 1 addition & 2 deletions xpra/client/client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,14 @@ def wn(w):
workspacelog("init_window(..) workspace from client properties %s: %s", self._client_properties, wn(workspace))
if workspace is not None:
#client properties override application specified workspace value on init only:
metadata.pop(b"workspace", None)
metadata["workspace"] = workspace
self._window_workspace = WORKSPACE_UNSET #will get set in set_metadata if present
self._desktop_workspace = self.get_desktop_workspace() #pylint: disable=assignment-from-none
workspacelog("init_window(..) workspace=%s, current workspace=%s",
wn(self._window_workspace), wn(self._desktop_workspace))
if self.max_window_size and "size-constraints" not in metadata:
#this ensures that we will set size-constraints and honour max_window_size:
metadata.pop(b"workspace", None)
metadata.pop("workspace", None)
metadata["size-constraints"] = {}
#initialize gravity early:
sc = typedict(metadata.dictget("size-constraints", {}))
Expand Down
8 changes: 4 additions & 4 deletions xpra/client/mixins/serverinfo_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ def get_remote_lib_versions(c : typedict,
):
versions = {}
for x in libs:
v = c.rawget("%s.version" % x, None)
v = c.get("%s.version" % x, None)
if v is None:
#fallback to structured access:
d = c.rawget(x, None)
d = c.get(x, None)
if isinstance(d, dict):
v = typedict(d).rawget("version", None)
v = typedict(d).get("version", None)
if v:
versions[x] = bytestostr(v)
return versions
Expand Down Expand Up @@ -57,7 +57,7 @@ def parse_server_capabilities(self, c : typedict) -> bool:
self._remote_uuid = c.strget("uuid")
self._remote_version = c.strget("build.version", c.strget("version"))
self._remote_revision = c.strget("build.revision", c.strget("revision"))
mods = c.rawget("build.local_modifications")
mods = c.get("build.local_modifications")
if mods and str(mods).find("dfsg")>=0: # pragma: no cover
get_util_logger().warn("Warning: the xpra server is running a buggy Debian version")
get_util_logger().warn(" those are usually out of date and unstable")
Expand Down
4 changes: 2 additions & 2 deletions xpra/client/top_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ def strget(key, sep="."):
#depending on where we get the gl info from,
#the value might be a list of strings,
#or a byte string...
v = gli.rawget(key)
v = gli.get(key)
if isinstance(v, (tuple, list)):
return sep.join(bytestostr(x) for x in v)
return bytestostr(v)
Expand All @@ -800,7 +800,7 @@ def strget(key, sep="."):
depth = gli.intget("depth")
if depth not in (0, 24):
gl_info += ", %ibits" % depth
modes = gli.rawget("display_mode")
modes = gli.get("display_mode")
if modes:
gl_info += " - %s" % strget("display_mode", ", ")
return gl_info
Expand Down
11 changes: 9 additions & 2 deletions xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,14 @@ def parse_server_capabilities(self, c : typedict) -> bool:
if not cb.parse_server_capabilities(self, c):
log.info("failed to parse server capabilities in %s", cb)
return False
self.server_session_name = strtobytes(c.rawget("session_name", b"")).decode("utf-8")
self.server_session_name = c.get("session_name")
#legacy packet encoders give us a string,
#but that's actually a utf8 bytearray...
if self._protocol.encoder!="rencodeplus":
try:
self.server_session_name = strtobytes(self.server_session_name).decode("utf8")
except UnicodeDecodeError:
pass
set_name("Xpra", self.session_name or self.server_session_name or "Xpra")
self.server_platform = c.strget("platform")
self.server_sharing = c.boolget("sharing")
Expand Down Expand Up @@ -621,7 +628,7 @@ def _process_control(self, packet):
self._protocol.enable_encoder(pe)
elif command=="name":
assert len(args)>=3
self.server_session_name = args[2]
self.server_session_name = bytestostr(args[2])
log.info("session name updated from server: %s", self.server_session_name)
#TODO: reset tray tooltip, session info title, etc..
elif command=="debug":
Expand Down
2 changes: 0 additions & 2 deletions xpra/client/window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ def free_buffer(*_args):
data = img.get_pixels()
stride = img.get_rowstride()
#replace with the actual rgb format we get from the decoder:
options.pop(b"rgb_format", None)
options["rgb_format"] = rgb_format
self.idle_add(self.do_paint_rgb, rgb_format, data,
x, y, iwidth, iheight, width, height, stride, options, callbacks)
Expand Down Expand Up @@ -432,7 +431,6 @@ def do_paint_rgb(self, rgb_format, img_data,
paint_fn = self._do_paint_rgb32
else:
raise Exception("invalid rgb format '%s'" % rgb_format)
options.pop(b"rgb_format", None)
options["rgb_format"] = rgb_format
success = paint_fn(img_data, x, y, width, height, render_width, render_height, rowstride, options)
fire_paint_callbacks(callbacks, success)
Expand Down
1 change: 0 additions & 1 deletion xpra/platform/win32/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,6 @@ def apply_maxsize_hints(window, hints):
#which would remove the maximize button:
for x in ("min_width", "min_height", "max_width", "max_height"):
hints.pop(x, None)
hints.pop(strtobytes(x), None)
window_state_updated(window)

def apply_geometry_hints(self, hints):
Expand Down
2 changes: 1 addition & 1 deletion xpra/server/server_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2129,7 +2129,7 @@ def make_hello(self, source=None):
if mid:
capabilities["machine_id"] = mid
if self.session_name:
capabilities["session_name"] = self.session_name.encode("utf-8")
capabilities["session_name"] = self.session_name
return capabilities


Expand Down
2 changes: 1 addition & 1 deletion xpra/server/source/mmap_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def is_needed(cls, caps : typedict) -> bool:
#pre 2.3 clients;
if caps.strget("mmap_file"):
return True
v = caps.rawget("mmap")
v = caps.get("mmap")
#we should be receiving a dict with mmap attributes
#(but pre v4 clients also send a boolean telling us if mmap is supported by the platform..)
return isinstance(v, dict)
Expand Down
2 changes: 1 addition & 1 deletion xpra/server/window/window_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ def do_set_client_properties(self, properties):
rgb_formats = [x for x in rgb_formats if x.find("A")<0]
self.rgb_formats = rgb_formats
self.send_window_size = properties.boolget("encoding.send-window-size", self.send_window_size)
self.parse_csc_modes(properties.dictget("encoding.full_csc_modes", default_value=None))
self.parse_csc_modes(properties.dictget("encoding.full_csc_modes", default=None))
#select the defaults encoders:
#(in case pillow was selected previously and the client side scaling changed)
for encoding, encoders in self._all_encoders.items():
Expand Down
124 changes: 67 additions & 57 deletions xpra/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import re
import sys
import binascii
import traceback
import threading
import sys
import os
import re
from itertools import chain


XPRA_APP_ID = 0
Expand Down Expand Up @@ -293,69 +294,79 @@ def __cmp__(self, other):
return self.counter-int(other)


class typedict(dict):
def strtobytes(x) -> bytes:
if isinstance(x, bytes):
return x
return str(x).encode("latin1")
def bytestostr(x) -> str:
if isinstance(x, (bytes, bytearray)):
return x.decode("latin1")
return str(x)

_RaiseKeyError = object()

class typedict(dict):
__slots__ = () # no __dict__ - that would be redundant
@staticmethod # because this doesn't make sense as a global function.
def _process_args(mapping=(), **kwargs):
if hasattr(mapping, "items"):
mapping = getattr(mapping, "items")()
return ((bytestostr(k), v) for k, v in chain(mapping, getattr(kwargs, "items")()))
def __init__(self, mapping=(), **kwargs):
super().__init__(self._process_args(mapping, **kwargs))
def __getitem__(self, k):
return super().__getitem__(bytestostr(k))
def __setitem__(self, k, v):
return super().__setitem__(bytestostr(k), v)
def __delitem__(self, k):
return super().__delitem__(bytestostr(k))
def get(self, k, default=None):
return super().get(bytestostr(k), default)
def setdefault(self, k, default=None):
return super().setdefault(bytestostr(k), default)
def pop(self, k, v=_RaiseKeyError):
if v is _RaiseKeyError:
return super().pop(bytestostr(k))
return super().pop(bytestostr(k), v)
def update(self, mapping=(), **kwargs):
super().update(self._process_args(mapping, **kwargs))
def __contains__(self, k):
return super().__contains__(bytestostr(k))
@classmethod
def fromkeys(cls, keys, v=None):
return super().fromkeys((bytestostr(k) for k in keys), v)
def __repr__(self):
return '{0}({1})'.format(type(self).__name__, super().__repr__())
def _warn(self, msg, *args, **kwargs):
get_util_logger().warn(msg, *args, **kwargs)

def __contains__(self, key):
if super().__contains__(key):
return True
if isinstance(key, str):
from xpra.os_util import strtobytes
return super().__contains__(strtobytes(key))
return False

def rawget(self, key, default=None):
if key in self:
return self[key]
#py3k and bytes as keys...
if isinstance(key, str):
from xpra.os_util import strtobytes
return self.get(strtobytes(key), default)
return default
def conv_get(self, k, default=None, conv=None):
if not super().__contains__(bytestostr(k)):
return default
v = self.get(k, default)
try:
return conv(v)
except (TypeError, ValueError, AssertionError) as e:
self._warn("Warning: failed to convert %s using %s: %s", type(v), conv, e)
return default

def strget(self, k, default=None):
v = self.rawget(k, default)
if v is None:
return default
from xpra.os_util import bytestostr
return bytestostr(v)
return self.conv_get(k, default, bytestostr)

def bytesget(self, k : str, default=None):
v = self.rawget(k, default)
if v is None:
return default
from xpra.os_util import strtobytes
return strtobytes(v)
return self.conv_get(k, default, strtobytes)

def intget(self, k : str, d=0):
v = self.rawget(k)
if v is None:
return d
try:
return int(v)
except Exception as e:
self._warn("intget(%s, %s)", k, d, exc_info=True)
self._warn("Warning: failed to parse %s value '%s':", k, v)
self._warn(" %s", e)
return d
def intget(self, k : str, default=0):
return self.conv_get(k, default, int)

def boolget(self, k : str, default_value=False):
v = self.rawget(k)
if v is None:
return default_value
return bool(v)
def boolget(self, k : str, default=False):
return self.conv_get(k, default, bool)

def dictget(self, k : str, default_value=None):
v = self.rawget(k, default_value)
if v is None:
return default_value
if not isinstance(v, dict):
self._warn("dictget(%s, %s)", k, default_value)
self._warn("Warning: expected a dict value for %s but got %s", k, type(v))
return default_value
return v
def dictget(self, k : str, default=None):
def checkdict(v):
assert isinstance(v, dict)
return v
return self.conv_get(k, default, checkdict)

def intpair(self, k : str, default_value=None):
v = self.inttupleget(k, default_value)
Expand All @@ -382,7 +393,7 @@ def tupleget(self, k : str, default_value=(), item_type=None, min_items=None, ma
return v

def _listget(self, k : str, default_value, item_type=None, min_items=None, max_items=None):
v = self.rawget(k)
v = self.get(k)
if v is None:
return default_value
if not isinstance(v, (list, tuple)):
Expand All @@ -401,7 +412,6 @@ def _listget(self, k : str, default_value, item_type=None, min_items=None, max_i
if item_type:
for i, x in enumerate(aslist):
if isinstance(x, bytes) and item_type==str:
from xpra.os_util import bytestostr
x = bytestostr(x)
aslist[i] = x
elif isinstance(x, str) and item_type==str:
Expand Down
1 change: 0 additions & 1 deletion xpra/x11/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,6 @@ def wn(w):
if workspace is not None:
window.move_to_workspace(workspace)
#we have handled it on the window directly, so remove it from client properties
new_client_properties.pop(b"workspace", None)
new_client_properties.pop("workspace", None)
#handle the rest as normal:
super()._set_client_properties(proto, wid, window, new_client_properties)
Expand Down

0 comments on commit 6283443

Please sign in to comment.