Python: add type hinting to python code.

Types are checked using mypy.

Fixes #10394.
This commit is contained in:
Kristian Bendiksen 2023-07-12 11:42:17 +02:00
parent b1157436fe
commit 7aabe8c4a8
31 changed files with 522 additions and 292 deletions

View File

@ -46,7 +46,7 @@ jobs:
run: | run: |
execute_process( execute_process(
COMMAND cmake COMMAND cmake
-S Fwk/AppFwk -S Fwk
-B cmakebuild -B cmakebuild
-G Ninja -G Ninja
RESULT_VARIABLE result RESULT_VARIABLE result
@ -70,13 +70,13 @@ jobs:
- name: Run Unit Tests - name: Run Unit Tests
shell: bash shell: bash
run: | run: |
cmakebuild/cafProjectDataModel/cafPdmCore/cafPdmCore_UnitTests/cafPdmCore_UnitTests cmakebuild/AppFwk/cafProjectDataModel/cafPdmCore/cafPdmCore_UnitTests/cafPdmCore_UnitTests
cmakebuild/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmXml_UnitTests cmakebuild/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmXml_UnitTests
cmakebuild/cafProjectDataModel/cafProjectDataModel_UnitTests/cafProjectDataModel_UnitTests cmakebuild/AppFwk/cafProjectDataModel/cafProjectDataModel_UnitTests/cafProjectDataModel_UnitTests
cmakebuild/cafPdmScripting/cafPdmScripting_UnitTests/cafPdmScripting_UnitTests cmakebuild/AppFwk/cafPdmScripting/cafPdmScripting_UnitTests/cafPdmScripting_UnitTests
- name: Run Unit Tests Windows (does not work on Linux) - name: Run Unit Tests Windows (does not work on Linux)
if: contains( matrix.os, 'windows') if: contains( matrix.os, 'windows')
shell: bash shell: bash
run: | run: |
cmakebuild/cafUserInterface/cafUserInterface_UnitTests/cafUserInterface_UnitTests cmakebuild/AppFwk/cafUserInterface/cafUserInterface_UnitTests/cafUserInterface_UnitTests

View File

@ -230,6 +230,14 @@ jobs:
run: | run: |
cmakebuild/ApplicationExeCode/ResInsight --unittest cmakebuild/ApplicationExeCode/ResInsight --unittest
- name: (Python) Check types using mypy
if: matrix.config.build-python-module
shell: bash
run: |
${{ steps.python-path.outputs.PYTHON_EXECUTABLE }} -m pip install mypy types-protobuf
cd GrpcInterface/Python/rips
${{ steps.python-path.outputs.PYTHON_EXECUTABLE }} -m mypy *.py generated/generated_classes.py
- name: Run pytest - name: Run pytest
if: matrix.config.build-python-module if: matrix.config.build-python-module
env: env:

View File

@ -16,8 +16,8 @@
# Python version of RiaVersionInfo.h # Python version of RiaVersionInfo.h
# Just sets version constants # Just sets version constants
RESINSIGHT_MAJOR_VERSION = "@RESINSIGHT_MAJOR_VERSION@" RESINSIGHT_MAJOR_VERSION : str = "@RESINSIGHT_MAJOR_VERSION@"
RESINSIGHT_MINOR_VERSION = "@RESINSIGHT_MINOR_VERSION@" RESINSIGHT_MINOR_VERSION : str = "@RESINSIGHT_MINOR_VERSION@"
RESINSIGHT_PATCH_VERSION = "@RESINSIGHT_PATCH_VERSION@" RESINSIGHT_PATCH_VERSION : str = "@RESINSIGHT_PATCH_VERSION@"
PYTHON_GRPC_PROTOC_VERSION = "@PYTHON_GRPC_PROTOC_VERSION@" PYTHON_GRPC_PROTOC_VERSION : str = "@PYTHON_GRPC_PROTOC_VERSION@"

View File

@ -29,6 +29,7 @@
#include "RicVec3dPickEventHandler.h" #include "RicVec3dPickEventHandler.h"
#include "cafCmdFeatureManager.h" #include "cafCmdFeatureManager.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiPickableLineEditor.h" #include "cafPdmUiPickableLineEditor.h"
#include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiTextEditor.h" #include "cafPdmUiTextEditor.h"
@ -44,7 +45,7 @@ CAF_PDM_SOURCE_INIT( RimTextAnnotation, "RimTextAnnotation" );
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
RimTextAnnotation::RimTextAnnotation() RimTextAnnotation::RimTextAnnotation()
{ {
CAF_PDM_InitObject( "TextAnnotation", ":/TextAnnotation16x16.png" ); CAF_PDM_InitScriptableObject( "TextAnnotation", ":/TextAnnotation16x16.png" );
setUi3dEditorTypeName( RicTextAnnotation3dEditor::uiEditorTypeName() ); setUi3dEditorTypeName( RicTextAnnotation3dEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_anchorPointXyd, "AnchorPointXyd", Vec3d::ZERO, "Anchor Point" ); CAF_PDM_InitField( &m_anchorPointXyd, "AnchorPointXyd", Vec3d::ZERO, "Anchor Point" );

View File

@ -32,6 +32,7 @@
#include "RimWellPath.h" #include "RimWellPath.h"
#include "RimWellPathValve.h" #include "RimWellPathValve.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiDateEditor.h" #include "cafPdmUiDateEditor.h"
#include "cafPdmUiDoubleSliderEditor.h" #include "cafPdmUiDoubleSliderEditor.h"
@ -42,7 +43,7 @@ CAF_PDM_SOURCE_INIT( RimPerforationInterval, "Perforation" );
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
RimPerforationInterval::RimPerforationInterval() RimPerforationInterval::RimPerforationInterval()
{ {
CAF_PDM_InitObject( "Perforation", ":/PerforationInterval16x16.png" ); CAF_PDM_InitScriptableObject( "Perforation", ":/PerforationInterval16x16.png" );
CAF_PDM_InitField( &m_startMD, "StartMeasuredDepth", 0.0, "Start MD" ); CAF_PDM_InitField( &m_startMD, "StartMeasuredDepth", 0.0, "Start MD" );
CAF_PDM_InitField( &m_endMD, "EndMeasuredDepth", 0.0, "End MD" ); CAF_PDM_InitField( &m_endMD, "EndMeasuredDepth", 0.0, "End MD" );

View File

@ -26,6 +26,7 @@
#include "RimWellPath.h" #include "RimWellPath.h"
#include "RimWellPathFractureCollection.h" #include "RimWellPathFractureCollection.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiDoubleSliderEditor.h" #include "cafPdmUiDoubleSliderEditor.h"
CAF_PDM_SOURCE_INIT( RimWellPathFracture, "WellPathFracture" ); CAF_PDM_SOURCE_INIT( RimWellPathFracture, "WellPathFracture" );
@ -35,7 +36,7 @@ CAF_PDM_SOURCE_INIT( RimWellPathFracture, "WellPathFracture" );
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
RimWellPathFracture::RimWellPathFracture() RimWellPathFracture::RimWellPathFracture()
{ {
CAF_PDM_InitObject( "Fracture", ":/FractureSymbol16x16.png" ); CAF_PDM_InitScriptableObject( "Fracture", ":/FractureSymbol16x16.png" );
CAF_PDM_InitField( &m_measuredDepth, "MeasuredDepth", 0.0f, "Measured Depth Location" ); CAF_PDM_InitField( &m_measuredDepth, "MeasuredDepth", 0.0f, "Measured Depth Location" );
m_measuredDepth.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); m_measuredDepth.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );

View File

@ -24,6 +24,7 @@
#include "RimColorLegendItem.h" #include "RimColorLegendItem.h"
#include "cafPdmFieldReorderCapability.h" #include "cafPdmFieldReorderCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include <algorithm> #include <algorithm>
@ -34,7 +35,7 @@ CAF_PDM_SOURCE_INIT( RimColorLegend, "ColorLegend" );
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
RimColorLegend::RimColorLegend() RimColorLegend::RimColorLegend()
{ {
CAF_PDM_InitObject( "ColorLegend", ":/Legend.png" ); CAF_PDM_InitScriptableObject( "ColorLegend", ":/Legend.png" );
CAF_PDM_InitField( &m_colorLegendName, "ColorLegendName", QString( "" ), "Color Legend Name" ); CAF_PDM_InitField( &m_colorLegendName, "ColorLegendName", QString( "" ), "Color Legend Name" );

View File

@ -34,6 +34,7 @@
#include "RimEclipseInputPropertyCollection.h" #include "RimEclipseInputPropertyCollection.h"
#include "RimReservoirCellResultsStorage.h" #include "RimReservoirCellResultsStorage.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafProgressInfo.h" #include "cafProgressInfo.h"
#include <QDir> #include <QDir>
@ -46,7 +47,7 @@ CAF_PDM_SOURCE_INIT( RimRoffCase, "RimRoffCase" );
RimRoffCase::RimRoffCase() RimRoffCase::RimRoffCase()
: RimEclipseCase() : RimEclipseCase()
{ {
CAF_PDM_InitObject( "RimRoffCase", ":/EclipseInput48x48.png" ); CAF_PDM_InitScriptableObject( "RimRoffCase", ":/EclipseInput48x48.png" );
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------

View File

@ -703,7 +703,6 @@ add_subdirectory(Fwk/AppFwk/cafPdmCvf)
add_subdirectory(Fwk/AppFwk/CommonCode) add_subdirectory(Fwk/AppFwk/CommonCode)
add_subdirectory(Fwk/AppFwk/cafVizExtensions) add_subdirectory(Fwk/AppFwk/cafVizExtensions)
option(CAF_CVF_SCRIPTING "" ON)
add_subdirectory(Fwk/AppFwk/cafPdmScripting) add_subdirectory(Fwk/AppFwk/cafPdmScripting)
add_subdirectory(Fwk/AppFwk/cafCommandFeatures) add_subdirectory(Fwk/AppFwk/cafCommandFeatures)

View File

@ -1,7 +1,5 @@
project(cafPdmScripting) project(cafPdmScripting)
option(CAF_CVF_SCRIPTING "Enable CVF-data support in Scripting" OFF)
# Unity Build # Unity Build
if(CAF_ENABLE_UNITY_BUILD) if(CAF_ENABLE_UNITY_BUILD)
message("Cmake Unity build is enabled on : ${PROJECT_NAME}") message("Cmake Unity build is enabled on : ${PROJECT_NAME}")
@ -30,21 +28,13 @@ set(PROJECT_FILES
cafPdmMarkdownGenerator.cpp cafPdmMarkdownGenerator.cpp
cafPdmMarkdownBuilder.h cafPdmMarkdownBuilder.h
cafPdmMarkdownBuilder.cpp cafPdmMarkdownBuilder.cpp
)
set(CAF_LIBS cafProjectDataModel)
if(CAF_CVF_SCRIPTING)
list(
APPEND
PROJECT_FILES
cafPdmFieldScriptingCapabilityCvfColor3.h cafPdmFieldScriptingCapabilityCvfColor3.h
cafPdmFieldScriptingCapabilityCvfColor3.cpp cafPdmFieldScriptingCapabilityCvfColor3.cpp
cafPdmFieldScriptingCapabilityCvfVec3d.h cafPdmFieldScriptingCapabilityCvfVec3d.h
cafPdmFieldScriptingCapabilityCvfVec3d.cpp cafPdmFieldScriptingCapabilityCvfVec3d.cpp
) )
list(APPEND CAF_LIBS cafPdmCvf)
endif() set(CAF_LIBS cafProjectDataModel cafPdmCvf)
add_library(${PROJECT_NAME} ${PROJECT_FILES}) add_library(${PROJECT_NAME} ${PROJECT_FILES})

View File

@ -38,6 +38,8 @@
#include "cafPdmAbstractFieldScriptingCapability.h" #include "cafPdmAbstractFieldScriptingCapability.h"
#include "cafPdmChildArrayField.h" #include "cafPdmChildArrayField.h"
#include "cafPdmChildField.h" #include "cafPdmChildField.h"
#include "cafPdmFieldScriptingCapabilityCvfColor3.h"
#include "cafPdmFieldScriptingCapabilityCvfVec3d.h"
#include "cafPdmObject.h" #include "cafPdmObject.h"
#include "cafPdmObjectFactory.h" #include "cafPdmObjectFactory.h"
#include "cafPdmObjectMethod.h" #include "cafPdmObjectMethod.h"
@ -135,10 +137,10 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
auto pdmChildArrayField = dynamic_cast<const PdmChildArrayFieldHandle*>( field ); auto pdmChildArrayField = dynamic_cast<const PdmChildArrayFieldHandle*>( field );
if ( pdmValueField ) if ( pdmValueField )
{ {
QString dataType = PdmPythonGenerator::dataTypeString( field, true ); QString dataType = PdmPythonGenerator::dataTypeString( field, false );
if ( field->xmlCapability()->isVectorField() ) if ( field->xmlCapability()->isVectorField() )
{ {
dataType = QString( "List of %1" ).arg( dataType ); dataType = QString( "List[%1]" ).arg( dataType );
} }
bool shouldBeMethod = false; bool shouldBeMethod = false;
@ -159,9 +161,10 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
.arg( comment ) .arg( comment )
.arg( dataType ); .arg( dataType );
QString fieldCode = QString( " def %1(self):\n%2\n return " QString fieldCode = QString( " def %1(self) -> %2:\n%3\n return "
"self._call_get_method(\"%3\")\n" ) "self._call_get_method(\"%4\")\n" )
.arg( snake_field_name ) .arg( snake_field_name )
.arg( dataType )
.arg( fullComment ) .arg( fullComment )
.arg( scriptability->scriptFieldName() ); .arg( scriptability->scriptFieldName() );
classMethodsGenerated[field->ownerClass()][snake_field_name] = fieldCode; classMethodsGenerated[field->ownerClass()][snake_field_name] = fieldCode;
@ -173,11 +176,13 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
.arg( comment ) .arg( comment )
.arg( dataType ); .arg( dataType );
QString fieldCode = QString( " def set_%1(self, values):\n%2\n " QString fieldCode =
"self._call_set_method(\"%3\", values)\n" ) QString( " def set_%1(self, values : %2) -> None:\n%3\n "
.arg( snake_field_name ) "self._call_set_method(\"%4\", values)\n" )
.arg( fullComment ) .arg( snake_field_name )
.arg( scriptability->scriptFieldName() ); .arg( dataType )
.arg( fullComment )
.arg( scriptability->scriptFieldName() );
classMethodsGenerated[field->ownerClass()][QString( "set_%1" ).arg( snake_field_name )] = classMethodsGenerated[field->ownerClass()][QString( "set_%1" ).arg( snake_field_name )] =
fieldCode; fieldCode;
} }
@ -186,8 +191,13 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
{ {
QString valueString = getDefaultValue( field ); QString valueString = getDefaultValue( field );
if ( valueString == "None" )
{
dataType = QString( "Optional[%1]" ).arg( dataType );
}
QString fieldCode = QString fieldCode =
QString( " self.%1 = %2\n" ).arg( snake_field_name ).arg( valueString ); QString( " self.%1: %2 = %3\n" ).arg( snake_field_name ).arg( dataType ).arg( valueString );
QString fullComment; QString fullComment;
{ {
@ -218,7 +228,7 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
dataTypesInChildFields.insert( scriptDataType ); dataTypesInChildFields.insert( scriptDataType );
QString commentDataType = field->xmlCapability()->isVectorField() QString commentDataType = field->xmlCapability()->isVectorField()
? QString( "List of %1" ).arg( scriptDataType ) ? QString( "List[%1]" ).arg( scriptDataType )
: scriptDataType; : scriptDataType;
QString fullComment = QString fullComment =
@ -228,20 +238,22 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
if ( pdmChildField ) if ( pdmChildField )
{ {
QString fieldCode = QString( " def %1(self):\n%2\n children = " QString fieldCode =
"self.children(\"%3\", %4)\n return children[0] if " QString( " def %1(self) -> Optional[%4]:\n%2\n children = "
"len(children) > 0 else None\n" ) "self.children(\"%3\", %4)\n return children[0] if "
.arg( snake_field_name ) "len(children) > 0 else None\n" )
.arg( fullComment ) .arg( snake_field_name )
.arg( scriptability->scriptFieldName() ) .arg( fullComment )
.arg( scriptDataType ); .arg( scriptability->scriptFieldName() )
.arg( scriptDataType );
classMethodsGenerated[field->ownerClass()][snake_field_name] = fieldCode; classMethodsGenerated[field->ownerClass()][snake_field_name] = fieldCode;
} }
else else
{ {
QString fieldCode = QString( " def %1(self):\n%2\n return " QString fieldCode = QString( " def %1(self) -> List[%2]:\n%3\n return "
"self.children(\"%3\", %4)\n" ) "self.children(\"%4\", %5)\n" )
.arg( snake_field_name ) .arg( snake_field_name )
.arg( scriptDataType )
.arg( fullComment ) .arg( fullComment )
.arg( scriptability->scriptFieldName() ) .arg( scriptability->scriptFieldName() )
.arg( scriptDataType ); .arg( scriptDataType );
@ -269,8 +281,22 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
QStringList argumentComments; QStringList argumentComments;
outputArgumentStrings.push_back( QString( "\"%1\"" ).arg( methodName ) ); outputArgumentStrings.push_back( QString( "\"%1\"" ).arg( methodName ) );
QString returnDataType = "None";
QString returnComment; QString returnComment;
if ( method->defaultResult() ) returnComment = method->defaultResult()->xmlCapability()->classKeyword(); if ( method->defaultResult() )
{
QString classKeyword = method->defaultResult()->xmlCapability()->classKeyword();
returnComment = classKeyword;
returnDataType = PdmObjectScriptingCapabilityRegister::scriptClassNameFromClassKeyword( classKeyword );
outputArgumentStrings.push_back( QString( "%1" ).arg( returnDataType ) );
if ( method->isNullptrValidResult() )
{
returnDataType = QString( "Optional[%1]" ).arg( returnDataType );
}
}
for ( auto field : arguments ) for ( auto field : arguments )
{ {
@ -279,9 +305,13 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
auto dataType = dataTypeString( field, false ); auto dataType = dataTypeString( field, false );
bool isList = field->xmlCapability()->isVectorField(); bool isList = field->xmlCapability()->isVectorField();
if ( isList ) dataType = "List of " + dataType; if ( isList ) dataType = QString( "List[%1]" ).arg( dataType );
QString defaultValue = getDefaultValue( field ); QString defaultValue = getDefaultValue( field );
if ( defaultValue == "None" )
{
dataType = QString( "Optional[%1]" ).arg( dataType );
}
QString commentOrEnumDescription = field->uiCapability()->uiWhatsThis(); QString commentOrEnumDescription = field->uiCapability()->uiWhatsThis();
@ -293,7 +323,8 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
commentOrEnumDescription = "One of [" + enumTexts.join( ", " ) + "]"; commentOrEnumDescription = "One of [" + enumTexts.join( ", " ) + "]";
} }
inputArgumentStrings.push_back( QString( "%1=%2" ).arg( argumentName ).arg( defaultValue ) ); inputArgumentStrings.push_back(
QString( "%1: %2=%3" ).arg( argumentName ).arg( dataType ).arg( defaultValue ) );
outputArgumentStrings.push_back( QString( "%1=%1" ).arg( argumentName ) ); outputArgumentStrings.push_back( QString( "%1=%1" ).arg( argumentName ) );
argumentComments.push_back( argumentComments.push_back(
QString( "%1 (%2): %3" ).arg( argumentName ).arg( dataType ).arg( commentOrEnumDescription ) ); QString( "%1 (%2): %3" ).arg( argumentName ).arg( dataType ).arg( commentOrEnumDescription ) );
@ -304,12 +335,27 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
.arg( argumentComments.join( "\n " ) ) .arg( argumentComments.join( "\n " ) )
.arg( returnComment ); .arg( returnComment );
QString methodCode = QString( " def %1(self, %2):\n%3\n return " QString methodBody = QString( "self._call_pdm_method_void(%1)" ).arg( outputArgumentStrings.join( ", " ) );
"self._call_pdm_method(%4)\n" ) if ( returnDataType != "None" )
{
if ( method->isNullptrValidResult() )
{
methodBody = QString( "return self._call_pdm_method_return_optional_value(%1)" )
.arg( outputArgumentStrings.join( ", " ) );
}
else
{
methodBody =
QString( "return self._call_pdm_method_return_value(%1)" ).arg( outputArgumentStrings.join( ", " ) );
}
}
QString methodCode = QString( " def %1(self, %2) -> %3:\n%4\n %5\n" )
.arg( snake_method_name ) .arg( snake_method_name )
.arg( inputArgumentStrings.join( ", " ) ) .arg( inputArgumentStrings.join( ", " ) )
.arg( returnDataType )
.arg( fullComment ) .arg( fullComment )
.arg( outputArgumentStrings.join( ", " ) ); .arg( methodBody );
classMethodsGenerated[classKeyword][snake_method_name] = methodCode; classMethodsGenerated[classKeyword][snake_method_name] = methodCode;
} }
@ -320,7 +366,12 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
std::set<QString> classesWritten; std::set<QString> classesWritten;
classesWritten.insert( "PdmObjectBase" ); classesWritten.insert( "PdmObjectBase" );
out << "from __future__ import annotations\n";
out << "from rips.pdmobject import PdmObjectBase\n"; out << "from rips.pdmobject import PdmObjectBase\n";
out << "import PdmObject_pb2\n";
out << "import grpc\n";
out << "from typing import Optional, Dict, List, Type\n";
out << "\n";
for ( std::shared_ptr<PdmObject> object : dummyObjects ) for ( std::shared_ptr<PdmObject> object : dummyObjects )
{ {
@ -367,7 +418,9 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
classCode += classCode +=
QString( " __custom_init__ = None #: Assign a custom init routine to be run at __init__\n\n" ); QString( " __custom_init__ = None #: Assign a custom init routine to be run at __init__\n\n" );
classCode += QString( " def __init__(self, pb2_object=None, channel=None):\n" ); classCode +=
QString( " def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: "
"Optional[grpc.Channel]=None) -> None:\n" );
if ( !scriptSuperClassNames.empty() ) if ( !scriptSuperClassNames.empty() )
{ {
// Own attributes. This initializes a lot of attributes to None. // Own attributes. This initializes a lot of attributes to None.
@ -398,15 +451,16 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
scriptSuperClassNames.push_back( scriptClassName ); scriptSuperClassNames.push_back( scriptClassName );
} }
} }
out << "def class_dict():\n";
out << " classes = {}\n"; out << "def class_dict() -> Dict[str, Type[PdmObjectBase]]:\n";
out << " classes : Dict[str, Type[PdmObjectBase]] = {}\n";
for ( QString classKeyword : classesWritten ) for ( QString classKeyword : classesWritten )
{ {
out << QString( " classes['%1'] = %1\n" ).arg( classKeyword ); out << QString( " classes['%1'] = %1\n" ).arg( classKeyword );
} }
out << " return classes\n\n"; out << " return classes\n\n";
out << "def class_from_keyword(class_keyword):\n"; out << "def class_from_keyword(class_keyword : str) -> Optional[Type[PdmObjectBase]]:\n";
out << " all_classes = class_dict()\n"; out << " all_classes = class_dict()\n";
out << " if class_keyword in all_classes.keys():\n"; out << " if class_keyword in all_classes.keys():\n";
out << " return all_classes[class_keyword]\n"; out << " return all_classes[class_keyword]\n";
@ -449,7 +503,12 @@ QString PdmPythonGenerator::getDefaultValue( PdmFieldHandle* field )
QTextStream valueStream( &valueString ); QTextStream valueStream( &valueString );
scriptability->readFromField( valueStream, true, true ); scriptability->readFromField( valueStream, true, true );
} }
if ( valueString.isEmpty() ) valueString = QString( "\"\"" );
if ( valueString.isEmpty() )
{
valueString = defaultValue;
}
valueString = pythonifyDataValue( valueString ); valueString = pythonifyDataValue( valueString );
defaultValue = valueString; defaultValue = valueString;
@ -482,14 +541,20 @@ QString PdmPythonGenerator::dataTypeString( const PdmFieldHandle* field, bool us
auto scriptability = field->capability<PdmAbstractFieldScriptingCapability>(); auto scriptability = field->capability<PdmAbstractFieldScriptingCapability>();
if ( scriptability && !scriptability->enumScriptTexts().empty() ) return "str"; if ( scriptability && !scriptability->enumScriptTexts().empty() ) return "str";
QString dataType = xmlObj->dataTypeName(); QString dataType = PdmObjectScriptingCapabilityRegister::scriptClassNameFromClassKeyword( xmlObj->dataTypeName() );
std::map<QString, QString> builtins = { { QString::fromStdString( typeid( double ).name() ), "float" }, std::map<QString, QString> builtins = {
{ QString::fromStdString( typeid( float ).name() ), "float" }, { QString::fromStdString( typeid( double ).name() ), "float" },
{ QString::fromStdString( typeid( int ).name() ), "int" }, { QString::fromStdString( typeid( float ).name() ), "float" },
{ QString::fromStdString( typeid( bool ).name() ), "bool" }, { QString::fromStdString( typeid( int ).name() ), "int" },
{ QString::fromStdString( typeid( time_t ).name() ), "time" }, { QString::fromStdString( typeid( bool ).name() ), "bool" },
{ QString::fromStdString( typeid( QString ).name() ), "str" } }; { QString::fromStdString( typeid( time_t ).name() ), "int" },
{ QString::fromStdString( typeid( QString ).name() ), "str" },
{ QString::fromStdString( typeid( cvf::Vec3d ).name() ), "List[float]" },
{ QString::fromStdString( typeid( cvf::Color3f ).name() ), "str" },
{ QString::fromStdString( typeid( caf::FilePath ).name() ), "str" },
{ QString::fromStdString( typeid( std::vector<double> ).name() ), "List[float]" },
};
bool foundBuiltin = false; bool foundBuiltin = false;
for ( auto builtin : builtins ) for ( auto builtin : builtins )

View File

@ -12,6 +12,27 @@ set(CMAKE_CXX_STANDARD 17)
project (TestCafAndVizFwk) project (TestCafAndVizFwk)
# ##############################################################################
# Setup the main platform defines
# ##############################################################################
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_definitions(-DCVF_LINUX)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_definitions(-DCVF_OSX)
elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(_HAS_STD_BYTE 0)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_CXX_FLAGS
"-DCVF_LINUX -pipe -Wextra -Woverloaded-virtual -Wformat -Wno-unused-parameter"
)
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -g3 -O0 -DDEBUG -D_DEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNO_DEBUG")
endif()
find_package(Qt5 COMPONENTS REQUIRED Core Gui OpenGL Widgets) find_package(Qt5 COMPONENTS REQUIRED Core Gui OpenGL Widgets)
set(QT_LIBRARIES Qt5::Core Qt5::Gui Qt5::OpenGL Qt5::Widgets) set(QT_LIBRARIES Qt5::Core Qt5::Gui Qt5::OpenGL Qt5::Widgets)

View File

@ -184,6 +184,7 @@ foreach(rips_proto ${GRPC_PROTO_FILES})
${RESINSIGHT_GRPC_PYTHON_EXECUTABLE} ARGS -m grpc_tools.protoc -I ${RESINSIGHT_GRPC_PYTHON_EXECUTABLE} ARGS -m grpc_tools.protoc -I
"${rips_proto_path}" --python_out "${rips_proto_path}" --python_out
"${GRPC_PYTHON_SOURCE_PATH}/rips/generated" --grpc_python_out "${GRPC_PYTHON_SOURCE_PATH}/rips/generated" --grpc_python_out
"${GRPC_PYTHON_SOURCE_PATH}/rips/generated" --pyi_out
"${GRPC_PYTHON_SOURCE_PATH}/rips/generated" "${rips_proto}" "${GRPC_PYTHON_SOURCE_PATH}/rips/generated" "${rips_proto}"
DEPENDS "${rips_proto}" DEPENDS "${rips_proto}"
COMMENT "Generating ${rips_proto_python} and ${rips_grpc_python}" COMMENT "Generating ${rips_proto_python} and ${rips_grpc_python}"

View File

@ -2,3 +2,4 @@ grpcio
grpcio-tools grpcio-tools
protobuf protobuf
wheel wheel
typing-extensions

View File

@ -17,7 +17,9 @@ from .contour_map import EclipseContourMap, GeoMechContourMap
from .well_log_plot import WellLogPlot from .well_log_plot import WellLogPlot
from .simulation_well import SimulationWell from .simulation_well import SimulationWell
__all__ = [] from typing import List
__all__: List[str] = []
for key in class_dict(): for key in class_dict():
__all__.append(key) __all__.append(key)

View File

@ -37,6 +37,7 @@ result
import builtins import builtins
import grpc import grpc
from typing import List, Tuple
import Case_pb2 import Case_pb2
import Case_pb2_grpc import Case_pb2_grpc
@ -74,7 +75,7 @@ def __custom_init__(self, pb2_object, channel):
@add_method(Case) @add_method(Case)
def __grid_count(self): def __grid_count(self) -> int:
"""Get number of grids in the case""" """Get number of grids in the case"""
try: try:
return self.__case_stub.GetGridCount(self.__request()).count return self.__case_stub.GetGridCount(self.__request()).count
@ -125,7 +126,7 @@ def __generate_property_input_chunks(self, array, parameters):
@add_method(Case) @add_method(Case)
def grid(self, index=0): def grid(self, index: int = 0) -> Grid:
"""Get Grid of a given index """Get Grid of a given index
Arguments: Arguments:
@ -138,7 +139,7 @@ def grid(self, index=0):
@add_method(Case) @add_method(Case)
def grids(self): def grids(self) -> List[Grid]:
"""Get a list of all rips Grid objects in the case """Get a list of all rips Grid objects in the case
Returns: Returns:
@ -166,7 +167,7 @@ def replace(self, new_grid_file):
@add_method(Case) @add_method(Case)
def cell_count(self, porosity_model="MATRIX_MODEL"): def cell_count(self, porosity_model: str = "MATRIX_MODEL") -> int:
"""Get a cell count object containing number of active cells and total number of cells """Get a cell count object containing number of active cells and total number of cells
Arguments: Arguments:
@ -193,7 +194,7 @@ def cell_count(self, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def cell_info_for_active_cells_async(self, porosity_model="MATRIX_MODEL"): def cell_info_for_active_cells_async(self, porosity_model: str = "MATRIX_MODEL"):
"""Get Stream of cell info objects for current case """Get Stream of cell info objects for current case
Arguments: Arguments:
@ -213,7 +214,7 @@ def cell_info_for_active_cells_async(self, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def cell_info_for_active_cells(self, porosity_model="MATRIX_MODEL"): def cell_info_for_active_cells(self, porosity_model: str = "MATRIX_MODEL"):
"""Get list of cell info objects for current case """Get list of cell info objects for current case
Arguments: Arguments:
@ -298,7 +299,7 @@ def reservoir_boundingbox(self):
@add_method(Case) @add_method(Case)
def reservoir_depth_range(self): def reservoir_depth_range(self) -> Tuple[float, float]:
"""Get the reservoir depth range """Get the reservoir depth range
Returns: Returns:
@ -604,7 +605,7 @@ def export_flow_characteristics(
@add_method(Case) @add_method(Case)
def available_properties(self, property_type, porosity_model="MATRIX_MODEL"): def available_properties(self, property_type, porosity_model: str = "MATRIX_MODEL"):
"""Get a list of available properties """Get a list of available properties
For argument details, see :ref:`Result Definition <result-definition-label>` For argument details, see :ref:`Result Definition <result-definition-label>`
@ -626,7 +627,7 @@ def available_properties(self, property_type, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def active_cell_property_async( def active_cell_property_async(
self, property_type, property_name, time_step, porosity_model="MATRIX_MODEL" self, property_type, property_name, time_step, porosity_model: str = "MATRIX_MODEL"
): ):
"""Get a cell property for all active cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all active cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -655,7 +656,7 @@ def active_cell_property_async(
@add_method(Case) @add_method(Case)
def active_cell_property( def active_cell_property(
self, property_type, property_name, time_step, porosity_model="MATRIX_MODEL" self, property_type, property_name, time_step, porosity_model: str = "MATRIX_MODEL"
): ):
"""Get a cell property for all active cells. Sync, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all active cells. Sync, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -681,7 +682,7 @@ def active_cell_property(
@add_method(Case) @add_method(Case)
def selected_cell_property_async( def selected_cell_property_async(
self, property_type, property_name, time_step, porosity_model="MATRIX_MODEL" self, property_type, property_name, time_step, porosity_model: str = "MATRIX_MODEL"
): ):
"""Get a cell property for all selected cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all selected cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -710,7 +711,7 @@ def selected_cell_property_async(
@add_method(Case) @add_method(Case)
def selected_cell_property( def selected_cell_property(
self, property_type, property_name, time_step, porosity_model="MATRIX_MODEL" self, property_type, property_name, time_step, porosity_model: str = "MATRIX_MODEL"
): ):
"""Get a cell property for all selected cells. Sync, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all selected cells. Sync, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -741,7 +742,7 @@ def grid_property_async(
property_name, property_name,
time_step, time_step,
grid_index=0, grid_index=0,
porosity_model="MATRIX_MODEL", porosity_model: str = "MATRIX_MODEL",
): ):
"""Get a cell property for all grid cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all grid cells. Async, so returns an iterator. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -777,7 +778,7 @@ def grid_property(
property_name, property_name,
time_step, time_step,
grid_index=0, grid_index=0,
porosity_model="MATRIX_MODEL", porosity_model: str = "MATRIX_MODEL",
): ):
"""Get a cell property for all grid cells. Synchronous, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>` """Get a cell property for all grid cells. Synchronous, so returns a list. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -808,7 +809,7 @@ def set_active_cell_property_async(
property_type, property_type,
property_name, property_name,
time_step, time_step,
porosity_model="MATRIX_MODEL", porosity_model: str = "MATRIX_MODEL",
): ):
"""Set cell property for all active cells Async. Takes an iterator to the input values. For argument details, see :ref:`Result Definition <result-definition-label>` """Set cell property for all active cells Async. Takes an iterator to the input values. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -835,7 +836,12 @@ def set_active_cell_property_async(
@add_method(Case) @add_method(Case)
def set_active_cell_property( def set_active_cell_property(
self, values, property_type, property_name, time_step, porosity_model="MATRIX_MODEL" self,
values,
property_type,
property_name,
time_step,
porosity_model: str = "MATRIX_MODEL",
): ):
"""Set a cell property for all active cells. For argument details, see :ref:`Result Definition <result-definition-label>` """Set a cell property for all active cells. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -869,7 +875,7 @@ def set_grid_property(
property_name, property_name,
time_step, time_step,
grid_index=0, grid_index=0,
porosity_model="MATRIX_MODEL", porosity_model: str = "MATRIX_MODEL",
): ):
"""Set a cell property for all grid cells. For argument details, see :ref:`Result Definition <result-definition-label>` """Set a cell property for all grid cells. For argument details, see :ref:`Result Definition <result-definition-label>`
@ -989,7 +995,7 @@ def simulation_wells(self):
@add_method(Case) @add_method(Case)
def active_cell_centers_async(self, porosity_model="MATRIX_MODEL"): def active_cell_centers_async(self, porosity_model: str = "MATRIX_MODEL"):
"""Get a cell centers for all active cells. Async, so returns an iterator """Get a cell centers for all active cells. Async, so returns an iterator
Arguments: Arguments:
@ -1007,7 +1013,7 @@ def active_cell_centers_async(self, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def active_cell_centers(self, porosity_model="MATRIX_MODEL"): def active_cell_centers(self, porosity_model: str = "MATRIX_MODEL"):
"""Get a cell centers for all active cells. Synchronous, so returns a list. """Get a cell centers for all active cells. Synchronous, so returns a list.
Arguments: Arguments:
@ -1025,7 +1031,7 @@ def active_cell_centers(self, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def active_cell_corners_async(self, porosity_model="MATRIX_MODEL"): def active_cell_corners_async(self, porosity_model: str = "MATRIX_MODEL"):
"""Get a cell corners for all active cells. Async, so returns an iterator """Get a cell corners for all active cells. Async, so returns an iterator
Arguments: Arguments:
@ -1043,7 +1049,7 @@ def active_cell_corners_async(self, porosity_model="MATRIX_MODEL"):
@add_method(Case) @add_method(Case)
def active_cell_corners(self, porosity_model="MATRIX_MODEL"): def active_cell_corners(self, porosity_model: str = "MATRIX_MODEL"):
"""Get a cell corners for all active cells. Synchronous, so returns a list. """Get a cell corners for all active cells. Synchronous, so returns a list.
Arguments: Arguments:
@ -1287,7 +1293,7 @@ def __generate_nnc_property_input_chunks(self, array, parameters):
@add_method(Case) @add_method(Case)
def set_nnc_connections_values( def set_nnc_connections_values(
self, values, property_name, time_step, porosity_model="MATRIX_MODEL" self, values, property_name, time_step, porosity_model: str = "MATRIX_MODEL"
): ):
"""Set nnc connection values for all connections.. """Set nnc connection values for all connections..

View File

@ -10,11 +10,11 @@ from .resinsight_classes import EclipseContourMap, GeoMechContourMap
@add_method(EclipseContourMap) @add_method(EclipseContourMap)
def export_to_text( def export_to_text(
self, self: EclipseContourMap,
export_file_name="", export_file_name: str = "",
export_local_coordinates=False, export_local_coordinates: bool = False,
undefined_value_label="NaN", undefined_value_label: str = "NaN",
exclude_undefined_values=False, exclude_undefined_values: bool = False,
): ):
"""Export snapshot for the current view """Export snapshot for the current view
@ -37,11 +37,11 @@ def export_to_text(
@add_method(GeoMechContourMap) @add_method(GeoMechContourMap)
def export_to_text( def export_to_text(
self, self: GeoMechContourMap,
export_file_name="", export_file_name: str = "",
export_local_coordinates=False, export_local_coordinates: bool = False,
undefined_value_label="NaN", undefined_value_label: str = "NaN",
exclude_undefined_values=False, exclude_undefined_values: bool = False,
): ):
"""Export snapshot for the current view """Export snapshot for the current view

View File

@ -8,6 +8,12 @@ about Case grids.
import Case_pb2 import Case_pb2
import Grid_pb2 import Grid_pb2
import Grid_pb2_grpc import Grid_pb2_grpc
import Definitions_pb2
from typing import Tuple, Optional, List
from grpc import Channel
from .case import Case
class Grid: class Grid:
@ -16,15 +22,15 @@ class Grid:
:meth:`rips.case.grids()` :meth:`rips.case.grids()`
""" """
def __init__(self, index, case, channel): def __init__(self, index: int, case: Case, channel: Channel) -> None:
self.__channel = channel self.__channel = channel
self.__stub = Grid_pb2_grpc.GridStub(self.__channel) self.__stub = Grid_pb2_grpc.GridStub(self.__channel)
self.case = case self.case: Case = case
self.index = index self.index: int = index
self.cached_dimensions = None self.cached_dimensions = None
def dimensions(self): def dimensions(self) -> Optional[Definitions_pb2.Vec3i]:
"""The dimensions in i, j, k direction """The dimensions in i, j, k direction
Returns: Returns:
@ -52,7 +58,7 @@ class Grid:
for chunk in chunks: for chunk in chunks:
yield chunk yield chunk
def cell_centers(self): def cell_centers(self) -> List[Definitions_pb2.Vec3d]:
"""The cell center for all cells in given grid """The cell center for all cells in given grid
Returns: Returns:
@ -92,7 +98,7 @@ class Grid:
corners.append(center) corners.append(center)
return corners return corners
def property_data_index_from_ijk(self, i, j, k): def property_data_index_from_ijk(self, i: int, j: int, k: int) -> int:
"""Compute property index from 1-based IJK cell address. Cell Property Result data is organized by I, J and K. """Compute property index from 1-based IJK cell address. Cell Property Result data is organized by I, J and K.
property_data_index = dims.i * dims.j * (k - 1) + dims.i * (j - 1) + (i - 1) property_data_index = dims.i * dims.j * (k - 1) + dims.i * (j - 1) + (i - 1)
@ -102,12 +108,12 @@ class Grid:
""" """
dims = self.dimensions() dims = self.dimensions()
if dims:
return int(dims.i * dims.j * (k - 1) + dims.i * (j - 1) + (i - 1))
else:
return -1
property_data_index = dims.i * dims.j * (k - 1) + dims.i * (j - 1) + (i - 1) def cell_count(self) -> int:
return property_data_index
def cell_count(self):
"""Cell count in grid """Cell count in grid
Returns: Returns:
@ -115,7 +121,7 @@ class Grid:
""" """
dims = self.dimensions() dims = self.dimensions()
if dims:
count = dims.i * dims.j * dims.k return int(dims.i * dims.j * dims.k)
else:
return count return 0

View File

@ -5,6 +5,7 @@ The Instance class contained have static methods launch and find for
creating connections to ResInsight creating connections to ResInsight
""" """
from __future__ import annotations
import os import os
import socket import socket
import logging import logging
@ -27,6 +28,9 @@ from .retry_policy import ExponentialBackoffRetryPolicy
from .grpc_retry_interceptor import RetryOnRpcErrorClientInterceptor from .grpc_retry_interceptor import RetryOnRpcErrorClientInterceptor
from .generated.generated_classes import CommandRouter from .generated.generated_classes import CommandRouter
from typing import List, Optional, Tuple
from typing_extensions import Self
class Instance: class Instance:
"""The ResInsight Instance class. Use to launch or find existing ResInsight instances """The ResInsight Instance class. Use to launch or find existing ResInsight instances
@ -40,13 +44,13 @@ class Instance:
""" """
@staticmethod @staticmethod
def __is_port_in_use(port): def __is_port_in_use(port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as my_socket: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as my_socket:
my_socket.settimeout(0.2) my_socket.settimeout(0.2)
return my_socket.connect_ex(("localhost", port)) == 0 return my_socket.connect_ex(("localhost", port)) == 0
@staticmethod @staticmethod
def __is_valid_port(port): def __is_valid_port(port: int) -> bool:
location = "localhost:" + str(port) location = "localhost:" + str(port)
channel = grpc.insecure_channel( channel = grpc.insecure_channel(
location, options=[("grpc.enable_http_proxy", False)] location, options=[("grpc.enable_http_proxy", False)]
@ -59,7 +63,7 @@ class Instance:
return True return True
@staticmethod @staticmethod
def __read_port_number_from_file(file_path): def __read_port_number_from_file(file_path: str) -> int:
retry_count = 0 retry_count = 0
while not os.path.exists(file_path) and retry_count < 60: while not os.path.exists(file_path) and retry_count < 60:
time.sleep(1) time.sleep(1)
@ -75,7 +79,7 @@ class Instance:
return -1 return -1
@staticmethod @staticmethod
def __kill_process(pid): def __kill_process(pid: int) -> None:
""" """
Kill the process with a given pid. Kill the process with a given pid.
""" """
@ -88,11 +92,11 @@ class Instance:
@staticmethod @staticmethod
def launch( def launch(
resinsight_executable="", resinsight_executable: str = "",
console=False, console: bool = False,
launch_port=-1, launch_port: int = -1,
command_line_parameters=None, command_line_parameters: List[str] = [],
): ) -> Optional[Instance]:
"""Launch a new Instance of ResInsight. This requires the environment variable """Launch a new Instance of ResInsight. This requires the environment variable
RESINSIGHT_EXECUTABLE to be set or the parameter resinsight_executable to be provided. RESINSIGHT_EXECUTABLE to be set or the parameter resinsight_executable to be provided.
The RESINSIGHT_GRPC_PORT environment variable can be set to an alternative port number. The RESINSIGHT_GRPC_PORT environment variable can be set to an alternative port number.
@ -110,7 +114,7 @@ class Instance:
Instance: an instance object if it worked. None if not. Instance: an instance object if it worked. None if not.
""" """
requested_port = 50051 requested_port: int = 50051
port_env = os.environ.get("RESINSIGHT_GRPC_PORT") port_env = os.environ.get("RESINSIGHT_GRPC_PORT")
if port_env: if port_env:
requested_port = int(port_env) requested_port = int(port_env)
@ -118,29 +122,25 @@ class Instance:
requested_port = launch_port requested_port = launch_port
if not resinsight_executable: if not resinsight_executable:
resinsight_executable = os.environ.get("RESINSIGHT_EXECUTABLE") resinsight_executable_from_env = os.environ.get("RESINSIGHT_EXECUTABLE")
if not resinsight_executable: if not resinsight_executable_from_env:
print( print(
"ERROR: Could not launch ResInsight because the environment variable" "ERROR: Could not launch ResInsight because the environment variable"
" RESINSIGHT_EXECUTABLE is not set" " RESINSIGHT_EXECUTABLE is not set"
) )
return None return None
else:
resinsight_executable = resinsight_executable_from_env
print("Trying to launch", resinsight_executable) print("Trying to launch", resinsight_executable)
if command_line_parameters is None:
command_line_parameters = []
elif isinstance(command_line_parameters, str):
command_line_parameters = [str]
with tempfile.TemporaryDirectory() as tmp_dir_path: with tempfile.TemporaryDirectory() as tmp_dir_path:
port_number_file = tmp_dir_path + "/portnumber.txt" port_number_file = tmp_dir_path + "/portnumber.txt"
parameters = [ parameters: List[str] = [
"ResInsight", "ResInsight",
"--server", "--server",
requested_port, str(requested_port),
"--portnumberfile", "--portnumberfile",
port_number_file, str(port_number_file),
] + command_line_parameters ] + command_line_parameters
if console: if console:
print("Launching as console app") print("Launching as console app")
@ -163,7 +163,7 @@ class Instance:
return None return None
@staticmethod @staticmethod
def find(start_port=50051, end_port=50071): def find(start_port: int = 50051, end_port: int = 50071) -> Optional[Instance]:
"""Search for an existing Instance of ResInsight by testing ports. """Search for an existing Instance of ResInsight by testing ports.
By default we search from port 50051 to 50071 or if the environment By default we search from port 50051 to 50071 or if the environment
@ -198,7 +198,7 @@ class Instance:
def __execute_command(self, **command_params): def __execute_command(self, **command_params):
return self.commands.Execute(Commands_pb2.CommandParams(**command_params)) return self.commands.Execute(Commands_pb2.CommandParams(**command_params))
def __check_version(self): def __check_version(self) -> Tuple[bool, bool]:
try: try:
major_version_ok = self.major_version() == int( major_version_ok = self.major_version() == int(
RiaVersionInfo.RESINSIGHT_MAJOR_VERSION RiaVersionInfo.RESINSIGHT_MAJOR_VERSION
@ -210,14 +210,14 @@ class Instance:
except grpc.RpcError: except grpc.RpcError:
return False, False return False, False
def __init__(self, port=50051, launched=False): def __init__(self, port: int = 50051, launched: bool = False) -> None:
"""Attempts to connect to ResInsight at aa specific port on localhost """Attempts to connect to ResInsight at a specific port on localhost
Args: Args:
port(int): port number port(int): port number
""" """
logging.basicConfig() logging.basicConfig()
self.location = "localhost:" + str(port) self.location: str = "localhost:" + str(port)
self.channel = grpc.insecure_channel( self.channel = grpc.insecure_channel(
self.location, options=[("grpc.enable_http_proxy", False)] self.location, options=[("grpc.enable_http_proxy", False)]
@ -256,7 +256,9 @@ class Instance:
path = os.getcwd() path = os.getcwd()
self.set_start_dir(path=path) self.set_start_dir(path=path)
def _check_connection_and_version(self, channel, launched, location): def _check_connection_and_version(
self, channel: grpc.Channel, launched: bool, location: str
) -> None:
connection_ok = False connection_ok = False
version_ok = False version_ok = False
@ -288,10 +290,10 @@ class Instance:
self.client_version_string(), self.client_version_string(),
) )
def __version_message(self): def __version_message(self) -> App_pb2.Version:
return self.app.GetVersion(Empty()) return self.app.GetVersion(Empty())
def set_start_dir(self, path): def set_start_dir(self, path: str):
"""Set current start directory """Set current start directory
Arguments: Arguments:
@ -302,7 +304,9 @@ class Instance:
setStartDir=Commands_pb2.FilePathRequest(path=path) setStartDir=Commands_pb2.FilePathRequest(path=path)
) )
def set_export_folder(self, export_type, path, create_folder=False): def set_export_folder(
self, export_type: str, path: str, create_folder: bool = False
):
""" """
Set the export folder used for all export functions Set the export folder used for all export functions
@ -330,7 +334,7 @@ class Instance:
) )
) )
def set_main_window_size(self, width, height): def set_main_window_size(self, width: int, height: int):
""" """
Set the main window size in pixels Set the main window size in pixels
@ -348,7 +352,7 @@ class Instance:
) )
) )
def set_plot_window_size(self, width, height): def set_plot_window_size(self, width: int, height: int):
""" """
Set the plot window size in pixels Set the plot window size in pixels
@ -365,19 +369,19 @@ class Instance:
) )
) )
def major_version(self): def major_version(self) -> int:
"""Get an integer with the major version number""" """Get an integer with the major version number"""
return self.__version_message().major_version return int(self.__version_message().major_version)
def minor_version(self): def minor_version(self) -> int:
"""Get an integer with the minor version number""" """Get an integer with the minor version number"""
return self.__version_message().minor_version return int(self.__version_message().minor_version)
def patch_version(self): def patch_version(self) -> int:
"""Get an integer with the patch version number""" """Get an integer with the patch version number"""
return self.__version_message().patch_version return int(self.__version_message().patch_version)
def version_string(self): def version_string(self) -> str:
"""Get a full version string, i.e. 2019.04.01""" """Get a full version string, i.e. 2019.04.01"""
return ( return (
str(self.major_version()) str(self.major_version())
@ -387,9 +391,9 @@ class Instance:
+ str(self.patch_version()) + str(self.patch_version())
) )
def client_version_string(self): def client_version_string(self) -> str:
"""Get a full version string, i.e. 2019.04.01""" """Get a full version string, i.e. 2019.04.01"""
version_string = RiaVersionInfo.RESINSIGHT_MAJOR_VERSION + "." version_string: str = RiaVersionInfo.RESINSIGHT_MAJOR_VERSION + "."
version_string += RiaVersionInfo.RESINSIGHT_MINOR_VERSION + "." version_string += RiaVersionInfo.RESINSIGHT_MINOR_VERSION + "."
version_string += RiaVersionInfo.RESINSIGHT_PATCH_VERSION version_string += RiaVersionInfo.RESINSIGHT_PATCH_VERSION
return version_string return version_string
@ -399,14 +403,16 @@ class Instance:
print("Telling ResInsight to Exit") print("Telling ResInsight to Exit")
return self.app.Exit(Empty()) return self.app.Exit(Empty())
def is_console(self): def is_console(self) -> bool:
"""Returns true if the connected ResInsight instance is a console app""" """Returns true if the connected ResInsight instance is a console app"""
return self.app.GetRuntimeInfo( return bool(
Empty() self.app.GetRuntimeInfo(Empty()).app_type
).app_type == App_pb2.ApplicationTypeEnum.Value("CONSOLE_APPLICATION") == App_pb2.ApplicationTypeEnum.Value("CONSOLE_APPLICATION")
)
def is_gui(self): def is_gui(self) -> bool:
"""Returns true if the connected ResInsight instance is a GUI app""" """Returns true if the connected ResInsight instance is a GUI app"""
return self.app.GetRuntimeInfo( return bool(
Empty() self.app.GetRuntimeInfo(Empty()).app_type
).app_type == App_pb2.ApplicationTypeEnum.Value("GUI_APPLICATION") == App_pb2.ApplicationTypeEnum.Value("GUI_APPLICATION")
)

View File

@ -0,0 +1,65 @@
[mypy]
# General configuration
python_version = 3.8
show_error_codes = True
show_column_numbers = True
# Try to make mypy as strict as possible
strict = True
ignore_missing_imports = True
implicit_reexport = True
allow_redefinition = False
strict_optional = True
warn_no_return = True
warn_unused_configs = True
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True
no_implicit_optional = True
warn_redundant_casts = True
warn_return_any = True
strict_equality = True
# Explicit exceptions where type definitions are incomplete
[mypy-rips.grpc_retry_interceptor.*]
disable_error_code = misc, no-untyped-def, no-untyped-call
[mypy-rips.pdmobject.*]
disable_error_code = assignment, return-value, call-arg, no-untyped-def, no-untyped-call, no-any-return
[mypy-rips.case.*]
disable_error_code = no-untyped-def, no-any-return
[mypy-rips.grid.*]
disable_error_code = no-untyped-def, no-untyped-call
[mypy-rips.gridcasegroup.*]
disable_error_code = no-any-return, no-untyped-def
[mypy-rips.project.*]
disable_error_code = no-untyped-def, no-any-return, attr-defined
[mypy-rips.well_log_plot.*]
disable_error_code = no-any-return
[mypy-rips.contour_map.*]
disable_error_code = no-redef
[mypy-rips.plot.*]
disable_error_code = no-untyped-def
[mypy-rips.view.*]
disable_error_code = no-untyped-def
[mypy-rips.instance.*]
disable_error_code = no-untyped-def, no-untyped-call, attr-defined
[mypy-rips.simulation_well.*]
disable_error_code = no-any-return, attr-defined
[mypy-rips.generated.generated_classes.*]
disable_error_code = no-any-return

View File

@ -17,27 +17,39 @@ import Commands_pb2
import Commands_pb2_grpc import Commands_pb2_grpc
def camel_to_snake(name): from typing import Any, Callable, TypeVar, Tuple, cast, Union, List, Optional, Type
from typing_extensions import ParamSpec, Self
def camel_to_snake(name: str) -> str:
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
def snake_to_camel(name): def snake_to_camel(name: str) -> str:
return "".join(word.title() for word in name.split("_")) return "".join(word.title() for word in name.split("_"))
def add_method(cls): F = TypeVar("F", bound=Callable[..., Any])
def decorator(func): C = TypeVar("C")
def add_method(cls: C) -> Callable[[F], F]:
def decorator(func: F) -> F:
setattr(cls, func.__name__, func) setattr(cls, func.__name__, func)
return func # returning func means func can still be used normally return func # returning func means func can still be used normally
return decorator return decorator
def add_static_method(cls): P = ParamSpec("P")
def decorator(func): T = TypeVar("T")
def add_static_method(cls: C) -> Callable[[Callable[P, T]], Callable[P, T]]:
def decorator(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
return func(*args, **kwargs) return func(*args, **kwargs)
setattr(cls, func.__name__, wrapper) setattr(cls, func.__name__, wrapper)
@ -52,7 +64,9 @@ class PdmObjectBase:
The ResInsight base class for the Project Data Model The ResInsight base class for the Project Data Model
""" """
def _execute_command(self, **command_params): __custom_init__ = None
def _execute_command(self, **command_params) -> Any:
self.__warnings = [] self.__warnings = []
response, call = self._commands.Execute.with_call( response, call = self._commands.Execute.with_call(
Commands_pb2.CommandParams(**command_params) Commands_pb2.CommandParams(**command_params)
@ -64,7 +78,11 @@ class PdmObjectBase:
return response return response
def __init__(self, pb2_object, channel): def __init__(
self,
pb2_object: Optional[PdmObject_pb2.PdmObject],
channel: Optional[grpc.Channel],
) -> None:
self.__warnings = [] self.__warnings = []
self.__chunk_size = 8160 self.__chunk_size = 8160
@ -91,11 +109,11 @@ class PdmObjectBase:
) )
self.__copy_to_pb2() self.__copy_to_pb2()
def copy_from(self, object): def copy_from(self, obj: object) -> None:
"""Copy attribute values from object to self""" """Copy attribute values from object to self"""
for attribute in dir(object): for attribute in dir(obj):
if not attribute.startswith("__"): if not attribute.startswith("__"):
value = getattr(object, attribute) value = getattr(obj, attribute)
# This is crucial to avoid overwriting methods # This is crucial to avoid overwriting methods
if not callable(value): if not callable(value):
setattr(self, attribute, value) setattr(self, attribute, value)
@ -103,13 +121,13 @@ class PdmObjectBase:
self.__custom_init__(self._pb2_object, self._channel) self.__custom_init__(self._pb2_object, self._channel)
self.update() self.update()
def warnings(self): def warnings(self) -> List[str]:
return self.__warnings return self.__warnings
def has_warnings(self): def has_warnings(self) -> bool:
return len(self.__warnings) > 0 return len(self.__warnings) > 0
def __copy_to_pb2(self): def __copy_to_pb2(self) -> None:
if self._pb2_object is not None: if self._pb2_object is not None:
for snake_kw in dir(self): for snake_kw in dir(self):
if not snake_kw.startswith("_"): if not snake_kw.startswith("_"):
@ -119,15 +137,15 @@ class PdmObjectBase:
camel_kw = snake_to_camel(snake_kw) camel_kw = snake_to_camel(snake_kw)
self.__set_grpc_value(camel_kw, value) self.__set_grpc_value(camel_kw, value)
def pb2_object(self): def pb2_object(self) -> PdmObject_pb2.PdmObject:
"""Private method""" """Private method"""
return self._pb2_object return self._pb2_object
def channel(self): def channel(self) -> grpc.Channel:
"""Private method""" """Private method"""
return self._channel return self._channel
def address(self): def address(self) -> Any:
"""Get the unique address of the PdmObject """Get the unique address of the PdmObject
Returns: Returns:
@ -136,15 +154,15 @@ class PdmObjectBase:
return self._pb2_object.address return self._pb2_object.address
def set_visible(self, visible): def set_visible(self, visible: bool) -> None:
"""Set the visibility of the object in the ResInsight project tree""" """Set the visibility of the object in the ResInsight project tree"""
self._pb2_object.visible = visible self._pb2_object.visible = visible
def visible(self): def visible(self) -> bool:
"""Get the visibility of the object in the ResInsight project tree""" """Get the visibility of the object in the ResInsight project tree"""
return self._pb2_object.visible return bool(self._pb2_object.visible)
def print_object_info(self): def print_object_info(self) -> None:
"""Print the structure and data content of the PdmObject""" """Print the structure and data content of the PdmObject"""
print("=========== " + self.__class__.__name__ + " =================") print("=========== " + self.__class__.__name__ + " =================")
print("Object Attributes: ") print("Object Attributes: ")
@ -164,7 +182,10 @@ class PdmObjectBase:
if not snake_kw.startswith("_") and callable(getattr(self, snake_kw)): if not snake_kw.startswith("_") and callable(getattr(self, snake_kw)):
print(" " + snake_kw) print(" " + snake_kw)
def __convert_from_grpc_value(self, value): Value = Union[bool, str, float, int, "ValueArray"]
ValueArray = List[Value]
def __convert_from_grpc_value(self, value: str) -> Value:
if value.lower() == "false": if value.lower() == "false":
return False return False
if value.lower() == "true": if value.lower() == "true":
@ -183,7 +204,7 @@ class PdmObjectBase:
return self.__makelist(value) return self.__makelist(value)
return value return value
def __convert_to_grpc_value(self, value): def __convert_to_grpc_value(self, value: Any) -> str:
if isinstance(value, bool): if isinstance(value, bool):
if value: if value:
return "true" return "true"
@ -197,15 +218,15 @@ class PdmObjectBase:
return "[" + ", ".join(list_of_values) + "]" return "[" + ", ".join(list_of_values) + "]"
return str(value) return str(value)
def __get_grpc_value(self, camel_keyword): def __get_grpc_value(self, camel_keyword: str) -> Value:
return self.__convert_from_grpc_value( return self.__convert_from_grpc_value(
self._pb2_object.parameters[camel_keyword] self._pb2_object.parameters[camel_keyword]
) )
def __set_grpc_value(self, camel_keyword, value): def __set_grpc_value(self, camel_keyword: str, value: str) -> None:
self._pb2_object.parameters[camel_keyword] = self.__convert_to_grpc_value(value) self._pb2_object.parameters[camel_keyword] = self.__convert_to_grpc_value(value)
def set_value(self, snake_keyword, value): def set_value(self, snake_keyword: str, value: object) -> None:
"""Set the value associated with the provided keyword and updates ResInsight """Set the value associated with the provided keyword and updates ResInsight
Arguments: Arguments:
@ -217,10 +238,10 @@ class PdmObjectBase:
setattr(self, snake_keyword, value) setattr(self, snake_keyword, value)
self.update() self.update()
def __islist(self, value): def __islist(self, value: str) -> bool:
return value.startswith("[") and value.endswith("]") return value.startswith("[") and value.endswith("]")
def __makelist(self, list_string): def __makelist(self, list_string: str) -> Value:
list_string = list_string.lstrip("[") list_string = list_string.lstrip("[")
list_string = list_string.rstrip("]") list_string = list_string.rstrip("]")
if not list_string: if not list_string:
@ -232,7 +253,13 @@ class PdmObjectBase:
values.append(self.__convert_from_grpc_value(string)) values.append(self.__convert_from_grpc_value(string))
return values return values
def __from_pb2_to_resinsight_classes(self, pb2_object_list, super_class_definition): D = TypeVar("D")
def __from_pb2_to_resinsight_classes(
self,
pb2_object_list: List[PdmObject_pb2.PdmObject],
super_class_definition: Type[D],
) -> List[D]:
pdm_object_list = [] pdm_object_list = []
from .generated.generated_classes import class_from_keyword from .generated.generated_classes import class_from_keyword
@ -241,13 +268,15 @@ class PdmObjectBase:
if child_class_definition is None: if child_class_definition is None:
child_class_definition = super_class_definition child_class_definition = super_class_definition
assert child_class_definition is not None
pdm_object = child_class_definition( pdm_object = child_class_definition(
pb2_object=pb2_object, channel=self.channel() pb2_object=pb2_object, channel=self.channel()
) )
pdm_object_list.append(pdm_object) pdm_object_list.append(pdm_object)
return pdm_object_list return pdm_object_list
def descendants(self, class_definition): def descendants(self, class_definition: Type[D]) -> List[D]:
"""Get a list of all project tree descendants matching the class keyword """Get a list of all project tree descendants matching the class keyword
Arguments: Arguments:
class_definition[class]: A class definition matching the type of class wanted class_definition[class]: A class definition matching the type of class wanted
@ -269,7 +298,7 @@ class PdmObjectBase:
return [] # Valid empty result return [] # Valid empty result
raise e raise e
def children(self, child_field, class_definition): def children(self, child_field: str, class_definition: Type[D]) -> List[D]:
"""Get a list of all direct project tree children inside the provided child_field """Get a list of all direct project tree children inside the provided child_field
Arguments: Arguments:
child_field[str]: A field name child_field[str]: A field name
@ -287,7 +316,9 @@ class PdmObjectBase:
return [] return []
raise e raise e
def add_new_object(self, class_definition, child_field=""): def add_new_object(
self, class_definition: Type[D], child_field: str = ""
) -> Optional[D]:
"""Create and add an object to the specified child field """Create and add an object to the specified child field
Arguments: Arguments:
class_definition[class]: Class definition of the object to create class_definition[class]: Class definition of the object to create
@ -311,18 +342,18 @@ class PdmObjectBase:
child_class_definition = class_from_keyword(pb2_object.class_keyword) child_class_definition = class_from_keyword(pb2_object.class_keyword)
if child_class_definition is None: if child_class_definition is None:
child_class_definition = class_keyword child_class_definition = class_definition
assert child_class_definition.__name__ == class_definition.__name__
pdm_object = class_definition(pb2_object=pb2_object, channel=self.channel())
pdm_object = child_class_definition(
pb2_object=pb2_object, channel=self.channel()
)
return pdm_object return pdm_object
except grpc.RpcError as e: except grpc.RpcError as e:
if e.code() == grpc.StatusCode.NOT_FOUND: if e.code() == grpc.StatusCode.NOT_FOUND:
return None return None
raise e raise e
def ancestor(self, class_definition): def ancestor(self, class_definition: Type[D]) -> Optional[D]:
"""Find the first ancestor that matches the provided class_keyword """Find the first ancestor that matches the provided class_keyword
Arguments: Arguments:
class_definition[class]: A class definition matching the type of class wanted class_definition[class]: A class definition matching the type of class wanted
@ -330,35 +361,27 @@ class PdmObjectBase:
assert inspect.isclass(class_definition) assert inspect.isclass(class_definition)
class_keyword = class_definition.__name__ class_keyword = class_definition.__name__
from .generated.generated_classes import class_from_keyword
request = PdmObject_pb2.PdmParentObjectRequest( request = PdmObject_pb2.PdmParentObjectRequest(
object=self._pb2_object, parent_keyword=class_keyword object=self._pb2_object, parent_keyword=class_keyword
) )
try: try:
pb2_object = self._pdm_object_stub.GetAncestorPdmObject(request) pb2_object = self._pdm_object_stub.GetAncestorPdmObject(request)
child_class_definition = class_from_keyword(pb2_object.class_keyword) pdm_object = class_definition(pb2_object=pb2_object, channel=self.channel())
if child_class_definition is None:
child_class_definition = class_definition
pdm_object = child_class_definition(
pb2_object=pb2_object, channel=self.channel()
)
return pdm_object return pdm_object
except grpc.RpcError as e: except grpc.RpcError as e:
if e.code() == grpc.StatusCode.NOT_FOUND: if e.code() == grpc.StatusCode.NOT_FOUND:
return None return None
raise e raise e
def _call_get_method_async(self, method_name): def _call_get_method_async(self, method_name: str):
request = PdmObject_pb2.PdmObjectGetterRequest( request = PdmObject_pb2.PdmObjectGetterRequest(
object=self._pb2_object, method=method_name object=self._pb2_object, method=method_name
) )
for chunk in self._pdm_object_stub.CallPdmObjectGetter(request): for chunk in self._pdm_object_stub.CallPdmObjectGetter(request):
yield chunk yield chunk
def _call_get_method(self, method_name): def _call_get_method(self, method_name: str):
all_values = [] all_values = []
generator = self._call_get_method_async(method_name) generator = self._call_get_method_async(method_name)
for chunk in generator: for chunk in generator:
@ -413,7 +436,7 @@ class PdmObjectBase:
chunk = PdmObject_pb2.PdmObjectSetterChunk() chunk = PdmObject_pb2.PdmObjectSetterChunk()
yield chunk yield chunk
def _call_set_method(self, method_name, values): def _call_set_method(self, method_name: str, values) -> None:
method_request = PdmObject_pb2.PdmObjectGetterRequest( method_request = PdmObject_pb2.PdmObjectGetterRequest(
object=self._pb2_object, method=method_name object=self._pb2_object, method=method_name
) )
@ -422,7 +445,42 @@ class PdmObjectBase:
if reply.accepted_value_count < len(values): if reply.accepted_value_count < len(values):
raise IndexError raise IndexError
def _call_pdm_method(self, method_name, **kwargs): def _call_pdm_method_void(self, method_name: str, **kwargs: Any) -> None:
pb2_params = PdmObject_pb2.PdmObject(class_keyword=method_name)
for key, value in kwargs.items():
pb2_params.parameters[snake_to_camel(key)] = self.__convert_to_grpc_value(
value
)
request = PdmObject_pb2.PdmObjectMethodRequest(
object=self._pb2_object, method=method_name, params=pb2_params
)
self._pdm_object_stub.CallPdmObjectMethod(request)
X = TypeVar("X")
def _call_pdm_method_return_value(
self, method_name: str, class_definition: Type[X], **kwargs: Any
) -> X:
pb2_params = PdmObject_pb2.PdmObject(class_keyword=method_name)
for key, value in kwargs.items():
pb2_params.parameters[snake_to_camel(key)] = self.__convert_to_grpc_value(
value
)
request = PdmObject_pb2.PdmObjectMethodRequest(
object=self._pb2_object, method=method_name, params=pb2_params
)
pb2_object = self._pdm_object_stub.CallPdmObjectMethod(request)
pdm_object = class_definition(pb2_object=pb2_object, channel=self.channel())
return pdm_object
O = TypeVar("O")
def _call_pdm_method_return_optional_value(
self, method_name: str, class_definition: Type[O], **kwargs: Any
) -> Optional[O]:
pb2_params = PdmObject_pb2.PdmObject(class_keyword=method_name) pb2_params = PdmObject_pb2.PdmObject(class_keyword=method_name)
for key, value in kwargs.items(): for key, value in kwargs.items():
pb2_params.parameters[snake_to_camel(key)] = self.__convert_to_grpc_value( pb2_params.parameters[snake_to_camel(key)] = self.__convert_to_grpc_value(
@ -440,12 +498,11 @@ class PdmObjectBase:
if child_class_definition is None: if child_class_definition is None:
return None return None
pdm_object = child_class_definition( assert class_definition.__name__ == child_class_definition.__name__
pb2_object=pb2_object, channel=self.channel() pdm_object = class_definition(pb2_object=pb2_object, channel=self.channel())
)
return pdm_object return pdm_object
def update(self): def update(self) -> None:
"""Sync all fields from the Python Object to ResInsight""" """Sync all fields from the Python Object to ResInsight"""
self.__copy_to_pb2() self.__copy_to_pb2()
if self._pdm_object_stub is not None: if self._pdm_object_stub is not None:

View File

@ -17,16 +17,18 @@ from Definitions_pb2 import Empty
import Project_pb2_grpc import Project_pb2_grpc
import Project_pb2 import Project_pb2
import PdmObject_pb2 import PdmObject_pb2
from .resinsight_classes import Project, PlotWindow, WellPath, SummaryCase from .resinsight_classes import Project, PlotWindow, WellPath, SummaryCase, Reservoir
from typing import Optional, List
@add_method(Project) @add_method(Project)
def __custom_init__(self, pb2_object, channel): def __custom_init__(self, pb2_object, channel: grpc.Channel) -> None:
self._project_stub = Project_pb2_grpc.ProjectStub(self._channel) self._project_stub = Project_pb2_grpc.ProjectStub(self._channel)
@add_static_method(Project) @add_static_method(Project)
def create(channel): def create(channel: grpc.Channel) -> Project:
project_stub = Project_pb2_grpc.ProjectStub(channel) project_stub = Project_pb2_grpc.ProjectStub(channel)
pb2_object = project_stub.GetPdmObject(Empty()) pb2_object = project_stub.GetPdmObject(Empty())
return Project(pb2_object, channel) return Project(pb2_object, channel)
@ -56,13 +58,13 @@ def save(self, path=""):
@add_method(Project) @add_method(Project)
def close(self): def close(self) -> None:
"""Close the current project (and open new blank project)""" """Close the current project (and open new blank project)"""
self._execute_command(closeProject=Empty()) self._execute_command(closeProject=Empty())
@add_method(Project) @add_method(Project)
def load_case(self, path, grid_only=False): def load_case(self: Project, path: str, grid_only: bool = False) -> Reservoir:
"""Load a new grid case from the given file path """Load a new grid case from the given file path
Arguments: Arguments:
@ -77,7 +79,7 @@ def load_case(self, path, grid_only=False):
@add_method(Project) @add_method(Project)
def selected_cases(self): def selected_cases(self) -> List[Case]:
"""Get a list of all grid cases selected in the project tree """Get a list of all grid cases selected in the project tree
Returns: Returns:
@ -91,17 +93,17 @@ def selected_cases(self):
@add_method(Project) @add_method(Project)
def cases(self): def cases(self: Project) -> List[Reservoir]:
"""Get a list of all grid cases in the project """Get a list of all grid cases in the project
Returns: Returns:
A list of :class:`rips.generated.generated_classes.Case` A list of :class:`rips.generated.generated_classes.Case`
""" """
return self.descendants(Case) return self.descendants(Reservoir)
@add_method(Project) @add_method(Project)
def case(self, case_id): def case(self: Project, case_id: int) -> Optional[Reservoir]:
"""Get a specific grid case from the provided case Id """Get a specific grid case from the provided case Id
Arguments: Arguments:

View File

View File

@ -6,7 +6,7 @@ import random
class RetryPolicy(abc.ABC): class RetryPolicy(abc.ABC):
@abc.abstractmethod @abc.abstractmethod
def sleep(self, retry_num): def sleep(self, retry_num: int) -> None:
""" """
How long to sleep in milliseconds. How long to sleep in milliseconds.
:param retry_num: the number of retry (starting from zero) :param retry_num: the number of retry (starting from zero)
@ -14,14 +14,14 @@ class RetryPolicy(abc.ABC):
assert retry_num >= 0 assert retry_num >= 0
@abc.abstractmethod @abc.abstractmethod
def time_out_message(self): def time_out_message(self) -> str:
""" """
Generate a error message for user on time out. Generate a error message for user on time out.
""" """
pass pass
@abc.abstractmethod @abc.abstractmethod
def num_retries(self): def num_retries(self) -> int:
""" """
Max number retries. Max number retries.
""" """
@ -29,29 +29,34 @@ class RetryPolicy(abc.ABC):
class FixedRetryPolicy(RetryPolicy): class FixedRetryPolicy(RetryPolicy):
def __init__(self, sleep_time=1000, max_num_retries=10): def __init__(self, sleep_time: int = 1000, max_num_retries: int = 10):
""" """
Create a fixed time retry policy. Create a fixed time retry policy.
:param sleep_time: time to sleep in milliseconds. :param sleep_time: time to sleep in milliseconds.
:param max_num_retries: max number of retries. :param max_num_retries: max number of retries.
""" """
self.sleep_time = sleep_time self.sleep_time: int = sleep_time
self.max_num_retries = max_num_retries self.max_num_retries: int = max_num_retries
def sleep(self, retry_num): def sleep(self, retry_num: int) -> None:
time.sleep(self.sleep_time / 1000) time.sleep(self.sleep_time / 1000)
def time_out_message(self): def time_out_message(self) -> str:
return "Tried {} times with {} milliseconds apart.".format( return "Tried {} times with {} milliseconds apart.".format(
self.max_num_retries, self.sleep_time self.max_num_retries, self.sleep_time
) )
def num_retries(self): def num_retries(self) -> int:
return self.max_num_retries return self.max_num_retries
class ExponentialBackoffRetryPolicy(RetryPolicy): class ExponentialBackoffRetryPolicy(RetryPolicy):
def __init__(self, min_backoff=200, max_backoff=10000, max_num_retries=20): def __init__(
self,
min_backoff: int = 200,
max_backoff: int = 10000,
max_num_retries: int = 20,
):
""" """
Create a truncated exponential backoff policy. Create a truncated exponential backoff policy.
See: https://en.wikipedia.org/wiki/Exponential_backoff See: https://en.wikipedia.org/wiki/Exponential_backoff
@ -59,12 +64,12 @@ class ExponentialBackoffRetryPolicy(RetryPolicy):
:param max_backoff: maximum time to sleep in milliseconds. :param max_backoff: maximum time to sleep in milliseconds.
:param max_num_retries: max number of retries. :param max_num_retries: max number of retries.
""" """
self.min_backoff = min_backoff self.min_backoff: int = min_backoff
self.max_backoff = max_backoff self.max_backoff: int = max_backoff
self.max_num_retries = max_num_retries self.max_num_retries: int = max_num_retries
self.multiplier = 2 self.multiplier: int = 2
def sleep(self, retry_num): def sleep(self, retry_num: int) -> None:
# Add a random component to avoid synchronized retries # Add a random component to avoid synchronized retries
wiggle = random.randint(0, 100) wiggle = random.randint(0, 100)
sleep_ms = min( sleep_ms = min(
@ -72,12 +77,12 @@ class ExponentialBackoffRetryPolicy(RetryPolicy):
) )
time.sleep(sleep_ms / 1000) time.sleep(sleep_ms / 1000)
def time_out_message(self): def time_out_message(self) -> str:
return ( return (
"Tried {} times with increasing delay (from {} to {} milliseconds).".format( "Tried {} times with increasing delay (from {} to {} milliseconds).".format(
self.max_num_retries, self.min_backoff, self.max_backoff self.max_num_retries, self.min_backoff, self.max_backoff
) )
) )
def num_retries(self): def num_retries(self) -> int:
return self.max_num_retries return self.max_num_retries

View File

@ -8,21 +8,27 @@ import SimulationWell_pb2_grpc
import Properties_pb2 import Properties_pb2
import Properties_pb2_grpc import Properties_pb2_grpc
import PdmObject_pb2
from .resinsight_classes import SimulationWell from .resinsight_classes import SimulationWell
from .case import Case
from .pdmobject import PdmObjectBase, add_method from .pdmobject import PdmObjectBase, add_method
import rips.case from typing import List, Optional
@add_method(SimulationWell) @add_method(SimulationWell)
def __custom_init__(self, pb2_object, channel): def __custom_init__(
self._simulation_well_stub = SimulationWell_pb2_grpc.SimulationWellStub(channel) self: SimulationWell, pb2_object: PdmObject_pb2.PdmObject, channel: grpc.Channel
) -> None:
self.__simulation_well_stub = SimulationWell_pb2_grpc.SimulationWellStub(channel)
@add_method(SimulationWell) @add_method(SimulationWell)
def status(self, timestep): def status(
self: SimulationWell, timestep: int
) -> List[SimulationWell_pb2.SimulationWellStatus]:
"""Get simulation well status """Get simulation well status
**SimulationWellStatus class description**:: **SimulationWellStatus class description**::
@ -39,11 +45,13 @@ def status(self, timestep):
sim_well_request = SimulationWell_pb2.SimulationWellRequest( sim_well_request = SimulationWell_pb2.SimulationWellRequest(
case_id=self.case().id, well_name=self.name, timestep=timestep case_id=self.case().id, well_name=self.name, timestep=timestep
) )
return self._simulation_well_stub.GetSimulationWellStatus(sim_well_request) return self.__simulation_well_stub.GetSimulationWellStatus(sim_well_request)
@add_method(SimulationWell) @add_method(SimulationWell)
def cells(self, timestep): def cells(
self: SimulationWell, timestep: int
) -> List[SimulationWell_pb2.SimulationWellCellInfo]:
"""Get reservoir cells the simulation well is defined for """Get reservoir cells the simulation well is defined for
**SimulationWellCellInfo class description**:: **SimulationWellCellInfo class description**::
@ -66,9 +74,9 @@ def cells(self, timestep):
sim_well_request = SimulationWell_pb2.SimulationWellRequest( sim_well_request = SimulationWell_pb2.SimulationWellRequest(
case_id=self.case().id, well_name=self.name, timestep=timestep case_id=self.case().id, well_name=self.name, timestep=timestep
) )
return self._simulation_well_stub.GetSimulationWellCells(sim_well_request).data return self.__simulation_well_stub.GetSimulationWellCells(sim_well_request).data
@add_method(SimulationWell) @add_method(SimulationWell)
def case(self): def case(self: SimulationWell) -> Optional[Case]:
return self.ancestor(rips.case.Case) return self.ancestor(Case)

View File

@ -140,10 +140,6 @@ def test_PdmObject(rips_instance, initialize_test):
assert case.__class__.__name__ == "EclipseCase" assert case.__class__.__name__ == "EclipseCase"
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason="Brugge is currently exceptionally slow on Linux",
)
def test_brugge_0010(rips_instance, initialize_test): def test_brugge_0010(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real10/BRUGGE_0010.EGRID" case_path = dataroot.PATH + "/Case_with_10_timesteps/Real10/BRUGGE_0010.EGRID"
case = rips_instance.project.load_case(path=case_path) case = rips_instance.project.load_case(path=case_path)
@ -157,10 +153,6 @@ def test_brugge_0010(rips_instance, initialize_test):
assert len(days_since_start) == 11 assert len(days_since_start) == 11
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason="Brugge is currently exceptionally slow on Linux",
)
def test_replaceCase(rips_instance, initialize_test): def test_replaceCase(rips_instance, initialize_test):
project = rips_instance.project.open( project = rips_instance.project.open(
dataroot.PATH + "/TEST10K_FLT_LGR_NNC/10KWithWellLog.rsp" dataroot.PATH + "/TEST10K_FLT_LGR_NNC/10KWithWellLog.rsp"
@ -192,10 +184,6 @@ def test_loadNonExistingCase(rips_instance, initialize_test):
assert rips_instance.project.load_case(case_path) assert rips_instance.project.load_case(case_path)
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason="Brugge is currently exceptionally slow on Linux",
)
def test_exportFlowCharacteristics(rips_instance, initialize_test): def test_exportFlowCharacteristics(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID" case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
case = rips_instance.project.load_case(case_path) case = rips_instance.project.load_case(case_path)

View File

@ -3,8 +3,6 @@ import os
from typing import Any, Dict, List, TypedDict from typing import Any, Dict, List, TypedDict
import math import math
from generated.generated_classes import Case
sys.path.insert(1, os.path.join(sys.path[0], "../../")) sys.path.insert(1, os.path.join(sys.path[0], "../../"))
import rips import rips
@ -69,7 +67,7 @@ def test_10k(rips_instance, initialize_test):
check_corner(cell_corners[cell_index].c7, expected_corners[7]) check_corner(cell_corners[cell_index].c7, expected_corners[7])
def check_reek_grid_box(case: Case): def check_reek_grid_box(case: rips.Case):
assert len(case.grids()) == 1 assert len(case.grids()) == 1
grid = case.grid(index=0) grid = case.grid(index=0)
@ -107,7 +105,7 @@ def test_load_grdecl_grid(rips_instance, initialize_test):
def verify_load_grid_and_separate_properties( def verify_load_grid_and_separate_properties(
case: Case, property_name_and_paths: NameAndPath case: rips.Reservoir, property_name_and_paths: NameAndPath
): ):
# Load case without properties # Load case without properties
available_properties = case.available_properties("INPUT_PROPERTY") available_properties = case.available_properties("INPUT_PROPERTY")

View File

@ -54,10 +54,6 @@ def test_well_log_plots(rips_instance, initialize_test):
assert plot2.depth_type == "TRUE_VERTICAL_DEPTH_RKB" assert plot2.depth_type == "TRUE_VERTICAL_DEPTH_RKB"
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason="Brugge is currently exceptionally slow on Linux",
)
def test_loadGridCaseGroup(rips_instance, initialize_test): def test_loadGridCaseGroup(rips_instance, initialize_test):
case_paths = [] case_paths = []
case_paths.append(dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID") case_paths.append(dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID")

View File

@ -10,10 +10,6 @@ import rips
import dataroot import dataroot
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason="Brugge is currently exceptionally slow on Linux",
)
def test_create_and_export_surface(rips_instance, initialize_test): def test_create_and_export_surface(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID" case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
case = rips_instance.project.load_case(path=case_path) case = rips_instance.project.load_case(path=case_path)

View File

@ -8,17 +8,19 @@ from .plot import Plot
from .pdmobject import PdmObjectBase, add_method from .pdmobject import PdmObjectBase, add_method
from .resinsight_classes import WellLogPlot from .resinsight_classes import WellLogPlot
from typing import List
@add_method(WellLogPlot) @add_method(WellLogPlot)
def export_data_as_las( def export_data_as_las(
self, self: WellLogPlot,
export_folder, export_folder: str,
file_prefix="", file_prefix: str = "",
export_tvdrkb=False, export_tvdrkb: bool = False,
capitalize_file_names=False, capitalize_file_names: bool = False,
resample_interval=0.0, resample_interval: float = 0.0,
convert_to_standard_units=False, convert_to_standard_units: bool = False,
): ) -> List[str]:
"""Export LAS file(s) for the current plot """Export LAS file(s) for the current plot
Arguments: Arguments:
@ -48,8 +50,11 @@ def export_data_as_las(
@add_method(WellLogPlot) @add_method(WellLogPlot)
def export_data_as_ascii( def export_data_as_ascii(
self, export_folder, file_prefix="", capitalize_file_names=False self: WellLogPlot,
): export_folder: str,
file_prefix: str = "",
capitalize_file_names: bool = False,
) -> List[str]:
"""Export LAS file(s) for the current plot """Export LAS file(s) for the current plot
Arguments: Arguments:

View File

@ -4,7 +4,7 @@ with open('README.md') as f:
readme = f.read() readme = f.read()
with open('LICENSE') as f: with open('LICENSE') as f:
license = f.read() license = f.read()
RIPS_DIST_VERSION = '2' RIPS_DIST_VERSION = '2'
@ -18,6 +18,6 @@ setup(
url='http://www.resinsight.org', url='http://www.resinsight.org',
license=license, license=license,
packages=['rips'], packages=['rips'],
package_data={'rips': ['*.py', 'generated/*.py', 'PythonExamples/*.py', 'tests/*.py']}, package_data={'rips': ['py.typed', '*.py', 'generated/*.py', 'PythonExamples/*.py', 'tests/*.py']},
install_requires=['grpcio', 'protobuf', 'wheel'] install_requires=['grpcio', 'protobuf', 'wheel']
) )