diff --git a/pyproject.toml b/pyproject.toml index 65ad501..9c9fbbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,5 +43,5 @@ line-length = 100 [tool.ruff.lint] select = [ "A", "B", "C90", "D", "E", "EM", "F", "I", "N", "PTH", "RUF", "W",] -ignore = ["D103", "D102", "D101", "D100", "D107", "D104", "D105", "EM101"] +ignore = ["D103", "D102", "D101", "D100", "D107", "D104", "D105", "EM101", "N802", "N815", "EM102"] exclude = ["windows/*", "tests/*"] diff --git a/src/pireal/core/relation.py b/src/pireal/core/relation.py index 2e7c4a9..0160bc0 100644 --- a/src/pireal/core/relation.py +++ b/src/pireal/core/relation.py @@ -20,18 +20,18 @@ # This module is based on Relational: -import re import itertools +import re -from pireal.utils import eval_expr -from pireal.core.rtypes import RType from pireal.core.ordered_set import OrderedSet +from pireal.core.rtypes import RType +from pireal.utils import eval_expr IS_VALID_FIELD_NAME = re.compile("^[_á-úa-zA-Z][_á-úa-zA-Z0-9]*$") class Error(Exception): - """Base exception""" + """Base exception.""" class FieldError(Error): @@ -43,7 +43,7 @@ def __init__(self, campo, msg=None): class InvalidFieldNameError(FieldError): - """Excepción lanzada cuando un nombre de campo no es válido""" + """Excepción lanzada cuando un nombre de campo no es válido.""" def __init__(self, campo, msg=None): super().__init__( @@ -59,7 +59,7 @@ def __init__(self, campo, msg=None): class FieldNotInHeaderError(FieldError): - """Excepción lanzada cuando un campo no existe en la relación""" + """Excepción lanzada cuando un campo no existe en la relación.""" def __init__(self, campo, relacion, msg=None): super().__init__( @@ -69,8 +69,11 @@ def __init__(self, campo, relacion, msg=None): class WrongSizeError(Error): - """Excepción lanzada cuando se trata de insertar un tamaño de - tuplas diferente a los que acepta la relación""" + """Wrong Size. + + Excepción lanzada cuando se trata de insertar un tamaño de + tuplas diferente a los que acepta la relación + """ def __init__(self, expected, got, msg=None): if msg is None: @@ -89,13 +92,13 @@ class UnionCompatibleError(Error): def union_compatible(operation): - """Decorador que comprueba que dos relaciones sean compatibles""" + """Decorador que comprueba que dos relaciones sean compatibles.""" def inner(self, *args, **kwargs): header_other = args[0].header if len(self._header) != len(header_other): raise UnionCompatibleError( - "Union not compatible for '{}'".format(operation.__name__) + f"Union not compatible for '{operation.__name__}'" ) return operation(self, *args, **kwargs) @@ -134,8 +137,7 @@ def update(self, row, column, new_value): self.content[row] = tuple(old) def append_row(self): - """Agrega una fila/tupla al final""" - + """Agrega una fila/tupla al final.""" nulls = [] for _ in range(self.degree()): nulls.append("null ({})".format(self._null_count)) @@ -143,28 +145,25 @@ def append_row(self): self.insert(tuple(nulls)) def cardinality(self): - """Devuelve la cantidad de filas de la relación""" - + """Devuelve la cantidad de filas de la relación.""" return len(self.content) def degree(self): - """Devuelve el grado de la relación""" - + """Devuelve el grado de la relación.""" return len(self._header) def project(self, *args): - """ - The project operator returns a new relation. + """Return a new relation. + Extract columns (attributes) resulting in a vertical subset of attributes of the relation """ - indexes = [] for arg in args: try: indexes.append(self._header.index(arg)) except ValueError as reason: - raise FieldNotInHeaderError(str(reason).split()[0], self.name) + raise FieldNotInHeaderError(str(reason).split()[0], self.name) from None # New fields header = [self._header[i] for i in indexes] # New relation @@ -177,11 +176,10 @@ def project(self, *args): return new_relation def select(self, expression): - """ - The select operator returns a new relation with the tuples that - satisfy an expression. - """ + """Return a new relation. + with the tuples thatsatisfy an expression. + """ new_relation = Relation() new_relation.header = self._header @@ -254,11 +252,11 @@ def fouter(self, other_relation): @union_compatible def union(self, other_relation): - """ - The union is defined as: R ∪ S. Returns the set of tuples in R, + """R U S. + + Returns the set of tuples in R, or S, or both. R and S must be compatible unions. """ - new_relation = Relation() new_relation.header = self._header content = self.content.union(other_relation.content) @@ -270,11 +268,11 @@ def union(self, other_relation): @union_compatible def difference(self, other_relation): - """ - The difference is defined as: R - S. It is the set of all tuples + """R - S. + + It is the set of all tuples in R, but not in S. R and S must be compatible unions """ - new_relation = Relation() new_relation.header = self._header content = self.content - other_relation.content @@ -284,11 +282,11 @@ def difference(self, other_relation): @union_compatible def intersect(self, other_relation): - """ - The intersection is defined as: R ∩ S. corresponds to the set of + """R ∩ S. + + Corresponds to the set of all tuples in R and S, R and S compatible unions. """ - new_relation = Relation() new_relation.header = self._header content = self.content.intersection(other_relation.content) @@ -299,14 +297,14 @@ def intersect(self, other_relation): return new_relation def product(self, other_relation): - """ - The cartesian product is defined as: R x S, its outline + """R x S. + + its outline corresponds to a combination of all tuples in R with each S tuples, and attributes corresponding to those of R followed by S. This method throws an exception when you are duplicate field names """ - for field in self._header: if field in other_relation.header: raise DuplicateFieldError(field) @@ -321,8 +319,6 @@ def product(self, other_relation): return new_relation def __str__(self): - """Magic method. Returns a representation of the relation""" - header = "" for field in self._header: header += "| " + field.center(10) + " " diff --git a/src/pireal/gui/__init__.py b/src/pireal/gui/__init__.py index f916ce5..d579114 100644 --- a/src/pireal/gui/__init__.py +++ b/src/pireal/gui/__init__.py @@ -17,10 +17,6 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -""" -Metadata -""" - __author__ = "Gabriel Acosta" __email__ = "acostadariogabriel at gmail" __source_code__ = "https://github.com/centaurialpha/pireal" diff --git a/src/pireal/gui/central_widget.py b/src/pireal/gui/central_widget.py index 2376a98..0d1694b 100644 --- a/src/pireal/gui/central_widget.py +++ b/src/pireal/gui/central_widget.py @@ -17,40 +17,32 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -import os import csv import logging -from pathlib import Path +import os from collections import defaultdict +from pathlib import Path -from PyQt6.QtWidgets import QWidget -from PyQt6.QtWidgets import QVBoxLayout -from PyQt6.QtWidgets import QStackedWidget -from PyQt6.QtWidgets import QFileDialog -from PyQt6.QtWidgets import QMessageBox -from PyQt6.QtGui import QShortcut - -from PyQt6.QtGui import QKeySequence -from PyQt6.QtCore import Qt -from PyQt6.QtCore import QSettings +from PyQt6.QtCore import QSettings, Qt +from PyQt6.QtGui import QKeySequence, QShortcut +from PyQt6.QtWidgets import ( + QFileDialog, + QMessageBox, + QStackedWidget, + QVBoxLayout, + QWidget, +) import pireal -from pireal.dirs import EXAMPLE_DB_FILENAME from pireal import settings -from pireal.core import ( - file_manager, - pfile, -) +from pireal import translations as tr +from pireal.core import file_manager, pfile +from pireal.dirs import DATA_SETTINGS, EXAMPLE_DB_FILENAME from pireal.gui import start_page from pireal.gui.database_container import DatabaseContainer -from pireal.gui.dialogs import ( - preferences, - new_relation_dialog, -) +from pireal.gui.dialogs import new_relation_dialog, preferences from pireal.gui.dialogs.new_database_dialog import DBInputDialog from pireal.gui.lateral_widget import RelationItemType -from pireal.dirs import DATA_SETTINGS -from pireal import translations as tr # Logger logger = logging.getLogger(__name__) @@ -117,8 +109,8 @@ def rdb_container_to_pdb_container(self): def create_database(self): """Show a wizard widget to create a new database, - only have one database open at time.""" - + only have one database open at time. + """ if self.created: return self.__say_about_one_db_container_at_time() db_container_filepath = DBInputDialog.ask_db_name(parent=self) @@ -248,11 +240,9 @@ def save_query_as(self): db_container.save_query_as() def __sanitize_data(self, data): - """ - Este método convierte el contenido de la base de datos a un + """Este método convierte el contenido de la base de datos a un diccionario para un mejor manejo despues """ - # FIXME: controlar cuando al final de la línea hay una coma data_dict = defaultdict(list) for line_count, line in enumerate(data.splitlines()): @@ -281,7 +271,6 @@ def __sanitize_data(self, data): def remove_last_widget(self): """Remove last widget from stacked""" - widget = self.stack.widget(self.stack.count() - 1) self.stack.removeWidget(widget) @@ -295,7 +284,6 @@ def close_query(self): def close_database(self) -> None: """Close the database and return to the main widget""" - pireal_instance = pireal.get_pireal_instance() db_container = pireal_instance.db_container if db_container is None: @@ -450,7 +438,6 @@ def _create_relation(relation, relation_name): def load_relation(self, filename=""): """Load Relation file""" - filenames = [] if not filename: if self._last_open_folder is None: @@ -478,24 +465,20 @@ def load_relation(self, filename=""): def add_start_page(self): """This function adds the Start Page to the stacked widget""" - sp = start_page.StartPage() self.add_widget(sp) def show_settings(self): """Show settings dialog on stacked""" - settings_dialog = preferences.SettingsDialog(self) settings_dialog.exec() def widget(self, index): """Returns the widget at the given index""" - return self.stack.widget(index) def add_widget(self, widget): """Appends and show the given widget to the Stacked""" - index = self.stack.addWidget(widget) self.stack.setCurrentIndex(index) diff --git a/src/pireal/gui/database_container.py b/src/pireal/gui/database_container.py index e1cd829..b7e08c4 100644 --- a/src/pireal/gui/database_container.py +++ b/src/pireal/gui/database_container.py @@ -20,28 +20,19 @@ import csv import logging -from PyQt6.QtWidgets import QSplitter -from PyQt6.QtWidgets import QFileDialog -from PyQt6.QtWidgets import QMessageBox - -from PyQt6.QtCore import Qt -from PyQt6.QtCore import QSettings +from PyQt6.QtCore import QSettings, Qt from PyQt6.QtCore import pyqtSlot as Slot +from PyQt6.QtWidgets import QFileDialog, QMessageBox, QSplitter -from pireal.gui.lateral_widget import LateralWidget -from pireal.gui.table_widget import TableWidget -from pireal.gui.model_view_delegate import create_view -from pireal.gui.lateral_widget import RelationItemType - -from pireal.gui.query_container.query_container import ( - QueryContainer, - QueryWidget, -) import pireal -from pireal.core import relation, pfile, file_manager -from pireal.dirs import DATA_SETTINGS -from pireal import translations as tr from pireal import settings +from pireal import translations as tr +from pireal.core import file_manager, pfile, relation +from pireal.dirs import DATA_SETTINGS +from pireal.gui.lateral_widget import LateralWidget, RelationItemType +from pireal.gui.model_view_delegate import create_view +from pireal.gui.query_container.query_container import QueryContainer, QueryWidget +from pireal.gui.table_widget import TableWidget logger = logging.getLogger(__name__) @@ -88,7 +79,6 @@ def _on_relation_clicked(self, index): def dbname(self): """Return display name""" - return self.pfile.display_name def is_new(self): @@ -120,7 +110,6 @@ def create_database(self, data): def create_table(self, relation_obj, relation_name, editable=True): """Se crea la vista, el model y el delegado para @relation_obj""" - return create_view(relation_obj, editable=editable) @Slot(bool) @@ -257,7 +246,6 @@ def showEvent(self, event): def save_sizes(self): """Save sizes of Splitters""" - qsettings = QSettings(str(DATA_SETTINGS), QSettings.Format.IniFormat) qsettings.setValue("vsplitter_sizes", self._vsplitter.saveState()) qsettings.setValue("hsplitter_sizes", self.saveState()) diff --git a/src/pireal/gui/dialogs/about_dialog.py b/src/pireal/gui/dialogs/about_dialog.py index 6c7c05e..2b16c61 100644 --- a/src/pireal/gui/dialogs/about_dialog.py +++ b/src/pireal/gui/dialogs/about_dialog.py @@ -19,17 +19,18 @@ from datetime import datetime +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QPixmap from PyQt6.QtWidgets import ( QDialog, - QVBoxLayout, - QLabel, QHBoxLayout, - QSpacerItem, - QSizePolicy, + QLabel, QPushButton, + QSizePolicy, + QSpacerItem, + QVBoxLayout, ) -from PyQt6.QtGui import QPixmap -from PyQt6.QtCore import Qt + from pireal import __version__, gui from pireal import translations as tr diff --git a/src/pireal/gui/dialogs/edit_relation_dialog.py b/src/pireal/gui/dialogs/edit_relation_dialog.py index 0602c02..d89301f 100644 --- a/src/pireal/gui/dialogs/edit_relation_dialog.py +++ b/src/pireal/gui/dialogs/edit_relation_dialog.py @@ -17,18 +17,18 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -from PyQt6.QtWidgets import QDialog -from PyQt6.QtWidgets import QVBoxLayout -from PyQt6.QtWidgets import QHBoxLayout -from PyQt6.QtWidgets import QPlainTextEdit -from PyQt6.QtWidgets import QLabel -from PyQt6.QtWidgets import QPushButton -from PyQt6.QtWidgets import QStyle -from PyQt6.QtWidgets import QMessageBox - -from PyQt6.QtGui import QFont - from PyQt6.QtCore import pyqtSignal +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import ( + QDialog, + QHBoxLayout, + QLabel, + QMessageBox, + QPlainTextEdit, + QPushButton, + QStyle, + QVBoxLayout, +) from pireal.gui.main_window import Pireal from pireal.settings import CONFIG diff --git a/src/pireal/gui/dialogs/new_database_dialog.py b/src/pireal/gui/dialogs/new_database_dialog.py index 7934ff9..00988a8 100644 --- a/src/pireal/gui/dialogs/new_database_dialog.py +++ b/src/pireal/gui/dialogs/new_database_dialog.py @@ -19,18 +19,19 @@ import os -from PyQt6.QtWidgets import QDialog -from PyQt6.QtWidgets import QDialogButtonBox -from PyQt6.QtWidgets import QFormLayout -from PyQt6.QtWidgets import QLineEdit -from PyQt6.QtWidgets import QStyle -from PyQt6.QtWidgets import QFileDialog - -from PyQt6.QtCore import pyqtSlot as Slot from PyQt6.QtCore import Qt +from PyQt6.QtCore import pyqtSlot as Slot +from PyQt6.QtWidgets import ( + QDialog, + QDialogButtonBox, + QFileDialog, + QFormLayout, + QLineEdit, + QStyle, +) -from pireal import translations as tr from pireal import dirs +from pireal import translations as tr PIREAL_DB_EXTENSION = ".pdb" diff --git a/src/pireal/gui/dialogs/new_relation_dialog.py b/src/pireal/gui/dialogs/new_relation_dialog.py index c3a14f8..3b8a910 100644 --- a/src/pireal/gui/dialogs/new_relation_dialog.py +++ b/src/pireal/gui/dialogs/new_relation_dialog.py @@ -17,25 +17,24 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . +from PyQt6.QtCore import Qt +from PyQt6.QtCore import pyqtSignal as Signal +from PyQt6.QtGui import QStandardItemModel from PyQt6.QtWidgets import ( - QMessageBox, QDialog, - QVBoxLayout, QHBoxLayout, QLineEdit, + QMessageBox, QPushButton, - QSpacerItem, QSizePolicy, + QSpacerItem, + QVBoxLayout, ) -from PyQt6.QtGui import QStandardItemModel -from PyQt6.QtCore import Qt -from PyQt6.QtCore import pyqtSignal as Signal import pireal -from pireal.core import relation -from pireal.gui.model_view_delegate import View, Header - from pireal import translations as tr +from pireal.core import relation +from pireal.gui.model_view_delegate import Header, View class NewRelationDialog(QDialog): @@ -91,7 +90,6 @@ def __init__(self, parent=None): def __add_tuple(self): """Agrega una tupla/fila al final de la tabla""" - model = self._view.model() model.insertRow(model.rowCount()) diff --git a/src/pireal/gui/dialogs/preferences.py b/src/pireal/gui/dialogs/preferences.py index 4a5405a..318af02 100644 --- a/src/pireal/gui/dialogs/preferences.py +++ b/src/pireal/gui/dialogs/preferences.py @@ -17,23 +17,23 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -from PyQt6.QtWidgets import QDialog -from PyQt6.QtWidgets import QVBoxLayout -from PyQt6.QtWidgets import QGridLayout -from PyQt6.QtWidgets import QLabel -from PyQt6.QtWidgets import QComboBox -from PyQt6.QtWidgets import QFontComboBox -from PyQt6.QtWidgets import QCheckBox -from PyQt6.QtWidgets import QGroupBox -from PyQt6.QtWidgets import QDialogButtonBox - -from PyQt6.QtGui import QFontDatabase - from PyQt6.QtCore import Qt +from PyQt6.QtGui import QFontDatabase +from PyQt6.QtWidgets import ( + QCheckBox, + QComboBox, + QDialog, + QDialogButtonBox, + QFontComboBox, + QGridLayout, + QGroupBox, + QLabel, + QVBoxLayout, +) import pireal -from pireal.settings import SETTINGS from pireal import translations as tr +from pireal.settings import SETTINGS class SettingsDialog(QDialog): diff --git a/src/pireal/gui/lateral_widget.py b/src/pireal/gui/lateral_widget.py index 8ea70ca..d58ba7a 100644 --- a/src/pireal/gui/lateral_widget.py +++ b/src/pireal/gui/lateral_widget.py @@ -20,23 +20,29 @@ import enum from collections import namedtuple -from PyQt6.QtWidgets import QListView -from PyQt6.QtWidgets import QVBoxLayout -from PyQt6.QtWidgets import QFrame -from PyQt6.QtWidgets import QLabel -from PyQt6.QtWidgets import QSplitter -from PyQt6.QtWidgets import QStyledItemDelegate -from PyQt6.QtWidgets import QStyleOptionViewItem -from PyQt6.QtWidgets import QStyle -from PyQt6.QtWidgets import QSizePolicy - -from PyQt6.QtCore import Qt, QRect, QModelIndex -from PyQt6.QtCore import QAbstractListModel -from PyQt6.QtCore import pyqtSignal as Signal +from PyQt6.QtCore import ( + QAbstractListModel, + QModelIndex, + QRect, + Qt, +) +from PyQt6.QtCore import ( + pyqtSignal as Signal, +) +from PyQt6.QtWidgets import ( + QFrame, + QLabel, + QListView, + QSizePolicy, + QSplitter, + QStyle, + QStyledItemDelegate, + QStyleOptionViewItem, + QVBoxLayout, +) from pireal import translations as tr - RelationItem = namedtuple("RelationItem", "name cardinality degree") @@ -176,8 +182,7 @@ def __init__(self, header_text="", parent=None): class LateralWidget(QSplitter): - """ - Widget que contiene la lista de relaciones y la lista de relaciones + """Widget que contiene la lista de relaciones y la lista de relaciones del resultado de consultas """ diff --git a/src/pireal/gui/main_window.py b/src/pireal/gui/main_window.py index 13f2d81..fa7049c 100644 --- a/src/pireal/gui/main_window.py +++ b/src/pireal/gui/main_window.py @@ -17,50 +17,49 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -""" Pireal Main Window """ +"""Pireal Main Window.""" import webbrowser -from PyQt6.QtWidgets import ( - QMainWindow, - QMessageBox, - QSystemTrayIcon, - QFrame, - QGridLayout, - QLabel, - QToolButton, - QHBoxLayout, - QApplication, -) -from PyQt6.QtGui import QIcon - from PyQt6.QtCore import ( QSettings, - QThread, Qt, + QThread, QTimer, - pyqtSlot as Slot, +) +from PyQt6.QtCore import ( pyqtSignal as Signal, ) +from PyQt6.QtCore import ( + pyqtSlot as Slot, +) +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import ( + QApplication, + QFrame, + QGridLayout, + QHBoxLayout, + QLabel, + QMainWindow, + QMessageBox, + QSystemTrayIcon, + QToolButton, +) import pireal -from pireal import keymap +from pireal import __version__, keymap +from pireal import translations as tr +from pireal.dirs import DATA_SETTINGS +from pireal.gui import menu_actions from pireal.gui.central_widget import CentralWidget from pireal.gui.database_container import DatabaseContainer +from pireal.gui.theme import apply_theme from pireal.gui.updater import Updater -from pireal.gui import ( - menu_actions, -) - from pireal.settings import SETTINGS -from pireal.gui.theme import apply_theme -from pireal import __version__ -from pireal.dirs import DATA_SETTINGS -from pireal import translations as tr class _StatusBar(QFrame): - """Status bar divide in three areas""" + """Status bar divide in three areas.""" playClicked = Signal() gearClicked = Signal() @@ -145,9 +144,7 @@ def update_line_and_col(self, line, col): class Pireal(QMainWindow): - - """ - Main Window class + """Main Window class. This class is responsible for installing all application services. """ @@ -247,10 +244,6 @@ def open_download_release(self): webbrowser.open_new("https://github.com/centaurialpha/pireal/releases/latest") def __load_menubar(self, menubar): - """ - This method installs the menubar and toolbar, menus and QAction's, - also connects to a slot each QAction. - """ menu_bar = self.menuBar() for menu in menu_actions.MENU: @@ -300,26 +293,22 @@ def change_title(self, title=""): self.setWindowTitle(_title) def about_qt(self): - """Show about qt dialog""" - + """Show about qt dialog.""" QMessageBox.aboutQt(self) def about_pireal(self): - """Show the bout Pireal dialog""" - + """Show the bout Pireal dialog.""" from pireal.gui.dialogs import about_dialog dialog = about_dialog.AboutDialog(self) dialog.exec() def report_issue(self): - """Open in the browser the page to create new issue""" - + """Open in the browser the page to create new issue.""" webbrowser.open("http://github.com/centaurialpha/pireal/issues/new") def show_hide_menubar(self): - """Change visibility of menu bar""" - + """Change visibility of menu bar.""" if self.menuBar().isVisible(): self.menuBar().hide() else: diff --git a/src/pireal/gui/menu_actions.py b/src/pireal/gui/menu_actions.py index 779dfca..ee3c9ec 100644 --- a/src/pireal/gui/menu_actions.py +++ b/src/pireal/gui/menu_actions.py @@ -17,8 +17,7 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -""" -This module contains an ordered dictionary, in turn this has a dictionary +"""This module contains an ordered dictionary, in turn this has a dictionary with the name and a list of items with properties as an icon , shortcut, slot for each item in a menu. diff --git a/src/pireal/gui/model_view_delegate.py b/src/pireal/gui/model_view_delegate.py index 12e2b3d..62e8a38 100644 --- a/src/pireal/gui/model_view_delegate.py +++ b/src/pireal/gui/model_view_delegate.py @@ -19,20 +19,18 @@ import logging -from PyQt6.QtWidgets import QTableView -from PyQt6.QtWidgets import QHeaderView -from PyQt6.QtWidgets import QAbstractItemView -from PyQt6.QtWidgets import QItemDelegate -from PyQt6.QtWidgets import QInputDialog - +from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt +from PyQt6.QtCore import pyqtSlot as Slot from PyQt6.QtGui import QColor +from PyQt6.QtWidgets import ( + QAbstractItemView, + QHeaderView, + QInputDialog, + QItemDelegate, + QTableView, +) -from PyQt6.QtCore import QAbstractTableModel -from PyQt6.QtCore import Qt -from PyQt6.QtCore import QModelIndex -from PyQt6.QtCore import pyqtSlot as Slot from pireal import translations as tr - from pireal.gui.theme import get_color logger = logging.getLogger("gui.model_view_delegate") @@ -126,7 +124,6 @@ def resizeEvent(self, event): def adjust_columns(self): """Resize all sections to content and user interactive""" - header = self.horizontalHeader() for column in range(header.count()): header.setSectionResizeMode(column, QHeaderView.ResizeMode.ResizeToContents) diff --git a/src/pireal/gui/query_container/editor.py b/src/pireal/gui/query_container/editor.py index 6dcadf5..de4bbd3 100644 --- a/src/pireal/gui/query_container/editor.py +++ b/src/pireal/gui/query_container/editor.py @@ -17,22 +17,21 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . +from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtGui import QColor, QFont, QTextCharFormat, QTextCursor, QTextDocument from PyQt6.QtWidgets import ( - QPlainTextEdit, - QTextEdit, QFrame, - QLabel, QHBoxLayout, + QLabel, + QPlainTextEdit, + QTextEdit, QToolButton, ) -from PyQt6.QtGui import QTextCharFormat, QTextCursor, QFont, QColor, QTextDocument -from PyQt6.QtCore import Qt, QTimer from pireal.gui.query_container import highlighter, sidebar from pireal.gui.theme import get_editor_color from pireal.settings import SETTINGS - BRACKETS = "()" OPOSITE_BRACKET = { "(": ")", @@ -206,7 +205,6 @@ def filename(self): :returns: filename of PFile """ - return self.pfile.filename @property @@ -228,7 +226,8 @@ def paintEvent(self, event): def word_under_cursor(self, cursor=None): """Returns QTextCursor that contains a word under passed cursor - or actual cursor""" + or actual cursor + """ if cursor is None: cursor = self.textCursor() start_pos = end_pos = cursor.position() @@ -312,7 +311,6 @@ def zoom_out(self): def show_run_cursor(self): """Highlight momentarily a piece of code. Tomado de Ninja-IDE""" - cursor = self.textCursor() if cursor.hasSelection(): # Get selection range @@ -342,7 +340,6 @@ def saved(self): def comment(self): """Comment one or more lines""" - tcursor = self.textCursor() block_start = self.document().findBlock(tcursor.selectionStart()) block_end = self.document().findBlock(tcursor.selectionEnd()).next() @@ -360,7 +357,6 @@ def comment(self): def uncomment(self): """Uncomment one or more lines""" - tcursor = self.textCursor() block_start = self.document().findBlock(tcursor.selectionStart()) block_end = self.document().findBlock(tcursor.selectionEnd()).next() diff --git a/src/pireal/gui/query_container/highlighter.py b/src/pireal/gui/query_container/highlighter.py index c45c729..9643299 100644 --- a/src/pireal/gui/query_container/highlighter.py +++ b/src/pireal/gui/query_container/highlighter.py @@ -17,14 +17,14 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . +from PyQt6.QtCore import QRegularExpression from PyQt6.QtGui import ( - QSyntaxHighlighter, - QTextCharFormat, + QColor, QFont, + QSyntaxHighlighter, QTextBlockUserData, - QColor, + QTextCharFormat, ) -from PyQt6.QtCore import QRegularExpression from pireal.gui.theme import get_editor_color @@ -105,7 +105,6 @@ def __init__(self, editor): def highlightBlock(self, text): """Reimplementation""" - block_data = TextBlockData() # Paren # index = self.paren.indexIn(text, 0) diff --git a/src/pireal/gui/query_container/query_container.py b/src/pireal/gui/query_container/query_container.py index dc80092..5e200cc 100644 --- a/src/pireal/gui/query_container/query_container.py +++ b/src/pireal/gui/query_container/query_container.py @@ -19,37 +19,34 @@ import re -from PyQt6.QtWidgets import QWidget -from PyQt6.QtWidgets import QVBoxLayout -from PyQt6.QtWidgets import QHBoxLayout -from PyQt6.QtWidgets import QSplitter -from PyQt6.QtWidgets import QStackedWidget -from PyQt6.QtWidgets import QDialog -from PyQt6.QtWidgets import QPushButton -from PyQt6.QtWidgets import QLineEdit - -from PyQt6.QtCore import Qt -from PyQt6.QtCore import QSettings - +from PyQt6.QtCore import QSettings, Qt from PyQt6.QtCore import pyqtSignal as Signal +from PyQt6.QtWidgets import ( + QDialog, + QHBoxLayout, + QLineEdit, + QPushButton, + QSplitter, + QStackedWidget, + QVBoxLayout, + QWidget, +) import pireal +from pireal import translations as tr +from pireal.dirs import DATA_SETTINGS +from pireal.gui.lateral_widget import RelationItemType + +# from pireal.gui.main_window import Pireal +from pireal.gui.query_container import editor, tab_widget from pireal.interpreter import parser from pireal.interpreter.exceptions import ( + ConsumeError, + DuplicateRelationNameError, InvalidSyntaxError, MissingQuoteError, - DuplicateRelationNameError, - ConsumeError, ) -# from pireal.gui.main_window import Pireal -from pireal.gui.query_container import editor -from pireal.gui.query_container import tab_widget -from pireal.gui.lateral_widget import RelationItemType - -from pireal import translations as tr -from pireal.dirs import DATA_SETTINGS - class QueryContainer(QWidget): saveEditor = Signal(object) @@ -86,14 +83,11 @@ def set_focus_editor_tab(self, index): def current_index(self): """This property holds the index position of the current tab page""" - return self._tabs.currentIndex() def tab_text(self, index): + """Returns the label text for the tab on the page at position index """ - Returns the label text for the tab on the page at position index - """ - return self._tabs.tabText(index) def __hide(self): @@ -149,7 +143,6 @@ def __on_save_editor(self, editor): def execute_queries(self, query=""): """This function executes queries""" - # If text is selected, then this text is the query, # otherwise the query is all text that has the editor editor_widget = self.currentWidget().get_editor() @@ -227,7 +220,6 @@ def _highlight_error_in_editor(self, line_error, col=-1): @staticmethod def parse_error(text): """Replaces quotes by tag""" - return re.sub(r"\'(.*?)\'", r"\1", text) def __add_table(self, rela, rname): @@ -338,7 +330,6 @@ def show_relation(self, item): def save_sizes(self): """Save sizes of Splitters""" - qsettings = QSettings(str(DATA_SETTINGS), QSettings.IniFormat) qsettings.setValue( "result_splitter_query_sizes", self.result_splitter.saveState() diff --git a/src/pireal/gui/query_container/sidebar.py b/src/pireal/gui/query_container/sidebar.py index 440c4c8..11ace82 100644 --- a/src/pireal/gui/query_container/sidebar.py +++ b/src/pireal/gui/query_container/sidebar.py @@ -17,22 +17,15 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -""" -Sidebar widget with line numbers +"""Sidebar widget with line numbers. based on: john.nachtimwald.com/2009/08/19/better-qplaintextedit-with-line-numbers/ """ +from PyQt6.QtCore import QSize, Qt +from PyQt6.QtGui import QColor, QFontMetrics, QPainter, QPen from PyQt6.QtWidgets import QFrame -from PyQt6.QtGui import ( - QFontMetrics, - QPainter, - QPen, - QColor, -) -from PyQt6.QtCore import Qt -from PyQt6.QtCore import QSize from pireal.gui.theme import get_editor_color @@ -79,7 +72,6 @@ def paintEvent(self, event): :param event: QEvent """ - painter = QPainter(self) painter.fillRect(event.rect(), self._background_color) width = self.width() - 8 diff --git a/src/pireal/gui/query_container/tab_widget.py b/src/pireal/gui/query_container/tab_widget.py index 12d31f3..ec54782 100644 --- a/src/pireal/gui/query_container/tab_widget.py +++ b/src/pireal/gui/query_container/tab_widget.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -from PyQt6.QtWidgets import QTabWidget, QMessageBox from PyQt6.QtCore import pyqtSignal as Signal +from PyQt6.QtWidgets import QMessageBox, QTabWidget class TabWidget(QTabWidget): diff --git a/src/pireal/gui/start_page.py b/src/pireal/gui/start_page.py index 0f7237e..12635ab 100644 --- a/src/pireal/gui/start_page.py +++ b/src/pireal/gui/start_page.py @@ -21,39 +21,35 @@ import os from datetime import datetime +from PyQt6.QtCore import ( + QAbstractListModel, + QModelIndex, + QRect, + QSettings, + Qt, + QTimer, +) +from PyQt6.QtCore import ( + pyqtSlot as Slot, +) +from PyQt6.QtGui import QPixmap from PyQt6.QtWidgets import ( - QWidget, - QLabel, QFrame, - QPushButton, - QVBoxLayout, QHBoxLayout, + QLabel, QListView, + QPushButton, QSizePolicy, + QStyle, QStyledItemDelegate, QStyleOptionViewItem, - QStyle, -) -from PyQt6.QtGui import ( - QPixmap, -) -from PyQt6.QtCore import ( - QAbstractListModel, - Qt, - QRect, - QModelIndex, - QSettings, - QTimer, - pyqtSlot as Slot, + QVBoxLayout, + QWidget, ) import pireal - -from pireal.dirs import ( - DATA_SETTINGS, - EXAMPLES_DIR, -) from pireal import translations as tr +from pireal.dirs import DATA_SETTINGS, EXAMPLES_DIR logger = logging.getLogger(__name__) @@ -80,7 +76,8 @@ def data(self, index, role=Qt.ItemDataRole.DisplayRole): class RecentDBDelegate(QStyledItemDelegate): """Custom delegate that show database name and database path - in same item""" + in same item + """ def paint(self, painter, option, index): opt = QStyleOptionViewItem(option) diff --git a/src/pireal/gui/table_widget.py b/src/pireal/gui/table_widget.py index ba25756..10bf1c0 100644 --- a/src/pireal/gui/table_widget.py +++ b/src/pireal/gui/table_widget.py @@ -17,16 +17,11 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -from PyQt6.QtWidgets import QSplitter -from PyQt6.QtWidgets import QTabWidget -from PyQt6.QtWidgets import QToolButton -from PyQt6.QtWidgets import QStackedWidget -from PyQt6.QtWidgets import QMenu - from PyQt6.QtCore import Qt -from pireal.gui.model_view_delegate import create_view +from PyQt6.QtWidgets import QMenu, QSplitter, QStackedWidget, QTabWidget, QToolButton from pireal import translations as tr +from pireal.gui.model_view_delegate import create_view class TableWidget(QSplitter): @@ -139,7 +134,6 @@ def add_relation(self, name, rela): def add_table(self, rela, name, table): """Add new table from New Relation Dialog""" - self.add_relation(name, rela) self.stacked.addWidget(table) @@ -174,7 +168,6 @@ def delete_tuple(self): def delete_column(self): """Elimina la/las columnas seleccionadas""" - current_view = self.current_table() if current_view is not None: model = current_view.model() @@ -193,5 +186,4 @@ def delete_column(self): def create_table(self, rela, editable=True): """Se crea la vista y el modelo""" - return create_view(rela, editable=editable) diff --git a/src/pireal/gui/theme.py b/src/pireal/gui/theme.py index 6fd174f..f3d66f9 100644 --- a/src/pireal/gui/theme.py +++ b/src/pireal/gui/theme.py @@ -1,5 +1,4 @@ -from PyQt6.QtGui import QColor -from PyQt6.QtGui import QPalette +from PyQt6.QtGui import QColor, QPalette from pireal.settings import SETTINGS diff --git a/src/pireal/gui/updater.py b/src/pireal/gui/updater.py index 031d895..5099d0d 100644 --- a/src/pireal/gui/updater.py +++ b/src/pireal/gui/updater.py @@ -17,12 +17,12 @@ # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . import logging - -from pkg_resources import parse_version -from urllib.request import urlopen from urllib.error import URLError +from urllib.request import urlopen -from PyQt6.QtCore import QObject, pyqtSignal as Signal +from pkg_resources import parse_version +from PyQt6.QtCore import QObject +from PyQt6.QtCore import pyqtSignal as Signal from pireal import __version__ diff --git a/src/pireal/interpreter/exceptions.py b/src/pireal/interpreter/exceptions.py index aa4b324..2b8c8f8 100644 --- a/src/pireal/interpreter/exceptions.py +++ b/src/pireal/interpreter/exceptions.py @@ -22,11 +22,11 @@ class InterpreterError(Exception): - """Excepción básica para errores generados por el intérprete""" + """Excepción básica para errores generados por el intérprete.""" class MissingQuoteError(InterpreterError): - """Excepción para comillas faltantes en strings""" + """Excepción para comillas faltantes en strings.""" def __init__(self, msg, lineno, col): InterpreterError.__init__(self, msg.format(lineno)) @@ -35,7 +35,7 @@ def __init__(self, msg, lineno, col): class InvalidSyntaxError(InterpreterError): - """Excepción para errores de sintáxis generados por el Lexer""" + """Excepción para errores de sintáxis generados por el Lexer.""" def __init__(self, lineno, col, char, msg="Invalid syntax on '{0}':'{1}'"): InterpreterError.__init__(self, msg.format(lineno, col)) @@ -45,8 +45,6 @@ def __init__(self, lineno, col, char, msg="Invalid syntax on '{0}':'{1}'"): class ConsumeError(InterpreterError): - """Excepción para errores generados por el Parser cuando no se espera - un determinado símbolo del lenguaje""" def __init__(self, expected, got, lineno, msg=None): if msg is None: @@ -61,8 +59,6 @@ def __init__(self, expected, got, lineno, msg=None): class DuplicateRelationNameError(InterpreterError): - """Excepción para errores generados por el Interpreter cuando se - usa un nombre que ya existe en el SCOPE""" def __init__(self, rname): super().__init__() diff --git a/src/pireal/interpreter/lexer.py b/src/pireal/interpreter/lexer.py index 992c5cb..5199fa2 100644 --- a/src/pireal/interpreter/lexer.py +++ b/src/pireal/interpreter/lexer.py @@ -20,21 +20,14 @@ # This module is responsible for organizing called "tokens" pieces, # each of these tokens has a meaning in language -from pireal.interpreter.tokens import ( - Token, - TokenTypes, - RESERVED_KEYWORDS, -) +from pireal.interpreter.exceptions import InvalidSyntaxError, MissingQuoteError from pireal.interpreter.scanner import Scanner -from pireal.interpreter.exceptions import MissingQuoteError, InvalidSyntaxError -from pireal.interpreter.utils import ( - is_date, - is_time, -) +from pireal.interpreter.tokens import RESERVED_KEYWORDS, Token, TokenTypes +from pireal.interpreter.utils import is_date, is_time class Lexer: - """This is the first stage of analysis. + """First stage of analysis. The Lexer serves to break up the source text into chuncks, "tokens". It calls the Scanner to get characters one at a time and organizes them @@ -68,8 +61,7 @@ def _skip_comment(self): self.sc.next() def get_identifier_or_keyword(self) -> Token: - """Handle identifiers and reserved keywords""" - + """Handle identifiers and reserved keywords.""" token = Token( type=TokenTypes.UNKNOWN, value=None, line=self.sc.lineno, col=self.sc.colno ) @@ -99,8 +91,7 @@ def get_identifier_or_keyword(self) -> Token: return token def get_number(self) -> Token: - """Returns a multidigit integer or float""" - + """Return a multidigit integer or float.""" token = Token( type=TokenTypes.UNKNOWN, value=None, line=self.sc.lineno, col=self.sc.colno ) @@ -166,7 +157,6 @@ def next_token(self) -> Token: This method is responsible for breaking a sentence apart into tokens. One token at a time """ - while self.sc.char is not None: # Recognize identifiers and keywords if self.sc.char.isalpha() or self.sc.char.startswith("_"): @@ -241,7 +231,9 @@ def next_token(self) -> Token: try: token_type = TokenTypes(self.sc.char) except ValueError: - raise InvalidSyntaxError(self.sc.lineno, self.sc.colno, self.sc.char) + raise InvalidSyntaxError( + self.sc.lineno, self.sc.colno, self.sc.char + ) from None else: token = Token( type=token_type, diff --git a/src/pireal/interpreter/parser.py b/src/pireal/interpreter/parser.py index fa29698..7f14070 100644 --- a/src/pireal/interpreter/parser.py +++ b/src/pireal/interpreter/parser.py @@ -16,7 +16,8 @@ # # You should have received a copy of the GNU General Public License # along with Pireal; If not, see . -""" +"""BNF. + : | : SEMI @@ -59,20 +60,17 @@ | TIME """ -from pireal.interpreter.tokens import ( - TokenTypes, - BINARY_OPERATORS, -) -from pireal.interpreter.exceptions import ConsumeError -from pireal.interpreter.exceptions import DuplicateRelationNameError - -from pireal.interpreter.scanner import Scanner -from pireal.interpreter.lexer import Lexer from pireal.interpreter import rast as ast +from pireal.interpreter.exceptions import ConsumeError, DuplicateRelationNameError +from pireal.interpreter.lexer import Lexer +from pireal.interpreter.scanner import Scanner +from pireal.interpreter.tokens import BINARY_OPERATORS, TokenTypes class Parser(object): - """The Parser is the part that really understands the syntax of + """The Parser. + + Is the part that really understands the syntax of the language. It calls the Lexer to get tokens and processes the tokens per the syntax of the language. """ @@ -83,10 +81,10 @@ def __init__(self, lexer): def consume(self, token_type): """Consume a token of a given type and get the next token. + If the current token is not of the expected type, then raise an error """ - if self.token.type == token_type: self.token = self.lexer.next_token() else: @@ -240,7 +238,7 @@ def operator(self): return node def attributes(self): - """Return a list of ast.Variable nodes""" + """Return a list of ast.Variable nodes.""" node = self.variable() attribute_list = [node] @@ -252,7 +250,9 @@ def attributes(self): class Interpreter(ast.NodeVisitor): - """Este objeto es el encargado de 'visitar' los nodos con el + """Visitor. + + Este objeto es el encargado de 'visitar' los nodos con el método Interpreter.to_python(), que convierte a un string que luego es evaluado como código Python diff --git a/src/pireal/interpreter/rast.py b/src/pireal/interpreter/rast.py index 70f449b..0d94a83 100644 --- a/src/pireal/interpreter/rast.py +++ b/src/pireal/interpreter/rast.py @@ -1,5 +1,5 @@ class AST(object): - """Base class for all nodes""" + """Base class for all nodes.""" def __eq__(self, other): attrs = [attr for attr in dir(self) if not attr.startswith("_")] @@ -86,7 +86,7 @@ def __init__(self): class NodeVisitor(object): - """Visitor pattern + """Visitor pattern. A node visitor base class that walks the abstract syntax tree and calls a visitor function for every node found. This function may return a value @@ -103,13 +103,11 @@ class NodeVisitor(object): """ def visit(self, node): - """Visit a node""" - + """Visit a node.""" method_name = "visit_" + node.__class__.__name__ visitor = getattr(self, method_name, self._generic_visit) return visitor(node) def _generic_visit(self, node): - """Called if not explicit visitor function exists for a node""" - - raise Exception("No visit_{} method".format(node.__class__.__name__)) + """Call if not explicit visitor function exists for a node.""" + raise Exception(f"No visit_{node.__class__.__name__} method") diff --git a/src/pireal/interpreter/scanner.py b/src/pireal/interpreter/scanner.py index 402c86b..9b69dcc 100644 --- a/src/pireal/interpreter/scanner.py +++ b/src/pireal/interpreter/scanner.py @@ -21,8 +21,9 @@ class Scanner: - """ - The Scanner is used to step through text character by character + """The Scanner. + + Is used to step through text character by character and keep track of the line and column number of each passed character """ @@ -36,8 +37,7 @@ def __init__(self, text: str): @property def char(self) -> str | None: - """Returns a character in the current index""" - + """Return a character in the current index.""" if self.index < len(self._text): return self._text[self.index] @@ -51,8 +51,7 @@ def peek(self) -> str | None: return self._text[peek_pos] def next(self) -> None: - """Move on to the next character in the scanned text""" - + """Move on to the next character in the scanned text.""" if self.char == "\n": # We are in a new line, therefore we increase the line # number and restart the column number @@ -63,8 +62,7 @@ def next(self) -> None: self.index += 1 def next_char(self) -> str | None: - """Returns the next character in the source text""" - + """Return the next character in the source text.""" self.next() return self.char diff --git a/src/pireal/interpreter/tokens.py b/src/pireal/interpreter/tokens.py index 6e3c4bb..8a6e5be 100644 --- a/src/pireal/interpreter/tokens.py +++ b/src/pireal/interpreter/tokens.py @@ -18,8 +18,8 @@ # along with Pireal; If not, see . from __future__ import annotations -import enum import datetime +import enum from dataclasses import dataclass