Addon Manager: Use Vermin to detect min Python
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
@@ -33,6 +34,7 @@ from PySide2.QtWidgets import (
|
||||
QListWidgetItem,
|
||||
QDialog,
|
||||
QSizePolicy,
|
||||
QMessageBox,
|
||||
)
|
||||
from PySide2.QtGui import (
|
||||
QIcon,
|
||||
@@ -46,6 +48,7 @@ from addonmanager_devmode_validators import NameValidator, VersionValidator
|
||||
from addonmanager_devmode_predictor import Predictor
|
||||
from addonmanager_devmode_people_table import PeopleTable
|
||||
from addonmanager_devmode_licenses_table import LicensesTable
|
||||
import addonmanager_utilities as utils
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
@@ -66,7 +69,9 @@ class AddonGitInterface:
|
||||
try:
|
||||
AddonGitInterface.git_manager = GitManager()
|
||||
except NoGitFound:
|
||||
FreeCAD.Console.PrintLog("No git found, Addon Manager Developer Mode disabled.")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"No git found, Addon Manager Developer Mode disabled."
|
||||
)
|
||||
return
|
||||
|
||||
self.path = path
|
||||
@@ -96,14 +101,15 @@ class AddonGitInterface:
|
||||
return AddonGitInterface.git_manager.get_last_authors(self.path, 10)
|
||||
return []
|
||||
|
||||
#pylint: disable=too-many-instance-attributes
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
|
||||
class DeveloperMode:
|
||||
"""The main Developer Mode dialog, for editing package.xml metadata graphically."""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
|
||||
# In the UI we want to show a translated string for the person type, but the underlying
|
||||
# string must be the one expected by the metadata parser, in English
|
||||
self.person_type_translation = {
|
||||
@@ -137,6 +143,7 @@ class DeveloperMode:
|
||||
|
||||
self.dialog.displayNameLineEdit.setValidator(NameValidator())
|
||||
self.dialog.versionLineEdit.setValidator(VersionValidator())
|
||||
self.dialog.minPythonLineEdit.setValidator(VersionValidator())
|
||||
|
||||
self.dialog.addContentItemToolButton.setIcon(
|
||||
QIcon.fromTheme("add", QIcon(":/icons/list-add.svg"))
|
||||
@@ -210,6 +217,7 @@ class DeveloperMode:
|
||||
self.dialog.displayNameLineEdit.setText(self.metadata.Name)
|
||||
self.dialog.descriptionTextEdit.setPlainText(self.metadata.Description)
|
||||
self.dialog.versionLineEdit.setText(self.metadata.Version)
|
||||
self.dialog.minPythonLineEdit.setText(self.metadata.PythonMin)
|
||||
|
||||
self._populate_urls_from_metadata(self.metadata)
|
||||
self._populate_contents_from_metadata(self.metadata)
|
||||
@@ -342,6 +350,7 @@ class DeveloperMode:
|
||||
self.dialog.readmeURLLineEdit.clear()
|
||||
self.dialog.documentationURLLineEdit.clear()
|
||||
self.dialog.discussionURLLineEdit.clear()
|
||||
self.dialog.minPythonLineEdit.clear()
|
||||
self.dialog.iconDisplayLabel.setPixmap(QPixmap())
|
||||
self.dialog.iconPathLineEdit.clear()
|
||||
|
||||
@@ -354,6 +363,9 @@ class DeveloperMode:
|
||||
self.dialog.pathToAddonComboBox.editTextChanged.connect(
|
||||
self._addon_combo_text_changed
|
||||
)
|
||||
self.dialog.detectMinPythonButton.clicked.connect(
|
||||
self._detect_min_python_clicked
|
||||
)
|
||||
self.dialog.iconBrowseButton.clicked.connect(self._browse_for_icon_clicked)
|
||||
|
||||
self.dialog.addContentItemToolButton.clicked.connect(self._add_content_clicked)
|
||||
@@ -427,6 +439,11 @@ class DeveloperMode:
|
||||
)
|
||||
self.metadata.Urls = urls
|
||||
|
||||
if self.dialog.minPythonLineEdit.text():
|
||||
self.metadata.PythonMin = self.dialog.minPythonLineEdit.text()
|
||||
else:
|
||||
self.metadata.PythonMin = "0.0.0" # Code for "unset"
|
||||
|
||||
# Content, people, and licenses should already be sync'ed
|
||||
|
||||
###############################################################################################
|
||||
@@ -565,6 +582,135 @@ class DeveloperMode:
|
||||
version_string = f"{year}.{month:>02}.{day:>02}"
|
||||
self.dialog.versionLineEdit.setText(version_string)
|
||||
|
||||
def _detect_min_python_clicked(self):
|
||||
if not self._ensure_vermin_loaded():
|
||||
FreeCAD.Console.PrintWarning(
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"No Vermin, cancelling operation.\n",
|
||||
"'Vermin' is a Python package - do not translate",
|
||||
)
|
||||
)
|
||||
return
|
||||
FreeCAD.Console.PrintMessage(
|
||||
translate(
|
||||
"AddonsInstaller", "Scanning Addon for Python version compatibility"
|
||||
)
|
||||
+ "...\n"
|
||||
)
|
||||
#pylint: disable=import-outside-toplevel
|
||||
import vermin
|
||||
|
||||
required_minor_version = 0
|
||||
for dirpath, _, filenames in os.walk(self.current_mod):
|
||||
for filename in filenames:
|
||||
if filename.endswith(".py"):
|
||||
|
||||
with open(
|
||||
os.path.join(dirpath, filename), "r", encoding="utf-8"
|
||||
) as f:
|
||||
contents = f.read()
|
||||
version_strings = vermin.version_strings(
|
||||
vermin.detect(contents)
|
||||
)
|
||||
version = version_strings.split(",")
|
||||
if len(version) >= 2:
|
||||
# Only care about Py3, and only if there is a dot in the version:
|
||||
if "." in version[1]:
|
||||
py3 = version[1].split(".")
|
||||
major = int(py3[0].strip())
|
||||
minor = int(py3[1].strip())
|
||||
if major == 3:
|
||||
FreeCAD.Console.PrintLog(
|
||||
f"Detected Python 3.{minor} required by {filename}\n"
|
||||
)
|
||||
required_minor_version = max(
|
||||
required_minor_version, minor
|
||||
)
|
||||
self.dialog.minPythonLineEdit.setText(f"3.{required_minor_version}")
|
||||
QMessageBox.information(
|
||||
self.dialog,
|
||||
translate("AddonsInstaller", "Minimum Python Version Detected"),
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"Vermin auto-detected a required version of Python 3.{}",
|
||||
).format(required_minor_version),
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
|
||||
def _ensure_vermin_loaded(self) -> bool:
|
||||
try:
|
||||
#pylint: disable=import-outside-toplevel,unused-import
|
||||
import vermin
|
||||
except ImportError:
|
||||
#pylint: disable=line-too-long
|
||||
response = QMessageBox.question(
|
||||
self.dialog,
|
||||
translate("AddonsInstaller", "Install Vermin?"),
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"Autodetecting the required version of Python for this Addon requires Vermin (https://pypi.org/project/vermin/). OK to install?",
|
||||
),
|
||||
QMessageBox.Yes | QMessageBox.Cancel,
|
||||
)
|
||||
if response == QMessageBox.Cancel:
|
||||
return False
|
||||
FreeCAD.Console.PrintMessage(
|
||||
translate("AddonsInstaller", "Attempting to install Vermin from PyPi")
|
||||
+ "...\n"
|
||||
)
|
||||
python_exe = utils.get_python_exe()
|
||||
vendor_path = os.path.join(
|
||||
FreeCAD.getUserAppDataDir(), "AdditionalPythonPackages"
|
||||
)
|
||||
if not os.path.exists(vendor_path):
|
||||
os.makedirs(vendor_path)
|
||||
|
||||
proc = subprocess.run(
|
||||
[
|
||||
python_exe,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--disable-pip-version-check",
|
||||
"--target",
|
||||
vendor_path,
|
||||
"vermin",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(proc.stdout.decode())
|
||||
if proc.returncode != 0:
|
||||
response = QMessageBox.critical(
|
||||
self.dialog,
|
||||
translate("AddonsInstaller", "Installation failed"),
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"Failed to install Vermin -- check Report View for details.",
|
||||
"'Vermin' is the name of a Python package, do not translate",
|
||||
),
|
||||
QMessageBox.Cancel,
|
||||
)
|
||||
return False
|
||||
try:
|
||||
#pylint: disable=import-outside-toplevel
|
||||
import vermin
|
||||
except ImportError:
|
||||
response = QMessageBox.critical(
|
||||
self.dialog,
|
||||
translate("AddonsInstaller", "Installation failed"),
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"Failed to import vermin after installation -- cannot scan Addon.",
|
||||
"'vermin' is the name of a Python package, do not translate",
|
||||
),
|
||||
QMessageBox.Cancel,
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _browse_for_icon_clicked(self):
|
||||
"""Callback: when the "Browse..." button for the icon field is clicked"""
|
||||
new_icon_path, _ = QFileDialog.getOpenFileName(
|
||||
|
||||
@@ -339,6 +339,7 @@ def get_python_exe() -> str:
|
||||
if not python_exe or not os.path.exists(python_exe):
|
||||
return ""
|
||||
|
||||
python_exe = python_exe.replace("/",os.path.sep)
|
||||
prefs.SetString("PythonExecutableForPip", python_exe)
|
||||
return python_exe
|
||||
|
||||
|
||||
@@ -54,95 +54,6 @@
|
||||
<string>Metadata</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="labelIcon">
|
||||
<property name="text">
|
||||
<string>Icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="labelReadmeURL">
|
||||
<property name="text">
|
||||
<string>README URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="displayNameLineEdit">
|
||||
<property name="toolTip">
|
||||
<string>Displayed in the Addon Manager's list of Addons. Should not include the word "FreeCAD", and must be a valid directory name on all support operating systems.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPlainTextEdit" name="descriptionTextEdit">
|
||||
<property name="toolTip">
|
||||
<string>Explanation of what this Addon provides. Displayed in the Addon Manager. It is not necessary for this to state that this is a FreeCAD Addon.</string>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>TIP: Since this is displayed within FreeCAD, in the Addon Manager, it is not necessary to take up space saying things like "This is a FreeCAD Addon..." -- just say what it does.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="readmeURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Recommended)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLineEdit" name="websiteURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLineEdit" name="bugtrackerURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="peopleAndLicenseshorizontalLayout"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelDisplayName">
|
||||
<property name="toolTip">
|
||||
<string>Displayed in the Addon Manager's list of Addons. Should not include the word "FreeCAD", and must be a valid directory name on all support operating systems.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Addon Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelVersion">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="labelDocumentationURL">
|
||||
<property name="text">
|
||||
<string>Documentation URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="labelRepoURL">
|
||||
<property name="text">
|
||||
<string>Repository URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
@@ -160,13 +71,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="labelDiscssionURL">
|
||||
<property name="text">
|
||||
<string>Discussion URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelDescription">
|
||||
<property name="toolTip">
|
||||
@@ -180,17 +84,17 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLineEdit" name="documentationURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="labelDiscssionURL">
|
||||
<property name="text">
|
||||
<string>Discussion URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="labelWebsiteURL">
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="labelIcon">
|
||||
<property name="text">
|
||||
<string>Website URL</string>
|
||||
<string>Icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -219,7 +123,65 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="peopleAndLicenseshorizontalLayout"/>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QLineEdit" name="discussionURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="displayNameLineEdit">
|
||||
<property name="toolTip">
|
||||
<string>Displayed in the Addon Manager's list of Addons. Should not include the word "FreeCAD", and must be a valid directory name on all support operating systems.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLineEdit" name="documentationURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="labelReadmeURL">
|
||||
<property name="text">
|
||||
<string>README URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPlainTextEdit" name="descriptionTextEdit">
|
||||
<property name="toolTip">
|
||||
<string>Explanation of what this Addon provides. Displayed in the Addon Manager. It is not necessary for this to state that this is a FreeCAD Addon.</string>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>TIP: Since this is displayed within FreeCAD, in the Addon Manager, it is not necessary to take up space saying things like "This is a FreeCAD Addon..." -- just say what it does.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="labelRepoURL">
|
||||
<property name="text">
|
||||
<string>Repository URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLineEdit" name="websiteURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<layout class="QHBoxLayout" name="iconHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="iconDisplayLabel"/>
|
||||
@@ -236,13 +198,76 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QLineEdit" name="discussionURLLineEdit">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="labelWebsiteURL">
|
||||
<property name="text">
|
||||
<string>Website URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="labelDocumentationURL">
|
||||
<property name="text">
|
||||
<string>Documentation URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLineEdit" name="bugtrackerURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelDisplayName">
|
||||
<property name="toolTip">
|
||||
<string>Displayed in the Addon Manager's list of Addons. Should not include the word "FreeCAD", and must be a valid directory name on all support operating systems.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Addon Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelVersion">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="readmeURLLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Recommended)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Minimum Python</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="minPythonLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>(Optional, only 3.x version supported)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="detectMinPythonButton">
|
||||
<property name="text">
|
||||
<string>Detect...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
Reference in New Issue
Block a user