Skip to content

Commit

Permalink
Merge pull request #190 from lysnikolaou/support-free-threading
Browse files Browse the repository at this point in the history
Add CI/wheel builder for the free-threaded build
  • Loading branch information
MatthieuDartiailh authored Dec 9, 2024
2 parents 24d3b2e + 1123323 commit 0048efc
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 29 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,20 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', 'pypy-3.10']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.13t', 'pypy-3.10']
exclude:
# TODO: Reenable this once there's a setuptools release that sets Py_GIL_DISABLED
# correctly on Windows
- os: windows-latest
python-version: 3.13t
steps:
- uses: actions/checkout@v4
- name: Get history and tags for SCM versioning to work
run: |
git fetch --prune --unshallow
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: Quansight-Labs/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ jobs:
- name: Build wheels
if: matrix.manylinux_version == 'manylinux2014'
env:
CIBW_BUILD: "cp311-* cp312-* cp313-* pp310-*"
CIBW_BUILD: "cp311-* cp312-* cp313-* cp313t-* pp310-*"
CIBW_SKIP: "cp313t-win*"
CIBW_ENABLE: python-freethreading
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
CIBW_ARCHS_LINUX: ${{ matrix.archs }}
CIBW_ARCHS_WINDOWS: auto64
Expand Down
46 changes: 39 additions & 7 deletions py/src/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ Constraint_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
cn->expression = reduce_expression(pyexpr);
if (!cn->expression)
return 0;

ACQUIRE_GLOBAL_LOCK();
kiwi::Expression expr(convert_to_kiwi_expression(cn->expression));
new (&cn->constraint) kiwi::Constraint(expr, op, strength);
RELEASE_GLOBAL_LOCK();

return pycn.release();
}

Expand All @@ -68,7 +72,9 @@ void Constraint_dealloc(Constraint *self)
{
PyObject_GC_UnTrack(self);
Constraint_clear(self);
ACQUIRE_GLOBAL_LOCK();
self->constraint.~Constraint();
RELEASE_GLOBAL_LOCK();
Py_TYPE(self)->tp_free(pyobject_cast(self));
}

Expand All @@ -83,11 +89,21 @@ Constraint_repr(Constraint *self)
PyObject *item = PyTuple_GET_ITEM(expr->terms, i);
Term *term = reinterpret_cast<Term *>(item);
stream << term->coefficient << " * ";
stream << reinterpret_cast<Variable *>(term->variable)->variable.name();
ACQUIRE_GLOBAL_LOCK();
std::string name = reinterpret_cast<Variable *>(term->variable)->variable.name();
RELEASE_GLOBAL_LOCK();
stream << name;
stream << " + ";
}
stream << expr->constant;
switch (self->constraint.op())

ACQUIRE_GLOBAL_LOCK();
kiwi::RelationalOperator op = self->constraint.op();
double strength = self->constraint.strength();
bool violated = self->constraint.violated();
RELEASE_GLOBAL_LOCK();

switch (op)
{
case kiwi::OP_EQ:
stream << " == 0";
Expand All @@ -99,8 +115,8 @@ Constraint_repr(Constraint *self)
stream << " >= 0";
break;
}
stream << " | strength = " << self->constraint.strength();
if (self->constraint.violated())
stream << " | strength = " << strength;
if (violated)
{
stream << " (VIOLATED)";
}
Expand All @@ -117,7 +133,12 @@ PyObject *
Constraint_op(Constraint *self)
{
PyObject *res = 0;
switch (self->constraint.op())

ACQUIRE_GLOBAL_LOCK();
kiwi::RelationalOperator op = self->constraint.op();
RELEASE_GLOBAL_LOCK();

switch (op)
{
case kiwi::OP_EQ:
res = PyUnicode_FromString("==");
Expand All @@ -135,13 +156,20 @@ Constraint_op(Constraint *self)
PyObject *
Constraint_strength(Constraint *self)
{
return PyFloat_FromDouble(self->constraint.strength());
ACQUIRE_GLOBAL_LOCK();
double strength = self->constraint.strength();
RELEASE_GLOBAL_LOCK();
return PyFloat_FromDouble(strength);
}

PyObject *
Constraint_violated(Constraint *self)
{
if (self->constraint.violated()) {
ACQUIRE_GLOBAL_LOCK();
bool violated = self->constraint.violated();
RELEASE_GLOBAL_LOCK();

if (violated) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
Expand All @@ -162,7 +190,11 @@ Constraint_or(PyObject *pyoldcn, PyObject *value)
Constraint *oldcn = reinterpret_cast<Constraint *>(pyoldcn);
Constraint *newcn = reinterpret_cast<Constraint *>(pynewcn);
newcn->expression = cppy::incref(oldcn->expression);

ACQUIRE_GLOBAL_LOCK();
new (&newcn->constraint) kiwi::Constraint(oldcn->constraint, strength);
RELEASE_GLOBAL_LOCK();

return pynewcn;
}

Expand Down
10 changes: 8 additions & 2 deletions py/src/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ Expression_repr( Expression* self )
PyObject* item = PyTuple_GET_ITEM( self->terms, i );
Term* term = reinterpret_cast<Term*>( item );
stream << term->coefficient << " * ";
stream << reinterpret_cast<Variable*>( term->variable )->variable.name();
ACQUIRE_GLOBAL_LOCK();
std::string name = reinterpret_cast<Variable*>( term->variable )->variable.name();
RELEASE_GLOBAL_LOCK();
stream << name;
stream << " + ";
}
stream << self->constant;
Expand Down Expand Up @@ -121,7 +124,10 @@ Expression_value( Expression* self )
PyObject* item = PyTuple_GET_ITEM( self->terms, i );
Term* term = reinterpret_cast<Term*>( item );
Variable* pyvar = reinterpret_cast<Variable*>( term->variable );
result += term->coefficient * pyvar->variable.value();
ACQUIRE_GLOBAL_LOCK();
double value = pyvar->variable.value();
RELEASE_GLOBAL_LOCK();
result += term->coefficient * value;
}
return PyFloat_FromDouble( result );
}
Expand Down
10 changes: 10 additions & 0 deletions py/src/kiwisolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#include <mutex>
#include <cppy/cppy.h>
#include <kiwi/kiwi.h>
#include "types.h"
#include "version.h"

namespace kiwisolver
{

std::recursive_mutex global_lock;

}

namespace
{
Expand Down Expand Up @@ -162,6 +169,9 @@ kiwisolver_methods[] = {

PyModuleDef_Slot kiwisolver_slots[] = {
{Py_mod_exec, reinterpret_cast<void*>( kiwi_modexec ) },
#ifdef Py_GIL_DISABLED
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
#endif
{0, NULL}
};

Expand Down
47 changes: 43 additions & 4 deletions py/src/solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
if( !pysolver )
return 0;
Solver* self = reinterpret_cast<Solver*>( pysolver );
ACQUIRE_GLOBAL_LOCK();
new( &self->solver ) kiwi::Solver();
RELEASE_GLOBAL_LOCK();
return pysolver;
}


void
Solver_dealloc( Solver* self )
{
ACQUIRE_GLOBAL_LOCK();
self->solver.~Solver();
RELEASE_GLOBAL_LOCK();
Py_TYPE( self )->tp_free( pyobject_cast( self ) );
}

Expand All @@ -47,15 +51,19 @@ Solver_addConstraint( Solver* self, PyObject* other )
Constraint* cn = reinterpret_cast<Constraint*>( other );
try
{
ACQUIRE_GLOBAL_LOCK();
self->solver.addConstraint( cn->constraint );
RELEASE_GLOBAL_LOCK();
}
catch( const kiwi::DuplicateConstraint& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( DuplicateConstraint, other );
return 0;
}
catch( const kiwi::UnsatisfiableConstraint& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( UnsatisfiableConstraint, other );
return 0;
}
Expand All @@ -71,10 +79,13 @@ Solver_removeConstraint( Solver* self, PyObject* other )
Constraint* cn = reinterpret_cast<Constraint*>( other );
try
{
ACQUIRE_GLOBAL_LOCK();
self->solver.removeConstraint( cn->constraint );
RELEASE_GLOBAL_LOCK();
}
catch( const kiwi::UnknownConstraint& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( UnknownConstraint, other );
return 0;
}
Expand All @@ -88,7 +99,12 @@ Solver_hasConstraint( Solver* self, PyObject* other )
if( !Constraint::TypeCheck( other ) )
return cppy::type_error( other, "Constraint" );
Constraint* cn = reinterpret_cast<Constraint*>( other );
return cppy::incref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False );

ACQUIRE_GLOBAL_LOCK();
bool hasConstraint = self->solver.hasConstraint( cn->constraint );
RELEASE_GLOBAL_LOCK();

return cppy::incref( hasConstraint ? Py_True : Py_False );
}


Expand All @@ -107,15 +123,19 @@ Solver_addEditVariable( Solver* self, PyObject* args )
Variable* var = reinterpret_cast<Variable*>( pyvar );
try
{
ACQUIRE_GLOBAL_LOCK();
self->solver.addEditVariable( var->variable, strength );
RELEASE_GLOBAL_LOCK();
}
catch( const kiwi::DuplicateEditVariable& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( DuplicateEditVariable, pyvar );
return 0;
}
catch( const kiwi::BadRequiredStrength& e )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetString( BadRequiredStrength, e.what() );
return 0;
}
Expand All @@ -131,10 +151,13 @@ Solver_removeEditVariable( Solver* self, PyObject* other )
Variable* var = reinterpret_cast<Variable*>( other );
try
{
ACQUIRE_GLOBAL_LOCK();
self->solver.removeEditVariable( var->variable );
RELEASE_GLOBAL_LOCK();
}
catch( const kiwi::UnknownEditVariable& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( UnknownEditVariable, other );
return 0;
}
Expand All @@ -148,7 +171,10 @@ Solver_hasEditVariable( Solver* self, PyObject* other )
if( !Variable::TypeCheck( other ) )
return cppy::type_error( other, "Variable" );
Variable* var = reinterpret_cast<Variable*>( other );
return cppy::incref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False );
ACQUIRE_GLOBAL_LOCK();
bool hasEditVariable = self->solver.hasEditVariable( var->variable );
RELEASE_GLOBAL_LOCK();
return cppy::incref( hasEditVariable ? Py_True : Py_False );
}


Expand All @@ -167,10 +193,13 @@ Solver_suggestValue( Solver* self, PyObject* args )
Variable* var = reinterpret_cast<Variable*>( pyvar );
try
{
ACQUIRE_GLOBAL_LOCK();
self->solver.suggestValue( var->variable, value );
RELEASE_GLOBAL_LOCK();
}
catch( const kiwi::UnknownEditVariable& )
{
RELEASE_GLOBAL_LOCK();
PyErr_SetObject( UnknownEditVariable, pyvar );
return 0;
}
Expand All @@ -181,31 +210,41 @@ Solver_suggestValue( Solver* self, PyObject* args )
PyObject*
Solver_updateVariables( Solver* self )
{
ACQUIRE_GLOBAL_LOCK();
self->solver.updateVariables();
RELEASE_GLOBAL_LOCK();
Py_RETURN_NONE;
}


PyObject*
Solver_reset( Solver* self )
{
ACQUIRE_GLOBAL_LOCK();
self->solver.reset();
RELEASE_GLOBAL_LOCK();
Py_RETURN_NONE;
}


PyObject*
Solver_dump( Solver* self )
{
cppy::ptr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) );
ACQUIRE_GLOBAL_LOCK();
std::string dumps = self->solver.dumps();
RELEASE_GLOBAL_LOCK();
cppy::ptr dump_str( PyUnicode_FromString( dumps.c_str() ) );
PyObject_Print( dump_str.get(), stdout, 0 );
Py_RETURN_NONE;
}

PyObject*
Solver_dumps( Solver* self )
{
return PyUnicode_FromString( self->solver.dumps().c_str() );
ACQUIRE_GLOBAL_LOCK();
std::string dumps = self->solver.dumps();
RELEASE_GLOBAL_LOCK();
return PyUnicode_FromString( dumps.c_str() );
}

static PyMethodDef
Expand Down
2 changes: 2 additions & 0 deletions py/src/symbolics.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,10 @@ PyObject* makecn( T first, U second, kiwi::RelationalOperator op )
cn->expression = reduce_expression( pyexpr.get() );
if( !cn->expression )
return 0;
ACQUIRE_GLOBAL_LOCK();
kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) );
new( &cn->constraint ) kiwi::Constraint( expr, op, kiwi::strength::required );
RELEASE_GLOBAL_LOCK();
return pycn.release();
}

Expand Down
Loading

0 comments on commit 0048efc

Please sign in to comment.