Coverage for qutebrowser/utils/utils.py : 99%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
except ImportError: # pragma: no cover from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper YAML_C_EXT = False
"""Raised when there was unreachable code."""
"""Raised if the clipboard contents are unavailable for some reason."""
"""Raised if [gs]et_clipboard is used and selection=True is unsupported."""
"platform!")
"""Raised if get_clipboard is used and the clipboard is empty."""
"""Elide text so it uses a maximum of length chars.""" else:
"""Elide a filename to the given length.
The difference to the elide() is that the text is removed from the middle instead of from the end. This preserves file name extensions. Additionally, standard ASCII dots are used ("...") instead of the unicode "…" (U+2026) so it works regardless of the filesystem encoding.
This function does not handle path separators.
Args: filename: The filename to elide. length: The maximum length of the filename, must be at least 3.
Return: The elided filename. """ # Account for '...' else:
"""Remove leading whitespace and newlines from a text and maybe elide it.
Args: text: The text to compact. elidelength: To how many chars to elide. """
"""Get the contents of a file contained with qutebrowser.
Args: filename: The filename to open as string. binary: Whether to return a binary string. If False, the data is UTF-8-decoded.
Return: The file contents as string. """ # PyInstaller doesn't support pkg_resources :( # https://github.com/pyinstaller/pyinstaller/wiki/FAQ#misc else: else:
"""Get the absolute filename of a file contained with qutebrowser.
Args: filename: The filename.
Return: The absolute filename. """
"""Get a color which is percent% interpolated between start and end.
Args: a_c1, a_c2, a_c3: Start color components (R, G, B / H, S, V / H, S, L) b_c1, b_c2, b_c3: End color components (R, G, B / H, S, V / H, S, L) percent: Percentage to interpolate, 0-100. 0: Start color will be returned. 100: End color will be returned.
Return: A (c1, c2, c3) tuple with the interpolated color components. """
"""Get an interpolated color value.
Args: start: The start color. end: The end color. percent: Which value to get (0 - 100) colorspace: The desired interpolation color system, QColor::{Rgb,Hsv,Hsl} (from QColor::Spec enum) If None, start is used except when percent is 100.
Return: The interpolated QColor, with the same spec as the given start color. """
else:
percent) percent) percent) else:
"""Format a count of seconds to get a [H:]M:SS string.""" else:
"""Format a byte size so it's human readable.
Inspired by http://stackoverflow.com/q/1094841 """
"""Convert a Qt::Key member to a meaningful name.
Args: key: A Qt::Key member.
Return: A name of the key as a string. """ # Some keys handled in a weird way by QKeySequence::toString. # See https://bugreports.qt.io/browse/QTBUG-40030 # Most are unlikely to be ever needed, but you never know ;) # For dead/combining keys, we return the corresponding non-combining # key, as that's easier to add to the config. 'Key_Blue': 'Blue', 'Key_Calendar': 'Calendar', 'Key_ChannelDown': 'Channel Down', 'Key_ChannelUp': 'Channel Up', 'Key_ContrastAdjust': 'Contrast Adjust', 'Key_Dead_Abovedot': '˙', 'Key_Dead_Abovering': '˚', 'Key_Dead_Acute': '´', 'Key_Dead_Belowdot': 'Belowdot', 'Key_Dead_Breve': '˘', 'Key_Dead_Caron': 'ˇ', 'Key_Dead_Cedilla': '¸', 'Key_Dead_Circumflex': '^', 'Key_Dead_Diaeresis': '¨', 'Key_Dead_Doubleacute': '˝', 'Key_Dead_Grave': '`', 'Key_Dead_Hook': 'Hook', 'Key_Dead_Horn': 'Horn', 'Key_Dead_Iota': 'Iota', 'Key_Dead_Macron': '¯', 'Key_Dead_Ogonek': '˛', 'Key_Dead_Semivoiced_Sound': 'Semivoiced Sound', 'Key_Dead_Tilde': '~', 'Key_Dead_Voiced_Sound': 'Voiced Sound', 'Key_Exit': 'Exit', 'Key_Green': 'Green', 'Key_Guide': 'Guide', 'Key_Info': 'Info', 'Key_LaunchG': 'LaunchG', 'Key_LaunchH': 'LaunchH', 'Key_MediaLast': 'MediaLast', 'Key_Memo': 'Memo', 'Key_MicMute': 'Mic Mute', 'Key_Mode_switch': 'Mode switch', 'Key_Multi_key': 'Multi key', 'Key_PowerDown': 'Power Down', 'Key_Red': 'Red', 'Key_Settings': 'Settings', 'Key_SingleCandidate': 'Single Candidate', 'Key_ToDoList': 'Todo List', 'Key_TouchpadOff': 'Touchpad Off', 'Key_TouchpadOn': 'Touchpad On', 'Key_TouchpadToggle': 'Touchpad toggle', 'Key_Yellow': 'Yellow', 'Key_Alt': 'Alt', 'Key_AltGr': 'AltGr', 'Key_Control': 'Control', 'Key_Direction_L': 'Direction L', 'Key_Direction_R': 'Direction R', 'Key_Hyper_L': 'Hyper L', 'Key_Hyper_R': 'Hyper R', 'Key_Meta': 'Meta', 'Key_Shift': 'Shift', 'Key_Super_L': 'Super L', 'Key_Super_R': 'Super R', 'Key_unknown': 'Unknown', } # We now build our real special_names dict from the string mapping above. # The reason we don't do this directly is that certain Qt versions don't # have all the keys, so we want to ignore AttributeErrors. # Now we check if the key is any special one - if not, we use # QKeySequence::toString. 'Backtab': 'Tab', 'Esc': 'Escape', } else:
"""Convert a QKeyEvent to a meaningful name.
Args: e: A QKeyEvent.
Return: A name of the key (combination) as a string or None if only modifiers are pressed.. """ # Qt swaps Ctrl/Meta on macOS, so we switch it back here so the user # can use it in the config as expected. See: # https://github.com/qutebrowser/qutebrowser/issues/110 # http://doc.qt.io/qt-5.4/osx-issues.html#special-keys (Qt.MetaModifier, 'Ctrl'), (Qt.AltModifier, 'Alt'), (Qt.ControlModifier, 'Meta'), (Qt.ShiftModifier, 'Shift'), ]) else: modmask2str = collections.OrderedDict([ (Qt.ControlModifier, 'Ctrl'), (Qt.AltModifier, 'Alt'), (Qt.MetaModifier, 'Meta'), (Qt.ShiftModifier, 'Shift'), ]) Qt.Key_AltGr, Qt.Key_Super_L, Qt.Key_Super_R, Qt.Key_Hyper_L, Qt.Key_Hyper_R, Qt.Key_Direction_L, Qt.Key_Direction_R) # Only modifier pressed
class KeyInfo:
"""Stores information about a key, like used in a QKeyEvent.
Attributes: key: Qt::Key modifiers: Qt::KeyboardModifiers text: str """
def __repr__(self): if self.modifiers is None: modifiers = None else: #modifiers = qflags_key(Qt, self.modifiers) modifiers = hex(int(self.modifiers)) return get_repr(self, constructor=True, key=debug.qenum_key(Qt, self.key), modifiers=modifiers, text=self.text)
"""Raised by _parse_single_key/parse_keystring on parse errors."""
"""True if keystr is a 'special' keystring (e.g. <ctrl-x> or <space>)."""
"""Convert a single key string to a (Qt.Key, Qt.Modifiers, text) tuple.""" # Special key # vim-like key else: "<Ctrl-x> like keybinding.")
len(seq)))
Qt.AltModifier | Qt.MetaModifier | Qt.KeypadModifier | Qt.GroupSwitchModifier)
# Let's hope this is accurate... else:
"""Parse a keystring like <Ctrl-x> or xyz and return a KeyInfo list.""" else:
"""Normalize a keystring like Ctrl-Q to a keystring like Ctrl+Q.
Args: keystr: The key combination as a string.
Return: The normalized keystring. """ ('control', 'ctrl'), ('windows', 'meta'), ('mod1', 'alt'), ('mod4', 'meta'), )
"""A fake file-like stream which calls a function for write-calls."""
def fake_io(write_func): """Run code with stdout and stderr replaced by FakeIOStreams.
Args: write_func: The function to call when write is called. """ finally: # If the code we did run did change sys.stdout/sys.stderr, we leave it # unchanged. Otherwise, we reset it.
def disabled_excepthook(): """Run code with the exception hook temporarily disabled.""" finally: # If the code we did run did change sys.excepthook, we leave it # unchanged. Otherwise, we reset it.
"""Decorator to ignore and log exceptions.
This needs to be used for some places where PyQt segfaults on exceptions or silently ignores them.
We used to re-raise the exception with a single-shot QTimer in a similar case, but that lead to a strange problem with a KeyError with some random jinja template stuff as content. For now, we only log it, so it doesn't pass 100% silently.
This could also be a function, but as a class (with a "wrong" name) it's much cleaner to implement.
Attributes: _retval: The value to return in case of an exception. _predicate: The condition which needs to be True to prevent exceptions """
"""Save decorator arguments.
Gets called on parse-time with the decorator arguments.
Args: See class attributes. """
"""Called when a function should be decorated.
Args: func: The function to be decorated.
Return: The decorated function. """
def wrapper(*args, **kwargs): """Call the original function."""
"""Check if a given object is an enum."""
"""Get a suitable __repr__ string for an object.
Args: obj: The object to get a repr for. constructor: If True, show the Foo(one=1, two=2) form instead of <Foo one=1 two=2>. attrs: The attributes to add. """ else: else:
"""Get the fully qualified name of an object.
Based on twisted.python.reflect.fullyQualifiedName.
Should work with: - functools.partial objects - functions - classes - methods - modules """
else:
else:
"""Check if a function raises a given exception.
Args: exc: A single exception or an iterable of exceptions. func: A function to call. *args: The arguments to pass to the function.
Returns: True if the exception was raised, False otherwise. """ else:
"""Make sure a given text is encodable with the given encoding.
This replaces all chars not encodable with question marks. """
"""Replace invalid filename characters.
Note: This should be used for the basename, as it also removes the path separator.
Args: name: The filename. replacement: The replacement character (or None). """ # Bad characters taken from Windows, there are even fewer on Linux # See also # https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
"""Set the clipboard to some given data.""" else:
"""Get data from the clipboard.
Args: selection: Use the primary selection. fallback: Fall back to the clipboard if primary selection is unavailable. """ global fake_clipboard
else:
else:
"""Check if the OS supports primary selection."""
"""Get a random free port."""
"""Open the given file.
If cmdline is not given, downloads.open_dispatcher is used. If open_dispatcher is unset, the system's default application is used.
Args: filename: The filename to open. cmdline: The command to use as string. A `{}` is expanded to the filename. None means to use the system's default application or `downloads.open_dispatcher` if set. If no `{}` is found, the filename is appended to the cmdline. """ # Import late to avoid circular imports: # - usertypes -> utils -> guiprocess -> message -> usertypes # - usertypes -> utils -> config -> configdata -> configtypes -> # cmdutils -> command -> message -> usertypes
# the default program to open downloads with - will be empty string # if we want to use the default
# precedence order: cmdline > downloads.open_dispatcher > openUrl
.format(filename))
.format(filename, [cmd] + args))
"""Function which does nothing to avoid pylint complaining."""
r"""Expand a drive-path like E: into E:\.
Does nothing for other paths.
Args: path: The path to expand. """ # Usually, "E:" on Windows refers to the current working directory on drive # E:\. The correct way to specifify drive E: is "E:\", but most users # probably don't use the "multiple working directories" feature and expect # "E:" and "E:\" to be equal. else:
"""Wrapper over yaml.load using the C loader if possible."""
if delta > deadline: # pragma: no cover log.misc.warning( "YAML load took unusually long, please report this at " "https://github.com/qutebrowser/qutebrowser/issues/2777\n" "duration: {}s\n" "PyYAML version: {}\n" "C extension: {}\n" "Stack:\n\n" "{}".format( delta, yaml.__version__, YAML_C_EXT, ''.join(traceback.format_stack())))
"""Wrapper over yaml.dump using the C dumper if possible.
Also returns a str instead of bytes. """ encoding='utf-8', allow_unicode=True) else: |