44Not intended to be used outside compas* packages.
55"""
66import os
7- import shutil
8- import sys
7+ import re
98import tempfile
9+ import shutil
1010import subprocess
11+ import sys
1112
1213try :
1314 NotADirectoryError
@@ -16,10 +17,12 @@ class NotADirectoryError(Exception):
1617 pass
1718
1819PY3 = sys .version_info [0 ] == 3
20+ SYMLINK_REGEX = re .compile (r"\n.*\<SYMLINKD\>\s(.*)\s\[(.*)\]\r" )
1921
2022
2123__all__ = [
2224 'absjoin' ,
25+ 'realpath' ,
2326 'create_symlink' ,
2427 'create_symlinks' ,
2528 'remove_symlink' ,
@@ -252,6 +255,49 @@ def absjoin(*parts):
252255 return os .path .abspath (os .path .join (* parts ))
253256
254257
258+ def realpath (path ):
259+ """Return the canonical path of the specified filename, resolving any symbolic links encountered in the path.
260+
261+ This function uses Python's stdlib `os.path.realpath` in most cases,
262+ except when inside IronPython because (guess what?) it is broken and
263+ doesn't really eliminate sym links, so, we fallback to a different
264+ way to identifying symlinks in that situation.
265+ """
266+ if not PY3 and is_ironpython ():
267+ if is_windows ():
268+ return _realpath_ipy_win (path )
269+ else :
270+ return _realpath_ipy_posix (path )
271+
272+ return os .path .realpath (path )
273+
274+
275+ def _realpath_ipy_win (path ):
276+ dirname = os .path .basename (path )
277+ parent_path = os .path .join (path , '..' )
278+
279+ args = 'dir /c "{}" /Al' .format (parent_path )
280+ process = subprocess .Popen (args , shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
281+
282+ output , _error = process .communicate ()
283+ matches = SYMLINK_REGEX .finditer (output )
284+ for match in matches :
285+ match_name = match .groups ()[0 ].strip ()
286+ match_link = match .groups ()[1 ]
287+
288+ if match_name == dirname :
289+ return match_link
290+
291+ return path
292+
293+
294+ def _realpath_ipy_posix (path ):
295+ args = 'readlink -f "{}"' .format (path )
296+ process = subprocess .Popen (args , shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
297+ output , _error = process .communicate ()
298+ return output
299+
300+
255301# Cache whatever symlink function works (native or polyfill)
256302_os_symlink = None
257303
0 commit comments