Skip to content

Commit

Permalink
[core] Address review: Move loadFunctionsFromProject and cleanFunctio…
Browse files Browse the repository at this point in the history
…nsFromProject to QgsProject::read and ::clear, respectively. Introduce function pythonEmbeddedInProjectAllowed to ask for permissions to load both macros and expression functions, with a parameter enum and rename existing enum for macros. Sort includes. Drop not needed file. Add python/expressions/ to gitignore, to get rid of files produced by the introduced Python test.
  • Loading branch information
gacarrillor committed Sep 6, 2024
1 parent 053e95e commit c5286e4
Show file tree
Hide file tree
Showing 25 changed files with 298 additions and 333 deletions.
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ desktop.ini
doc/INSTALL.tex
i18n/*.qm
ms-windows/*.exe*
ms-windows/Installer-Files/postinstall.bat
ms-windows/Installer-Files/preremove.bat
ms-windows/nsis/
ms-windows/osgeo4w/addons/
ms-windows/osgeo4w/binary-*
ms-windows/osgeo4w/build-*
ms-windows/osgeo4w/nsis/
ms-windows/osgeo4w/packages-x86/
ms-windows/osgeo4w/packages-x86_64/
ms-windows/osgeo4w/unpacked/
ms-windows/osgeo4w/untgz/
ms-windows/packages/
ms-windows/progs/
ms-windows/untgz/
python/expressions/
python/plugins/grassprovider/description/algorithms.json
python/plugins/grassprovider/tests/testdata/directions.tif.aux.xml
python/plugins/processing/tests/testdata/*.aux.xml
Expand Down
49 changes: 26 additions & 23 deletions python/PyQt6/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,34 +620,37 @@
Qgis.VectorLayerTypeFlags.baseClass = Qgis
VectorLayerTypeFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.Never = Qgis.PythonMacroMode.Never
Qgis.Never.is_monkey_patched = True
Qgis.Never.__doc__ = "Macros are never run"
Qgis.Ask = Qgis.PythonMacroMode.Ask
Qgis.Ask.is_monkey_patched = True
Qgis.Ask.__doc__ = "User is prompt before running"
Qgis.SessionOnly = Qgis.PythonMacroMode.SessionOnly
Qgis.SessionOnly.is_monkey_patched = True
Qgis.SessionOnly.__doc__ = "Only during this session"
Qgis.Always = Qgis.PythonMacroMode.Always
Qgis.Always.is_monkey_patched = True
Qgis.Always.__doc__ = "Macros are always run"
Qgis.NotForThisSession = Qgis.PythonMacroMode.NotForThisSession
Qgis.NotForThisSession.is_monkey_patched = True
Qgis.NotForThisSession.__doc__ = "Macros will not be run for this session"
Qgis.PythonMacroMode.__doc__ = """Authorisation to run Python Macros

.. versionadded:: 3.10

* ``Never``: Macros are never run
Qgis.PythonEmbeddedMode.Never.__doc__ = "Python embedded never run"
Qgis.PythonEmbeddedMode.Ask.__doc__ = "User is prompt before running"
Qgis.PythonEmbeddedMode.SessionOnly.__doc__ = "Only during this session"
Qgis.PythonEmbeddedMode.Always.__doc__ = "Python embedded is always run"
Qgis.PythonEmbeddedMode.NotForThisSession.__doc__ = "Python embedded will not be run for this session"
Qgis.PythonEmbeddedMode.__doc__ = """Authorisation to run Python Embedded in projects

.. versionadded:: 3.40

* ``Never``: Python embedded never run
* ``Ask``: User is prompt before running
* ``SessionOnly``: Only during this session
* ``Always``: Macros are always run
* ``NotForThisSession``: Macros will not be run for this session
* ``Always``: Python embedded is always run
* ``NotForThisSession``: Python embedded will not be run for this session

"""
# --
Qgis.PythonEmbeddedMode.baseClass = Qgis
# monkey patching scoped based enum
Qgis.PythonEmbeddedType.Macro.__doc__ = ""
Qgis.PythonEmbeddedType.ExpressionFunction.__doc__ = ""
Qgis.PythonEmbeddedType.__doc__ = """Type of Python Embedded in projects

.. versionadded:: 3.40

* ``Macro``:
* ``ExpressionFunction``:

"""
# --
Qgis.PythonMacroMode.baseClass = Qgis
Qgis.PythonEmbeddedType.baseClass = Qgis
QgsDataProvider.ReadFlag = Qgis.DataProviderReadFlag
# monkey patching scoped based enum
QgsDataProvider.FlagTrustDataSource = Qgis.DataProviderReadFlag.TrustDataSource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,6 @@ Returns the number of functions defined in the parser
:return: The number of function defined in the parser.
%End



static QString quotedColumnRef( QString name );
%Docstring
Returns a quoted column reference (in double quotes)
Expand Down
3 changes: 3 additions & 0 deletions python/PyQt6/core/auto_generated/project/qgsproject.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,9 @@ Sets the elevation shading renderer used for global map shading
.. versionadded:: 3.30
%End




SIP_PYOBJECT __repr__();
%MethodCode
QString str = QStringLiteral( "<QgsProject: '%1'%2>" ).arg( sipCpp->fileName(),
Expand Down
10 changes: 8 additions & 2 deletions python/PyQt6/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,21 @@ The development version
typedef QFlags<Qgis::VectorLayerTypeFlag> VectorLayerTypeFlags;


enum class PythonMacroMode /BaseType=IntEnum/
{
enum class PythonEmbeddedMode /BaseType=IntEnum/
{
Never,
Ask,
SessionOnly,
Always,
NotForThisSession,
};

enum class PythonEmbeddedType /BaseType=IntEnum/
{
Macro,
ExpressionFunction,
};

enum class DataProviderReadFlag /BaseType=IntFlag/
{
TrustDataSource,
Expand Down
1 change: 0 additions & 1 deletion python/PyQt6/gui/auto_generated/qgsgui.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ Returns the screen at the given global ``point`` (pixel).




signals:

void optionsChanged();
Expand Down
49 changes: 26 additions & 23 deletions python/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,34 +612,37 @@
Qgis.VectorLayerTypeFlags.baseClass = Qgis
VectorLayerTypeFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.Never = Qgis.PythonMacroMode.Never
Qgis.Never.is_monkey_patched = True
Qgis.Never.__doc__ = "Macros are never run"
Qgis.Ask = Qgis.PythonMacroMode.Ask
Qgis.Ask.is_monkey_patched = True
Qgis.Ask.__doc__ = "User is prompt before running"
Qgis.SessionOnly = Qgis.PythonMacroMode.SessionOnly
Qgis.SessionOnly.is_monkey_patched = True
Qgis.SessionOnly.__doc__ = "Only during this session"
Qgis.Always = Qgis.PythonMacroMode.Always
Qgis.Always.is_monkey_patched = True
Qgis.Always.__doc__ = "Macros are always run"
Qgis.NotForThisSession = Qgis.PythonMacroMode.NotForThisSession
Qgis.NotForThisSession.is_monkey_patched = True
Qgis.NotForThisSession.__doc__ = "Macros will not be run for this session"
Qgis.PythonMacroMode.__doc__ = """Authorisation to run Python Macros

.. versionadded:: 3.10

* ``Never``: Macros are never run
Qgis.PythonEmbeddedMode.Never.__doc__ = "Python embedded never run"
Qgis.PythonEmbeddedMode.Ask.__doc__ = "User is prompt before running"
Qgis.PythonEmbeddedMode.SessionOnly.__doc__ = "Only during this session"
Qgis.PythonEmbeddedMode.Always.__doc__ = "Python embedded is always run"
Qgis.PythonEmbeddedMode.NotForThisSession.__doc__ = "Python embedded will not be run for this session"
Qgis.PythonEmbeddedMode.__doc__ = """Authorisation to run Python Embedded in projects

.. versionadded:: 3.40

* ``Never``: Python embedded never run
* ``Ask``: User is prompt before running
* ``SessionOnly``: Only during this session
* ``Always``: Macros are always run
* ``NotForThisSession``: Macros will not be run for this session
* ``Always``: Python embedded is always run
* ``NotForThisSession``: Python embedded will not be run for this session

"""
# --
Qgis.PythonEmbeddedMode.baseClass = Qgis
# monkey patching scoped based enum
Qgis.PythonEmbeddedType.Macro.__doc__ = ""
Qgis.PythonEmbeddedType.ExpressionFunction.__doc__ = ""
Qgis.PythonEmbeddedType.__doc__ = """Type of Python Embedded in projects

.. versionadded:: 3.40

* ``Macro``:
* ``ExpressionFunction``:

"""
# --
Qgis.PythonMacroMode.baseClass = Qgis
Qgis.PythonEmbeddedType.baseClass = Qgis
QgsDataProvider.ReadFlag = Qgis.DataProviderReadFlag
# monkey patching scoped based enum
QgsDataProvider.FlagTrustDataSource = Qgis.DataProviderReadFlag.TrustDataSource
Expand Down
2 changes: 0 additions & 2 deletions python/core/auto_generated/expression/qgsexpression.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,6 @@ Returns the number of functions defined in the parser
:return: The number of function defined in the parser.
%End



static QString quotedColumnRef( QString name );
%Docstring
Returns a quoted column reference (in double quotes)
Expand Down
3 changes: 3 additions & 0 deletions python/core/auto_generated/project/qgsproject.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,9 @@ Sets the elevation shading renderer used for global map shading
.. versionadded:: 3.30
%End




SIP_PYOBJECT __repr__();
%MethodCode
QString str = QStringLiteral( "<QgsProject: '%1'%2>" ).arg( sipCpp->fileName(),
Expand Down
10 changes: 8 additions & 2 deletions python/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,21 @@ The development version
typedef QFlags<Qgis::VectorLayerTypeFlag> VectorLayerTypeFlags;


enum class PythonMacroMode
{
enum class PythonEmbeddedMode
{
Never,
Ask,
SessionOnly,
Always,
NotForThisSession,
};

enum class PythonEmbeddedType
{
Macro,
ExpressionFunction,
};

enum class DataProviderReadFlag
{
TrustDataSource,
Expand Down
1 change: 0 additions & 1 deletion python/gui/auto_generated/qgsgui.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ Returns the screen at the given global ``point`` (pixel).




signals:

void optionsChanged();
Expand Down
16 changes: 8 additions & 8 deletions src/app/options/qgsoptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,11 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
// non-default themes are best rendered using the Fusion style, therefore changing themes must require a restart to
lblUITheme->setText( QStringLiteral( "%1 <i>(%2)</i>" ).arg( lblUITheme->text(), tr( "QGIS restart required" ) ) );

mEnableMacrosComboBox->addItem( tr( "Never" ), QVariant::fromValue( Qgis::PythonMacroMode::Never ) );
mEnableMacrosComboBox->addItem( tr( "Ask" ), QVariant::fromValue( Qgis::PythonMacroMode::Ask ) );
mEnableMacrosComboBox->addItem( tr( "For This Session Only" ), QVariant::fromValue( Qgis::PythonMacroMode::SessionOnly ) );
mEnableMacrosComboBox->addItem( tr( "Not During This Session" ), QVariant::fromValue( Qgis::PythonMacroMode::NotForThisSession ) );
mEnableMacrosComboBox->addItem( tr( "Always (Not Recommended)" ), QVariant::fromValue( Qgis::PythonMacroMode::Always ) );
mEnableMacrosComboBox->addItem( tr( "Never" ), QVariant::fromValue( Qgis::PythonEmbeddedMode::Never ) );
mEnableMacrosComboBox->addItem( tr( "Ask" ), QVariant::fromValue( Qgis::PythonEmbeddedMode::Ask ) );
mEnableMacrosComboBox->addItem( tr( "For This Session Only" ), QVariant::fromValue( Qgis::PythonEmbeddedMode::SessionOnly ) );
mEnableMacrosComboBox->addItem( tr( "Not During This Session" ), QVariant::fromValue( Qgis::PythonEmbeddedMode::NotForThisSession ) );
mEnableMacrosComboBox->addItem( tr( "Always (Not Recommended)" ), QVariant::fromValue( Qgis::PythonEmbeddedMode::Always ) );

mIdentifyHighlightColorButton->setColorDialogTitle( tr( "Identify Highlight Color" ) );
mIdentifyHighlightColorButton->setAllowOpacity( true );
Expand Down Expand Up @@ -823,8 +823,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
chbAskToSaveProjectChanges->setChecked( mSettings->value( QStringLiteral( "qgis/askToSaveProjectChanges" ), QVariant( true ) ).toBool() );
mLayerDeleteConfirmationChkBx->setChecked( mSettings->value( QStringLiteral( "qgis/askToDeleteLayers" ), true ).toBool() );
chbWarnOldProjectVersion->setChecked( mSettings->value( QStringLiteral( "/qgis/warnOldProjectVersion" ), QVariant( true ) ).toBool() );
Qgis::PythonMacroMode pyMacroMode = mSettings->enumValue( QStringLiteral( "/qgis/enableMacros" ), Qgis::PythonMacroMode::Ask );
mEnableMacrosComboBox->setCurrentIndex( mEnableMacrosComboBox->findData( QVariant::fromValue( pyMacroMode ) ) );
Qgis::PythonEmbeddedMode pyEmbeddedMode = mSettings->enumValue( QStringLiteral( "/qgis/enablePythonEmbedded" ), Qgis::PythonEmbeddedMode::Ask );
mEnableMacrosComboBox->setCurrentIndex( mEnableMacrosComboBox->findData( QVariant::fromValue( pyEmbeddedMode ) ) );

mDefaultPathsComboBox->addItem( tr( "Absolute" ), static_cast< int >( Qgis::FilePathType::Absolute ) );
mDefaultPathsComboBox->addItem( tr( "Relative" ), static_cast< int >( Qgis::FilePathType::Relative ) );
Expand Down Expand Up @@ -1655,7 +1655,7 @@ void QgsOptions::saveOptions()
mSettings->setValue( QStringLiteral( "/qgis/projectTemplateDir" ), leTemplateFolder->text() );
QgisApp::instance()->updateProjectFromTemplates();
}
mSettings->setEnumValue( QStringLiteral( "/qgis/enableMacros" ), mEnableMacrosComboBox->currentData().value<Qgis::PythonMacroMode>() );
mSettings->setEnumValue( QStringLiteral( "/qgis/enablePythonEmbedded" ), mEnableMacrosComboBox->currentData().value<Qgis::PythonEmbeddedMode>() );

mSettings->setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ),
static_cast< Qgis::FilePathType >( mDefaultPathsComboBox->currentData().toInt() ) == Qgis::FilePathType::Relative );
Expand Down
29 changes: 10 additions & 19 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2889,17 +2889,17 @@ void QgisApp::readSettings()
readRecentProjects();

// this is a new session, reset enable macros value when they are set for session
Qgis::PythonMacroMode macroMode = settings.enumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::Ask );
switch ( macroMode )
Qgis::PythonEmbeddedMode pythonEmbeddedMode = settings.enumValue( QStringLiteral( "qgis/enablePythonEmbedded" ), Qgis::PythonEmbeddedMode::Ask );
switch ( pythonEmbeddedMode )
{
case Qgis::PythonMacroMode::NotForThisSession:
case Qgis::PythonMacroMode::SessionOnly:
settings.setEnumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::Ask );
case Qgis::PythonEmbeddedMode::NotForThisSession:
case Qgis::PythonEmbeddedMode::SessionOnly:
settings.setEnumValue( QStringLiteral( "qgis/enablePythonEmbedded" ), Qgis::PythonEmbeddedMode::Ask );
break;

case Qgis::PythonMacroMode::Always:
case Qgis::PythonMacroMode::Never:
case Qgis::PythonMacroMode::Ask:
case Qgis::PythonEmbeddedMode::Always:
case Qgis::PythonEmbeddedMode::Never:
case Qgis::PythonEmbeddedMode::Ask:
break;
}
}
Expand Down Expand Up @@ -6575,14 +6575,14 @@ bool QgisApp::addProject( const QString &projectFile )
if ( !QgsProject::instance()->readEntry( QStringLiteral( "Macros" ), QStringLiteral( "/pythonCode" ), QString() ).isEmpty() )
{
auto lambda = []() {QgisApp::instance()->enableProjectMacros();};
QgsGui::pythonMacroAllowed( lambda, mInfoBar );
QgsGui::pythonEmbeddedInProjectAllowed( lambda, mInfoBar, Qgis::PythonEmbeddedType::Macro );
}

// does the project have expression functions?
const QString projectFunctions = QgsProject::instance()->readEntry( QStringLiteral( "ExpressionFunctions" ), QStringLiteral( "/pythonCode" ), QString() );
if ( !projectFunctions.isEmpty() )
{
QgsGui::pythonExpressionFromProjectAllowed( mInfoBar );
QgsGui::pythonEmbeddedInProjectAllowed( nullptr, mInfoBar, Qgis::PythonEmbeddedType::ExpressionFunction );
}
}
#endif
Expand Down Expand Up @@ -13865,15 +13865,6 @@ void QgisApp::closeProject()
}
mPythonMacrosEnabled = false;

#ifdef WITH_BINDINGS
// unload the project expression functions and reload user expressions
const QString projectFunctions = QgsProject::instance()->readEntry( QStringLiteral( "ExpressionFunctions" ), QStringLiteral( "/pythonCode" ), QString() );
if ( !projectFunctions.isEmpty() )
{
QgsExpression::cleanFunctionsFromProject();
}
#endif

mLegendExpressionFilterButton->setExpressionText( QString() );
mLegendExpressionFilterButton->setChecked( false );
mFilterLegendByMapContentAction->setChecked( false );
Expand Down
18 changes: 0 additions & 18 deletions src/core/expression/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -630,24 +630,6 @@ class CORE_EXPORT QgsExpression
*/
static int functionCount();

/**
* Loads python expression functions stored in the currrent project
* \returns Whether the project functions where loaded or not.
*
* \note not available in Python bindings
* \since QGIS 3.40
*/
static bool loadFunctionsFromProject() SIP_SKIP;

/**
* Unloads python expression functions stored in the current project
* and reloads local functions from the user profile.
*
* \note not available in Python bindings
* \since QGIS 3.40
*/
static void cleanFunctionsFromProject() SIP_SKIP;

/**
* Returns a quoted column reference (in double quotes)
* \see quotedString()
Expand Down
17 changes: 0 additions & 17 deletions src/core/expression/qgsexpressionfunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
#include "qgsunittypes.h"
#include "qgsspatialindex.h"
#include "qgscolorrampimpl.h"
#include "qgspythonrunner.h"

#include <QMimeDatabase>
#include <QProcessEnvironment>
Expand Down Expand Up @@ -9502,22 +9501,6 @@ const QStringList &QgsExpression::BuiltinFunctions()
return *sBuiltinFunctions();
}

bool QgsExpression::loadFunctionsFromProject()
{
const QString projectFunctions = QgsProject::instance()->readEntry( QStringLiteral( "ExpressionFunctions" ), QStringLiteral( "/pythonCode" ), QString() );
if ( !projectFunctions.isEmpty() )
{
QgsPythonRunner::run( projectFunctions );
return true;
}
return false;
}

void QgsExpression::cleanFunctionsFromProject()
{
QgsPythonRunner::run( "qgis.utils.clean_project_expression_functions()" );
}

QgsArrayForeachExpressionFunction::QgsArrayForeachExpressionFunction()
: QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
<< QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
Expand Down
Loading

0 comments on commit c5286e4

Please sign in to comment.