@@ -134,23 +134,37 @@ def _same_path(cls, path1, path2):
134134 return path1 == path2
135135
136136 @classmethod
137- def _abspath_resolve_leaf (cls , path ):
138- """Returns the absolute path, resolving links to the last component.
139-
140- If there's a cycle, os.path.abspath(path) is returned
137+ def _getpath_realpath (cls , path ):
138+ """Mimics getpath.realpath
139+
140+ It only mimics it for HAVE_READLINK.
141+ There are a few differences listed here:
142+ - we ensure that we have a resolvable abspath first
143+ (i.e. exists and no symlink loop)
144+ - we stop if a candidate does not resolve to the same file
145+ (this can happen with normpath)
141146 """
142- path = os .path .abspath (path )
143- result = path
144- while os .path .islink (result ):
145- link = os .readlink (result )
146- if os .path .isabs (link ):
147- result = link
148- else :
149- result = os .path .join (os .path .dirname (result ), link )
150- result = os .path .abspath (result )
151- if result == path :
152- # circular links
153- break
147+ result = os .path .abspath (path )
148+ try :
149+ real_path = os .path .realpath (result , strict = True )
150+ except OSError :
151+ logger .warning ('Unable to resolve %r real path' , result )
152+ return result
153+ if sysconfig .get_config_var ('HAVE_READLINK' ):
154+ while os .path .islink (result ):
155+ link = os .readlink (result )
156+ if os .path .isabs (link ):
157+ candidate = link
158+ else :
159+ candidate = os .path .join (os .path .dirname (result ), link )
160+ candidate = os .path .normpath (candidate )
161+ # shall exists and be the same file as the original one
162+ valid = os .path .exists (candidate ) and os .path .samefile (real_path , candidate )
163+ if not valid :
164+ logger .warning ('Stopped resolving %r because %r is not the same file' ,
165+ result , candidate )
166+ break
167+ result = candidate
154168 return result
155169
156170 def ensure_directories (self , env_dir ):
@@ -184,7 +198,7 @@ def create_if_needed(d):
184198 'check that your PATH environment variable is '
185199 'correctly set.' )
186200 # only resolve executable symlinks, not the full chain, see gh-106045
187- dirname , exename = os .path .split (self ._abspath_resolve_leaf (executable ))
201+ dirname , exename = os .path .split (self ._getpath_realpath (executable ))
188202 if sys .platform == 'win32' :
189203 # Always create the simplest name in the venv. It will either be a
190204 # link back to executable, or a copy of the appropriate launcher
0 commit comments