Sun, 03 Jul 2022 13:52:59 +0200
Refined the setup wizard a little bit.
# -*- coding: utf-8 -*- # Copyright (c) 2013 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the setup.py wizard dialog. """ import configparser import datetime import io import os import pathlib import tomlkit import trove_classifiers from PyQt6.QtCore import pyqtSlot, Qt from PyQt6.QtWidgets import ( QDialog, QDialogButtonBox, QTreeWidgetItem, QListWidgetItem ) from EricWidgets.EricApplication import ericApp from EricWidgets import EricFileDialog from EricWidgets.EricPathPicker import EricPathPickerModes from .AddEntryPointDialog import AddEntryPointDialog from .AddProjectUrlDialog import AddProjectUrlDialog from .Ui_SetupWizardDialog import Ui_SetupWizardDialog import Utilities import Preferences class SetupWizardDialog(QDialog, Ui_SetupWizardDialog): """ Class implementing the setup.py wizard dialog. It displays a dialog for entering the parameters for the setup.py code generator. """ def __init__(self, category, editor, parent=None): """ Constructor @param category category of setup file to create @type str @param editor reference to the editor object to receive the code @type Editor @param parent reference to the parent widget (defaults to None) @type QWidget (optional) @exception ValueError raised for an illegal setup file category """ if category not in ("setup.py", "setup.cfg", "pyproject.toml"): raise ValueError("illegal setup file category given") super().__init__(parent) self.setupUi(self) self.setWindowTitle(self.tr("{0} Wizard").format(category)) self.__replies = [] self.__category = category self.__editor = editor if category != "setup.py": self.introCheckBox.setVisible(False) self.importCheckBox.setVisible(False) self.metaDataCheckBox.setVisible(False) self.dataTabWidget.setCurrentIndex(0) self.packageRootPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) self.sourceDirectoryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) self.__mandatoryStyleSheet = ( "QLineEdit {border: 2px solid; border-color: #dd8888}" if ericApp().usesDarkPalette() else "QLineEdit {border: 2px solid; border-color: #800000}" ) for lineEdit in [self.nameEdit, self.versionEdit, self.homePageUrlEdit, self.authorEdit, self.authorEmailEdit, self.maintainerEdit, self.maintainerEmailEdit]: lineEdit.setStyleSheet(self.__mandatoryStyleSheet) self.__populateClassifiers() self.__okButton = self.buttonBox.button( QDialogButtonBox.StandardButton.Ok) self.__okButton.setEnabled(False) projectOpen = ericApp().getObject("Project").isOpen() self.projectButton.setEnabled(projectOpen) self.projectUrlsList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) self.entryPointsList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) self.descriptionContentTypeComboBox.addItem("", "") for contentType, mimetype in sorted([ (self.tr("Plain Text"), "text/plain"), (self.tr("Markdown"), "text/markdown"), (self.tr("reStructuredText"), "text/x-rst") ]): self.descriptionContentTypeComboBox.addItem(contentType, mimetype) self.homePageUrlEdit.textChanged.connect(self.__enableOkButton) self.nameEdit.textChanged.connect(self.__enableOkButton) self.versionEdit.textChanged.connect(self.__enableOkButton) self.authorEdit.textChanged.connect(self.__enableOkButton) self.authorEmailEdit.textChanged.connect(self.__enableOkButton) self.maintainerEdit.textChanged.connect(self.__enableOkButton) self.maintainerEmailEdit.textChanged.connect(self.__enableOkButton) def __enableOkButton(self): """ Private slot to set the state of the OK button. """ enable = ( bool(self.nameEdit.text()) and bool(self.versionEdit.text()) and bool(self.homePageUrlEdit.text()) and ((bool(self.authorEdit.text()) and bool(self.authorEmailEdit.text())) or (bool(self.maintainerEdit.text()) and bool(self.maintainerEmailEdit.text()))) and self.homePageUrlEdit.text().startswith(("http://", "https://")) ) self.__okButton.setEnabled(enable) def __populateClassifiers(self): """ Private method to populate the classifiers. """ self.licenseClassifierComboBox.clear() self.classifiersList.clear() self.developmentStatusComboBox.clear() self.developmentStatusComboBox.addItem("", "") self.__classifiersDict = {} for classifier in trove_classifiers.sorted_classifiers: if classifier.startswith("License ::"): self.licenseClassifierComboBox.addItem( "/".join(classifier.split(" :: ")[1:]), classifier ) elif classifier.startswith("Development Status ::"): self.developmentStatusComboBox.addItem( classifier.split(" :: ")[1], classifier) else: self.__addClassifierEntry(classifier) self.__classifiersDict = {} self.licenseClassifierComboBox.setCurrentIndex( self.licenseClassifierComboBox.findText( "(GPLv3)", Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchCaseSensitive ) ) def __addClassifierEntry(self, classifier): """ Private method to add a new entry to the list of trove classifiers. @param classifier classifier containing the data for the entry @type str """ itm = None pitm = None dataList = classifier.split(" :: ") for index in range(len(dataList)): key = " :: ".join(dataList[:index + 1]) if key not in self.__classifiersDict: if pitm is None: itm = QTreeWidgetItem( self.classifiersList, [dataList[index]]) pitm = itm else: itm = QTreeWidgetItem(pitm, [dataList[index]]) itm.setExpanded(True) self.__classifiersDict[key] = itm else: pitm = self.__classifiersDict[key] itm.setCheckState(0, Qt.CheckState.Unchecked) itm.setData(0, Qt.ItemDataRole.UserRole, classifier) def __getLicenseText(self): """ Private method to get the license text. @return license text @rtype str """ if not self.licenseClassifierCheckBox.isChecked(): return self.licenseEdit.text() else: lic = self.licenseClassifierComboBox.currentText() if "(" in lic: lic = lic.rsplit("(", 1)[1].split(")", 1)[0] return lic def __getSetupPyCode(self, indLevel, indString): """ Private method to get the source code for a 'setup.py' file. @param indLevel indentation level @type int @param indString string used for indentation (space or tab) @type str @return generated code @rtype str """ # Note: all paths are created with '/'; setup will do the right thing # calculate our indentation level and the indentation string il = indLevel + 1 istring = il * indString i1string = (il + 1) * indString i2string = (il + 2) * indString estring = os.linesep + indLevel * indString # now generate the code if self.introCheckBox.isChecked(): sourceCode = "#!/usr/bin/env python3{0}".format(os.linesep) sourceCode += "# -*- coding: utf-8 -*-{0}{0}".format(os.linesep) else: sourceCode = "" if self.metaDataCheckBox.isChecked(): sourceCode += '# metadata{0}'.format(os.linesep) sourceCode += '"{0}"{1}'.format( self.summaryEdit.text() or "Setup routine", os.linesep ) sourceCode += '__version__ = "{0}"{1}'.format( self.versionEdit.text(), os.linesep) sourceCode += '__license__ = "{0}"{1}'.format( self.__getLicenseText(), os.linesep) sourceCode += '__author__ = "{0}"{1}'.format( self.authorEdit.text() or self.maintainerEdit.text(), os.linesep) sourceCode += '__email__ = "{0}"{1}'.format( self.authorEmailEdit.text() or self.maintainerEmailEdit.text(), os.linesep) sourceCode += '__url__ = "{0}"{1}'.format( self.homePageUrlEdit.text(), os.linesep) sourceCode += '__date__ = "{0}"{1}'.format( datetime.datetime.now().isoformat().split('.')[0], os.linesep) sourceCode += '__prj__ = "{0}"{1}'.format( self.nameEdit.text(), os.linesep) sourceCode += os.linesep if self.importCheckBox.isChecked(): additionalImport = ", find_packages" sourceCode += "from setuptools import setup{0}{1}".format( additionalImport, os.linesep) if sourceCode: sourceCode += "{0}{0}".format(os.linesep) if self.descriptionFromFilesCheckBox.isChecked(): sourceCode += 'def get_long_description():{0}'.format(os.linesep) sourceCode += '{0}descr = []{1}'.format(istring, os.linesep) sourceCode += '{0}for fname in ("{1}"):{2}'.format( istring, '", "'.join(self.descriptionEdit.toPlainText().splitlines()), os.linesep) sourceCode += ( '{0}with open(fname, "r", encoding="utf-8") as f:{1}' ).format(i1string, os.linesep) sourceCode += '{0}descr.append(f.read()){1}'.format( i2string, os.linesep) sourceCode += '{0}return "\\n\\n".join(descr){1}'.format( istring, os.linesep) sourceCode += "{0}{0}".format(os.linesep) sourceCode += 'setup({0}'.format(os.linesep) sourceCode += '{0}name="{1}",{2}'.format( istring, self.nameEdit.text(), os.linesep) sourceCode += '{0}version="{1}",{2}'.format( istring, self.versionEdit.text(), os.linesep) if self.summaryEdit.text(): sourceCode += '{0}description="{1}",{2}'.format( istring, self.summaryEdit.text(), os.linesep) if self.descriptionFromFilesCheckBox.isChecked(): sourceCode += '{0}long_description=get_long_description(),{1}'.format( istring, os.linesep) elif self.descriptionEdit.toPlainText(): sourceCode += '{0}long_description="""{1}""",{2}'.format( istring, self.descriptionEdit.toPlainText(), os.linesep) if self.descriptionContentTypeComboBox.currentData(): sourceCode += '{0}long_description_content_type="{1}",{2}'.format( istring, self.descriptionContentTypeComboBox.currentData(), os.linesep) if self.authorEdit.text(): sourceCode += '{0}author="{1}",{2}'.format( istring, self.authorEdit.text(), os.linesep) sourceCode += '{0}author_email="{1}",{2}'.format( istring, self.authorEmailEdit.text(), os.linesep) if self.maintainerEdit.text(): sourceCode += '{0}maintainer="{1}",{2}'.format( istring, self.maintainerEdit.text(), os.linesep) sourceCode += '{0}maintainer_email="{1}",{2}'.format( istring, self.maintainerEmailEdit.text(), os.linesep) sourceCode += '{0}url="{1}",{2}'.format( istring, self.homePageUrlEdit.text(), os.linesep) if self.downloadUrlEdit.text(): sourceCode += '{0}download_url="{1}",{2}'.format( istring, self.downloadUrlEdit.text(), os.linesep) if self.projectUrlsList.topLevelItemCount(): sourceCode += '{0}project_urls={{{1}'.format(istring, os.linesep) for row in range(self.projectUrlsList.topLevelItemCount()): urlItem = self.projectUrlsList.topLevelItem(row) sourceCode += '{0}"{1}": "{2}",{3}'.format( i1string, urlItem.text(0), urlItem.text(1), os.linesep) sourceCode += '{0}}},{1}'.format(istring, os.linesep) classifiers = [] if not self.licenseClassifierCheckBox.isChecked(): sourceCode += '{0}license="{1}",{2}'.format( istring, self.licenseEdit.text(), os.linesep) else: classifiers.append( self.licenseClassifierComboBox.itemData( self.licenseClassifierComboBox.currentIndex())) platforms = self.platformsEdit.toPlainText().splitlines() if platforms: sourceCode += '{0}platforms=[{1}'.format(istring, os.linesep) sourceCode += '{0}"{1}"{2}'.format( i1string, '",{0}{1}"'.format(os.linesep, i1string).join(platforms), os.linesep) sourceCode += '{0}],{1}'.format(istring, os.linesep) if self.developmentStatusComboBox.currentIndex() != 0: classifiers.append(self.developmentStatusComboBox.currentData()) itm = self.classifiersList.topLevelItem(0) while itm: itm.setExpanded(True) if itm.checkState(0) == Qt.CheckState.Checked: classifiers.append(itm.data(0, Qt.ItemDataRole.UserRole)) itm = self.classifiersList.itemBelow(itm) # cleanup classifiers list - remove all invalid entries classifiers = [c for c in classifiers if bool(c)] if classifiers: sourceCode += '{0}classifiers=[{1}'.format(istring, os.linesep) sourceCode += '{0}"{1}"{2}'.format( i1string, '",{0}{1}"'.format(os.linesep, i1string).join(classifiers), os.linesep) sourceCode += '{0}],{1}'.format(istring, os.linesep) del classifiers if self.keywordsEdit.text(): sourceCode += '{0}keywords="{1}",{2}'.format( istring, self.keywordsEdit.text(), os.linesep) if self.pyVersionEdit.text(): sourceCode += '{0}python_requires="{1}",{2}'.format( istring, self.pyVersionEdit.text(), os.linesep) sourceCode += '{0}packages=find_packages('.format(istring) src = Utilities.fromNativeSeparators( self.sourceDirectoryPicker.text()) excludePatterns = [] for row in range(self.excludePatternList.count()): excludePatterns.append( self.excludePatternList.item(row).text()) if src: sourceCode += '{0}{1}"{2}"'.format(os.linesep, i1string, src) if excludePatterns: sourceCode += ',' else: sourceCode += '{0}{1}'.format(os.linesep, istring) if excludePatterns: sourceCode += '{0}{1}exclude=[{0}'.format(os.linesep, i1string) sourceCode += '{0}"{1}"{2}'.format( i2string, '",{0}{1}"'.format(os.linesep, i2string) .join(excludePatterns), os.linesep) sourceCode += '{0}]{1}{2}'.format(i1string, os.linesep, istring) sourceCode += '),{0}'.format(os.linesep) if self.includePackageDataCheckBox.isChecked(): sourceCode += '{0}include_package_data = True,{1}'.format( istring, os.linesep) modules = [] for row in range(self.modulesList.count()): modules.append(self.modulesList.item(row).text()) if modules: sourceCode += '{0}py_modules=[{1}'.format(istring, os.linesep) sourceCode += '{0}"{1}"{2}'.format( i1string, '",{0}{1}"'.format(os.linesep, i1string).join(modules), os.linesep) sourceCode += '{0}],{1}'.format(istring, os.linesep) del modules if self.entryPointsList.topLevelItemCount(): entryPoints = { "console_scripts": [], "gui_scripts": [], } for row in range(self.entryPointsList.topLevelItemCount()): itm = self.entryPointsList.topLevelItem(row) entryPoints[itm.data(0, Qt.ItemDataRole.UserRole)].append( "{0} = {1}".format(itm.text(1), itm.text(2)) ) sourceCode += '{0}entry_points={{{1}'.format(istring, os.linesep) for epCategory in entryPoints: if entryPoints[epCategory]: sourceCode += '{0}"{1}": [{2}'.format( i1string, epCategory, os.linesep) for entryPoint in entryPoints[epCategory]: sourceCode += '{0}"{1}",{2}'.format( i2string, entryPoint, os.linesep) sourceCode += '{0}],{1}'.format(i1string, os.linesep) sourceCode += '{0}}},{1}'.format(istring, os.linesep) sourceCode += "){0}".format(estring) return sourceCode def __getSetupCfgCode(self): """ Private method to get the source code for a 'setup.cfg' file. @return generated code @rtype str """ from . import SetupCfgUtilities metadata = { "name": self.nameEdit.text(), "version": self.versionEdit.text(), } if self.summaryEdit.text(): metadata["description"] = self.summaryEdit.text() if self.descriptionEdit.toPlainText(): metadata["long_description"] = ( "file: {0}".format( ", ".join(self.descriptionEdit.toPlainText().splitlines()) ) if self.descriptionFromFilesCheckBox.isChecked() else self.descriptionEdit.toPlainText() ) if self.descriptionContentTypeComboBox.currentData(): metadata["long_description_content_type"] = ( self.descriptionContentTypeComboBox.currentData() ) if self.authorEdit.text(): metadata["author"] = self.authorEdit.text() metadata["author_email"] = self.authorEmailEdit.text() if self.maintainerEdit.text(): metadata["maintainer"] = self.maintainerEdit.text() metadata["maintainer_email"] = self.maintainerEmailEdit.text() metadata["url"] = self.homePageUrlEdit.text() if self.downloadUrlEdit.text(): metadata["download_url"] = self.downloadUrlEdit.text() if self.projectUrlsList.topLevelItemCount(): projectURLs = {} for row in range(self.projectUrlsList.topLevelItemCount()): urlItem = self.projectUrlsList.topLevelItem(row) projectURLs[urlItem.text(0)] = urlItem.text(1) metadata["project_urls"] = SetupCfgUtilities.toString(projectURLs) classifiers = [] if not self.licenseClassifierCheckBox.isChecked(): metadata["license"] = self.licenseEdit.text() else: classifiers.append( self.licenseClassifierComboBox.itemData( self.licenseClassifierComboBox.currentIndex())) platforms = self.platformsEdit.toPlainText().splitlines() if platforms: metadata["platforms"] = SetupCfgUtilities.toString(platforms) if self.developmentStatusComboBox.currentIndex() != 0: classifiers.append(self.developmentStatusComboBox.currentData()) itm = self.classifiersList.topLevelItem(0) while itm: itm.setExpanded(True) if itm.checkState(0) == Qt.CheckState.Checked: classifiers.append(itm.data(0, Qt.ItemDataRole.UserRole)) itm = self.classifiersList.itemBelow(itm) # cleanup classifiers list - remove all invalid entries classifiers = [c for c in classifiers if bool(c)] if classifiers: metadata["classifiers"] = SetupCfgUtilities.toString(classifiers) if self.keywordsEdit.text(): metadata["keywords"] = SetupCfgUtilities.toString( self.keywordsEdit.text().split()) options = { "packages": "find:" } if self.pyVersionEdit.text(): options["python_requires"] = self.pyVersionEdit.text() findOptions = {} src = Utilities.fromNativeSeparators(self.sourceDirectoryPicker.text()) excludePatterns = [] for row in range(self.excludePatternList.count()): excludePatterns.append( self.excludePatternList.item(row).text()) if src: options["package_dir"] = SetupCfgUtilities.toString({"": src}) findOptions["where"] = src if excludePatterns: findOptions["exclude"] = SetupCfgUtilities.toString(excludePatterns) if self.includePackageDataCheckBox.isChecked(): options["include_package_data"] = SetupCfgUtilities.toString(True) packageData = {} # placeholder section else: packageData = None modules = [] for row in range(self.modulesList.count()): modules.append(self.modulesList.item(row).text()) if modules: options["py_modules"] = SetupCfgUtilities.toString(modules) if self.entryPointsList.topLevelItemCount(): entryPoints = { "console_scripts": {}, "gui_scripts": {}, } for row in range(self.entryPointsList.topLevelItemCount()): itm = self.entryPointsList.topLevelItem(row) entryPoints[itm.data(0, Qt.ItemDataRole.UserRole)][ itm.text(1)] = itm.text(2) for epType in list(entryPoints.keys()): if entryPoints[epType]: entryPoints[epType] = SetupCfgUtilities.toString( entryPoints[epType]) else: del entryPoints[epType] else: entryPoints = {} configDict = { "metadata": metadata, "options": options, "options.packages.find": findOptions, } if packageData is not None: configDict["options.package_data"] = packageData if entryPoints: configDict["options.entry_points"] = entryPoints cparser = configparser.ConfigParser() cparser.read_dict(configDict) sio = io.StringIO() cparser.write(sio) sourceCode = sio.getvalue() return sourceCode def __getPyprojectCode(self): """ Private method to get the source code for a 'pyproject.toml' file. @return generated code @rtype str """ doc = tomlkit.document() buildSystem = tomlkit.table() buildSystem["requires"] = ["setuptools>=61.0.0", "wheel"] buildSystem["build-backend"] = "setuptools.build_meta" doc["build-system"] = buildSystem project = tomlkit.table() project["name"] = self.nameEdit.text() project["version"] = self.versionEdit.text() if self.summaryEdit.text(): project["description"] = self.summaryEdit.text() if self.descriptionEdit.toPlainText(): if self.descriptionFromFilesCheckBox.isChecked(): project["readme"] = self.descriptionEdit.toPlainText().splitlines()[0] else: readme = tomlkit.table() readme["text"] = self.descriptionEdit.toPlainText() readme["content-type"] = ( self.descriptionContentTypeComboBox.currentData() ) project["readme"] = readme if self.authorEdit.text(): authors = tomlkit.array() author = tomlkit.inline_table() author["name"] = self.authorEdit.text() author["email"] = self.authorEmailEdit.text() authors.append(author) project["authors"] = authors if self.maintainerEdit.text(): maintainers = tomlkit.array() maintainer = tomlkit.inline_table() maintainer["name"] = self.maintainerEdit.text() maintainer["email"] = self.maintainerEmailEdit.text() maintainers.append(maintainer) project["maintainers"] = maintainers urls = tomlkit.table() urls["Homepage"] = self.homePageUrlEdit.text() if self.downloadUrlEdit.text(): urls["Download"] = self.downloadUrlEdit.text() if self.projectUrlsList.topLevelItemCount(): for row in range(self.projectUrlsList.topLevelItemCount()): urlItem = self.projectUrlsList.topLevelItem(row) urls[urlItem.text(0)] = urlItem.text(1) project["urls"] = urls classifiers = [] if not self.licenseClassifierCheckBox.isChecked(): license = tomlkit.table() license["text"] = self.licenseEdit.text() project["license"] = license else: classifiers.append( self.licenseClassifierComboBox.itemData( self.licenseClassifierComboBox.currentIndex())) if self.developmentStatusComboBox.currentIndex() != 0: classifiers.append(self.developmentStatusComboBox.currentData()) itm = self.classifiersList.topLevelItem(0) while itm: itm.setExpanded(True) if itm.checkState(0) == Qt.CheckState.Checked: classifiers.append(itm.data(0, Qt.ItemDataRole.UserRole)) itm = self.classifiersList.itemBelow(itm) # cleanup classifiers list - remove all invalid entries classifiers = [c for c in classifiers if bool(c)] if classifiers: classifiersArray = tomlkit.array() for classifier in classifiers: classifiersArray.add_line(classifier) classifiersArray.append(tomlkit.nl()) project["classifiers"] = classifiersArray if self.keywordsEdit.text(): keywords = tomlkit.array() for kw in self.keywordsEdit.text().split(): keywords.add_line(kw) keywords.append(tomlkit.nl()) project["keywords"] = keywords if self.pyVersionEdit.text(): project["requires-python"] = self.pyVersionEdit.text() if self.entryPointsList.topLevelItemCount(): entryPoints = { "console_scripts": {}, "gui_scripts": {}, } for row in range(self.entryPointsList.topLevelItemCount()): itm = self.entryPointsList.topLevelItem(row) entryPoints[itm.data(0, Qt.ItemDataRole.UserRole)][ itm.text(1)] = itm.text(2) if entryPoints["console_scripts"]: scripts = tomlkit.table() for name, function in entryPoints["console_scripts"].items(): scripts[name] = function project["scripts"] = scripts if entryPoints["gui_scripts"]: guiScripts = tomlkit.table() for name, function in entryPoints["gui_scripts"].items(): guiScripts[name] = function project["gui-scripts"] = guiScripts # placeholder dependencies = tomlkit.array() dependencies.append(tomlkit.comment( "TODO: enter project dependencies " # __NO-TASK__ )) project["dependencies"] = dependencies doc["project"] = project setuptools = tomlkit.table() platforms = self.platformsEdit.toPlainText().splitlines() if platforms: platformsArray = tomlkit.array() for plt in platforms: platformsArray.add_line(plt) platformsArray.append(tomlkit.nl()) setuptools["platforms"] = platformsArray setuptools["include-package-data"] = self.includePackageDataCheckBox.isChecked() if self.includePackageDataCheckBox.isChecked(): # placeholder setuptools["package-data"] = tomlkit.table() setuptools["package-data"].add(tomlkit.comment( "TODO: enter package data patterns" # __NO-TASK__ )) if self.modulesList.count(): modulesArray = tomlkit.array() for row in range(self.modulesList.count()): modulesArray.add_line(self.modulesList.item(row).text()) modulesArray.append(tomlkit.nl()) setuptools["py-modules"] = modulesArray findspec = tomlkit.table() src = Utilities.fromNativeSeparators(self.sourceDirectoryPicker.text()) excludePatterns = [] for row in range(self.excludePatternList.count()): excludePatterns.append( self.excludePatternList.item(row).text()) if src: findspec["where"] = [ericApp().getObject("Project").getRelativePath(src)] if excludePatterns: excludePatternsArray = tomlkit.array() for pattern in excludePatterns: excludePatternsArray.add_line(pattern) excludePatternsArray.append(tomlkit.nl()) findspec["exclude"] = excludePatternsArray if bool(findspec): setuptools["packages"] = tomlkit.table(is_super_table=True) setuptools["packages"]["find"] = findspec doc["tool"] = tomlkit.table(is_super_table=True) doc["tool"]["setuptools"] = setuptools sourceCode = tomlkit.dumps(doc) return sourceCode @pyqtSlot() def accept(self): """ Public slot to handle pressing the OK button. """ line, index = self.__editor.getCursorPosition() indLevel = self.__editor.indentation(line) // self.__editor.indentationWidth() if self.__editor.indentationsUseTabs(): indString = '\t' else: indString = self.__editor.indentationWidth() * ' ' if self.__category == "setup.py": sourceCode = self.__getSetupPyCode(indLevel, indString) elif self.__category == "setup.cfg": sourceCode = self.__getSetupCfgCode() elif self.__category == "pyproject.toml": sourceCode = self.__getPyprojectCode() else: # should not happen, but play it safe sourceCode = "" if sourceCode: line, index = self.__editor.getCursorPosition() # It should be done this way to allow undo self.__editor.beginUndoAction() self.__editor.insertAt(sourceCode, line, index) self.__editor.endUndoAction() super().accept() @pyqtSlot() def on_projectButton_clicked(self): """ Private slot to populate some fields with data retrieved from the current project. """ project = ericApp().getObject("Project") self.nameEdit.setText(project.getProjectName()) try: self.versionEdit.setText(project.getProjectVersion()) self.authorEdit.setText(project.getProjectAuthor()) self.authorEmailEdit.setText(project.getProjectAuthorEmail()) description = project.getProjectDescription() except AttributeError: self.versionEdit.setText(project.pdata["VERSION"][0]) self.authorEdit.setText(project.pdata["AUTHOR"][0]) self.authorEmailEdit.setText(project.pdata["EMAIL"][0]) description = project.pdata["DESCRIPTION"][0] summary = ( description.split(".", 1)[0].replace("\r", "").replace("\n", "") + "." ) self.summaryEdit.setText(summary) self.descriptionEdit.setPlainText(description) self.packageRootPicker.setText(project.getProjectPath()) # prevent overwriting of entries by disabling the button self.projectButton.setEnabled(False) def __getStartDir(self): """ Private method to get the start directory for selection dialogs. @return start directory @rtype str """ return (Preferences.getMultiProject("Workspace") or Utilities.getHomeDir()) @pyqtSlot() def on_entryPointsList_itemSelectionChanged(self): """ Private slot to handle a change of selected items of the entry points list. """ self.deleteEntryPointButton.setEnabled( bool(self.entryPointsList.selectedItems())) self.editEntryPointButton.setEnabled( len(self.entryPointsList.selectedItems()) == 1) @pyqtSlot() def on_deleteEntryPointButton_clicked(self): """ Private slot to delete the selected entry point items. """ for itm in self.entryPointsList.selectedItems(): self.entryPointsList.takeTopLevelItem(self.entryPointsList.row(itm)) del itm @pyqtSlot() def on_addEntryPointButton_clicked(self): """ Private slot to add an entry point to the list. """ project = ericApp().getObject("Project") rootDir = ( project.getProjectPath() if project.isOpen() else "" ) dlg = AddEntryPointDialog(rootDir, parent=self) if dlg.exec() == QDialog.DialogCode.Accepted: epType, epCategory, name, script = dlg.getEntryPoint() itm = QTreeWidgetItem(self.entryPointsList, [epType, name, script]) itm.setData(0, Qt.ItemDataRole.UserRole, epCategory) @pyqtSlot() def on_editEntryPointButton_clicked(self): """ Private slot to edit the selected entry point. """ project = ericApp().getObject("Project") rootDir = ( project.getProjectPath() if project.isOpen() else "" ) itm = self.entryPointsList.selectedItems()[0] dlg = AddEntryPointDialog(rootDir, epType=itm.text(0), name=itm.text(1), script=itm.text(2), parent=self) if dlg.exec() == QDialog.DialogCode.Accepted: epType, epCategory, name, script = dlg.getEntryPoint() itm.setText(0, epType) itm.setText(1, name) itm.setText(2, script) itm.setData(0, Qt.ItemDataRole.UserRole, epCategory) @pyqtSlot() def on_modulesList_itemSelectionChanged(self): """ Private slot to handle a change of selected items of the modules list. """ self.deleteModuleButton.setEnabled( bool(self.modulesList.selectedItems())) @pyqtSlot() def on_deleteModuleButton_clicked(self): """ Private slot to delete the selected module items. """ for itm in self.modulesList.selectedItems(): self.modulesList.takeItem(self.modulesList.row(itm)) del itm @pyqtSlot() def on_addModuleButton_clicked(self): """ Private slot to add Python modules to the list. """ startDir = self.packageRootPicker.text() or self.__getStartDir() modulesList = EricFileDialog.getOpenFileNames( self, self.tr("Add Python Modules"), startDir, self.tr("Python Files (*.py)")) for module in modulesList: module = module.replace( Utilities.toNativeSeparators(startDir), "") if module.startswith(("\\", "/")): module = module[1:] if module: QListWidgetItem( str(pathlib.Path(module).with_suffix("")) .replace("\\", ".") .replace("/", "."), self.modulesList ) @pyqtSlot() def on_excludePatternList_itemSelectionChanged(self): """ Private slot to handle a change of selected items of the exclude pattern list. """ self.deleteExcludePatternButton.setEnabled( bool(self.excludePatternList.selectedItems())) @pyqtSlot() def on_deleteExcludePatternButton_clicked(self): """ Private slot to delete the selected exclude pattern items. """ for itm in self.excludePatternList.selectedItems(): self.excludePatternList.takeItem( self.excludePatternList.row(itm)) del itm @pyqtSlot() def on_addExludePatternButton_clicked(self): """ Private slot to add an exclude pattern to the list. """ pattern = ( self.excludePatternEdit.text().replace("\\", ".").replace("/", ".") ) if not self.excludePatternList.findItems( pattern, Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive ): QListWidgetItem(pattern, self.excludePatternList) @pyqtSlot(str) def on_excludePatternEdit_textChanged(self, txt): """ Private slot to handle a change of the exclude pattern text. @param txt text of the line edit @type str """ self.addExludePatternButton.setEnabled(bool(txt)) @pyqtSlot() def on_excludePatternEdit_returnPressed(self): """ Private slot handling a press of the return button of the exclude pattern edit. """ self.on_addExludePatternButton_clicked() @pyqtSlot() def on_urlDeleteButton_clicked(self): """ Private slot to delete the selected URL items. """ for itm in self.projectUrlsList.selectedItems(): self.projectUrlsList.takeTopLevelItem(self.projectUrlsList.row(itm)) del itm @pyqtSlot() def on_urlAddButton_clicked(self): """ Private slot to add a project URL to the list. """ dlg = AddProjectUrlDialog(parent=self) if dlg.exec() == QDialog.DialogCode.Accepted: name, url = dlg.getUrl() QTreeWidgetItem(self.projectUrlsList, [name, url]) @pyqtSlot() def on_urlEditButton_clicked(self): """ Private slot to edit the selected project URL. """ itm = self.projectUrlsList.selectedItems()[0] dlg = AddProjectUrlDialog(name=itm.text(0), url=itm.text(1), parent=self) if dlg.exec() == QDialog.DialogCode.Accepted: name, url = dlg.getUrl() itm.setText(0, name) itm.setText(1, url) @pyqtSlot() def on_projectUrlsList_itemSelectionChanged(self): """ Private slot to handle a change of selected items of the project URLs list. """ self.urlDeleteButton.setEnabled(bool(self.projectUrlsList.selectedItems())) self.urlEditButton.setEnabled(len(self.projectUrlsList.selectedItems()) == 1)