scikit-build / scikit-build-core

A next generation Python CMake adaptor and Python API for plugins
https://scikit-build-core.readthedocs.io
Apache License 2.0
236 stars 47 forks source link

Editable install fails in Jupyter notebook #926

Open tikuma-lsuhsc opened 1 week ago

tikuma-lsuhsc commented 1 week ago

It appears that editable install fails when trying to import the package in Jupyter notebook.

Here is an example:

In my current project, I have a native package (parselmouth) built with the scikit build core (as git submodule) so I can troubleshoot/enhance the custom features that I'd like to have for this project.

In this project's venv, I installed parselmouth by

pip install --no-build-isolation --config-settings=editable.rebuild=true -Cbuild-dir=./build -ve ./parselmouth

Then, in a Python script, I confirm the expected behavior:

import parselmouth

triggers cmake --build and cmake --install at run-time.

In a Jupyter notebook, however, import parselmouth prints

Running cmake --build & --install in [...snipped path...]\parselmouth\build

then leaves the following error:

{
    "name": "UnsupportedOperation",
    "message": "fileno",
    "stack": "---------------------------------------------------------------------------
UnsupportedOperation                      Traceback (most recent call last)
Cell In[1], line 1
----> 1 import parselmouth

File <frozen importlib._bootstrap>:1176, in _find_and_load(name, import_)

File <frozen importlib._bootstrap>:1138, in _find_and_load_unlocked(name, import_)

File <frozen importlib._bootstrap>:1078, in _find_spec(name, path, target)

File [...snipped path...]\\.venv\\Lib\\site-packages\\_praat_parselmouth_editable.py:94, in ScikitBuildRedirectingFinder.find_spec(self, fullname, path, target)
     92     redir = self.known_wheel_files[fullname]
     93     if self.rebuild_flag:
---> 94         self.rebuild()
     95     return importlib.util.spec_from_file_location(
     96         fullname,
     97         os.path.join(self.dir, redir),
   (...)
    100         else None,
    101     )
    102 if fullname in self.known_source_files:

File [...snipped path...]\\.venv\\Lib\\site-packages\\_praat_parselmouth_editable.py:131, in ScikitBuildRedirectingFinder.rebuild(self)
    128 if verbose:
    129     print(f\"Running cmake --build & --install in {self.path}\")  # noqa: T201
--> 131 result = subprocess.run(
    132     [\"cmake\", \"--build\", \".\", *self.build_options],
    133     cwd=self.path,
    134     stdout=sys.stderr if verbose else subprocess.PIPE,
    135     env=env,
    136     check=False,
    137     text=True,
    138 )
    139 if result.returncode and verbose:
    140     print(  # noqa: T201
    141         f\"ERROR: {result.stdout}\",
    142         file=sys.stderr,
    143     )

File ~\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py:548, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
    545     kwargs['stdout'] = PIPE
    546     kwargs['stderr'] = PIPE
--> 548 with Popen(*popenargs, **kwargs) as process:
    549     try:
    550         stdout, stderr = process.communicate(input, timeout=timeout)

File ~\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py:992, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask, pipesize, process_group)
    973         raise ValueError(f\"User ID cannot be negative, got {uid}\")
    975 # Input and output objects. The general principle is like
    976 # this:
    977 #
   (...)
    987 # are -1 when not using PIPEs. The child objects are -1
    988 # when not redirecting.
    990 (p2cread, p2cwrite,
    991  c2pread, c2pwrite,
--> 992  errread, errwrite) = self._get_handles(stdin, stdout, stderr)
    994 # From here on, raising exceptions may cause file descriptor leakage
    995 
    996 # We wrap OS handles *before* launching the child, otherwise a
    997 # quickly terminating child could make our fds unwrappable
    998 # (see #8458).
   1000 if _mswindows:

File ~\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py:1384, in Popen._get_handles(self, stdin, stdout, stderr)
   1381     c2pwrite = msvcrt.get_osfhandle(stdout)
   1382 else:
   1383     # Assuming file-like object
-> 1384     c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
   1385 c2pwrite = self._make_inheritable(c2pwrite)
   1387 if stderr is None:

File [...snipped path...]\\.venv\\Lib\\site-packages\\ipykernel\\iostream.py:371, in OutStream.fileno(self)
    369     return self._original_stdstream_copy
    370 msg = \"fileno\"
--> 371 raise io.UnsupportedOperation(msg)

UnsupportedOperation: fileno"
}

I'd be happy to provide additional information.

henryiii commented 1 week ago

Can you try editable.verbose = false? Looks like iPython is taking over the output streams and not handling them correctly.

tikuma-lsuhsc commented 1 week ago

Yes, this worked:

pip install --no-build-isolation --config-settings=editable.rebuild=true --config-settings=editable.verbose=false -Cbuild-dir=./build -ve ./parselmouth

Thanks!