Migration Guide dsviper to Viper

Introduction

dsviper is the Python extension module for Viper.

dsviper is just a thin wrapper over Viper's C++ classes and implements the seamless interop with Python's native objects str, real, bool, dict, list, ...

The wrapper tries to expose the C++ API through Python's idioms and hide some subsystem mechanisms to provide a simpler API for Pythonist.

The Python API hides the use of temporary intermediate objects to provide the result (the facade pattern).

DSMDefinitions / Assemble and Parse.

The namespace Viper::DSMHelper implements many tools to assemble and parse DSM Definitions,

Only the high-level tools are exposed in Viper.

dsviper Viper C++
DSMBuilder.assemble(...) Viper::DSMHelper::assemble(...)
builder.parse() Viper::DSMHelper::parse(...)

Viper Value

User-friendly tools to create values.

dsviper Viper C++
Value.create(...) Viper::Value::create(...)
Value.copy(...) Viper::Value::copy(...)
ValueUUId.create(...) Viper::ValueUUId::make(...)
ValueBlobId(...) Viper::ValueBlobId::make(...)
ValueKey.create(...) Viper::ValueKey::make(...)

Encoder/Decoder tools for StreamCodec

Encode and decode Viper's concepts with the stream codec system

dsviper Viper C++
Codec.STREAM_BINARY Viper::StreamBinaryCodec::Instance()
Codec.STREAM_TOKEN_BINARY Viper::StreamTokenBinaryCodec::Instance()
Codec.query(...) Viper::Codec::query(...)
Codec.check(...) Viper::Coded::check(...)
Type.encode(...) Viper::DefinitionsTypeEncoder::encode(...)
Type.decode(...) Viper::DefinitionsTypeDecoder::decode(...)
Value.encode(...) Viper::ValueEncoder::encode(...)
Value.decode(...) Viper::ValueDecoder::decode(...)
PathConst.encode(self, ...) Viper::PathEncoder::encode(...)
Path.decode(...) Viper::PathDecoder::decode(...)
DefinitionsConst.encode(self,...) Viper::DefinitionsEncoder::encode(...)
Definitions.decode_definitions(...) Viper::DefinitionsDecoder::decode(...)
DSMDefinitions.encode(self, ...) Viper::DSMDefinitionEncoder::encode(...)
DSMDefinitions.decode(...) Viper::DSMDefinitionsDecoder::decode(...)

Encoder/Decoder Tools for JSON and bson

Encode and decode Viper's concepts with JSON and bson.

dsviper Viper C++
DSMDefinitions.json_encode(self, ...) Viper::JsonDSMDefinitionEncoder::json_encode(...)
DSMDefinitions.json_decode(...) Viper::JsonDSMDefinitionDecoder::json_decode(...)
DSMDefinitions.bson_encode(self Viper::JsonDSMDefinitionsEncoder::encode(...)
DSMDefinitions.bson_decode(...) Viper::JsonDSMDefinitionsDecoder::decode(...)
Value.json_encode(...) Viper::JsonValueEncoder::json_encode(...)
Value.json_decode(...) Viper::JsonValueDecoder::json_decode(...)
Value.bson_encode(...) Viper::JsonValueEncoder::bson_encode(...)
Value.bson_decode(...) Viper::JsonValueDecoder::bson_decode(...)

Encoder/Decoder Tools for base64

dsviper Viper C++
Blob.base64_encode(self) Viper::Base64::encode(...)
Blob.base64_decode(...) Viper::Base64::decode(...)

Examples of Python API vs. C++ API

Since Viper works at the Data Level, the API is trivial and based on getters, setters and converters.

Many Viper objects are also immutable.

Assemble and parse DSMDefinitions from a file

Python

# Python's idiom (return a tuple).
>>> report, dsm_definitions, definitions = DSMBuilder.assemble('file.dsm'').parse()

>>> if report.has_error():       # property for value (no code)
>>>   for e in report.errors():  # method for container (code for convertion)
>>>     print e                  # special method __repr__

C++

// Viper::DSMHelper and related Viper's classes
auto const builder DSMHelper::assemble('file.dsm')
auto const report {DSMParseReport::make()}
std::shared_ptr<DSMDefinitions> dsm_definitions;
std::shared_ptr<Definitions> definitions;
auto status{DSMHelper::parse(builder, report, dsm_definitions, definitions)};

if (report.hasError())                                 
    for (auto const & e: report.errors())              
        std::cout << e->representation() << std::endl; 

Convert a DSMDefinitions to a Definitions

Python

# DSMDefinition to Definitions
>>> definitions = dsm_definitions.to_definition() # most plausible pythonist usage

C++

// DSMDefinition to Definitions
auto const definitions {DSMDefinitionsToDefinitions::convert(dsm_definitions)};

Viper Type and Value

Using the Viper C++ Type and Value system is only necessary for application based on dynamic introspection, like the Commit Database Editor or Database Commit Editor (Qt).

Python

# Type and Value
>>> type = TypeVector(TypeFloat())
>>> vector = make(t)
>>> v = 3.0          # Python native float
>>> vector.append(v) # Implicit convertion to Viper::ValueFloat
>>> print(vector.description())

C++

// Type and Value
auto const type{TypeVector::make(TypeFloat::Instance())};
auto const vector{ValueVector::make(type)};
auto const v{ValueFloat::make(3.0f)}; // Explicit construction of Viper::Float
vector->append(v)
std::cout << vector->description() << std::endl;

The C++ API for containers was designed to simplify the wrapping of special methods defined by the Object Model of the Python/C API.

Interfaces

When a C++ object is used through a specific interface, we must get the wrapper for the interface.

Python

# Open the Databases and synchronize
source = CommitDatabase::open('source.db')
target = CommitDatabase::open('target.db')
s = CommitSynchronizer(source.databasing(), target.databasing()) # Explicit Interface

C++

// Open the Databases and synchronize
auto const source{DatabaseCommit::open('source.db')};
auto const target{DatabaseCommit::open('target.db')};
auto const sync{CommitSynchronizer::make(source, target)}; // Implicit Interface

Exposed Interfaces in dsviper

  • Database / Databasing --> db.databasing()
  • CommitDatabase / CommitDatabasing --> db.commit_databasing()
  • CommitState / CommitGetting --> s.commit_getting()
  • CommitMutableState / CommitGetting & CommitMutating --> ms.commit_getting(), ms.commit_mutating()
  • DocumentGetting = o.getting().document_getting()
  • BlobGetting = db.databasing().blob_getting()

Definition and DefinitionsConst

When a C++ object is used through its const interface, we must get the wrapper for the interface.

Python

# Definitions
defs = Definitions()
defs.create_concept("C");
db.extend_definitions(defs.const()) // Explicit wrapper for std::shared_ptr<Definitions const>

C++

auto const defs {Definitions::make()};
defs->createConcept("C");
db->extendDefinitions(defs); // Implicit convertion to std::shared_ptr<Definitions const> 

dsviper Const Type

  • Definitions const --> DefinitionsConst --> defs.const()
  • Path const --> PathConst --> path.const()

How to convert Python code to C++ code.

The best way to convert python code to C++ code is to extract the Viper logic from the thin-wrapper.

The source file for the wrapper of a Viper class is P_Viper_\<class>.cpp.

Take a look at P_Viper_DSMDefinition.cpp

1) Find the function tp_m_to_dsm 2) Identify the Python type used for the parameters 3) Identify Viper intermediate objects used to provide the result (if any) 4) Extract what you need 5) Recreate the object graph that provides the result.

Python

# convert a DSMDefinition to a DSM representation in HTML.
>>> dsm = dsm_definitions.to_dsm(html=true, show_documentation=true)

C++

// convert a DSMDefinition to a DSM representation in HTML
auto const configuration{DSMDefinitionsRenderConfiguration::make(true, false)};
auto const rendering{DSMDefinitionsRendererHtml::make(configuration)};
auto const dsm{DSMDefinitionsRenderer::render(dsmDefinitions, configuration, rendering)};

What's Next ...

The document P_Viper_Internal explains how the wrapper works and describes the pattern used to expose Viper C++ in Python.