What replaces the now-deprecated Carbon.File.FSResolveAliasFile in Python on OSX?
Asked Answered
L

3

1

In Python 2, I can use the following code to resolve either a MacOS alias or a symbolic link:

from Carbon import File
File.FSResolveAliasFile(alias_fp, True)[0].as_pathname()

where alias_fp is the path to the file I'm curious about, stored as a string (source).

However, the documentation cheerfully tells me that the whole Carbon family of modules is deprecated. What should I be using instead?

EDIT: I believe the code below is a step in the right direction for the PyObjC approach. It doesn't resolve aliases, but it seems to detect them.

from AppKit import NSWorkspace
def is_alias (path):
    uti, err = NSWorkspace.sharedWorkspace().typeOfFile_error_(
        os.path.realpath(path), None)
    if err:
        raise Exception(unicode(err))
    else:
        return "com.apple.alias-file" == uti

(source)

Unfortunately I'm not able to get @Milliways's solution working (knowing nothing about Cocoa) and stuff I find elsewhere on the internet looks far more complicated (perhaps it's handling all kinds of edge cases?).

Lying answered 20/1, 2014 at 21:44 Comment(0)
K
1

The PyObjC bridge lets you access NSURL's bookmark handling, which is the modern (backwards compatible) replacement for aliases:

import os.path
from Foundation import *

def target_of_alias(path):
    url = NSURL.fileURLWithPath_(path)
    bookmarkData, error = NSURL.bookmarkDataWithContentsOfURL_error_(url, None)
    if bookmarkData is None:
        return None
    opts = NSURLBookmarkResolutionWithoutUI | NSURLBookmarkResolutionWithoutMounting
    resolved, stale, error = NSURL.URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, opts, None, None, None)
    return resolved.path()

def resolve_links_and_aliases(path):
    while True:
        alias_target = target_of_alias(path)
        if alias_target:
            path = alias_target
            continue
        if os.path.islink(path):
            path = os.path.realpath(path)
            continue
        return path
Kunlun answered 26/1, 2015 at 18:17 Comment(1)
I added a wrapper function that resolves both aliases and symlinks.Kunlun
I
1

The following Cocoa code will resolve alias.

NSURL *targetOfAlias(NSURL *url) {
    CFErrorRef *errorRef = NULL;
    CFDataRef bookmark = CFURLCreateBookmarkDataFromFile (NULL, (__bridge CFURLRef)url, errorRef);
    if (bookmark == nil) return nil;
    CFURLRef resolvedUrl = CFURLCreateByResolvingBookmarkData (NULL, bookmark, kCFBookmarkResolutionWithoutUIMask, NULL, NULL, false, errorRef);
    CFRelease(bookmark);
    return CFBridgingRelease(resolvedUrl);
}

I don't know how to invoke Cocoa framework from Python, but I am sure someone has done it

The following link shows code to resolve aslias or symlink https://mcmap.net/q/1332143/-how-to-use-mac-finder-to-list-all-aliases-in-a-folder

Incestuous answered 21/1, 2014 at 0:28 Comment(3)
@SevenBits I agree this did not solve the issue, but addressed other issues. Alias is resolvable using CoreFoundation not Carbon. It would be useful to someone who could code a Python wrapper.Incestuous
So hard to figure this out in any language - at least this helped me get it done in c++. Let me get rid of that downvote..Tableau
The above solution is backed up by a Deprecated notice in Apple's Documentation on FSResolveAliasFile.Mcclanahan
K
1

The PyObjC bridge lets you access NSURL's bookmark handling, which is the modern (backwards compatible) replacement for aliases:

import os.path
from Foundation import *

def target_of_alias(path):
    url = NSURL.fileURLWithPath_(path)
    bookmarkData, error = NSURL.bookmarkDataWithContentsOfURL_error_(url, None)
    if bookmarkData is None:
        return None
    opts = NSURLBookmarkResolutionWithoutUI | NSURLBookmarkResolutionWithoutMounting
    resolved, stale, error = NSURL.URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, opts, None, None, None)
    return resolved.path()

def resolve_links_and_aliases(path):
    while True:
        alias_target = target_of_alias(path)
        if alias_target:
            path = alias_target
            continue
        if os.path.islink(path):
            path = os.path.realpath(path)
            continue
        return path
Kunlun answered 26/1, 2015 at 18:17 Comment(1)
I added a wrapper function that resolves both aliases and symlinks.Kunlun
P
0

The APIs those modules use are deprecated by Apple, it appears. You should use POSIX APIs instead.

os.path.realpath(FILE_OBJECT.name)
Phore answered 20/1, 2014 at 22:1 Comment(3)
Sorry--I marked this as accepted too soon. It seems to work for symbolic links but not aliases. Now that I think about it, I believe aliases are Mac-specific and so are not likely to be handled well by POSIX tools.Lying
I don't believe there is a standard library method to do that. All of the OSX specific modules have been removed in Python 3 due to apple removing/depreciating the APIs that backed them.Phore
pythonhosted.org/pyobjc may be useful for the another answer. Again, not a standard library option, but if you make many Cocoa calls, this would be the way.Phore

© 2022 - 2024 — McMap. All rights reserved.