Commit 055379d9 authored by Morten Ofstad's avatar Morten Ofstad
Browse files

Merge branch feature/stein.pedersen/python-bindings with refs/heads/master...

Merge branch feature/stein.pedersen/python-bindings with refs/heads/master into refs/merge-requests/15/train
parents e95709a7 53d5bf78
Pipeline #539 passed with stages
in 8 minutes and 41 seconds
......@@ -5,7 +5,14 @@ CMakeSettings.json
CMakeLists.txt.user
python/.eggs
python/_skbuild
python/*.log
python/**/*.pyd
python/openvds/test/aws_defs.py
CMakeSettings.json
out
*.pyc
build/
python/tools/tmpinclude/
python/tools/generated
python/tools/.vscode
python/tools/WrapperReport.txt
\ No newline at end of file
Copyright = """/****************************************************************************
** Copyright 2019 The Open Group
** Copyright 2019 Bluware, Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
****************************************************************************/"""
Namespace = "OpenVDS"
@python %~dp0make_cppclass.py -i%~dp0cppclasstemplate.txt %*
\ No newline at end of file
# This script creates class files (.cpp/.h) with a given name.
# Base class and namespace can also be defined.
# Stein Pedersen 13.10.2019
from __future__ import print_function
import sys
import os.path
import runpy
_header_template = """Copyright
#ifndef CLASSNAME_H_INCLUDED
#define CLASSNAME_H_INCLUDED
NAMESPACE_BEGIN
class ClassNameBASECLASS
{
public:
ClassName();
virtual ~ClassName();
};
NAMESPACE_END
#endif
"""
_impl_template = """Copyright
#include "HeaderName"
NAMESPACE_BEGIN
ClassName::ClassName()
{
}
ClassName::~ClassName()
{
}
NAMESPACE_END
"""
usage_string = """Usage: make_class <-doi> [ClassName] <variable=VariableName> ...
-d : print debugging info
-o : overwrite existing files
-i<filename> : include template
where variable is one of:"""
def usage(vars):
print(usage_string)
for key in vars.keys():
print("{0:20}: {1:40} (default) -> '{2}'".format(key, "'{}'".format(vars[key]), substituteVar(key, vars)))
def printVars(vars):
for key in vars.keys():
print("{0:20}: {1:40} -> '{2}'".format(key, "'{}'".format(vars[key]), substituteVar(key, vars)))
def parseVar(value, tbegin, tend):
token = value[value.index(tbegin)+1:value.index(tend)]
prefix = value[0:value.index(tbegin)]
suffix = value[value.index(tend)+1:]
return prefix, token, suffix
def substituteVars(value, vars):
if value.startswith('?'):
# For ?var|suffix, if vars[var] is non-empty,
# substitute ?var| with suffix, otherwise substitute it with ''
pre, tok, suf = parseVar(value, '?', '|')
if vars[tok]:
value = substituteVars(suf, vars)
else:
value = ''
elif '<' in value:
# Substitute <var> with vars[var]
pre, tok, suf = parseVar(value, '<', '>')
value = pre + vars[tok] + suf
elif '[' in value:
# Substitute <var> with vars[var].upper()
pre, tok, suf = parseVar(value, '[', ']')
value = pre + vars[tok].upper() + suf
return value
def substituteVar(key, vars):
value = vars[key]
return substituteVars(value, vars)
def substituteTemplate(vars, template):
result = template
for var in vars:
result = result.replace(var, substituteVar(var, vars))
return result
def trywritefile(fname, contents, overwrite):
if os.path.isfile(fname) and not overwrite:
print("File '{}' already exists. Use -o option to overwrite.".format(fname), file=sys.stderr)
else:
with open(fname, "w") as file:
file.write(contents)
def main():
debug = False
overwrite = False
vars = {
"HeaderTemplate": _header_template,
"ImplTemplate": _impl_template,
"BaseName": "",
"Namespace": "",
"Copyright": "",
"ClassName": "MyClass",
"HeaderName": "<ClassName>.h",
"ImplName": "<ClassName>.cpp",
"CLASSNAME": "[ClassName]",
"NAMESPACE_BEGIN": "?Namespace|namespace <Namespace> {",
"NAMESPACE_END": "?Namespace|}",
"BASECLASS": "?BaseName| : public <BaseName>"
}
if len(sys.argv) <= 1:
usage(vars)
exit(-1)
for arg in sys.argv[1:]:
if '=' in arg:
key,val = arg.split('=')
vars[key] = val
elif arg.startswith('-d'):
debug = True
elif arg.startswith('-o'):
overwrite = True
elif arg.startswith('-i'):
templatefile = arg[2:]
d = runpy.run_path(templatefile)
overridevars = [ k for k in d.keys() if k not in globals().keys() and not k.startswith('__')]
for k in overridevars:
vars[k] = d[k]
else:
vars['ClassName'] = arg
header_template = vars["HeaderTemplate"]
impl_template = vars["ImplTemplate"]
del vars["HeaderTemplate"]
del vars["ImplTemplate"]
if debug:
printVars(vars)
header = substituteTemplate(vars, header_template)
headerfilename = substituteVar("HeaderName", vars)
impl = substituteTemplate(vars, impl_template)
implfilename = substituteVar("ImplName", vars)
trywritefile(headerfilename, header, overwrite)
trywritefile(implfilename, impl, overwrite)
if __name__ == "__main__":
main()
......@@ -28,6 +28,11 @@ add_custom_target(
$<$<BOOL:$<CONFIG>>:--build-type=$<CONFIG>>
-- # cmake to the extension
-Dopenvds_DIR=${OPENVDS_LIB_BINARY_DIR}
-Dpython3_EXE=${Python3_EXECUTABLE}
-Dpackage_DIR=${CMAKE_CURRENT_SOURCE_DIR}
-DTEST_AWS_BUCKET=${TEST_AWS_BUCKET}
-DTEST_AWS_OBJECTID=${TEST_AWS_OBJECTID}
-DTEST_AWS_REGION=${TEST_AWS_REGION}
${ZLIB_PREFIX_PATH}
# "install" to the python/openvds dir with rpath, so there's no need
# to fiddle with environment in ctest to load the core library from
......
TEST_AWS_BUCKET = "@TEST_AWS_BUCKET@"
TEST_AWS_OBJECTID = "@TEST_AWS_OBJECTID@"
TEST_AWS_REGION = "@TEST_AWS_REGION@"
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommand>@PYTHONEXE@</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>@PYTHONPATH@</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerEnvironment>PYTHONPATH=@PYTHONPATH@;$(PYTHONPATH)
PATH=@BINDIR@\Debug;$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x64'">
<LocalDebuggerCommand>@PYTHONEXE@</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>@PYTHONPATH@</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerEnvironment>PYTHONPATH=@PYTHONPATH@;$(PYTHONPATH)
PATH=@BINDIR@\MinSizeRel;$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommand>@PYTHONEXE@</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>@PYTHONPATH@</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerEnvironment>PYTHONPATH=@PYTHONPATH@;$(PYTHONPATH)
PATH=@BINDIR@\Release;$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
<LocalDebuggerCommand>@PYTHONEXE@</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>@PYTHONPATH@</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerEnvironment>PYTHONPATH=@PYTHONPATH@;$(PYTHONPATH)
PATH=@BINDIR@\RelWithDebInfo;$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
</Project>
\ No newline at end of file
......@@ -15,7 +15,45 @@ endif()
find_package(ZLIB REQUIRED)
find_package(openvds REQUIRED)
add_library(core MODULE core.cpp)
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
# Set up debugging environment for "core" project in Visual Studio
set(PYTHONEXE "${python3_EXE}")
set(BINDIR "${openvds_DIR}/SEG-Y") # A bit of a hack to get all the dependent dlls
set(PYTHONPATH "${package_DIR}")
configure_file("../core.vcxproj.user.txt" "${CMAKE_CURRENT_BINARY_DIR}/core.vcxproj.user" @ONLY)
endif()
configure_file("../aws_defs.txt" "${CMAKE_CURRENT_SOURCE_DIR}/test/aws_defs.py" @ONLY)
add_library(core MODULE
core.cpp
PyGlobal.cpp
PyGlobal.h
PyGlobalMetadataCommon.cpp
PyGlobalMetadataCommon.h
PyKnownMetadata.cpp
PyKnownMetadata.h
PyMetadata.cpp
PyMetadata.h
PyRange.cpp
PyRange.h
PyVector.cpp
PyVector.h
PyVolumeData.cpp
PyVolumeData.h
PyVolumeDataAccess.cpp
PyVolumeDataAccess.h
PyVolumeDataAxisDescriptor.cpp
PyVolumeDataAxisDescriptor.h
PyVolumeDataChannelDescriptor.cpp
PyVolumeDataChannelDescriptor.h
PyVolumeDataLayout.cpp
PyVolumeDataLayout.h
PyVolumeDataLayoutDescriptor.cpp
PyVolumeDataLayoutDescriptor.h
PyVolumeSampler.cpp
PyVolumeSampler.h
)
target_include_directories(core
PRIVATE
${PYBIND11_INCLUDE_DIRS}
......@@ -29,6 +67,12 @@ if (MSVC)
PRIVATE
/EHsc
)
string(REPLACE "/" "\\" _package_dir ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_command(
TARGET core
POST_BUILD
COMMAND copy $(TargetPath) ${_package_dir}
)
endif ()
install(TARGETS core LIBRARY DESTINATION openvds)
/****************************************************************************
** Copyright 2019 The Open Group
** Copyright 2019 Bluware, Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
****************************************************************************/
#include "PyGlobal.h"
using namespace native;
void
PyGlobal::initModule(py::module& m)
{
// These are opaque pointers, so they must not be destructed from pybind11 code
py::class_<VDS, std::unique_ptr<VDS, py::nodelete>>(m, "VDS");
py::class_<IOManager, std::unique_ptr<IOManager, py::nodelete>>(m, "IOManager");
//AUTOGEN-BEGIN
// OpenOptions
py::class_<OpenOptions>
OpenOptions_(m,"OpenOptions", OPENVDS_DOCSTRING(OpenOptions));
OpenOptions_.def_readwrite("connectionType" , &OpenOptions::connectionType , OPENVDS_DOCSTRING(OpenOptions_connectionType));
py::enum_<OpenOptions::ConnectionType>
OpenOptions_ConnectionType_(OpenOptions_,"ConnectionType", OPENVDS_DOCSTRING(OpenOptions_ConnectionType));
OpenOptions_ConnectionType_.value("AWS" , OpenOptions::ConnectionType::AWS , OPENVDS_DOCSTRING(OpenOptions_ConnectionType_AWS));
OpenOptions_ConnectionType_.value("Azure" , OpenOptions::ConnectionType::Azure , OPENVDS_DOCSTRING(OpenOptions_ConnectionType_Azure));
OpenOptions_ConnectionType_.value("File" , OpenOptions::ConnectionType::File , OPENVDS_DOCSTRING(OpenOptions_ConnectionType_File));
OpenOptions_ConnectionType_.value("InMemory" , OpenOptions::ConnectionType::InMemory , OPENVDS_DOCSTRING(OpenOptions_ConnectionType_InMemory));
// AWSOpenOptions
py::class_<AWSOpenOptions, OpenOptions>
AWSOpenOptions_(m,"AWSOpenOptions", OPENVDS_DOCSTRING(AWSOpenOptions));
AWSOpenOptions_.def(py::init< >(), OPENVDS_DOCSTRING(AWSOpenOptions_AWSOpenOptions));
AWSOpenOptions_.def(py::init<const std::string &, const std::string &, const std::string &>(), OPENVDS_DOCSTRING(AWSOpenOptions_AWSOpenOptions_2));
AWSOpenOptions_.def_readwrite("bucket" , &AWSOpenOptions::bucket , OPENVDS_DOCSTRING(AWSOpenOptions_bucket));
AWSOpenOptions_.def_readwrite("key" , &AWSOpenOptions::key , OPENVDS_DOCSTRING(AWSOpenOptions_key));
AWSOpenOptions_.def_readwrite("region" , &AWSOpenOptions::region , OPENVDS_DOCSTRING(AWSOpenOptions_region));
// InMemoryOpenOptions
py::class_<InMemoryOpenOptions, OpenOptions>
InMemoryOpenOptions_(m,"InMemoryOpenOptions", OPENVDS_DOCSTRING(InMemoryOpenOptions));
InMemoryOpenOptions_.def(py::init< >(), OPENVDS_DOCSTRING(InMemoryOpenOptions_InMemoryOpenOptions));
// Error
py::class_<Error>
Error_(m,"Error", OPENVDS_DOCSTRING(Error));
Error_.def_readwrite("code" , &Error::code , OPENVDS_DOCSTRING(Error_code));
Error_.def_readwrite("string" , &Error::string , OPENVDS_DOCSTRING(Error_string));
m.def("open" , static_cast<native::VDSHandle(*)(const native::OpenOptions &, native::Error &)>(&Open), OPENVDS_DOCSTRING(Open));
m.def("open" , static_cast<native::VDSHandle(*)(native::IOManager *, native::Error &)>(&Open), OPENVDS_DOCSTRING(Open_2));
m.def("create" , static_cast<native::VDSHandle(*)(const native::OpenOptions &, const native::VolumeDataLayoutDescriptor &, VectorWrapper<native::VolumeDataAxisDescriptor>, VectorWrapper<native::VolumeDataChannelDescriptor>, const native::MetadataReadAccess &, native::Error &)>(&Create), OPENVDS_DOCSTRING(Create));
m.def("create" , static_cast<native::VDSHandle(*)(native::IOManager *, const native::VolumeDataLayoutDescriptor &, VectorWrapper<native::VolumeDataAxisDescriptor>, VectorWrapper<native::VolumeDataChannelDescriptor>, const native::MetadataReadAccess &, native::Error &)>(&Create), OPENVDS_DOCSTRING(Create_2));
m.def("getLayout" , static_cast<native::VolumeDataLayout *(*)(native::VDSHandle)>(&GetLayout), OPENVDS_DOCSTRING(GetLayout));
m.def("getAccessManager" , static_cast<native::VolumeDataAccessManager *(*)(native::VDSHandle)>(&GetAccessManager), OPENVDS_DOCSTRING(GetAccessManager));
m.def("close" , static_cast<void(*)(native::VDSHandle)>(&Close), OPENVDS_DOCSTRING(Close));
//AUTOGEN-END
Error_.def(py::init<>());
Error_.def("__repr__", [](native::Error const& self){ std::string tmp = std::to_string(self.code); return std::string("Error(code=") + tmp + ", string='" + self.string + "')"; });
}
/****************************************************************************
** Copyright 2019 The Open Group
** Copyright 2019 Bluware, Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
****************************************************************************/
#ifndef PYGLOBAL_H_INCLUDED
#define PYGLOBAL_H_INCLUDED
#include <OpenVDS/OpenVDS.h>
#include <OpenVDS/VolumeDataAxisDescriptor.h>
#include <OpenVDS/VolumeDataLayout.h>
#include <OpenVDS/VolumeDataLayoutDescriptor.h>
#include <OpenVDS/VolumeDataChannelDescriptor.h>
#include <OpenVDS/VolumeData.h>
#include <OpenVDS/VolumeDataAccess.h>
#include "generated_docstrings.h"
#include <stdexcept>
#define OPENVDS_DOCSTRING(name) __doc_OpenVDS_ ## name
namespace OpenVDS {
#ifdef OPENVDS_DEVELOPMENT_BUILD
struct VDS;
class IOManager;
#else
struct VDS
{
private:
VDS();
~VDS();
};
class IOManager
{
private:
IOManager();
~IOManager();
};
#endif
}
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <pybind11/attr.h>
namespace py = pybind11;
namespace native = OpenVDS;
// Adapter class to check fixed-size numpy arrays
template<typename T, int LEN, bool MUTABLE>
struct PyArrayAdapter;
template<typename T, int LEN>
struct PyArrayAdapter<T, LEN, true>
{
static T*
getArrayBufferChecked(py::array_t<T>& arr, int* arrayCount = nullptr)
{
py::ssize_t n = 0;
int count = 0;
if (arr.ndim() == 1)
{
count = 1;
n = arr.shape(0);
}
else if (arr.ndim() == 2)
{
count = (int)arr.shape(0);
n = arr.shape(1);
}
if (arrayCount)
{
*arrayCount = count;
}
if (n != LEN)
{
throw std::invalid_argument("Array has the wrong size/dimensions.");
}
else
{
return arr.mutable_unchecked().mutable_data(0);
}
}
static T (&getArrayChecked(py::array_t<T> & arr, int* arrayCount = nullptr))[LEN]
{
T* tmp = getArrayBufferChecked(arr, arrayCount);
return *reinterpret_cast<T (*)[LEN]>(tmp);
}
};
template<typename T, int LEN>
struct PyArrayAdapter<T, LEN, false>
{
static const T*
getArrayBufferChecked(py::array_t<T> const& arr, int* arrayCount = nullptr)
{
py::ssize_t n = 0;
int count = 0;
if (arr.ndim() == 1)
{
count = 1;
n = arr.shape(0);
}
else if (arr.ndim() == 2)
{
count = (int)arr.shape(0);
n = arr.shape(1);
}
if (arrayCount)
{
*arrayCount = count;
}
if (n != LEN)
{
throw std::invalid_argument("Array has the wrong size/dimensions.");
}
else
{
return arr.unchecked().data(0);
}
}
static const T (&getArrayChecked(py::array_t<T> const& arr, int* arrayCount = nullptr))[LEN]
{
const T* tmp = getArrayBufferChecked(arr, arrayCount);
return *reinterpret_cast<const T (*)[LEN]>(tmp);
}
};
struct BLOB
{
uint8_t* m_Data;
size_t m_Size;
BLOB() : m_Data(nullptr), m_Size(0)
{
}
BLOB(uint8_t* data, size_t size) : m_Data(data), m_Size(size)
{
}
};
template<typename T, size_t N>
struct VectorAdapter
{
};
template<typename T>
struct VectorAdapter<T, 2>
{
typedef native::Vector<T, 2> VectorType;
typedef std::tuple<T, T> AdaptedType;
static VectorType fromTuple(AdaptedType const& val) { return val; }
static AdaptedType asTuple(VectorType const& val) { return val; }
};
template<typename T>
struct VectorAdapter<T, 3>
{
typedef native::Vector<T, 3> VectorType;
typedef std::tuple<T, T, T> AdaptedType;
static VectorType fromTuple(AdaptedType const& val) { return val; }
static AdaptedType asTuple(VectorType const& val) { return val; }
};
template<typename T>
struct VectorAdapter<T, 4>
{
typedef native::Vector<T, 4> VectorType;
typedef std::tuple<T, T, T, T> AdaptedType;
static VectorType fromTuple(AdaptedType const& val) { return val; }
static AdaptedType asTuple(VectorType const& val) { return val; }
};
template<typename T>
struct VectorAdapter<T, 6>
{
typedef native::Vector<T, 6> VectorType;
typedef std::tuple<T, T, T, T, T, T> AdaptedType;
static VectorType fromTuple(AdaptedType const& val) { return val; }
static AdaptedType asTuple(VectorType const& val) { return val; }
};
template<typename T>
using Vector2Adapter = VectorAdapter<T, 2>;
template<typename T>
using Vector3Adapter = VectorAdapter<T, 3>;
template<typename T>
using Vector4Adapter = VectorAdapter<T, 4>;
template<typename T>
using Vector6Adapter = VectorAdapter<T, 6>;
typedef Vector2Adapter<int> IntVector2Adapter;
typedef Vector3Adapter<int> IntVector3Adapter;