-
-
Notifications
You must be signed in to change notification settings - Fork 628
Description
So I was trying to work out how to organize interaction between tox and pip-tools for my new/future tox-lock
plugin where I'd generate constraint files for tox environments.
I figured I'd try making a named pipe in tox's tmp dir and pass that as an input to pip-compile
. Then, I'd write whatever's necessary from the tox plugin into the pipe and be done with it. What could go wrong, right?
When I started testing pip-compile
even w/o tox in the mix, it was hanging on me.
Eventually, I started running the editable install of pip-tools and stuck breakpoint()
into a couple of places. I've found out that it reads from the pipe just fine for the first time. It goes through the dependency resolution and all's good.
It's when it gets to writing the output, the problems begin. _determine_linesep(strategy="preserve", filenames=('-', './named-pipe'))
gets called with the default "preserve"
strategy. And in this mode, it attempts opening the file by path in binary mode, getting stuck on existing_file.read()
:
pip-tools/piptools/scripts/compile.py
Line 60 in 78f18e1
existing_text = existing_file.read() |
Environment Versions
- OS Type: Gentoo Linux (shouldn't be relevant)
- Python version:
3.13.7
(shouldn't be relevant) - pip version:
25.2
- pip-tools version:
main
(78f18e1)
Steps to replicate
(*NIX instructions)
mkfifo -m0600 named-pipe
python -m piptools compile -o- ./named-pipe
<-- this is where it gets stuck- (in a separate terminal tab)
echo build > named-pipe
<-- this unblocks reading the file contents for the first time and letspip-compile
compute the dep tree, then getting stuck in_determine_linesep()
- another
echo build > named-pipe
unblockspip-compile
because it's able to read something from the pipe again
Expected result
It shouldn't hang.
Actual result
It does.
Since I'm writing to stdout, the first loop iteration in _determine_linesep()
gets a "file not found" and skips to the second iteration that gets stuck. It also occurred to me that if a file called -
were to exist, it'd mess up the linesep detection since it just reads from stdin at the beginning but there, it'd read from a file. This is a loosely related bug, I suppose.
I've tested that it's possible to identify a named file programmatically though stat
or pathlib
, but maybe this needs a more generic "is file" check.
$ python -u -m piptools compile -o- ./named-pipe
> ~/src/github/jazzband/pip-tools/piptools/scripts/compile.py(58)_determine_linesep()
-> breakpoint()
(Pdb) args
strategy = 'preserve'
filenames = ('-', './named-pipe')
(Pdb) pp locals()
{'filenames': ('-', './named-pipe'), 'fname': '-', 'strategy': 'preserve'}
(Pdb) c
> ~/src/github/jazzband/pip-tools/piptools/scripts/compile.py(58)_determine_linesep()
-> breakpoint()
(Pdb) pp locals()
{'filenames': ('-', './named-pipe'),
'fname': './named-pipe',
'strategy': 'preserve'}
(Pdb) pp os.path.isfile(fname)
False
(Pdb) pp os.path.isjunction(fname)
False
(Pdb) pp os.path.islink(fname)
False
(Pdb) pp os.path.ismount(fname)
False
(Pdb) pp os.path.isdir(fname)
False
(Pdb) pp os.path.isdevdrive(fname)
False
(Pdb) pp os.path.isabs(fname)
False
(Pdb) import stat
(Pdb) stat.S_ISFIFO(os.stat(fname).st_mode)
True
(Pdb) from pathlib import Path
(Pdb) pp Path(fname).is_fifo()
True
This is probably a regression introduced in @AndydeCleyre's #1652 that first appeared in pip-tools v6.10.0. I bet checked the workaround would be is --newline=[lf|crlf|native]
.
@sirosen I think we need a strategy for dealing with this. Perhaps, have some pathlib-like objects reused across multiple layers. Special-casing strings in multiple places clearly doesn't work well. Can we possibly auto-detect the new lines on first read?