From 5f83228c75e18393cf4ef83f13a372c8287d20c9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 18 Feb 2014 17:54:17 +0100 Subject: [PATCH] Reorder some methods, widgets not done yet --- qutebrowser/app.py | 234 ++++++++++++------------- qutebrowser/commands/keys.py | 58 +++--- qutebrowser/commands/utils.py | 36 ++-- qutebrowser/models/completion.py | 176 +++++++++---------- qutebrowser/models/completionfilter.py | 21 +-- qutebrowser/utils/signals.py | 8 +- qutebrowser/utils/url.py | 92 +++++----- qutebrowser/utils/version.py | 46 ++--- 8 files changed, 336 insertions(+), 335 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 2fc0e88f1..150a24a9f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -131,6 +131,54 @@ class QuteBrowser(QApplication): timer = QTimer.singleShot(0, self._process_init_args) self._timers.append(timer) + def _parseopts(self): + """Parse command line options.""" + parser = ArgumentParser("usage: %(prog)s [options]") + parser.add_argument('-l', '--log', dest='loglevel', + help='Set loglevel', default='info') + parser.add_argument('-c', '--confdir', help='Set config directory ' + '(empty for no config storage)') + parser.add_argument('-d', '--debug', help='Turn on debugging options.', + action='store_true') + parser.add_argument('command', nargs='*', help='Commands to execute ' + 'on startup.', metavar=':command') + # URLs will actually be in command + parser.add_argument('url', nargs='*', help='URLs to open on startup.') + return parser.parse_args() + + def _initlog(self): + """Initialisation of the logging output.""" + loglevel = 'debug' if self._args.debug else self._args.loglevel + numeric_level = getattr(logging, loglevel.upper(), None) + if not isinstance(numeric_level, int): + raise ValueError('Invalid log level: {}'.format(loglevel)) + logging.basicConfig( + level=numeric_level, + format='%(asctime)s [%(levelname)s] ' + '[%(module)s:%(funcName)s:%(lineno)s] %(message)s', + datefmt='%Y-%m-%d %H:%M:%S') + + def _initmisc(self): + """Initialize misc things.""" + if self._args.debug: + os.environ['QT_FATAL_WARNINGS'] = '1' + self.setApplicationName("qutebrowser") + self.setApplicationVersion(qutebrowser.__version__) + + def _init_cmds(self): + """Initialisation of the qutebrowser commands. + + Registers all commands, connects its signals, and sets up keyparser. + + """ + cmdutils.register_all() + for cmd in cmdutils.cmd_dict.values(): + cmd.signal.connect(self.cmd_handler) + try: + self.keyparser.from_config_sect(config.config['keybind']) + except KeyError: + pass + def _process_init_args(self): """Process initial positional args. @@ -158,6 +206,20 @@ class QuteBrowser(QApplication): for url in config.config.get('general', 'startpage').split(','): self.mainwindow.tabs.tabopen(url) + def _python_hacks(self): + """Get around some PyQt-oddities by evil hacks. + + This sets up the uncaught exception hook, quits with an appropriate + exit status, and handles Ctrl+C properly by passing control to the + Python interpreter once all 500ms. + + """ + signal(SIGINT, lambda *args: self.exit(128 + SIGINT)) + timer = QTimer() + timer.start(500) + timer.timeout.connect(lambda: None) + self._timers.append(timer) + def _recover_pages(self): """Try to recover all open pages. @@ -179,6 +241,15 @@ class QuteBrowser(QApplication): pass return pages + def _save_geometry(self): + """Save the window geometry to the state config.""" + geom = b64encode(bytes(self.mainwindow.saveGeometry())).decode('ASCII') + try: + config.state.add_section('geometry') + except configparser.DuplicateSectionError: + pass + config.state['geometry']['mainwindow'] = geom + def _exception_hook(self, exctype, excvalue, tb): """Handle uncaught python exceptions. @@ -246,123 +317,6 @@ class QuteBrowser(QApplication): logging.debug("maybe_quit quitting.") self.quit() - def _python_hacks(self): - """Get around some PyQt-oddities by evil hacks. - - This sets up the uncaught exception hook, quits with an appropriate - exit status, and handles Ctrl+C properly by passing control to the - Python interpreter once all 500ms. - - """ - signal(SIGINT, lambda *args: self.exit(128 + SIGINT)) - timer = QTimer() - timer.start(500) - timer.timeout.connect(lambda: None) - self._timers.append(timer) - - def _parseopts(self): - """Parse command line options.""" - parser = ArgumentParser("usage: %(prog)s [options]") - parser.add_argument('-l', '--log', dest='loglevel', - help='Set loglevel', default='info') - parser.add_argument('-c', '--confdir', help='Set config directory ' - '(empty for no config storage)') - parser.add_argument('-d', '--debug', help='Turn on debugging options.', - action='store_true') - parser.add_argument('command', nargs='*', help='Commands to execute ' - 'on startup.', metavar=':command') - # URLs will actually be in command - parser.add_argument('url', nargs='*', help='URLs to open on startup.') - return parser.parse_args() - - def _initlog(self): - """Initialisation of the logging output.""" - loglevel = 'debug' if self._args.debug else self._args.loglevel - numeric_level = getattr(logging, loglevel.upper(), None) - if not isinstance(numeric_level, int): - raise ValueError('Invalid log level: {}'.format(loglevel)) - logging.basicConfig( - level=numeric_level, - format='%(asctime)s [%(levelname)s] ' - '[%(module)s:%(funcName)s:%(lineno)s] %(message)s', - datefmt='%Y-%m-%d %H:%M:%S') - - def _initmisc(self): - """Initialize misc things.""" - if self._args.debug: - os.environ['QT_FATAL_WARNINGS'] = '1' - self.setApplicationName("qutebrowser") - self.setApplicationVersion(qutebrowser.__version__) - - def _init_cmds(self): - """Initialisation of the qutebrowser commands. - - Registers all commands, connects its signals, and sets up keyparser. - - """ - cmdutils.register_all() - for cmd in cmdutils.cmd_dict.values(): - cmd.signal.connect(self.cmd_handler) - try: - self.keyparser.from_config_sect(config.config['keybind']) - except KeyError: - pass - - @pyqtSlot() - def shutdown(self, do_quit=True): - """Try to shutdown everything cleanly. - - For some reason lastWindowClosing sometimes seem to get emitted twice, - so we make sure we only run once here. - - quit -- Whether to quit after shutting down. - - """ - if self._shutting_down: - return - self._shutting_down = True - logging.debug("Shutting down... (do_quit={})".format(do_quit)) - try: - config.config.save() - except AttributeError: - logging.exception("Could not save config.") - try: - self._save_geometry() - config.state.save() - except AttributeError: - logging.exception("Could not save window geometry.") - try: - if do_quit: - self.mainwindow.tabs.shutdown_complete.connect( - self.on_tab_shutdown_complete) - else: - self.mainwindow.tabs.shutdown_complete.connect( - functools.partial(self._maybe_quit, 'shutdown')) - self.mainwindow.tabs.shutdown() - except AttributeError: # mainwindow or tabs could still be None - logging.exception("No mainwindow/tabs to shut down.") - if do_quit: - self.quit() - - def _save_geometry(self): - """Save the window geometry to the state config.""" - geom = b64encode(bytes(self.mainwindow.saveGeometry())).decode('ASCII') - try: - config.state.add_section('geometry') - except configparser.DuplicateSectionError: - pass - config.state['geometry']['mainwindow'] = geom - - @pyqtSlot() - def on_tab_shutdown_complete(self): - """Quit application after a shutdown. - - Gets called when all tabs finished shutting down after shutdown(). - - """ - logging.debug("Shutdown complete, quitting.") - self.quit() - @pyqtSlot(tuple) def cmd_handler(self, tpl): """Handle commands and delegate the specific actions. @@ -438,3 +392,49 @@ class QuteBrowser(QApplication): """ raise Exception + + @pyqtSlot() + def shutdown(self, do_quit=True): + """Try to shutdown everything cleanly. + + For some reason lastWindowClosing sometimes seem to get emitted twice, + so we make sure we only run once here. + + quit -- Whether to quit after shutting down. + + """ + if self._shutting_down: + return + self._shutting_down = True + logging.debug("Shutting down... (do_quit={})".format(do_quit)) + try: + config.config.save() + except AttributeError: + logging.exception("Could not save config.") + try: + self._save_geometry() + config.state.save() + except AttributeError: + logging.exception("Could not save window geometry.") + try: + if do_quit: + self.mainwindow.tabs.shutdown_complete.connect( + self.on_tab_shutdown_complete) + else: + self.mainwindow.tabs.shutdown_complete.connect( + functools.partial(self._maybe_quit, 'shutdown')) + self.mainwindow.tabs.shutdown() + except AttributeError: # mainwindow or tabs could still be None + logging.exception("No mainwindow/tabs to shut down.") + if do_quit: + self.quit() + + @pyqtSlot() + def on_tab_shutdown_complete(self): + """Quit application after a shutdown. + + Gets called when all tabs finished shutting down after shutdown(). + + """ + logging.debug("Shutdown complete, quitting.") + self.quit() diff --git a/qutebrowser/commands/keys.py b/qutebrowser/commands/keys.py index 5fcc18b2e..0ed29624e 100644 --- a/qutebrowser/commands/keys.py +++ b/qutebrowser/commands/keys.py @@ -62,35 +62,6 @@ class KeyParser(QObject): self._bindings = {} self._modifier_bindings = {} - def from_config_sect(self, sect): - """Load keybindings from a ConfigParser section. - - Config format: key = command, e.g.: - gg = scrollstart - - """ - for (key, cmd) in sect.items(): - if key.startswith('@') and key.endswith('@'): - # normalize keystring - keystr = self._normalize_keystr(key.strip('@')) - logging.debug('registered mod key: {} -> {}'.format(keystr, - cmd)) - self._modifier_bindings[keystr] = cmd - else: - logging.debug('registered key: {} -> {}'.format(key, cmd)) - self._bindings[key] = cmd - - def handle(self, e): - """Handle a new keypress and call the respective handlers. - - e -- the KeyPressEvent from Qt - - """ - handled = self._handle_modifier_key(e) - if not handled: - self._handle_single_key(e) - self.keystring_updated.emit(self._keystring) - def _handle_modifier_key(self, e): """Handle a new keypress with modifiers. @@ -239,3 +210,32 @@ class KeyParser(QObject): cmdstr)) self.set_cmd_text.emit(':{} '.format(cmdstr)) return + + def from_config_sect(self, sect): + """Load keybindings from a ConfigParser section. + + Config format: key = command, e.g.: + gg = scrollstart + + """ + for (key, cmd) in sect.items(): + if key.startswith('@') and key.endswith('@'): + # normalize keystring + keystr = self._normalize_keystr(key.strip('@')) + logging.debug('registered mod key: {} -> {}'.format(keystr, + cmd)) + self._modifier_bindings[keystr] = cmd + else: + logging.debug('registered key: {} -> {}'.format(key, cmd)) + self._bindings[key] = cmd + + def handle(self, e): + """Handle a new keypress and call the respective handlers. + + e -- the KeyPressEvent from Qt + + """ + handled = self._handle_modifier_key(e) + if not handled: + self._handle_single_key(e) + self.keystring_updated.emit(self._keystring) diff --git a/qutebrowser/commands/utils.py b/qutebrowser/commands/utils.py index 4e20bfeec..f63c3f11d 100644 --- a/qutebrowser/commands/utils.py +++ b/qutebrowser/commands/utils.py @@ -69,24 +69,6 @@ class SearchParser(QObject): self._flags = 0 super().__init__(parent) - @pyqtSlot(str) - def search(self, text): - """Search for a text on a website. - - text -- The text to search for. - - """ - self._search(text) - - @pyqtSlot(str) - def search_rev(self, text): - """Search for a text on a website in reverse direction. - - text -- The text to search for. - - """ - self._search(text, rev=True) - def _search(self, text, rev=False): """Search for a text on the current page. @@ -106,6 +88,24 @@ class SearchParser(QObject): self._flags |= QWebPage.FindBackward self.do_search.emit(self._text, self._flags) + @pyqtSlot(str) + def search(self, text): + """Search for a text on a website. + + text -- The text to search for. + + """ + self._search(text) + + @pyqtSlot(str) + def search_rev(self, text): + """Search for a text on a website in reverse direction. + + text -- The text to search for. + + """ + self._search(text, rev=True) + def nextsearch(self, count=1): """Continue the search to the ([count]th) next term.""" if self._text is not None: diff --git a/qutebrowser/models/completion.py b/qutebrowser/models/completion.py index 7d2ab64a4..695e980a7 100644 --- a/qutebrowser/models/completion.py +++ b/qutebrowser/models/completion.py @@ -39,17 +39,6 @@ class CompletionModel(QAbstractItemModel): self._root = CompletionItem([""] * 2) self._id_map[id(self._root)] = self._root - def removeRows(self, position=0, count=1, parent=QModelIndex()): - """Remove rows from the model. - - Override QAbstractItemModel::removeRows. - - """ - node = self._node(parent) - self.beginRemoveRows(parent, position, position + count - 1) - node.children.pop(position) - self.endRemoveRows() - def _node(self, index): """Return the interal data representation for index. @@ -62,6 +51,60 @@ class CompletionModel(QAbstractItemModel): else: return self._root + def _get_marks(self, needle, haystack): + """Return the marks for needle in haystack.""" + pos1 = pos2 = 0 + marks = [] + if not needle: + return marks + while True: + pos1 = haystack.find(needle, pos2) + if pos1 == -1: + break + pos2 = pos1 + len(needle) + marks.append((pos1, pos2)) + return marks + + def mark_all_items(self, needle): + """Mark a string in all items (children of root-children). + + needle -- The string to mark. + + """ + for i in range(self.rowCount()): + cat = self.index(i, 0) + for k in range(self.rowCount(cat)): + idx = self.index(k, 0, cat) + old = self.data(idx).value() + marks = self._get_marks(needle, old) + self.setData(idx, marks, Qt.UserRole) + + def init_data(self, data): + """Initialize the Qt model based on the data given. + + data -- dict of data to process. + + """ + for (cat, items) in data.items(): + newcat = CompletionItem([cat], self._root) + self._id_map[id(newcat)] = newcat + self._root.children.append(newcat) + for item in items: + newitem = CompletionItem(item, newcat) + self._id_map[id(newitem)] = newitem + newcat.children.append(newitem) + + def removeRows(self, position=0, count=1, parent=QModelIndex()): + """Remove rows from the model. + + Override QAbstractItemModel::removeRows. + + """ + node = self._node(parent) + self.beginRemoveRows(parent, position, position + count - 1) + node.children.pop(position) + self.endRemoveRows() + def columnCount(self, parent=QModelIndex()): """Return the column count in the model. @@ -71,6 +114,23 @@ class CompletionModel(QAbstractItemModel): # pylint: disable=unused-argument return self._root.column_count() + def rowCount(self, parent=QModelIndex()): + """Return the children count of an item. + + Use the root frame if parent is invalid. + Override QAbstractItemModel::rowCount. + + """ + if parent.column() > 0: + return 0 + + if not parent.isValid(): + pitem = self._root + else: + pitem = self._id_map[parent.internalId()] + + return len(pitem.children) + def data(self, index, role=Qt.DisplayRole): """Return the data for role/index as QVariant. @@ -89,23 +149,6 @@ class CompletionModel(QAbstractItemModel): except (IndexError, ValueError): return QVariant() - def flags(self, index): - """Return the item flags for index. - - Return Qt.NoItemFlags on error. - Override QAbstractItemModel::flags. - - """ - # FIXME categories are not selectable, but moving via arrow keys still - # tries to select them - if not index.isValid(): - return Qt.NoItemFlags - flags = Qt.ItemIsEnabled - if len(self._id_map[index.internalId()].children) > 0: - return flags - else: - return flags | Qt.ItemIsSelectable - def headerData(self, section, orientation, role=Qt.DisplayRole): """Return the header data for role/index as QVariant. @@ -134,6 +177,23 @@ class CompletionModel(QAbstractItemModel): self.dataChanged.emit(index, index) return True + def flags(self, index): + """Return the item flags for index. + + Return Qt.NoItemFlags on error. + Override QAbstractItemModel::flags. + + """ + # FIXME categories are not selectable, but moving via arrow keys still + # tries to select them + if not index.isValid(): + return Qt.NoItemFlags + flags = Qt.ItemIsEnabled + if len(self._id_map[index.internalId()].children) > 0: + return flags + else: + return flags | Qt.ItemIsSelectable + def index(self, row, column, parent=QModelIndex()): """Return the QModelIndex for row/column/parent. @@ -174,23 +234,6 @@ class CompletionModel(QAbstractItemModel): return QModelIndex() return self.createIndex(item.row(), 0, id(item)) - def rowCount(self, parent=QModelIndex()): - """Return the children count of an item. - - Use the root frame if parent is invalid. - Override QAbstractItemModel::rowCount. - - """ - if parent.column() > 0: - return 0 - - if not parent.isValid(): - pitem = self._root - else: - pitem = self._id_map[parent.internalId()] - - return len(pitem.children) - def sort(self, column, order=Qt.AscendingOrder): """Sort the data in column according to order. @@ -200,49 +243,6 @@ class CompletionModel(QAbstractItemModel): """ raise NotImplementedError - def init_data(self, data): - """Initialize the Qt model based on the data given. - - data -- dict of data to process. - - """ - for (cat, items) in data.items(): - newcat = CompletionItem([cat], self._root) - self._id_map[id(newcat)] = newcat - self._root.children.append(newcat) - for item in items: - newitem = CompletionItem(item, newcat) - self._id_map[id(newitem)] = newitem - newcat.children.append(newitem) - - def mark_all_items(self, needle): - """Mark a string in all items (children of root-children). - - needle -- The string to mark. - - """ - for i in range(self.rowCount()): - cat = self.index(i, 0) - for k in range(self.rowCount(cat)): - idx = self.index(k, 0, cat) - old = self.data(idx).value() - marks = self._get_marks(needle, old) - self.setData(idx, marks, Qt.UserRole) - - def _get_marks(self, needle, haystack): - """Return the marks for needle in haystack.""" - pos1 = pos2 = 0 - marks = [] - if not needle: - return marks - while True: - pos1 = haystack.find(needle, pos2) - if pos1 == -1: - break - pos2 = pos1 + len(needle) - marks.append((pos1, pos2)) - return marks - class CompletionItem(): diff --git a/qutebrowser/models/completionfilter.py b/qutebrowser/models/completionfilter.py index 371784e68..c5a7bd954 100644 --- a/qutebrowser/models/completionfilter.py +++ b/qutebrowser/models/completionfilter.py @@ -71,10 +71,21 @@ class CompletionFilterModel(QSortFilterProxyModel): model -- The new source model. """ + # FIXME change this to a property self.setSourceModel(model) self.srcmodel = model self.pattern = '' + def first_item(self): + """Return the first item in the model.""" + cat = self.index(0, 0) + return self.index(0, 0, cat) + + def last_item(self): + """Return the last item in the model.""" + cat = self.index(self.rowCount() - 1, 0) + return self.index(self.rowCount(cat) - 1, 0, cat) + def filterAcceptsRow(self, row, parent): """Custom filter implementation. @@ -120,13 +131,3 @@ class CompletionFilterModel(QSortFilterProxyModel): return False else: return left < right - - def first_item(self): - """Return the first item in the model.""" - cat = self.index(0, 0) - return self.index(0, 0, cat) - - def last_item(self): - """Return the last item in the model.""" - cat = self.index(self.rowCount() - 1, 0) - return self.index(self.rowCount(cat) - 1, 0, cat) diff --git a/qutebrowser/utils/signals.py b/qutebrowser/utils/signals.py index b933c57ad..69ca4ab24 100644 --- a/qutebrowser/utils/signals.py +++ b/qutebrowser/utils/signals.py @@ -64,6 +64,10 @@ class SignalCache(QObject): self._uncached = uncached self._signal_dict = OrderedDict() + def _signal_needs_caching(self, signal): + """Return True if a signal should be cached, false otherwise.""" + return not signal_name(signal) in self._uncached + def add(self, sig, args): """Add a new signal to the signal cache. @@ -88,7 +92,3 @@ class SignalCache(QObject): for (signal, args) in self._signal_dict.values(): logging.debug('emitting {}'.format(dbg_signal(signal, args))) signal.emit(*args) - - def _signal_needs_caching(self, signal): - """Return True if a signal should be cached, false otherwise.""" - return not signal_name(signal) in self._uncached diff --git a/qutebrowser/utils/url.py b/qutebrowser/utils/url.py index 68069bc93..acc5f154c 100644 --- a/qutebrowser/utils/url.py +++ b/qutebrowser/utils/url.py @@ -27,6 +27,52 @@ from PyQt5.QtCore import QUrl import qutebrowser.utils.config as config +def _get_search_url(txt): + """Return a search engine URL (QUrl) for a text.""" + logging.debug('Finding search engine for "{}"'.format(txt)) + r = re.compile(r'(^|\s+)!(\w+)($|\s+)') + m = r.search(txt) + if m: + engine = m.group(2) + # FIXME why doesn't fallback work?! + template = config.config.get('searchengines', engine, fallback=None) + term = r.sub('', txt) + logging.debug('engine {}, term "{}"'.format(engine, term)) + else: + template = config.config.get('searchengines', 'DEFAULT', + fallback=None) + term = txt + logging.debug('engine: default, term "{}"'.format(txt)) + if template is None or not term: + raise ValueError + # pylint: disable=maybe-no-member + return QUrl.fromUserInput(template.format(urllib.parse.quote(term))) + + +def _is_url_naive(url): + """Naive check if given url (QUrl) is really an url.""" + PROTOCOLS = ['http://', 'https://'] + u = urlstring(url) + return (any(u.startswith(proto) for proto in PROTOCOLS) or '.' in u or + u == 'localhost') + + +def _is_url_dns(url): + """Check if an url (QUrl) is really an url via DNS.""" + # FIXME we could probably solve this in a nicer way by attempting to open + # the page in the webview, and then open the search if that fails. + host = url.host() + logging.debug("DNS request for {}".format(host)) + if not host: + return False + try: + socket.gethostbyname(host) + except socket.gaierror: + return False + else: + return True + + def qurl(url): """Get a QUrl from an url string.""" return url if isinstance(url, QUrl) else QUrl(url) @@ -64,28 +110,6 @@ def fuzzy_url(url): return newurl -def _get_search_url(txt): - """Return a search engine URL (QUrl) for a text.""" - logging.debug('Finding search engine for "{}"'.format(txt)) - r = re.compile(r'(^|\s+)!(\w+)($|\s+)') - m = r.search(txt) - if m: - engine = m.group(2) - # FIXME why doesn't fallback work?! - template = config.config.get('searchengines', engine, fallback=None) - term = r.sub('', txt) - logging.debug('engine {}, term "{}"'.format(engine, term)) - else: - template = config.config.get('searchengines', 'DEFAULT', - fallback=None) - term = txt - logging.debug('engine: default, term "{}"'.format(txt)) - if template is None or not term: - raise ValueError - # pylint: disable=maybe-no-member - return QUrl.fromUserInput(template.format(urllib.parse.quote(term))) - - def is_about_url(url): """Return True if url (QUrl) is an about:... or other special URL.""" return urlstring(url).replace('http://', '').startswith('about:') @@ -128,27 +152,3 @@ def is_url(url): return _is_url_naive(url) else: raise ValueError("Invalid autosearch value") - - -def _is_url_naive(url): - """Naive check if given url (QUrl) is really an url.""" - PROTOCOLS = ['http://', 'https://'] - u = urlstring(url) - return (any(u.startswith(proto) for proto in PROTOCOLS) or '.' in u or - u == 'localhost') - - -def _is_url_dns(url): - """Check if an url (QUrl) is really an url via DNS.""" - # FIXME we could probably solve this in a nicer way by attempting to open - # the page in the webview, and then open the search if that fails. - host = url.host() - logging.debug("DNS request for {}".format(host)) - if not host: - return False - try: - socket.gethostbyname(host) - except socket.gaierror: - return False - else: - return True diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 93a5fd147..3b518205d 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -28,6 +28,29 @@ from PyQt5.QtWebKit import qWebKitVersion import qutebrowser +def _git_str(): + """Try to find out git version and return a string if possible. + + Return None if there was an error or we're not in a git repo. + + """ + if hasattr(sys, "frozen"): + return None + try: + gitpath = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.path.pardir, os.path.pardir) + except NameError: + return None + if not os.path.isdir(os.path.join(gitpath, ".git")): + return None + try: + return subprocess.check_output( + ['git', 'describe', '--tags', '--dirty', '--always'], + cwd=gitpath).decode('UTF-8').strip() + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + def version(): """Return a string with various version informations.""" if sys.platform == 'linux': @@ -56,26 +79,3 @@ def version(): lines.append('\nGit commit: {}'.format(gitver)) return ''.join(lines) - - -def _git_str(): - """Try to find out git version and return a string if possible. - - Return None if there was an error or we're not in a git repo. - - """ - if hasattr(sys, "frozen"): - return None - try: - gitpath = os.path.join(os.path.dirname(os.path.realpath(__file__)), - os.path.pardir, os.path.pardir) - except NameError: - return None - if not os.path.isdir(os.path.join(gitpath, ".git")): - return None - try: - return subprocess.check_output( - ['git', 'describe', '--tags', '--dirty', '--always'], - cwd=gitpath).decode('UTF-8').strip() - except (subprocess.CalledProcessError, FileNotFoundError): - return None