diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml
index 638fbfa4..13e49f90 100644
--- a/.github/workflows/packages.yml
+++ b/.github/workflows/packages.yml
@@ -148,7 +148,7 @@ jobs:
pip3 wheel -r mergin_client.egg-info/requires.txt -w mergin/deps
# special care for pygeodiff
unzip mergin/deps/pygeodiff-*.whl -d mergin/deps
- # remove unncesessary files
+ # remove unnecessary files
rm -rf mergin/deps/*.dist-info
rm -rf mergin/deps/*.data
rm -rf mergin/deps/pygeodiff.libs
diff --git a/Mergin/clone_project_dialog.py b/Mergin/clone_project_dialog.py
index 2b2420ce..e022a71f 100644
--- a/Mergin/clone_project_dialog.py
+++ b/Mergin/clone_project_dialog.py
@@ -6,10 +6,6 @@
from qgis.PyQt.QtWidgets import (
QDialog,
QDialogButtonBox,
- QFileDialog,
- QApplication,
- QMessageBox,
- QComboBox,
)
from qgis.PyQt.QtCore import Qt
from qgis.PyQt import uic
diff --git a/Mergin/collapsible_message_box.py b/Mergin/collapsible_message_box.py
index 960cf056..92f4e511 100644
--- a/Mergin/collapsible_message_box.py
+++ b/Mergin/collapsible_message_box.py
@@ -2,10 +2,10 @@
# Copyright Lutra Consulting Limited
from qgis.PyQt.QtCore import Qt
-from qgis.PyQt.QtWidgets import QMessageBox
+from qgis.PyQt.QtWidgets import QMessageBox, QWidget
-class CollapsibleBox(QtWidgets.QWidget):
+class CollapsibleBox(QWidget):
def __init__(self, text, details, title="Mergin Maps error", parent=None):
msg = QMessageBox()
msg.setWindowTitle(title)
diff --git a/Mergin/configuration_dialog.py b/Mergin/configuration_dialog.py
index 6b8dd23e..1d91985d 100644
--- a/Mergin/configuration_dialog.py
+++ b/Mergin/configuration_dialog.py
@@ -172,7 +172,7 @@ def test_connection(self):
self.ui.test_status.setText(" OK ")
return True
- self.ui.test_status.setText(f"Follow the instructions in the browser...")
+ self.ui.test_status.setText("Follow the instructions in the browser...")
ok, msg = test_server_connection(self.server_url(), use_sso=True, sso_email=self.get_sso_email())
if url_reachable(self.server_url()):
diff --git a/Mergin/create_project_wizard.py b/Mergin/create_project_wizard.py
index 874c9acd..27f22981 100644
--- a/Mergin/create_project_wizard.py
+++ b/Mergin/create_project_wizard.py
@@ -5,10 +5,9 @@
import shutil
from pathlib import Path
from qgis.PyQt import uic
-from qgis.PyQt.QtCore import QSettings, Qt, QVariant, QSortFilterProxyModel
+from qgis.PyQt.QtCore import QSettings, Qt, QSortFilterProxyModel
from qgis.PyQt.QtWidgets import (
QAbstractItemView,
- QApplication,
QComboBox,
QFileDialog,
QHeaderView,
diff --git a/Mergin/data_item.py b/Mergin/data_item.py
index 1c043177..21a06c26 100644
--- a/Mergin/data_item.py
+++ b/Mergin/data_item.py
@@ -47,7 +47,6 @@
get_local_mergin_projects_info,
)
from .utils_auth import AuthTokenExpiredError
-from .mergin.merginproject import MerginProject
class MerginRemoteProjectItem(QgsDataItem):
@@ -204,7 +203,7 @@ def remove_local_project(self):
cur_proj_path = cur_proj.absolutePath()
msg = (
"Your local changes will be lost. Make sure your project is synchronised with server. \n\n"
- "Do you want to proceed?".format(self.project_name)
+ "Do you want to proceed?"
)
btn_reply = QMessageBox.question(
None,
@@ -219,10 +218,7 @@ def remove_local_project(self):
if os.path.exists(self.path):
try:
if same_dir(cur_proj_path, self.path):
- msg = (
- "The project is currently open. It will get cleared if you proceed.\n\n"
- "Proceed anyway?".format(self.project_name)
- )
+ msg = "The project is currently open. It will get cleared if you proceed.\n\n" "Proceed anyway?"
btn_reply = QMessageBox.question(
None,
"Remove local project",
@@ -508,7 +504,7 @@ def fetch_more(self):
QMessageBox.information(None, "Fetch Mergin Maps Projects", "All projects already listed.")
return
page_to_get = floor(len(self.projects) / PROJS_PER_PAGE) + 1
- dummy = self.fetch_projects(page=page_to_get)
+ self.fetch_projects(page=page_to_get)
self.refresh()
def reload(self):
diff --git a/Mergin/diff.py b/Mergin/diff.py
index 00bb8637..82f6d538 100644
--- a/Mergin/diff.py
+++ b/Mergin/diff.py
@@ -13,14 +13,11 @@
from qgis.PyQt.QtGui import QColor
from qgis.core import (
- QgsApplication,
QgsVectorLayer,
QgsFeature,
QgsGeometry,
QgsFields,
QgsField,
- QgsProject,
- QgsLayerTreeLayer,
QgsConditionalStyle,
QgsSymbolLayerUtils,
QgsMarkerSymbol,
@@ -78,14 +75,22 @@ def get_row_from_db(db_conn, schema_table, entry_changes):
Fetches a single row from DB's table based on the values of pkeys
in changeset entry
"""
- c = db_conn.cursor()
- where_bits = []
- for i, col in enumerate(schema_table.columns):
- if col.pkey:
- where_bits.append('"{}" = {}'.format(col.name, old_value_for_column_by_index(entry_changes, i)))
+ with db_conn.cursor() as c:
+ where_clauses = []
+ query_params = []
- c.execute('SELECT * FROM "{}" WHERE {}'.format(schema_table.name, " AND ".join(where_bits)))
- return c.fetchone()
+ for i, col in enumerate(schema_table.columns):
+ if col.pkey:
+ where_clauses.append('"{}" = %s'.format(col.name))
+ val = old_value_for_column_by_index(entry_changes, i)
+ query_params.append(val)
+
+ # We parameterize values securely; table/column names are trusted internal schema objects.
+ query = 'SELECT * FROM "{}" WHERE {}'.format(schema_table.name, " AND ".join(where_clauses)) # nosec B608
+
+ c.execute(query, tuple(query_params))
+
+ return c.fetchone()
def parse_gpkg_geom_encoding(wkb_with_gpkg_hdr):
@@ -215,7 +220,6 @@ def diff_table_to_features(diff_table, schema_table, fields, cols_to_flds, db_co
Input is list of tuples (type, changes) where type is 'insert'/'update'/'delete'
and changes is a list of dicts. Each dict with 'column', 'old', 'new' (old/new optional)
"""
- column_names = [column.name for column in schema_table.columns]
features = []
fld_geometry_idx = fields.indexOf("geometry")
@@ -225,7 +229,6 @@ def diff_table_to_features(diff_table, schema_table, fields, cols_to_flds, db_co
for entry_type, entry_changes in diff_table:
f = QgsFeature(fields)
- row = [None for i in range(len(column_names))]
f["_op"] = entry_type
@@ -235,7 +238,7 @@ def diff_table_to_features(diff_table, schema_table, fields, cols_to_flds, db_co
for i in range(len(db_row)):
if i == geom_col_index:
- if db_row[i] == None:
+ if db_row[i] is None:
continue
wkb = parse_gpkg_geom_encoding(db_row[i])
g = QgsGeometry()
@@ -254,7 +257,7 @@ def diff_table_to_features(diff_table, schema_table, fields, cols_to_flds, db_co
i = entry_change["column"]
if "old" in entry_change:
if i == geom_col_index:
- if entry_change["old"] == None:
+ if entry_change["old"] is None:
# Empty geometry
continue
wkb_with_gpkg_hdr = base64.decodebytes(entry_change["old"].encode("ascii"))
@@ -278,7 +281,7 @@ def diff_table_to_features(diff_table, schema_table, fields, cols_to_flds, db_co
value = "?"
if i == geom_col_index:
- if value == None:
+ if value is None:
# Empty geometry
continue
wkb_with_gpkg_hdr = base64.decodebytes(value.encode("ascii"))
@@ -357,7 +360,7 @@ def make_local_changes_layer(mp, layer):
"memory",
)
if not vl.isValid():
- return None, f"Failed to create memory layer for local changes"
+ return None, "Failed to create memory layer for local changes"
vl.dataProvider().addAttributes(fields)
vl.updateFields()
@@ -443,7 +446,7 @@ def get_layer_geometry_info(schema_json, table_name):
def style_diff_layer(layer, schema_table):
"""Apply conditional styling and symbology to diff layer"""
- ### setup conditional styles!
+ # setup conditional styles!
st = layer.conditionalStyles()
color_red = QColor("#ffdce0")
color_green = QColor("#dcffe4")
diff --git a/Mergin/diff_dialog.py b/Mergin/diff_dialog.py
index a2a81046..7ede3e19 100644
--- a/Mergin/diff_dialog.py
+++ b/Mergin/diff_dialog.py
@@ -6,7 +6,7 @@
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, QSettings
from qgis.PyQt.QtGui import QIcon, QColor
-from qgis.PyQt.QtWidgets import QDialog, QPushButton, QDialogButtonBox, QMenu, QAction
+from qgis.PyQt.QtWidgets import QDialog, QPushButton, QMenu, QAction
from qgis.core import (
QgsProject,
QgsVectorLayerCache,
@@ -15,7 +15,6 @@
QgsMessageLog,
Qgis,
QgsApplication,
- QgsWkbTypes,
)
from qgis.gui import (
QgsGui,
diff --git a/Mergin/plugin.py b/Mergin/plugin.py
index 734e5425..372d4a90 100644
--- a/Mergin/plugin.py
+++ b/Mergin/plugin.py
@@ -3,9 +3,9 @@
# GPLv3 license
# Copyright Lutra Consulting Limited
try:
- import sip
+ import sip # noqa: F401
except ImportError:
- from PyQt6 import sip
+ pass
import os
from functools import partial
from qgis.PyQt.QtCore import QUrl, QSettings, Qt
@@ -351,7 +351,7 @@ def configure_db_sync(self):
mp = MerginProject(project_path)
try:
project_name = mp.project_full_name()
- except InvalidProject as e:
+ except InvalidProject:
iface.messageBar().pushMessage(
"Mergin",
"Current project is not a Mergin project. Please open a Mergin project first.",
@@ -401,7 +401,7 @@ def set_current_workspace(self, workspace):
# check action required flag
try:
service_response = self.mc.workspace_service(workspace_id)
- except ClientError as e:
+ except ClientError:
return
except AuthTokenExpiredError:
self.auth_token_expired()
@@ -492,8 +492,8 @@ def find_project(self):
try:
workspaces = self.mc.workspaces_list()
dlg.enable_workspace_switching(len(workspaces) > 1)
- except:
- pass
+ except ClientError:
+ dlg.enable_workspace_switching(False)
dlg.exec()
@@ -501,7 +501,7 @@ def switch_workspace(self):
"""Open new Switch workspace dialog"""
try:
workspaces = self.mc.workspaces_list()
- except (URLError, ClientError) as e:
+ except (URLError, ClientError):
return # Server does not support workspaces
except AuthTokenExpiredError:
self.auth_token_expired()
@@ -547,9 +547,9 @@ def add_context_menu_actions(self, layers):
"arcgisvectortileservice",
"vtpkvectortiles",
)
- for l in layers:
- if l.dataProvider().name() in provider_names:
- self.iface.addCustomActionForLayer(self.action_export_mbtiles, l)
+ for layer in layers:
+ if layer.dataProvider().name() in provider_names:
+ self.iface.addCustomActionForLayer(self.action_export_mbtiles, layer)
def unload(self):
from .utils import pygeodiff
diff --git a/Mergin/processing/algs/create_diff.py b/Mergin/processing/algs/create_diff.py
index 81037f2d..9fca41d7 100644
--- a/Mergin/processing/algs/create_diff.py
+++ b/Mergin/processing/algs/create_diff.py
@@ -6,16 +6,13 @@
import os
import sqlite3
-import shutil
from qgis.PyQt.QtGui import QIcon
from qgis.core import (
QgsFeatureSink,
- QgsProcessing,
QgsProcessingUtils,
QgsProcessingException,
QgsProcessingAlgorithm,
- QgsProcessingContext,
QgsProcessingParameterFile,
QgsProcessingParameterNumber,
QgsProcessingParameterVectorLayer,
@@ -25,7 +22,6 @@
from ..postprocessors import StylingPostProcessor
from ...mergin.merginproject import MerginProject
-from ...mergin.utils import get_versions_with_file_changes
from ...mergin.deps import pygeodiff
from ...diff import (
@@ -144,7 +140,7 @@ def processAlgorithm(self, parameters, context, feedback):
except (ClientError, ValueError) as e:
raise QgsProcessingException(f"Error creating Mergin Maps client: {e}")
- mp = MerginProject(project_dir)
+ mp = MerginProject(project_dir) # noqa: F841
feedback.pushInfo("Downloading base fileā¦")
base_file = QgsProcessingUtils.generateTempFilename(file_name)
@@ -189,7 +185,6 @@ def processAlgorithm(self, parameters, context, feedback):
features = diff_table_to_features(diff[table_name], db_schema[table_name], fields, fields_mapping, db_conn)
feedback.setProgress(40)
- current = 40
step = 60.0 / len(features) if features else 0
for i, f in enumerate(features):
if feedback.isCanceled():
diff --git a/Mergin/processing/algs/create_report.py b/Mergin/processing/algs/create_report.py
index 6caf9074..04ed54af 100644
--- a/Mergin/processing/algs/create_report.py
+++ b/Mergin/processing/algs/create_report.py
@@ -2,8 +2,6 @@
from qgis.PyQt.QtGui import QIcon
from qgis.core import (
- QgsVectorFileWriter,
- QgsProcessing,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingContext,
diff --git a/Mergin/processing/algs/download_vector_tiles.py b/Mergin/processing/algs/download_vector_tiles.py
index a14df1ca..42be8ab7 100644
--- a/Mergin/processing/algs/download_vector_tiles.py
+++ b/Mergin/processing/algs/download_vector_tiles.py
@@ -18,7 +18,6 @@
QgsCsException,
QgsCoordinateReferenceSystem,
QgsBlockingNetworkRequest,
- QgsSqliteUtils,
QgsDataSourceUri,
QgsVectorTileLayer,
QgsCoordinateTransform,
@@ -218,9 +217,9 @@ def processAlgorithm(self, parameters, context, feedback):
f"{wgs_extent.xMinimum()},{wgs_extent.yMinimum()},{wgs_extent.xMaximum()},{wgs_extent.yMaximum()}"
)
writer.set_metadata_value("bounds", bounds_str)
- except QgsCsException as e:
+ except QgsCsException:
pass
- except AttributeError as e:
+ except AttributeError:
pass
step_feedback = QgsProcessingMultiStepFeedback(self.max_zoom + 1, feedback)
@@ -284,7 +283,7 @@ def postProcessAlgorithm(self, context, feedback):
tile_layer = QgsVectorTileLayer(bytes(ds_uri.encodedUri()).decode(), name)
if tile_layer.isValid():
if context.project():
- err = tile_layer.importNamedStyle(self.style_document)
+ tile_layer.importNamedStyle(self.style_document)
metadata = tile_layer.metadata()
metadata.setRights(self.attribution)
tile_layer.setMetadata(metadata)
diff --git a/Mergin/processing/algs/extract_local_changes.py b/Mergin/processing/algs/extract_local_changes.py
index 577eccde..e4d07823 100644
--- a/Mergin/processing/algs/extract_local_changes.py
+++ b/Mergin/processing/algs/extract_local_changes.py
@@ -10,10 +10,8 @@
from qgis.PyQt.QtGui import QIcon
from qgis.core import (
QgsFeatureSink,
- QgsProcessing,
QgsProcessingException,
QgsProcessingAlgorithm,
- QgsProcessingContext,
QgsProcessingParameterFile,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterFeatureSink,
@@ -122,7 +120,6 @@ def processAlgorithm(self, parameters, context, feedback):
features = diff_table_to_features(diff[table_name], db_schema[table_name], fields, fields_mapping, db_conn)
feedback.setProgress(20)
- current = 20
step = 80.0 / len(features) if features else 0
for i, f in enumerate(features):
if feedback.isCanceled():
diff --git a/Mergin/processing/provider.py b/Mergin/processing/provider.py
index 439196b4..e8a74553 100644
--- a/Mergin/processing/provider.py
+++ b/Mergin/processing/provider.py
@@ -4,8 +4,6 @@
# Copyright Lutra Consulting Limited
-import os
-
from qgis.PyQt.QtGui import QIcon
from qgis.core import QgsProcessingProvider
diff --git a/Mergin/project_selection_dialog.py b/Mergin/project_selection_dialog.py
index 144d2031..6f262246 100644
--- a/Mergin/project_selection_dialog.py
+++ b/Mergin/project_selection_dialog.py
@@ -27,7 +27,7 @@
QStandardItemModel,
)
-from .mergin.client import MerginProject, ServerType
+from .mergin.client import MerginProject
from .mergin.common import InvalidProject
from .mergin.client import AuthTokenExpiredError
from .utils import (
@@ -219,7 +219,7 @@ def run(self):
return
self.finished.emit(projects)
- except (URLError, ClientError) as e:
+ except (URLError, ClientError):
return
except AuthTokenExpiredError:
return
diff --git a/Mergin/project_status_dialog.py b/Mergin/project_status_dialog.py
index cabe38d9..9eee433a 100644
--- a/Mergin/project_status_dialog.py
+++ b/Mergin/project_status_dialog.py
@@ -120,19 +120,19 @@ def _get_info_text(self, has_files_to_replace, has_write_permissions, has_unfini
msg = []
if not has_write_permissions:
msg.append(
- f"You don't have writing permissions to this project. Changes won't be synced!\n"
- f"You may package the current project to a writable workspace instead, by selecting Create New Project."
+ "You don't have writing permissions to this project. Changes won't be synced!\n"
+ "You may package the current project to a writable workspace instead, by selecting Create New Project."
)
if has_files_to_replace:
msg.append(
- f"Unable to compare some of the modified files with their server version - "
- f"their history will be lost if uploaded."
+ "Unable to compare some of the modified files with their server version - "
+ "their history will be lost if uploaded."
)
if has_unfinished_pull:
msg.append(
- f"The previous pull has not finished completely: status of some files may be reported incorrectly."
+ "The previous pull has not finished completely: status of some files may be reported incorrectly."
)
return msg
diff --git a/Mergin/projects_manager.py b/Mergin/projects_manager.py
index e4392aaf..3980dc1b 100644
--- a/Mergin/projects_manager.py
+++ b/Mergin/projects_manager.py
@@ -52,7 +52,7 @@
class MerginProjectsManager(object):
"""Class for managing Mergin Maps projects in QGIS."""
- def __init__(self, plugin: "MerginPlugin"):
+ def __init__(self, plugin: "MerginPlugin"): # noqa F821
self.mc: MerginClient = plugin.mc
self.iface = iface
self.plugin = plugin
diff --git a/Mergin/sync_dialog.py b/Mergin/sync_dialog.py
index a86dfca7..64042f6a 100644
--- a/Mergin/sync_dialog.py
+++ b/Mergin/sync_dialog.py
@@ -118,7 +118,8 @@ def download_start_internal(self):
self.reset_operation(success=False, close=True, exception=e)
return
- assert self.job # if there was no error thrown, we should have a job
+ if not self.job:
+ return
# use kilobytes as a unit, so we do not need to worry about int overflow with projects of few GB size
self.progress.setMaximum(int(self.job.total_size / 1024))
diff --git a/Mergin/test/test_help.py b/Mergin/test/test_help.py
index bbeb55c9..8d60d1a9 100644
--- a/Mergin/test/test_help.py
+++ b/Mergin/test/test_help.py
@@ -5,19 +5,9 @@
import os
-import tempfile
import urllib.request
-
-from qgis.PyQt.QtCore import QVariant
from qgis.core import (
QgsVectorLayer,
- QgsFields,
- QgsField,
- QgsVectorFileWriter,
- QgsWkbTypes,
- QgsCoordinateTransformContext,
- QgsCoordinateReferenceSystem,
- QgsProject,
)
from qgis.testing import start_app, unittest
from Mergin.help import MerginHelp
diff --git a/Mergin/test/test_utils.py b/Mergin/test/test_utils.py
index 5c429158..9c3b575a 100644
--- a/Mergin/test/test_utils.py
+++ b/Mergin/test/test_utils.py
@@ -124,7 +124,7 @@ def test_name_validation(self):
(" project", False),
(".project", False),
("proj~ect", False),
- ("pro\ject", False),
+ (r"pro\ject", False),
("pro/ject", False),
("pro|ject", False),
("pro+ject", False),
diff --git a/Mergin/test/test_validations.py b/Mergin/test/test_validations.py
index 9b6071d6..86344dec 100644
--- a/Mergin/test/test_validations.py
+++ b/Mergin/test/test_validations.py
@@ -87,7 +87,7 @@ def test_attachment_widget(self):
# local path
config["RelativeStorage"] = QgsFileWidget.RelativeStorage.RelativeProject
- config["DefaultRoot"] = "/tmp/photos"
+ config["DefaultRoot"] = os.path.join(self.temp_dir, "photos")
widget_setup = QgsEditorWidgetSetup("ExternalResource", config)
self.mem_layer.setEditorWidgetSetup(photo_field_idx, widget_setup)
validator.check_attachment_widget()
@@ -135,7 +135,7 @@ def test_attachment_widget(self):
# right setup, valid expression
config["PropertyCollection"]["properties"]["propertyRootPath"]["expression"] = "@project_folder + '/photos'"
- config["DefaultRoot"] = "/tmp/photos" # default root should be override
+ config["DefaultRoot"] = os.path.join(self.temp_dir, "photos") # default root should be override
widget_setup = QgsEditorWidgetSetup("ExternalResource", config)
self.mem_layer.setEditorWidgetSetup(photo_field_idx, widget_setup)
validator.check_attachment_widget()
diff --git a/Mergin/utils.py b/Mergin/utils.py
index f09cac21..9b9de2ab 100644
--- a/Mergin/utils.py
+++ b/Mergin/utils.py
@@ -75,28 +75,35 @@
from .mergin.merginproject import MerginProject
try:
- from .mergin.common import ClientError, ErrorCode, LoginError, InvalidProject, SYNC_ATTEMPTS, SYNC_ATTEMPT_WAIT
- from .mergin.client import MerginClient, ServerType, AuthTokenExpiredError
- from .mergin.client_pull import (
+ from .mergin.common import ( # noqa: F401
+ ClientError,
+ ErrorCode,
+ LoginError,
+ InvalidProject,
+ SYNC_ATTEMPTS,
+ SYNC_ATTEMPT_WAIT,
+ )
+ from .mergin.client import MerginClient, ServerType, AuthTokenExpiredError # noqa: F401
+ from .mergin.client_pull import ( # noqa: F401
download_project_async,
download_project_is_running,
download_project_finalize,
download_project_cancel,
)
- from .mergin.client_pull import (
+ from .mergin.client_pull import ( # noqa: F401
pull_project_async,
pull_project_is_running,
pull_project_finalize,
pull_project_cancel,
)
- from .mergin.client_push import (
+ from .mergin.client_push import ( # noqa: F401
push_project_async,
push_project_is_running,
push_project_finalize,
push_project_cancel,
get_push_changes_batch,
)
- from .mergin.report import create_report
+ from .mergin.report import create_report # noqa: F401
from .mergin.deps import pygeodiff
except ImportError:
import sys
@@ -104,22 +111,22 @@
this_dir = os.path.dirname(os.path.realpath(__file__))
path = os.path.join(this_dir, "mergin_client.whl")
sys.path.append(path)
- from mergin.client import MerginClient, ServerType
- from mergin.common import ClientError, InvalidProject, LoginError, SYNC_ATTEMPTS, SYNC_ATTEMPT_WAIT
+ from mergin.client import MerginClient, ServerType, AuthTokenExpiredError # noqa: F401
+ from mergin.common import ClientError, InvalidProject, LoginError, SYNC_ATTEMPTS, SYNC_ATTEMPT_WAIT # noqa: F401
- from mergin.client_pull import (
+ from mergin.client_pull import ( # noqa: F401
download_project_async,
download_project_is_running,
download_project_finalize,
download_project_cancel,
)
- from mergin.client_pull import (
+ from mergin.client_pull import ( # noqa: F401
pull_project_async,
pull_project_is_running,
pull_project_finalize,
pull_project_cancel,
)
- from mergin.client_push import (
+ from mergin.client_push import ( # noqa: F401
push_project_async,
push_project_is_running,
push_project_finalize,
@@ -483,7 +490,7 @@ def create_basic_qgis_project(project_path=None, project_name=None):
new_project.addMapLayer(vt_layer)
mem_uri = "Point?crs=epsg:3857"
mem_layer = QgsVectorLayer(mem_uri, "Survey points", "memory")
- res = mem_layer.dataProvider().addAttributes(
+ mem_layer.dataProvider().addAttributes(
[
QgsField("date", QVariant.DateTime),
QgsField("notes", QVariant.String),
@@ -965,7 +972,7 @@ def get_local_mergin_projects_info(workspace=None):
# - needs project dir to load metadata
key_parts = key.split("/")
if len(key_parts) > 2 and key_parts[2] == "path":
- if workspace != None and key_parts[0] != workspace:
+ if workspace is not None and key_parts[0] != workspace:
continue
local_path = settings.value(key, None)
@@ -1083,7 +1090,7 @@ def remove_prefix(text: str, prefix: str):
Similar to str.removeprefix remove once we drop support for 3.22/python 3.8
"""
if text.startswith(prefix):
- return text[len(prefix) :]
+ return text[len(prefix) :] # noqa: E203
return text
@@ -1108,9 +1115,6 @@ def has_schema_change(mp, layer):
Check whether the layer has schema changes using schema representaion
in JSON format generated by geodiff.
"""
-
- geodiff = pygeodiff.GeoDiff()
-
local_path = layer.publicSource().split("|")[0]
f_name = os.path.split(local_path)[1]
base_path = mp.fpath_meta(f_name)
@@ -1192,8 +1196,6 @@ def get_primary_keys(layer):
"""
Returns list of column names which are used as a primary key
"""
- geodiff = pygeodiff.GeoDiff()
-
file_path = layer.publicSource().split("|")[0]
table_name = os.path.splitext(os.path.split(file_path)[1])[0]
@@ -1436,7 +1438,7 @@ def create_map_sketches_layer(project_path):
options.driverName = "GPKG"
options.layerName = "Map sketches"
- writer = QgsVectorFileWriter.create(
+ writer = QgsVectorFileWriter.create( # noqa: F841
filename,
fields,
QgsWkbTypes.MultiLineStringZM,
@@ -1707,7 +1709,7 @@ def qvariant_to_string(val: Any) -> str:
if s:
return s
except Exception:
- pass
+ pass # nosec
# Fallback
return str(val)
diff --git a/Mergin/utils_auth.py b/Mergin/utils_auth.py
index f9594dd4..0b76f37b 100644
--- a/Mergin/utils_auth.py
+++ b/Mergin/utils_auth.py
@@ -39,7 +39,7 @@
class LoginType(Enum):
"""Types of login supported by Mergin Maps."""
- PASSWORD = "password" # classic login with username and password
+ PASSWORD = "password" # nosec B105 # pragma: allowlist secret
SSO = "sso" # login with SSO token
def __str__(self) -> str:
@@ -295,7 +295,7 @@ def login_sso(server_url: str, oauth2_client_id: str, email: typing.Optional[str
"id": "mmmmsso",
"name": "Mergin Maps SSO",
"objectName": "",
- "password": "",
+ "password": "", # nosec B105
"persistToken": False,
"queryPairs": {
"state": str(uuid.uuid4()),
diff --git a/Mergin/validation.py b/Mergin/validation.py
index 7b91b75d..acb86fa2 100644
--- a/Mergin/validation.py
+++ b/Mergin/validation.py
@@ -31,8 +31,8 @@
is_inside,
)
-INVALID_FIELD_NAME_CHARS = re.compile('[\\\/\(\)\[\]\{\}"\n\r]')
-PROJECT_VARS = re.compile("\@project_home|\@project_path|\@project_folder")
+INVALID_FIELD_NAME_CHARS = re.compile(r'[\\\/\(\)\[\]\{\}"\n\r]')
+PROJECT_VARS = re.compile("@project_home|@project_path|@project_folder")
class Warning(Enum):
@@ -160,7 +160,8 @@ def check_proj_loaded(self):
def check_proj_paths_relative(self):
"""Check if the QGIS project has relative paths, i.e. not absolute ones."""
abs_paths, ok = self.qgis_proj.readEntry("Paths", "/Absolute")
- assert ok
+ if not ok:
+ raise RuntimeError("Cannot read project paths configuration")
if not abs_paths == "false":
self.issues.append(MultipleLayersWarning(Warning.ABSOLUTE_PATHS))
diff --git a/Mergin/version_viewer_dialog.py b/Mergin/version_viewer_dialog.py
index 36547e5c..778d5311 100644
--- a/Mergin/version_viewer_dialog.py
+++ b/Mergin/version_viewer_dialog.py
@@ -32,13 +32,12 @@ class QgsTiledSceneLayer:
QgsGui,
QgsMapToolPan,
)
-from qgis.PyQt import QtCore, uic
+from qgis.PyQt import uic
from qgis.PyQt.QtCore import (
QAbstractTableModel,
QItemSelectionModel,
QModelIndex,
QSettings,
- QStringListModel,
Qt,
QThread,
pyqtSignal,
@@ -62,7 +61,6 @@ class QgsTiledSceneLayer:
from .mergin.merginproject import MerginProject
from .mergin.utils import bytes_to_human_size, int_version
from .utils import (
- PROJS_PER_PAGE,
ClientError,
contextual_date,
format_datetime,
@@ -214,7 +212,7 @@ def __init__(self, mc, mp, version):
def run(self):
try:
version_info = self.mc.project_version_info(self.mp.project_id(), version=f"v{self.version}")
- except AuthTokenExpiredError:
+ except AuthTokenExpiredError as e:
self.error_occured.emit(e)
return
@@ -243,7 +241,7 @@ def run(self):
if "diff" not in f:
continue
try:
- file_diffs = self.mc.download_file_diffs(self.mp.dir, f["path"], [f"v{self.version}"])
+ self.mc.download_file_diffs(self.mp.dir, f["path"], [f"v{self.version}"])
full_gpkg = self.mp.fpath_cache(f["path"], version=f"v{self.version}")
if not os.path.exists(full_gpkg):
self.mc.download_file(self.mp.dir, f["path"], full_gpkg, f"v{self.version}")
@@ -295,7 +293,7 @@ def has_more_page(self):
return self.current_page <= self.nb_page
def fetch_another_page(self):
- if self.has_more_page() == False:
+ if self.has_more_page() is False:
return
self.model.beginFetching()
try:
@@ -469,7 +467,7 @@ def exec(self):
except ClientError:
# Some versions e.g CE, EE edition doesn't have
pass
- except AuthTokenExpiredError as e:
+ except AuthTokenExpiredError:
self.plugin.auth_token_expired()
super().exec()
@@ -546,7 +544,7 @@ def selected_version_changed(self, current_index: QModelIndex, previous_index):
try:
item = self.versionModel.item_from_index(current_index)
- except:
+ except Exception:
# Click on invalid item like loading
return
version_name = item["name"]
@@ -633,7 +631,7 @@ def toggle_background_layers(self, checked):
self.update_canvas(layers, set_extent=False)
def update_canvas(self, layers, set_extent=True):
- if self.current_diff and self.current_diff.isSpatial() == False:
+ if self.current_diff and self.current_diff.isSpatial() is False:
self.map_canvas.setEnabled(False)
self.save_splitters_state()
self.splitter_map_table.setSizes([0, 1])
@@ -715,7 +713,7 @@ def handle_exception(self, e: Exception):
else:
self.failed_to_fetch = True
additional_log = str(e)
- QgsMessageLog.logMessage(f"Download history error: " + additional_log, "Mergin")
+ QgsMessageLog.logMessage("Download history error: " + additional_log, "Mergin")
self.label_info.setText(
"There was an issue loading this version. Please try again later or contact our support if the issue persists. Refer to the QGIS messages log for more details."
)