oqtopus 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oqtopus/core/module.py +176 -32
- oqtopus/core/module_operation_task.py +234 -0
- oqtopus/core/module_package.py +27 -10
- oqtopus/core/modules_config.py +2 -0
- oqtopus/core/package_prepare_task.py +240 -25
- oqtopus/gui/database_connection_widget.py +12 -5
- oqtopus/gui/database_create_dialog.py +3 -3
- oqtopus/gui/database_duplicate_dialog.py +4 -4
- oqtopus/gui/logs_widget.py +94 -7
- oqtopus/gui/main_dialog.py +118 -31
- oqtopus/gui/module_selection_widget.py +110 -22
- oqtopus/gui/module_widget.py +647 -61
- oqtopus/gui/parameters_groupbox.py +25 -13
- oqtopus/gui/plugin_widget.py +13 -0
- oqtopus/gui/project_widget.py +5 -0
- oqtopus/gui/settings_dialog.py +2 -0
- oqtopus/oqtopus_plugin.py +10 -1
- oqtopus/ui/module_selection_widget.ui +96 -96
- oqtopus/ui/module_widget.ui +72 -58
- oqtopus/ui/settings_dialog.ui +18 -11
- oqtopus/utils/plugin_utils.py +113 -19
- oqtopus/utils/qt_utils.py +54 -0
- {oqtopus-0.2.0.dist-info → oqtopus-1.0.0.dist-info}/METADATA +1 -1
- oqtopus-1.0.0.dist-info/RECORD +47 -0
- {oqtopus-0.2.0.dist-info → oqtopus-1.0.0.dist-info}/WHEEL +1 -1
- tests/test_imports.py +59 -0
- oqtopus-0.2.0.dist-info/RECORD +0 -45
- {oqtopus-0.2.0.dist-info → oqtopus-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {oqtopus-0.2.0.dist-info → oqtopus-1.0.0.dist-info}/top_level.txt +0 -0
oqtopus/utils/plugin_utils.py
CHANGED
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
***************************************************************************/
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
+
import glob
|
|
20
21
|
import logging
|
|
21
22
|
import os
|
|
23
|
+
from datetime import datetime
|
|
22
24
|
from logging import LogRecord
|
|
23
|
-
from logging.handlers import TimedRotatingFileHandler
|
|
24
25
|
|
|
25
26
|
from qgis.PyQt.QtCore import (
|
|
26
27
|
QDir,
|
|
@@ -34,12 +35,14 @@ from qgis.PyQt.QtCore import (
|
|
|
34
35
|
from qgis.PyQt.QtGui import QColor, QDesktopServices, QIcon
|
|
35
36
|
from qgis.PyQt.uic import loadUiType
|
|
36
37
|
|
|
38
|
+
from ..libs.pum import SQL
|
|
39
|
+
|
|
37
40
|
logger = logging.getLogger("oqtopus")
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
class PluginUtils:
|
|
41
44
|
|
|
42
|
-
PLUGIN_NAME = "
|
|
45
|
+
PLUGIN_NAME = "oQtopus"
|
|
43
46
|
|
|
44
47
|
logsDirectory = ""
|
|
45
48
|
|
|
@@ -55,8 +58,30 @@ class PluginUtils:
|
|
|
55
58
|
"""
|
|
56
59
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
|
57
60
|
|
|
61
|
+
@staticmethod
|
|
62
|
+
def plugin_cache_path():
|
|
63
|
+
"""Returns the persistent cache path for the plugin (GitHub API cache, downloaded packages).
|
|
64
|
+
|
|
65
|
+
Uses CacheLocation which is appropriate for data that should persist
|
|
66
|
+
across sessions but can be safely deleted.
|
|
67
|
+
On macOS: ~/Library/Caches/oqtopus
|
|
68
|
+
On Linux: ~/.cache/oqtopus
|
|
69
|
+
On Windows: C:/Users/<USER>/AppData/Local/oqtopus/cache
|
|
70
|
+
"""
|
|
71
|
+
cache_dir = os.path.join(
|
|
72
|
+
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
|
|
73
|
+
"oqtopus",
|
|
74
|
+
)
|
|
75
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
76
|
+
return cache_dir
|
|
77
|
+
|
|
58
78
|
@staticmethod
|
|
59
79
|
def plugin_temp_path():
|
|
80
|
+
"""Returns the temporary path for the plugin.
|
|
81
|
+
|
|
82
|
+
DEPRECATED: Use plugin_cache_path() instead for persistent cache.
|
|
83
|
+
This is kept for backward compatibility.
|
|
84
|
+
"""
|
|
60
85
|
plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
|
|
61
86
|
|
|
62
87
|
plugin_temp_dir = os.path.join(
|
|
@@ -68,6 +93,33 @@ class PluginUtils:
|
|
|
68
93
|
|
|
69
94
|
return plugin_temp_dir
|
|
70
95
|
|
|
96
|
+
@staticmethod
|
|
97
|
+
def get_all_cache_paths():
|
|
98
|
+
"""Returns all cache directories used by the plugin.
|
|
99
|
+
|
|
100
|
+
This is used by the cleanup function to ensure all caches are deleted.
|
|
101
|
+
"""
|
|
102
|
+
paths = []
|
|
103
|
+
|
|
104
|
+
# Main cache directory (CacheLocation)
|
|
105
|
+
cache_path = os.path.join(
|
|
106
|
+
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
|
|
107
|
+
"oqtopus",
|
|
108
|
+
)
|
|
109
|
+
if os.path.exists(cache_path):
|
|
110
|
+
paths.append(cache_path)
|
|
111
|
+
|
|
112
|
+
# Legacy temp directory (TempLocation) - for backward compatibility
|
|
113
|
+
plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
|
|
114
|
+
temp_path = os.path.join(
|
|
115
|
+
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
|
|
116
|
+
plugin_basename,
|
|
117
|
+
)
|
|
118
|
+
if os.path.exists(temp_path):
|
|
119
|
+
paths.append(temp_path)
|
|
120
|
+
|
|
121
|
+
return paths
|
|
122
|
+
|
|
71
123
|
@staticmethod
|
|
72
124
|
def get_plugin_icon_path(icon_filename):
|
|
73
125
|
return os.path.join(PluginUtils.plugin_root_path(), "icons", icon_filename)
|
|
@@ -108,33 +160,66 @@ class PluginUtils:
|
|
|
108
160
|
directory.mkpath(PluginUtils.logsDirectory)
|
|
109
161
|
|
|
110
162
|
if directory.exists():
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
163
|
+
# Create a new log file for each session with timestamp
|
|
164
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
165
|
+
logfile = QFileInfo(directory, f"oQtopus_{timestamp}.log")
|
|
166
|
+
PluginUtils.currentLogFile = logfile.filePath()
|
|
167
|
+
|
|
168
|
+
# Clean up old log files, keep only the 10 most recent
|
|
169
|
+
PluginUtils._cleanup_old_logs(PluginUtils.logsDirectory, keep=10)
|
|
170
|
+
|
|
171
|
+
# Create file handler for this session
|
|
172
|
+
fileHandler = logging.FileHandler(logfile.filePath(), mode="w")
|
|
173
|
+
fileHandler.setLevel(SQL) # Use SQL level (5) to capture all messages including SQL
|
|
174
|
+
fileHandler.setFormatter(logging.Formatter("%(asctime)s %(levelname)-7s %(message)s"))
|
|
175
|
+
|
|
176
|
+
# Configure logging - basicConfig might not work if already called
|
|
177
|
+
# so we configure the root logger directly
|
|
178
|
+
root_logger = logging.getLogger()
|
|
179
|
+
root_logger.setLevel(SQL) # Set root to SQL level to allow SQL messages through
|
|
180
|
+
root_logger.addHandler(fileHandler)
|
|
181
|
+
|
|
182
|
+
# Set the pum library to SQL level (5) to capture all SQL statements
|
|
183
|
+
# SQL (5) < DEBUG (10) < INFO (20)
|
|
184
|
+
logging.getLogger("pum").setLevel(SQL)
|
|
124
185
|
else:
|
|
125
186
|
logger.error(f"Can't create log files directory '{PluginUtils.logsDirectory}'.")
|
|
126
187
|
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _cleanup_old_logs(logs_dir: str, keep: int = 10):
|
|
190
|
+
"""Remove old log files, keeping only the most recent ones.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
logs_dir: Directory containing log files
|
|
194
|
+
keep: Number of most recent log files to keep
|
|
195
|
+
"""
|
|
196
|
+
log_files = glob.glob(os.path.join(logs_dir, "oQtopus_*.log"))
|
|
197
|
+
# Sort by modification time, newest first
|
|
198
|
+
log_files.sort(key=os.path.getmtime, reverse=True)
|
|
199
|
+
# Remove old files beyond the keep limit
|
|
200
|
+
for old_file in log_files[keep:]:
|
|
201
|
+
try:
|
|
202
|
+
os.remove(old_file)
|
|
203
|
+
except OSError:
|
|
204
|
+
pass # Ignore errors when removing old logs
|
|
205
|
+
|
|
127
206
|
@staticmethod
|
|
128
207
|
def open_logs_folder():
|
|
129
208
|
QDesktopServices.openUrl(QUrl.fromLocalFile(PluginUtils.logsDirectory))
|
|
130
209
|
|
|
131
210
|
@staticmethod
|
|
132
211
|
def open_log_file():
|
|
133
|
-
log_file_path =
|
|
134
|
-
if os.path.exists(log_file_path):
|
|
212
|
+
log_file_path = getattr(PluginUtils, "currentLogFile", None)
|
|
213
|
+
if log_file_path and os.path.exists(log_file_path):
|
|
135
214
|
QDesktopServices.openUrl(QUrl.fromLocalFile(log_file_path))
|
|
136
215
|
else:
|
|
137
|
-
|
|
216
|
+
# Fallback: open most recent log file
|
|
217
|
+
log_files = glob.glob(os.path.join(PluginUtils.logsDirectory, "oQtopus_*.log"))
|
|
218
|
+
if log_files:
|
|
219
|
+
log_files.sort(key=os.path.getmtime, reverse=True)
|
|
220
|
+
QDesktopServices.openUrl(QUrl.fromLocalFile(log_files[0]))
|
|
221
|
+
else:
|
|
222
|
+
logger.error("No log file found.")
|
|
138
223
|
|
|
139
224
|
@staticmethod
|
|
140
225
|
def get_github_token():
|
|
@@ -146,6 +231,16 @@ class PluginUtils:
|
|
|
146
231
|
settings = QSettings()
|
|
147
232
|
settings.setValue("oqtopus/github_token", token)
|
|
148
233
|
|
|
234
|
+
@staticmethod
|
|
235
|
+
def get_allow_multiple_modules():
|
|
236
|
+
settings = QSettings()
|
|
237
|
+
return settings.value("oqtopus/allow_multiple_modules", False, type=bool)
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def set_allow_multiple_modules(allow: bool):
|
|
241
|
+
settings = QSettings()
|
|
242
|
+
settings.setValue("oqtopus/allow_multiple_modules", allow)
|
|
243
|
+
|
|
149
244
|
@staticmethod
|
|
150
245
|
def get_github_headers():
|
|
151
246
|
token = PluginUtils.get_github_token()
|
|
@@ -176,5 +271,4 @@ class LoggingBridge(logging.Handler, QObject):
|
|
|
176
271
|
|
|
177
272
|
def emit(self, record):
|
|
178
273
|
log_entry = self.format(record)
|
|
179
|
-
print(log_entry)
|
|
180
274
|
self.loggedLine.emit(record, log_entry)
|
oqtopus/utils/qt_utils.py
CHANGED
|
@@ -68,6 +68,60 @@ class QtUtils:
|
|
|
68
68
|
font.setItalic(italic)
|
|
69
69
|
widget.setFont(font)
|
|
70
70
|
|
|
71
|
+
@staticmethod
|
|
72
|
+
def setTextWithEllipsis(label, text, max_length=80):
|
|
73
|
+
"""
|
|
74
|
+
Set text on a label with ellipsis in the middle if too long.
|
|
75
|
+
Sets full text as tooltip.
|
|
76
|
+
:param label: The QLabel widget to set text on.
|
|
77
|
+
:param text: The text to display.
|
|
78
|
+
:param max_length: Maximum length before truncating (default: 80).
|
|
79
|
+
"""
|
|
80
|
+
if len(text) <= max_length:
|
|
81
|
+
label.setText(text)
|
|
82
|
+
label.setToolTip("")
|
|
83
|
+
else:
|
|
84
|
+
# Calculate how many characters to keep on each side
|
|
85
|
+
side_length = (max_length - 3) // 2 # 3 for the ellipsis
|
|
86
|
+
truncated = f"{text[:side_length]}…{text[-side_length:]}"
|
|
87
|
+
label.setText(truncated)
|
|
88
|
+
label.setToolTip(text)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def shortenPath(path: str, max_length: int = 50) -> str:
|
|
92
|
+
"""Shorten a long path by replacing middle part with ellipsis.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
path: The full path to shorten
|
|
96
|
+
max_length: Maximum length before shortening
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Shortened path with … in the middle if too long
|
|
100
|
+
"""
|
|
101
|
+
if len(path) <= max_length:
|
|
102
|
+
return path
|
|
103
|
+
|
|
104
|
+
# Calculate how many characters to keep from start and end
|
|
105
|
+
# Reserve 1 character for the ellipsis
|
|
106
|
+
chars_to_keep = max_length - 1
|
|
107
|
+
start_chars = chars_to_keep // 2
|
|
108
|
+
end_chars = chars_to_keep - start_chars
|
|
109
|
+
|
|
110
|
+
return f"{path[:start_chars]}…{path[-end_chars:]}"
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def setPathLinkWithEllipsis(label, path: str, max_length: int = 50):
|
|
114
|
+
"""Set a file path as a clickable link on a label, with ellipsis for long paths.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
label: The QLabel widget to set the link on.
|
|
118
|
+
path: The full file path.
|
|
119
|
+
max_length: Maximum display length before truncating (default: 50).
|
|
120
|
+
"""
|
|
121
|
+
display_path = QtUtils.shortenPath(str(path), max_length)
|
|
122
|
+
label.setText(f"<a href='file://{path}'>{display_path}</a>")
|
|
123
|
+
label.setToolTip(str(path))
|
|
124
|
+
|
|
71
125
|
|
|
72
126
|
class CriticalMessageBox(QMessageBox):
|
|
73
127
|
def __init__(self, title: str, description: str, exception: Exception = None, parent=None):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oqtopus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: oQtopus : A QGIS module manager that helps you deploy, manage and upgrade your QGIS projects, plugins and associated postgreSQL / PostGIS data model implementations
|
|
5
5
|
Author-email: Damiano Lombardi <damiano@opengis.ch>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
oqtopus/__init__.py,sha256=PUl5ObSgoyF6tcNphBip9Rff8lul0t9VobiwsLKwgFA,104
|
|
2
|
+
oqtopus/oqtopus.py,sha256=BF_JSF2oOCzc8Qr7Z0B7FJdYrdAR3FR9L7oXh3QatL4,1860
|
|
3
|
+
oqtopus/oqtopus_plugin.py,sha256=G5gDxJQQuTwhaf7b-sNDvcd_W4S1BQ1WC1msXJLmqKw,6516
|
|
4
|
+
oqtopus/core/module.py,sha256=iQ4UqI-HJ1X5I1uVEvs7Kplrt9lWxYun4r1FO7kiKmw,10060
|
|
5
|
+
oqtopus/core/module_asset.py,sha256=qr_wwriJ_rZd86EuBo_Rx-MDCDJNYA6odhk6F-Z6YFA,467
|
|
6
|
+
oqtopus/core/module_operation_task.py,sha256=YCBrIsbmJvS4WpXlRw4XioZWZgSaPl8RTBxv5qgMmSQ,8352
|
|
7
|
+
oqtopus/core/module_package.py,sha256=b5fEMVDl1r0fqhswFT24UPRebar68hrEfw82MH41yvI,4988
|
|
8
|
+
oqtopus/core/modules_config.py,sha256=-56Jm1s0NsB-Byf2qxEm3vzsgDm2RsRSCtCSJH8s5Xk,276
|
|
9
|
+
oqtopus/core/package_prepare_task.py,sha256=zpUnTIM57wqt_DDk4kvpllrXc3muDqKd94lB7XhjwKY,15387
|
|
10
|
+
oqtopus/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
oqtopus/gui/about_dialog.py,sha256=dDNwHxKf_M82Z-TKmXW1fXmnI_WSV_7MShqz05trzRw,2345
|
|
12
|
+
oqtopus/gui/database_connection_widget.py,sha256=swsHmIXdYICTDHeoROGocVqhAVmTysszzlgnfwWIUbo,7044
|
|
13
|
+
oqtopus/gui/database_create_dialog.py,sha256=INyajKxVeIfUS5GWLPmC4nEUAVTck1SxGod99qLDu8I,8901
|
|
14
|
+
oqtopus/gui/database_duplicate_dialog.py,sha256=9byfL6WBYuep4aBU9_YZaRmJ_96wwrXXNTSO-7rzhlM,4969
|
|
15
|
+
oqtopus/gui/logs_widget.py,sha256=mcxlxvH9XHSIwToTHtWiwlaT1-446ZDwtouYc9ZwW20,9795
|
|
16
|
+
oqtopus/gui/main_dialog.py,sha256=smpYnRtx5WuAk-R7Azmu83L7346w_bKPtVCv-J23uxE,10303
|
|
17
|
+
oqtopus/gui/module_selection_widget.py,sha256=ivVLsSo0c7otVa3qET5RFvPgGoICbles0ydfuf-Jmvo,20623
|
|
18
|
+
oqtopus/gui/module_widget.py,sha256=uUUHErSqEkY2Qn3dqb1n29ql2uvlYR2NH-mEKrgUO_g,34483
|
|
19
|
+
oqtopus/gui/parameters_groupbox.py,sha256=ZAAu4b38k9js6EsW9VSZWyZ2ZG_KBDLKMUn2qeLJoV8,3489
|
|
20
|
+
oqtopus/gui/plugin_widget.py,sha256=vIcmaSw_0ZTaalaAwSbXWZNbQ0-hwD2MmpeMIU_-DYg,6382
|
|
21
|
+
oqtopus/gui/project_widget.py,sha256=7noewlFmMwa8tIMgzcHnmRVOY0z9Al0W9lEdYrvmeUg,7455
|
|
22
|
+
oqtopus/gui/settings_dialog.py,sha256=e-n7BatRbR-2pQleteKmEQs0zFfijBt-GYyVI6oDJHA,1742
|
|
23
|
+
oqtopus/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
oqtopus/ui/about_dialog.ui,sha256=tAc1F5NYOsM3rMwYBes1Id0avaLeFo0uSOMXRUK89UM,6244
|
|
25
|
+
oqtopus/ui/database_connection_widget.ui,sha256=0hLBaOx0KcLTH6RlkziVj-tsczjHDUGC-3EXQYgcTuE,3729
|
|
26
|
+
oqtopus/ui/database_create_dialog.ui,sha256=c-fZJRH4tmAeOr6O6M2sPIhzzYQMrSc1DI6a7at-UjI,9230
|
|
27
|
+
oqtopus/ui/database_duplicate_dialog.ui,sha256=EzMRYhlxsiP_sr9ixBJn4lnefM5R_p1oXx4RCyMAj9U,3826
|
|
28
|
+
oqtopus/ui/logs_widget.ui,sha256=oAgEtRUBXB-DzLC0vmBP-PEA-Uu3d3ppFm0ahs1jHQI,2785
|
|
29
|
+
oqtopus/ui/main_dialog.ui,sha256=A1U97A6Cl9h3GHtI4U6ZNI7KXpMEJM6aBKheuAshzto,2650
|
|
30
|
+
oqtopus/ui/module_selection_widget.ui,sha256=bsuOdy19iRuDfRjfJEK4HcNAyr8pT-GIhyAQoi1zyj4,5646
|
|
31
|
+
oqtopus/ui/module_widget.ui,sha256=LczVV5FBnvYPrQW32EZPvuei4PV7glsIC686w1ejkkA,6251
|
|
32
|
+
oqtopus/ui/plugin_widget.ui,sha256=osJ_QwzmXahV5fEHk4WfUjBBQ7jdsrL4pixiMF2jURA,2570
|
|
33
|
+
oqtopus/ui/project_widget.ui,sha256=5YqSjHR8mQFmHzbSB2M6JnNfrpkqhlRC0N9MSlgcXY8,1600
|
|
34
|
+
oqtopus/ui/settings_dialog.ui,sha256=iJ3yg3xWRFYa6DfMVKNv8EgFEb9gKRqAkUu_lgP6xQk,2472
|
|
35
|
+
oqtopus/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
oqtopus/utils/plugin_utils.py,sha256=ZOWYfI162suuVPROqJ2z0mbnl5JW3ff1gfbZ_iBr91o,9809
|
|
37
|
+
oqtopus/utils/qt_utils.py,sha256=ByJxFfbml1wwHvRvKxRNvlgltVU6zI1dmOjgALtFXT8,5291
|
|
38
|
+
oqtopus/utils/tmmtlogging.py,sha256=BSAPrhQGCuH2boMG4bP8QFyLfoss1uR4pO_pXjTA1eQ,1733
|
|
39
|
+
oqtopus/utils/translation.py,sha256=p1d5N6CYIf94fGYdPQnJamdum9MVBlPkJ24c557rqdg,2647
|
|
40
|
+
oqtopus-1.0.0.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
41
|
+
tests/__init__.py,sha256=z5CxS92efenLcDm64Sifx26EjZBCWPO4oVvaEEHJJ6w,444
|
|
42
|
+
tests/test_imports.py,sha256=uxa1OGKq2RVXm1lPQT3qb23hWd9yCncLI9aVWM1Cz_I,2507
|
|
43
|
+
tests/test_plugin_load.py,sha256=HuVLdrsn2PSjKIrvcLj0EH7vNE8-0jBMmHZHihsqB3Q,517
|
|
44
|
+
oqtopus-1.0.0.dist-info/METADATA,sha256=h2x2M-pF49m5CIqPqjX4rF2sFygrZUxJ4yJqKJihxfA,21787
|
|
45
|
+
oqtopus-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
46
|
+
oqtopus-1.0.0.dist-info/top_level.txt,sha256=i4DHi21kcGIzrF8DlgsKj-UCENHg_NebTRac7cwt32A,14
|
|
47
|
+
oqtopus-1.0.0.dist-info/RECORD,,
|
tests/test_imports.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Test to ensure bundled libraries are imported correctly."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_no_direct_bundled_lib_imports():
|
|
8
|
+
"""
|
|
9
|
+
Ensure that pum and pgserviceparser are never imported directly.
|
|
10
|
+
They should always be imported from oqtopus.libs to use the bundled versions.
|
|
11
|
+
"""
|
|
12
|
+
oqtopus_dir = Path(__file__).parent.parent / "oqtopus"
|
|
13
|
+
forbidden_imports = ["pum", "pgserviceparser"]
|
|
14
|
+
violations = []
|
|
15
|
+
|
|
16
|
+
# Scan all Python files in the oqtopus directory
|
|
17
|
+
for py_file in oqtopus_dir.rglob("*.py"):
|
|
18
|
+
# Skip __init__.py in libs directory (it's allowed to import directly)
|
|
19
|
+
if py_file.parent.name == "libs" and py_file.name == "__init__.py":
|
|
20
|
+
continue
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
with open(py_file, encoding="utf-8") as f:
|
|
24
|
+
tree = ast.parse(f.read(), filename=str(py_file))
|
|
25
|
+
|
|
26
|
+
for node in ast.walk(tree):
|
|
27
|
+
# Check for "import pum" or "import pgserviceparser"
|
|
28
|
+
if isinstance(node, ast.Import):
|
|
29
|
+
for alias in node.names:
|
|
30
|
+
if alias.name in forbidden_imports or any(
|
|
31
|
+
alias.name.startswith(f"{lib}.") for lib in forbidden_imports
|
|
32
|
+
):
|
|
33
|
+
violations.append(
|
|
34
|
+
f"{py_file.relative_to(oqtopus_dir.parent)}: "
|
|
35
|
+
f"Direct import '{alias.name}' found at line {node.lineno}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Check for "from pum import ..." or "from pgserviceparser import ..."
|
|
39
|
+
elif isinstance(node, ast.ImportFrom):
|
|
40
|
+
if node.module in forbidden_imports or any(
|
|
41
|
+
node.module and node.module.startswith(f"{lib}.")
|
|
42
|
+
for lib in forbidden_imports
|
|
43
|
+
):
|
|
44
|
+
violations.append(
|
|
45
|
+
f"{py_file.relative_to(oqtopus_dir.parent)}: "
|
|
46
|
+
f"Direct import 'from {node.module}' found at line {node.lineno}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
except SyntaxError:
|
|
50
|
+
# Skip files with syntax errors (might be templates or non-Python)
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
if violations:
|
|
54
|
+
error_msg = (
|
|
55
|
+
"Found direct imports of bundled libraries. "
|
|
56
|
+
"Please use 'from ..libs.pum import ...' or 'from ..libs.pgserviceparser import ...' instead:\n"
|
|
57
|
+
+ "\n".join(violations)
|
|
58
|
+
)
|
|
59
|
+
raise AssertionError(error_msg)
|
oqtopus-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
oqtopus/__init__.py,sha256=PUl5ObSgoyF6tcNphBip9Rff8lul0t9VobiwsLKwgFA,104
|
|
2
|
-
oqtopus/oqtopus.py,sha256=BF_JSF2oOCzc8Qr7Z0B7FJdYrdAR3FR9L7oXh3QatL4,1860
|
|
3
|
-
oqtopus/oqtopus_plugin.py,sha256=WJQrEcH1l08Yhmvk4POv9Coh27q4k5i5-vKOTPwCs3k,6116
|
|
4
|
-
oqtopus/core/module.py,sha256=47iDvEOXFNWY77cbi7LhQs5qTUwRNuauieqNTPf9UKY,4766
|
|
5
|
-
oqtopus/core/module_asset.py,sha256=qr_wwriJ_rZd86EuBo_Rx-MDCDJNYA6odhk6F-Z6YFA,467
|
|
6
|
-
oqtopus/core/module_package.py,sha256=sYCnKMOMW4R9ULP3oresDNMAaFEb8y_pLa4Lvjdl7RI,3958
|
|
7
|
-
oqtopus/core/modules_config.py,sha256=iEN_JYuFuGpF4dyfnmcJPfp_bIzrn_PNbq9G1DElyCg,186
|
|
8
|
-
oqtopus/core/package_prepare_task.py,sha256=8Oe876v1XDJSJgtO--f087nMRkFSIay14Eb1pjyy-Xg,5759
|
|
9
|
-
oqtopus/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
oqtopus/gui/about_dialog.py,sha256=dDNwHxKf_M82Z-TKmXW1fXmnI_WSV_7MShqz05trzRw,2345
|
|
11
|
-
oqtopus/gui/database_connection_widget.py,sha256=_SM9K-2dUmXMgjJzqdWrfutRnU4I0WDWZ2FM1dkIa6A,6688
|
|
12
|
-
oqtopus/gui/database_create_dialog.py,sha256=Z7XK47EVs3S1GKWEGOZDE5a4cEWUgW4NcPzAD0JqyEg,8880
|
|
13
|
-
oqtopus/gui/database_duplicate_dialog.py,sha256=Scc1eo8hmniAIyDDK_Qks1RgZn6MsDbtIokg9woEldA,4944
|
|
14
|
-
oqtopus/gui/logs_widget.py,sha256=lxP0rZYUF4ahR7RtM3SJCbk5nk70SVC9Ien6TvBsflY,6698
|
|
15
|
-
oqtopus/gui/main_dialog.py,sha256=OX63uivnOuc9hlv1KAxjkh1F7Yxu_NCjlfcg4569BBU,6874
|
|
16
|
-
oqtopus/gui/module_selection_widget.py,sha256=kjU2cQmK9lYcuKnZwsbRbqrG6dR43hpQkc9S16TT0vE,16180
|
|
17
|
-
oqtopus/gui/module_widget.py,sha256=ckdhXYfUrvOy2mbYGUTYOrDIDplWypmqJ7cp-Pyf1qQ,8529
|
|
18
|
-
oqtopus/gui/parameters_groupbox.py,sha256=ZXoIbTDrpoe_SfxyYr7mNbQOi0vHAJSW8cPIXzGKdmE,2938
|
|
19
|
-
oqtopus/gui/plugin_widget.py,sha256=8av8KI3p8gnXOlSssB5-dFVvq4vaLfqGjY9NVSNiBb0,5757
|
|
20
|
-
oqtopus/gui/project_widget.py,sha256=FhWvMBtQ6kw7NJ4pdCzNs48wMrA5ihjvcbplE4RMWbE,7281
|
|
21
|
-
oqtopus/gui/settings_dialog.py,sha256=-iHT86Ltm1Gsk30YmDL4U0C9yK8LFqt15obZOBdKb28,1547
|
|
22
|
-
oqtopus/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
oqtopus/ui/about_dialog.ui,sha256=tAc1F5NYOsM3rMwYBes1Id0avaLeFo0uSOMXRUK89UM,6244
|
|
24
|
-
oqtopus/ui/database_connection_widget.ui,sha256=0hLBaOx0KcLTH6RlkziVj-tsczjHDUGC-3EXQYgcTuE,3729
|
|
25
|
-
oqtopus/ui/database_create_dialog.ui,sha256=c-fZJRH4tmAeOr6O6M2sPIhzzYQMrSc1DI6a7at-UjI,9230
|
|
26
|
-
oqtopus/ui/database_duplicate_dialog.ui,sha256=EzMRYhlxsiP_sr9ixBJn4lnefM5R_p1oXx4RCyMAj9U,3826
|
|
27
|
-
oqtopus/ui/logs_widget.ui,sha256=oAgEtRUBXB-DzLC0vmBP-PEA-Uu3d3ppFm0ahs1jHQI,2785
|
|
28
|
-
oqtopus/ui/main_dialog.ui,sha256=A1U97A6Cl9h3GHtI4U6ZNI7KXpMEJM6aBKheuAshzto,2650
|
|
29
|
-
oqtopus/ui/module_selection_widget.ui,sha256=IWfoWT5vHoayQPoOPIHOJlg4JdNmVX8DHFQnTIv-Hdk,5646
|
|
30
|
-
oqtopus/ui/module_widget.ui,sha256=2sm0J-qb4CRiKaGk5lyg6AMWlq0d2hE0IW5OSYmJQZo,5662
|
|
31
|
-
oqtopus/ui/plugin_widget.ui,sha256=osJ_QwzmXahV5fEHk4WfUjBBQ7jdsrL4pixiMF2jURA,2570
|
|
32
|
-
oqtopus/ui/project_widget.ui,sha256=5YqSjHR8mQFmHzbSB2M6JnNfrpkqhlRC0N9MSlgcXY8,1600
|
|
33
|
-
oqtopus/ui/settings_dialog.ui,sha256=3PqxTMo22Ola1zl6XkwhXCi6G-uum6g_1-nbjSXWilI,2216
|
|
34
|
-
oqtopus/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
oqtopus/utils/plugin_utils.py,sha256=uGuhiiVC0qfNYH5vN-XE8poB22aNZyZeHf2g515uUrI,5950
|
|
36
|
-
oqtopus/utils/qt_utils.py,sha256=HPe9tOQQH9R9xZp4rGphVhSJO7220q368xmreDu5-6g,3243
|
|
37
|
-
oqtopus/utils/tmmtlogging.py,sha256=BSAPrhQGCuH2boMG4bP8QFyLfoss1uR4pO_pXjTA1eQ,1733
|
|
38
|
-
oqtopus/utils/translation.py,sha256=p1d5N6CYIf94fGYdPQnJamdum9MVBlPkJ24c557rqdg,2647
|
|
39
|
-
oqtopus-0.2.0.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
40
|
-
tests/__init__.py,sha256=z5CxS92efenLcDm64Sifx26EjZBCWPO4oVvaEEHJJ6w,444
|
|
41
|
-
tests/test_plugin_load.py,sha256=HuVLdrsn2PSjKIrvcLj0EH7vNE8-0jBMmHZHihsqB3Q,517
|
|
42
|
-
oqtopus-0.2.0.dist-info/METADATA,sha256=c8kCTBzJGtLT5YHnWFttiXzce372ORfDOsYQjOuxbsw,21787
|
|
43
|
-
oqtopus-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
44
|
-
oqtopus-0.2.0.dist-info/top_level.txt,sha256=i4DHi21kcGIzrF8DlgsKj-UCENHg_NebTRac7cwt32A,14
|
|
45
|
-
oqtopus-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|