-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compare folders correctly when folder path contains symbolic links (Fixes #9452) #9453
Conversation
Hello! I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the |
Hi there! Could you update this PR, and add a test for the usecase you're thinking of? |
Hi! Yes I could. I tried to just set up the development environment for pip as described here, using a fresh copy of the Here is an example. I run the tox output(venv) PS C:\repos\pip> tox -e py38 -- -n auto --exitfirst
GLOB sdist-make: C:\repos\pip\setup.py
py38 recreate: C:\repos\pip\.tox\py38
py38 installdeps: -rC:\repos\pip/tools/requirements/tests.txt
py38 inst: C:\repos\pip\.tox\.tmp\package\2\pip-21.1.dev0.zip
py38 installed: apipkg==1.5,atomicwrites==1.4.0,attrs==20.3.0,cffi==1.14.5,colorama==0.4.4,coverage==5.5,cryptography==3.4.7,execnet==1.8.0,freezegun==1.1.0,iniconfig==1.1.1,packaging==20.9,pip @ file:///C:/repos/pip/.tox/.tmp/package/2/pip-21.1.dev0.zip,pluggy==0.13.1,pretend==1.0.9,py==1.10.0,pycparser==2.20,pyparsing==2.4.7,pytest==6.2.2,pytest-cov==2.11.1,pytest-forked==1.3.0,pytest-rerunfailures==9.1.1,pytest-xdist==2.2.1,python-dateutil==2.8.1,scripttest==1.3,setuptools==54.1.2,six==1.15.0,toml==0.10.2,virtualenv==16.7.10,Werkzeug==1.0.1,wheel==0.36.2
py38 run-test-pre: PYTHONHASHSEED='578'
py38 run-test-pre: commands[0] | python -c 'import shutil, sys; shutil.rmtree(sys.argv[1], ignore_errors=True)' 'C:\repos\pip/tests/data/common_wheels'
py38 run-test-pre: commands[1] | python 'C:\repos\pip/tools/tox_pip.py' wheel -w 'C:\repos\pip/tests/data/common_wheels' -r 'C:\repos\pip/tools/requirements/tests-common_wheels.txt'
Collecting setuptools>=40.8.0
Using cached setuptools-54.2.0-py3-none-any.whl (785 kB)
Collecting wheel
Using cached wheel-0.36.2-py2.py3-none-any.whl (35 kB)
Collecting coverage>=4.4
Using cached coverage-5.5-cp38-cp38-win_amd64.whl (211 kB)
Saved c:\repos\pip\tests\data\common_wheels\coverage-5.5-cp38-cp38-win_amd64.whl
Saved c:\repos\pip\tests\data\common_wheels\wheel-0.36.2-py2.py3-none-any.whl
Saved c:\repos\pip\tests\data\common_wheels\setuptools-54.2.0-py3-none-any.whl
py38 run-test: commands[0] | pytest -n auto --exitfirst
===================================================================== test session starts =====================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
cachedir: .tox\py38\.pytest_cache
rootdir: C:\repos\pip, configfile: setup.cfg
plugins: cov-2.11.1, forked-1.3.0, rerunfailures-9.1.1, xdist-2.2.1
gw0 [2149] / gw1 [2149] / gw2 [2149] / gw3 [2149] / gw4 [2149] / gw5 [2149] / gw6 [2149] / gw7 [2149]
....E
=========================================================================== ERRORS ============================================================================
___________________________________________________ ERROR at setup of test_build_env_allow_only_one_install ___________________________________________________
[gw4] win32 -- Python 3.8.5 c:\repos\pip\.tox\py38\scripts\python.exe
request = <SubRequest 'virtualenv_template' for <Function test_build_env_allow_only_one_install>>
tmpdir_factory = TempdirFactory(_tmppath_factory=TempPathFactory(_given_basetemp=WindowsPath('C:/Users/niko/AppData/Local/Temp/pytest-o...t at 0x0000027410B3E8E0>, _basetemp=WindowsPath('C:/Users/niko/AppData/Local/Temp/pytest-of-niko/pytest-4/popen-gw4')))
pip_src = Path('C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\pip_src0\\pip_src')
setuptools_install = Path('C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\setuptools0\\install')
coverage_install = Path('C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\coverage0\\install')
@pytest.fixture(scope="session")
def virtualenv_template(
request, tmpdir_factory, pip_src, setuptools_install, coverage_install
):
if request.config.getoption("--use-venv"):
venv_type = "venv"
else:
venv_type = "virtualenv"
# Create the virtual environment
tmpdir = Path(str(tmpdir_factory.mktemp("virtualenv")))
> venv = VirtualEnvironment(tmpdir.joinpath("venv_orig"), venv_type=venv_type)
tests\conftest.py:342:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests\lib\venv.py:27: in __init__
self._create()
tests\lib\venv.py:58: in _create
_virtualenv.create_environment(
.tox\py38\lib\site-packages\virtualenv.py:1161: in create_environment
install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear, symlink=symlink)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
home_dir = 'C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\virtualenv0\\venv_orig'
lib_dir = 'C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\virtualenv0\\venv_orig\\Lib'
inc_dir = 'C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\virtualenv0\\venv_orig\\Include'
bin_dir = 'C:\\Users\\niko\\AppData\\Local\\Temp\\pytest-of-niko\\pytest-4\\popen-gw4\\virtualenv0\\venv_orig\\Scripts', site_packages = False, clear = False
symlink = True
def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, symlink=True):
"""Install just the base environment, no distutils patches etc"""
if sys.executable.startswith(bin_dir):
print("Please use the *system* python to run this script")
return
if clear:
rm_tree(lib_dir)
# FIXME: why not delete it?
# Maybe it should delete everything with #!/path/to/venv/python in it
logger.notify("Not deleting %s", bin_dir)
if hasattr(sys, "real_prefix"):
logger.notify("Using real prefix %r", sys.real_prefix)
prefix = sys.real_prefix
elif hasattr(sys, "base_prefix"):
logger.notify("Using base prefix %r", sys.base_prefix)
prefix = sys.base_prefix
else:
prefix = sys.prefix
prefix = os.path.abspath(prefix)
mkdir(lib_dir)
fix_lib64(lib_dir, symlink)
stdlib_dirs = [os.path.dirname(os.__file__)]
if IS_WIN:
stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), "DLLs"))
elif IS_DARWIN:
stdlib_dirs.append(join(stdlib_dirs[0], "site-packages"))
if hasattr(os, "symlink"):
logger.info("Symlinking Python bootstrap modules")
else:
logger.info("Copying Python bootstrap modules")
logger.indent += 2
try:
# copy required files...
for stdlib_dir in stdlib_dirs:
copy_required_files(stdlib_dir, lib_dir, symlink)
# ...and modules
copy_required_modules(home_dir, symlink)
copy_license(prefix, home_dir, lib_dir, symlink)
finally:
logger.indent -= 2
# ...copy tcl/tk
if IS_WIN:
copy_tcltk(prefix, home_dir, symlink)
mkdir(join(lib_dir, "site-packages"))
import site
site_filename = site.__file__
if site_filename.endswith(".pyc") or site_filename.endswith(".pyo"):
site_filename = site_filename[:-1]
elif site_filename.endswith("$py.class"):
site_filename = site_filename.replace("$py.class", ".py")
site_filename_dst = change_prefix(site_filename, home_dir)
site_dir = os.path.dirname(site_filename_dst)
writefile(site_filename_dst, SITE_PY)
writefile(join(site_dir, "orig-prefix.txt"), prefix)
site_packages_filename = join(site_dir, "no-global-site-packages.txt")
if not site_packages:
writefile(site_packages_filename, "")
if IS_PYPY or IS_WIN:
standard_lib_include_dir = join(prefix, "include")
else:
standard_lib_include_dir = join(prefix, "include", PY_VERSION + ABI_FLAGS)
if os.path.exists(standard_lib_include_dir):
copy_include_dir(standard_lib_include_dir, inc_dir, symlink)
else:
logger.debug("No include dir %s", standard_lib_include_dir)
platform_include_dir = distutils.sysconfig.get_python_inc(plat_specific=1)
if platform_include_dir != standard_lib_include_dir:
platform_include_dest = distutils.sysconfig.get_python_inc(plat_specific=1, prefix=home_dir)
if platform_include_dir == platform_include_dest:
# Do platinc_dest manually due to a CPython bug;
# not http://bugs.python.org/issue3386 but a close cousin
platform_include_dest = subst_path(platform_include_dir, prefix, home_dir)
if platform_include_dest:
# PyPy's stdinc_dir and prefix are relative to the original binary
# (traversing virtualenvs), whereas the platinc_dir is relative to
# the inner virtualenv and ignores the prefix argument.
# This seems more evolved than designed.
copy_include_dir(platform_include_dir, platform_include_dest, symlink)
# pypy never uses exec_prefix, just ignore it
if os.path.realpath(sys.exec_prefix) != os.path.realpath(prefix) and not IS_PYPY:
if IS_WIN:
exec_dir = join(sys.exec_prefix, "lib")
else:
exec_dir = join(sys.exec_prefix, "lib", PY_VERSION)
copy_required_files(exec_dir, lib_dir, symlink)
mkdir(bin_dir)
py_executable = join(bin_dir, os.path.basename(sys.executable))
if "Python.framework" in prefix or "Python3.framework" in prefix:
# OS X framework builds cause validation to break
# https://github.com/pypa/virtualenv/issues/322
if os.environ.get("__PYVENV_LAUNCHER__"):
del os.environ["__PYVENV_LAUNCHER__"]
if re.search(r"/Python(?:-32|-64)*$", py_executable):
# The name of the python executable is not quite what
# we want, rename it.
py_executable = os.path.join(os.path.dirname(py_executable), "python")
logger.notify("New %s executable in %s", EXPECTED_EXE, py_executable)
pc_build_dir = os.path.dirname(sys.executable)
pyd_pth = os.path.join(lib_dir, "site-packages", "virtualenv_builddir_pyd.pth")
if IS_WIN and os.path.exists(os.path.join(pc_build_dir, "build.bat")):
logger.notify("Detected python running from build directory %s", pc_build_dir)
logger.notify("Writing .pth file linking to build directory for *.pyd files")
writefile(pyd_pth, pc_build_dir)
else:
if os.path.exists(pyd_pth):
logger.info("Deleting %s (not Windows env or not build directory python)", pyd_pth)
os.unlink(pyd_pth)
if sys.executable != py_executable:
# FIXME: could I just hard link?
executable = sys.executable
shutil.copyfile(executable, py_executable)
make_exe(py_executable)
if IS_WIN or IS_CYGWIN:
python_w = os.path.join(os.path.dirname(sys.executable), "pythonw.exe")
if os.path.exists(python_w):
logger.info("Also created pythonw.exe")
shutil.copyfile(python_w, os.path.join(os.path.dirname(py_executable), "pythonw.exe"))
python_d = os.path.join(os.path.dirname(sys.executable), "python_d.exe")
python_d_dest = os.path.join(os.path.dirname(py_executable), "python_d.exe")
if os.path.exists(python_d):
logger.info("Also created python_d.exe")
shutil.copyfile(python_d, python_d_dest)
elif os.path.exists(python_d_dest):
logger.info("Removed python_d.exe as it is no longer at the source")
os.unlink(python_d_dest)
# we need to copy the DLL to enforce that windows will load the correct one.
# may not exist if we are cygwin.
if IS_PYPY:
py_executable_dll_s = [("libpypy-c.dll", "libpypy_d-c.dll")]
else:
py_executable_dll_s = [
("python{}.dll".format(sys.version_info[0]), "python{}_d.dll".format(sys.version_info[0])),
(
"python{}{}.dll".format(sys.version_info[0], sys.version_info[1]),
"python{}{}_d.dll".format(sys.version_info[0], sys.version_info[1]),
),
]
for py_executable_dll, py_executable_dll_d in py_executable_dll_s:
python_dll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
python_dll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
python_dll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
if os.path.exists(python_dll):
logger.info("Also created %s", py_executable_dll)
shutil.copyfile(python_dll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
if os.path.exists(python_dll_d):
logger.info("Also created %s", py_executable_dll_d)
shutil.copyfile(python_dll_d, python_dll_d_dest)
elif os.path.exists(python_dll_d_dest):
logger.info("Removed %s as the source does not exist", python_dll_d_dest)
os.unlink(python_dll_d_dest)
if IS_PYPY:
# make a symlink python --> pypy-c
python_executable = os.path.join(os.path.dirname(py_executable), "python")
if IS_WIN or IS_CYGWIN:
python_executable += ".exe"
logger.info("Also created executable %s", python_executable)
copyfile(py_executable, python_executable, symlink)
if IS_WIN:
for name in ["libexpat.dll", "libeay32.dll", "ssleay32.dll", "sqlite3.dll", "tcl85.dll", "tk85.dll"]:
src = join(prefix, name)
if os.path.exists(src):
copyfile(src, join(bin_dir, name), symlink)
for d in sys.path:
if d.endswith("lib_pypy"):
break
else:
logger.fatal("Could not find lib_pypy in sys.path")
raise SystemExit(3)
logger.info("Copying lib_pypy")
copyfile(d, os.path.join(home_dir, "lib_pypy"), symlink)
if os.path.splitext(os.path.basename(py_executable))[0] != EXPECTED_EXE:
secondary_exe = os.path.join(os.path.dirname(py_executable), EXPECTED_EXE)
py_executable_ext = os.path.splitext(py_executable)[1]
if py_executable_ext.lower() == ".exe":
# python2.4 gives an extension of '.4' :P
secondary_exe += py_executable_ext
if os.path.exists(secondary_exe):
logger.warn(
"Not overwriting existing {} script {} (you must use {})".format(
EXPECTED_EXE, secondary_exe, py_executable
)
)
else:
logger.notify("Also creating executable in %s", secondary_exe)
shutil.copyfile(sys.executable, secondary_exe)
make_exe(secondary_exe)
if ".framework" in prefix:
original_python = None
if "Python.framework" in prefix or "Python3.framework" in prefix:
logger.debug("MacOSX Python framework detected")
# Make sure we use the embedded interpreter inside
# the framework, even if sys.executable points to
# the stub executable in ${sys.prefix}/bin
# See http://groups.google.com/group/python-virtualenv/
# browse_thread/thread/17cab2f85da75951
original_python = os.path.join(prefix, "Resources/Python.app/Contents/MacOS/Python")
if "EPD" in prefix:
logger.debug("EPD framework detected")
original_python = os.path.join(prefix, "bin/python")
shutil.copy(original_python, py_executable)
# Copy the framework's dylib into the virtual
# environment
virtual_lib = os.path.join(home_dir, ".Python")
if os.path.exists(virtual_lib):
os.unlink(virtual_lib)
lib_name = "Python3" if "Python3.framework" in prefix else "Python"
copyfile(os.path.join(prefix, lib_name), virtual_lib, symlink)
# And then change the install_name of the copied python executable
search = (
"@executable_path/../../../../Python3" if "Python3.framework" in prefix else os.path.join(prefix, lib_name)
)
# noinspection PyBroadException
try:
mach_o_change(py_executable, search, "@executable_path/../.Python")
except Exception:
e = sys.exc_info()[1]
logger.warn("Could not call mach_o_change: %s. " "Trying to call install_name_tool instead.", e)
try:
call_subprocess(["install_name_tool", "-change", search, "@executable_path/../.Python", py_executable])
except Exception:
logger.fatal("Could not call install_name_tool -- you must " "have Apple's development tools installed")
raise
if not IS_WIN:
# Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist
py_exe_version_major = "python{}".format(sys.version_info[0])
py_exe_version_major_minor = "python{}.{}".format(sys.version_info[0], sys.version_info[1])
py_exe_no_version = "python"
required_symlinks = [py_exe_no_version, py_exe_version_major, py_exe_version_major_minor]
py_executable_base = os.path.basename(py_executable)
if py_executable_base in required_symlinks:
# Don't try to symlink to yourself.
required_symlinks.remove(py_executable_base)
for pth in required_symlinks:
full_pth = join(bin_dir, pth)
if os.path.exists(full_pth):
os.unlink(full_pth)
if symlink:
os.symlink(py_executable_base, full_pth)
else:
copyfile(py_executable, full_pth, symlink)
cmd = [
py_executable,
"-c",
"import sys;out=sys.stdout;" 'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))',
]
logger.info('Testing executable with %s %s "%s"', *cmd)
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
proc_stdout, proc_stderr = proc.communicate()
except OSError:
e = sys.exc_info()[1]
if e.errno == errno.EACCES:
logger.fatal("ERROR: The executable {} could not be run: {}".format(py_executable, e))
sys.exit(100)
else:
raise e
proc_stdout = proc_stdout.strip().decode("utf-8")
# normalize paths using realpath to ensure that a virtualenv correctly identifies itself even
# when addressed over a symlink
proc_stdout = os.path.normcase(os.path.realpath(proc_stdout))
norm_home_dir = os.path.normcase(os.path.realpath(home_dir))
if hasattr(norm_home_dir, "decode"):
norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding())
if proc_stdout != norm_home_dir:
logger.fatal("ERROR: The executable %s is not functioning", py_executable)
logger.fatal("ERROR: It thinks sys.prefix is {!r} (should be {!r})".format(proc_stdout, norm_home_dir))
logger.fatal("ERROR: virtualenv is not compatible with this system or executable")
if IS_WIN:
logger.fatal(
"Note: some Windows users have reported this error when they "
'installed Python for "Only this user" or have multiple '
"versions of Python installed. Copying the appropriate "
"PythonXX.dll to the virtualenv Scripts/ directory may fix "
"this problem."
)
> sys.exit(100)
E SystemExit: 100
.tox\py38\lib\site-packages\virtualenv.py:1711: SystemExit
-------------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------------
ERROR: The executable C:\Users\niko\AppData\Local\Temp\pytest-of-niko\pytest-4\popen-gw4\virtualenv0\venv_orig\Scripts\python.exe is not functioning
ERROR: It thinks sys.prefix is 'c:\\repos\\pip' (should be 'c:\\users\\niko\\appdata\\local\\temp\\pytest-of-niko\\pytest-4\\popen-gw4\\virtualenv0\\venv_orig')ERROR: virtualenv is not compatible with this system or executable
Note: some Windows users have reported this error when they installed Python for "Only this user" or have multiple versions of Python installed. Copying the appropriate PythonXX.dll to the virtualenv Scripts/ directory may fix this problem.
-------------------------------------------------------------------- Captured stderr setup --------------------------------------------------------------------
No pyvenv.cfg file
====================================================================== warnings summary =======================================================================
.tox\py38\lib\site-packages\pip\_vendor\packaging\version.py:127: 1080 warnings
c:\repos\pip\.tox\py38\lib\site-packages\pip\_vendor\packaging\version.py:127: DeprecationWarning: Creating a LegacyVersion has been deprecated and will be removed in the next major release
warnings.warn(
-- Docs: https://docs.pytest.org/en/stable/warnings.html
=================================================================== short test summary info ===================================================================
ERROR tests/functional/test_build_env.py::test_build_env_allow_only_one_install - SystemExit: 100
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! xdist.dsession.Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================= 4 passed, 1080 warnings, 1 error in 43.74s ========================================================== It says specifically that
I tried copying It also said
Which I don't understand. Where it is checking for Is it possible to setup the development environment, and run the tests on Windows? |
Adding to the previous. I decided to check what is happening when the error is raised. As seen in the example tox output, the error happens at file
Now I understand what the note about missing
This explains why (almost) all the tests will fail. Usually, when a virtual environment is created, there is also a
or
with contents
the temporary python.exe will run without problems:
Now there is a question to be answered: Why tox creates python virtual environments without a |
Digging down the problem. So, the
which will fail, precisely here:
in other words, the script fails when running
Luckily, the
I finally started to get reasonable output! 🎉 The used (non-working) |
Seems that this commit d08f968, which was the latest commit on tox output
|
@np-8 Thanks for posting the analysis here! This is super useful to know about, especially as I'm investigating #8273 right now. ^>^ Could you file a new bug report with the latest failures? Based on a quick look, this seems like bugs in the test suite that we should track and address. :) |
Here you are! :) I try to create a test for the #9452 after running tests is possible without failures on some branch. |
link_pointer = normalize_path(os.path.normcase(fh.readline().strip())) | ||
assert (link_pointer == dist_path), ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, it’s probably better to use Path.resolve(strict=False)
instead. These various path string functions should be removed eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have so much familiarity with the internals of pip
so I really am not the best person to comment. Anyway, I did some testing on the Path.resolve
with a folder that is also symlinked on Windows. C:\cases\myproj
points to C:\Users\niko\Dropbox\myproj
.
In [1]: from pathlib import Path
In [2]: mypath = Path(r'C:\cases\myproj')
In [3]: mypath.resolve()
Out[3]: WindowsPath('C:/Users/niko/Dropbox/myproj')
In [4]: mypath.resolve(strict=True)
Out[4]: WindowsPath('C:/Users/niko/Dropbox/myproj')
In [5]: mypath.resolve(strict=False)
Out[5]: WindowsPath('C:/Users/niko/Dropbox/myproj')
Did not see any difference with strict=True
or strict=False
. Which lines / objects you would change to use Path.resolve
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted something like
pathlib.Path(link_pointer).resolve(strict=False) == pathlib.Path(fh.readline().strip()).resolve(strict=False)
or
The difference between strict=False
and True (the default) is only when the target does not exist; by default a FileNotFoundError
is thrown in strict mode.
Closing since this has bitrotten. |
Fixes #9452