Example #1
1
class SortedDotDict(object):
    def __init__(self, *args, **kwargs):
        super(SortedDotDict, self).__init__(*args, **kwargs)
        self._dict = SortedDict()

    def __contains__(self, *args, **kwargs):
        return self._dict.__contains__(*args, **kwargs)

    def __eq__(self, *args, **kwargs):
        return self._dict.__eq__(*args, **kwargs)

    def __format__(self, *args, **kwargs):
        return self._dict.__format__(*args, **kwargs)

    def __ge__(self, *args, **kwargs):
        return self._dict.__ge__(*args, **kwargs)

    def __getattr__(self, key):
        try:
            return self._dict[key]
        except:
            raise AttributeError(key)

    def __iter__(self):
        vals = list(self.values())
        for k in vals:
            yield k

    def __getitem__(self, key):
        return self._dict[key]

    def __setitem__(self, key, value):
        self._dict[key] = value

    def __delitem__(self, key):
        del self._dict[key]

    def keys(self):
        return list(self._dict.keys())

    def values(self):
        vals = list(self._dict.values())
        vals = [v for v in vals if isinstance(v, (ConfigurationGroup, Value))]
        vals.sort()
        return vals

    def items(self):
        return list(self._dict.items())

    def iterkeys(self):
        return iter(self._dict.keys())

    def itervalues(self):
        return iter(self._dict.values())

    def iteritems(self):
        return iter(self._dict.items())

    def get(self, *args, **kwargs):
        return self._dict.get(*args, **kwargs)

    def clear(self):
        return self._dict.clear()

    def copy(self):
        s = SortedDotDict()
        s._dict = self._dict.copy()
        return s

    def fromkeys(self):
        return self._dict.fromkeys()

    def has_key(self, key):
        return key in self._dict

    def pop(self, *args, **kwargs):
        return self._dict.pop(*args, **kwargs)

    def popitem(self, *args, **kwargs):
        return self._dict.popitem(*args, **kwargs)

    def setdefault(self, key, default):
        return self._dict.setdefault(key, default)

    def update(self, d):
        return self._dict.update(d)

    def viewitems(self, *args, **kwargs):
        return self._dict.viewitems(*args, **kwargs)

    def viewvalues(self, *args, **kwargs):
        return self._dict.viewvalues(*args, **kwargs)
Example #2
1
class DotMap(OrderedDict):
    def __init__(self, *args, **kwargs):
        self._map = OrderedDict()
        if args:
            d = args[0]
            if type(d) is dict:
                for k, v in self.__call_items(d):
                    if type(v) is dict:
                        v = DotMap(v)
                    self._map[k] = v
        if kwargs:
            for k, v in self.__call_items(kwargs):
                self._map[k] = v

    def __call_items(self, obj):
        if hasattr(obj, "iteritems") and ismethod(getattr(obj, "iteritems")):
            return obj.iteritems()
        else:
            return obj.items()

    def items(self):
        return self.iteritems()

    def iteritems(self):
        return self.__call_items(self._map)

    def __iter__(self):
        return self._map.__iter__()

    def next(self):
        return self._map.next()

    def __setitem__(self, k, v):
        self._map[k] = v

    def __getitem__(self, k):
        if k not in self._map:
            # automatically extend to new DotMap
            self[k] = DotMap()
        return self._map[k]

    def __setattr__(self, k, v):
        if k == "_map":
            super(DotMap, self).__setattr__(k, v)
        else:
            self[k] = v

    def __getattr__(self, k):
        if k == "_map":
            super(DotMap, self).__getattr__(k)
        else:
            return self[k]

    def __delattr__(self, key):
        return self._map.__delitem__(key)

    def __contains__(self, k):
        return self._map.__contains__(k)

    def __str__(self):
        items = []
        for k, v in self.__call_items(self._map):
            items.append("{0}={1}".format(k, repr(v)))
        out = "DotMap({0})".format(", ".join(items))
        return out

    def __repr__(self):
        return str(self)

    def toDict(self):
        d = {}
        for k, v in self.items():
            if type(v) is DotMap:
                v = v.toDict()
            d[k] = v
        return d

    def pprint(self):
        pprint(self.toDict())

        # proper dict subclassing

    def values(self):
        return self._map.values()

    @classmethod
    def parseOther(self, other):
        if type(other) is DotMap:
            return other._map
        else:
            return other

    def __cmp__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__cmp__(other)

    def __eq__(self, other):
        other = DotMap.parseOther(other)
        if not isinstance(other, dict):
            return False
        return self._map.__eq__(other)

    def __ge__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__ge__(other)

    def __gt__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__gt__(other)

    def __le__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__le__(other)

    def __lt__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__lt__(other)

    def __ne__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__ne__(other)

    def __delitem__(self, key):
        return self._map.__delitem__(key)

    def __len__(self):
        return self._map.__len__()

    def clear(self):
        self._map.clear()

    def copy(self):
        return self

    def get(self, key, default=None):
        return self._map.get(key, default)

    def has_key(self, key):
        return key in self._map

    def iterkeys(self):
        return self._map.iterkeys()

    def itervalues(self):
        return self._map.itervalues()

    def keys(self):
        return self._map.keys()

    def pop(self, key, default=None):
        return self._map.pop(key, default)

    def popitem(self):
        return self._map.popitem()

    def setdefault(self, key, default=None):
        self._map.setdefault(key, default)

    def update(self, *args, **kwargs):
        if len(args) != 0:
            self._map.update(*args)
        self._map.update(kwargs)

    def viewitems(self):
        return self._map.viewitems()

    def viewkeys(self):
        return self._map.viewkeys()

    def viewvalues(self):
        return self._map.viewvalues()

    @classmethod
    def fromkeys(cls, seq, value=None):
        d = DotMap()
        d._map = OrderedDict.fromkeys(seq, value)
        return d
Example #3
1
class DotMap(MutableMapping, OrderedDict):
    def __init__(self, *args, **kwargs):
        self._map = OrderedDict()
        self._dynamic = True
        if kwargs:
            if "_dynamic" in kwargs:
                self._dynamic = kwargs["_dynamic"]
        if args:
            d = args[0]
            if isinstance(d, dict):
                for k, v in self.__call_items(d):
                    if type(v) is dict:
                        v = DotMap(v, _dynamic=self._dynamic)
                    if type(v) is list:
                        l = []
                        for i in v:
                            n = i
                            if type(i) is dict:
                                n = DotMap(i, _dynamic=self._dynamic)
                            l.append(n)
                        v = l
                    self._map[k] = v
        if kwargs:
            for k, v in self.__call_items(kwargs):
                if k is not "_dynamic":
                    self._map[k] = v

    def __call_items(self, obj):
        if hasattr(obj, "iteritems") and ismethod(getattr(obj, "iteritems")):
            return obj.iteritems()
        else:
            return obj.items()

    def items(self):
        return self.iteritems()

    def iteritems(self):
        return self.__call_items(self._map)

    def __iter__(self):
        return self._map.__iter__()

    def next(self):
        return self._map.next()

    def __setitem__(self, k, v):
        self._map[k] = v

    def __getitem__(self, k):
        if k not in self._map and self._dynamic and k != "_ipython_canary_method_should_not_exist_":
            # automatically extend to new DotMap
            self[k] = DotMap()
        return self._map[k]

    def __setattr__(self, k, v):
        if k in {"_map", "_dynamic", "_ipython_canary_method_should_not_exist_"}:
            super(DotMap, self).__setattr__(k, v)
        else:
            self[k] = v

    def __getattr__(self, k):
        if k == {"_map", "_dynamic", "_ipython_canary_method_should_not_exist_"}:
            super(DotMap, self).__getattr__(k)
        else:
            return self[k]

    def __delattr__(self, key):
        return self._map.__delitem__(key)

    def __contains__(self, k):
        return self._map.__contains__(k)

    def __str__(self):
        items = []
        for k, v in self.__call_items(self._map):
            # bizarre recursive assignment situation (why someone would do this is beyond me)
            if id(v) == id(self):
                items.append("{0}=DotMap(...)".format(k))
            else:
                items.append("{0}={1}".format(k, repr(v)))
        out = "DotMap({0})".format(", ".join(items))
        return out

    def __repr__(self):
        return str(self)

    def toDict(self):
        d = {}
        for k, v in self.items():
            if type(v) is DotMap:
                # bizarre recursive assignment support
                if id(v) == id(self):
                    v = d
                else:
                    v = v.toDict()
            elif type(v) is list:
                l = []
                for i in v:
                    n = i
                    if type(i) is DotMap:
                        n = i.toDict()
                    l.append(n)
                v = l
            d[k] = v
        return d

    def pprint(self):
        pprint(self.toDict())

    def empty(self):
        return not any(self)

        # proper dict subclassing

    def values(self):
        return self._map.values()

        # ipython support

    def __dir__(self):
        return self.keys()

    @classmethod
    def parseOther(self, other):
        if type(other) is DotMap:
            return other._map
        else:
            return other

    def __cmp__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__cmp__(other)

    def __eq__(self, other):
        other = DotMap.parseOther(other)
        if not isinstance(other, dict):
            return False
        return self._map.__eq__(other)

    def __ge__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__ge__(other)

    def __gt__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__gt__(other)

    def __le__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__le__(other)

    def __lt__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__lt__(other)

    def __ne__(self, other):
        other = DotMap.parseOther(other)
        return self._map.__ne__(other)

    def __delitem__(self, key):
        return self._map.__delitem__(key)

    def __len__(self):
        return self._map.__len__()

    def clear(self):
        self._map.clear()

    def copy(self):
        return DotMap(self.toDict())

    def __copy__(self):
        return self.copy()

    def __deepcopy__(self, memo=None):
        return self.copy()

    def get(self, key, default=None):
        return self._map.get(key, default)

    def has_key(self, key):
        return key in self._map

    def iterkeys(self):
        return self._map.iterkeys()

    def itervalues(self):
        return self._map.itervalues()

    def keys(self):
        return self._map.keys()

    def pop(self, key, default=None):
        return self._map.pop(key, default)

    def popitem(self):
        return self._map.popitem()

    def setdefault(self, key, default=None):
        self._map.setdefault(key, default)

    def update(self, *args, **kwargs):
        if len(args) != 0:
            self._map.update(*args)
        self._map.update(kwargs)

    def viewitems(self):
        return self._map.viewitems()

    def viewkeys(self):
        return self._map.viewkeys()

    def viewvalues(self):
        return self._map.viewvalues()

    @classmethod
    def fromkeys(cls, seq, value=None):
        d = DotMap()
        d._map = OrderedDict.fromkeys(seq, value)
        return d

    def __getstate__(self):
        return self.__dict__

    def __setstate__(self, d):
        self.__dict__.update(d)
Example #4
1
class NotificationDisplay(object):
    """Interface to display notification stack.
		Should have "display(note, cb_dismiss=None) -> nid(UInt32, >0)", "close(nid)"
			methods and NoWindowError(nid) exception, raised on erroneous nid's in close().
		Current implementation based on notipy: git://github.com/the-isz/notipy.git"""

    window = namedtuple("Window", "gobj event_boxes")
    base_css = b"""
		#notification { background: transparent; }
		#notification #frame { background-color: #d4ded8; padding: 3px; }
		#notification #hs { background-color: black; }

		#notification #critical { background-color: #ffaeae; }
		#notification #normal { background-color: #f0ffec; }
		#notification #low { background-color: #bee3c6; }

		#notification #summary {
			padding-left: 5px;
			font-size: 1.2em;
			text-shadow: 1px 1px 0px gray;
		}
		#notification #body { font-size: 1em; }
		#notification #body * { background-color: #d4ded8; }
	"""
    base_css_min = b"#notification * { font-size: 8; }"  # simpliest fallback

    def __init__(
        self,
        layout_margin,
        layout_anchor,
        layout_direction,
        icon_scale=dict(),
        markup_default=False,
        markup_warn=False,
        markup_strip=False,
    ):
        self.margins = dict(
            it.chain.from_iterable(map(lambda ax: ((2 ** ax, layout_margin), (-2 ** ax, layout_margin)), xrange(2)))
        )
        self.layout_anchor = layout_anchor
        self.layout_direction = layout_direction
        self.icon_scale = icon_scale
        self.markup_default = markup_default
        self.markup_warn, self.markup_strip = markup_warn, markup_strip

        self._windows = OrderedDict()

        self._default_style = self._get_default_css()
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(), self._default_style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

    def _pango_markup_parse(self, text, _err_mark="[TN82u8] "):
        success = True
        try:
            _, attr_list, text, _ = Pango.parse_markup(text, -1, "\0")
        except GLib.GError as err:
            if self.markup_warn:
                msg_start = "{}Pango formatting failed".format(_err_mark)
                if msg_start not in text:  # detect and avoid possible feedback loops
                    log.warn("%s (%s) for text, stripping markup: %r", msg_start, err, text)
                else:
                    text = xml_escape(text)  # escape message so it'd render bugged part
            if self.markup_strip:
                text = strip_markup(text)
            try:
                _, attr_list, text, _ = Pango.parse_markup(text, -1, "\0")
            except GLib.GError:
                attr_list = None
            success = False
        return success, text, attr_list

    def _pango_markup_to_gtk(
        self,
        text,
        attr_list=None,
        _pango_classes={
            "SIZE": Pango.AttrInt,
            "WEIGHT": Pango.AttrInt,
            "UNDERLINE": Pango.AttrInt,
            "STRETCH": Pango.AttrInt,
            "VARIANT": Pango.AttrInt,
            "STYLE": Pango.AttrInt,
            "SCALE": Pango.AttrFloat,
            "FAMILY": Pango.AttrString,
            "FONT_DESC": Pango.AttrFontDesc,
            "STRIKETHROUGH": Pango.AttrInt,
            "BACKGROUND": Pango.AttrColor,
            "FOREGROUND": Pango.AttrColor,
            "RISE": Pango.AttrInt,
        },
        _pango_to_gtk={"font_desc": "font"},
    ):
        # See https://bugzilla.gnome.org/show_bug.cgi?id=59390 for why it is necessary
        # And doesn't work with GI anyway because of
        #  https://bugzilla.gnome.org/show_bug.cgi?id=646788
        #  "Behdad Esfahbod [pango developer]: I personally have no clue how to fix this"
        # Workaround from https://github.com/matasbbb/pitivit/commit/da815339e
        # TODO: fix when AttrList.get_iterator will be accessible via GI or textbuffer gets set_markup()
        if attr_list is None:
            _, text, attr_list = self._pango_markup_parse(text)
            if attr_list is None:
                yield (text, None)
                raise StopIteration

        gtk_tags = defaultdict(dict)

        def parse_attr(attr, _data):
            gtk_attr = attr.klass.type.value_nick
            if gtk_attr in _pango_to_gtk:
                gtk_attr = _pango_to_gtk[gtk_attr]
            pango_attr, span = attr.klass.type.value_name, (attr.start_index, attr.end_index)
            assert pango_attr.startswith("PANGO_ATTR_"), pango_attr
            attr.__class__ = _pango_classes[pango_attr[11:]]  # allows to access attr.value
            for k in "value", "ink_rect", "logical_rect", "desc", "color":
                if not hasattr(attr, k):
                    continue
                val = getattr(attr, k)
                if k == "color":
                    val = "#" + "".join("{:02x}".format(v / 256) for v in [val.red, val.green, val.blue])
                gtk_tags[span][gtk_attr] = val
                break
            else:
                raise KeyError("Failed to extract value for pango attribute: {}".format(pango_attr))
            return False

        attr_list.filter(parse_attr, None)

        pos = 0
        for (a, b), props in sorted(gtk_tags.viewitems()):
            if a > pos:
                yield (text[pos:a], None)
            yield (text[a:b], props)
            pos = b
        if text[pos:]:
            yield (text[pos:], None)

    def _get_default_css(self):
        css, base_css = Gtk.CssProvider(), self.base_css
        for attempt in xrange(6):
            try:
                css.load_from_data(base_css)
            except GLib.GError as err:
                log.warn("Failed to load default CSS style (try %s): %s", attempt + 1, err)
                # print(base_css)
            else:
                break
            # Try to work around https://bugzilla.gnome.org/show_bug.cgi?id=678876 and similar issues
            if attempt == 0:
                base_css = re.sub(br"\b(background-color:)\s*rgba\([^;]+;", br"\1 white;", base_css)
            elif attempt == 1:
                base_css = re.sub(br"\b(font-size:)\s*(\d+)px\s*;", br"\1 \2;", base_css)
            elif attempt == 2:
                base_css = re.sub(br"\b(text-shadow:)[^;]+;", br"\1 1 1 0 gray;", base_css)
            elif attempt == 3:
                base_css = re.sub(br"\btext-shadow:[^;]+;", b"", base_css)
            elif attempt == 4:
                base_css = self.base_css_min  # last resort before no-css-at-all
            else:
                break  # don't load any css
        return css

    def _update_layout(self):
        # Get the coordinates of the "anchor" corner (screen corner +/- margins)
        base = tuple(
            map(
                lambda ax, gdk_dim=("width", "height"): (getattr(Gdk.Screen, gdk_dim[ax])() - self.margins[2 ** ax])
                if 2 ** ax & self.layout_anchor
                else self.margins[-2 ** ax],
                xrange(2),
            )
        )
        # Iterate over windows in order, placing each one starting from a "base" corner
        for win in map(op.attrgetter("gobj"), self._windows.viewvalues()):
            win.move(*map(lambda ax: base[ax] - (win.get_size()[ax] if 2 ** ax & self.layout_anchor else 0), xrange(2)))
            margin = self.margins[
                (2 * ((2 ** self.layout_direction) & self.layout_anchor) / 2 ** self.layout_direction - 1)
                * 2 ** self.layout_direction
            ]
            base = tuple(
                map(
                    lambda ax: base[ax]
                    if self.layout_direction != ax
                    else base[ax]
                    + (margin + win.get_size()[ax]) * (2 * (2 ** ax ^ (2 ** ax & self.layout_anchor)) / 2 ** ax - 1),
                    xrange(2),
                )
            )

    def _get_icon(self, icon, remote=False):
        widget_icon = None

        if icon is not None:
            if isinstance(icon, types.StringTypes):
                icon_path = os.path.expanduser(urllib.url2pathname(icon))
                if icon_path.startswith("file://"):
                    icon_path = icon_path[7:]
                if os.path.isfile(icon_path):
                    widget_icon = GdkPixbuf.Pixbuf.new_from_file(icon_path)
                else:
                    # Available names: Gtk.IconTheme.get_default().list_icons(None)
                    theme = Gtk.IconTheme.get_default()
                    icon_size = any(self.icon_scale.get("fixed", list())) or 32
                    widget_icon = theme.lookup_icon(icon, icon_size, Gtk.IconLookupFlags.USE_BUILTIN)
                    if widget_icon:
                        widget_icon = widget_icon.load_icon()
                    else:
                        # Msgs from remote hosts natually can have non-local icon paths in them
                        (log.warn if not remote else log.debug)(
                            "Provided icon info seem to be neither valid icon file nor"
                            " a name in a freedesktop.org-compliant icon theme (or current theme"
                            " does not have that one), ignoring it: %r",
                            core.format_trunc(icon),
                        )
            else:
                w, h, rowstride, has_alpha, bits_per_sample, channels, data = icon
                data = bytes(bytearray(data))
                widget_icon = GdkPixbuf.Pixbuf.new_from_data(
                    data,
                    GdkPixbuf.Colorspace.RGB,
                    bool(has_alpha),
                    int(bits_per_sample),
                    int(w),
                    int(h),
                    int(rowstride),
                )
                widget_icon._data = data  # must be preserved from gc

        if widget_icon:
            if any(it.chain.from_iterable(self.icon_scale.values())):  # scale icon
                w, h = widget_icon.get_width(), widget_icon.get_height()
                for k in "fixed", "min", "max":
                    box_w, box_h = self.icon_scale.get(k, (0, 0))
                    if not any([box_w, box_h]):
                        continue
                    if k == "min" and not ((box_w and w < box_w) or (box_h and h < box_h)):
                        continue
                    if k == "max" and not ((box_w and w > box_w) or (box_h and h > box_h)):
                        continue
                    scale_down = (box_w and w > box_w) or (box_h and h > box_h)
                    if scale_down:
                        scale = min  # factor<1, unspec=1, must fit on both dimensions
                    elif box_w and box_h:
                        scale = min  # factor>1, but still pick min to fit on both
                    else:
                        scale = max  # ignore unspec=1 and scale to max possible factor
                    scale = scale(float(box_w or w) / w, float(box_h or h) / h)
                    box_w, box_h = w * scale, h * scale
                    log.debug(
                        "Scaling image (%s, criteria: %s) by a factor of" " %.3f: %dx%d -> %dx%d",
                        ["up", "down"][scale_down],
                        k,
                        scale,
                        w,
                        h,
                        box_w,
                        box_h,
                    )
                    widget_icon = widget_icon.scale_simple(box_w, box_h, GdkPixbuf.InterpType.BILINEAR)
                    if k == "fixed":
                        break  # no need to apply min/max after that
            widget_icon, pixbuf = Gtk.Image(), widget_icon
            widget_icon.set_from_pixbuf(pixbuf)

        return widget_icon

    def _set_visual(self, win, ev=None):
        visual = win.get_screen().get_rgba_visual()
        if visual:
            win.set_visual(visual)

    def _create_win(self, summary, body, icon=None, urgency_label=None, markup=False, remote=None):
        log.debug(
            "Creating window with parameters: %s",
            core.repr_trunc_rec(dict(summary=summary, body=body, icon=icon, urgency=urgency_label, markup=markup)),
        )

        win = Gtk.Window(name="notification", type=Gtk.WindowType.POPUP)
        win.set_default_size(400, 20)
        win.connect("screen-changed", self._set_visual)
        self._set_visual(win)
        ev_boxes = [win]

        frame = Gtk.Box(name="frame")
        win.add(frame)

        try:
            widget_icon = self._get_icon(icon, remote=remote)
        except Exception:  # Gdk may raise errors for some images/formats
            log.exception("Failed to set notification icon")
            widget_icon = None

        box_margin = 3
        v_box = Gtk.VBox(spacing=box_margin, expand=False)
        if widget_icon is not None:
            h_box = Gtk.HBox(spacing=box_margin * 2)
            frame.pack_start(h_box, True, True, 0)
            h_box.pack_start(widget_icon, False, False, 0)
            h_box.pack_start(v_box, True, True, 0)
            ev_boxes.append(h_box)
        else:
            frame.pack_start(v_box, True, True, 0)

        widget_summary = Gtk.Label(name="summary")

        # Sanitize tags through pango first, so set_markup won't produce empty label
        summary_markup, summary_text, summary = self.get_display_summary(summary, markup)
        if summary_markup:
            widget_summary.set_markup(summary)
        else:
            widget_summary.set_text(summary)

        widget_summary.set_alignment(0, 0)
        if urgency_label:
            summary_box = Gtk.EventBox(name=urgency_label)
            summary_box.add(widget_summary)
        else:
            summary_box = widget_summary
        v_box.pack_start(summary_box, False, False, 0)
        ev_boxes.append(summary_box)

        v_box.pack_start(Gtk.HSeparator(name="hs"), False, False, 0)

        widget_body = Gtk.TextView(name="body", wrap_mode=Gtk.WrapMode.WORD_CHAR, cursor_visible=False, editable=False)
        widget_body_buffer = widget_body.get_buffer()

        body_markup, body_text, body_attrs = self.get_display_body(body, markup)
        if not body_markup:
            widget_body_buffer.set_text(body_text)
        else:
            # This buffer uses pango markup, even though GtkTextView does not support it
            # Most magic is in pango_markup_to_gtk(), there doesn't seem to be any cleaner way
            def get_tag(props, _tag_id=iter(xrange(2 ** 31 - 1)), _tag_table=dict()):
                k = tuple(sorted(props.viewitems()))
                if k not in _tag_table:
                    _tag_table[k] = widget_body_buffer.create_tag("x{}".format(next(_tag_id)), **props)
                return _tag_table[k]

            pos = widget_body_buffer.get_end_iter()
            for text, props in body_attrs:
                if props:
                    widget_body_buffer.insert_with_tags(pos, text, get_tag(props))
                else:
                    widget_body_buffer.insert(pos, text)

        v_box.pack_start(widget_body, True, True, 0)
        ev_boxes.append(widget_body)

        # Make sure the window is initially drawn off-screen, because it can't be
        #  placed properly until it's size is known, and it's size is unknown until it's
        #  actually handled by window manager and then drawn by X
        # Proper placement is done on update_layout() call
        win.move(-2000, -2000)

        win.show_all()
        return self.window(win, ev_boxes)

    def get_display_summary(self, summary, markup):
        if markup:
            success, text, _ = self._pango_markup_parse(summary)
            if not success:
                markup, summary = False, text
        else:
            text = summary
        return markup, text, summary

    def get_display_body(self, body, markup):
        if markup:
            _, text, attr_list = self._pango_markup_parse(body)
            if attr_list is None:
                markup, body_attrs = False, [(text, None)]
            else:
                body_attrs = self._pango_markup_to_gtk(text, attr_list)
        else:
            text, body_attrs = body, [(body, None)]
        return markup, text, body_attrs

    def get_note_markup(self, note):
        return note.hints.get("x-nt-markup", self.markup_default)

    def get_note_text(self, note):
        "Returns note text, stripped of all markup, if any (and if enabled)."
        markup = self.get_note_markup(note)
        _, summary_text, _ = self.get_display_summary(note.summary, markup)
        _, body_text, _ = self.get_display_body(note.body, markup)
        return summary_text, body_text

    def display(self, note, cb_dismiss=None, cb_hover=None, cb_leave=None):
        try:
            # Priorities for icon sources:
            #  image{-,_}data: hint. raw image data structure of signature (iiibiiay)
            #  image{-,_}path: hint. either an URI (file://...) or a name in a f.o-compliant icon theme
            #  app_icon: parameter. same as image-path
            #  icon_data: hint. same as image-data
            # image_* is a deprecated hints from 1.1 spec, 1.2 is preferred
            #  (don't seem to be even mentioned in 1.2 spec icon priorities section)
            hints = note.hints.copy()
            k = "__app_icon"  # to avoid clobbering anything
            hints[k] = note.icon
            for k in "image-data", "image_data", "image-path", "image_path", k, "icon_data":
                image = hints.get(k)
                if image:
                    log.debug("Got icon image from hint: %s", k)
                    break

            urgency = note.hints.get("urgency")
            if urgency is not None:
                urgency = core.urgency_levels.by_id(int(urgency))
            markup = self.get_note_markup(note)

            win = self._create_win(
                note.summary, note.body, image, urgency, markup=markup, remote=note.hints.get("x-nt-from-remote")
            )

            for eb in win.event_boxes:
                eb.add_events(
                    Gdk.EventMask.BUTTON_PRESS_MASK
                    | Gdk.EventMask.POINTER_MOTION_MASK
                    | Gdk.EventMask.LEAVE_NOTIFY_MASK
                )
                for ev, cb in [
                    ("button-press-event", cb_dismiss),
                    ("motion-notify-event", cb_hover),
                    ("leave-notify-event", cb_leave),
                ]:
                    if cb:
                        eb.connect(ev, lambda w, ev, cb, nid: cb(nid), cb, note.id)
            if cb_dismiss and win.event_boxes:
                # Connect only to window object (or first eventbox in the list)
                win.event_boxes[0].connect("destroy", lambda w, cb, nid: cb(nid), cb_dismiss, note.id)

                # update_layout() *must* be delayed until window "configure-event", because
                #  actual window size is unknown until it's resized by window manager and drawn by X
                # See the list of caveats here:
                #  http://developer.gnome.org/gtk3/unstable/GtkWindow.html#gtk-window-get-size
            win.gobj.connect("configure-event", lambda w, void: self._update_layout())
            self._windows[note.id] = win

        except:
            log.exception("Failed to create notification window")

    class NoWindowError(Exception):
        pass

    def _close(self, nid):
        try:
            win = self._windows.pop(nid).gobj
        except KeyError:
            raise self.NoWindowError(nid)
        win.hide(), win.destroy()

    def close(self, nid):
        self._close(nid)
        self._update_layout()
Example #5
1
def main(args=None):
    import argparse

    parser = argparse.ArgumentParser(
        description="Tool to measure resources consumed"
        " by a group of processes, no matter how hard they fork."
        " Does that by creating a temp cgroup and running passed command there."
    )
    parser.add_argument("cmdline", nargs="+", help="Command to run and any arguments for it.")
    parser.add_argument(
        "-g",
        "--cgroup",
        default="bench/tmp",
        metavar="{ /path | tagged-path }",
        help="Hierarchy path to create temp-cgroup under"
        ' ("/" means root cgroup, default: %(default)s).'
        " Any missing path components will be created."
        " If relative name is specified, it will be interpreted from /tagged path.",
    )
    parser.add_argument(
        "-c",
        "--rcs",
        default="cpuacct, blkio, memory",
        metavar="rc1[,rc2,...]",
        help="Comma-separated list of rc hierarchies to get metrics from (default: %(default)s)."
        " Should have corresponding path mounted under {}.".format(cg_root),
    )
    parser.add_argument(
        "-q", "--quiet", action="store_true", help="Redirect stderr/stdout for started pid to /dev/null."
    )
    parser.add_argument("-d", "--debug", action="store_true", help="Verbose operation mode.")
    opts = parser.parse_args(sys.argv[1:] if args is None else args)

    global log
    import logging

    logging.basicConfig(level=logging.DEBUG if opts.debug else logging.INFO)
    log = logging.getLogger()

    # Check all rc tasks-file paths
    cg_subpath = "tmp.{}".format(cmd_pid)
    cg_tasks, cg_path = OrderedDict(), join("tagged", opts.cgroup).lstrip("/")
    for rc in map(bytes.strip, opts.rcs.split(",")):
        tasks = join(cg_root, rc, cg_path, cg_subpath, "tasks")
        assert "\n" not in tasks, repr(tasks)
        os.makedirs(dirname(tasks))
        assert exists(tasks), tasks
        cg_tasks[rc] = tasks

        # Append cmdline, send data to child
    data = cg_tasks.values()
    if opts.quiet:
        data.append("-")
    data = "\n".join(it.chain(data, ["\0".join(map(lambda arg: arg.encode("hex"), opts.cmdline))]))
    cmd_w.write(struct.pack(len_fmt, len(data)) + data)
    cmd_w.flush()

    # Wait for signal to start counting
    mark = cmd_start_r.read(1)
    ts0 = time()
    assert mark == ".", repr(mark)
    cmd_start_r.close()

    pid, status = os.waitpid(cmd_pid, 0)
    ts1 = time()

    err = status >> 8
    if status & 0xFF:
        print("Unclean exit of child pid due to signal: {}".format((status & 0xFF) >> 1))
        err = err or 1

        # Make sure everything finished running there
    leftovers = set()
    for tasks in cg_tasks.values():
        with open(tasks) as src:
            leftovers.update(map(int, src.read().splitlines()))
    if leftovers:
        print(
            "Main pid has finished, but cgroups have leftover threads"
            " still running: {}".format(", ".join(map(bytes, leftovers))),
            file=sys.stderr,
        )
        err = err or 1

        # Collect/print accounting data
    acct = OrderedDict()
    acct["cmd"] = " ".join(opts.cmdline)
    acct["wall_clock"] = "{:.3f}".format(ts1 - ts0)
    acct["exit_status"] = "{} {}".format(status >> 8, status & 0xFF >> 1)

    acct_srcs = OrderedDict()
    for cg_path in map(dirname, cg_tasks.viewvalues()):
        for p in os.listdir(cg_path):
            acct_srcs[p] = join(cg_path, p)

    acct_nums = OrderedDict(
        [
            ("cpuacct", ["usage", "usage_percpu"]),
            (
                "memory",
                [
                    "max_usage_in_bytes",
                    "memsw.max_usage_in_bytes",
                    "kmem.max_usage_in_bytes",
                    "kmem.tcp.max_usage_in_bytes",
                ],
            ),
        ]
    )
    for rc, metrics in acct_nums.viewitems():
        for p in metrics:
            p = "{}.{}".format(rc, p)
            if p not in acct_srcs:
                continue
            with open(acct_srcs[p]) as src:
                numbers = map(int, src.read().strip().split())
                acct[p] = " ".join(map(num_format, numbers))

    for p in "time sectors io_merged io_serviced io_wait_time".split():
        p = "blkio.{}".format(p)
        try:
            src = acct_srcs[p]
        except KeyError:
            pass
        else:
            with open(src) as src:
                src = src.read().splitlines()
            for line in src:
                line = line.split()
                if not line or line[0] == "Total":
                    continue
                t = None
                try:
                    dev, t, v = line
                except ValueError:
                    dev, v = line
                dev = dev_resolve(*map(int, dev.split(":")))
                if not dev:
                    continue
                label = "{}[{}]".format(p, dev)
                if t:
                    label += "[{}]".format(t)
                acct[label] = num_format(int(v))

    for k, v in acct.viewitems():
        print("{}: {}".format(k, v), file=sys.stderr)

        # Cleanup tmp dirs
    leftovers = set()
    for tasks in cg_tasks.values():
        tasks_dir = dirname(tasks)
        try:
            os.rmdir(tasks_dir)
        except (OSError, IOError):
            leftovers.add(tasks_dir)
    if leftovers:
        print("Leftover cgroup dirs remaining:{}\n".format("\n  ".join([""] + sorted(leftovers))), file=sys.stderr)
        err = err or 1

    return err
class DebugVariableViewer:
    def __init__(self, window, items=[]):
        """
        Constructor
        @param window: Reference to the Debug Variable Panel
        @param items: List of DebugVariableItem displayed by Viewer
        """
        self.ParentWindow = window
        self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items])
        self.Items = self.ItemsDict.viewvalues()

        # Variable storing current highlight displayed in Viewer
        self.Highlight = HIGHLIGHT_NONE
        # List of buttons
        self.Buttons = []

    def __del__(self):
        """
        Destructor
        """
        # Remove reference to Debug Variable Panel
        self.ParentWindow = None

    def GetIndex(self):
        """
        Return position of Viewer in Debug Variable Panel
        @return: Position of Viewer
        """
        return self.ParentWindow.GetViewerIndex(self)

    def GetItem(self, variable):
        """
        Return item storing values of a variable
        @param variable: Variable path
        @return: Item storing values of this variable
        """
        return self.ItemsDict.get(variable, None)

    def GetItems(self):
        """
        Return items displayed by Viewer
        @return: List of items displayed in Viewer
        """
        return self.ItemsDict.values()

    def AddItem(self, item):
        """
        Add an item to the list of items displayed by Viewer
        @param item: Item to add to the list
        """
        self.ItemsDict[item.GetVariable()] = item

    def RemoveItem(self, item):
        """
        Remove an item from the list of items displayed by Viewer
        @param item: Item to remove from the list
        """
        self.ItemsDict.pop(item.GetVariable(), None)

    def ClearItems(self):
        """
        Clear list of items displayed by Viewer
        """
        # Unsubscribe every items of the list
        for item in self.Items:
            self.ParentWindow.RemoveDataConsumer(item)

        # Clear list
        self.ItemsDict.clear()

    def ItemsIsEmpty(self):
        """
        Return if list of items displayed by Viewer is empty
        @return: True if list is empty
        """
        return len(self.Items) == 0

    def SubscribeAllDataConsumers(self):
        """
        Function that unsubscribe and remove every item that store values of
        a variable that doesn't exist in PLC anymore
        """
        for item in self.ItemsDict.values()[:]:
            iec_path = item.GetVariable()

            # Check that variablepath exist in PLC
            if self.ParentWindow.GetDataType(iec_path) is None:
                # If not, unsubscribe and remove it
                self.ParentWindow.RemoveDataConsumer(item)
                self.RemoveItem(item)
            else:
                # If it exist, resubscribe and refresh data type
                self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True)
                item.RefreshVariableType()

    def ResetItemsData(self):
        """
        Reset data stored in every items displayed in Viewer
        """
        for item in self.Items:
            item.ResetData()

    def GetItemsMinCommonTick(self):
        """
        Return the minimum tick common to all iems displayed in Viewer
        @return: Minimum common tick between items
        """
        return reduce(max, [item.GetData()[0, 0] for item in self.Items if len(item.GetData()) > 0], 0)

    def RefreshViewer(self):
        """
        Method that refresh the content displayed by Viewer
        Need to be overridden by inherited classes
        """
        pass

    def SetHighlight(self, highlight):
        """
        Set Highlight type displayed in Viewer
        @return: True if highlight has changed
        """
        # Return immediately if highlight don't change
        if self.Highlight == highlight:
            return False

        self.Highlight = highlight
        return True

    def GetButtons(self):
        """
        Return list of buttons defined in Viewer
        @return: List of buttons
        """
        return self.Buttons

    def IsOverButton(self, x, y):
        """
        Return if point is over one button of Viewer
        @param x: X coordinate of point
        @param y: Y coordinate of point
        @return: button where point is over
        """
        for button in self.GetButtons():
            if button.HitTest(x, y):
                return button
        return None

    def HandleButton(self, x, y):
        """
        Search for the button under point and if found execute associated
        callback
        @param x: X coordinate of point
        @param y: Y coordinate of point
        @return: True if a button was found and callback executed
        """
        button = self.IsOverButton(x, y)
        if button is None:
            return False

        button.ProcessCallback()
        return True

    def ShowButtons(self, show):
        """
        Set display state of buttons in Viewer
        @param show: Display state (True if buttons must be displayed)
        """
        # Change display of every buttons
        for button in self.Buttons:
            button.Show(show)

        # Refresh button positions
        self.RefreshButtonsPosition()
        self.RefreshViewer()

    def RefreshButtonsPosition(self):
        """
        Function that refresh buttons position in Viewer
        """
        # Get Viewer size
        width, height = self.GetSize()

        # Buttons are align right so we calculate buttons positions in
        # reverse order
        buttons = self.Buttons[:]
        buttons.reverse()

        # Position offset on x coordinate
        x_offset = 0
        for button in buttons:
            # Buttons are stacked right, removing those that are not active
            if button.IsEnabled():
                # Update button position according to button width and offset
                # on x coordinate
                w, h = button.GetSize()
                button.SetPosition(width - 5 - w - x_offset, 5)
                # Update offset on x coordinate
                x_offset += w + 2

    def DrawCommonElements(self, dc, buttons=None):
        """
        Function that draw common graphics for every Viewers
        @param dc: wx.DC object corresponding to Device context where drawing
        common graphics
        @param buttons: List of buttons to draw if different from default
        (default None)
        """
        # Get Viewer size
        width, height = self.GetSize()

        # Set dc styling for drop before or drop after highlight
        dc.SetPen(HIGHLIGHT_DROP_PEN)
        dc.SetBrush(HIGHLIGHT_DROP_BRUSH)

        # Draw line at upper side of Viewer if highlight is drop before
        if self.Highlight == HIGHLIGHT_BEFORE:
            dc.DrawLine(0, 1, width - 1, 1)

        # Draw line at lower side of Viewer if highlight is drop before
        elif self.Highlight == HIGHLIGHT_AFTER:
            dc.DrawLine(0, height - 1, width - 1, height - 1)

        # If no specific buttons are defined, get default buttons
        if buttons is None:
            buttons = self.Buttons
        # Draw buttons
        for button in buttons:
            button.Draw(dc)

        # If graph dragging is processing
        if self.ParentWindow.IsDragging():
            destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self)
            srcPos = self.ParentWindow.GetDraggingAxesPosition(self)
            if destBBox.width > 0 and destBBox.height > 0:
                srcPanel = self.ParentWindow.DraggingAxesPanel
                srcBBox = srcPanel.GetAxesBoundingBox()

                srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0)
                srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0)

                srcBmp = _convert_agg_to_wx_bitmap(srcPanel.get_renderer(), None)
                srcDC = wx.MemoryDC()
                srcDC.SelectObject(srcBmp)

                dc.Blit(destBBox.x, destBBox.y, int(destBBox.width), int(destBBox.height), srcDC, srcX, srcY)

    def OnEnter(self, event):
        """
        Function called when entering Viewer
        @param event: wx.MouseEvent 
        """
        # Display buttons
        self.ShowButtons(True)
        event.Skip()

    def OnLeave(self, event):
        """
        Function called when leaving Viewer
        @param event: wx.MouseEvent 
        """
        # Hide buttons
        self.ShowButtons(False)
        event.Skip()

    def OnCloseButton(self):
        """
        Function called when Close button is pressed
        """
        wx.CallAfter(self.ParentWindow.DeleteValue, self)

    def OnForceButton(self):
        """
        Function called when Force button is pressed
        """
        self.ForceValue(self.ItemsDict.values()[0])

    def OnReleaseButton(self):
        """
        Function called when Release button is pressed
        """
        self.ReleaseValue(self.ItemsDict.values()[0])

    def OnMouseDragging(self, x, y):
        """
        Function called when mouse is dragged over Viewer
        @param x: X coordinate of mouse pointer
        @param y: Y coordinate of mouse pointer
        """
        xw, yw = self.GetPosition()
        # Refresh highlight in Debug Variable Panel (highlight can be displayed
        # in another Viewer
        self.ParentWindow.RefreshHighlight(x + xw, y + yw)

    def RefreshHighlight(self, x, y):
        """
        Function called by Debug Variable Panel asking Viewer to refresh
        highlight according to mouse position
        @param x: X coordinate of mouse pointer
        @param y: Y coordinate of mouse pointer
        """
        # Get Viewer size
        width, height = self.GetSize()

        # Mouse is in the first half of Viewer
        if y < height / 2:
            # If Viewer is the upper one, draw drop before highlight
            if self.ParentWindow.IsViewerFirst(self):
                self.SetHighlight(HIGHLIGHT_BEFORE)

            # Else draw drop after highlight in previous Viewer
            else:
                self.SetHighlight(HIGHLIGHT_NONE)
                self.ParentWindow.HighlightPreviousViewer(self)

        # Mouse is in the second half of Viewer, draw drop after highlight
        else:
            self.SetHighlight(HIGHLIGHT_AFTER)

    def OnEraseBackground(self, event):
        """
        Function called when Viewer background is going to be erase
        @param event: wx.EraseEvent
        """
        # Prevent flicker on Windows
        pass

    def OnResize(self, event):
        """
        Function called when Viewer size changed
        @param event: wx.ResizeEvent
        """
        # Refresh button positions
        self.RefreshButtonsPosition()
        self.ParentWindow.ForceRefresh()
        event.Skip()

    def ForceValue(self, item):
        """
        Force value of item given
        @param item: Item to force value
        """
        # Check variable data type
        iec_path = item.GetVariable()
        iec_type = self.ParentWindow.GetDataType(iec_path)
        # Return immediately if not found
        if iec_type is None:
            return

        # Open a dialog to enter varaible forced value
        dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
        if dialog.ShowModal() == wx.ID_OK:
            self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue())

    def ReleaseValue(self, item):
        """
        Release value of item given
        @param item: Item to release value
        """
        self.ParentWindow.ReleaseDataValue(item.GetVariable().upper())
Example #7
1
class BaseCache(object):
    """
    BaseCache is a class that saves and operates on an OrderedDict. It has a
    certain capacity, stored in the attribute `maxsize`. Whether this
    capacity is reached, can be checked by using the boolean property
    `is_full`. To implement a custom cache, inherit from this class and
    override the methods ``__getitem__`` and ``__setitem__``.
    Call the method `sunpy.database.caching.BaseCache.callback` as soon
    as an item from the cache is removed.
    """

    __metaclass__ = ABCMeta

    def __init__(self, maxsize=float("inf")):
        self.maxsize = maxsize
        self._dict = OrderedDict()

    def get(self, key, default=None):  # pragma: no cover
        """Return the corresponding value to `key` if `key` is in the cache,
        `default` otherwise. This method has no side-effects, multiple calls
        with the same cache and the same passed key must always return the same
        value.

        """
        try:
            return self._dict[key]
        except KeyError:
            return default

    @abstractmethod
    def __getitem__(self, key):
        """abstract method: this method must be overwritten by inheriting
        subclasses. It defines what happens if an item from the cache is
        attempted to be accessed.

        """
        return  # pragma: no cover

    @abstractmethod
    def __setitem__(self, key, value):
        """abstract method: this method must be overwritten by inheriting
        subclasses. It defines what happens if a new value should be assigned
        to the given key. If the given key does already exist in the cache or
        not must be checked by the person who implements this method.
        """

    @abstractproperty
    def to_be_removed(self):
        """The item that will be removed on the next
        :meth:`sunpy.database.caching.BaseCache.remove` call.

        """

    @abstractmethod
    def remove(self):
        """Call this method to manually remove one item from the cache. Which
        item is removed, depends on the implementation of the cache. After the
        item has been removed, the callback method is called.

        """

    def callback(self, key, value):
        """This method should be called (by convention) if an item is removed
        from the cache because it is full. The passed key and value are the
        ones that are removed. By default this method does nothing, but it
        can be customized in a custom cache that inherits from this base class.

        """

    @property
    def is_full(self):
        """True if the number of items in the cache equals :attr:`maxsize`,
        False otherwise.

        """
        return len(self._dict) == self.maxsize

    def __delitem__(self, key):
        self._dict.__delitem__(key)

    def __contains__(self, key):
        return key in self._dict.keys()

    def __len__(self):
        return len(self._dict)

    def __iter__(self):
        for key in self._dict.__iter__():
            yield key

    def __reversed__(self):  # pragma: no cover
        for key in self._dict.__reversed__():
            yield key

    def clear(self):  # pragma: no cover
        return self._dict.clear()

    def keys(self):  # pragma: no cover
        return self._dict.keys()

    def values(self):  # pragma: no cover
        return self._dict.values()

    def items(self):  # pragma: no cover
        return self._dict.items()

    def iterkeys(self):  # pragma: no cover
        return self._dict.iterkeys()

    def itervalues(self):  # pragma: no cover
        for value in self._dict.itervalues():
            yield value

    def iteritems(self):  # pragma: no cover
        for key, value in self._dict.iteritems():
            yield key, value

    def update(self, *args, **kwds):  # pragma: no cover
        self._dict.update(*args, **kwds)

    def pop(self, key, default=MutableMapping._MutableMapping__marker):  # pragma: no cover
        return self._dict.pop(key, default)

    def setdefault(self, key, default=None):  # pragma: no cover
        return self._dict.setdefault(key, default)

    def popitem(self, last=True):  # pragma: no cover
        return self._dict.popitem(last)

    def __reduce__(self):  # pragma: no cover
        return self._dict.__reduce__()

    def copy(self):  # pragma: no cover
        return self._dict.copy()

    def __eq__(self, other):  # pragma: no cover
        return self._dict.__eq__(other)

    def __ne__(self, other):  # pragma: no cover
        return self._dict.__ne__(other)

    def viewkeys(self):  # pragma: no cover
        return self._dict.viewkeys()

    def viewvalues(self):  # pragma: no cover
        return self._dict.viewvalues()

    def viewitems(self):  # pragma: no cover
        return self._dict.viewitems()

    @classmethod
    def fromkeys(cls, iterable, value=None):  # pragma: no cover
        return OrderedDict.fromkeys(iterable, value)

    def __repr__(self):  # pragma: no cover
        return "{0}({1!r})".format(self.__class__.__name__, dict(self._dict))