Skip to content

Commit

Permalink
Python in OpenSCAD (openscad#5690)
Browse files Browse the repository at this point in the history
Add first python module (`cube()`). Switch hash library from crypto++ to nettle.
---------
Co-authored-by: Guenther Sohler <[email protected]>
Co-authored-by: Torsten Paul <[email protected]>
  • Loading branch information
gsohler authored Feb 22, 2025
1 parent 91070a0 commit 69998f8
Show file tree
Hide file tree
Showing 10 changed files with 711 additions and 64 deletions.
20 changes: 13 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,17 @@ target_link_libraries(OpenSCAD PRIVATE ${LIBXML2_LIBRARIES})

if(ENABLE_PYTHON)
find_package(Python REQUIRED COMPONENTS Interpreter Development)
find_package(CryptoPP REQUIRED)
message(STATUS "Python enabled, using Crypto++ ${PC_CRYPTOPP_VERSION}")
target_include_directories(OpenSCAD PRIVATE ${Python_INCLUDE_DIRS})
target_include_directories(OpenSCAD PRIVATE ${CRYPTOPP_INCLUDE_DIRS})
target_link_libraries(OpenSCAD PRIVATE ${Python_LIBRARIES})
target_link_libraries(OpenSCAD PRIVATE ${CRYPTOPP_LIBRARIES})
find_package(Nettle 3.4)
if(NOT Nettle_FOUND)
message(WARNING "Nettle not found, disabling python support.")
set(ENABLE_PYTHON OFF CACHE BOOL "" FORCE)
else()
message(STATUS "Python enabled, using Nettle ${Nettle_VERSION}")
target_include_directories(OpenSCAD PRIVATE ${Python_INCLUDE_DIRS})
target_link_libraries(OpenSCAD PRIVATE ${Python_LIBRARIES})
target_include_directories(OpenSCAD PRIVATE ${Nettle_INCLUDE_DIRS})
target_link_libraries(OpenSCAD PRIVATE ${Nettle_LIBRARIES})
endif()
endif()

if(ENABLE_HIDAPI)
Expand Down Expand Up @@ -900,7 +905,8 @@ set(CORE_SOURCES
${BISON_comment_parser_OUTPUTS})
if(ENABLE_PYTHON)
list(APPEND CORE_SOURCES
src/core/pyopenscad.cc )
src/python/pyopenscad.cc
src/python/pyfunctions.cc )
target_compile_definitions(OpenSCAD PRIVATE ENABLE_PYTHON)
endif()

Expand Down
141 changes: 141 additions & 0 deletions cmake/Modules/FindNettle.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Copyright (C) 2020 Dieter Baron and Thomas Klausner
#
# The authors can be contacted at <[email protected]>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. The names of the authors may not be used to endorse or promote
# products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#[=======================================================================[.rst:
FindNettle
-------
Finds the Nettle library.
Imported Targets
^^^^^^^^^^^^^^^^
This module provides the following imported targets, if found:
``Nettle::Nettle``
The Nettle library
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``Nettle_FOUND``
True if the system has the Nettle library.
``Nettle_VERSION``
The version of the Nettle library which was found.
``Nettle_INCLUDE_DIRS``
Include directories needed to use Nettle.
``Nettle_LIBRARIES``
Libraries needed to link to Nettle.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``Nettle_INCLUDE_DIR``
The directory containing ``nettle/aes.h``.
``Nettle_LIBRARY``
The path to the Nettle library.
#]=======================================================================]

find_package(PkgConfig)
pkg_check_modules(PC_Nettle QUIET nettle)

find_path(Nettle_INCLUDE_DIR
NAMES nettle/aes.h nettle/md5.h nettle/pbkdf2.h nettle/ripemd160.h nettle/sha.h
PATHS ${PC_Nettle_INCLUDE_DIRS}
)
find_library(Nettle_LIBRARY
NAMES nettle
PATHS ${PC_Nettle_LIBRARY_DIRS}
)

# Extract version information from the header file
if(Nettle_INCLUDE_DIR)
# This file only exists in nettle>=3.0
if(EXISTS ${Nettle_INCLUDE_DIR}/nettle/version.h)
file(STRINGS ${Nettle_INCLUDE_DIR}/nettle/version.h _ver_major_line
REGEX "^#define NETTLE_VERSION_MAJOR *[0-9]+"
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+"
Nettle_MAJOR_VERSION "${_ver_major_line}")
file(STRINGS ${Nettle_INCLUDE_DIR}/nettle/version.h _ver_minor_line
REGEX "^#define NETTLE_VERSION_MINOR *[0-9]+"
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+"
Nettle_MINOR_VERSION "${_ver_minor_line}")
set(Nettle_VERSION "${Nettle_MAJOR_VERSION}.${Nettle_MINOR_VERSION}")
unset(_ver_major_line)
unset(_ver_minor_line)
else()
if(PC_Nettle_VERSION)
set(Nettle_VERSION ${PC_Nettle_VERSION})
else()
set(Nettle_VERSION "1.0")
endif()
endif()
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Nettle
FOUND_VAR Nettle_FOUND
REQUIRED_VARS
Nettle_LIBRARY
Nettle_INCLUDE_DIR
VERSION_VAR Nettle_VERSION
)

if(Nettle_FOUND)
set(Nettle_LIBRARIES ${Nettle_LIBRARY})
set(Nettle_INCLUDE_DIRS ${Nettle_INCLUDE_DIR})
set(Nettle_DEFINITIONS ${PC_Nettle_CFLAGS_OTHER})
endif()

if(Nettle_FOUND AND NOT TARGET Nettle::Nettle)
add_library(Nettle::Nettle UNKNOWN IMPORTED)
set_target_properties(Nettle::Nettle PROPERTIES
IMPORTED_LOCATION "${Nettle_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PC_Nettle_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${Nettle_INCLUDE_DIR}"
)
endif()

mark_as_advanced(
Nettle_INCLUDE_DIR
Nettle_LIBRARY
)

# compatibility variables
set(Nettle_VERSION_STRING ${Nettle_VERSION})
2 changes: 1 addition & 1 deletion scripts/github-ci-linux-get-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ QT="$1"
DIST=$(. /etc/os-release; echo $VERSION_CODENAME)

PACKAGES1="build-essential bison cmake curl flex gettext git imagemagick ghostscript"
PACKAGES2="libboost-program-options-dev libboost-regex-dev libboost-system-dev libeigen3-dev libcrypto++-dev"
PACKAGES2="libboost-program-options-dev libboost-regex-dev libboost-system-dev libeigen3-dev nettle-dev"
PACKAGES3="libxi-dev libxmu-dev qtbase5-dev qtmultimedia5-dev libqt5opengl5-dev libqt5svg5-dev libqscintilla2-qt5-dev"
PACKAGES4="libcairo2-dev libcgal-dev libglew-dev libgmp-dev libmpfr-dev libegl-dev libegl1-mesa-dev libxml2-dev"
PACKAGES5="libdouble-conversion-dev libfontconfig-dev libharfbuzz-dev libopencsg-dev lib3mf-dev libtbb-dev libzip-dev"
Expand Down
36 changes: 0 additions & 36 deletions src/core/pyopenscad.cc

This file was deleted.

33 changes: 18 additions & 15 deletions src/gui/MainWindow.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,26 @@
#include "gui/QSettingsCached.h"

#ifdef ENABLE_PYTHON
extern std::shared_ptr<AbstractNode> python_result_node;
std::string evaluatePython(const std::string& code, double time);
extern bool python_trusted;

#include "cryptopp/sha.h"
#include "cryptopp/filters.h"
#include "cryptopp/base64.h"
#include "python/python_public.h"
#include "nettle/sha2.h"
#include "nettle/base64.h"

std::string SHA256HashString(std::string aString){
std::string digest;
CryptoPP::SHA256 hash;
uint8_t digest[SHA256_DIGEST_SIZE];
sha256_ctx sha256_ctx;

sha256_init(&sha256_ctx);
sha256_update(&sha256_ctx, aString.length(), (uint8_t *) aString.c_str());
sha256_digest(&sha256_ctx, SHA256_DIGEST_SIZE, digest);

CryptoPP::StringSource foo(aString, true,
new CryptoPP::HashFilter(hash,
new CryptoPP::Base64Encoder(
new CryptoPP::StringSink(digest))));
base64_encode_ctx base64_ctx;
char digest_base64[BASE64_ENCODE_LENGTH(SHA256_DIGEST_SIZE)+1];
memset(digest_base64,0,sizeof(digest_base64));

return digest;
base64_encode_init(&base64_ctx);
base64_encode_update(&base64_ctx, digest_base64, SHA256_DIGEST_SIZE, digest);
base64_encode_final(&base64_ctx, digest_base64);
return digest_base64;
}

#endif // ifdef ENABLE_PYTHON
Expand Down Expand Up @@ -1885,7 +1887,8 @@ void MainWindow::parseTopLevelDocument()
auto fulltext_py =
std::string(this->lastCompiledDoc.toUtf8().constData());

auto error = evaluatePython(fulltext_py, this->animateWidget->getAnimTval());
initPython(this->animateWidget->getAnimTval());
auto error = evaluatePython(fulltext_py, false);
if (error.size() > 0) LOG(message_group::Error, Location::NONE, "", error.c_str());
fulltext = "\n";
}
Expand Down
8 changes: 3 additions & 5 deletions src/openscad.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,7 @@


#ifdef ENABLE_PYTHON
extern std::shared_ptr<AbstractNode> python_result_node;
std::string evaluatePython(const std::string &code, double time);
bool python_active = false;
bool python_trusted = false;
#include "python/python_public.h"
#endif

namespace po = boost::program_options;
Expand Down Expand Up @@ -602,7 +599,8 @@ int cmdline(const CommandLine& cmd)

if(python_active) {
auto fulltext_py = text;
auto error = evaluatePython(fulltext_py, 0.0);
initPython(0.0);
auto error = evaluatePython(fulltext_py, false);
if(error.size() > 0) LOG(error.c_str());
text ="\n";
}
Expand Down
101 changes: 101 additions & 0 deletions src/python/pyfunctions.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <[email protected]> and
* Marius Kintel <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

#include <Python.h>
#include "src/python/pyopenscad.h"
#include "src/core/primitives.h"

PyObject *python_cube(PyObject *self, PyObject *args, PyObject *kwargs)
{
DECLARE_INSTANCE
auto node = std::make_shared<CubeNode>(instance);

char *kwlist[] = {"size", "center", NULL};
PyObject *size = NULL;

PyObject *center = NULL;


if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO", kwlist,
&size,
&center)){
PyErr_SetString(PyExc_TypeError, "Error during parsing cube(size)");
return NULL;
}

if (size != NULL) {
if (python_vectorval(size, 3, 3, &(node->x), &(node->y), &(node->z))) {
PyErr_SetString(PyExc_TypeError, "Invalid Cube dimensions");
return NULL;
}
}
if(node->x <= 0 || node->y <= 0 || node ->z <= 0) {
PyErr_SetString(PyExc_TypeError, "Cube Dimensions must be positive");
return NULL;
}
node->center = false;
if (center == Py_False || center == NULL ) ;
else if (center == Py_True){
for(int i=0;i<3;i++) node->center=true;
} else {
PyErr_SetString(PyExc_TypeError, "Unknown Value for center parameter");
return NULL;
}
return PyOpenSCADObjectFromNode(&PyOpenSCADType, node);
}

PyObject *python_show_core(PyObject *obj)
{
std::shared_ptr<AbstractNode> child = PyOpenSCADObjectToNode(obj);
if (child == NULL) {
PyErr_SetString(PyExc_TypeError, "Invalid type for Object in output");
return NULL;
}
PyObject *key, *value;
Py_ssize_t pos = 0;
python_result_node = child;
return Py_None;
}

PyObject *python_show(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *obj = NULL;
char *kwlist[] = {"obj", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist,
&obj
)) {
PyErr_SetString(PyExc_TypeError, "Error during parsing output(object)");
return NULL;
}
return python_show_core(obj);
}

PyMethodDef PyOpenSCADFunctions[] = {
{"cube", (PyCFunction) python_cube, METH_VARARGS | METH_KEYWORDS, "Create Cube."},
{"show", (PyCFunction) python_show, METH_VARARGS | METH_KEYWORDS, "Show the result."},
{NULL, NULL, 0, NULL}
};

Loading

0 comments on commit 69998f8

Please sign in to comment.