From 0ad2cc333757c5b6036f1e996f0f25e46f41bd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= Date: Wed, 5 Jun 2024 15:53:57 -0500 Subject: [PATCH 1/5] Add Python annotations to have type hints (partial) --- .pre-commit-config.yaml | 2 + modelbaker/dataobjects/fields.py | 10 +- modelbaker/dataobjects/form.py | 145 ++++++++++-------- modelbaker/dataobjects/layers.py | 84 +++++----- modelbaker/dataobjects/legend.py | 34 ++-- modelbaker/dataobjects/project.py | 41 ++--- modelbaker/dataobjects/relations.py | 14 +- .../db_factory/db_command_config_manager.py | 14 +- modelbaker/db_factory/db_factory.py | 16 +- modelbaker/db_factory/db_simple_factory.py | 7 +- .../db_factory/gpkg_command_config_manager.py | 12 +- modelbaker/db_factory/gpkg_factory.py | 21 ++- modelbaker/db_factory/gpkg_layer_uri.py | 4 +- modelbaker/db_factory/layer_uri.py | 4 +- .../mssql_command_config_manager.py | 12 +- modelbaker/db_factory/mssql_factory.py | 22 ++- modelbaker/db_factory/mssql_layer_uri.py | 6 +- .../db_factory/pg_command_config_manager.py | 14 +- modelbaker/db_factory/pg_factory.py | 20 ++- modelbaker/db_factory/pg_layer_uri.py | 4 +- modelbaker/generator/generator.py | 31 ++-- 21 files changed, 304 insertions(+), 213 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 429f540..8a3d203 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,3 +41,5 @@ repos: rev: v3.3.1 hooks: - id: pyupgrade + args: + - "--keep-runtime-typing" diff --git a/modelbaker/dataobjects/fields.py b/modelbaker/dataobjects/fields.py index b9f3f29..6c29f00 100644 --- a/modelbaker/dataobjects/fields.py +++ b/modelbaker/dataobjects/fields.py @@ -19,9 +19,11 @@ from qgis.core import QgsDefaultValue, QgsEditorWidgetSetup +from modelbaker.dataobjects.layers import Layer + class Field: - def __init__(self, name): + def __init__(self, name: str) -> None: self.name = name self.alias = None self.read_only = False @@ -31,18 +33,18 @@ def __init__(self, name): self.enum_domain = None self.oid_domain = None - def dump(self): + def dump(self) -> dict: definition = dict() if self.alias: definition["alias"] = self.alias return definition - def load(self, definition): + def load(self, definition: dict) -> None: if "alias" in definition: self.alias = definition["alias"] - def create(self, layer): + def create(self, layer: Layer) -> None: field_idx = layer.layer.fields().indexOf(self.name) if self.alias: diff --git a/modelbaker/dataobjects/form.py b/modelbaker/dataobjects/form.py index 358a0cb..76b6c8c 100644 --- a/modelbaker/dataobjects/form.py +++ b/modelbaker/dataobjects/form.py @@ -16,24 +16,98 @@ * * ***************************************************************************/ """ +from typing import Optional, Union from qgis.core import ( Qgis, QgsAttributeEditorContainer, + QgsAttributeEditorElement, QgsAttributeEditorField, QgsAttributeEditorRelation, QgsEditFormConfig, + QgsVectorLayer, ) +from modelbaker.dataobjects.layers import Layer +from modelbaker.dataobjects.project import Project +from modelbaker.dataobjects.relations import Relation + + +class FormFieldWidget: + def __init__(self, name: str, field_name: str) -> None: + self.name = name if name else field_name + self.field_name = field_name + + def create( + self, parent: QgsAttributeEditorElement, layer: QgsVectorLayer + ) -> QgsAttributeEditorField: + index = layer.fields().indexOf(self.field_name) + widget = QgsAttributeEditorField(self.field_name, index, parent) + return widget + + +class FormRelationWidget: + def __init__( + self, relation: Relation, nm_relation: Optional[Relation] = None + ) -> None: + self.relation = relation + self.nm_relation = nm_relation + + def create( + self, parent: QgsAttributeEditorElement, _layer + ) -> QgsAttributeEditorRelation: + try: + widget = QgsAttributeEditorRelation(self.relation.id, parent) + except TypeError: + # Feed deprecated API for 3.0.0 and 3.0.1 + widget = QgsAttributeEditorRelation( + self.relation.id, self.relation.id, parent + ) + if self.nm_relation: + widget.setNmRelationId(self.nm_relation.id) + + if Qgis.QGIS_VERSION_INT >= 31800: + widget.setRelationWidgetTypeId("linking_relation_editor") + + if not self.nm_relation and self.relation.cardinality_max == "1": + configuration = widget.relationEditorConfiguration() + configuration["one_to_one"] = True + widget.setRelationEditorConfiguration(configuration) + + return widget + + +class FormTab: + def __init__(self, name: str, columns: int = 1) -> None: + self.name = name + self.children = list() + self.columns = columns + + def addChild(self, child: Union[FormFieldWidget, FormRelationWidget]) -> None: + self.children.append(child) + + def create( + self, parent: QgsAttributeEditorElement, layer: QgsVectorLayer + ) -> QgsAttributeEditorContainer: + container = QgsAttributeEditorContainer(self.name, parent) + container.setIsGroupBox(False) + container.setColumnCount(self.columns) + + for child in self.children: + container.addChildElement(child.create(container, layer)) + return container + class Form: - def __init__(self): + def __init__(self) -> None: self.__elements = list() - def elements(self): + def elements(self) -> list[Union[FormFieldWidget, FormRelationWidget]]: return self.__elements - def create(self, layer, qgis_layer, project): + def create( + self, layer: Layer, qgis_layer: QgsVectorLayer, project: Project + ) -> QgsEditFormConfig: edit_form_config = qgis_layer.editFormConfig() root_container = edit_form_config.invisibleRootContainer() root_container.clear() @@ -58,39 +132,20 @@ def create(self, layer, qgis_layer, project): break return edit_form_config - def add_element(self, element): + def add_element(self, element: Union[FormTab, FormFieldWidget]) -> None: self.__elements.append(element) -class FormTab: - def __init__(self, name, columns=1): - self.name = name - self.children = list() - self.columns = columns - - def addChild(self, child): - self.children.append(child) - - def create(self, parent, layer): - container = QgsAttributeEditorContainer(self.name, parent) - container.setIsGroupBox(False) - container.setColumnCount(self.columns) - - for child in self.children: - container.addChildElement(child.create(container, layer)) - return container - - class FormGroupBox: - def __init__(self, name, columns=1): + def __init__(self, name: str, columns: int = 1) -> None: self.name = name self.children = list() self.columns = columns - def addChild(self, child): + def addChild(self, child) -> None: self.children.append(child) - def create(self, parent, layer): + def create(self, _parent, layer) -> QgsAttributeEditorContainer: container = QgsAttributeEditorContainer(self.name) container.setIsGroupBox(True) container.setColumnCount(self.columns) @@ -98,41 +153,3 @@ def create(self, parent, layer): for child in self.children: container.addChildElement(child, layer) return container - - -class FormFieldWidget: - def __init__(self, name, field_name): - self.name = name if name else field_name - self.field_name = field_name - - def create(self, parent, layer): - index = layer.fields().indexOf(self.field_name) - widget = QgsAttributeEditorField(self.field_name, index, parent) - return widget - - -class FormRelationWidget: - def __init__(self, relation, nm_relation=None): - self.relation = relation - self.nm_relation = nm_relation - - def create(self, parent, layer): - try: - widget = QgsAttributeEditorRelation(self.relation.id, parent) - except TypeError: - # Feed deprecated API for 3.0.0 and 3.0.1 - widget = QgsAttributeEditorRelation( - self.relation.id, self.relation.id, parent - ) - if self.nm_relation: - widget.setNmRelationId(self.nm_relation.id) - - if Qgis.QGIS_VERSION_INT >= 31800: - widget.setRelationWidgetTypeId("linking_relation_editor") - - if not self.nm_relation and self.relation.cardinality_max == "1": - configuration = widget.relationEditorConfiguration() - configuration["one_to_one"] = True - widget.setRelationEditorConfiguration(configuration) - - return widget diff --git a/modelbaker/dataobjects/layers.py b/modelbaker/dataobjects/layers.py index e2f0bf8..edcc484 100644 --- a/modelbaker/dataobjects/layers.py +++ b/modelbaker/dataobjects/layers.py @@ -17,6 +17,7 @@ ***************************************************************************/ """ import logging +from typing import Optional, Union from qgis.core import ( Qgis, @@ -31,6 +32,9 @@ ) from qgis.PyQt.QtCore import QCoreApplication, QSettings +from modelbaker.dataobjects.fields import Field +from modelbaker.dataobjects.project import Project + from ..generator.config import BASKET_FIELDNAMES, IGNORED_FIELDNAMES from ..utils.globals import OptimizeStrategy from .form import Form, FormFieldWidget, FormRelationWidget, FormTab @@ -39,29 +43,33 @@ class Layer: def __init__( self, - provider=None, - uri=None, - name=None, - srid=None, - extent=None, - geometry_column=None, - wkb_type=QgsWkbTypes.Unknown, - alias=None, - is_domain=False, - is_structure=False, - is_nmrel=False, - display_expression=None, - coordinate_precision=None, - is_basket_table=False, - is_dataset_table=False, - ili_name=None, - is_relevant=True, - all_topics=[], # all the topics this class (or an instance of it) are located - relevant_topics=[], # the topics of the most extended instance of it only - definitionfile=None, - qmlstylefile=None, - styles={}, - ): + provider: str = None, + uri: str = None, + name: str = None, + srid: Optional[int] = None, + extent: Optional[str] = None, + geometry_column: str = None, + wkb_type: QgsWkbTypes = QgsWkbTypes.Unknown, + alias: Optional[str] = None, + is_domain: bool = False, + is_structure: bool = False, + is_nmrel: bool = False, + display_expression: str = None, + coordinate_precision: Optional[float] = None, + is_basket_table: bool = False, + is_dataset_table: bool = False, + ili_name: Optional[str] = None, + is_relevant: bool = True, + all_topics: list[ + str + ] = [], # all the topics this class (or an instance of it) are located + relevant_topics: list[ + str + ] = [], # the topics of the most extended instance of it only + definitionfile: Optional[str] = None, + qmlstylefile: Optional[str] = None, + styles: dict[str, dict[str, str]] = {}, + ) -> None: self.provider = provider self.uri = uri self.name = name @@ -111,7 +119,7 @@ def __init__( self.checked = True self.featurecount = False - def dump(self): + def dump(self) -> dict: definition = dict() definition["provider"] = self.provider definition["uri"] = self.uri @@ -132,7 +140,7 @@ def dump(self): definition["form"] = self.__form.dump() return definition - def load(self, definition): + def load(self, definition: dict) -> None: self.provider = definition["provider"] self.uri = definition["uri"] self.is_domain = definition["isdomain"] @@ -151,7 +159,7 @@ def load(self, definition): self.styles = definition["styles"] self.__form.load(definition["form"]) - def create(self): + def create(self) -> Union[QgsRasterLayer, QgsVectorLayer]: if self.definitionfile: if self.__layer is None: layers = QgsLayerDefinition.loadLayerDefinitionLayers( @@ -196,11 +204,11 @@ def create(self): return self.__layer - def create_form(self, project): + def create_form(self, project: Project) -> None: edit_form = self.__form.create(self, self.__layer, project) self.__layer.setEditFormConfig(edit_form) - def load_styles(self): + def load_styles(self) -> None: if self.qmlstylefile: self.__layer.loadNamedStyle(self.qmlstylefile) if self.styles: @@ -215,7 +223,7 @@ def load_styles(self): # set the default style self.__layer.styleManager().setCurrentStyle("default") - def store_variables(self, project): + def store_variables(self, project: Project) -> None: """ Set the layer variables according to the strategy """ @@ -232,13 +240,15 @@ def store_variables(self, project): self.__layer, "oid_domain", self.oid_domain ) - def _create_layer(self, uri, layer_name, provider): + def _create_layer( + self, uri: str, layer_name: str, provider: str + ) -> Union[QgsRasterLayer, QgsVectorLayer]: if provider and provider.lower() == "wms": return QgsRasterLayer(uri, layer_name, provider) # return QgsVectorLayer even when it's an invalid layer with no provider return QgsVectorLayer(uri, layer_name, provider) - def post_generate(self, project): + def post_generate(self, project: Project) -> None: """ Will be called when the whole project has been generated and therefore all relations are available and the form @@ -328,15 +338,15 @@ def post_generate(self, project): widget = FormFieldWidget(field.alias, field.name) self.__form.add_element(widget) - def source(self): + def source(self) -> QgsDataSourceUri: return QgsDataSourceUri(self.uri) @property - def layer(self): + def layer(self) -> Union[QgsRasterLayer, QgsVectorLayer]: return self.__layer @property - def real_id(self): + def real_id(self) -> Optional[str]: """ The layer id. Only valid after creating the layer. """ @@ -346,20 +356,20 @@ def real_id(self): return None @property - def oid_domain(self): + def oid_domain(self) -> Optional[str]: t_ili_tid_field = self.t_ili_tid_field if t_ili_tid_field: return t_ili_tid_field.oid_domain return None @property - def t_ili_tid_field(self): + def t_ili_tid_field(self) -> Optional[Field]: for field in self.fields: if field.name.lower() == "t_ili_tid": return field return None - def isPureLinkTable(self, project): + def isPureLinkTable(self, project: Project) -> bool: """ Returns True if the layer is a pure link table in a n:m relation. With "pure" it is meant the layer has no more fields than foreign keys and its id. diff --git a/modelbaker/dataobjects/legend.py b/modelbaker/dataobjects/legend.py index 04905c9..33b3f74 100644 --- a/modelbaker/dataobjects/legend.py +++ b/modelbaker/dataobjects/legend.py @@ -16,16 +16,30 @@ * * ***************************************************************************/ """ +from __future__ import annotations -from qgis.core import QgsLayerDefinition, QgsLayerTreeLayer, QgsProject +from typing import Any, Optional, Union + +from qgis.core import ( + QgsLayerDefinition, + QgsLayerTreeGroup, + QgsLayerTreeLayer, + QgsProject, +) + +from modelbaker.dataobjects.layers import Layer from ..utils.qgis_utils import get_group_non_recursive, get_suggested_index_for_layer class LegendGroup: def __init__( - self, name=None, expanded=True, ignore_node_names=None, static_sorting=False - ): + self, + name: str = None, + expanded: bool = True, + ignore_node_names: bool = None, + static_sorting: bool = False, + ) -> None: self.name = name self.items = list() self.expanded = expanded @@ -40,16 +54,16 @@ def __init__( # groups that should be always on top) self.ignore_node_names = ignore_node_names - def dump(self): + def dump(self) -> list[dict[str, Any]]: definition = list() for item in self.items: definition.append(item.dump()) return definition - def append(self, item): + def append(self, item: Union[LegendGroup, Layer]) -> None: self.items.append(item) - def __getitem__(self, item): + def __getitem__(self, item: str) -> Union[LegendGroup, Layer]: for i in self.items: try: if i.name == item: @@ -60,10 +74,12 @@ def __getitem__(self, item): raise KeyError(item) - def load(self, definition): + def load(self, definition: list[Union[LegendGroup, Layer]]) -> None: self.items = definition - def create(self, qgis_project: QgsProject, group=None): + def create( + self, qgis_project: QgsProject, group: Optional[QgsLayerTreeGroup] = None + ) -> None: if not group: group = qgis_project.layerTreeRoot() @@ -112,5 +128,5 @@ def create(self, qgis_project: QgsProject, group=None): group.insertChildNode(index, layernode) static_index += 1 - def is_empty(self): + def is_empty(self) -> bool: return not bool(self.items) diff --git a/modelbaker/dataobjects/project.py b/modelbaker/dataobjects/project.py index 3980f87..1a7b960 100644 --- a/modelbaker/dataobjects/project.py +++ b/modelbaker/dataobjects/project.py @@ -16,7 +16,7 @@ * * ***************************************************************************/ """ -from typing import List +from typing import Any, Optional from qgis.core import ( Qgis, @@ -46,20 +46,20 @@ class Project(QObject): def __init__( self, - auto_transaction=True, - evaluate_default_values=True, - context={}, - optimize_strategy=OptimizeStrategy.NONE, - ): + auto_transaction: bool = True, + evaluate_default_values: bool = True, + context: dict[str, str] = {}, + optimize_strategy: OptimizeStrategy = OptimizeStrategy.NONE, + ) -> None: QObject.__init__(self) self.crs = None self.name = "Not set" - self.layers = List[Layer] + self.layers = list[Layer] self.legend = LegendGroup() self.custom_layer_order_structure = list() self.auto_transaction = auto_transaction self.evaluate_default_values = evaluate_default_values - self.relations = List[Relation] + self.relations = list[Relation] self.custom_variables = {} self.layouts = {} self.mapthemes = {} @@ -69,10 +69,10 @@ def __init__( # {Layer_class_name: {dbattribute: {Layer_class, cardinality, Layer_domain, key_field, value_field]} self.bags_of_enum = dict() - def add_layer(self, layer): + def add_layer(self, layer: Layer) -> None: self.layers.append(layer) - def dump(self): + def dump(self) -> dict[str, Any]: definition = dict() definition["crs"] = self.crs.toWkt() definition["auto_transaction"] = self.auto_transaction @@ -96,7 +96,7 @@ def dump(self): return definition - def load(self, definition): + def load(self, definition: dict[str, Any]) -> None: self.crs = definition["crs"] self.auto_transaction = definition["auto_transaction"] self.evaluate_default_values = definition["evaluate_default_values"] @@ -113,8 +113,11 @@ def load(self, definition): self.mapthemes = definition["mapthemes"] def create( - self, path: str, qgis_project: QgsProject, group: QgsLayerTreeGroup = None - ): + self, + path: str, + qgis_project: QgsProject, + group: Optional[QgsLayerTreeGroup] = None, + ) -> None: if Qgis.QGIS_VERSION_INT < 32600: # set auto_transaction as boolean qgis_project.setAutoTransaction(self.auto_transaction) @@ -288,7 +291,7 @@ def create( if path: qgis_project.write(path) - def load_custom_layer_order(self, qgis_project): + def load_custom_layer_order(self, qgis_project: QgsProject) -> None: custom_layer_order = list() for custom_layer_name in self.custom_layer_order_structure: custom_layer = qgis_project.mapLayersByName(custom_layer_name) @@ -301,13 +304,13 @@ def load_custom_layer_order(self, qgis_project): root.setCustomLayerOrder(custom_layer_order) root.setHasCustomLayerOrder(True) - def load_custom_variables(self, qgis_project): + def load_custom_variables(self, qgis_project: QgsProject) -> None: for key in self.custom_variables.keys(): QgsExpressionContextUtils.setProjectVariable( qgis_project, key, self.custom_variables[key] ) - def load_layouts(self, qgis_project): + def load_layouts(self, qgis_project: QgsProject) -> None: for layout_name in self.layouts.keys(): # create the layout layout = QgsPrintLayout(qgis_project) @@ -324,7 +327,7 @@ def load_layouts(self, qgis_project): layout.setName(layout_name) qgis_project.layoutManager().addLayout(layout) - def load_mapthemes(self, qgis_project): + def load_mapthemes(self, qgis_project: QgsProject) -> None: if self.mapthemes: for name in self.mapthemes.keys(): map_theme_record = QgsMapThemeCollection.MapThemeRecord() @@ -387,11 +390,11 @@ def load_mapthemes(self, qgis_project): qgis_project.mapThemeCollection().insert(name, map_theme_record) - def store_project_variables(self, qgis_project): + def store_project_variables(self, qgis_project: QgsProject) -> None: QgsExpressionContextUtils.setProjectVariable( qgis_project, "optimize_strategy", self.optimize_strategy.name ) - def post_generate(self): + def post_generate(self) -> None: for layer in self.layers: layer.post_generate(self) diff --git a/modelbaker/dataobjects/relations.py b/modelbaker/dataobjects/relations.py index 6077dd0..96cae5a 100644 --- a/modelbaker/dataobjects/relations.py +++ b/modelbaker/dataobjects/relations.py @@ -1,8 +1,8 @@ -from qgis.core import QgsRelation +from qgis.core import QgsProject, QgsRelation class Relation: - def __init__(self): + def __init__(self) -> None: self.referencing_layer = None self.referenced_layer = None self.referencing_field = None @@ -14,7 +14,7 @@ def __init__(self): self.qgis_relation = None self._id = None - def dump(self): + def dump(self) -> dict: definition = dict() definition["referencingLayer"] = self.referencing_layer definition["referencingField"] = self.referencing_field @@ -26,7 +26,7 @@ def dump(self): return definition - def load(self, definition): + def load(self, definition: str) -> None: self.referencing_layer = definition["referencingLayer"] self.referencing_field = definition["referencingField"] self.referenced_layer = definition["referencedLayer"] @@ -35,7 +35,9 @@ def load(self, definition): self.cardinality_max = definition["cardinality_max"] self.child_domain_name = definition["child_domain_name"] - def create(self, qgis_project, relations): + def create( + self, qgis_project: QgsProject, relations: list[QgsRelation] + ) -> QgsRelation: relation = QgsRelation() project_ids = qgis_project.relationManager().relations().keys() base_id = self.name @@ -61,5 +63,5 @@ def create(self, qgis_project, relations): return relation @property - def id(self): + def id(self) -> str: return self._id diff --git a/modelbaker/db_factory/db_command_config_manager.py b/modelbaker/db_factory/db_command_config_manager.py index 422833e..3a341f2 100644 --- a/modelbaker/db_factory/db_command_config_manager.py +++ b/modelbaker/db_factory/db_command_config_manager.py @@ -32,7 +32,7 @@ class DbCommandConfigManager(ABC): :ivar configuration object that will be managed """ - def __init__(self, configuration: Ili2DbCommandConfiguration): + def __init__(self, configuration: Ili2DbCommandConfiguration) -> None: """ :param configuration: Configuration object that will be managed. :type configuration: :class:`Ili2DbCommandConfiguration` @@ -40,7 +40,7 @@ def __init__(self, configuration: Ili2DbCommandConfiguration): self.configuration = configuration @abstractmethod - def get_uri(self, su: bool = False, qgis: bool = False): + def get_uri(self, su: bool = False, qgis: bool = False) -> str: """Gets database uri (connection string) for db connectors (:class:`DBConnector`). :param bool su: *True* to use super user credentials, *False* otherwise. @@ -50,7 +50,7 @@ def get_uri(self, su: bool = False, qgis: bool = False): """ @abstractmethod - def get_db_args(self, hide_password=False, su=False): + def get_db_args(self, hide_password: bool = False, su: bool = False) -> list[str]: """Gets a list of ili2db arguments related to database. :param bool hide_password: *True* to mask the password, *False* otherwise. @@ -59,7 +59,7 @@ def get_db_args(self, hide_password=False, su=False): :rtype: list """ - def get_schema_import_args(self): + def get_schema_import_args(self) -> list[str]: """Gets a list of ili2db arguments to use in operation schema import. :return: ili2db arguments list. @@ -67,7 +67,7 @@ def get_schema_import_args(self): """ return list() - def get_ili2db_args(self, hide_password=False): + def get_ili2db_args(self, hide_password: bool = False) -> list[str]: """Gets a complete list of ili2db arguments in order to execute the app. :param bool hide_password: *True* to mask the password, *False* otherwise. @@ -84,14 +84,14 @@ def get_ili2db_args(self, hide_password=False): return ili2dbargs @abstractmethod - def save_config_in_qsettings(self): + def save_config_in_qsettings(self) -> None: """Saves configuration values related to database in QSettings. :return: None """ @abstractmethod - def load_config_from_qsettings(self): + def load_config_from_qsettings(self) -> None: """Loads configuration values related to database from Qsettings. :return: None diff --git a/modelbaker/db_factory/db_factory.py b/modelbaker/db_factory/db_factory.py index b4c0d97..9dca768 100644 --- a/modelbaker/db_factory/db_factory.py +++ b/modelbaker/db_factory/db_factory.py @@ -16,7 +16,7 @@ ***************************************************************************/ """ from abc import ABC, abstractmethod -from typing import Tuple +from typing import Optional from ..dataobjects.fields import Field from ..dbconnector.db_connector import DBConnector @@ -26,10 +26,10 @@ class DbFactory(ABC): - """Creates an entire set of objects so that modelbaker supports some database. This is a abstract class.""" + """Creates an entire set of objects so that modelbaker supports some database. This is an abstract class.""" @abstractmethod - def get_db_connector(self, uri: str, schema: str) -> DBConnector: + def get_db_connector(self, uri: str, schema: Optional[str]) -> DBConnector: """Returns an instance of connector to database (:class:`DBConnector`). :param str uri: Database connection string. @@ -63,7 +63,7 @@ def get_layer_uri(self, uri: str) -> LayerUri: @abstractmethod def pre_generate_project( self, configuration: Ili2DbCommandConfiguration - ) -> Tuple[bool, str]: + ) -> tuple[bool, str]: """This method will be called before an operation of generate project is executed. :param configuration: Configuration parameters with which will be executed the operation of generate project. @@ -74,7 +74,7 @@ def pre_generate_project( @abstractmethod def post_generate_project_validations( self, configuration: Ili2DbCommandConfiguration - ) -> Tuple[bool, str]: + ) -> tuple[bool, str]: """This method will be called after an operation of generate project is executed. :param configuration: Configuration parameters with which were executed the operation of generate project. @@ -82,8 +82,8 @@ def post_generate_project_validations( :return: *True* and an empty message if the called method was succeeded, *False* and a warning message otherwise. """ - def get_specific_messages(self): - """Returns specific words that will be use in warning and error messages. + def get_specific_messages(self) -> dict[str, str]: + """Returns specific words that will be used in warning and error messages. :rtype dict """ @@ -91,7 +91,7 @@ def get_specific_messages(self): return messages - def customize_widget_editor(self, field: Field, data_type: str): + def customize_widget_editor(self, field: Field, data_type: str) -> None: """Allows customizing the way a field is shown in the widget editor. For instance, a boolean field can be shown as a checkbox. diff --git a/modelbaker/db_factory/db_simple_factory.py b/modelbaker/db_factory/db_simple_factory.py index c3de42c..eb186ce 100644 --- a/modelbaker/db_factory/db_simple_factory.py +++ b/modelbaker/db_factory/db_simple_factory.py @@ -16,6 +16,7 @@ ***************************************************************************/ """ import logging +from typing import Optional from ..iliwrapper.globals import DbIliMode from .db_factory import DbFactory @@ -44,7 +45,7 @@ class DbSimpleFactory: """Provides a single point (simple factory) to create a database factory (:class:`DbFactory`).""" - def create_factory(self, ili_mode: DbIliMode) -> DbFactory: + def create_factory(self, ili_mode: DbIliMode) -> Optional[DbFactory]: """Creates an instance of :class:`DbFactory` based on ili_mode parameter. :param ili_mode: Value specifying which factory will be instantiated. @@ -67,7 +68,7 @@ def create_factory(self, ili_mode: DbIliMode) -> DbFactory: return result - def get_db_list(self, is_schema_import=False): + def get_db_list(self, is_schema_import: bool = False) -> list[DbIliMode]: """Gets a list containing the databases available in modelbaker. This list can be used to show the available databases in GUI, for example, **QComboBox source** @@ -89,7 +90,7 @@ def get_db_list(self, is_schema_import=False): return result @property - def default_database(self): + def default_database(self) -> DbIliMode: """Gets a default database for modelbaker. :return: Default database for modelbaker. diff --git a/modelbaker/db_factory/gpkg_command_config_manager.py b/modelbaker/db_factory/gpkg_command_config_manager.py index 9533238..ba6ba2c 100644 --- a/modelbaker/db_factory/gpkg_command_config_manager.py +++ b/modelbaker/db_factory/gpkg_command_config_manager.py @@ -17,6 +17,8 @@ """ from qgis.PyQt.QtCore import QSettings +from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration + from .db_command_config_manager import DbCommandConfigManager @@ -31,21 +33,21 @@ class GpkgCommandConfigManager(DbCommandConfigManager): _settings_base_path = "ili2gpkg/" - def __init__(self, configuration): + def __init__(self, configuration: Ili2DbCommandConfiguration) -> None: DbCommandConfigManager.__init__(self, configuration) - def get_uri(self, su=False, qgis=False): + def get_uri(self, su: bool = False, qgis: bool = False) -> str: return self.configuration.dbfile - def get_db_args(self, hide_password=False, su=False): + def get_db_args(self, hide_password: bool = False, su: bool = False) -> list[str]: return ["--dbfile", self.configuration.dbfile] - def save_config_in_qsettings(self): + def save_config_in_qsettings(self) -> None: settings = QSettings() settings.setValue( self._settings_base_path + "dbfile", self.configuration.dbfile ) - def load_config_from_qsettings(self): + def load_config_from_qsettings(self) -> None: settings = QSettings() self.configuration.dbfile = settings.value(self._settings_base_path + "dbfile") diff --git a/modelbaker/db_factory/gpkg_factory.py b/modelbaker/db_factory/gpkg_factory.py index d4fafe8..1a758df 100644 --- a/modelbaker/db_factory/gpkg_factory.py +++ b/modelbaker/db_factory/gpkg_factory.py @@ -15,6 +15,9 @@ * * ***************************************************************************/ """ +from typing import Optional + +from modelbaker.db_factory.db_command_config_manager import Ili2DbCommandConfiguration from ..dbconnector.gpkg_connector import GPKGConnector from .db_factory import DbFactory @@ -25,22 +28,28 @@ class GpkgFactory(DbFactory): """Creates an entire set of objects so that QgisMmodelbakerodelBaker supports Geopackage database.""" - def get_db_connector(self, uri, schema): + def get_db_connector(self, uri: str, schema: Optional[str]) -> GPKGConnector: return GPKGConnector(uri, None) - def get_db_command_config_manager(self, configuration): + def get_db_command_config_manager( + self, configuration: Ili2DbCommandConfiguration + ) -> GpkgCommandConfigManager: return GpkgCommandConfigManager(configuration) - def get_layer_uri(self, uri): + def get_layer_uri(self, uri: str) -> GpkgLayerUri: return GpkgLayerUri(uri) - def pre_generate_project(self, configuration): + def pre_generate_project( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: return True, "" - def post_generate_project_validations(self, configuration): + def post_generate_project_validations( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: return True, "" - def get_specific_messages(self): + def get_specific_messages(self) -> dict[str, str]: messages = {"db_or_schema": "database", "layers_source": "GeoPackage"} return messages diff --git a/modelbaker/db_factory/gpkg_layer_uri.py b/modelbaker/db_factory/gpkg_layer_uri.py index 20cb936..3077236 100644 --- a/modelbaker/db_factory/gpkg_layer_uri.py +++ b/modelbaker/db_factory/gpkg_layer_uri.py @@ -26,11 +26,11 @@ class GpkgLayerUri(LayerUri): :ivar str uri: Database uri. """ - def __init__(self, uri): + def __init__(self, uri: str) -> None: LayerUri.__init__(self, uri) self.provider = "ogr" - def get_data_source_uri(self, record): + def get_data_source_uri(self, record: dict) -> str: data_source_uri = "{uri}|layername={table}".format( uri=self.uri, table=record["tablename"] ) diff --git a/modelbaker/db_factory/layer_uri.py b/modelbaker/db_factory/layer_uri.py index 6333927..3626aa4 100644 --- a/modelbaker/db_factory/layer_uri.py +++ b/modelbaker/db_factory/layer_uri.py @@ -27,7 +27,7 @@ class LayerUri(ABC): :ivar str provider: Database provider. """ - def __init__(self, uri): + def __init__(self, uri: str) -> None: """ :param str uri: Database uri. This is the same database uri of the db connectors. """ @@ -35,7 +35,7 @@ def __init__(self, uri): self.provider = None @abstractmethod - def get_data_source_uri(self, record: dict): + def get_data_source_uri(self, record: dict) -> str: """Provides layer uri based on database uri and specific information of the data source. :param str record: Dictionary containing specific information of the data source. diff --git a/modelbaker/db_factory/mssql_command_config_manager.py b/modelbaker/db_factory/mssql_command_config_manager.py index 6bbc5bb..2e9ecd1 100644 --- a/modelbaker/db_factory/mssql_command_config_manager.py +++ b/modelbaker/db_factory/mssql_command_config_manager.py @@ -17,6 +17,8 @@ """ from qgis.PyQt.QtCore import QSettings +from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration + from .db_command_config_manager import DbCommandConfigManager @@ -24,10 +26,10 @@ class MssqlCommandConfigManager(DbCommandConfigManager): _settings_base_path = "ili2mssql/" - def __init__(self, configuration): + def __init__(self, configuration: Ili2DbCommandConfiguration) -> None: DbCommandConfigManager.__init__(self, configuration) - def get_uri(self, su=False, qgis=False): + def get_uri(self, su: bool = False, qgis: bool = False) -> str: separator = ";" uri = [] uri += ["DRIVER={{{}}}".format(self.configuration.db_odbc_driver)] @@ -44,7 +46,7 @@ def get_uri(self, su=False, qgis=False): return separator.join(uri) - def get_db_args(self, hide_password=False, su=False): + def get_db_args(self, hide_password: bool = False, su: bool = False) -> list[str]: db_args = list() db_args += ["--dbhost", self.configuration.dbhost] if self.configuration.dbport: @@ -65,7 +67,7 @@ def get_db_args(self, hide_password=False, su=False): return db_args - def save_config_in_qsettings(self): + def save_config_in_qsettings(self) -> None: settings = QSettings() settings.setValue(self._settings_base_path + "host", self.configuration.dbhost) @@ -87,7 +89,7 @@ def save_config_in_qsettings(self): self._settings_base_path + "odbc_driver", self.configuration.db_odbc_driver ) - def load_config_from_qsettings(self): + def load_config_from_qsettings(self) -> None: settings = QSettings() self.configuration.dbhost = settings.value( diff --git a/modelbaker/db_factory/mssql_factory.py b/modelbaker/db_factory/mssql_factory.py index 0965e5b..92bad1b 100644 --- a/modelbaker/db_factory/mssql_factory.py +++ b/modelbaker/db_factory/mssql_factory.py @@ -15,6 +15,10 @@ * * ***************************************************************************/ """ +from typing import Optional + +from modelbaker.db_factory.db_command_config_manager import Ili2DbCommandConfiguration + from ..dataobjects.fields import Field from ..dbconnector.mssql_connector import MssqlConnector from .db_factory import DbFactory @@ -23,22 +27,28 @@ class MssqlFactory(DbFactory): - def get_db_connector(self, uri, schema): + def get_db_connector(self, uri: str, schema: Optional[str]) -> MssqlConnector: return MssqlConnector(uri, schema) - def get_db_command_config_manager(self, configuration): + def get_db_command_config_manager( + self, configuration: Ili2DbCommandConfiguration + ) -> MssqlCommandConfigManager: return MssqlCommandConfigManager(configuration) - def get_layer_uri(self, uri): + def get_layer_uri(self, uri: str) -> MssqlLayerUri: return MssqlLayerUri(uri) - def pre_generate_project(self, configuration): + def pre_generate_project( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: return True, "" - def post_generate_project_validations(self, configuration): + def post_generate_project_validations( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: return True, "" - def customize_widget_editor(self, field: Field, data_type: str): + def customize_widget_editor(self, field: Field, data_type: str) -> None: if "bit" in data_type: field.widget = "CheckBox" field.widget_config["CheckedState"] = "1" diff --git a/modelbaker/db_factory/mssql_layer_uri.py b/modelbaker/db_factory/mssql_layer_uri.py index 38d8831..61d4a55 100644 --- a/modelbaker/db_factory/mssql_layer_uri.py +++ b/modelbaker/db_factory/mssql_layer_uri.py @@ -19,11 +19,11 @@ class MssqlLayerUri(LayerUri): - def __init__(self, uri): + def __init__(self, uri: str) -> None: LayerUri.__init__(self, uri) self.provider = "mssql" - def get_data_source_uri(self, record): + def get_data_source_uri(self, record: dict) -> str: if record["geometry_column"]: data_source_uri = '{uri} key={primary_key} estimatedmetadata=true srid={srid} type={type} table="{schema}"."{table}" ({geometry_column}) sql='.format( uri=self._get_layer_uri_common(), @@ -44,7 +44,7 @@ def get_data_source_uri(self, record): return data_source_uri - def _get_layer_uri_common(self): + def _get_layer_uri_common(self) -> str: param_db = dict() lst_item = self.uri.split(";") for item in lst_item: diff --git a/modelbaker/db_factory/pg_command_config_manager.py b/modelbaker/db_factory/pg_command_config_manager.py index 5221135..2a77ac5 100644 --- a/modelbaker/db_factory/pg_command_config_manager.py +++ b/modelbaker/db_factory/pg_command_config_manager.py @@ -17,6 +17,8 @@ """ from qgis.PyQt.QtCore import QSettings +from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration + from ..libs import pgserviceparser from .db_command_config_manager import DbCommandConfigManager @@ -32,10 +34,10 @@ class PgCommandConfigManager(DbCommandConfigManager): _settings_base_path = "ili2pg/" - def __init__(self, configuration): + def __init__(self, configuration: Ili2DbCommandConfiguration) -> None: DbCommandConfigManager.__init__(self, configuration) - def get_uri(self, su=False, qgis=False): + def get_uri(self, su: bool = False, qgis: bool = False) -> str: uri = [] if su: @@ -109,7 +111,7 @@ def get_uri(self, su=False, qgis=False): return " ".join(uri) - def get_db_args(self, hide_password=False, su=False): + def get_db_args(self, hide_password: bool = False, su: bool = False) -> list[str]: db_args = list() db_args += ["--dbhost", self.configuration.dbhost] if self.configuration.dbport: @@ -141,12 +143,12 @@ def get_db_args(self, hide_password=False, su=False): ] return db_args - def get_schema_import_args(self): + def get_schema_import_args(self) -> list[str]: args = list() args += ["--setupPgExt"] return args - def save_config_in_qsettings(self): + def save_config_in_qsettings(self) -> None: settings = QSettings() # PostgreSQL specific options settings.setValue(self._settings_base_path + "host", self.configuration.dbhost) @@ -172,7 +174,7 @@ def save_config_in_qsettings(self): self._settings_base_path + "service", self.configuration.dbservice ) - def load_config_from_qsettings(self): + def load_config_from_qsettings(self) -> None: settings = QSettings() self.configuration.dbhost = settings.value( diff --git a/modelbaker/db_factory/pg_factory.py b/modelbaker/db_factory/pg_factory.py index 8f955ef..97a63af 100644 --- a/modelbaker/db_factory/pg_factory.py +++ b/modelbaker/db_factory/pg_factory.py @@ -15,8 +15,12 @@ * * ***************************************************************************/ """ +from typing import Optional + from qgis.PyQt.QtCore import QCoreApplication +from modelbaker.db_factory.db_command_config_manager import Ili2DbCommandConfiguration + from ..dbconnector.db_connector import DBConnectorError from ..dbconnector.pg_connector import PGConnector from .db_factory import DbFactory @@ -27,16 +31,20 @@ class PgFactory(DbFactory): """Creates an entire set of objects so that modelbaker supports Postgres/Postgis database.""" - def get_db_connector(self, uri, schema): + def get_db_connector(self, uri: str, schema: Optional[str]) -> PGConnector: return PGConnector(uri, schema) - def get_db_command_config_manager(self, configuration): + def get_db_command_config_manager( + self, configuration: Ili2DbCommandConfiguration + ) -> PgCommandConfigManager: return PgCommandConfigManager(configuration) - def get_layer_uri(self, uri): + def get_layer_uri(self, uri: str) -> PgLayerUri: return PgLayerUri(uri) - def pre_generate_project(self, configuration): + def pre_generate_project( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: result = not configuration.db_use_super_login message = "" @@ -61,7 +69,9 @@ def pre_generate_project(self, configuration): return result, message - def post_generate_project_validations(self, configuration): + def post_generate_project_validations( + self, configuration: Ili2DbCommandConfiguration + ) -> tuple[bool, str]: result = False message = "" diff --git a/modelbaker/db_factory/pg_layer_uri.py b/modelbaker/db_factory/pg_layer_uri.py index c6ab999..def9798 100644 --- a/modelbaker/db_factory/pg_layer_uri.py +++ b/modelbaker/db_factory/pg_layer_uri.py @@ -26,12 +26,12 @@ class PgLayerUri(LayerUri): :ivar str uri: Database uri. """ - def __init__(self, uri): + def __init__(self, uri: str) -> None: LayerUri.__init__(self, uri) self.pg_estimated_metadata = False self.provider = "postgres" - def get_data_source_uri(self, record): + def get_data_source_uri(self, record: dict) -> str: if record["geometry_column"]: str_pg_estimated_metadata = ( "true" if self.pg_estimated_metadata else "false" diff --git a/modelbaker/generator/generator.py b/modelbaker/generator/generator.py index 5493d88..e166a87 100644 --- a/modelbaker/generator/generator.py +++ b/modelbaker/generator/generator.py @@ -17,10 +17,13 @@ ***************************************************************************/ """ import re +from typing import Optional from qgis.core import QgsApplication, QgsRelation, QgsWkbTypes from qgis.PyQt.QtCore import QCoreApplication, QLocale, QObject, pyqtSignal +from modelbaker.iliwrapper.globals import DbIliMode + from ..dataobjects.fields import Field from ..dataobjects.layers import Layer from ..dataobjects.legend import LegendGroup @@ -40,16 +43,16 @@ class Generator(QObject): def __init__( self, - tool, - uri, - inheritance, - schema=None, - pg_estimated_metadata=False, - parent=None, - mgmt_uri=None, - consider_basket_handling=False, - optimize_strategy=OptimizeStrategy.NONE, - ): + tool: DbIliMode, + uri: str, + inheritance: str, + schema: Optional[str] = None, + pg_estimated_metadata: bool = False, + parent: QObject = None, + mgmt_uri: Optional[str] = None, + consider_basket_handling: bool = False, + optimize_strategy: OptimizeStrategy = OptimizeStrategy.NONE, + ) -> None: """ Creates a new Generator objects. :param uri: The uri that should be used in the resulting project. If authcfg is used, make sure the mgmt_uri is set as well. @@ -78,21 +81,21 @@ def __init__( self.collected_print_messages = [] - def print_info(self, text): + def print_info(self, text: str) -> None: self.stdout.emit(text) - def print_messages(self): + def print_messages(self) -> None: for message in self.collected_print_messages: self.new_message.emit(message["level"], message["text"]) self.collected_print_messages.clear() - def append_print_message(self, level, text): + def append_print_message(self, level, text) -> None: message = {"level": level, "text": text} if message not in self.collected_print_messages: self.collected_print_messages.append(message) - def layers(self, filter_layer_list=[]): + def layers(self, filter_layer_list: list = []) -> list[Layer]: ignore_basket_tables = not self.basket_handling tables_info = self.get_tables_info_without_ignored_tables(ignore_basket_tables) layers = list() From c738c8d551bce32583382a0457c3be1313f48bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= Date: Wed, 5 Jun 2024 16:28:32 -0500 Subject: [PATCH 2/5] Ensure modern typing hints compatibility with Python 3.7+ (otherwise, this syntax requires Python 3.9+) --- modelbaker/dataobjects/form.py | 2 ++ modelbaker/dataobjects/layers.py | 2 ++ modelbaker/dataobjects/project.py | 2 ++ modelbaker/dataobjects/relations.py | 2 ++ modelbaker/db_factory/db_command_config_manager.py | 2 ++ modelbaker/db_factory/db_factory.py | 2 ++ modelbaker/db_factory/db_simple_factory.py | 2 ++ modelbaker/db_factory/gpkg_command_config_manager.py | 2 ++ modelbaker/db_factory/gpkg_factory.py | 2 ++ modelbaker/db_factory/mssql_command_config_manager.py | 2 ++ modelbaker/db_factory/mssql_factory.py | 2 ++ modelbaker/db_factory/pg_command_config_manager.py | 2 ++ modelbaker/db_factory/pg_factory.py | 2 ++ modelbaker/generator/generator.py | 2 ++ 14 files changed, 28 insertions(+) diff --git a/modelbaker/dataobjects/form.py b/modelbaker/dataobjects/form.py index 76b6c8c..3bc0431 100644 --- a/modelbaker/dataobjects/form.py +++ b/modelbaker/dataobjects/form.py @@ -16,6 +16,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import Optional, Union from qgis.core import ( diff --git a/modelbaker/dataobjects/layers.py b/modelbaker/dataobjects/layers.py index edcc484..11ad99d 100644 --- a/modelbaker/dataobjects/layers.py +++ b/modelbaker/dataobjects/layers.py @@ -16,6 +16,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + import logging from typing import Optional, Union diff --git a/modelbaker/dataobjects/project.py b/modelbaker/dataobjects/project.py index 1a7b960..93a8dec 100644 --- a/modelbaker/dataobjects/project.py +++ b/modelbaker/dataobjects/project.py @@ -16,6 +16,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import Any, Optional from qgis.core import ( diff --git a/modelbaker/dataobjects/relations.py b/modelbaker/dataobjects/relations.py index 96cae5a..91bbaf2 100644 --- a/modelbaker/dataobjects/relations.py +++ b/modelbaker/dataobjects/relations.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from qgis.core import QgsProject, QgsRelation diff --git a/modelbaker/db_factory/db_command_config_manager.py b/modelbaker/db_factory/db_command_config_manager.py index 3a341f2..a0d8ebb 100644 --- a/modelbaker/db_factory/db_command_config_manager.py +++ b/modelbaker/db_factory/db_command_config_manager.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from abc import ABC, abstractmethod from ..iliwrapper.ili2dbconfig import ( diff --git a/modelbaker/db_factory/db_factory.py b/modelbaker/db_factory/db_factory.py index 9dca768..a49df1e 100644 --- a/modelbaker/db_factory/db_factory.py +++ b/modelbaker/db_factory/db_factory.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from abc import ABC, abstractmethod from typing import Optional diff --git a/modelbaker/db_factory/db_simple_factory.py b/modelbaker/db_factory/db_simple_factory.py index eb186ce..82b046c 100644 --- a/modelbaker/db_factory/db_simple_factory.py +++ b/modelbaker/db_factory/db_simple_factory.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + import logging from typing import Optional diff --git a/modelbaker/db_factory/gpkg_command_config_manager.py b/modelbaker/db_factory/gpkg_command_config_manager.py index ba6ba2c..8ce8792 100644 --- a/modelbaker/db_factory/gpkg_command_config_manager.py +++ b/modelbaker/db_factory/gpkg_command_config_manager.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from qgis.PyQt.QtCore import QSettings from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration diff --git a/modelbaker/db_factory/gpkg_factory.py b/modelbaker/db_factory/gpkg_factory.py index 1a758df..137fa71 100644 --- a/modelbaker/db_factory/gpkg_factory.py +++ b/modelbaker/db_factory/gpkg_factory.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import Optional from modelbaker.db_factory.db_command_config_manager import Ili2DbCommandConfiguration diff --git a/modelbaker/db_factory/mssql_command_config_manager.py b/modelbaker/db_factory/mssql_command_config_manager.py index 2e9ecd1..e312ace 100644 --- a/modelbaker/db_factory/mssql_command_config_manager.py +++ b/modelbaker/db_factory/mssql_command_config_manager.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from qgis.PyQt.QtCore import QSettings from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration diff --git a/modelbaker/db_factory/mssql_factory.py b/modelbaker/db_factory/mssql_factory.py index 92bad1b..7ddb78d 100644 --- a/modelbaker/db_factory/mssql_factory.py +++ b/modelbaker/db_factory/mssql_factory.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import Optional from modelbaker.db_factory.db_command_config_manager import Ili2DbCommandConfiguration diff --git a/modelbaker/db_factory/pg_command_config_manager.py b/modelbaker/db_factory/pg_command_config_manager.py index 2a77ac5..cbcbd78 100644 --- a/modelbaker/db_factory/pg_command_config_manager.py +++ b/modelbaker/db_factory/pg_command_config_manager.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from qgis.PyQt.QtCore import QSettings from modelbaker.iliwrapper.ili2dbconfig import Ili2DbCommandConfiguration diff --git a/modelbaker/db_factory/pg_factory.py b/modelbaker/db_factory/pg_factory.py index 97a63af..82c5c9d 100644 --- a/modelbaker/db_factory/pg_factory.py +++ b/modelbaker/db_factory/pg_factory.py @@ -15,6 +15,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import Optional from qgis.PyQt.QtCore import QCoreApplication diff --git a/modelbaker/generator/generator.py b/modelbaker/generator/generator.py index e166a87..4e8be37 100644 --- a/modelbaker/generator/generator.py +++ b/modelbaker/generator/generator.py @@ -16,6 +16,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + import re from typing import Optional From 477086725c4c1e87c6059afc3c7f18bb19ce650d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= Date: Wed, 5 Jun 2024 17:13:31 -0500 Subject: [PATCH 3/5] Use TYPE_CHECKING (PEP 563) to avoid circular imports that are only due to type hints --- modelbaker/dataobjects/fields.py | 4 +++- modelbaker/dataobjects/form.py | 9 +++++---- modelbaker/dataobjects/layers.py | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/modelbaker/dataobjects/fields.py b/modelbaker/dataobjects/fields.py index 6c29f00..30aebd2 100644 --- a/modelbaker/dataobjects/fields.py +++ b/modelbaker/dataobjects/fields.py @@ -16,10 +16,12 @@ * * ***************************************************************************/ """ +from typing import TYPE_CHECKING from qgis.core import QgsDefaultValue, QgsEditorWidgetSetup -from modelbaker.dataobjects.layers import Layer +if TYPE_CHECKING: + from modelbaker.dataobjects.layers import Layer class Field: diff --git a/modelbaker/dataobjects/form.py b/modelbaker/dataobjects/form.py index 3bc0431..55e80ab 100644 --- a/modelbaker/dataobjects/form.py +++ b/modelbaker/dataobjects/form.py @@ -18,7 +18,7 @@ """ from __future__ import annotations -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union from qgis.core import ( Qgis, @@ -30,9 +30,10 @@ QgsVectorLayer, ) -from modelbaker.dataobjects.layers import Layer -from modelbaker.dataobjects.project import Project -from modelbaker.dataobjects.relations import Relation +if TYPE_CHECKING: + from modelbaker.dataobjects.layers import Layer + from modelbaker.dataobjects.project import Project + from modelbaker.dataobjects.relations import Relation class FormFieldWidget: diff --git a/modelbaker/dataobjects/layers.py b/modelbaker/dataobjects/layers.py index 11ad99d..38f73a2 100644 --- a/modelbaker/dataobjects/layers.py +++ b/modelbaker/dataobjects/layers.py @@ -19,7 +19,7 @@ from __future__ import annotations import logging -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union from qgis.core import ( Qgis, @@ -34,8 +34,9 @@ ) from qgis.PyQt.QtCore import QCoreApplication, QSettings -from modelbaker.dataobjects.fields import Field -from modelbaker.dataobjects.project import Project +if TYPE_CHECKING: + from modelbaker.dataobjects.fields import Field + from modelbaker.dataobjects.project import Project from ..generator.config import BASKET_FIELDNAMES, IGNORED_FIELDNAMES from ..utils.globals import OptimizeStrategy From 50704810e0ad234e78a5247755400695ab8306eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= Date: Wed, 5 Jun 2024 17:33:33 -0500 Subject: [PATCH 4/5] Use 'from __future__ import annotations' to avoid 'Layer is not defined issues' --- modelbaker/dataobjects/fields.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modelbaker/dataobjects/fields.py b/modelbaker/dataobjects/fields.py index 30aebd2..548304d 100644 --- a/modelbaker/dataobjects/fields.py +++ b/modelbaker/dataobjects/fields.py @@ -16,6 +16,8 @@ * * ***************************************************************************/ """ +from __future__ import annotations + from typing import TYPE_CHECKING from qgis.core import QgsDefaultValue, QgsEditorWidgetSetup From 8bc7bd9b8c736e2fabe0346c41ef63658e8710a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= Date: Fri, 7 Jun 2024 19:42:36 -0500 Subject: [PATCH 5/5] Bump Python to 3.7 to be able to use annotation from __future__ imports --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 715f0ba..9caf6f3 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,6 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">=3.6", + python_requires=">=3.7", packages=setuptools.find_packages(exclude=["tests"]), )