Python module segfaults python interpreter but runs in jupyter?

I’m still (slowly) working on getting cadquery to work on aarch64-darwin.

I’ve never figured out how to build OCP (which I think requires a newer MacOS SDK than nixpkgs offers), but someone recently made a wheel available for aarch64-darwin, so I’ve made it a lot farther!

Curiously, the below flake builds for me, and runs in jupyter – hooray!

Oddly, it segfaults when I try to run it from the console.

{
  description = "";

  outputs = {
    self,
    nixpkgs,
  }: let
    system = "aarch64-darwin";
    pkgs = import nixpkgs {
      inherit system;
    };
    py = pkgs.python311;
    pyPkgs = py.pkgs;
    inherit (pyPkgs) toPythonModule buildPythonPackage;
  in {
    packages.${system} = {
      default =
        py.withPackages
        (ps:
          [
            ps.jupyterlab
          ]
          ++ [self.outputs.packages.${system}.cadquery]);
      cadquery = let
        pname = "cadquery";
        version = "2.4.0";
      in
        buildPythonPackage {
          inherit pname version;
          format = "pyproject";
          propagatedBuildInputs =
            (with pyPkgs; [
              ezdxf
              multimethod
              nptyping
              numpy
              pyparsing
              setuptools
              typing-extensions
              typish
            ])
            ++ (with self.outputs.packages.${system}; [
              casadi-python
              nlopt-python
              ocp
            ]);
          src = pkgs.fetchPypi {
            inherit pname version;
            hash = "sha256-OOjjAgYPLlCUOrD4rKuYXDenMAnpcsewJ2fJC+9/s+c=";
          };
          # nativeCheckInputs = [pyPkgs.pytestCheckHook];
          # segfault? why??
          # pythonImportsCheck = ["cadquery"];
        };
      nlopt-python =
        toPythonModule
        ((pkgs.nlopt.overrideAttrs
            (prev: {
              configureFlags = builtins.filter (each: each != "--without-python") prev.configureFlags or [];
              buildInputs = prev.buildInputs or [] ++ [pkgs.swig];
              propagatedBuildInputs = prev.propagatedBuildInputs or [] ++ [pyPkgs.numpy];
              nativeCheckInputs =
                prev.nativeCheckInputs
                or []
                ++ [
                  (py.withPackages (ps: [ps.numpy]))
                  pyPkgs.pythonImportsCheckHook
                ];
            }))
          .overrideAttrs (_: {
            doCheck = true;
            pythonImportsCheck = ["nlopt"];
          }));

      casadi-python = let
        version = "3.6.4";
      in
        toPythonModule
        ((pkgs.stdenv.mkDerivation {
            inherit version;
            name = "casadi";
            buildInputs = with pkgs; [
              py
              swig4
            ];
            nativeBuildInputs = [pkgs.cmake];
            nativeCheckImports = [pyPkgs.pythonImportsCheckHook];
            propagatedBuildInputs = [pyPkgs.numpy];
            cmakeFlags = [
              "-DWITH_PYTHON=ON"
              "-DWITH_PYTHON3=ON"
              "-DPYTHON_PREFIX=${placeholder "out"}/${py.sitePackages}"
              # Fails with:
              # Broken paths found in a .pc file! /nix/path/to/lib/pkgconfig/tinyxml2.pc
              "-DWITH_TINYXML=OFF"
            ];
            src = pkgs.fetchFromGitHub {
              "owner" = "casadi";
              "repo" = "casadi";
              rev = version;
              hash = "sha256-BfUpSXbllQUNn5BsBQ+ZmQ15OLOp/QegT8igOxSgukE=";
            };
          })
          .overrideAttrs (_: {
            pythonImportsCheck = ["casadi-fake"];
          }));

      ocp = let
        version = "7.7.2";
      in
        buildPythonPackage {
          inherit version;
          pname = "cadquery-ocp";
          format = "wheel";
          src = pkgs.fetchurl {
            url = "https://github.com/jdegenstein/ocp-build-system/releases/download/${version}_macos_arm64/cadquery_ocp-${version}-cp311-cp311-macosx_11_0_arm64.whl";
            hash = "sha256-CkWRA+3g77VVH0Sc75KmXHb1HEdUVl81OvWMZQdHoys=";
          };
          pythonImportsCheck = ["OCP"];
        };
    };

    apps.${system}.default = {
      type = "app";
      program = "${self.packages.${system}.default}/bin/jupyter-lab";
    };
  };
}

If I uncomment even just the import check:

$ nix build .#cadquery
...
Check whether the following modules can be imported: cadquery
/nix/store/6p3h1n7hmgymb7gm6yh9zvm6dckxfg0x-python-imports-check-hook.sh/nix-support/setup-hook: line 10: 89952 Segmentation fault: 11  /nix/store/7b0rz3bnx7msw2wawkv1hhn5lqf1b0wi-python3-3.11.6/bin/python3.11 -c 'import os; import importlib; list(map(lambda mod: importlib.import_module(mod), os.environ["pythonImportsCheck"].split()))'

If I comment out the checks for cadquery (the pytest and import check), it builds and runs fine, and jupyterlab is able to import cadquery, and it seems to work!

$ nix build .#cadquery
$ nix build
$ otool -L result/bin/python
result/bin/python:
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
$ ./result/bin/python -m jupyterlab

But oddly importing from the python interpreter fails with a segfault (as expected with the failure of the import check):

$ ./result/bin/python -c 'import cadquery'
Segmentation fault: 11

I’ve uploaded ./result/bin/python -v -c 'import cadquery' to gist:bf7654c1871d384a966d0b1eb6633793 · GitHub; I’m not sure if the permission denied errors for creating .pyc (or was it __pycache__) files are relevant?

Any ideas why this module would run fine in jupyterlab but fail to even import (segfault) from the interpreter?

Never quite figured this out. In the latest iteration, I installed the OCP wheel from a different source and things seem to work. ¯\_(ツ)_/¯