Skip to content

Commit d18c74b

Browse files
authored
Merge pull request #990 from compas-dev/realpath
realpath_but_for_real_this_time
2 parents 185a910 + bf77712 commit d18c74b

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
* Fixed symlink expansion for directories relative to the COMPAS installation folder, eg. `compas.DATA` when used from IronPython.
17+
* Fixed the result of `compas.__version__` on dev installs to properly include git hash.
18+
1619
### Removed
1720

1821

src/compas/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
version = LooseVersion(compas.__version__)
4747
versionstring = version.vstring.split('-')[0]
4848

49-
HERE = os.path.dirname(__file__)
49+
HERE = compas._os.realpath(os.path.dirname(__file__))
5050
"""str: Path to the location of the compas package."""
5151

5252
HOME = compas._os.absjoin(HERE, '../..')

src/compas/_os.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
Not intended to be used outside compas* packages.
55
"""
66
import os
7-
import shutil
8-
import sys
7+
import re
98
import tempfile
9+
import shutil
1010
import subprocess
11+
import sys
1112

1213
try:
1314
NotADirectoryError
@@ -16,10 +17,12 @@ class NotADirectoryError(Exception):
1617
pass
1718

1819
PY3 = 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

Comments
 (0)