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.