diff --git a/.travis.yml b/.travis.yml index c3671df4..66d8c0f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,24 +16,18 @@ branches: jobs: include: - - name: "run test suite with python 2.7" - python: 2.7 - dist: trusty - - name: "run test suite with python 3.4" - python: 3.4 - dist: trusty - - name: "run test suite with python 3.5" - python: 3.5 - dist: trusty - name: "run test suite with python 3.6" python: 3.6 - name: "run test suite with python 3.7" python: 3.7 + - name: "run test suite with python 3.8" + python: 3.8 env: - CPPFLAGS=--coverage before_install: - pip install --upgrade pip + - pip install https://github.com/nucleic/cppy/tarball/master install: - python setup.py develop script: diff --git a/MANIFEST.in b/MANIFEST.in index 175af968..fa33b204 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,17 @@ include MANIFEST.in -include COPYING.txt +include LICENSE include README.rst include releasenotes.rst recursive-include kiwi *.h recursive-include py *.cpp *.h +recursive-include docs/source *.rst +recursive-include docs/source *.png +recursive-include docs/source *.py +recursive-include docs/source *.svg +recursive docs/make.bat +recursive docs/Makefile +recursive-include tests *.py +prune .git +prune dist +prune build +prune docs/build diff --git a/README.rst b/README.rst index 4faa3c32..98dabebe 100644 --- a/README.rst +++ b/README.rst @@ -17,3 +17,6 @@ from 10x to 500x faster than the original Cassowary solver with typical use cases gaining a 40x improvement. Memory savings are consistently > 5x. In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. + +The version 1.1.0 of the Python bindings will be the last one to support +Python 2, moving forward support will be limited to Python 3.5+. diff --git a/benchmarks/README.rst b/benchmarks/README.rst new file mode 100644 index 00000000..08438abb --- /dev/null +++ b/benchmarks/README.rst @@ -0,0 +1,9 @@ +Benchmarks for Kiwi +------------------- + +Those benchmarks are mostly used to check the performance of kiwi depending on +different c++ data structure. + +Running these benchmarks require to install the perf module:: + + >>> python enaml_like_benchmarks.py diff --git a/benchmarks/enaml_like_benchmark.py b/benchmarks/enaml_like_benchmark.py new file mode 100644 index 00000000..f474b3ce --- /dev/null +++ b/benchmarks/enaml_like_benchmark.py @@ -0,0 +1,197 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2019, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#------------------------------------------------------------------------------ +"""Time updating an EditVariable in a set of constraints typical of enaml use. + +""" +import perf +from kiwisolver import Variable, Solver, strength + +solver = Solver() + +# Create custom strength +mmedium = strength.create(0, 1, 0, 1.25) +smedium = strength.create(0, 100, 0) + +# Create the variable +left = Variable('left') +height = Variable('height') +top = Variable('top') +width = Variable('width') +contents_top = Variable('contents_top') +contents_bottom = Variable('contents_bottom') +contents_left = Variable('contents_left') +contents_right = Variable('contents_right') +midline = Variable('midline') +ctleft = Variable('ctleft') +ctheight = Variable('ctheight') +cttop = Variable('cttop') +ctwidth = Variable('ctwidth') +lb1left = Variable('lb1left') +lb1height = Variable('lb1height') +lb1top = Variable('lb1top') +lb1width = Variable('lb1width') +lb2left = Variable('lb2left') +lb2height = Variable('lb2height') +lb2top = Variable('lb2top') +lb2width = Variable('lb2width') +lb3left = Variable('lb3left') +lb3height = Variable('lb3height') +lb3top = Variable('lb3top') +lb3width = Variable('lb3width') +fl1left = Variable('fl1left') +fl1height = Variable('fl1height') +fl1top = Variable('fl1top') +fl1width = Variable('fl1width') +fl2left = Variable('fl2left') +fl2height = Variable('fl2height') +fl2top = Variable('fl2top') +fl2width = Variable('fl2width') +fl3left = Variable('fl3left') +fl3height = Variable('fl3height') +fl3top = Variable('fl3top') +fl3width = Variable('fl3width') + +# Add the edit variables +solver.addEditVariable(width, 'strong') +solver.addEditVariable(height, 'strong') + +# Add the constraints +for c in [(left + -0 >= 0) | "required", + (height + 0 == 0) | "medium", + (top + -0 >= 0) | "required", + (width + -0 >= 0) | "required", + (height + -0 >= 0) | "required", + (- top + contents_top + -10 == 0) | "required", + (lb3height + -16 == 0) | "strong", + (lb3height + -16 >= 0) | "strong", + (ctleft + -0 >= 0) | "required", + (cttop + -0 >= 0) | "required", + (ctwidth + -0 >= 0) | "required", + (ctheight + -0 >= 0) | "required", + (fl3left + -0 >= 0) | "required", + (ctheight + -24 >= 0) | smedium, + (ctwidth + -1.67772e+07 <= 0) | smedium, + (ctheight + -24 <= 0) | smedium, + (fl3top + -0 >= 0) | "required", + (fl3width + -0 >= 0) | "required", + (fl3height + -0 >= 0) | "required", + (lb1width + -67 == 0) | "weak", + (lb2width + -0 >= 0) | "required", + (lb2height + -0 >= 0) | "required", + (fl2height + -0 >= 0) | "required", + (lb3left + -0 >= 0) | "required", + (fl2width + -125 >= 0) | "strong", + (fl2height + -21 == 0) | "strong", + (fl2height + -21 >= 0) | "strong", + (lb3top + -0 >= 0) | "required", + (lb3width + -0 >= 0) | "required", + (fl1left + -0 >= 0) | "required", + (fl1width + -0 >= 0) | "required", + (lb1width + -67 >= 0) | "strong", + (fl2left + -0 >= 0) | "required", + (lb2width + -66 == 0) | "weak", + (lb2width + -66 >= 0) | "strong", + (lb2height + -16 == 0) | "strong", + (fl1height + -0 >= 0) | "required", + (fl1top + -0 >= 0) | "required", + (lb2top + -0 >= 0) | "required", + (- lb2top + lb3top + - lb2height + -10 == 0) | mmedium, + (- lb3top + - lb3height + fl3top + -10 >= 0) | "required", + (- lb3top + - lb3height + fl3top + -10 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 == 0) | mmedium, + (fl1top + - contents_top + 0 >= 0) | "required", + (fl1top + - contents_top + 0 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 >= 0) | "required", + (- left + - width + contents_right + 10 == 0) | "required", + (- top + - height + contents_bottom + 10 == 0) | "required", + (- left + contents_left + -10 == 0) | "required", + (lb3left + - contents_left + 0 == 0) | mmedium, + (fl1left + - midline + 0 == 0) | "strong", + (fl2left + - midline + 0 == 0) | "strong", + (ctleft + - midline + 0 == 0) | "strong", + (fl1top + 0.5 * fl1height + - lb1top + -0.5 * lb1height + 0 == 0) | "strong", + (lb1left + - contents_left + 0 >= 0) | "required", + (lb1left + - contents_left + 0 == 0) | mmedium, + (- lb1left + fl1left + - lb1width + -10 >= 0) | "required", + (- lb1left + fl1left + - lb1width + -10 == 0) | mmedium, + (- fl1left + contents_right + - fl1width + -0 >= 0) | "required", + (width + 0 == 0) | "medium", + (- fl1top + fl2top + - fl1height + -10 >= 0) | "required", + (- fl1top + fl2top + - fl1height + -10 == 0) | mmedium, + (cttop + - fl2top + - fl2height + -10 >= 0) | "required", + (- ctheight + - cttop + fl3top + -10 >= 0) | "required", + (contents_bottom + - fl3height + - fl3top + -0 >= 0) | "required", + (cttop + - fl2top + - fl2height + -10 == 0) | mmedium, + (- fl1left + contents_right + - fl1width + -0 == 0) | mmedium, + (- lb2top + -0.5 * lb2height + fl2top + 0.5 * fl2height + 0 == 0) | "strong", + (- contents_left + lb2left + 0 >= 0) | "required", + (- contents_left + lb2left + 0 == 0) | mmedium, + (fl2left + - lb2width + - lb2left + -10 >= 0) | "required", + (- ctheight + - cttop + fl3top + -10 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 == 0) | mmedium, + (lb1top + -0 >= 0) | "required", + (lb1width + -0 >= 0) | "required", + (lb1height + -0 >= 0) | "required", + (fl2left + - lb2width + - lb2left + -10 == 0) | mmedium, + (- fl2left + - fl2width + contents_right + -0 == 0) | mmedium, + (- fl2left + - fl2width + contents_right + -0 >= 0) | "required", + (lb3left + - contents_left + 0 >= 0) | "required", + (lb1left + -0 >= 0) | "required", + (0.5 * ctheight + cttop + - lb3top + -0.5 * lb3height + 0 == 0) | "strong", + (ctleft + - lb3left + - lb3width + -10 >= 0) | "required", + (- ctwidth + - ctleft + contents_right + -0 >= 0) | "required", + (ctleft + - lb3left + - lb3width + -10 == 0) | mmedium, + (fl3left + - contents_left + 0 >= 0) | "required", + (fl3left + - contents_left + 0 == 0) | mmedium, + (- ctwidth + - ctleft + contents_right + -0 == 0) | mmedium, + (- fl3left + contents_right + - fl3width + -0 == 0) | mmedium, + (- contents_top + lb1top + 0 >= 0) | "required", + (- contents_top + lb1top + 0 == 0) | mmedium, + (- fl3left + contents_right + - fl3width + -0 >= 0) | "required", + (lb2top + - lb1top + - lb1height + -10 >= 0) | "required", + (- lb2top + lb3top + - lb2height + -10 >= 0) | "required", + (lb2top + - lb1top + - lb1height + -10 == 0) | mmedium, + (fl1height + -21 == 0) | "strong", + (fl1height + -21 >= 0) | "strong", + (lb2left + -0 >= 0) | "required", + (lb2height + -16 >= 0) | "strong", + (fl2top + -0 >= 0) | "required", + (fl2width + -0 >= 0) | "required", + (lb1height + -16 >= 0) | "strong", + (lb1height + -16 == 0) | "strong", + (fl3width + -125 >= 0) | "strong", + (fl3height + -21 == 0) | "strong", + (fl3height + -21 >= 0) | "strong", + (lb3height + -0 >= 0) | "required", + (ctwidth + -119 >= 0) | smedium, + (lb3width + -24 == 0) | "weak", + (lb3width + -24 >= 0) | "strong", + (fl1width + -125 >= 0) | "strong", + ]: + solver.addConstraint(c) + + +def bench_update_variables(loops, solver): + """Suggest new values and update variables. + + This mimic the use of kiwi in enaml in the case of a resizing. + + """ + t0 = perf.perf_counter() + for w, h in [(400, 600), (600, 400), (800, 1200), (1200, 800), + (400, 800), (800, 400)]*loops: + solver.suggestValue(width, w) + solver.suggestValue(height, h) + solver.updateVariables() + + return perf.perf_counter() - t0 + + +runner = perf.Runner() +runner.bench_time_func('kiwi.suggestValue', bench_update_variables, + solver, inner_loops=1) diff --git a/docs/source/basis/solver_internals.rst b/docs/source/basis/solver_internals.rst index a0e82338..2b26e481 100644 --- a/docs/source/basis/solver_internals.rst +++ b/docs/source/basis/solver_internals.rst @@ -170,3 +170,27 @@ Those classes are used internally in constraints and are created automatically by the library. A |Term| represents a variable/symbol and the coefficient that multiplies it, |Expression| represents a sum of terms and a constant value and is used as the left hand side of a constraint. + + +Performance implementation tricks +--------------------------------- + +Map type +^^^^^^^^ + +Kiwi uses maps to represent the state of the solver and to manipulate it. As a +consequence the map type should be fast, with a particular emphasis on +iteration. The C++ standard library provides unordered_map and map that could +be used in kiwi, but none of those are very friendly to the CPU cache. For +this reason, Kiwi uses the AssocVector class implemented in Loki (slightly +updated to respect c++11 standards). The use of this class provides a 2x +speedups over std::map. + + +Symbol representation +^^^^^^^^^^^^^^^^^^^^^ + +Symbol are used in Kiwi to represent the state of the solver. Since solving the +system requires a large number of manipulation of the symbols the operations +have to compile down to an efficient representation. In Kiwi, symbols compile +down to long long meaning that a vector of them fits in a CPU cache line. diff --git a/docs/source/developer_notes/index.rst b/docs/source/developer_notes/index.rst new file mode 100644 index 00000000..e532deac --- /dev/null +++ b/docs/source/developer_notes/index.rst @@ -0,0 +1,29 @@ +.. _developer: + +Developer notes +================ + +These notes are meant to help developers and contributors with regards to some +details of the implementation and coding style of the project. + +C++ codebase +------------ + +The C++ codebase currently targets C++11 compliance. It is header-only since +one of the focus of the library is speed. + + +Python bindings +--------------- + +Python bindings targets Python 3.6 and above. The bindings are hand-written and +relies on cppy (https://github.com/nucleic/cppy). Kiwisolver tries to use a +reasonably modern C API and to support sub-interpreter, this has a couple of +consequences: + +- all the non exported symbol are enclosed in anonymous namespaces +- kiwisolver does not use static types and only dynamical types (note that the + type slots and related structures are stored in a static variable) +- modules use the multi-phases initialization mechanism as defined in + PEP 489 -- Multi-phase extension module initialization +- static variables use is limited to type slots, method def diff --git a/docs/source/index.rst b/docs/source/index.rst index ea94e8b6..547bd893 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. Getting started Use cases + Developer notes API Documentation Indices and tables diff --git a/docs/source/use_cases/enaml.rst b/docs/source/use_cases/enaml.rst index b534e449..f6486fad 100644 --- a/docs/source/use_cases/enaml.rst +++ b/docs/source/use_cases/enaml.rst @@ -82,7 +82,9 @@ widgets should be in contact. When generating the constraints, `hbox` will be passed the container and use the spacers to generate the constraints by simply glueing the anchors of surrounding widgets. Each spacer can generate multiple constraints which gives -this process a lot of flexibility. +this process a lot of flexibility. Furthermore, those helpers define the same +variable as the widgets allowing for to position groups with respect to one +another. .. note:: diff --git a/kiwi/AssocVector.h b/kiwi/AssocVector.h index 0d5eb288..2a509241 100644 --- a/kiwi/AssocVector.h +++ b/kiwi/AssocVector.h @@ -12,6 +12,8 @@ // suitability of this software for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// +// Updated 2019 by Matthieu Dartiailh for C++11 compliancy +//////////////////////////////////////////////////////////////////////////////// #pragma once // $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $ @@ -107,7 +109,7 @@ namespace Loki typedef typename Base::const_reverse_iterator const_reverse_iterator; class value_compare - : public std::binary_function + : public std::function , private key_compare { friend class AssocVector; diff --git a/kiwi/maptype.h b/kiwi/maptype.h index d125b07c..f8977ad0 100644 --- a/kiwi/maptype.h +++ b/kiwi/maptype.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -24,14 +24,14 @@ template< typename V, typename C = std::less, typename A = std::allocator< std::pair > > -class MapType -{ -public: - typedef Loki::AssocVector Type; - //typedef std::map Type; -private: - MapType(); -}; +using MapType = Loki::AssocVector; + +// template< +// typename K, +// typename V, +// typename C = std::less, +// typename A = std::allocator< std::pair > > +// using MapType = std::map; } // namespace impl diff --git a/kiwi/row.h b/kiwi/row.h index 33b501df..c1fc5550 100644 --- a/kiwi/row.h +++ b/kiwi/row.h @@ -22,7 +22,7 @@ class Row public: - typedef MapType::Type CellMap; + typedef MapType CellMap; Row() : m_constant( 0.0 ) {} diff --git a/kiwi/solverimpl.h b/kiwi/solverimpl.h index 9b56ee28..9b78392d 100644 --- a/kiwi/solverimpl.h +++ b/kiwi/solverimpl.h @@ -44,13 +44,13 @@ class SolverImpl double constant; }; - typedef MapType::Type VarMap; + typedef MapType VarMap; - typedef MapType::Type RowMap; + typedef MapType RowMap; - typedef MapType::Type CnMap; + typedef MapType CnMap; - typedef MapType::Type EditMap; + typedef MapType EditMap; struct DualOptimizeGuard { @@ -88,7 +88,7 @@ class SolverImpl // constraints and since exceptional conditions are uncommon, // i'm not too worried about aggressive cleanup of the var map. Tag tag; - std::auto_ptr rowptr( createRow( constraint, tag ) ); + std::unique_ptr rowptr( createRow( constraint, tag ) ); Symbol subject( chooseSubject( *rowptr, tag ) ); // If chooseSubject could not find a valid entering symbol, one @@ -155,7 +155,7 @@ class SolverImpl RowMap::iterator row_it = m_rows.find( tag.marker ); if( row_it != m_rows.end() ) { - std::auto_ptr rowptr( row_it->second ); + std::unique_ptr rowptr( row_it->second ); m_rows.erase( row_it ); } else @@ -164,7 +164,7 @@ class SolverImpl if( row_it == m_rows.end() ) throw InternalSolverError( "failed to find leaving row" ); Symbol leaving( row_it->first ); - std::auto_ptr rowptr( row_it->second ); + std::unique_ptr rowptr( row_it->second ); m_rows.erase( row_it ); rowptr->solveFor( leaving, tag.marker ); substitute( tag.marker, *rowptr ); @@ -511,7 +511,7 @@ class SolverImpl RowMap::iterator it = m_rows.find( art ); if( it != m_rows.end() ) { - std::auto_ptr rowptr( it->second ); + std::unique_ptr rowptr( it->second ); m_rows.erase( it ); if( rowptr->cells().empty() ) return success; @@ -830,8 +830,8 @@ class SolverImpl VarMap m_vars; EditMap m_edits; std::vector m_infeasible_rows; - std::auto_ptr m_objective; - std::auto_ptr m_artificial; + std::unique_ptr m_objective; + std::unique_ptr m_artificial; Symbol::Id m_id_tick; }; diff --git a/kiwi/variable.h b/kiwi/variable.h index eaee93f2..11862f89 100644 --- a/kiwi/variable.h +++ b/kiwi/variable.h @@ -100,7 +100,7 @@ class Variable ~VariableData() {} std::string m_name; - std::auto_ptr m_context; + std::unique_ptr m_context; double m_value; private: diff --git a/kiwi/version.h b/kiwi/version.h index 197f3ce1..8d83c114 100644 --- a/kiwi/version.h +++ b/kiwi/version.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -8,7 +8,7 @@ #pragma once #define KIWI_MAJOR_VERSION 1 -#define KIWI_MINOR_VERSION 0 -#define KIWI_MICRO_VERSION 1 +#define KIWI_MINOR_VERSION 2 +#define KIWI_MICRO_VERSION 0 #define KIWI_VERSION_HEX 0x010001 -#define KIWI_VERSION "1.1.0" +#define KIWI_VERSION "1.2.0" diff --git a/py/constraint.cpp b/py/constraint.cpp index 2fbdd0ae..08b8376a 100644 --- a/py/constraint.cpp +++ b/py/constraint.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -7,17 +7,18 @@ |----------------------------------------------------------------------------*/ #include #include -#include +#include #include -#include "pythonhelpers.h" #include "types.h" #include "util.h" +namespace kiwisolver +{ -using namespace PythonHelpers; - +namespace +{ -static PyObject* +PyObject* Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "expression", "op", "strength", 0 }; @@ -29,14 +30,14 @@ Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &pyexpr, &pyop, &pystrength ) ) return 0; if( !Expression::TypeCheck( pyexpr ) ) - return py_expected_type_fail( pyexpr, "Expression" ); + return cppy::type_error( pyexpr, "Expression" ); kiwi::RelationalOperator op; if( !convert_to_relational_op( pyop, op ) ) return 0; double strength = kiwi::strength::required; if( pystrength && !convert_to_strength( pystrength, strength ) ) return 0; - PyObjectPtr pycn( PyType_GenericNew( type, args, kwargs ) ); + cppy::ptr pycn( PyType_GenericNew( type, args, kwargs ) ); if( !pycn ) return 0; Constraint* cn = reinterpret_cast( pycn.get() ); @@ -49,14 +50,14 @@ Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Constraint_clear( Constraint* self ) { Py_CLEAR( self->expression ); } -static int +int Constraint_traverse( Constraint* self, visitproc visit, void* arg ) { Py_VISIT( self->expression ); @@ -64,7 +65,7 @@ Constraint_traverse( Constraint* self, visitproc visit, void* arg ) } -static void +void Constraint_dealloc( Constraint* self ) { PyObject_GC_UnTrack( self ); @@ -74,7 +75,7 @@ Constraint_dealloc( Constraint* self ) } -static PyObject* +PyObject* Constraint_repr( Constraint* self ) { std::stringstream stream; @@ -102,45 +103,45 @@ Constraint_repr( Constraint* self ) break; } stream << " | strength = " << self->constraint.strength(); - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } -static PyObject* +PyObject* Constraint_expression( Constraint* self ) { - return newref( self->expression ); + return cppy::incref( self->expression ); } -static PyObject* +PyObject* Constraint_op( Constraint* self ) { PyObject* res = 0; switch( self->constraint.op() ) { case kiwi::OP_EQ: - res = FROM_STRING( "==" ); + res = PyUnicode_FromString( "==" ); break; case kiwi::OP_LE: - res = FROM_STRING( "<=" ); + res = PyUnicode_FromString( "<=" ); break; case kiwi::OP_GE: - res = FROM_STRING( ">=" ); + res = PyUnicode_FromString( ">=" ); break; } return res; } -static PyObject* +PyObject* Constraint_strength( Constraint* self ) { return PyFloat_FromDouble( self->constraint.strength() ); } -static PyObject* +PyObject* Constraint_or( PyObject* pyoldcn, PyObject* value ) { if( !Constraint::TypeCheck( pyoldcn ) ) @@ -148,12 +149,12 @@ Constraint_or( PyObject* pyoldcn, PyObject* value ) double strength; if( !convert_to_strength( value, strength ) ) return 0; - PyObject* pynewcn = PyType_GenericNew( &Constraint_Type, 0, 0 ); + PyObject* pynewcn = PyType_GenericNew( Constraint::TypeObject, 0, 0 ); if( !pynewcn ) return 0; Constraint* oldcn = reinterpret_cast( pyoldcn ); Constraint* newcn = reinterpret_cast( pynewcn ); - newcn->expression = newref( oldcn->expression ); + newcn->expression = cppy::incref( oldcn->expression ); new( &newcn->constraint ) kiwi::Constraint( oldcn->constraint, strength ); return pynewcn; } @@ -171,129 +172,47 @@ Constraint_methods[] = { }; -static PyNumberMethods -Constraint_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_divide */ -#endif - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 - 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)Constraint_or, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)0, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 - (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif +static PyType_Slot Constraint_Type_slots[] = { + { Py_tp_dealloc, void_cast( Constraint_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Constraint_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Constraint_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Constraint_repr ) }, /* tp_repr */ + { Py_tp_methods, void_cast( Constraint_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Constraint_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_or, void_cast( Constraint_or ) }, /* nb_or */ + { 0, 0 }, }; -PyTypeObject Constraint_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Constraint", /* tp_name */ - sizeof( Constraint ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Constraint_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)Constraint_repr, /* tp_repr */ - (PyNumberMethods*)&Constraint_as_number,/* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif - 0, /* Documentation string */ - (traverseproc)Constraint_traverse, /* tp_traverse */ - (inquiry)Constraint_clear, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Constraint_methods,/* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Constraint_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Constraint::TypeObject = NULL; + + +PyType_Spec Constraint::TypeObject_Spec = { + "kiwisolver.Constraint", /* tp_name */ + sizeof( Constraint ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Constraint_Type_slots /* slots */ }; -int import_constraint() +bool Constraint::Ready() { - return PyType_Ready( &Constraint_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver diff --git a/py/expression.cpp b/py/expression.cpp index 5cd34e4c..5e3fcb24 100644 --- a/py/expression.cpp +++ b/py/expression.cpp @@ -1,22 +1,24 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include -#include -#include "pythonhelpers.h" +#include #include "symbolics.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; +namespace kiwisolver +{ +namespace +{ -static PyObject* +PyObject* Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "terms", "constant", 0 }; @@ -26,7 +28,7 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) args, kwargs, "O|O:__new__", const_cast( kwlist ), &pyterms, &pyconstant ) ) return 0; - PyObjectPtr terms( PySequence_Tuple( pyterms ) ); + cppy::ptr terms( PySequence_Tuple( pyterms ) ); if( !terms ) return 0; Py_ssize_t end = PyTuple_GET_SIZE( terms.get() ); @@ -34,7 +36,7 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { PyObject* item = PyTuple_GET_ITEM( terms.get(), i ); if( !Term::TypeCheck( item ) ) - return py_expected_type_fail( item, "Term" ); + return cppy::type_error( item, "Term" ); } double constant = 0.0; if( pyconstant && !convert_to_double( pyconstant, constant ) ) @@ -49,14 +51,14 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Expression_clear( Expression* self ) { Py_CLEAR( self->terms ); } -static int +int Expression_traverse( Expression* self, visitproc visit, void* arg ) { Py_VISIT( self->terms ); @@ -64,7 +66,7 @@ Expression_traverse( Expression* self, visitproc visit, void* arg ) } -static void +void Expression_dealloc( Expression* self ) { PyObject_GC_UnTrack( self ); @@ -73,7 +75,7 @@ Expression_dealloc( Expression* self ) } -static PyObject* +PyObject* Expression_repr( Expression* self ) { std::stringstream stream; @@ -87,25 +89,25 @@ Expression_repr( Expression* self ) stream << " + "; } stream << self->constant; - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } -static PyObject* +PyObject* Expression_terms( Expression* self ) { - return newref( self->terms ); + return cppy::incref( self->terms ); } -static PyObject* +PyObject* Expression_constant( Expression* self ) { return PyFloat_FromDouble( self->constant ); } -static PyObject* +PyObject* Expression_value( Expression* self ) { double result = self->constant; @@ -121,42 +123,42 @@ Expression_value( Expression* self ) } -static PyObject* +PyObject* Expression_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Expression_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -194,133 +196,52 @@ Expression_methods[] = { }; -static PyNumberMethods -Expression_as_number = { - (binaryfunc)Expression_add, /* nb_add */ - (binaryfunc)Expression_sub, /* nb_subtract */ - (binaryfunc)Expression_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Expression_div, /* nb_divide */ -#endif - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Expression_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 - 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif - 0, /* nb_int */ -#if PY_MAJOR_VERSION >= 3 - (void *)0, /* nb_reserved */ -#else - 0, /* nb_long */ -#endif - 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Expression_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 - (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif +static PyType_Slot Expression_Type_slots[] = { + { Py_tp_dealloc, void_cast( Expression_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Expression_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Expression_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Expression_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Expression_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Expression_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Expression_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Expression_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Expression_sub ) }, /* nb_sub */ + { Py_nb_multiply, void_cast( Expression_mul ) }, /* nb_mul */ + { Py_nb_negative, void_cast( Expression_neg ) }, /* nb_neg */ + { Py_nb_true_divide, void_cast( Expression_div ) }, /* nb_div */ + { 0, 0 }, }; -PyTypeObject Expression_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Expression", /* tp_name */ - sizeof( Expression ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Expression_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)Expression_repr, /* tp_repr */ - (PyNumberMethods*)&Expression_as_number,/* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif - 0, /* Documentation string */ - (traverseproc)Expression_traverse, /* tp_traverse */ - (inquiry)Expression_clear, /* tp_clear */ - (richcmpfunc)Expression_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Expression_methods,/* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Expression_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Expression::TypeObject = NULL; + + +PyType_Spec Expression::TypeObject_Spec = { + "kiwisolver.Expression", /* tp_name */ + sizeof( Expression ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Expression_Type_slots /* slots */ }; -int import_expression() +bool Expression::Ready() { - return PyType_Ready( &Expression_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namesapce kiwisolver diff --git a/py/kiwisolver.cpp b/py/kiwisolver.cpp index 54b333a2..4d4f5023 100644 --- a/py/kiwisolver.cpp +++ b/py/kiwisolver.cpp @@ -1,86 +1,187 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include +#include #include -#include "pythonhelpers.h" #include "types.h" -#define PY_KIWI_VERSION "1.1.0" +#define PY_KIWI_VERSION "1.2.0.dev" + +namespace +{ + + +bool ready_types() +{ + using namespace kiwisolver; + if( !Variable::Ready() ) + { + return false; + } + if( !Term::Ready() ) + { + return false; + } + if( !Expression::Ready() ) + { + return false; + } + if( !Constraint::Ready() ) + { + return false; + } + if( !strength::Ready() ) + { + return false; + } + if( !Solver::Ready() ) + { + return false; + } + return true; +} + +bool add_objects( PyObject* mod ) +{ + using namespace kiwisolver; + + cppy::ptr kiwiversion( PyUnicode_FromString( KIWI_VERSION ) ); + if( !kiwiversion ) + { + return false; + } + cppy::ptr pyversion( PyUnicode_FromString( PY_KIWI_VERSION ) ); + if( !pyversion ) + { + return false; + } + cppy::ptr pystrength( PyType_GenericNew( strength::TypeObject, 0, 0 ) ); + if( !pystrength ) + { + return false; + } + + if( PyModule_AddObject( mod, "__version__", pyversion.get() ) < 0 ) + { + return false; + } + pyversion.release(); + + if( PyModule_AddObject( mod, "__kiwi_version__", kiwiversion.get() ) < 0 ) + { + return false; + } + kiwiversion.release(); + + if( PyModule_AddObject( mod, "strength", pystrength.get() ) < 0 ) + { + return false; + } + pystrength.release(); + + // Variable + cppy::ptr var( pyobject_cast( Variable::TypeObject ) ); + if( PyModule_AddObject( mod, "Variable", var.get() ) < 0 ) + { + return false; + } + var.release(); + + // Term + cppy::ptr term( pyobject_cast( Term::TypeObject ) ); + if( PyModule_AddObject( mod, "Term", term.get() ) < 0 ) + { + return false; + } + term.release(); + + // Expression + cppy::ptr expr( pyobject_cast( Expression::TypeObject ) ); + if( PyModule_AddObject( mod, "Expression", expr.get() ) < 0 ) + { + return false; + } + expr.release(); + + // Constraint + cppy::ptr cons( pyobject_cast( Constraint::TypeObject ) ); + if( PyModule_AddObject( mod, "Constraint", cons.get() ) < 0 ) + { + return false; + } + cons.release(); + + cppy::ptr solver( pyobject_cast( Solver::TypeObject ) ); + if( PyModule_AddObject( mod, "Solver", solver.get() ) < 0 ) + { + return false; + } + solver.release(); + + PyModule_AddObject( mod, "DuplicateConstraint", DuplicateConstraint ); + PyModule_AddObject( mod, "UnsatisfiableConstraint", UnsatisfiableConstraint ); + PyModule_AddObject( mod, "UnknownConstraint", UnknownConstraint ); + PyModule_AddObject( mod, "DuplicateEditVariable", DuplicateEditVariable ); + PyModule_AddObject( mod, "UnknownEditVariable", UnknownEditVariable ); + PyModule_AddObject( mod, "BadRequiredStrength", BadRequiredStrength ); + + return true; +} + + +int +catom_modexec( PyObject *mod ) +{ + if( !ready_types() ) + { + return -1; + } + if( !kiwisolver::init_exceptions() ) + { + return -1; + } + if( !add_objects( mod ) ) + { + return -1; + } + + + return 0; +} -using namespace PythonHelpers; static PyMethodDef kiwisolver_methods[] = { { 0 } // Sentinel }; -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef kiwisolver_moduledef = { - PyModuleDef_HEAD_INIT, - "kiwisolver", - NULL, - sizeof( struct module_state ), - kiwisolver_methods, - NULL + +PyModuleDef_Slot kiwisolver_slots[] = { + {Py_mod_exec, reinterpret_cast( catom_modexec ) }, + {0, NULL} }; -PyMODINIT_FUNC -PyInit_kiwisolver( void ) -#else -PyMODINIT_FUNC -initkiwisolver( void ) -#endif + +struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "kiwisolver", + "kiwisolver extension module", + 0, + kiwisolver_methods, + kiwisolver_slots, + NULL, + NULL, + NULL +}; + +} // namespace + + +PyMODINIT_FUNC PyInit_kiwisolver( void ) { -#if PY_MAJOR_VERSION >= 3 - PyObject *mod = PyModule_Create( &kiwisolver_moduledef ); -#else - PyObject* mod = Py_InitModule( "kiwisolver", kiwisolver_methods ); -#endif - if( !mod ) - INITERROR; - if( import_variable() < 0 ) - INITERROR; - if( import_term() < 0 ) - INITERROR; - if( import_expression() < 0 ) - INITERROR; - if( import_constraint() < 0 ) - INITERROR; - if( import_solver() < 0 ) - INITERROR; - if( import_strength() < 0 ) - INITERROR; - PyObject* kiwiversion = FROM_STRING( KIWI_VERSION ); - if( !kiwiversion ) - INITERROR; - PyObject* pyversion = FROM_STRING( PY_KIWI_VERSION ); - if( !pyversion ) - INITERROR; - PyObject* pystrength = PyType_GenericNew( &strength_Type, 0, 0 ); - if( !pystrength ) - INITERROR; - - PyModule_AddObject( mod, "__version__", pyversion ); - PyModule_AddObject( mod, "__kiwi_version__", kiwiversion ); - PyModule_AddObject( mod, "strength", pystrength ); - PyModule_AddObject( mod, "Variable", newref( pyobject_cast( &Variable_Type ) ) ); - PyModule_AddObject( mod, "Term", newref( pyobject_cast( &Term_Type ) ) ); - PyModule_AddObject( mod, "Expression", newref( pyobject_cast( &Expression_Type ) ) ); - PyModule_AddObject( mod, "Constraint", newref( pyobject_cast( &Constraint_Type ) ) ); - PyModule_AddObject( mod, "Solver", newref( pyobject_cast( &Solver_Type ) ) ); - PyModule_AddObject( mod, "DuplicateConstraint", newref( DuplicateConstraint ) ); - PyModule_AddObject( mod, "UnsatisfiableConstraint", newref( UnsatisfiableConstraint ) ); - PyModule_AddObject( mod, "UnknownConstraint", newref( UnknownConstraint ) ); - PyModule_AddObject( mod, "DuplicateEditVariable", newref( DuplicateEditVariable ) ); - PyModule_AddObject( mod, "UnknownEditVariable", newref( UnknownEditVariable ) ); - PyModule_AddObject( mod, "BadRequiredStrength", newref( BadRequiredStrength ) ); - -#if PY_MAJOR_VERSION >= 3 - return mod; -#endif + return PyModuleDef_Init( &moduledef ); } diff --git a/py/pythonhelpers.h b/py/pythonhelpers.h deleted file mode 100644 index a9e0db63..00000000 --- a/py/pythonhelpers.h +++ /dev/null @@ -1,771 +0,0 @@ -/*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. -| -| Distributed under the terms of the Modified BSD License. -| -| The full license is in the file COPYING.txt, distributed with this software. -|----------------------------------------------------------------------------*/ -#pragma once -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 -#define FROM_STRING PyUnicode_FromString -#define INITERROR return NULL -#define MOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void) -#else -#define FROM_STRING PyString_FromString -#define INITERROR return -#define MOD_INIT_FUNC(name) PyMODINIT_FUNC init##name(void) -#endif - -#ifndef Py_RETURN_NOTIMPLEMENTED -#define Py_RETURN_NOTIMPLEMENTED \ - return Py_INCREF(Py_NotImplemented), Py_NotImplemented -#endif - -#define pyobject_cast( o ) ( reinterpret_cast( o ) ) -#define pytype_cast( o ) ( reinterpret_cast( o ) ) - -struct module_state { - PyObject *error; -}; - - -namespace PythonHelpers -{ - - -/*----------------------------------------------------------------------------- -| Exception Handling -|----------------------------------------------------------------------------*/ -inline PyObject* -py_bad_internal_call( const char* message ) -{ - PyErr_SetString( PyExc_SystemError, message ); - return 0; -} - - -inline PyObject* -py_type_fail( const char* message ) -{ - PyErr_SetString( PyExc_TypeError, message ); - return 0; -} - - -inline PyObject* -py_expected_type_fail( PyObject* pyobj, const char* expected_type ) -{ - PyErr_Format( - PyExc_TypeError, - "Expected object of type `%s`. Got object of type `%s` instead.", - expected_type, pyobj->ob_type->tp_name - ); - return 0; -} - - -inline PyObject* -py_value_fail( const char* message ) -{ - PyErr_SetString( PyExc_ValueError, message ); - return 0; -} - - -inline PyObject* -py_runtime_fail( const char* message ) -{ - PyErr_SetString( PyExc_RuntimeError, message ); - return 0; -} - - -inline PyObject* -py_attr_fail( const char* message ) -{ - PyErr_SetString( PyExc_AttributeError, message ); - return 0; -} - - -inline PyObject* -py_no_attr_fail( PyObject* pyobj, const char* attr ) -{ - PyErr_Format( - PyExc_AttributeError, - "'%s' object has no attribute '%s'", - pyobj->ob_type->tp_name, attr - ); - return 0; -} - - -/*----------------------------------------------------------------------------- -| Utilities -|----------------------------------------------------------------------------*/ -inline PyObject* -newref( PyObject* pyobj ) -{ - Py_INCREF( pyobj ); - return pyobj; -} - - -inline PyObject* -xnewref( PyObject* pyobj ) -{ - Py_XINCREF( pyobj ); - return pyobj; -} - - -inline PyObject* -py_bool( bool val ) -{ - return newref( val ? Py_True : Py_False ); -} - - -inline PyCFunction -lookup_method( PyTypeObject* type, const char* name ) -{ - PyMethodDef* method = type->tp_methods; - for( ; method->ml_name != 0; ++method ) - { - if( strcmp( method->ml_name, name ) == 0 ) - return method->ml_meth; - } - return 0; -} - - -/*----------------------------------------------------------------------------- -| Object Ptr -|----------------------------------------------------------------------------*/ -class PyObjectPtr { - -public: - - PyObjectPtr() : m_pyobj( 0 ) {} - - PyObjectPtr( const PyObjectPtr& objptr ) : - m_pyobj( PythonHelpers::xnewref( objptr.m_pyobj ) ) {} - - PyObjectPtr( PyObject* pyobj ) : m_pyobj( pyobj ) {} - - ~PyObjectPtr() - { - xdecref_release(); - } - - PyObject* get() const - { - return m_pyobj; - } - - void set( PyObject* pyobj ) - { - PyObject* old = m_pyobj; - m_pyobj = pyobj; - Py_XDECREF( old ); - } - - PyObject* release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - return pyobj; - } - - PyObject* decref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_DECREF( pyobj ); - return pyobj; - } - - PyObject* xdecref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_XDECREF( pyobj ); - return pyobj; - } - - PyObject* incref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_INCREF( pyobj ); - return pyobj; - } - - PyObject* xincref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_XINCREF( pyobj ); - return pyobj; - } - - void incref() const - { - Py_INCREF( m_pyobj ); - } - - void decref() const - { - Py_DECREF( m_pyobj ); - } - - void xincref() const - { - Py_XINCREF( m_pyobj ); - } - - void xdecref() const - { - Py_XDECREF( m_pyobj ); - } - - PyObject* newref() const - { - Py_INCREF( m_pyobj ); - return m_pyobj; - } - - PyObject* xnewref() const - { - Py_XINCREF( m_pyobj ); - return m_pyobj; - } - - size_t refcount() const - { - if( m_pyobj ) - return m_pyobj->ob_refcnt; - return 0; - } - - bool is_true( bool clear_err=true ) const - { - int truth = PyObject_IsTrue( m_pyobj ); - if( truth == 1 ) - return true; - if( truth == 0 ) - return false; - if( clear_err ) - PyErr_Clear(); - return false; - } - - bool is_None() const - { - return m_pyobj == Py_None; - } - - bool is_True() const - { - return m_pyobj == Py_True; - } - - bool is_False() const - { - return m_pyobj == Py_False; - } - - bool load_dict( PyObjectPtr& out, bool forcecreate=false ) - { - PyObject** dict = _PyObject_GetDictPtr( m_pyobj ); - if( !dict ) - return false; - if( forcecreate && !*dict ) - *dict = PyDict_New(); - out = PythonHelpers::xnewref( *dict ); - return true; - } - - bool richcompare( PyObject* other, int opid, bool clear_err=true ) - { - int r = PyObject_RichCompareBool( m_pyobj, other, opid ); - if( r == 1 ) - return true; - if( r == 0 ) - return false; - if( clear_err && PyErr_Occurred() ) - PyErr_Clear(); - return false; - } - - bool richcompare( PyObjectPtr& other, int opid, bool clear_err=true ) - { - return richcompare( other.m_pyobj, opid, clear_err ); - } - - bool hasattr( PyObject* attr ) - { - return PyObject_HasAttr( m_pyobj, attr ) == 1; - } - - bool hasattr( PyObjectPtr& attr ) - { - return PyObject_HasAttr( m_pyobj, attr.get() ) == 1; - } - - bool hasattr( const char* attr ) - { - return PyObject_HasAttrString( m_pyobj, attr ) == 1; - } - - bool hasattr( std::string& attr ) - { - return hasattr( attr.c_str() ); - } - - PyObjectPtr getattr( PyObject* attr ) - { - return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr ) ); - } - - PyObjectPtr getattr( PyObjectPtr& attr ) - { - return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr.get() ) ); - } - - PyObjectPtr getattr( const char* attr ) - { - return PyObjectPtr( PyObject_GetAttrString( m_pyobj, attr ) ); - } - - PyObjectPtr getattr( std::string& attr ) - { - return getattr( attr.c_str() ); - } - - bool setattr( PyObject* attr, PyObject* value ) - { - return PyObject_SetAttr( m_pyobj, attr, value ) == 0; - } - - bool setattr( PyObjectPtr& attr, PyObjectPtr& value ) - { - return PyObject_SetAttr( m_pyobj, attr.get(), value.get() ) == 0; - } - - PyObjectPtr operator()( PyObjectPtr& args ) const - { - return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), 0 ) ); - } - - PyObjectPtr operator()( PyObjectPtr& args, PyObjectPtr& kwargs ) const - { - return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), kwargs.get() ) ); - } - - operator void*() const - { - return static_cast( m_pyobj ); - } - - PyObjectPtr& operator=( const PyObjectPtr& rhs ) - { - PyObject* old = m_pyobj; - m_pyobj = rhs.m_pyobj; - Py_XINCREF( m_pyobj ); - Py_XDECREF( old ); - return *this; - } - - PyObjectPtr& operator=( PyObject* rhs ) - { - PyObject* old = m_pyobj; - m_pyobj = rhs; - Py_XDECREF( old ); - return *this; - } - -protected: - - PyObject* m_pyobj; - -}; - - -inline bool -operator!=( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) -{ - return lhs.get() != rhs.get(); -} - - -inline bool -operator!=( const PyObject* lhs, const PyObjectPtr& rhs ) -{ - return lhs != rhs.get(); -} - - -inline bool -operator!=( const PyObjectPtr& lhs, const PyObject* rhs ) -{ - return lhs.get() != rhs; -} - - -inline bool -operator==( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) -{ - return lhs.get() == rhs.get(); -} - - -inline bool -operator==( const PyObject* lhs, const PyObjectPtr& rhs ) -{ - return lhs == rhs.get(); -} - - -inline bool -operator==( const PyObjectPtr& lhs, const PyObject* rhs ) -{ - return lhs.get() == rhs; -} - - -/*----------------------------------------------------------------------------- -| Tuple Ptr -|----------------------------------------------------------------------------*/ -class PyTuplePtr : public PyObjectPtr { - -public: - - PyTuplePtr() : PyObjectPtr() {} - - PyTuplePtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyTuplePtr( PyObject* pytuple ) : PyObjectPtr( pytuple ) {} - - bool check() - { - return PyTuple_Check( m_pyobj ); - } - - bool check_exact() - { - return PyTuple_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyTuple_GET_SIZE( m_pyobj ); - } - - PyObjectPtr get_item( Py_ssize_t index ) const - { - return PyObjectPtr( PythonHelpers::newref( PyTuple_GET_ITEM( m_pyobj, index ) ) ); - } - - void set_item( Py_ssize_t index, PyObject* pyobj ) - { - PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); - PyTuple_SET_ITEM( m_pyobj, index, pyobj ); - Py_XDECREF( old_item ); - } - - void set_item( Py_ssize_t index, PyObjectPtr& item ) - { - PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); - PyTuple_SET_ITEM( m_pyobj, index, item.get() ); - Py_XINCREF( item.get() ); - Py_XDECREF( old_item ); - } - - // pyobj must not be null, only use to fill a new empty tuple - void initialize( Py_ssize_t index, PyObject* pyobj ) - { - PyTuple_SET_ITEM( m_pyobj, index, pyobj ); - } - - // ptr must not be empty, only use to fill a new empty tuple - void initialize( Py_ssize_t index, PyObjectPtr& item ) - { - PyTuple_SET_ITEM( m_pyobj, index, item.get() ); - Py_INCREF( item.get() ); - } - -}; - - -/*----------------------------------------------------------------------------- -| List Ptr -|----------------------------------------------------------------------------*/ -class PyListPtr : public PyObjectPtr { - -public: - - PyListPtr() : PyObjectPtr() {} - - PyListPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyListPtr( PyObject* pylist ) : PyObjectPtr( pylist ) {} - - bool check() const - { - return PyList_Check( m_pyobj ); - } - - bool check_exact() const - { - return PyList_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyList_GET_SIZE( m_pyobj ); - } - - PyObject* borrow_item( Py_ssize_t index ) const - { - return PyList_GET_ITEM( m_pyobj, index ); - } - - PyObjectPtr get_item( Py_ssize_t index ) const - { - return PyObjectPtr( PythonHelpers::newref( PyList_GET_ITEM( m_pyobj, index ) ) ); - } - - void set_item( Py_ssize_t index, PyObject* pyobj ) const - { - PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); - PyList_SET_ITEM( m_pyobj, index, pyobj ); - Py_XDECREF( old_item ); - } - - void set_item( Py_ssize_t index, PyObjectPtr& item ) const - { - PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); - PyList_SET_ITEM( m_pyobj, index, item.get() ); - Py_XINCREF( item.get() ); - Py_XDECREF( old_item ); - } - - bool del_item( Py_ssize_t index ) const - { - if( PySequence_DelItem( m_pyobj, index ) == -1 ) - return false; - return true; - } - - bool append( PyObjectPtr& pyobj ) const - { - if( PyList_Append( m_pyobj, pyobj.get() ) == 0 ) - return true; - return false; - } - - Py_ssize_t index( PyObjectPtr& item ) const - { - Py_ssize_t maxidx = size(); - for( Py_ssize_t idx = 0; idx < maxidx; idx++ ) - { - PyObjectPtr other( get_item( idx ) ); - if( item.richcompare( other, Py_EQ ) ) - return idx; - } - return -1; - } - -}; - - -/*----------------------------------------------------------------------------- -| Dict Ptr -|----------------------------------------------------------------------------*/ -class PyDictPtr : public PyObjectPtr { - -public: - - PyDictPtr() : PyObjectPtr() {} - - PyDictPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyDictPtr( PyObject* pydict ) : PyObjectPtr( pydict ) {} - - bool check() - { - return PyDict_Check( m_pyobj ); - } - - bool check_exact() - { - return PyDict_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyDict_Size( m_pyobj ); - } - - PyObjectPtr get_item( PyObject* key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key ) ) ) ; - } - - PyObjectPtr get_item( PyObjectPtr& key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key.get() ) ) ); - } - - PyObjectPtr get_item( const char* key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItemString( m_pyobj, key ) ) ); - } - - PyObjectPtr get_item( std::string& key ) const - { - return get_item( key.c_str() ); - } - - bool set_item( PyObject* key, PyObject* value ) const - { - if( PyDict_SetItem( m_pyobj, key, value ) == 0 ) - return true; - return false; - } - - bool set_item( PyObject* key, PyObjectPtr& value ) const - { - if( PyDict_SetItem( m_pyobj, key, value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( PyObjectPtr& key, PyObject* value ) const - { - if( PyDict_SetItem( m_pyobj, key.get(), value ) == 0 ) - return true; - return false; - } - - bool set_item( PyObjectPtr& key, PyObjectPtr& value ) const - { - if( PyDict_SetItem( m_pyobj, key.get(), value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( const char* key, PyObjectPtr& value ) const - { - if( PyDict_SetItemString( m_pyobj, key, value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( const char* key, PyObject* value ) const - { - if( PyDict_SetItemString( m_pyobj, key, value ) == 0 ) - return true; - return false; - } - - bool set_item( std::string& key, PyObjectPtr& value ) const - { - return set_item( key.c_str(), value ); - } - - bool del_item( PyObjectPtr& key ) const - { - if( PyDict_DelItem( m_pyobj, key.get() ) == 0 ) - return true; - return false; - } - - bool del_item( const char* key ) const - { - if( PyDict_DelItemString( m_pyobj, key ) == 0 ) - return true; - return false; - } - - bool del_item( std::string& key ) const - { - return del_item( key.c_str() ); - } - -}; - - -/*----------------------------------------------------------------------------- -| Method Ptr -|----------------------------------------------------------------------------*/ -class PyMethodPtr : public PyObjectPtr { - -public: - - PyMethodPtr() : PyObjectPtr() {} - - PyMethodPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyMethodPtr( PyObject* pymethod ) : PyObjectPtr( pymethod ) {} - - bool check() - { - return PyMethod_Check( m_pyobj ); - } - - PyObjectPtr get_self() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_SELF( m_pyobj ) ) ); - } - - PyObjectPtr get_function() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_FUNCTION( m_pyobj ) ) ); - } - -#if PY_MAJOR_VERSION < 3 - PyObjectPtr get_class() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_CLASS( m_pyobj ) ) ); - } -#endif -}; - - -/*----------------------------------------------------------------------------- -| Weakref Ptr -|----------------------------------------------------------------------------*/ -class PyWeakrefPtr : public PyObjectPtr { - -public: - - PyWeakrefPtr() : PyObjectPtr() {} - - PyWeakrefPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyWeakrefPtr( PyObject* pyweakref ) : PyObjectPtr( pyweakref ) {} - - bool check() - { - return PyWeakref_CheckRef( m_pyobj ); - } - - bool check_exact() - { - return PyWeakref_CheckRefExact( m_pyobj ); - } - - PyObjectPtr get_object() const - { - return PyObjectPtr( PythonHelpers::newref( PyWeakref_GET_OBJECT( m_pyobj ) ) ); - } - -}; - -} // namespace PythonHelpers diff --git a/py/solver.cpp b/py/solver.cpp index 7a2ef23c..144aff7b 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -1,25 +1,27 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include +#include #include -#include "pythonhelpers.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; +namespace kiwisolver +{ +namespace +{ -static PyObject* +PyObject* Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) ) - return py_type_fail( "Solver.__new__ takes no arguments" ); + return cppy::type_error( "Solver.__new__ takes no arguments" ); PyObject* pysolver = PyType_GenericNew( type, args, kwargs ); if( !pysolver ) return 0; @@ -29,7 +31,7 @@ Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Solver_dealloc( Solver* self ) { self->solver.~Solver(); @@ -37,11 +39,11 @@ Solver_dealloc( Solver* self ) } -static PyObject* +PyObject* Solver_addConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); try { @@ -61,11 +63,11 @@ Solver_addConstraint( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_removeConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); try { @@ -80,17 +82,17 @@ Solver_removeConstraint( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_hasConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); - return newref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); + return cppy::incref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); } -static PyObject* +PyObject* Solver_addEditVariable( Solver* self, PyObject* args ) { PyObject* pyvar; @@ -98,7 +100,7 @@ Solver_addEditVariable( Solver* self, PyObject* args ) if( !PyArg_ParseTuple( args, "OO", &pyvar, &pystrength ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double strength; if( !convert_to_strength( pystrength, strength ) ) return 0; @@ -121,11 +123,11 @@ Solver_addEditVariable( Solver* self, PyObject* args ) } -static PyObject* +PyObject* Solver_removeEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) - return py_expected_type_fail( other, "Variable" ); + return cppy::type_error( other, "Variable" ); Variable* var = reinterpret_cast( other ); try { @@ -140,17 +142,17 @@ Solver_removeEditVariable( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_hasEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) - return py_expected_type_fail( other, "Variable" ); + return cppy::type_error( other, "Variable" ); Variable* var = reinterpret_cast( other ); - return newref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); + return cppy::incref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); } -static PyObject* +PyObject* Solver_suggestValue( Solver* self, PyObject* args ) { PyObject* pyvar; @@ -158,7 +160,7 @@ Solver_suggestValue( Solver* self, PyObject* args ) if( !PyArg_ParseTuple( args, "OO", &pyvar, &pyvalue ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double value; if( !convert_to_double( pyvalue, value ) ) return 0; @@ -176,7 +178,7 @@ Solver_suggestValue( Solver* self, PyObject* args ) } -static PyObject* +PyObject* Solver_updateVariables( Solver* self ) { self->solver.updateVariables(); @@ -184,7 +186,7 @@ Solver_updateVariables( Solver* self ) } -static PyObject* +PyObject* Solver_reset( Solver* self ) { self->solver.reset(); @@ -192,15 +194,15 @@ Solver_reset( Solver* self ) } -static PyObject* +PyObject* Solver_dump( Solver* self ) { - PyObjectPtr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); + cppy::ptr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); PyObject_Print( dump_str.get(), stdout, 0 ); Py_RETURN_NONE; } -static PyObject* +PyObject* Solver_dumps( Solver* self ) { return PyUnicode_FromString( self->solver.dumps().c_str() ); @@ -234,62 +236,45 @@ Solver_methods[] = { }; -PyTypeObject Solver_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Solver", /* tp_name */ - sizeof( Solver ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Solver_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)0, /* tp_repr */ - (PyNumberMethods*)0, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)0, /* tp_traverse */ - (inquiry)0, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Solver_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Solver_new, /* tp_new */ - (freefunc)PyObject_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +static PyType_Slot Solver_Type_slots[] = { + { Py_tp_dealloc, void_cast( Solver_dealloc ) }, /* tp_dealloc */ + { Py_tp_methods, void_cast( Solver_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Solver_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */ + { 0, 0 }, +}; + + +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Solver::TypeObject = NULL; + + +PyType_Spec Solver::TypeObject_Spec = { + "kiwisolver.Solver", /* tp_name */ + sizeof( Solver ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Solver_Type_slots /* slots */ }; +bool Solver::Ready() +{ + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; +} + + PyObject* DuplicateConstraint; PyObject* UnsatisfiableConstraint; @@ -303,31 +288,51 @@ PyObject* UnknownEditVariable; PyObject* BadRequiredStrength; -int import_solver() +bool init_exceptions() { DuplicateConstraint = PyErr_NewException( const_cast( "kiwisolver.DuplicateConstraint" ), 0, 0 ); if( !DuplicateConstraint ) - return -1; + { + return false; + } + UnsatisfiableConstraint = PyErr_NewException( const_cast( "kiwisolver.UnsatisfiableConstraint" ), 0, 0 ); if( !UnsatisfiableConstraint ) - return -1; + { + return false; + } + UnknownConstraint = PyErr_NewException( const_cast( "kiwisolver.UnknownConstraint" ), 0, 0 ); if( !UnknownConstraint ) - return -1; + { + return false; + } + DuplicateEditVariable = PyErr_NewException( const_cast( "kiwisolver.DuplicateEditVariable" ), 0, 0 ); if( !DuplicateEditVariable ) - return -1; + { + return false; + } + UnknownEditVariable = PyErr_NewException( const_cast( "kiwisolver.UnknownEditVariable" ), 0, 0 ); if( !UnknownEditVariable ) - return -1; + { + return false; + } + BadRequiredStrength = PyErr_NewException( const_cast( "kiwisolver.BadRequiredStrength" ), 0, 0 ); if( !BadRequiredStrength ) - return -1; - return PyType_Ready( &Solver_Type ); + { + return false; + } + + return true; } + +} // namespace diff --git a/py/strength.cpp b/py/strength.cpp index df1552d1..85e4ef8f 100644 --- a/py/strength.cpp +++ b/py/strength.cpp @@ -1,61 +1,68 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include +#include #include -#include "pythonhelpers.h" #include "util.h" -using namespace PythonHelpers; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdeprecated-writable-strings" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wwrite-strings" +#endif -struct strength +namespace kiwisolver { - PyObject_HEAD; -}; -static void +namespace +{ + + +void strength_dealloc( PyObject* self ) { Py_TYPE( self )->tp_free( self ); } -static PyObject* +PyObject* strength_weak( strength* self ) { return PyFloat_FromDouble( kiwi::strength::weak ); } -static PyObject* +PyObject* strength_medium( strength* self ) { return PyFloat_FromDouble( kiwi::strength::medium ); } -static PyObject* +PyObject* strength_strong( strength* self ) { return PyFloat_FromDouble( kiwi::strength::strong ); } -static PyObject* +PyObject* strength_required( strength* self ) { return PyFloat_FromDouble( kiwi::strength::required ); } -static PyObject* +PyObject* strength_create( strength* self, PyObject* args ) { PyObject* pya; @@ -100,63 +107,43 @@ strength_methods[] = { }; -PyTypeObject strength_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.strength", /* tp_name */ - sizeof( strength ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)strength_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)0, /* tp_repr */ - (PyNumberMethods*)0, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)0, /* tp_traverse */ - (inquiry)0, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)strength_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - strength_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)0, /* tp_new */ - (freefunc)PyObject_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ + +static PyType_Slot strength_Type_slots[] = { + { Py_tp_dealloc, void_cast( strength_dealloc ) }, /* tp_dealloc */ + { Py_tp_getset, void_cast( strength_getset ) }, /* tp_getset */ + { Py_tp_methods, void_cast( strength_methods ) }, /* tp_methods */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */ + { 0, 0 }, }; -int import_strength() +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* strength::TypeObject = NULL; + + +PyType_Spec strength::TypeObject_Spec = { + "kiwisolver.strength", /* tp_name */ + sizeof( strength ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + strength_Type_slots /* slots */ +}; + + +bool strength::Ready() { - return PyType_Ready( &strength_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + + +} // namespace diff --git a/py/symbolics.h b/py/symbolics.h index ac575537..92c3337b 100644 --- a/py/symbolics.h +++ b/py/symbolics.h @@ -1,17 +1,19 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once -#include -#include "pythonhelpers.h" +#include #include "types.h" #include "util.h" +namespace kiwisolver +{ + template struct UnaryInvoke { @@ -61,10 +63,6 @@ struct BinaryInvoke return Invk()( primary, reinterpret_cast( secondary ) ); if( PyFloat_Check( secondary ) ) return Invk()( primary, PyFloat_AS_DOUBLE( secondary ) ); -#if PY_MAJOR_VERSION < 3 - if( PyInt_Check( secondary ) ) - return Invk()( primary, double( PyInt_AS_LONG( secondary ) ) ); -#endif if( PyLong_Check( secondary ) ) { double v = PyLong_AsDouble( secondary ); @@ -90,11 +88,11 @@ struct BinaryMul template<> inline PyObject* BinaryMul::operator()( Variable* first, double second ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( pyobject_cast( first ) ); + term->variable = cppy::incref( pyobject_cast( first ) ); term->coefficient = second; return pyterm; } @@ -103,11 +101,11 @@ PyObject* BinaryMul::operator()( Variable* first, double second ) template<> inline PyObject* BinaryMul::operator()( Term* first, double second ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( first->variable ); + term->variable = cppy::incref( first->variable ); term->coefficient = first->coefficient * second; return pyterm; } @@ -116,12 +114,11 @@ PyObject* BinaryMul::operator()( Term* first, double second ) template<> inline PyObject* BinaryMul::operator()( Expression* first, double second ) { - using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); - PyObjectPtr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); + cppy::ptr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); if( !terms ) return 0; Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); @@ -252,7 +249,7 @@ struct BinaryAdd template<> inline PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -267,8 +264,7 @@ PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, Term* second ) { - using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 ); @@ -278,9 +274,9 @@ PyObject* BinaryAdd::operator()( Expression* first, Term* second ) for( Py_ssize_t i = 0; i < end; ++i ) { PyObject* item = PyTuple_GET_ITEM( first->terms, i ); - PyTuple_SET_ITEM( terms, i, newref( item ) ); + PyTuple_SET_ITEM( terms, i, cppy::incref( item ) ); } - PyTuple_SET_ITEM( terms, end, newref( pyobject_cast( second ) ) ); + PyTuple_SET_ITEM( terms, end, cppy::incref( pyobject_cast( second ) ) ); Expression* expr = reinterpret_cast( pyexpr.get() ); expr->terms = terms; expr->constant = first->constant; @@ -291,7 +287,7 @@ PyObject* BinaryAdd::operator()( Expression* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + cppy::ptr temp( BinaryMul()( second, 1.0 ) ); if( !temp ) return 0; return operator()( first, reinterpret_cast( temp.get() ) ); @@ -301,12 +297,11 @@ PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, double second ) { - using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); - expr->terms = newref( first->terms ); + expr->terms = cppy::incref( first->terms ); expr->constant = first->constant + second; return pyexpr.release(); } @@ -315,7 +310,7 @@ PyObject* BinaryAdd::operator()( Expression* first, double second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, double second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -337,7 +332,7 @@ PyObject* BinaryAdd::operator()( Term* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, Term* second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -352,7 +347,7 @@ PyObject* BinaryAdd::operator()( Term* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + cppy::ptr temp( BinaryMul()( second, 1.0 ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -362,7 +357,7 @@ PyObject* BinaryAdd::operator()( Term* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, double second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -372,7 +367,7 @@ PyObject* BinaryAdd::operator()( Variable* first, double second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -382,7 +377,7 @@ PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -392,7 +387,7 @@ PyObject* BinaryAdd::operator()( Variable* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -440,7 +435,7 @@ PyObject* BinarySub::operator()( Variable* first, double second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -450,7 +445,7 @@ PyObject* BinarySub::operator()( Variable* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -460,7 +455,7 @@ PyObject* BinarySub::operator()( Variable* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -477,7 +472,7 @@ PyObject* BinarySub::operator()( Term* first, double second ) template<> inline PyObject* BinarySub::operator()( Term* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -487,7 +482,7 @@ PyObject* BinarySub::operator()( Term* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Term* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -497,7 +492,7 @@ PyObject* BinarySub::operator()( Term* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Term* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -514,7 +509,7 @@ PyObject* BinarySub::operator()( Expression* first, double second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -524,7 +519,7 @@ PyObject* BinarySub::operator()( Expression* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -534,7 +529,7 @@ PyObject* BinarySub::operator()( Expression* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -544,7 +539,7 @@ PyObject* BinarySub::operator()( Expression* first, Expression* second ) template<> inline PyObject* BinarySub::operator()( double first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -554,7 +549,7 @@ PyObject* BinarySub::operator()( double first, Variable* second ) template<> inline PyObject* BinarySub::operator()( double first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -564,7 +559,7 @@ PyObject* BinarySub::operator()( double first, Term* second ) template<> inline PyObject* BinarySub::operator()( double first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -574,10 +569,10 @@ PyObject* BinarySub::operator()( double first, Expression* second ) template PyObject* makecn( T first, U second, kiwi::RelationalOperator op ) { - PythonHelpers::PyObjectPtr pyexpr( BinarySub()( first, second ) ); + cppy::ptr pyexpr( BinarySub()( first, second ) ); if( !pyexpr ) return 0; - PythonHelpers::PyObjectPtr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); + cppy::ptr pycn( PyType_GenericNew( Constraint::TypeObject, 0, 0 ) ); if( !pycn ) return 0; Constraint* cn = reinterpret_cast( pycn.get() ); @@ -618,3 +613,6 @@ struct CmpGE return makecn( first, second, kiwi::OP_GE ); } }; + + +} // namespace kiwisolver diff --git a/py/term.cpp b/py/term.cpp index 4be64a1e..6d7a3df0 100644 --- a/py/term.cpp +++ b/py/term.cpp @@ -1,22 +1,26 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include -#include -#include "pythonhelpers.h" +#include #include "symbolics.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; +namespace kiwisolver +{ + +namespace +{ -static PyObject* + +PyObject* Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "variable", "coefficient", 0 }; @@ -27,7 +31,7 @@ Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &pyvar, &pycoeff ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double coefficient = 1.0; if( pycoeff && !convert_to_double( pycoeff, coefficient ) ) return 0; @@ -35,20 +39,20 @@ Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) if( !pyterm ) return 0; Term* self = reinterpret_cast( pyterm ); - self->variable = newref( pyvar ); + self->variable = cppy::incref( pyvar ); self->coefficient = coefficient; return pyterm; } -static void +void Term_clear( Term* self ) { Py_CLEAR( self->variable ); } -static int +int Term_traverse( Term* self, visitproc visit, void* arg ) { Py_VISIT( self->variable ); @@ -56,7 +60,7 @@ Term_traverse( Term* self, visitproc visit, void* arg ) } -static void +void Term_dealloc( Term* self ) { PyObject_GC_UnTrack( self ); @@ -65,31 +69,31 @@ Term_dealloc( Term* self ) } -static PyObject* +PyObject* Term_repr( Term* self ) { std::stringstream stream; stream << self->coefficient << " * "; stream << reinterpret_cast( self->variable )->variable.name(); - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } -static PyObject* +PyObject* Term_variable( Term* self ) { - return newref( self->variable ); + return cppy::incref( self->variable ); } -static PyObject* +PyObject* Term_coefficient( Term* self ) { return PyFloat_FromDouble( self->coefficient ); } -static PyObject* +PyObject* Term_value( Term* self ) { Variable* pyvar = reinterpret_cast( self->variable ); @@ -97,42 +101,42 @@ Term_value( Term* self ) } -static PyObject* +PyObject* Term_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Term_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -170,129 +174,52 @@ Term_methods[] = { }; -static PyNumberMethods -Term_as_number = { - (binaryfunc)Term_add, /* nb_add */ - (binaryfunc)Term_sub, /* nb_subtract */ - (binaryfunc)Term_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Term_div, /* nb_divide */ -#endif - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Term_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 - 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Term_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 - (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif +static PyType_Slot Term_Type_slots[] = { + { Py_tp_dealloc, void_cast( Term_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Term_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Term_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Term_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Term_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Term_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Term_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Term_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Term_sub ) }, /* nb_subatract */ + { Py_nb_multiply, void_cast( Term_mul ) }, /* nb_multiply */ + { Py_nb_negative, void_cast( Term_neg ) }, /* nb_negative */ + { Py_nb_true_divide, void_cast( Term_div ) }, /* nb_true_divide */ + { 0, 0 }, }; -PyTypeObject Term_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Term", /* tp_name */ - sizeof( Term ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Term_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)Term_repr, /* tp_repr */ - (PyNumberMethods*)&Term_as_number, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif - 0, /* Documentation string */ - (traverseproc)Term_traverse, /* tp_traverse */ - (inquiry)Term_clear, /* tp_clear */ - (richcmpfunc)Term_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Term_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Term_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Term::TypeObject = NULL; + + +PyType_Spec Term::TypeObject_Spec = { + "kiwisolver.Term", /* tp_name */ + sizeof( Term ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Term_Type_slots /* slots */ }; -int import_term() +bool Term::Ready() { - return PyType_Ready( &Term_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver diff --git a/py/tests/test_constraint.py b/py/tests/test_constraint.py index 449b9b0b..71b8ed97 100644 --- a/py/tests/test_constraint.py +++ b/py/tests/test_constraint.py @@ -52,7 +52,7 @@ def test_constraint_creation2(): with pytest.raises(TypeError) as excinfo: Constraint(v + 1, 1) - assert "unicode" in excinfo.exconly() + assert "str" in excinfo.exconly() with pytest.raises(ValueError) as excinfo: Constraint(v + 1, '!=') diff --git a/py/types.h b/py/types.h index 628efafb..a26fe09a 100644 --- a/py/types.h +++ b/py/types.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -10,30 +10,8 @@ #include -int import_variable(); - -int import_term(); - -int import_expression(); - -int import_constraint(); - -int import_solver(); - -int import_strength(); - - -extern PyTypeObject Variable_Type; - -extern PyTypeObject Term_Type; - -extern PyTypeObject Expression_Type; - -extern PyTypeObject Constraint_Type; - -extern PyTypeObject Solver_Type; - -extern PyTypeObject strength_Type; +namespace kiwisolver +{ extern PyObject* DuplicateConstraint; @@ -48,15 +26,33 @@ extern PyObject* UnknownEditVariable; extern PyObject* BadRequiredStrength; +struct strength +{ + PyObject_HEAD; + + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); +}; + + struct Variable { PyObject_HEAD PyObject* context; kiwi::Variable variable; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Variable_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -67,9 +63,15 @@ struct Term PyObject* variable; double coefficient; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Term_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -80,9 +82,15 @@ struct Expression PyObject* terms; double constant; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Expression_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -93,9 +101,15 @@ struct Constraint PyObject* expression; kiwi::Constraint constraint; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Constraint_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -105,8 +119,20 @@ struct Solver PyObject_HEAD kiwi::Solver solver; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Solver_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; + + +bool init_exceptions(); + + +} // namespace kiwisolver diff --git a/py/util.h b/py/util.h index 78e9cbd0..2796ad91 100644 --- a/py/util.h +++ b/py/util.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -8,12 +8,14 @@ #pragma once #include #include -#include +#include #include -#include "pythonhelpers.h" #include "types.h" +namespace kiwisolver +{ + inline bool convert_to_double( PyObject* obj, double& out ) { @@ -22,13 +24,6 @@ convert_to_double( PyObject* obj, double& out ) out = PyFloat_AS_DOUBLE( obj ); return true; } -#if PY_MAJOR_VERSION < 3 - if( PyInt_Check( obj ) ) - { - out = double( PyInt_AsLong( obj ) ); - return true; - } -#endif if( PyLong_Check( obj ) ) { out = PyLong_AsDouble( obj ); @@ -36,7 +31,7 @@ convert_to_double( PyObject* obj, double& out ) return false; return true; } - PythonHelpers::py_expected_type_fail( obj, "float, int, or long" ); + cppy::type_error( obj, "float, int, or long" ); return false; } @@ -44,19 +39,7 @@ convert_to_double( PyObject* obj, double& out ) inline bool convert_pystr_to_str( PyObject* value, std::string& out ) { -#if PY_MAJOR_VERSION >= 3 out = PyUnicode_AsUTF8( value ); -#else - if( PyUnicode_Check( value ) ) - { - PythonHelpers::PyObjectPtr py_str( PyUnicode_AsUTF8String( value ) ); - if( !py_str ) - return false; // LCOV_EXCL_LINE - out = PyString_AS_STRING( py_str.get() ); - } - else - out = PyString_AS_STRING( value ); -#endif return true; } @@ -64,13 +47,8 @@ convert_pystr_to_str( PyObject* value, std::string& out ) inline bool convert_to_strength( PyObject* value, double& out ) { -#if PY_MAJOR_VERSION >= 3 if( PyUnicode_Check( value ) ) { -#else - if( PyString_Check( value ) | PyUnicode_Check( value )) - { -#endif std::string str; if( !convert_pystr_to_str( value, str ) ) return false; @@ -103,19 +81,11 @@ convert_to_strength( PyObject* value, double& out ) inline bool convert_to_relational_op( PyObject* value, kiwi::RelationalOperator& out ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( value ) ) { - PythonHelpers::py_expected_type_fail( value, "unicode" ); + cppy::type_error( value, "str" ); return false; } -#else - if( !(PyString_Check( value ) | PyUnicode_Check( value ) ) ) - { - PythonHelpers::py_expected_type_fail( value, "str or unicode" ); - return false; - } -#endif std::string str; if( !convert_pystr_to_str( value, str ) ) return false; @@ -142,7 +112,7 @@ inline PyObject* make_terms( const std::map& coeffs ) { typedef std::map::const_iterator iter_t; - PythonHelpers::PyObjectPtr terms( PyTuple_New( coeffs.size() ) ); + cppy::ptr terms( PyTuple_New( coeffs.size() ) ); if( !terms ) return 0; Py_ssize_t size = PyTuple_GET_SIZE( terms.get() ); @@ -153,11 +123,11 @@ make_terms( const std::map& coeffs ) iter_t end = coeffs.end(); for( ; it != end; ++it, ++i ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( it->first ); + term->variable = cppy::incref( it->first ); term->coefficient = it->second; PyTuple_SET_ITEM( terms.get(), i, pyterm ); } @@ -177,10 +147,10 @@ reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression Term* term = reinterpret_cast( item ); coeffs[ term->variable ] += term->coefficient; } - PythonHelpers::PyObjectPtr terms( make_terms( coeffs ) ); + cppy::ptr terms( make_terms( coeffs ) ); if( !terms ) return 0; - PyObject* pynewexpr = PyType_GenericNew( &Expression_Type, 0, 0 ); + PyObject* pynewexpr = PyType_GenericNew( Expression::TypeObject, 0, 0 ); if( !pynewexpr ) return 0; Expression* newexpr = reinterpret_cast( pynewexpr ); @@ -228,3 +198,6 @@ pyop_str( int op ) return ""; } } + + +} // namespace kiwisolver diff --git a/py/variable.cpp b/py/variable.cpp index a622e852..24b568a3 100644 --- a/py/variable.cpp +++ b/py/variable.cpp @@ -1,22 +1,26 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include +#include #include -#include "pythonhelpers.h" #include "symbolics.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; +namespace kiwisolver +{ + + +namespace +{ -static PyObject* +PyObject* Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "name", "context", 0 }; @@ -28,24 +32,17 @@ Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &name, &context ) ) return 0; - PyObjectPtr pyvar( PyType_GenericNew( type, args, kwargs ) ); + cppy::ptr pyvar( PyType_GenericNew( type, args, kwargs ) ); if( !pyvar ) return 0; Variable* self = reinterpret_cast( pyvar.get() ); - self->context = xnewref( context ); + self->context = cppy::xincref( context ); if( name != 0 ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( name ) ) - return py_expected_type_fail( name, "unicode" ); -#else - if( !( PyString_Check( name ) | PyUnicode_Check( name ) ) ) - { - return py_expected_type_fail( name, "str or unicode" ); - } -#endif + return cppy::type_error( name, "str" ); std::string c_name; if( !convert_pystr_to_str(name, c_name) ) return 0; // LCOV_EXCL_LINE @@ -60,14 +57,14 @@ Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Variable_clear( Variable* self ) { Py_CLEAR( self->context ); } -static int +int Variable_traverse( Variable* self, visitproc visit, void* arg ) { Py_VISIT( self->context ); @@ -75,7 +72,7 @@ Variable_traverse( Variable* self, visitproc visit, void* arg ) } -static void +void Variable_dealloc( Variable* self ) { PyObject_GC_UnTrack( self ); @@ -85,32 +82,25 @@ Variable_dealloc( Variable* self ) } -static PyObject* +PyObject* Variable_repr( Variable* self ) { - return FROM_STRING( self->variable.name().c_str() ); + return PyUnicode_FromString( self->variable.name().c_str() ); } -static PyObject* +PyObject* Variable_name( Variable* self ) { - return FROM_STRING( self->variable.name().c_str() ); + return PyUnicode_FromString( self->variable.name().c_str() ); } -static PyObject* +PyObject* Variable_setName( Variable* self, PyObject* pystr ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( pystr ) ) - return py_expected_type_fail( pystr, "unicode" ); -#else - if( !(PyString_Check( pystr ) | PyUnicode_Check( pystr ) ) ) - { - return py_expected_type_fail( pystr, "str or unicode" ); - } -#endif + return cppy::type_error( pystr, "str" ); std::string str; if( !convert_pystr_to_str( pystr, str ) ) return 0; @@ -119,71 +109,71 @@ Variable_setName( Variable* self, PyObject* pystr ) } -static PyObject* +PyObject* Variable_context( Variable* self ) { if( self->context ) - return newref( self->context ); + return cppy::incref( self->context ); Py_RETURN_NONE; } -static PyObject* +PyObject* Variable_setContext( Variable* self, PyObject* value ) { if( value != self->context ) { PyObject* temp = self->context; - self->context = newref( value ); + self->context = cppy::incref( value ); Py_XDECREF( temp ); } Py_RETURN_NONE; } -static PyObject* +PyObject* Variable_value( Variable* self ) { return PyFloat_FromDouble( self->variable.value() ); } -static PyObject* +PyObject* Variable_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Variable_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -225,129 +215,52 @@ Variable_methods[] = { }; -static PyNumberMethods -Variable_as_number = { - (binaryfunc)Variable_add, /* nb_add */ - (binaryfunc)Variable_sub, /* nb_subtract */ - (binaryfunc)Variable_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Variable_div, /* nb_divide */ -#endif - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Variable_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 - 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Variable_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 - (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif +static PyType_Slot Variable_Type_slots[] = { + { Py_tp_dealloc, void_cast( Variable_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Variable_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Variable_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Variable_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Variable_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Variable_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Variable_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Variable_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Variable_sub ) }, /* nb_subtract */ + { Py_nb_multiply, void_cast( Variable_mul ) }, /* nb_multiply */ + { Py_nb_negative, void_cast( Variable_neg ) }, /* nb_negative */ + { Py_nb_true_divide, void_cast( Variable_div ) }, /* nb_true_divide */ + { 0, 0 }, }; -PyTypeObject Variable_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Variable", /* tp_name */ - sizeof( Variable ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Variable_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)Variable_repr, /* tp_repr */ - (PyNumberMethods*)&Variable_as_number, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif - 0, /* Documentation string */ - (traverseproc)Variable_traverse, /* tp_traverse */ - (inquiry)Variable_clear, /* tp_clear */ - (richcmpfunc)Variable_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Variable_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Variable_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Variable::TypeObject = NULL; + + +PyType_Spec Variable::TypeObject_Spec = { + "kiwisolver.Variable", /* tp_name */ + sizeof( Variable ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Variable_Type_slots /* slots */ }; -int import_variable() +bool Variable::Ready() { - return PyType_Ready( &Variable_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver diff --git a/releasenotes.rst b/releasenotes.rst index 3c4be3c1..f21f7f4d 100644 --- a/releasenotes.rst +++ b/releasenotes.rst @@ -1,13 +1,18 @@ Kiwi Release Notes ================== +Wrappers 1.2.0 | Solver 1.2.0 | unreleased +------------------------------------------ +- make the the c++ part of the code c++11 compliant PR #55 +- use cppy for Python/C bindings PR #55 + Wrappers 1.1.0 | Solver 1.1.0 | 04/24/2019 ------------------------------------------ - prevent attempting a dual optimize on a dummy row PR #56 closes #15 - add ``dump`` and ``dumps`` methods to inspect the internal state of the solver PR #56 - test on Python 3.7 PR #51 -- improvemnts to setup.py and tests PR #46 #50 +- improvements to setup.py and tests PR #46 #50 Wrappers 1.0.1 | Solver 1.0.0 | 10/24/2017 ------------------------------------------ diff --git a/setup.py b/setup.py index fce7d4e5..96ee54c6 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ +import sys from setuptools import setup, Extension from setuptools.command.build_ext import build_ext @@ -35,20 +36,29 @@ class BuildExt(build_ext): """ c_opts = { - 'msvc': ['/EHsc'] + 'msvc': ['/EHsc', '/std:c++11'], + 'unix': ['-std=c++11'] } def build_extensions(self): + + # Delayed import of cppy to let setup_requires install it if necessary + import cppy + ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) for ext in self.extensions: + ext.include_dirs.insert(0, cppy.get_include()) ext.extra_compile_args = opts + if sys.platform == 'darwin': + ext.extra_compile_args += ['-stdlib=libc++'] + ext.extra_link_args += ['-stdlib=libc++'] build_ext.build_extensions(self) setup( name='kiwisolver', - version='1.1.0', + version='1.2.0.dev', author='The Nucleic Development Team', author_email='sccolbert@gmail.com', url='https://github.com/nucleic/kiwi', @@ -58,17 +68,14 @@ def build_extensions(self): classifiers=[ # https://pypi.org/pypi?%3Aaction=list_classifiers 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', - install_requires=[], + python_requires='>=3.5', + setup_requires=['cppy'], ext_modules=ext_modules, cmdclass={'build_ext': BuildExt}, )