diff --git a/.bzrignore b/.bzrignore
deleted file mode 100644
index d907fb5..0000000
--- a/.bzrignore
+++ /dev/null
@@ -1,13 +0,0 @@
-.git
-.git-backup
-.gitignore
-__pycache__
-MANIFEST
-build
-doc/sphinx/build
-side-view_ui.py
-test-repository/bzr-repo/
-test-repository/git-repo/repo-branches/
-test-repository/git-repo/repo-twisted-branches/
-test-repository/git-repo/repo-two-branches/
-test-repository/git-repo/repo1/
diff --git a/.gitignore b/.gitignore
index a588905..e7c50d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,33 +1,75 @@
*~
+.pycache
__pycache__
-build
-doc/sphinx/build
-#
-gui-prototypes/side-view/side-view_ui.py
-CodeReview/PatienceDiff/_patiencediff_c.cpython-34m.so
-bzr-backup/
-gui-prototypes/qlog.ui
-links/
-notes/bzr-qlog1.png
-notes/diffview.py
-notes/github1.png
-notes/github2.png
-notes/github3.png
-notes/github4.png
-notes/github5.png
-notes/github6.png
-notes/qbzr-qdiff.png
-notes/qbzr-qlog.png
-notes/qgit1.png
-notes/qgit2.png
-notes/qgit3.png
-notes/qgit4.png
-notes/qgit5.png
-notes/qgit6.png
-test-repository/
-test/Model.py
-test/github3-test.py
-test/swalign-fabrice/
-test/swalign-mbreese/
-test/token.txt
-tools/rsync-vps.sh
+.directory
+
+du.log
+nogit/
+__TRASH__/
+review.json
+
+CodeReview.egg-info/
+MANIFEST
+build/
+dist/
+
+doc/sphinx/build/
+doc/sphinx/source/api/
+
+src/CodeReview/GitHub/code/
+src/CodeReview/QmlApplication/rcc/CodeReviewRessource.py
+src/CodeReview/QmlApplication/rcc/code-review.rcc
+src/CodeReview/TextDistance/buggy.c
+src/CodeReview/ovh.txt
+src/CodeReview/share/icons/copy-icons.sh
+
+src/CodeReview/PatienceDiff/_patiencediff_c.cpython-314-x86_64-linux-gnu.so
+src/CodeReview/TextDistance/levenshtein_distance_c.cpython-314-x86_64-linux-gnu.so
+src/code_review.egg-info/
+
+doc/IDEA.md
+doc/git-gui.md
+doc/gitkraken/
+
+old/RELEASE-TEST.rst
+old/__BUGS__/
+old/gitkraken-id.txt
+old/gui-prototypes/qlog.ui
+old/gui-prototypes/side-view/side-view_ui.py
+old/notes/bzr-qlog1.png
+old/notes/codereview.png
+old/notes/diffview-link.txt
+old/notes/git-man-pages/
+old/notes/github1.png
+old/notes/github2.png
+old/notes/github3.png
+old/notes/github4.png
+old/notes/github5.png
+old/notes/github6.png
+old/notes/qbzr-qdiff.png
+old/notes/qbzr-qlog.png
+old/notes/qgit1.png
+old/notes/qgit2.png
+old/notes/qgit3.png
+old/notes/qgit4.png
+old/notes/qgit5.png
+old/notes/qgit6.png
+old/profile-pyqgit.sh
+old/test-failure.txt
+old/test-repository/bzr-repo/
+old/test-repository/git-repo/repo-branches/
+old/test-repository/git-repo/repo-twisted-branches/
+old/test-repository/git-repo/repo-two-branches/
+old/test-repository/git-repo/repo1/
+old/test/Model.py
+old/test/github3-test.py
+old/test/init-stage-test-repo.sh
+old/test/stage-test
+old/test/swalign-fabrice/
+old/test/swalign-mbreese/
+old/test/test-diff.py
+old/test/test-levenshtein-distance.py
+old/test/test-repo/
+old/test/token.txt
+old/tools/rsync-vps.sh
+old/tools/upload-www
diff --git a/CodeReview/Config/ConfigInstall.py b/CodeReview/Config/ConfigInstall.py
deleted file mode 100644
index e99a8b6..0000000
--- a/CodeReview/Config/ConfigInstall.py
+++ /dev/null
@@ -1,67 +0,0 @@
-####################################################################################################
-
-import os
-import sys
-
-####################################################################################################
-
-import CodeReview.Tools.Path as PathTools # due to Path class
-
-####################################################################################################
-
-_this_file = PathTools.to_absolute_path(__file__)
-
-class Path(object):
-
- CodeReview_module_directory = PathTools.parent_directory_of(_this_file, step=2)
- config_directory = os.path.dirname(_this_file)
-
- ##############################################
-
- @staticmethod
- def share_directory():
-
- path = os.path.dirname(Path.CodeReview_module_directory)
- if path.startswith(sys.exec_prefix):
- return os.path.join(sys.exec_prefix, 'share', 'CodeReview')
- else:
- return os.path.join(path, 'share')
-
-####################################################################################################
-
-class Logging(object):
-
- default_config_file = 'logging.yml'
- directories = (Path.config_directory,)
-
- ##############################################
-
- @staticmethod
- def find(config_file):
-
- return PathTools.find(config_file, Logging.directories)
-
-####################################################################################################
-
-class Icon(object):
-
- icon_directory = os.path.join(Path.share_directory(), 'icons')
-
- ##############################################
-
- @staticmethod
- def find(file_name, icon_size):
-
- if icon_size == 'svg':
- size_directory = icon_size
- else:
- size_directory = '{0}x{0}'.format(icon_size)
-
- icon_directory = os.path.join(Icon.icon_directory, size_directory)
- return PathTools.find(file_name, (icon_directory,))
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Diff/__init__.py b/CodeReview/Diff/__init__.py
deleted file mode 100644
index 90d9e46..0000000
--- a/CodeReview/Diff/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/Forms/__init__.py b/CodeReview/GUI/Forms/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/GUI/Forms/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/LogBrowser/LogBrowserApplication.py b/CodeReview/GUI/LogBrowser/LogBrowserApplication.py
deleted file mode 100644
index 2e2b84b..0000000
--- a/CodeReview/GUI/LogBrowser/LogBrowserApplication.py
+++ /dev/null
@@ -1,134 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-###################################################################################################
-
-import logging
-import os
-
-from PyQt5 import QtCore, QtWidgets
-
-####################################################################################################
-
-from .CommitTableModel import CommitTableModel
-from .LogTableModel import LogTableModel
-from CodeReview.Application.ApplicationBase import ApplicationBase
-from CodeReview.GUI.Base.GuiApplicationBase import GuiApplicationBase
-from CodeReview.Repository.Git import RepositoryNotFound, GitRepository
-
-####################################################################################################
-
-class LogBrowserApplication(GuiApplicationBase, ApplicationBase):
-
- _logger = logging.getLogger(__name__)
-
- ###############################################
-
- def __init__(self, args):
-
- super(LogBrowserApplication, self).__init__(args=args)
- self._logger.debug(str(args))
-
- from .LogBrowserMainWindow import LogBrowserMainWindow
- self._main_window = LogBrowserMainWindow()
- self._main_window.showMaximized()
-
- self.post_init()
-
- ##############################################
-
- def _init_actions(self):
-
- super(LogBrowserApplication, self)._init_actions()
-
- ##############################################
-
- def post_init(self):
-
- super(LogBrowserApplication, self).post_init()
- self._init_repository()
-
- ##############################################
-
- def show_message(self, message=None, timeout=0, warn=False):
-
- """ Hides the normal status indications and displays the given message for the specified
- number of milli-seconds (timeout). If timeout is 0 (default), the message remains displayed
- until the clearMessage() slot is called or until the showMessage() slot is called again to
- change the message.
-
- Note that showMessage() is called to show temporary explanations of tool tip texts, so
- passing a timeout of 0 is not sufficient to display a permanent message.
- """
-
- self._main_window.show_message(message, timeout, warn)
-
- ##############################################
-
- def _init_repository(self):
-
- if self._args.path is None:
- path = os.getcwd()
- else:
- path = self._args.path
- try:
- self._repository = GitRepository(path)
- except RepositoryNotFound:
- self.show_message("Any Git repository was found in path {}".format(path), warn=True)
- self._repository = None
- return
-
- self._log_table_model = LogTableModel(self._repository)
- log_table = self._main_window._log_table
- log_table.setModel(self._log_table_model)
- # Set the column widths
- column_enum = self._log_table_model.column_enum
- width = 0
- for column in (
- column_enum.revision,
- # column_enum.message,
- column_enum.date,
- column_enum.comitter,
- ):
- log_table.resizeColumnToContents(int(column))
- width += log_table.columnWidth(int(column))
- width = log_table.width() - width
- width *= .9 # Fixme: subtract spaces ...
- log_table.setColumnWidth(int(column_enum.message), width)
-
- self._commit_table_model = CommitTableModel()
- commit_table = self._main_window._commit_table
- commit_table.setModel(self._commit_table_model)
-
- ##############################################
-
- def reload_repository(self):
-
- self._init_repository()
-
- ##############################################
-
- @property
- def repository(self):
- return self._repository
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/LogBrowser/LogBrowserMainWindow.py b/CodeReview/GUI/LogBrowser/LogBrowserMainWindow.py
deleted file mode 100644
index bd7942a..0000000
--- a/CodeReview/GUI/LogBrowser/LogBrowserMainWindow.py
+++ /dev/null
@@ -1,324 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-
-import logging
-
-from PyQt5 import QtWidgets
-from PyQt5.QtCore import Qt
-
-import pygit2 as git
-
-####################################################################################################
-
-from CodeReview.GUI.Base.MainWindowBase import MainWindowBase
-from CodeReview.GUI.Widgets.IconLoader import IconLoader
-from CodeReview.GUI.Widgets.MessageBox import MessageBox
-
-####################################################################################################
-
-_module_logger = logging.getLogger(__name__)
-
-####################################################################################################
-
-class LogBrowserMainWindow(MainWindowBase):
-
- _logger = _module_logger.getChild('LogBrowserMainWindow')
-
- ##############################################
-
- def __init__(self, parent=None):
-
- super(LogBrowserMainWindow, self).__init__(title='CodeReview Log Browser', parent=parent)
-
- self._current_revision = None
- self._diff = None
- self._current_patch = None
- self._diff_window = None
-
- self._init_ui()
- self._create_actions()
- self._create_toolbar()
-
- icon_loader = IconLoader()
- self.setWindowIcon(icon_loader['code-review@svg'])
-
- ##############################################
-
- def _init_ui(self):
-
- self._central_widget = QtWidgets.QWidget(self)
- self.setCentralWidget(self._central_widget)
-
- self._vertical_layout = QtWidgets.QVBoxLayout(self._central_widget)
- self._message_box = MessageBox(self)
- splitter = QtWidgets.QSplitter()
- splitter.setOrientation(Qt.Vertical)
- self._log_table = QtWidgets.QTableView()
- self._commit_table = QtWidgets.QTableView()
-
- for widget in (self._message_box, splitter):
- self._vertical_layout.addWidget(widget)
- for widget in (self._log_table, self._commit_table):
- splitter.addWidget(widget)
-
- table = self._log_table
- table.setSelectionMode(QtWidgets.QTableView.SingleSelection)
- table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
- table.verticalHeader().setVisible(False)
- table.setShowGrid(False)
- # table.setSortingEnabled(True)
- table.clicked.connect(self._update_commit_table)
-
- table = self._commit_table
- table.setSelectionMode(QtWidgets.QTableView.SingleSelection)
- table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
- table.verticalHeader().setVisible(False)
- table.setShowGrid(False)
- table.setSortingEnabled(True)
- table.clicked.connect(self._show_patch)
-
- # horizontal_header = table_view.horizontalHeader()
- # horizontal_header.setMovable(True)
-
- ##############################################
-
- def _create_actions(self):
-
- # icon_loader = IconLoader()
-
- self._stagged_mode_action = \
- QtWidgets.QAction('Stagged',
- self,
- toolTip='Stagged Mode',
- shortcut='Ctrl+1',
- checkable=True,
- )
-
- self._not_stagged_mode_action = \
- QtWidgets.QAction('Not Stagged',
- self,
- toolTip='Not Stagged Mode',
- shortcut='Ctrl+2',
- checkable=True,
- )
-
- self._all_change_mode_action = \
- QtWidgets.QAction('All',
- self,
- toolTip='All Mode',
- shortcut='Ctrl+3',
- checkable=True,
- )
-
- self._action_group = QtWidgets.QActionGroup(self)
- self._action_group.triggered.connect(self._update_working_tree_diff)
- for action in (self._all_change_mode_action,
- self._stagged_mode_action,
- self._not_stagged_mode_action,
- ):
- self._action_group.addAction(action)
- self._all_change_mode_action.setChecked(True)
-
- self._reload_action = \
- QtWidgets.QAction('Reload',
- self,
- toolTip='Reload',
- shortcut='Ctrl+4'
- )
- self._reload_action.triggered.connect(self._reload_repository)
-
- ##############################################
-
- def _create_toolbar(self):
-
- self._tool_bar = self.addToolBar('Diff on Working Tree')
- for item in self._action_group.actions():
- self._tool_bar.addAction(item)
- for item in (self._reload_action,):
- self._tool_bar.addAction(item)
-
- ##############################################
-
- def init_menu(self):
-
- super(LogBrowserMainWindow, self).init_menu()
-
- ##############################################
-
- def show_message(self, message=None, timeout=0, warn=False):
-
- """ Hides the normal status indications and displays the given message for the specified
- number of milli-seconds (timeout). If timeout is 0 (default), the message remains displayed
- until the clearMessage() slot is called or until the showMessage() slot is called again to
- change the message.
-
- Note that showMessage() is called to show temporary explanations of tool tip texts, so
- passing a timeout of 0 is not sufficient to display a permanent message.
- """
-
- if warn:
- self._message_box.push_message(message)
- else:
- status_bar = self.statusBar()
- if message is None:
- status_bar.clearMessage()
- else:
- status_bar.showMessage(message, timeout)
-
- ##############################################
-
- def _reload_repository(self):
-
- self._application.reload_repository()
- self._update_working_tree_diff()
-
- ##############################################
-
- def _update_working_tree_diff(self):
-
- if self._log_table.currentIndex().row() == 0:
- self._update_commit_table()
-
- ##############################################
-
- def _update_commit_table(self, index=None):
-
- if index is not None:
- index = index.row()
- else:
- index = 0
-
- if index:
- self._current_revision = index
- log_table_model = self._log_table.model()
- commit1 = log_table_model[index]
- try:
- commit2 = log_table_model[index +1]
- kwargs = dict(a=commit2, b=commit1) # Fixme:
- except IndexError:
- kwargs = dict(a=commit1)
- else: # working directory
- self._current_revision = None
- if self._stagged_mode_action.isChecked():
- # Changes between the index and your last commit
- kwargs = dict(a='HEAD', cached=True)
- elif self._not_stagged_mode_action.isChecked():
- # Changes in the working tree not yet staged for the next commit
- kwargs = {}
- elif self._all_change_mode_action.isChecked():
- # Changes in the working tree since your last commit
- kwargs = dict(a='HEAD')
-
- self._diff = self._application.repository.diff(**kwargs)
-
- commit_table_model = self._commit_table.model()
- commit_table_model.update(self._diff)
- self._commit_table.resizeColumnsToContents()
-
- ##############################################
-
- def _show_patch(self, index):
-
- self._current_patch = index.row()
- self._show_current_patch()
-
- ##############################################
-
- def _on_diff_window_closed(self):
-
- self._diff_window = None
-
- ##############################################
-
- @property
- def current_patch(self):
- return self._current_patch
-
- ##############################################
-
- @property
- def number_of_patches(self):
- return len(self._diff)
-
- ##############################################
-
- def _show_current_patch(self):
-
- if self._diff_window is None:
- from CodeReview.GUI.DiffViewer.DiffViewerMainWindow import DiffViewerMainWindow
- self._diff_window = DiffViewerMainWindow(self)
- self._diff_window.closed.connect(self._on_diff_window_closed)
- self._diff_window.showMaximized()
-
- patch = self._diff[self._current_patch]
- delta = patch.delta
- if not delta.is_binary:
- self._logger.info('revision {} '.format(self._current_revision) + delta.new_file.path)
- # print(delta.status, delta.similarity, delta.additions, delta.deletions, delta.is_binary)
- # for hunk in delta.hunks:
- # print(hunk.old_start, hunk.old_lines, hunk.new_start, hunk.new_lines, hunk.lines)
- repository = self._application.repository
- if delta.status in (git.GIT_DELTA_MODIFIED, git.GIT_DELTA_RENAMED):
- paths = (delta.old_file.path, delta.new_file.path)
- elif delta.status == git.GIT_DELTA_ADDED:
- paths = (None, delta.new_file.path)
- elif delta.status == git.GIT_DELTA_DELETED:
- paths = (delta.old_file.path, None)
- texts = [repository.file_content(blob_id)
- for blob_id in (delta.old_file.id, delta.new_file.id)]
- metadatas = [dict(path=delta.old_file.path, document_type='file', last_modification_date=None),
- dict(path=delta.new_file.path, document_type='file', last_modification_date=None)]
- self._diff_window.diff_documents(paths, texts, metadatas, workdir=repository.workdir)
- else:
- self._logger.info('revision {} Binary '.format(self._current_revision) + delta.new_file.path)
- # Fixme: show image pdf ...
-
- ##############################################
-
- @property
- def _last_path_index(self):
-
- return len(self._diff) -1
-
- ##############################################
-
- def previous_patch(self):
-
- if self._current_patch >= 1:
- self._current_patch -= 1
- else:
- self._current_patch = self._last_path_index
- self._show_current_patch()
-
- ##############################################
-
- def next_patch(self):
-
- if self._current_patch < self._last_path_index:
- self._current_patch += 1
- else:
- self._current_patch = 0
- self._show_current_patch()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/LogBrowser/__init__.py b/CodeReview/GUI/LogBrowser/__init__.py
deleted file mode 100644
index 90d9e46..0000000
--- a/CodeReview/GUI/LogBrowser/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/Widgets/__init__.py b/CodeReview/GUI/Widgets/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/GUI/Widgets/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/GUI/__init__.py b/CodeReview/GUI/__init__.py
deleted file mode 100644
index 90d9e46..0000000
--- a/CodeReview/GUI/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Logging/__init__.py b/CodeReview/Logging/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/Logging/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Math/__init__.py b/CodeReview/Math/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/Math/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Repository/Git.py b/CodeReview/Repository/Git.py
deleted file mode 100644
index 28ee654..0000000
--- a/CodeReview/Repository/Git.py
+++ /dev/null
@@ -1,136 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-
-import logging
-import os
-
-import pygit2 as git
-
-####################################################################################################
-
-_module_logger = logging.getLogger(__name__)
-
-####################################################################################################
-
-class RepositoryNotFound(Exception):
- pass
-
-####################################################################################################
-
-class GitRepository(object):
-
- _logger = _module_logger.getChild('GitRepository')
-
- ##############################################
-
- def __init__(self, path):
-
- path = os.path.realpath(path)
-
- try:
- repository_path = git.discover_repository(path)
- self._repository = git.Repository(repository_path)
- workdir = self._repository.workdir
- if os.path.isdir(path) and not path.endswith(os.sep):
- path += os.sep
- self._path_filter = path.replace(workdir, '')
- self._logger.info('\nPath %s\nWork Dir: %s\nPath Filter: %s',
- path, workdir, self._path_filter)
- except KeyError:
- raise RepositoryNotFound
-
- ##############################################
-
- @property
- def workdir(self):
- return self._repository.workdir
-
- ##############################################
-
- def commits(self):
-
- head = self._repository.head
- head_commit = self._repository[head.target]
- commits = [commit
- for commit in self._repository.walk(head_commit.id, git.GIT_SORT_TIME)]
-
- return commits
-
- ##############################################
-
- def diff(self, a=None, b=None, cached=False, path_filter=None):
-
- if path_filter is None:
- path_filter = self._path_filter
-
- patches = []
-
- # GIT_DIFF_PATIENCE
- diff = self._repository.diff(a=a, b=b, cached=cached)
- diff.find_similar()
- # flags, rename_threshold, copy_threshold, rename_from_rewrite_threshold, break_rewrite_threshold, rename_limit
- for patch in diff:
- delta = patch.delta
- if path_filter and not delta.old_file.path.startswith(path_filter):
- # self._logger.info('Skip ' + delta.old_file.path)
- continue
- patches.append(patch)
-
- return Diff(diff, patches)
-
- ##############################################
-
- def file_content(self, oid):
- try:
- return self._repository[oid].data.decode('utf-8')
- except KeyError:
- return None
-
-####################################################################################################
-
-class Diff(object):
-
- ##############################################
-
- def __init__(self, diff, patches):
-
- self.diff = diff
- self._patches = patches
-
- ##############################################
-
- def __len__(self):
- return len(self._patches)
-
- ##############################################
-
- def __iter__(self):
- return iter(self._patches)
-
- ##############################################
-
- def __getitem__(self, i):
- return self._patches[i]
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Repository/__init__.py b/CodeReview/Repository/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/Repository/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Tools/__init__.py b/CodeReview/Tools/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/Tools/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/Version.py b/CodeReview/Version.py
deleted file mode 100644
index c30d37f..0000000
--- a/CodeReview/Version.py
+++ /dev/null
@@ -1,39 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-
-# cf. http://en.wikipedia.org/wiki/Software_versioning
-
-####################################################################################################
-
-from CodeReview.Tools.RevisionVersion import RevisionVersion
-
-####################################################################################################
-
-CodeReview = RevisionVersion({'major':0,
- 'minor':1,
- 'revision':0,
- 'suffix':'',
- })
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/CodeReview/__init__.py b/CodeReview/__init__.py
deleted file mode 100644
index 395dbe6..0000000
--- a/CodeReview/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/MANIFEST.in b/MANIFEST.in
index 3e5c4ba..3550c33 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,32 +1,23 @@
-include .bzrignore
include .gitignore
include .pyflymakerc
include .travis.yml
-include CHANGES.txt
include GPL-V3.0.txt
-include LICENSE.txt
include MANIFEST.in
include README.html
include README.rst
include README.txt
-include make-release.sh
-include pylintrc.ini
include requirements.txt
-include setenv.sh start.sh
include setup_data.py
-include test-repository/git-repo/*.sh
-recursive-include CodeReview *.py *.yml *.c *.h *.ui Makefile.pyqt .in
-recursive-include bin
+recursive-include CodeReview *.py *.yml *.c *.h *.ui Makefile.pyqt *.png *.svg
recursive-include doc *
recursive-include doc/sphinx/source/_static *
recursive-include gh-pages *
-recursive-include share *.png *.svg
recursive-include spec *.spec *.desktop
-recursive-include tools *
recursive-include unit-test *.py
recursive-include unit-test/data *.txt
-prune *~ *.pyc
+prune *~
+prune *.pyc
prune build
prune doc/sphinx/build
-prune test-repository/bzr-repo/
-prune test-repository/git-repo/
+prune tasks
+prune QtShim
diff --git a/README.html b/README.html
index f147dfd..222b9f1 100644
--- a/README.html
+++ b/README.html
@@ -3,13 +3,13 @@
-
-
+
+README.rst
\ No newline at end of file
diff --git a/src/CodeReview/share/icons/32x32/go-next.svg b/src/CodeReview/share/icons/32x32/go-next.svg
new file mode 100644
index 0000000..ec1545d
--- /dev/null
+++ b/src/CodeReview/share/icons/32x32/go-next.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/CodeReview/share/icons/32x32/go-previous.svg b/src/CodeReview/share/icons/32x32/go-previous.svg
new file mode 100644
index 0000000..fbdfb7a
--- /dev/null
+++ b/src/CodeReview/share/icons/32x32/go-previous.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/CodeReview/share/icons/32x32/media-playlist-repeat.svg b/src/CodeReview/share/icons/32x32/media-playlist-repeat.svg
new file mode 100644
index 0000000..0e3071b
--- /dev/null
+++ b/src/CodeReview/share/icons/32x32/media-playlist-repeat.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/CodeReview/share/icons/32x32/view-refresh.svg b/src/CodeReview/share/icons/32x32/view-refresh.svg
new file mode 100644
index 0000000..c60eef3
--- /dev/null
+++ b/src/CodeReview/share/icons/32x32/view-refresh.svg
@@ -0,0 +1,13 @@
+
diff --git a/share/icons/48x48/dialog-warning.png b/src/CodeReview/share/icons/48x48/dialog-warning.png
similarity index 100%
rename from share/icons/48x48/dialog-warning.png
rename to src/CodeReview/share/icons/48x48/dialog-warning.png
diff --git a/share/icons/code-review-250.png b/src/CodeReview/share/icons/code-review-250.png
similarity index 100%
rename from share/icons/code-review-250.png
rename to src/CodeReview/share/icons/code-review-250.png
diff --git a/share/icons/svg/align-mode.svg b/src/CodeReview/share/icons/svg/align-mode.svg
similarity index 70%
rename from share/icons/svg/align-mode.svg
rename to src/CodeReview/share/icons/svg/align-mode.svg
index 5d08521..587cb44 100644
--- a/share/icons/svg/align-mode.svg
+++ b/src/CodeReview/share/icons/svg/align-mode.svg
@@ -9,12 +9,12 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="1000"
- height="1000"
+ width="1066.6666"
+ height="1066.6666"
viewBox="0 0 999.99996 999.99996"
id="svg4136"
version="1.1"
- inkscape:version="0.91 r13725"
+ inkscape:version="0.92.1 r"
sodipodi:docname="align-mode.svg">
@@ -63,7 +63,7 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.626"
- inkscape:cx="500"
+ inkscape:cx="40.734824"
inkscape:cy="500"
inkscape:document-units="px"
inkscape:current-layer="layer1"
@@ -71,10 +71,10 @@
units="px"
width="500mm"
inkscape:snap-grids="true"
- inkscape:window-width="1600"
- inkscape:window-height="853"
- inkscape:window-x="-2"
- inkscape:window-y="-3"
+ inkscape:window-width="1920"
+ inkscape:window-height="1007"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-text-baseline="true"
objecttolerance="10000"
@@ -87,7 +87,9 @@
id="grid4823"
spacingx="4.9999998"
spacingy="4.9999998"
- empspacing="1" />
+ empspacing="1"
+ originx="0"
+ originy="0" />
@@ -97,7 +99,7 @@
image/svg+xml
-
+
@@ -107,7 +109,7 @@
id="layer1"
transform="translate(0,-52.36226)">
+ y="-156.09781"
+ style="font-size:20px;line-height:1.25">
A
+ y="542.39453"
+ style="font-size:474.11590576px;line-height:1.25">A
A
+ y="542.39447"
+ style="font-size:474.11590576px;line-height:1.25">A
B
+ y="981.72424"
+ style="font-size:498.26498413px;line-height:1.20000005">B
diff --git a/share/icons/svg/code-review.svg b/src/CodeReview/share/icons/svg/code-review.svg
similarity index 100%
rename from share/icons/svg/code-review.svg
rename to src/CodeReview/share/icons/svg/code-review.svg
diff --git a/share/icons/svg/complete-mode.svg b/src/CodeReview/share/icons/svg/complete-mode.svg
similarity index 52%
rename from share/icons/svg/complete-mode.svg
rename to src/CodeReview/share/icons/svg/complete-mode.svg
index 73c55d4..45155fe 100644
--- a/share/icons/svg/complete-mode.svg
+++ b/src/CodeReview/share/icons/svg/complete-mode.svg
@@ -9,12 +9,12 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="1000"
- height="1000"
+ width="1066.6666"
+ height="1066.6666"
viewBox="0 0 999.99996 999.99996"
id="svg4136"
version="1.1"
- inkscape:version="0.91 r13725"
+ inkscape:version="0.92.1 r"
sodipodi:docname="complete-mode.svg">
@@ -46,19 +46,19 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="0.626"
- inkscape:cx="492.8115"
- inkscape:cy="500"
+ inkscape:zoom="0.74343753"
+ inkscape:cx="533.33331"
+ inkscape:cy="533.33331"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
width="500mm"
inkscape:snap-grids="true"
- inkscape:window-width="1600"
- inkscape:window-height="853"
- inkscape:window-x="-2"
- inkscape:window-y="-3"
+ inkscape:window-width="1920"
+ inkscape:window-height="1007"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-text-baseline="true"
objecttolerance="10000"
@@ -70,7 +70,9 @@
id="grid4823"
spacingx="9.9999996"
spacingy="9.9999996"
- empspacing="1" />
+ empspacing="1"
+ originx="0"
+ originy="0" />
@@ -80,7 +82,7 @@
image/svg+xml
-
+
@@ -90,56 +92,59 @@
id="layer1"
transform="translate(0,-52.36226)">
+ width="419.99997"
+ height="480"
+ x="40"
+ y="112.36226"
+ ry="0" />
+ y="-156.09781"
+ style="font-size:20px;line-height:1.25">
C
+ id="tspan4908"
+ x="83.626762"
+ y="553.81818"
+ style="font-size:552.68743896px;line-height:1.25">C
+ width="419.42871"
+ height="479.99997"
+ x="540"
+ y="512.36224" />
C
+ id="tspan4910"
+ x="583.62671"
+ y="953.81812"
+ style="font-size:552.68743896px;line-height:1.25">C
diff --git a/src/CodeReview/share/icons/svg/edit-delete.svg b/src/CodeReview/share/icons/svg/edit-delete.svg
new file mode 120000
index 0000000..39d3d8a
--- /dev/null
+++ b/src/CodeReview/share/icons/svg/edit-delete.svg
@@ -0,0 +1 @@
+../32x32/edit-delete.svg
\ No newline at end of file
diff --git a/src/CodeReview/share/icons/svg/go-next.svg b/src/CodeReview/share/icons/svg/go-next.svg
new file mode 120000
index 0000000..04c9bfa
--- /dev/null
+++ b/src/CodeReview/share/icons/svg/go-next.svg
@@ -0,0 +1 @@
+../32x32/go-next.svg
\ No newline at end of file
diff --git a/src/CodeReview/share/icons/svg/go-previous.svg b/src/CodeReview/share/icons/svg/go-previous.svg
new file mode 120000
index 0000000..b13fdba
--- /dev/null
+++ b/src/CodeReview/share/icons/svg/go-previous.svg
@@ -0,0 +1 @@
+../32x32/go-previous.svg
\ No newline at end of file
diff --git a/share/icons/svg/highlight.svg b/src/CodeReview/share/icons/svg/highlight.svg
similarity index 100%
rename from share/icons/svg/highlight.svg
rename to src/CodeReview/share/icons/svg/highlight.svg
diff --git a/share/icons/svg/line-number-mode.svg b/src/CodeReview/share/icons/svg/line-number-mode.svg
similarity index 62%
rename from share/icons/svg/line-number-mode.svg
rename to src/CodeReview/share/icons/svg/line-number-mode.svg
index 63bcade..79a5239 100644
--- a/share/icons/svg/line-number-mode.svg
+++ b/src/CodeReview/share/icons/svg/line-number-mode.svg
@@ -9,13 +9,13 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="1000"
- height="1000"
+ width="1066.6666"
+ height="1066.6666"
viewBox="0 0 999.99996 999.99996"
id="svg4136"
version="1.1"
- inkscape:version="0.91 r13725"
- sodipodi:docname="line-number.svg">
+ inkscape:version="0.92.1 r"
+ sodipodi:docname="line-number-mode.svg">
+ empspacing="1"
+ originx="0"
+ originy="0" />
@@ -107,7 +109,7 @@
id="layer1"
transform="translate(0,-52.36226)">
+ y="-156.09781"
+ style="font-size:20px;line-height:1.25">
1
+ id="tspan5538"
+ x="87.017891"
+ y="542.39453"
+ style="font-size:474.11245728px;line-height:1.25">1
A
+ y="542.39447"
+ style="font-size:474.11590576px;line-height:1.25">A
B
+ y="981.72424"
+ style="font-size:498.26498413px;line-height:1.20000005">B
2
+ id="tspan5558"
+ x="100.16022"
+ y="934.14233"
+ style="font-size:474.11245728px;line-height:1.25">2
diff --git a/src/CodeReview/share/icons/svg/media-playlist-repeat.svg b/src/CodeReview/share/icons/svg/media-playlist-repeat.svg
new file mode 120000
index 0000000..47a4244
--- /dev/null
+++ b/src/CodeReview/share/icons/svg/media-playlist-repeat.svg
@@ -0,0 +1 @@
+../32x32/media-playlist-repeat.svg
\ No newline at end of file
diff --git a/src/CodeReview/share/icons/svg/view-refresh.svg b/src/CodeReview/share/icons/svg/view-refresh.svg
new file mode 120000
index 0000000..7fa865d
--- /dev/null
+++ b/src/CodeReview/share/icons/svg/view-refresh.svg
@@ -0,0 +1 @@
+../32x32/view-refresh.svg
\ No newline at end of file
diff --git a/src/QtShim/QtConfig.py b/src/QtShim/QtConfig.py
new file mode 100644
index 0000000..e1b09db
--- /dev/null
+++ b/src/QtShim/QtConfig.py
@@ -0,0 +1,745 @@
+####################################################################################################
+
+"""Common members of all bindings
+
+This is where each member of Qt.py is explicitly defined.
+It is based on a 'lowest common denominator' of all bindings;
+including members found in each of the 4 bindings.
+
+The '_common_members' dictionary is generated using the
+build_membership.sh script.
+
+"""
+
+_common_members = {
+ 'QtCore': [
+ 'QAbstractAnimation',
+ 'QAbstractEventDispatcher',
+ 'QAbstractItemModel',
+ 'QAbstractListModel',
+ 'QAbstractState',
+ 'QAbstractTableModel',
+ 'QAbstractTransition',
+ 'QAnimationGroup',
+ 'QBasicTimer',
+ 'QBitArray',
+ 'QBuffer',
+ 'QByteArray',
+ 'QByteArrayMatcher',
+ 'QChildEvent',
+ 'QCoreApplication',
+ 'QCryptographicHash',
+ 'QDataStream',
+ 'QDate',
+ 'QDateTime',
+ 'QDir',
+ 'QDirIterator',
+ 'QDynamicPropertyChangeEvent',
+ 'QEasingCurve',
+ 'QElapsedTimer',
+ 'QEvent',
+ 'QEventLoop',
+ 'QEventTransition',
+ 'QFile',
+ 'QFileInfo',
+ 'QFileSystemWatcher',
+ 'QFinalState',
+ 'QGenericArgument',
+ 'QGenericReturnArgument',
+ 'QHistoryState',
+ 'QItemSelectionRange',
+ 'QIODevice',
+ 'QLibraryInfo',
+ 'QLine',
+ 'QLineF',
+ 'QLocale',
+ 'QMargins',
+ 'QMetaClassInfo',
+ 'QMetaEnum',
+ 'QMetaMethod',
+ 'QMetaObject',
+ 'QMetaProperty',
+ 'QMimeData',
+ 'QModelIndex',
+ 'QMutex',
+ 'QMutexLocker',
+ 'QObject',
+ 'QParallelAnimationGroup',
+ 'QPauseAnimation',
+ 'QPersistentModelIndex',
+ 'QPluginLoader',
+ 'QPoint',
+ 'QPointF',
+ 'QProcess',
+ 'QProcessEnvironment',
+ 'QPropertyAnimation',
+ 'QReadLocker',
+ 'QReadWriteLock',
+ 'QRect',
+ 'QRectF',
+ 'QRegExp',
+ 'QResource',
+ 'QRunnable',
+ 'QSemaphore',
+ 'QSequentialAnimationGroup',
+ 'QSettings',
+ 'QSignalMapper',
+ 'QSignalTransition',
+ 'QSize',
+ 'QSizeF',
+ 'QSocketNotifier',
+ 'QState',
+ 'QStateMachine',
+ 'QSysInfo',
+ 'QSystemSemaphore',
+ 'QT_TRANSLATE_NOOP',
+ 'QT_TR_NOOP',
+ 'QT_TR_NOOP_UTF8',
+ 'QTemporaryFile',
+ 'QTextBoundaryFinder',
+ 'QTextCodec',
+ 'QTextDecoder',
+ 'QTextEncoder',
+ 'QTextStream',
+ 'QTextStreamManipulator',
+ 'QThread',
+ 'QThreadPool',
+ 'QTime',
+ 'QTimeLine',
+ 'QTimer',
+ 'QTimerEvent',
+ 'QTranslator',
+ 'QUrl',
+ 'QVariantAnimation',
+ 'QWaitCondition',
+ 'QWriteLocker',
+ 'QXmlStreamAttribute',
+ 'QXmlStreamAttributes',
+ 'QXmlStreamEntityDeclaration',
+ 'QXmlStreamEntityResolver',
+ 'QXmlStreamNamespaceDeclaration',
+ 'QXmlStreamNotationDeclaration',
+ 'QXmlStreamReader',
+ 'QXmlStreamWriter',
+ 'Qt',
+ 'QtCriticalMsg',
+ 'QtDebugMsg',
+ 'QtFatalMsg',
+ 'QtMsgType',
+ 'QtSystemMsg',
+ 'QtWarningMsg',
+ 'qAbs',
+ 'qAddPostRoutine',
+ 'qChecksum',
+ 'qCritical',
+ 'qDebug',
+ 'qFatal',
+ 'qFuzzyCompare',
+ 'qIsFinite',
+ 'qIsInf',
+ 'qIsNaN',
+ 'qIsNull',
+ 'qRegisterResourceData',
+ 'qUnregisterResourceData',
+ 'qVersion',
+ 'qWarning',
+ 'qrand',
+ 'qsrand'
+ ],
+ 'QtGui': [
+ 'QAbstractTextDocumentLayout',
+ 'QActionEvent',
+ 'QBitmap',
+ 'QBrush',
+ 'QClipboard',
+ 'QCloseEvent',
+ 'QColor',
+ 'QConicalGradient',
+ 'QContextMenuEvent',
+ 'QCursor',
+ 'QDesktopServices',
+ 'QDoubleValidator',
+ 'QDrag',
+ 'QDragEnterEvent',
+ 'QDragLeaveEvent',
+ 'QDragMoveEvent',
+ 'QDropEvent',
+ 'QFileOpenEvent',
+ 'QFocusEvent',
+ 'QFont',
+ 'QFontDatabase',
+ 'QFontInfo',
+ 'QFontMetrics',
+ 'QFontMetricsF',
+ 'QGradient',
+ 'QGuiApplication',
+ 'QHelpEvent',
+ 'QHideEvent',
+ 'QHoverEvent',
+ 'QIcon',
+ 'QIconDragEvent',
+ 'QIconEngine',
+ 'QImage',
+ 'QImageIOHandler',
+ 'QImageReader',
+ 'QImageWriter',
+ 'QInputEvent',
+ 'QInputMethodEvent',
+ 'QIntValidator',
+ 'QKeyEvent',
+ 'QKeySequence',
+ 'QLinearGradient',
+ 'QMatrix2x2',
+ 'QMatrix2x3',
+ 'QMatrix2x4',
+ 'QMatrix3x2',
+ 'QMatrix3x3',
+ 'QMatrix3x4',
+ 'QMatrix4x2',
+ 'QMatrix4x3',
+ 'QMatrix4x4',
+ 'QMouseEvent',
+ 'QMoveEvent',
+ 'QMovie',
+ 'QPaintDevice',
+ 'QPaintEngine',
+ 'QPaintEngineState',
+ 'QPaintEvent',
+ 'QPainter',
+ 'QPainterPath',
+ 'QPainterPathStroker',
+ 'QPalette',
+ 'QPen',
+ 'QPicture',
+ 'QPictureIO',
+ 'QPixmap',
+ 'QPixmapCache',
+ 'QPolygon',
+ 'QPolygonF',
+ 'QQuaternion',
+ 'QRadialGradient',
+ 'QRegExpValidator',
+ 'QRegion',
+ 'QResizeEvent',
+ 'QSessionManager',
+ 'QShortcutEvent',
+ 'QShowEvent',
+ 'QStandardItem',
+ 'QStandardItemModel',
+ 'QStatusTipEvent',
+ 'QSyntaxHighlighter',
+ 'QTabletEvent',
+ 'QTextBlock',
+ 'QTextBlockFormat',
+ 'QTextBlockGroup',
+ 'QTextBlockUserData',
+ 'QTextCharFormat',
+ 'QTextCursor',
+ 'QTextDocument',
+ 'QTextDocumentFragment',
+ 'QTextFormat',
+ 'QTextFragment',
+ 'QTextFrame',
+ 'QTextFrameFormat',
+ 'QTextImageFormat',
+ 'QTextInlineObject',
+ 'QTextItem',
+ 'QTextLayout',
+ 'QTextLength',
+ 'QTextLine',
+ 'QTextList',
+ 'QTextListFormat',
+ 'QTextObject',
+ 'QTextObjectInterface',
+ 'QTextOption',
+ 'QTextTable',
+ 'QTextTableCell',
+ 'QTextTableCellFormat',
+ 'QTextTableFormat',
+ 'QTouchEvent',
+ 'QTransform',
+ 'QValidator',
+ 'QVector2D',
+ 'QVector3D',
+ 'QVector4D',
+ 'QWhatsThisClickedEvent',
+ 'QWheelEvent',
+ 'QWindowStateChangeEvent',
+ 'qAlpha',
+ 'qBlue',
+ 'qGray',
+ 'qGreen',
+ 'qIsGray',
+ 'qRed',
+ 'qRgb',
+ 'qRgba'
+ ],
+ # 'QtHelp': [
+ # 'QHelpContentItem',
+ # 'QHelpContentModel',
+ # 'QHelpContentWidget',
+ # 'QHelpEngine',
+ # 'QHelpEngineCore',
+ # 'QHelpIndexModel',
+ # 'QHelpIndexWidget',
+ # 'QHelpSearchEngine',
+ # 'QHelpSearchQuery',
+ # 'QHelpSearchQueryWidget',
+ # 'QHelpSearchResultWidget'
+ # ],
+ # 'QtMultimedia': [
+ # 'QAbstractVideoBuffer',
+ # 'QAbstractVideoSurface',
+ # 'QAudio',
+ # 'QAudioDeviceInfo',
+ # 'QAudioFormat',
+ # 'QAudioInput',
+ # 'QAudioOutput',
+ # 'QVideoFrame',
+ # 'QVideoSurfaceFormat'
+ # ],
+ # 'QtNetwork': [
+ # 'QAbstractNetworkCache',
+ # 'QAbstractSocket',
+ # 'QAuthenticator',
+ # 'QHostAddress',
+ # 'QHostInfo',
+ # 'QLocalServer',
+ # 'QLocalSocket',
+ # 'QNetworkAccessManager',
+ # 'QNetworkAddressEntry',
+ # 'QNetworkCacheMetaData',
+ # 'QNetworkConfiguration',
+ # 'QNetworkConfigurationManager',
+ # 'QNetworkCookie',
+ # 'QNetworkCookieJar',
+ # 'QNetworkDiskCache',
+ # 'QNetworkInterface',
+ # 'QNetworkProxy',
+ # 'QNetworkProxyFactory',
+ # 'QNetworkProxyQuery',
+ # 'QNetworkReply',
+ # 'QNetworkRequest',
+ # 'QNetworkSession',
+ # 'QSsl',
+ # 'QTcpServer',
+ # 'QTcpSocket',
+ # 'QUdpSocket'
+ # ],
+ # 'QtOpenGL': [
+ # 'QGL',
+ # 'QGLContext',
+ # 'QGLFormat',
+ # 'QGLWidget'
+ # ],
+ # 'QtPrintSupport': [
+ # 'QAbstractPrintDialog',
+ # 'QPageSetupDialog',
+ # 'QPrintDialog',
+ # 'QPrintEngine',
+ # 'QPrintPreviewDialog',
+ # 'QPrintPreviewWidget',
+ # 'QPrinter',
+ # 'QPrinterInfo'
+ # ],
+ # 'QtSql': [
+ # 'QSql',
+ # 'QSqlDatabase',
+ # 'QSqlDriver',
+ # 'QSqlDriverCreatorBase',
+ # 'QSqlError',
+ # 'QSqlField',
+ # 'QSqlIndex',
+ # 'QSqlQuery',
+ # 'QSqlQueryModel',
+ # 'QSqlRecord',
+ # 'QSqlRelation',
+ # 'QSqlRelationalDelegate',
+ # 'QSqlRelationalTableModel',
+ # 'QSqlResult',
+ # 'QSqlTableModel'
+ # ],
+ 'QtSvg': [
+ 'QGraphicsSvgItem',
+ 'QSvgGenerator',
+ 'QSvgRenderer',
+ 'QSvgWidget'
+ ],
+ # 'QtTest': [
+ # 'QTest'
+ # ],
+ 'QtWidgets': [
+ 'QAbstractButton',
+ 'QAbstractGraphicsShapeItem',
+ 'QAbstractItemDelegate',
+ 'QAbstractItemView',
+ 'QAbstractScrollArea',
+ 'QAbstractSlider',
+ 'QAbstractSpinBox',
+ 'QAction',
+ 'QActionGroup',
+ 'QApplication',
+ 'QBoxLayout',
+ 'QButtonGroup',
+ 'QCalendarWidget',
+ 'QCheckBox',
+ 'QColorDialog',
+ 'QColumnView',
+ 'QComboBox',
+ 'QCommandLinkButton',
+ 'QCommonStyle',
+ 'QCompleter',
+ 'QDataWidgetMapper',
+ 'QDateEdit',
+ 'QDateTimeEdit',
+ 'QDesktopWidget',
+ 'QDial',
+ 'QDialog',
+ 'QDialogButtonBox',
+ 'QDirModel',
+ 'QDockWidget',
+ 'QDoubleSpinBox',
+ 'QErrorMessage',
+ 'QFileDialog',
+ 'QFileIconProvider',
+ 'QFileSystemModel',
+ 'QFocusFrame',
+ 'QFontComboBox',
+ 'QFontDialog',
+ 'QFormLayout',
+ 'QFrame',
+ 'QGesture',
+ 'QGestureEvent',
+ 'QGestureRecognizer',
+ 'QGraphicsAnchor',
+ 'QGraphicsAnchorLayout',
+ 'QGraphicsBlurEffect',
+ 'QGraphicsColorizeEffect',
+ 'QGraphicsDropShadowEffect',
+ 'QGraphicsEffect',
+ 'QGraphicsEllipseItem',
+ 'QGraphicsGridLayout',
+ 'QGraphicsItem',
+ 'QGraphicsItemGroup',
+ 'QGraphicsLayout',
+ 'QGraphicsLayoutItem',
+ 'QGraphicsLineItem',
+ 'QGraphicsLinearLayout',
+ 'QGraphicsObject',
+ 'QGraphicsOpacityEffect',
+ 'QGraphicsPathItem',
+ 'QGraphicsPixmapItem',
+ 'QGraphicsPolygonItem',
+ 'QGraphicsProxyWidget',
+ 'QGraphicsRectItem',
+ 'QGraphicsRotation',
+ 'QGraphicsScale',
+ 'QGraphicsScene',
+ 'QGraphicsSceneContextMenuEvent',
+ 'QGraphicsSceneDragDropEvent',
+ 'QGraphicsSceneEvent',
+ 'QGraphicsSceneHelpEvent',
+ 'QGraphicsSceneHoverEvent',
+ 'QGraphicsSceneMouseEvent',
+ 'QGraphicsSceneMoveEvent',
+ 'QGraphicsSceneResizeEvent',
+ 'QGraphicsSceneWheelEvent',
+ 'QGraphicsSimpleTextItem',
+ 'QGraphicsTextItem',
+ 'QGraphicsTransform',
+ 'QGraphicsView',
+ 'QGraphicsWidget',
+ 'QGridLayout',
+ 'QGroupBox',
+ 'QHBoxLayout',
+ 'QHeaderView',
+ 'QInputDialog',
+ 'QItemDelegate',
+ 'QItemEditorCreatorBase',
+ 'QItemEditorFactory',
+ 'QKeyEventTransition',
+ 'QLCDNumber',
+ 'QLabel',
+ 'QLayout',
+ 'QLayoutItem',
+ 'QLineEdit',
+ 'QListView',
+ 'QListWidget',
+ 'QListWidgetItem',
+ 'QMainWindow',
+ 'QMdiArea',
+ 'QMdiSubWindow',
+ 'QMenu',
+ 'QMenuBar',
+ 'QMessageBox',
+ 'QMouseEventTransition',
+ 'QPanGesture',
+ 'QPinchGesture',
+ 'QPlainTextDocumentLayout',
+ 'QPlainTextEdit',
+ 'QProgressBar',
+ 'QProgressDialog',
+ 'QPushButton',
+ 'QRadioButton',
+ 'QRubberBand',
+ 'QScrollArea',
+ 'QScrollBar',
+ 'QShortcut',
+ 'QSizeGrip',
+ 'QSizePolicy',
+ 'QSlider',
+ 'QSpacerItem',
+ 'QSpinBox',
+ 'QSplashScreen',
+ 'QSplitter',
+ 'QSplitterHandle',
+ 'QStackedLayout',
+ 'QStackedWidget',
+ 'QStatusBar',
+ 'QStyle',
+ 'QStyleFactory',
+ 'QStyleHintReturn',
+ 'QStyleHintReturnMask',
+ 'QStyleHintReturnVariant',
+ 'QStyleOption',
+ 'QStyleOptionButton',
+ 'QStyleOptionComboBox',
+ 'QStyleOptionComplex',
+ 'QStyleOptionDockWidget',
+ 'QStyleOptionFocusRect',
+ 'QStyleOptionFrame',
+ 'QStyleOptionGraphicsItem',
+ 'QStyleOptionGroupBox',
+ 'QStyleOptionHeader',
+ 'QStyleOptionMenuItem',
+ 'QStyleOptionProgressBar',
+ 'QStyleOptionRubberBand',
+ 'QStyleOptionSizeGrip',
+ 'QStyleOptionSlider',
+ 'QStyleOptionSpinBox',
+ 'QStyleOptionTab',
+ 'QStyleOptionTabBarBase',
+ 'QStyleOptionTabWidgetFrame',
+ 'QStyleOptionTitleBar',
+ 'QStyleOptionToolBar',
+ 'QStyleOptionToolBox',
+ 'QStyleOptionToolButton',
+ 'QStyleOptionViewItem',
+ 'QStylePainter',
+ 'QStyledItemDelegate',
+ 'QSwipeGesture',
+ 'QSystemTrayIcon',
+ 'QTabBar',
+ 'QTabWidget',
+ 'QTableView',
+ 'QTableWidget',
+ 'QTableWidgetItem',
+ 'QTableWidgetSelectionRange',
+ 'QTapAndHoldGesture',
+ 'QTapGesture',
+ 'QTextBrowser',
+ 'QTextEdit',
+ 'QTimeEdit',
+ 'QToolBar',
+ 'QToolBox',
+ 'QToolButton',
+ 'QToolTip',
+ 'QTreeView',
+ 'QTreeWidget',
+ 'QTreeWidgetItem',
+ 'QTreeWidgetItemIterator',
+ 'QUndoCommand',
+ 'QUndoGroup',
+ 'QUndoStack',
+ 'QUndoView',
+ 'QVBoxLayout',
+ 'QWhatsThis',
+ 'QWidget',
+ 'QWidgetAction',
+ 'QWidgetItem',
+ 'QWizard',
+ 'QWizardPage'
+ ],
+ # 'QtX11Extras': [
+ # 'QX11Info'
+ # ],
+ # 'QtXml': [
+ # 'QDomAttr',
+ # 'QDomCDATASection',
+ # 'QDomCharacterData',
+ # 'QDomComment',
+ # 'QDomDocument',
+ # 'QDomDocumentFragment',
+ # 'QDomDocumentType',
+ # 'QDomElement',
+ # 'QDomEntity',
+ # 'QDomEntityReference',
+ # 'QDomImplementation',
+ # 'QDomNamedNodeMap',
+ # 'QDomNode',
+ # 'QDomNodeList',
+ # 'QDomNotation',
+ # 'QDomProcessingInstruction',
+ # 'QDomText',
+ # 'QXmlAttributes',
+ # 'QXmlContentHandler',
+ # 'QXmlDTDHandler',
+ # 'QXmlDeclHandler',
+ # 'QXmlDefaultHandler',
+ # 'QXmlEntityResolver',
+ # 'QXmlErrorHandler',
+ # 'QXmlInputSource',
+ # 'QXmlLexicalHandler',
+ # 'QXmlLocator',
+ # 'QXmlNamespaceSupport',
+ # 'QXmlParseException',
+ # 'QXmlReader',
+ # 'QXmlSimpleReader'
+ # ],
+ # 'QtXmlPatterns': [
+ # 'QAbstractMessageHandler',
+ # 'QAbstractUriResolver',
+ # 'QAbstractXmlNodeModel',
+ # 'QAbstractXmlReceiver',
+ # 'QSourceLocation',
+ # 'QXmlFormatter',
+ # 'QXmlItem',
+ # 'QXmlName',
+ # 'QXmlNamePool',
+ # 'QXmlNodeModelIndex',
+ # 'QXmlQuery',
+ # 'QXmlResultItems',
+ # 'QXmlSchema',
+ # 'QXmlSchemaValidator',
+ # 'QXmlSerializer'
+ # ]
+ 'QtQml': [
+ 'qmlRegisterType',
+ 'qmlRegisterUncreatableType',
+ 'QQmlApplicationEngine',
+ ],
+ 'QtQuick': [
+ 'QQuickPaintedItem',
+ 'QQuickView',
+ ],
+}
+
+####################################################################################################
+
+"""Misplaced members
+
+These members from the original submodule are misplaced relative PySide2
+
+"""
+
+_misplaced_members = {
+ 'PySide2': {
+ 'QtCore.QStringListModel': 'QtCore.QStringListModel',
+ 'QtGui.QStringListModel': 'QtCore.QStringListModel',
+ 'QtCore.Property': 'QtCore.Property',
+ 'QtCore.Signal': 'QtCore.Signal',
+ 'QtCore.Slot': 'QtCore.Slot',
+ 'QtCore.QAbstractProxyModel': 'QtCore.QAbstractProxyModel',
+ 'QtCore.QSortFilterProxyModel': 'QtCore.QSortFilterProxyModel',
+ 'QtCore.QItemSelection': 'QtCore.QItemSelection',
+ 'QtCore.QItemSelectionModel': 'QtCore.QItemSelectionModel',
+ 'QtCore.QItemSelectionRange': 'QtCore.QItemSelectionRange',
+ # 'QtUiTools.QUiLoader': ['QtCompat.loadUi', _loadUi],
+ # 'shiboken2.wrapInstance': ['QtCompat.wrapInstance', _wrapinstance],
+ # 'shiboken2.getCppPointer': ['QtCompat.getCppPointer', _getcpppointer],
+ 'QtWidgets.qApp': 'QtWidgets.QApplication.instance()',
+ # 'QtCore.QCoreApplication.translate': [
+ # 'QtCompat.translate', _translate
+ # ],
+ # 'QtWidgets.QApplication.translate': [
+ # 'QtCompat.translate', _translate
+ # ],
+ # 'QtCore.qInstallMessageHandler': [
+ # 'QtCompat.qInstallMessageHandler', _qInstallMessageHandler
+ # ],
+ },
+ 'PyQt5': {
+ 'QtCore.pyqtProperty': 'QtCore.Property',
+ 'QtCore.pyqtSignal': 'QtCore.Signal',
+ 'QtCore.pyqtSlot': 'QtCore.Slot',
+ 'QtCore.QAbstractProxyModel': 'QtCore.QAbstractProxyModel',
+ 'QtCore.QSortFilterProxyModel': 'QtCore.QSortFilterProxyModel',
+ 'QtCore.QStringListModel': 'QtCore.QStringListModel',
+ 'QtCore.QItemSelection': 'QtCore.QItemSelection',
+ 'QtCore.QItemSelectionModel': 'QtCore.QItemSelectionModel',
+ 'QtCore.QItemSelectionRange': 'QtCore.QItemSelectionRange',
+ # 'uic.loadUi': ['QtCompat.loadUi', _loadUi],
+ # 'sip.wrapinstance': ['QtCompat.wrapInstance', _wrapinstance],
+ # 'sip.unwrapinstance': ['QtCompat.getCppPointer', _getcpppointer],
+ 'QtWidgets.qApp': 'QtWidgets.QApplication.instance()',
+ # 'QtCore.QCoreApplication.translate': [
+ # 'QtCompat.translate', _translate
+ # ],
+ # 'QtWidgets.QApplication.translate': [
+ # 'QtCompat.translate', _translate
+ # ],
+ # 'QtCore.qInstallMessageHandler': [
+ # 'QtCompat.qInstallMessageHandler', _qInstallMessageHandler
+ # ],
+ },
+}
+
+####################################################################################################
+
+"""Compatibility Members
+
+This dictionary is used to build Qt.QtCompat objects that provide a consistent
+interface for obsolete members, and differences in binding return values.
+
+{
+ 'binding': {
+ 'classname': {
+ 'targetname': 'binding_namespace',
+ }
+ }
+}
+
+"""
+
+_compatibility_members = {
+ 'PySide2': {
+ # 'QWidget': {
+ # 'grab': 'QtWidgets.QWidget.grab',
+ # },
+ # 'QHeaderView': {
+ # 'sectionsClickable': 'QtWidgets.QHeaderView.sectionsClickable',
+ # 'setSectionsClickable':
+ # 'QtWidgets.QHeaderView.setSectionsClickable',
+ # 'sectionResizeMode': 'QtWidgets.QHeaderView.sectionResizeMode',
+ # 'setSectionResizeMode':
+ # 'QtWidgets.QHeaderView.setSectionResizeMode',
+ # 'sectionsMovable': 'QtWidgets.QHeaderView.sectionsMovable',
+ # 'setSectionsMovable': 'QtWidgets.QHeaderView.setSectionsMovable',
+ # },
+ # 'QFileDialog': {
+ # 'getOpenFileName': 'QtWidgets.QFileDialog.getOpenFileName',
+ # 'getOpenFileNames': 'QtWidgets.QFileDialog.getOpenFileNames',
+ # 'getSaveFileName': 'QtWidgets.QFileDialog.getSaveFileName',
+ # },
+ },
+ 'PyQt5': {
+ # 'QWidget': {
+ # 'grab': 'QtWidgets.QWidget.grab',
+ # },
+ # 'QHeaderView': {
+ # 'sectionsClickable': 'QtWidgets.QHeaderView.sectionsClickable',
+ # 'setSectionsClickable':
+ # 'QtWidgets.QHeaderView.setSectionsClickable',
+ # 'sectionResizeMode': 'QtWidgets.QHeaderView.sectionResizeMode',
+ # 'setSectionResizeMode':
+ # 'QtWidgets.QHeaderView.setSectionResizeMode',
+ # 'sectionsMovable': 'QtWidgets.QHeaderView.sectionsMovable',
+ # 'setSectionsMovable': 'QtWidgets.QHeaderView.setSectionsMovable',
+ # },
+ # 'QFileDialog': {
+ # 'getOpenFileName': 'QtWidgets.QFileDialog.getOpenFileName',
+ # 'getOpenFileNames': 'QtWidgets.QFileDialog.getOpenFileNames',
+ # 'getSaveFileName': 'QtWidgets.QFileDialog.getSaveFileName',
+ # },
+ },
+}
diff --git a/src/QtShim/Wrapper.py b/src/QtShim/Wrapper.py
new file mode 100644
index 0000000..a398f00
--- /dev/null
+++ b/src/QtShim/Wrapper.py
@@ -0,0 +1,34 @@
+####################################################################################################
+
+def _qInstallMessageHandler(handler):
+ '''Install a message handler that works in all bindings
+
+ Args:
+ handler: A function that takes 3 arguments, or None
+ '''
+
+ def messageOutputHandler(*args):
+ # In Qt5 bindings, message handlers are passed 3 arguments
+ # The first argument is a QtMsgType
+ # The last argument is the message to be printed
+ # The Middle argument (if passed) is a QMessageLogContext
+ if len(args) == 3:
+ msgType, logContext, msg = args
+ elif len(args) == 2:
+ msgType, msg = args
+ logContext = None
+ else:
+ raise TypeError(
+ 'handler expected 2 or 3 arguments, got {0}'.format(len(args)))
+
+ if isinstance(msg, bytes):
+ # In python 3, some bindings pass a bytestring, which cannot be
+ # used elsewhere. Decoding a python 2 or 3 bytestring object will
+ # consistently return a unicode object.
+ msg = msg.decode()
+
+ handler(msgType, logContext, msg)
+
+ if not handler:
+ handler = messageOutputHandler
+ return Qt._QtCore.qInstallMessageHandler(handler)
diff --git a/src/QtShim/__init__.py b/src/QtShim/__init__.py
new file mode 100644
index 0000000..0438cc0
--- /dev/null
+++ b/src/QtShim/__init__.py
@@ -0,0 +1,389 @@
+"""Minimal Python 3 shim around PyQt5 and Pyside2 Qt bindings for QML applications.
+
+Forked from https://github.com/mottosso/Qt.py under MIT License.
+Copyright (c) 2016 Marcus Ottosson
+
+Changes
+
+* Dropped Python2 and Qt4 support
+* Focus on last Python 3 release
+* Focus on last Qt API : QML
+
+Requirements
+
+* make use of lazy loading to speed up startup time !
+"""
+
+# Fixme: ressource file CodeReview/QtApplication/rcc/CodeReviewRessource.py
+
+####################################################################################################
+
+# Enable support for `from Qt import *`
+__all__ = []
+
+####################################################################################################
+
+import importlib
+import logging
+import os
+import sys
+import types
+
+from .QtConfig import _common_members, _misplaced_members, _compatibility_members
+
+####################################################################################################
+
+# _module_logger = logging.getLogger(__name__)
+
+####################################################################################################
+
+# Flags from environment variables
+QT_VERBOSE = bool(os.getenv('QT_VERBOSE'))
+
+QT_PREFERRED_BINDING = os.getenv('QT_PREFERRED_BINDING', '')
+if QT_PREFERRED_BINDING:
+ QT_PREFERRED_BINDING = list(x for x in QT_PREFERRED_BINDING.split(',') if x)
+else:
+ # on dec 2018, PySide2 is still not fully operational
+ QT_PREFERRED_BINDING = ('PyQt5', 'PySide2')
+
+####################################################################################################
+
+def _new_module(name):
+ return types.ModuleType(__name__ + '.' + name)
+
+####################################################################################################
+
+# Reference to Qt.py
+Qt = sys.modules[__name__]
+Qt.QtCompat = _new_module('QtCompat')
+
+####################################################################################################
+
+def _log(text):
+ if QT_VERBOSE:
+ # _logger print
+ sys.stdout.write(text + '\n')
+
+####################################################################################################
+
+def _import_sub_module(module, name):
+ """import a submodule"""
+ _log('_import_sub_module {} {}'.format(module, name))
+ module_name = module.__name__ + '.' + name # e.g. PyQt5.QtCore
+ module = importlib.import_module(module_name)
+ return module
+
+####################################################################################################
+
+def _setup(module, extras):
+ """Install common submodules"""
+
+ Qt.__binding__ = module.__name__
+
+ for name in list(_common_members) + extras:
+ try:
+ submodule = _import_sub_module(module, name)
+ except ImportError:
+ try:
+ # For extra modules like sip and shiboken that may not be
+ # children of the binding.
+ submodule = __import__(name)
+ except ImportError:
+ continue
+
+ setattr(Qt, '_' + name, submodule)
+
+ if name not in extras:
+ # Store reference to original binding
+ setattr(Qt, name, _new_module(name)) # Qt.QtCore = module(so module)
+
+####################################################################################################
+
+def _reassign_misplaced_members(binding):
+ """Apply misplaced members from `binding` to Qt.py
+
+ Arguments:
+ binding (dict): Misplaced members
+
+ """
+
+ for src, dst in _misplaced_members[binding].items():
+ # print()
+ dst_value = None
+
+ # Fixme: to func
+ src_parts = src.split('.')
+ src_module = src_parts[0]
+ if len(src_parts):
+ src_member = src_parts[1:]
+ else:
+ src_member = None
+
+ if isinstance(dst, (list, tuple)):
+ dst, dst_value = dst
+ # print(src, '->', dst, dst_value)
+ # print(src_module, src_member)
+
+ dst_parts = dst.split('.')
+ dst_module = dst_parts[0]
+ if len(dst_parts):
+ dst_member = dst_parts[1]
+ else:
+ dst_member = None
+ # print(dst_module, dst_member)
+
+ # Get the member we want to store in the namesapce.
+ if not dst_value:
+ try:
+ _part = getattr(Qt, '_' + src_module)
+ while src_member:
+ member = src_member.pop(0)
+ _part = getattr(_part, member)
+ dst_value = _part
+ except AttributeError:
+ # If the member we want to store in the namespace does not
+ # exist, there is no need to continue. This can happen if a
+ # request was made to rename a member that didn't exist, for
+ # example if QtWidgets isn't available on the target platform.
+ _log('Misplaced member has no source: {0}'.format(src))
+ continue
+ # print(dst_value)
+
+ try:
+ # Fixme: src_object ???
+ src_object = getattr(Qt, dst_module)
+ except AttributeError:
+ # print('Failed to get src_object')
+ if dst_module not in _common_members:
+ # Only create the Qt parent module if its listed in
+ # _common_members. Without this check, if you remove QtCore
+ # from _common_members, the default _misplaced_members will add
+ # Qt.QtCore so it can add Signal, Slot, etc.
+ msg = "Not creating missing member module '{m}' for '{c}'"
+ _log(msg.format(m=dst_module, c=dst_member))
+ continue
+ # If the dst is valid but the Qt parent module does not exist
+ # then go ahead and create a new module to contain the member.
+ setattr(Qt, dst_module, _new_module(dst_module))
+ src_object = getattr(Qt, dst_module)
+ # Enable direct import of the new module
+ sys.modules[__name__ + '.' + dst_module] = src_object
+
+ if not dst_value:
+ dst_value = getattr(Qt, '_' + src_module)
+ if src_member:
+ dst_value = getattr(dst_value, src_member)
+
+ setattr(
+ src_object,
+ dst_member or dst_module,
+ dst_value
+ )
+
+####################################################################################################
+
+def _build_compatibility_members(binding, decorators=None):
+ """Apply `binding` to QtCompat
+
+ Arguments:
+ binding (str): Top level binding in _compatibility_members.
+ decorators (dict, optional): Provides the ability to decorate the
+ original Qt methods when needed by a binding. This can be used
+ to change the returned value to a standard value. The key should
+ be the classname, the value is a dict where the keys are the
+ target method names, and the values are the decorator functions.
+
+ """
+
+ decorators = decorators or dict()
+
+ # Allow optional site-level customization of the compatibility members.
+ # This method does not need to be implemented in QtSiteConfig.
+ try:
+ import QtSiteConfig
+ except ImportError:
+ pass
+ else:
+ if hasattr(QtSiteConfig, 'update_compatibility_decorators'):
+ QtSiteConfig.update_compatibility_decorators(binding, decorators)
+
+ _QtCompat = type('QtCompat', (object,), {})
+
+ for classname, bindings in _compatibility_members[binding].items():
+ attrs = {}
+ for target, binding in bindings.items():
+ namespaces = binding.split('.')
+ try:
+ src_object = getattr(Qt, '_' + namespaces[0])
+ except AttributeError as e:
+ _log('QtCompat: AttributeError: %s' % e)
+ # Skip reassignment of non-existing members.
+ # This can happen if a request was made to
+ # rename a member that didn't exist, for example
+ # if QtWidgets isn't available on the target platform.
+ continue
+
+ # Walk down any remaining namespace getting the object assuming
+ # that if the first namespace exists the rest will exist.
+ for namespace in namespaces[1:]:
+ src_object = getattr(src_object, namespace)
+
+ # decorate the Qt method if a decorator was provided.
+ if target in decorators.get(classname, []):
+ # staticmethod must be called on the decorated method to
+ # prevent a TypeError being raised when the decorated method
+ # is called.
+ src_object = staticmethod(
+ decorators[classname][target](src_object))
+
+ attrs[target] = src_object
+
+ # Create the QtCompat class and install it into the namespace
+ compat_class = type(classname, (_QtCompat,), attrs)
+ setattr(Qt.QtCompat, classname, compat_class)
+
+####################################################################################################
+
+def _pyside2():
+ """Initialise PySide2
+
+ These functions serve to test the existence of a binding
+ along with set it up in such a way that it aligns with
+ the final step; adding members from the original binding
+ to Qt.py
+
+ """
+
+ import PySide2 as module
+ extras = []
+ # try:
+ # from PySide2 import shiboken2
+ # extras.append('shiboken2')
+ # except ImportError:
+ # pass
+
+ _setup(module, extras)
+ Qt.__binding_version__ = module.__version__
+
+ # if hasattr(Qt, '_shiboken2'):
+ # Qt.QtCompat.wrapInstance = _wrapinstance
+ # Qt.QtCompat.getCppPointer = _getcpppointer
+ # Qt.QtCompat.delete = shiboken2.delete
+
+ if hasattr(Qt, '_QtCore'):
+ Qt.__qt_version__ = Qt._QtCore.qVersion()
+
+ # if hasattr(Qt, '_QtWidgets'):
+ # Qt.QtCompat.setSectionResizeMode = \
+ # Qt._QtWidgets.QHeaderView.setSectionResizeMode
+
+ _reassign_misplaced_members('PySide2')
+ # _build_compatibility_members('PySide2')
+
+####################################################################################################
+
+def _pyqt5():
+ """Initialise PyQt5"""
+
+ import PyQt5 as module
+ extras = []
+ # try:
+ # import sip
+ # extras.append(sip.__name__)
+ # except ImportError:
+ # sip = None
+
+ _setup(module, extras)
+ # if hasattr(Qt, '_sip'):
+ # Qt.QtCompat.wrapInstance = _wrapinstance
+ # Qt.QtCompat.getCppPointer = _getcpppointer
+ # Qt.QtCompat.delete = sip.delete
+
+ if hasattr(Qt, '_QtCore'):
+ Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR
+ Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR
+
+ # if hasattr(Qt, '_QtWidgets'):
+ # Qt.QtCompat.setSectionResizeMode = \
+ # Qt._QtWidgets.QHeaderView.setSectionResizeMode
+
+ _reassign_misplaced_members('PyQt5')
+ # _build_compatibility_members('PyQt5')
+
+####################################################################################################
+
+def _install():
+
+ # Default order (customise order and content via QT_PREFERRED_BINDING)
+ order = QT_PREFERRED_BINDING
+
+ available = {
+ 'PySide2': _pyside2,
+ 'PyQt5': _pyqt5,
+ }
+
+ _log("Order: {}".format(' '.join(order)))
+
+ found_binding = False
+ for name in order:
+ _log('Trying %s' % name)
+
+ try:
+ available[name]()
+ found_binding = True
+ break
+
+ except ImportError as e:
+ _log('ImportError: %s' % e)
+
+ except KeyError:
+ _log("ImportError: Preferred binding '%s' not found." % name)
+
+ if not found_binding:
+ # If not binding were found, throw this error
+ raise ImportError('No Qt binding were found.')
+
+ # Install individual members
+ for name, members in _common_members.items():
+ try:
+ their_submodule = getattr(Qt, '_' + name)
+ except AttributeError:
+ continue
+
+ our_submodule = getattr(Qt, name)
+
+ # Enable import *
+ __all__.append(name)
+
+ # Enable direct import of submodule,
+ # e.g. import Qt.QtCore
+ sys.modules[__name__ + '.' + name] = our_submodule
+
+ for member in members:
+ # Accept that a submodule may miss certain members.
+ try:
+ their_member = getattr(their_submodule, member)
+ except AttributeError:
+ _log("'%s.%s' was missing." % (name, member))
+ continue
+ setattr(our_submodule, member, their_member)
+
+ # Enable direct import of QtCompat
+ sys.modules['Qt.QtCompat'] = Qt.QtCompat
+
+####################################################################################################
+
+_install()
+
+####################################################################################################
+
+# Fixme: Python 3.7
+# def __getattr__(name):
+# print('__getattr__', name)
+
+####################################################################################################
+
+# Setup Binding Enum states
+Qt.IsPySide2 = Qt.__binding__ == 'PySide2'
+Qt.IsPyQt5 = not Qt.IsPySide2
diff --git a/src/QtShim/not-implemented.py b/src/QtShim/not-implemented.py
new file mode 100644
index 0000000..be022e1
--- /dev/null
+++ b/src/QtShim/not-implemented.py
@@ -0,0 +1,116 @@
+####################################################################################################
+
+def _getcpppointer(object):
+ if hasattr(Qt, '_shiboken2'):
+ return getattr(Qt, '_shiboken2').getCppPointer(object)[0]
+ elif hasattr(Qt, '_sip'):
+ return getattr(Qt, '_sip').unwrapinstance(object)
+ raise AttributeError("'module' has no attribute 'getCppPointer'")
+
+####################################################################################################
+
+def _wrapinstance(ptr, base=None):
+ '''Enable implicit cast of pointer to most suitable class
+
+ This behaviour is available in sip per default.
+
+ Based on http://nathanhorne.com/pyqtpyside-wrap-instance
+
+ Usage:
+ This mechanism kicks in under these circumstances.
+ 1. Qt.py is using PySide 1 or 2.
+ 2. A `base` argument is not provided.
+
+ See :func:`QtCompat.wrapInstance()`
+
+ Arguments:
+ ptr (int): Pointer to QObject in memory
+ base (QObject, optional): Base class to wrap with. Defaults to QObject,
+ which should handle anything.
+
+ '''
+
+ assert isinstance(ptr, int), "Argument 'ptr' must be of type "
+ assert (base is None) or issubclass(base, Qt.QtCore.QObject), (
+ "Argument 'base' must be of type ")
+
+ if Qt.IsPyQt4 or Qt.IsPyQt5:
+ func = getattr(Qt, '_sip').wrapinstance
+ elif Qt.IsPySide2:
+ func = getattr(Qt, '_shiboken2').wrapInstance
+ elif Qt.IsPySide:
+ func = getattr(Qt, '_shiboken').wrapInstance
+ else:
+ raise AttributeError("'module' has no attribute 'wrapInstance'")
+
+ if base is None:
+ q_object = func(int(ptr), Qt.QtCore.QObject)
+ meta_object = q_object.metaObject()
+ class_name = meta_object.className()
+ super_class_name = meta_object.superClass().className()
+
+ if hasattr(Qt.QtWidgets, class_name):
+ base = getattr(Qt.QtWidgets, class_name)
+
+ elif hasattr(Qt.QtWidgets, super_class_name):
+ base = getattr(Qt.QtWidgets, super_class_name)
+
+ else:
+ base = Qt.QtCore.QObject
+
+ return func(int(ptr), base)
+
+####################################################################################################
+
+def _translate(context, sourceText, *args):
+ # In Qt4 bindings, translate can be passed 2 or 3 arguments
+ # In Qt5 bindings, translate can be passed 2 arguments
+ # The first argument is disambiguation[str]
+ # The last argument is n[int]
+ # The middle argument can be encoding[QtCore.QCoreApplication.Encoding]
+ if len(args) == 3:
+ disambiguation, encoding, n = args
+ elif len(args) == 2:
+ disambiguation, n = args
+ encoding = None
+ else:
+ raise TypeError(
+ 'Expected 4 or 5 arguments, got {0}.'.format(len(args) + 2))
+
+ if hasattr(Qt.QtCore, 'QCoreApplication'):
+ app = getattr(Qt.QtCore, 'QCoreApplication')
+ else:
+ raise NotImplementedError(
+ 'Missing QCoreApplication implementation for {binding}'.format(
+ binding=Qt.__binding__,
+ )
+ )
+ if Qt.__binding__ in ('PySide2', 'PyQt5'):
+ sanitized_args = [context, sourceText, disambiguation, n]
+ else:
+ sanitized_args = [
+ context,
+ sourceText,
+ disambiguation,
+ encoding or app.CodecForTr,
+ n
+ ]
+ return app.translate(*sanitized_args)
+
+####################################################################################################
+####################################################################################################
+
+def _none():
+ '''Internal option (used in installer)'''
+
+ Mock = type('Mock', (), {'__getattr__': lambda Qt, attr: None})
+
+ Qt.__binding__ = 'None'
+ Qt.__qt_version__ = '0.0.0'
+ Qt.__binding_version__ = '0.0.0'
+ Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None
+ Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None
+
+ for submodule in _common_members.keys():
+ setattr(Qt, submodule, Mock())
+ setattr(Qt, '_' + submodule, Mock())
diff --git a/tasks/__init__.py b/tasks/__init__.py
new file mode 100644
index 0000000..ce165f0
--- /dev/null
+++ b/tasks/__init__.py
@@ -0,0 +1,37 @@
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+# http://www.pyinvoke.org
+
+####################################################################################################
+
+from invoke import task, Collection
+ # import sys
+
+####################################################################################################
+
+from . import clean
+from . import doc
+from . import release
+
+ns = Collection()
+ns.add_collection(Collection.from_module(clean))
+ns.add_collection(Collection.from_module(release))
+ns.add_collection(Collection.from_module(doc))
diff --git a/tasks/clean.py b/tasks/clean.py
new file mode 100644
index 0000000..2ef1b71
--- /dev/null
+++ b/tasks/clean.py
@@ -0,0 +1,38 @@
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+####################################################################################################
+
+from invoke import task
+
+####################################################################################################
+
+@task
+def clean_flycheck(ctx):
+ with ctx.cd(ctx.Package):
+ ctx.run('find . -name "flycheck*.py" -exec rm {} \;')
+
+@task
+def clean_emacs_backup(ctx):
+ ctx.run('find . -name "*~" -type f -exec rm -f {} \;')
+
+@task(clean_flycheck, clean_emacs_backup)
+def clean(ctx):
+ pass
diff --git a/tasks/data/license-template.py b/tasks/data/license-template.py
new file mode 100644
index 0000000..1de9fa0
--- /dev/null
+++ b/tasks/data/license-template.py
@@ -0,0 +1,19 @@
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+####################################################################################################
diff --git a/tasks/data/license-template.qml b/tasks/data/license-template.qml
new file mode 100644
index 0000000..63f63df
--- /dev/null
+++ b/tasks/data/license-template.qml
@@ -0,0 +1,19 @@
+/***************************************************************************************************
+ *
+ * CodeReview - A Code Review GUI
+ * Copyright (C) 2019 Fabrice Salvaire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ ***************************************************************************************************/
diff --git a/tasks/doc.py b/tasks/doc.py
new file mode 100644
index 0000000..7e6af7d
--- /dev/null
+++ b/tasks/doc.py
@@ -0,0 +1,90 @@
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+####################################################################################################
+
+from pathlib import Path
+
+from invoke import task
+
+####################################################################################################
+
+from .clean import clean_flycheck as clean_flycheck
+
+####################################################################################################
+
+CODEREVIEW_SOURCE_PATH = Path(__file__).resolve().parents[1]
+
+SPHINX_PATH = CODEREVIEW_SOURCE_PATH.joinpath('doc', 'sphinx')
+BUILD_PATH = SPHINX_PATH.joinpath('build')
+RST_SOURCE_PATH = SPHINX_PATH.joinpath('source')
+RST_API_PATH = RST_SOURCE_PATH.joinpath('api')
+RST_EXAMPLES_PATH = RST_SOURCE_PATH.joinpath('examples')
+
+####################################################################################################
+
+@task
+def clean_api(ctx):
+ ctx.run('rm -rf doc/sphinx/source/api')
+
+####################################################################################################
+
+@task(clean_flycheck, clean_api)
+def make_api(ctx):
+ print('\nGenerate RST API files')
+ ctx.run('pyterate-rst-api {0.Package}'.format(ctx))
+ print('\nRun Sphinx')
+ with ctx.cd('doc/sphinx/'):
+ ctx.run('./make-html') #--clean
+
+####################################################################################################
+
+@task
+def run_sphinx(ctx):
+ print()
+ print('Run Sphinx')
+ working_path = SPHINX_PATH
+ # subprocess.run(('make-html'), cwd=working_path)
+ # --clean
+ with ctx.cd(str(working_path)):
+ ctx.run('make-html')
+
+@task(run_sphinx)
+def publish(ctx):
+ with ctx.cd(str(CODEREVIEW_SOURCE_PATH.joinpath('gh-pages'))):
+ ctx.run('update-gh-pages')
+
+####################################################################################################
+
+@task()
+def make_readme(ctx):
+ from setup_data import long_description
+ with open('README.rst', 'w') as fh:
+ fh.write(long_description)
+ # import subprocess
+ # subprocess.call(('rst2html', 'README.rst', 'README.html'))
+ ctx.run('rst2html README.rst README.html')
+
+####################################################################################################
+
+@task
+def update_authors(ctx):
+ # Keep authors in the order of appearance and use awk to filter out dupes
+ ctx.run("git log --format='- %aN <%aE>' --reverse | awk '!x[$0]++' > AUTHORS")
diff --git a/tasks/release.py b/tasks/release.py
new file mode 100644
index 0000000..f25e113
--- /dev/null
+++ b/tasks/release.py
@@ -0,0 +1,200 @@
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+####################################################################################################
+
+from pathlib import Path
+import re
+import shutil
+
+from invoke import task
+
+####################################################################################################
+
+STANDARD_PACKAGES = (
+ 'argparse',
+ 'atexit',
+ 'datetime',
+ 'glob',
+ 'hashlib',
+ 'importlib',
+ 'json',
+ 'logging',
+ 'operator',
+ 'os',
+ 'pathlib',
+ 'shutil',
+ 'signal',
+ 'stat',
+ 'subprocess',
+ 'sys',
+ 'time',
+ 'traceback',
+)
+
+@task()
+def show_import(ctx):
+ package = ctx.Package
+ with ctx.cd(package):
+ result = ctx.run("grep -r -h -E '^(import|from) [a-zA-Z]' . | sort | uniq", hide='out')
+ imports = set()
+ for line in result.stdout.split('\n'):
+ if line.startswith('from'):
+ position = line.find('import')
+ line = line[5:position]
+ elif line.startswith('import'):
+ line = line[7:]
+ # print('|{}|'.format(line))
+ position = line.find('.')
+ if position != -1:
+ line = line[:position]
+ line = line.strip()
+ if line:
+ imports.add(line)
+ imports -= set(STANDARD_PACKAGES)
+ imports -= set((package,))
+ for item in sorted(imports):
+ print(item)
+
+@task
+def find_package(ctx, name):
+ ctx.run('pip freeze | grep -i {}'.format(name))
+
+####################################################################################################
+
+@task()
+def update_git_sha(ctx):
+ # Fixme: wrong workflow, must tag the last commit
+ result = ctx.run('git describe --tags --abbrev=0 --always', hide='out')
+ tag = result.stdout.strip()
+ print(f"Tag is {tag}")
+ if tag.startswith('v'):
+ version = tag[1:]
+ version = version.replace('-branched', '')
+ else:
+ version = tag
+ if not re.match('\d+(\.\d+(\.\d+)?)?', version):
+ raise ValueError('Invalid version {}'.format(version))
+ result = ctx.run('git rev-parse HEAD', hide='out')
+ sha = result.stdout.strip()
+ print(f"SHA {sha}")
+ print(f"Tag {tag}")
+ print(f"Version {version}")
+ filename = Path(ctx.Package, '__init__.py')
+ with open(str(filename), 'r') as fh:
+ lines = fh.readlines()
+ with open(filename, 'w') as fh:
+ for line in lines:
+ if line.startswith('__version__'):
+ line = "__version__ = '{}'\n".format(version)
+ if line.startswith('GIT_TAG'):
+ line = "GIT_TAG = '{}'\n".format(tag)
+ if line.startswith('GIT_SHA'):
+ line = "GIT_SHA = '{}'\n".format(sha)
+ fh.write(line)
+
+####################################################################################################
+
+@task()
+def clean(ctx):
+ for directory in ('build', 'dist', 'PySpice.egg-info'):
+ shutil.rmtree(directory, ignore_errors=True)
+
+@task()
+def show_python_site(ctx):
+ ctx.run('python3 -m site')
+
+@task(update_git_sha)
+def build(ctx):
+ ctx.run('python3 setup.py build')
+
+@task(build)
+def install(ctx):
+ ctx.run('python3 setup.py install')
+
+# @task(clean, build)
+# def sdist(ctx):
+# ctx.run('python3 setup.py sdist')
+
+# @task(clean, build)
+# def bdist(ctx):
+# ctx.run('python3 setup.py bdist')
+
+@task(clean, build)
+def wheel(ctx):
+ ctx.run('python3 setup.py sdist')
+ ctx.run('python3 setup.py bdist_wheel')
+
+@task(wheel)
+def upload(ctx):
+ # Twine documentation: https://pypi.org/project/twine/
+ # ctx.run('twine register dist/*whl')
+ # Sign using
+ # BA24CE0F65CB8C67 Fabrice SALVAIRE
+ # registered on key servers: hkps://hkps.pool.sks-keyservers.net hkp://pgp.mit.edu
+ ctx.run('gpg --detach-sign --armor dist/*tar.gz')
+ ctx.run('gpg --detach-sign --armor dist/*whl')
+ # Binary wheel 'CodeReview-1.1.0-cp310-cp310-linux_x86_64.whl' has an unsupported platform tag 'linux_x86_64'.
+ # see https://github.com/pypa/manylinux
+ # see https://github.com/pypa/auditwheel
+ ctx.run('twine upload dist/*tar.gz')
+
+def _get_pipy_json():
+ import requests
+ response = requests.get('https://pypi.org/pypi/PySpice/json')
+ assert(response.status_code == requests.codes.ok)
+ return response.json()
+
+@task()
+def get_pypi_json(ctx):
+ import json
+ print(json.dumps(_get_pipy_json(), sort_keys=True, indent=4))
+
+@task()
+def get_wheel(ctx):
+ # https://dzone.com/articles/package-signing-in-pip-it-works-in-a-roundabout-so
+ data = _get_pipy_json()
+ version = data['info']['version']
+ filename = data['urls'][0]['filename']
+ wheel_url = data['urls'][0]['url']
+ print('Get version {}'.format(version))
+ ctx.run('curl --output {} {}'.format(filename, wheel_url))
+ asc_suffix = '.asc'
+ filename_asc = filename + asc_suffix
+ ctx.run('curl --output {} {}'.format(filename_asc, wheel_url + asc_suffix))
+ # ctx.run('gpg --keyserver pgp.mit.edu --search-keys {}'.format(key_id))
+ ctx.run('gpg --verify {} {}'.format(filename_asc, filename))
+
+####################################################################################################
+
+@task()
+def get_github_tar_sha(ctx):
+ # Fixme: check git sha
+ result = ctx.run('git describe --tags --abbrev=0 --always', hide='out')
+ tag = result.stdout.strip()
+ url = 'https://github.com/FabriceSalvaire/PySpice/archive/{}.tar.gz'.format(tag)
+ print('Get', url)
+ import hashlib
+ import requests
+ response = requests.get(url, allow_redirects=True)
+ assert(response.status_code == requests.codes.ok)
+ sha = hashlib.sha256(response.content)
+ print(sha.hexdigest())
+
diff --git a/tasks/scripts/Makefile b/tasks/scripts/Makefile
new file mode 100644
index 0000000..45e869a
--- /dev/null
+++ b/tasks/scripts/Makefile
@@ -0,0 +1,18 @@
+# -*- Makefile -*-
+
+####################################################################################################
+
+all: code-review.rcc CodeReviewRessource.py
+
+####################################################################################################
+
+%.rcc : %.qrc
+ rcc-qt5 -binary $< -o $@
+
+CodeReviewRessource.py : code-review.qrc
+ pyrcc5 -o $@ $<
+
+####################################################################################################
+
+clean:
+ rm *.py *.rcc
diff --git a/tasks/scripts/get-material-icon b/tasks/scripts/get-material-icon
new file mode 100755
index 0000000..61673a4
--- /dev/null
+++ b/tasks/scripts/get-material-icon
@@ -0,0 +1,184 @@
+#! /usr/bin/env python3
+
+####################################################################################################
+
+# https://material.io/tools/icons/static/icons/baseline-save-24px.svg
+# https://material.io/tools/icons/static/icons/baseline-save-black-36.zip
+
+# https://material.io/tools/icons/static/icons/outline-save-black-36.zip
+# https://material.io/tools/icons/static/icons/round-save-black-36.zip
+# https://material.io/tools/icons/static/icons/sharp-save-black-36.zip
+# https://material.io/tools/icons/static/icons/twotone-save-black-36.zip
+
+# https://material.io/tools/icons/static/icons/baseline-save-white-36.zip
+
+# 1x/baseline_save_black_18dp.png: PNG image data, 18 x 18, 8-bit gray+alpha, non-interlaced
+# 1x/baseline_save_black_24dp.png: PNG image data, 24 x 24, 8-bit gray+alpha, non-interlaced
+# 1x/baseline_save_black_36dp.png: PNG image data, 36 x 36, 8-bit gray+alpha, non-interlaced
+# 1x/baseline_save_black_48dp.png: PNG image data, 48 x 48, 8-bit gray+alpha, non-interlaced
+
+# 2x/baseline_save_black_18dp.png: PNG image data, 36 x 36, 8-bit gray+alpha, non-interlaced
+# 2x/baseline_save_black_24dp.png: PNG image data, 48 x 48, 8-bit gray+alpha, non-interlaced
+# 2x/baseline_save_black_36dp.png: PNG image data, 72 x 72, 8-bit gray+alpha, non-interlaced
+# 2x/baseline_save_black_48dp.png: PNG image data, 96 x 96, 8-bit gray+alpha, non-interlaced
+
+####################################################################################################
+
+from pathlib import Path
+from zipfile import ZipFile
+import argparse
+import os
+import shutil
+import tempfile
+import urllib3
+
+####################################################################################################
+
+parser = argparse.ArgumentParser(description='Fetch material icon.')
+
+parser.add_argument(
+ 'src_name', metavar='NAME',
+ help='icon name',
+)
+
+parser.add_argument(
+ '--dst-name',
+ default=None,
+ help='dst name, default is same name',
+)
+
+parser.add_argument(
+ '--style',
+ default='baseline',
+ help='style: [baseline], outline, round, twotone, sharp',
+)
+
+parser.add_argument(
+ '--color',
+ default='black',
+ help='color: [black], white',
+)
+
+args = parser.parse_args()
+
+####################################################################################################
+
+urllib3.disable_warnings()
+
+####################################################################################################
+
+class MaterialIconFetcher:
+
+ SCALE = (1, 2)
+ DP = (18, 24, 36, 48)
+
+ ##############################################
+
+ def __init__(self, icons_path, theme):
+
+ self._icons_path = Path(str(icons_path)).resolve()
+ self._theme = str(theme)
+ self._theme_path = self._icons_path.joinpath(self._theme)
+
+ if not self._icons_path.exists():
+ os.mkdir(self._icons_path)
+ if not self._theme_path.exists():
+ os.mkdir(self._theme_path)
+
+ self._http = urllib3.PoolManager()
+
+ # with tempfile.TemporaryDirectory() as tmp_directory:
+ self._tmp_directory = tempfile.TemporaryDirectory()
+ self._tmp_directory_path = Path(self._tmp_directory.name)
+ print('tmp_directory', self._tmp_directory_path)
+
+ ##############################################
+
+ def _fetch_ressource(self, url):
+ print('Fetch', url, '...')
+ request = self._http.request('GET', url)
+ return request.data
+
+ ##############################################
+
+ def _fetch_png_icon(self, **kwargs):
+
+ # https://material.io/tools/icons/static/icons/baseline-save-black-36.zip
+ root = 'https://material.io/tools/icons/static/icons/'
+ url_pattern = '{style}-{name}-{color}-{dp}.zip'
+ filename = url_pattern.format(**kwargs)
+ url = root + filename
+
+ data = self._fetch_ressource(url)
+
+ return filename, data
+
+ ##############################################
+
+ def _extract_png_archive(self, **kwargs):
+
+ filename, data = self._fetch_png_icon(**kwargs)
+ zip_path = self._tmp_directory_path.joinpath(filename)
+ with open(zip_path, 'wb') as fh:
+ fh.write(data)
+ with ZipFile(zip_path, 'r') as zip_archive:
+ zip_archive.extractall(self._tmp_directory_path)
+
+ ##############################################
+
+ def fetch_icon(self, src_name, dst_name, style, color):
+
+ kwargs = dict(src_name=src_name, dst_name=dst_name, style=style, color=color)
+
+ for dp in self.DP:
+ self._extract_png_archive(name=src_name, dp=dp, **kwargs)
+
+ print()
+ for scale in self.SCALE:
+ for dp in self.DP:
+ dst_kwargs = dict(kwargs)
+ dst_kwargs.update(dict(scale=scale, dp=dp))
+
+ # 1x/baseline_save_black_18dp.png
+ filename_pattern = '{style}_{src_name}_{color}_{dp}dp.png'
+ src_path = self._tmp_directory_path.joinpath(
+ '{scale}x'.format(**dst_kwargs),
+ filename_pattern.format(**dst_kwargs),
+ )
+
+ if scale > 1:
+ size_directory = '{dp}x{dp}@{scale}'.format(**dst_kwargs)
+ else:
+ size_directory = '{dp}x{dp}'.format(**dst_kwargs)
+ size_path = self._theme_path.joinpath(size_directory)
+ if not size_path.exists():
+ os.mkdir(size_path)
+ filename_pattern = '{dst_name}-{color}.png'
+ filename = filename_pattern.format(**dst_kwargs)
+ dst_path = size_path.joinpath(filename)
+
+ # print('Copy', src_path, dst_path)
+ shutil.copyfile(src_path, dst_path)
+
+ rcc_pattern = 'icons/{}/{}/{}'
+ rcc_line = rcc_pattern.format(self._theme, size_directory, filename)
+ if dp != 36:
+ rcc_line = ''.format(rcc_line)
+ rcc_line = ' '*8 + rcc_line
+ print(rcc_line)
+
+####################################################################################################
+
+root_path = Path(__file__).resolve().parents[1]
+icons_path = root_path.joinpath('share', 'icons')
+theme = 'material'
+print('Icons path:', icons_path, theme)
+
+fetcher = MaterialIconFetcher(icons_path, theme)
+
+fetcher.fetch_icon(
+ args.src_name,
+ args.dst_name or args.src_name.replace('_', '-'),
+ args.style,
+ args.color,
+)
diff --git a/tasks/scripts/make-icon-png b/tasks/scripts/make-icon-png
new file mode 100755
index 0000000..b982a8d
--- /dev/null
+++ b/tasks/scripts/make-icon-png
@@ -0,0 +1,80 @@
+#! /usr/bin/env python3
+
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+####################################################################################################
+
+from pathlib import Path
+import argparse
+import subprocess
+
+####################################################################################################
+
+parser = argparse.ArgumentParser(description='Generate PNG files from a SVG.')
+parser.add_argument(
+ 'svg_path', metavar='file.svg',
+ help='SVG PATH',
+)
+args = parser.parse_args()
+
+####################################################################################################
+
+theme = 'material'
+DP = (36, 48)
+SCALE = (1, 2)
+
+svg_path = Path(args.svg_path).resolve()
+theme_path = svg_path.parents[1].joinpath(theme)
+print('Theme path:', theme_path)
+
+####################################################################################################
+
+inkscape_options = [
+ '--export-area-page',
+ '--export-background=white',
+ '--export-background-opacity=0',
+]
+
+####################################################################################################
+
+def run_inkscape(svg_path, dp, scale):
+ filename = svg_path.name.replace('.svg', '.png')
+ if scale > 1:
+ size_directory = '{dp}x{dp}@{scale}'.format(dp=dp, scale=scale)
+ else:
+ size_directory = '{dp}x{dp}'.format(dp=dp)
+ png_path = theme_path.joinpath(size_directory, filename)
+ command = (
+ 'inkscape',
+ *inkscape_options,
+ '--export-png={}'.format(png_path),
+ # --export-width=
+ '--export-height={}'.format(dp*scale),
+ str(svg_path),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+####################################################################################################
+
+for dp in DP:
+ for scale in SCALE:
+ run_inkscape(svg_path, dp, scale)
diff --git a/tasks/scripts/make-logo-png b/tasks/scripts/make-logo-png
new file mode 100755
index 0000000..d5dd866
--- /dev/null
+++ b/tasks/scripts/make-logo-png
@@ -0,0 +1,67 @@
+#! /usr/bin/env python3
+
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+####################################################################################################
+
+from pathlib import Path
+import argparse
+import subprocess
+
+####################################################################################################
+
+parser = argparse.ArgumentParser(description='Generate PNG files from a SVG.')
+parser.add_argument(
+ 'svg_path', metavar='file.svg',
+ help='SVG PATH',
+)
+args = parser.parse_args()
+
+svg_path = Path(args.svg_path).resolve()
+
+####################################################################################################
+
+sizes = (32, 64, 96, 128, 256, 512)
+
+inkscape_options = [
+ '--export-area-page',
+ '--export-background=white',
+ '--export-background-opacity=0',
+]
+
+####################################################################################################
+
+def run_inkscape(svg_path, size):
+ command = (
+ 'inkscape',
+ *inkscape_options,
+ '--export-png=logo-{}.png'.format(size),
+ # --export-width=
+ '--export-height={}'.format(size),
+ str(svg_path),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+####################################################################################################
+
+for size in sizes:
+ run_inkscape(svg_path, size)
diff --git a/tasks/scripts/translate b/tasks/scripts/translate
new file mode 100755
index 0000000..d1f0d4c
--- /dev/null
+++ b/tasks/scripts/translate
@@ -0,0 +1,534 @@
+#! /usr/bin/env python3
+
+####################################################################################################
+#
+# CodeReview - A Code Review GUI
+# Copyright (C) 2019 Fabrice Salvaire
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+#
+####################################################################################################
+
+"""Program to manage and translate Qt translation files.
+
+It features:
+
+* lupdate, linguist, lrelease
+* conversion to/from .po file
+* poedit
+* translation using Google Translation
+
+Actions are run in the right order.
+
+"""
+
+####################################################################################################
+
+from pathlib import Path
+import argparse
+import os
+import subprocess
+
+from googletrans import Translator
+# https://py-googletrans.readthedocs.io/en/latest/
+
+####################################################################################################
+
+DEFAULT_LANGUAGES = (
+ 'fr_FR',
+)
+
+# Fixme: project dependent
+source_path = Path(__file__).resolve().parents[1]
+qml_path = source_path.joinpath('qml')
+py_path = source_path.joinpath('CodeReview')
+translation_path = source_path.joinpath('share', 'translations')
+base_name = 'code-review'
+
+####################################################################################################
+
+parser = argparse.ArgumentParser(description='Translate Qt Application.')
+
+parser.add_argument(
+ '--language',
+ default=None,
+ help='languages, default {}'.format(DEFAULT_LANGUAGES),
+)
+
+parser.add_argument(
+ '--update',
+ default=False,
+ action='store_true',
+ help='run lupdate',
+)
+
+parser.add_argument(
+ '--translate',
+ default=False,
+ action='store_true',
+ help='translate using Google Translate',
+)
+
+parser.add_argument(
+ '--convert-to-po',
+ default=False,
+ action='store_true',
+ help='convert to .po file',
+)
+
+parser.add_argument(
+ '--convert-from-po',
+ default=False,
+ action='store_true',
+ help='convert from .po file',
+)
+
+parser.add_argument(
+ '--poedit',
+ default=False,
+ action='store_true',
+ help='run poedit',
+)
+
+parser.add_argument(
+ '--linguist',
+ default=False,
+ action='store_true',
+ help='run linguist',
+)
+
+parser.add_argument(
+ '--release',
+ default=False,
+ action='store_true',
+ help='run lrelease',
+)
+
+args = parser.parse_args()
+
+####################################################################################################
+
+def _clean_path(path):
+ return Path(str(path)).resolve()
+
+####################################################################################################
+
+class TanslationManager:
+
+ ##############################################
+
+ def __init__(self, translation_path, base_name, py_path, qml_path):
+
+ self._translation_path = _clean_path(translation_path)
+ self._base_name = base_name
+ self._py_path = _clean_path(py_path)
+ self._qml_path = _clean_path(qml_path)
+
+ if not self._translation_path.exists():
+ os.mkdir(self._translation_path)
+
+ ##############################################
+
+ def ts_path(self, scope, language):
+ if scope:
+ filename = '{}.{}.{}.ts'.format(self._base_name, scope, language)
+ else:
+ filename = '{}.{}.ts'.format(self._base_name, language)
+ return self._translation_path.joinpath(filename)
+
+ def po_path(self, scope, language):
+ filename = '{}.{}.{}.po'.format(self._base_name, scope, language)
+ return self._translation_path.joinpath(filename)
+
+ ##############################################
+
+ def run_lupdate(self, language):
+
+ # Usage:
+ # lupdate [options] [project-file]...
+ # lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file
+ #
+ # lupdate is part of Qt's Linguist tool chain. It extracts translatable
+ # messages from Qt UI files, C++, Java and JavaScript/QtScript source code.
+ # Extracted messages are stored in textual translation source files (typically
+ # Qt TS XML). New and modified messages can be merged into existing TS files.
+ #
+ # Options:
+ # -help Display this information and exit.
+ # -no-obsolete
+ # Drop all obsolete and vanished strings.
+ # -extensions [,]...
+ # Process files with the given extensions only.
+ # The extension list must be separated with commas, not with whitespace.
+ # Default: 'java,jui,ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml,qrc'.
+ # -pluralonly
+ # Only include plural form messages.
+ # -silent
+ # Do not explain what is being done.
+ # -no-sort
+ # Do not sort contexts in TS files.
+ # -no-recursive
+ # Do not recursively scan the following directories.
+ # -recursive
+ # Recursively scan the following directories (default).
+ # -I or -I
+ # Additional location to look for include files.
+ # May be specified multiple times.
+ # -locations {absolute|relative|none}
+ # Specify/override how source code references are saved in TS files.
+ # Guessed from existing TS files if not specified.
+ # Default is absolute for new files.
+ # -no-ui-lines
+ # Do not record line numbers in references to UI files.
+ # -disable-heuristic {sametext|similartext|number}
+ # Disable the named merge heuristic. Can be specified multiple times.
+ # -pro
+ # Name of a .pro file. Useful for files with .pro file syntax but
+ # different file suffix. Projects are recursed into and merged.
+ # -pro-out
+ # Virtual output directory for processing subsequent .pro files.
+ # -pro-debug
+ # Trace processing .pro files. Specify twice for more verbosity.
+ # -source-language [_]
+ # Specify the language of the source strings for new files.
+ # Defaults to POSIX if not specified.
+ # -target-language [_]
+ # Specify the language of the translations for new files.
+ # Guessed from the file name if not specified.
+ # -tr-function-alias {+=,=}[,{+=,=}]...
+ # With +=, recognize as an alternative spelling of .
+ # With =, recognize as the only spelling of .
+ # Available s (with their currently defined aliases) are:
+ # Q_DECLARE_TR_FUNCTIONS (=Q_DECLARE_TR_FUNCTIONS)
+ # QT_TR_N_NOOP (=QT_TR_N_NOOP)
+ # QT_TRID_N_NOOP (=QT_TRID_N_NOOP)
+ # QT_TRANSLATE_N_NOOP (=QT_TRANSLATE_N_NOOP)
+ # QT_TRANSLATE_N_NOOP3 (=QT_TRANSLATE_N_NOOP3)
+ # QT_TR_NOOP (=QT_TR_NOOP)
+ # QT_TRID_NOOP (=QT_TRID_NOOP)
+ # QT_TRANSLATE_NOOP (=QT_TRANSLATE_NOOP)
+ # QT_TRANSLATE_NOOP3 (=QT_TRANSLATE_NOOP3)
+ # QT_TR_NOOP_UTF8 (=QT_TR_NOOP_UTF8)
+ # QT_TRANSLATE_NOOP_UTF8 (=QT_TRANSLATE_NOOP_UTF8)
+ # QT_TRANSLATE_NOOP3_UTF8 (=QT_TRANSLATE_NOOP3_UTF8)
+ # findMessage (=findMessage)
+ # qtTrId (=qtTrId)
+ # tr (=tr)
+ # trUtf8 (=trUtf8)
+ # translate (=translate)
+ # qsTr (=qsTr)
+ # qsTrId (=qsTrId)
+ # qsTranslate (=qsTranslate)
+ # -ts ...
+ # Specify the output file(s). This will override the TRANSLATIONS.
+ # -version
+ # Display the version of lupdate and exit.
+ # @lst-file
+ # Read additional file names (one per line) or includepaths (one per
+ # line, and prefixed with -I) from lst-file.
+
+ command = (
+ 'lupdate-qt5',
+ '-extensions', 'qml,js',
+ '-source-language', 'en_GB',
+ '-target-language', language,
+ str(self._qml_path),
+ '-ts', str(self.ts_path('qml', language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ py_filenames = []
+ for root, _, filenames in os.walk(self._py_path):
+ root = Path(root)
+ for filename in filenames:
+ filename = Path(filename)
+ if filename.suffix == '.py':
+ filename = str(root.joinpath(filename))
+ py_filenames.append(filename)
+
+ command = (
+ 'pylupdate5',
+ '-verbose',
+ *py_filenames,
+ '-ts', str(self.ts_path('py', language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ # Fixme: pylupdate5 don't support qml and folder !!!
+
+ # print('Fix obsolete')
+ # with open(ts_path, 'r') as fh:
+ # content = fh.readlines()
+ # with open(ts_path, 'w') as fh:
+ # for line in content:
+ # line = line.replace('type="obsolete"', '')
+ # line = line.replace('', '')
+ # fh.write(line)
+
+ ##############################################
+
+ def run_linguist(self, language):
+
+ for scope in ('qml', 'py'):
+ command = (
+ 'linguist-qt5',
+ str(self.ts_path(scope, language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ ##############################################
+
+ def run_poedit(self, language):
+
+ command = (
+ 'poedit',
+ str(self.po_path(language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ ##############################################
+
+ def run_lrelease(self, language):
+
+ # Usage:
+ # lrelease [options] project-file
+ # lrelease [options] ts-files [-qm qm-file]
+ #
+ # lrelease is part of Qt's Linguist tool chain. It can be used as a
+ # stand-alone tool to convert XML-based translations files in the TS
+ # format into the 'compiled' QM format used by QTranslator objects.
+ #
+ # Options:
+ # -help Display this information and exit
+ # -idbased
+ # Use IDs instead of source strings for message keying
+ # -compress
+ # Compress the QM files
+ # -nounfinished
+ # Do not include unfinished translations
+ # -removeidentical
+ # If the translated text is the same as
+ # the source text, do not include the message
+ # -markuntranslated
+ # If a message has no real translation, use the source text
+ # prefixed with the given string instead
+ # -silent
+ # Do not explain what is being done
+ # -version
+ # Display the version of lrelease and exit
+
+ command = (
+ 'lconvert-qt5',
+ '-i',
+ str(self.ts_path('qml', language)),
+ str(self.ts_path('py', language)),
+ '-o', str(self.ts_path(None, language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ command = (
+ 'lrelease-qt5',
+ str(self.ts_path(None, language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ ##############################################
+
+ def run_lconvert(self, language, format):
+
+ # Usage:
+ # lconvert [options] [...]
+ #
+ # lconvert is part of Qt's Linguist tool chain. It can be used as a
+ # stand-alone tool to convert and filter translation data files.
+ # The following file formats are supported:
+ #
+ # qm - Traductions Qt compilées
+ # pot - Fichiers de modèle de localisation GNU Gettext
+ # qph - Qt Linguist "livre de phrases"
+ # ts - Sources de traduction Qt
+ # po - Fichiers de localisation GNU Gettext
+ # xlf - Fichiers de localisation XLIFF
+ #
+ # If multiple input files are specified, they are merged with
+ # translations from later files taking precedence.
+ #
+ # Options:
+ # -h
+ # -help Display this information and exit.
+ #
+ # -i
+ # -input-file
+ # Specify input file. Use if might start with a dash.
+ # This option can be used several times to merge inputs.
+ # May be '-' (standard input) for use in a pipe.
+ #
+ # -o
+ # -output-file
+ # Specify output file. Default is '-' (standard output).
+ #
+ # -if
+ # -input-format
+ # Specify input format for subsequent s.
+ # The format is auto-detected from the file name and defaults to 'ts'.
+ #
+ # -of
+ # -output-format
+ # Specify output format. See -if.
+ #
+ # -drop-tags
+ # Drop named extra tags when writing TS or XLIFF files.
+ # May be specified repeatedly.
+ #
+ # -drop-translations
+ # Drop existing translations and reset the status to 'unfinished'.
+ # Note: this implies --no-obsolete.
+ #
+ # -source-language [_]
+ # Specify/override the language of the source strings. Defaults to
+ # POSIX if not specified and the file does not name it yet.
+ #
+ # -target-language [_]
+ # Specify/override the language of the translation.
+ # The target language is guessed from the file name if this option
+ # is not specified and the file contents name no language yet.
+ #
+ # -no-obsolete
+ # Drop obsolete messages.
+ #
+ # -no-finished
+ # Drop finished messages.
+ #
+ # -no-untranslated
+ # Drop untranslated messages.
+ #
+ # -sort-contexts
+ # Sort contexts in output TS file alphabetically.
+ #
+ # -locations {absolute|relative|none}
+ # Override how source code references are saved in TS files.
+ # Default is absolute.
+ #
+ # -no-ui-lines
+ # Drop line numbers from references to UI files.
+ #
+ # -verbose
+ # be a bit more verbose
+ #
+ # Long options can be specified with only one leading dash, too.
+ #
+ # Return value:
+ # 0 on success
+ # 1 on command line parse failures
+ # 2 on read failures
+ # 3 on write failures
+
+ is_po = format == 'po'
+
+ for scope in ('qml', 'py'):
+ command = (
+ 'lconvert-qt5',
+ '-i' if is_po else '-o', str(self.ts_path(scope, language)),
+ '-o' if is_po else '-i', str(self.po_path(language)),
+ )
+ print('>', ' '.join(command))
+ subprocess.check_call(command)
+
+ ##############################################
+
+ def translate(self, language):
+ for scope in ('qml', 'py'):
+ self.translate_scope(scope, language)
+
+ ##############################################
+
+ def translate_scope(self, scope, language):
+
+ print('Translate {} {} ...'.format(scope, language))
+
+ language_code = language[:2]
+ if language_code == 'en':
+ return
+
+ translator = Translator(
+ service_urls=[
+ 'translate.google.' + language_code,
+ 'translate.google.com',
+ ],
+ )
+
+ def translate_string(source):
+ return translator.translate(source, src='en', dest=language_code).text
+
+ ts_path = self.ts_path(scope, language)
+ with open(ts_path, 'r') as fh:
+ lines = list(fh.readlines())
+
+ with open(ts_path, 'w') as fh:
+ source = None
+ for line in lines:
+ striped_line = line.strip()
+ if striped_line.startswith(''):
+ source = striped_line[striped_line.find('>')+1:striped_line.rfind('<')]
+ elif striped_line.startswith(''):
+ old_translation = striped_line[striped_line.find('>')+1:striped_line.rfind('<')]
+ if not old_translation:
+ translation = translate_string(source)
+ print()
+ print(source)
+ print(translation)
+ line = line.replace(' type="unfinished">', '>' + translation)
+ fh.write(line)
+
+####################################################################################################
+
+manager = TanslationManager(
+ translation_path,
+ base_name,
+ py_path,
+ qml_path,
+)
+
+if not args.language:
+ languages = DEFAULT_LANGUAGES
+else:
+ languages = [x for x in [x.strip() for x in args.languages.split(',')] if x]
+
+if args.update:
+ for language in languages:
+ manager.run_lupdate(language)
+if args.translate:
+ for language in languages:
+ manager.translate(language)
+if args.convert_to_po:
+ for language in languages:
+ manager.run_lconvert(language, 'po')
+if args.poedit:
+ for language in languages:
+ manager.run_poedit(language)
+if args.convert_from_po:
+ for language in languages:
+ manager.run_lconvert(language, 'ts')
+if args.linguist:
+ for language in languages:
+ manager.run_linguist(language)
+if args.release:
+ for language in languages:
+ manager.run_lrelease(language)
diff --git a/tasks/scripts/upload-to-pypi b/tasks/scripts/upload-to-pypi
new file mode 100755
index 0000000..36398ab
--- /dev/null
+++ b/tasks/scripts/upload-to-pypi
@@ -0,0 +1,11 @@
+#! /bin/bash
+
+# bdist
+# python setup.py check --verbose --metadata --restructuredtext --strict && \
+# python setup.py register sdist upload
+
+python setup.py bdist_wheel
+
+twine register dist/*whl
+gpg --detach-sign -a dist/*whl
+twine upload dist/*
diff --git a/tools/RstFactory.py b/tools/RstFactory.py
deleted file mode 100644
index 0957398..0000000
--- a/tools/RstFactory.py
+++ /dev/null
@@ -1,242 +0,0 @@
-####################################################################################################
-#
-# CodeReview - A Code Review GUI
-# Copyright (C) 2015 Fabrice Salvaire
-#
-# This program 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.
-#
-# This program 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 this program. If
-# not, see .
-#
-####################################################################################################
-
-""" This module provides tools to build a Sphinx API documentation. """
-
-####################################################################################################
-
-import os
-
-####################################################################################################
-
-class RstFactory(object):
-
- """ This class build recursively a Sphinx API documentation from a Python root module. """
-
- init_file_name = '__init__.py'
-
- end_marker = """
-.. End
-"""
-
- ##############################################
-
- def __init__(self, module_path, rst_directory, excluded_directory):
-
- self._rst_directory = os.path.realpath(rst_directory)
- self._root_module_path = os.path.realpath(module_path)
-
- self._excluded_directory = [os.path.join(self._root_module_path, x) for x in excluded_directory]
- self._root_module_name = os.path.basename(self._root_module_path)
-
- print("RST API Path: ", self._rst_directory)
- print("Root Module Path:", self._root_module_path)
- print("Root Module Name:", self._root_module_name)
- print('Exclude:', '\n '.join(self._excluded_directory))
-
- if not os.path.exists(self._rst_directory):
- os.mkdir(self._rst_directory)
-
- self._process_recursively()
-
- ##############################################
-
- def _process_recursively(self):
-
- """ Process recursively the inner Python files and directories. """
-
- for module_path, sub_directories, files in os.walk(self._root_module_path, followlinks=True):
- for sub_directory in list(sub_directories):
- if os.path.join(module_path, sub_directory) in self._excluded_directory:
- del sub_directories[sub_directories.index(sub_directory)]
- if self.is_python_directory_module(module_path):
- python_files = [file_name
- for file_name in files
- if self.is_python_file(file_name)]
- sub_modules = [sub_directory
- for sub_directory in sub_directories
- if self.is_python_directory_module(os.path.join(module_path, sub_directory))]
- if python_files or sub_modules:
- self._process_directory_module(module_path, sorted(python_files), sorted(sub_modules))
-
- ##############################################
-
- def _process_directory_module(self, module_path, python_files, sub_modules):
-
- directory_module_name = os.path.basename(module_path)
- directory_module_python_path = self.module_path_to_python_path(module_path)
- dst_directory = self.join_rst_path(self.python_path_to_path(directory_module_python_path))
- print()
- print("Directory Module Name:", directory_module_name)
- print("Directory Module Python Path:", directory_module_python_path)
- print("Dest Path:", dst_directory)
-
- if not os.path.exists(dst_directory):
- os.mkdir(dst_directory)
-
- # Generate a RST file per module
- module_names = []
- for file_name in python_files:
- module_name = self.filename_to_module(file_name)
- module_names.append(module_name)
- print(" Module:", module_name)
- rst = self._generate_rst_module(directory_module_python_path, module_name)
- rst_file_name = os.path.join(dst_directory, module_name + '.rst')
- with open(rst_file_name, 'w') as f:
- f.write(rst)
-
- # Generate the TOC RST file
- rst = self._generate_toc(directory_module_name, sorted(module_names + sub_modules))
- rst += '\n'
- rst += self._generate_automodule(directory_module_python_path)
- rst += self.end_marker
- rst_file_name = os.path.join(os.path.dirname(dst_directory), directory_module_name + '.rst')
- with open(rst_file_name, 'w') as f:
- f.write(rst)
-
- ##############################################
-
- @staticmethod
- def is_python_directory_module(path):
-
- return os.path.exists(os.path.join(path, RstFactory.init_file_name))
-
- ##############################################
-
- @staticmethod
- def is_python_file(file_name):
-
- return (file_name.endswith('.py') and
- file_name != RstFactory.init_file_name and
- 'flymake'not in file_name)
-
- ##############################################
-
- @staticmethod
- def path_to_python_path(path):
-
- return path.replace(os.path.sep, '.')
-
- ##############################################
-
- @staticmethod
- def python_path_to_path(python_path):
-
- return python_path.replace('.', os.path.sep)
-
- ##############################################
-
- @staticmethod
- def join_python_path(*args):
-
- return '.'.join(args)
-
- ##############################################
-
- @staticmethod
- def filename_to_module(file_name):
-
- return file_name[:-3] # suppress '.py'
-
- ##############################################
-
- def module_path_to_python_path(self, path):
-
- return self.path_to_python_path(self._root_module_name + path[len(self._root_module_path):])
-
- ##############################################
-
- def join_rst_path(self, path):
-
- return os.path.join(self._rst_directory, path)
-
- ##############################################
-
- def _generate_title(self, module_name):
-
- mod_rst = ' :mod:`'
-
- template = """
-%(header_line)s
-%(mod)s%(module_name)s`
-%(header_line)s"""
-
- rst = template.lstrip() % dict(
- module_name=module_name,
- mod=mod_rst,
- header_line='*'*(len(module_name) + len(mod_rst) +2),
- )
-
- return rst
-
- ##############################################
-
- def _generate_toc(self, directory_module_name, module_names):
-
- template = """%(title)s
-
-.. toctree::
-"""
-
- rst = template.lstrip() % dict(
- title=self._generate_title(directory_module_name),
- )
-
- for module_name in module_names:
- rst += ' '*2 + os.path.join(directory_module_name, module_name) + '\n'
-
- return rst
-
- ##############################################
-
- def _generate_automodule(self, module_path):
-
- template = """
-.. automodule:: %(module_path)s
- :members:
- :show-inheritance:
-"""
-
- rst = template.lstrip() % dict(
- module_path=module_path,
- )
-
- return rst
-
- ##############################################
-
- def _generate_rst_module(self, module_path, module_name):
-
- template = """%(title)s
-
-%(automodule)s
-"""
-
- rst = template.lstrip() % dict(
- title=self._generate_title(module_name),
- automodule=self._generate_automodule(RstFactory.join_python_path(module_path, module_name))
- )
- rst += self.end_marker
-
- return rst
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/tools/generate-rst-api.sh b/tools/generate-rst-api.sh
deleted file mode 100755
index 9c772e7..0000000
--- a/tools/generate-rst-api.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-####################################################################################################
-
-api=doc/sphinx/source/api
-##old_api=doc/sphinx/old-api
-
-##mkdir -p ${old_api}
-#mv --backup=numbered $api ${old_api}
-
-echo
-echo Generate RST API files
-./tools/generate-rst-api
-
-echo
-echo Run Sphinx
-pushd doc/sphinx/
-./make-html #--clean
-popd
-
-##echo
-##echo Old api directory moved to
-##ls -l -h ${old_api}
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/tools/perl-grep b/tools/perl-grep
deleted file mode 100755
index d31cf01..0000000
--- a/tools/perl-grep
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /usr/bin/perl
-# -*- perl -*-
-
-exit(1)if (@ARGV < 2);
-
-my $pattern = $ARGV[0];
-my $filename = $ARGV[1];
-
-# print "$pattern $filename";
-
-open(INPUT, $filename);
-
-while ()
-{
- exit(0) if (/$pattern/);
-}
-
-exit(1);
-
-# End
diff --git a/tools/replace b/tools/replace
deleted file mode 100755
index 50bf4b0..0000000
--- a/tools/replace
+++ /dev/null
@@ -1,148 +0,0 @@
-#! /usr/bin/env python
-# -*- python -*-
-
-####################################################################################################
-#
-# CodeReview - Diff Viewer
-# Copyright (C) 2014 Salvaire Fabrice
-#
-# This program 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.
-#
-# This program 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 this program. If not, see .
-#
-####################################################################################################
-
-####################################################################################################
-
-import os
-import re
-import subprocess
-import sys
-
-from optparse import OptionParser
-
-####################################################################################################
-
-default_excluded_extension = ','.join(('~',
- '#',
- '.diff',
- '.pdf',
- '.pyc',
- '.tex',
- '.xml',
- '.txt',
- ))
-
-default_exclusion_pattern = '\.(bzr|git)'
-
-####################################################################################################
-#
-# Options
-#
-
-usage = 'usage: %prog [options]'
-
-parser = OptionParser(usage)
-
-parser.add_option('--root-tree-path',
- dest='root_tree_path',
- type='string', default='.',
- help='root path')
-
-parser.add_option('--exclude',
- dest='exclusion_pattern',
- type='string', default=default_exclusion_pattern,
- help='exclusion regexp [%s]' % (default_exclusion_pattern))
-
-parser.add_option('--exclude-extension',
- dest='excluded_extension',
- type='string', default=default_excluded_extension,
- help='exclude extension [%s]' % (default_excluded_extension))
-
-parser.add_option('--pattern',
- dest='pattern',
- type='string', default=None,
- help='pattern')
-
-parser.add_option('--new-pattern',
- dest='new_pattern',
- type='string', default=None,
- help='new pattern')
-
-opt, args = parser.parse_args()
-
-####################################################################################################
-
-def to_absolute_path(path):
-
- return os.path.abspath(os.path.expanduser(path))
-
-####################################################################################################
-
-program_path = os.path.dirname(os.path.abspath(__file__))
-perl_grep = os.path.join(program_path, 'perl-grep')
-
-root_tree_path = to_absolute_path(opt.root_tree_path)
-
-if opt.pattern is None:
- sys.exit(1)
-
-excluded_extension = opt.excluded_extension.split(',')
-
-if opt.exclusion_pattern is not None:
- exclude_re = re.compile(opt.exclusion_pattern)
-else:
- exclude_re = None
-
-####################################################################################################
-
-def process_file(absolut_file_name):
-
- return_code = subprocess.call([perl_grep, opt.pattern, absolut_file_name])
- if return_code == 0:
- print(absolut_file_name)
-
- if opt.new_pattern is not None:
- subprocess.call(['sed',
- '--in-place=~',
- 's/%s/%s/g' % (opt.pattern, opt.new_pattern),
- absolut_file_name,
- ])
-
-####################################################################################################
-
-for root, dirs, files in os.walk(root_tree_path):
- for file_name in files:
-
- absolut_file_name = os.path.join(root, file_name)
-
- skipped = False
- for extension in excluded_extension:
- if file_name.endswith(extension):
- # print 'Exclude for extension', extension, file_name
- skipped = True
- break
-
- if not skipped and exclude_re.search(absolut_file_name) is not None:
- # print 'Exclude for regexp', file_name
- skipped = True
-
- if not skipped:
- process_file(absolut_file_name)
-
-sys.exit(0)
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..a0b9b4d
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,11 @@
+# tox (https://tox.readthedocs.io/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py36
+
+[testenv]
+commands = pytest unit-test
+deps = pytest
diff --git a/unit-test/test_AttributeDictionaryInterface.py b/unit-test-broken/test_AttributeDictionaryInterface.py
similarity index 91%
rename from unit-test/test_AttributeDictionaryInterface.py
rename to unit-test-broken/test_AttributeDictionaryInterface.py
index 42843e1..be2b71a 100644
--- a/unit-test/test_AttributeDictionaryInterface.py
+++ b/unit-test-broken/test_AttributeDictionaryInterface.py
@@ -22,10 +22,11 @@
####################################################################################################
-from Babel.Tools.AttributeDictionaryInterface import (ExtendedDictionaryInterface,
- ReadOnlyAttributeDictionaryInterface,
- AttributeDictionaryInterface,
- AttributeDictionaryInterfaceDescriptor)
+from CodeReview.Tools.AttributeDictionaryInterface import (
+ ExtendedDictionaryInterface,
+ ReadOnlyAttributeDictionaryInterface,
+ AttributeDictionaryInterface,
+ AttributeDictionaryInterfaceDescriptor)
####################################################################################################
@@ -195,9 +196,3 @@ def test_iter(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/test_RawTextDocumentDiff.py b/unit-test-broken/test_RawTextDocumentDiff.py
similarity index 90%
rename from unit-test/test_RawTextDocumentDiff.py
rename to unit-test-broken/test_RawTextDocumentDiff.py
index e428a72..4d85952 100644
--- a/unit-test/test_RawTextDocumentDiff.py
+++ b/unit-test-broken/test_RawTextDocumentDiff.py
@@ -32,13 +32,13 @@ def test(self):
raw_text_document1 = RawTextDocument(text1)
raw_text_document2 = RawTextDocument(text2)
-
+
two_way_file_diff_factory = TwoWayFileDiffFactory()
file_diff = two_way_file_diff_factory.process(raw_text_document1, raw_text_document2)
file_diff.pretty_print()
file_diff.print_unidiff()
-
+
print('='*10)
replace_group = file_diff[1]
print(replace_group)
@@ -52,13 +52,13 @@ def test(self):
def test_empty(self):
text1 = ''
-
+
with open(self._join_data_path('test_file2.py')) as f:
text2 = f.read()
raw_text_document1 = RawTextDocument(text1)
raw_text_document2 = RawTextDocument(text2)
-
+
two_way_file_diff_factory = TwoWayFileDiffFactory()
file_diff = two_way_file_diff_factory.process(raw_text_document1, raw_text_document2)
@@ -70,9 +70,3 @@ def test_empty(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/test_TextDocumentDiffModel.py b/unit-test-broken/test_TextDocumentDiffModel.py
similarity index 92%
rename from unit-test/test_TextDocumentDiffModel.py
rename to unit-test-broken/test_TextDocumentDiffModel.py
index 488b6e0..dc5e29b 100644
--- a/unit-test/test_TextDocumentDiffModel.py
+++ b/unit-test-broken/test_TextDocumentDiffModel.py
@@ -41,14 +41,14 @@ def test(self):
text2 = f.read()
lexer = get_lexer_for_filename('data/test_file1.py', stripnl=False)
-
+
raw_text_document1 = RawTextDocument(text1)
raw_text_document2 = RawTextDocument(text2)
file_diff = TwoWayFileDiffFactory().process(raw_text_document1, raw_text_document2)
document_model1, document_model2 = TextDocumentDiffModelFactory().process(file_diff)
highlighted_text1 = HighlightedText(raw_text_document1, lexer)
-
+
print('Document 1:')
self._pretty_print(document_model1)
print('\nHighlighted Document 1:')
@@ -60,7 +60,7 @@ def test(self):
##############################################
def _pretty_print(self, document_model):
-
+
for text_block in document_model:
print('='*100)
print(text_block)
@@ -79,9 +79,3 @@ def _pretty_print(self, document_model):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/data/test_file1.py b/unit-test/data/test_file1.py
index 02943cd..68e6457 100644
--- a/unit-test/data/test_file1.py
+++ b/unit-test/data/test_file1.py
@@ -76,9 +76,3 @@ def function3(x):
adminiculi indigenis viribus a fundamento ipso carinae ad supremos usque carbasos aedificet
onerariam navem omnibusque armamentis instructam mari committat.
"""
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/data/test_file2.py b/unit-test/data/test_file2.py
index 80cb90e..4bc1b06 100644
--- a/unit-test/data/test_file2.py
+++ b/unit-test/data/test_file2.py
@@ -78,9 +78,3 @@ def function3(x):
adminiculi indigenis viribus a fundamento ipso carinae ad supremos usque carbasos aedificet
onerariam navem omnibusque armamentis instructam mari committat.
"""
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/data/test_file_template.py b/unit-test/data/test_file_template.py
index 7f78334..34a7ea3 100644
--- a/unit-test/data/test_file_template.py
+++ b/unit-test/data/test_file_template.py
@@ -25,5 +25,5 @@ def function2(x):
var1 = function2(10)
var2 = function2(20)
-
+
# this is the End
diff --git a/unit-test/test_EnumFactory.py b/unit-test/test_EnumFactory.py
index 714870e..c0c2508 100644
--- a/unit-test/test_EnumFactory.py
+++ b/unit-test/test_EnumFactory.py
@@ -13,7 +13,7 @@ class TestEnumFactory(unittest.TestCase):
def test(self):
enum1 = EnumFactory('Enum1', ('cst1', 'cst2'))
-
+
self.assertEqual(enum1.cst1, 0)
self.assertEqual(repr(enum1.cst1), 'cst1')
self.assertEqual(str(enum1.cst1), 'cst1')
@@ -21,12 +21,12 @@ def test(self):
self.assertEqual(repr(enum1.cst2), 'cst2')
self.assertEqual(str(enum1.cst2), 'cst2')
self.assertEqual(len(enum1), 2)
-
+
enum2 = ExplicitEnumFactory('Enum2', {'cst1':1, 'cst2':3})
-
+
self.assertEqual(enum2.cst1, 1)
self.assertEqual(enum2.cst2, 3)
-
+
self.assertTrue(enum2.cst2 in enum2)
####################################################################################################
@@ -34,9 +34,3 @@ def test(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/test_RawTextDocument.py b/unit-test/test_RawTextDocument.py
index 31de0d0..431ab1b 100644
--- a/unit-test/test_RawTextDocument.py
+++ b/unit-test/test_RawTextDocument.py
@@ -85,9 +85,3 @@ def test_empty(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/test_Slice.py b/unit-test/test_Slice.py
index 88d9410..12783a1 100644
--- a/unit-test/test_Slice.py
+++ b/unit-test/test_Slice.py
@@ -41,9 +41,3 @@ def test(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################
diff --git a/unit-test/test_SyntaxHighlighter.py b/unit-test/test_SyntaxHighlighter.py
index 6c4bf33..711f60a 100644
--- a/unit-test/test_SyntaxHighlighter.py
+++ b/unit-test/test_SyntaxHighlighter.py
@@ -23,7 +23,7 @@ def test(self):
text = f.read()
raw_text_document = RawTextDocument(text)
-
+
lexer = get_lexer_for_filename(test_file_path, stripnl=False)
highlighted_text = HighlightedText(raw_text_document, lexer)
@@ -35,9 +35,3 @@ def test(self):
if __name__ == '__main__':
unittest.main()
-
-####################################################################################################
-#
-# End
-#
-####################################################################################################