Skip to content

Commit

Permalink
[processing] Fix history doesn't correctly escape values
Browse files Browse the repository at this point in the history
Fixes #17229
  • Loading branch information
nyalldawson committed Oct 3, 2017
1 parent 9cbff3a commit b27382d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 8 deletions.
6 changes: 6 additions & 0 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -100,6 +100,12 @@ class QgsProcessingUtils
:rtype: str
%End

static QString stringToPythonLiteral( const QString &string );
%Docstring
Converts a string to a Python string literal. E.g. by replacing ' with \'.
:rtype: str
%End


static void createFeatureSinkPython(
QgsFeatureSink **sink /Out,TransferBack/,
Expand Down
18 changes: 10 additions & 8 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -1202,7 +1202,7 @@ QString QgsProcessingParameterDefinition::valueAsPythonString( const QVariant &v
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );

return value.toString().prepend( '\'' ).append( '\'' );
return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
}

QString QgsProcessingParameterDefinition::asScriptCode() const
Expand Down Expand Up @@ -1304,7 +1304,7 @@ QString QgsProcessingParameterCrs::valueAsPythonString( const QVariant &value, Q
p.insert( name(), value );
QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context );
if ( layer )
return QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' );
return QgsProcessingUtils::stringToPythonLiteral( QgsProcessingUtils::normalizeLayerSource( layer->source() ) );

return QgsProcessingParameterDefinition::valueAsPythonString( value, context );
}
Expand Down Expand Up @@ -1364,7 +1364,8 @@ QString QgsProcessingParameterMapLayer::valueAsPythonString( const QVariant &val
QVariantMap p;
p.insert( name(), val );
QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context );
return layer ? QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' ) : QString();
return layer ? QgsProcessingUtils::stringToPythonLiteral( QgsProcessingUtils::normalizeLayerSource( layer->source() ) )
: QString();
}

QgsProcessingParameterMapLayer *QgsProcessingParameterMapLayer::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
Expand Down Expand Up @@ -1833,7 +1834,7 @@ QString QgsProcessingParameterMultipleLayers::valueAsPythonString( const QVarian
QStringList parts;
Q_FOREACH ( const QgsMapLayer *layer, list )
{
parts << QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' );
parts << QgsProcessingUtils::stringToPythonLiteral( QgsProcessingUtils::normalizeLayerSource( layer->source() ) );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
Expand Down Expand Up @@ -2551,7 +2552,8 @@ QString QgsProcessingParameterVectorLayer::valueAsPythonString( const QVariant &
QVariantMap p;
p.insert( name(), val );
QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( this, p, context );
return layer ? QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' ) : QString();
return layer ? QgsProcessingUtils::stringToPythonLiteral( QgsProcessingUtils::normalizeLayerSource( layer->source() ) )
: QString();
}

QList<int> QgsProcessingParameterLimitedDataTypes::dataTypes() const
Expand Down Expand Up @@ -2652,7 +2654,7 @@ QString QgsProcessingParameterField::valueAsPythonString( const QVariant &value,
QStringList parts;
Q_FOREACH ( const QVariant &val, value.toList() )
{
parts << val.toString().prepend( '\'' ).append( '\'' );
parts << QgsProcessingUtils::stringToPythonLiteral( val.toString() );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
Expand All @@ -2661,12 +2663,12 @@ QString QgsProcessingParameterField::valueAsPythonString( const QVariant &value,
QStringList parts;
Q_FOREACH ( QString s, value.toStringList() )
{
parts << s.prepend( '\'' ).append( '\'' );
parts << QgsProcessingUtils::stringToPythonLiteral( s );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}

return value.toString().prepend( '\'' ).append( '\'' );
return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
}

QString QgsProcessingParameterField::asScriptCode() const
Expand Down
9 changes: 9 additions & 0 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -286,6 +286,15 @@ QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
return normalized.trimmed();
}

QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
{
QString s = string;
s.replace( '"', QStringLiteral( "\\\"" ) );
s.replace( '\'', QStringLiteral( "\\\'" ) );
s = s.prepend( '\'' ).append( '\'' );
return s;
}

void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &format, QMap<QString, QVariant> &options )
{
QRegularExpression splitRx( QStringLiteral( "^(.{3,}):(.*)$" ) );
Expand Down
5 changes: 5 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -115,6 +115,11 @@ class CORE_EXPORT QgsProcessingUtils
*/
static QString normalizeLayerSource( const QString &source );

/**
* Converts a string to a Python string literal. E.g. by replacing ' with \'.
*/
static QString stringToPythonLiteral( const QString &string );

/**
* Creates a feature sink ready for adding features. The \a destination specifies a destination
* URI for the resultant layer. It may be updated in place to reflect the actual destination
Expand Down
22 changes: 22 additions & 0 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -353,6 +353,7 @@ class TestQgsProcessing: public QObject
void convertCompatible();
void create();
void combineFields();
void stringToPythonLiteral();

private:

Expand Down Expand Up @@ -1670,6 +1671,7 @@ void TestQgsProcessing::parameterCrs()
QCOMPARE( def->valueAsPythonString( raster1, context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterCrs fromMap( "x" );
Expand Down Expand Up @@ -2007,6 +2009,7 @@ void TestQgsProcessing::parameterExtent()
QCOMPARE( def->valueAsPythonString( QgsRectangle( 11, 12, 13, 14 ), context ), QStringLiteral( "'11, 13, 12, 14'" ) );
QCOMPARE( def->valueAsPythonString( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11, 13, 12, 14 [EPSG:4326]'" ) );
QCOMPARE( def->valueAsPythonString( "1,2,3,4 [EPSG:4326]", context ), QStringLiteral( "'1,2,3,4 [EPSG:4326]'" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );

QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=extent 1,2,3,4" ) );
Expand Down Expand Up @@ -2204,6 +2207,7 @@ void TestQgsProcessing::parameterFile()

QCOMPARE( def->valueAsPythonString( "bricks.bmp", context ), QStringLiteral( "'bricks.bmp'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );

QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=file abc.bmp" ) );
Expand Down Expand Up @@ -2455,6 +2459,7 @@ void TestQgsProcessing::parameterLayerList()
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc']" ) );
QCOMPARE( def->valueAsPythonString( QStringList() << r1->id() << raster2, context ), QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc','" ) + testDataDir + QStringLiteral( "landsat.tif']" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );

QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=multiple vector" ) );
Expand Down Expand Up @@ -3090,6 +3095,7 @@ void TestQgsProcessing::parameterString()
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=string" ) );
Expand Down Expand Up @@ -3292,6 +3298,8 @@ void TestQgsProcessing::parameterField()

QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "probably\'invalid\"field", context ), QStringLiteral( "'probably\\'invalid\\\"field'" ) );


QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=field" ) );
Expand Down Expand Up @@ -3625,6 +3633,7 @@ void TestQgsProcessing::parameterFeatureSource()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( v2 ), context ), QStringLiteral( "'%1'" ).arg( vector2 ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterFeatureSource fromMap( "x" );
Expand Down Expand Up @@ -3719,6 +3728,7 @@ void TestQgsProcessing::parameterFeatureSink()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QCOMPARE( def->defaultFileExtension(), QStringLiteral( "shp" ) );
QCOMPARE( def->generateTemporaryDestination(), QStringLiteral( "memory:" ) );
Expand Down Expand Up @@ -3831,6 +3841,7 @@ void TestQgsProcessing::parameterVectorOut()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QCOMPARE( def->defaultFileExtension(), QStringLiteral( "shp" ) );
QVERIFY( def->generateTemporaryDestination().endsWith( QStringLiteral( ".shp" ) ) );
Expand Down Expand Up @@ -3942,6 +3953,7 @@ void TestQgsProcessing::parameterRasterOut()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterRasterDestination fromMap( "x" );
Expand Down Expand Up @@ -4060,6 +4072,7 @@ void TestQgsProcessing::parameterFileOut()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\'complex\' username=\"complex\"'" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterFileDestination fromMap( "x" );
Expand Down Expand Up @@ -4133,6 +4146,7 @@ void TestQgsProcessing::parameterFolderOut()

QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\\\"complex\\\"'" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterFolderDestination fromMap( "x" );
Expand Down Expand Up @@ -5587,5 +5601,13 @@ void TestQgsProcessing::combineFields()
QCOMPARE( res.at( 3 ).name(), QStringLiteral( "new_2" ) );
}

void TestQgsProcessing::stringToPythonLiteral()
{
QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a" ) ), QStringLiteral( "'a'" ) );
QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QString() ), QStringLiteral( "''" ) );
QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a 'string'" ) ), QStringLiteral( "'a \\'string\\''" ) );
QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a \"string\"" ) ), QStringLiteral( "'a \\\"string\\\"'" ) );
}

QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc"

0 comments on commit b27382d

Please sign in to comment.