2

Does python have a simple function for checking if a directory is an actual directory or if it's just an alias to another directory? I'm trying to list all files/folders in a directory but because of these alias folders, I'm getting a lost of stuff that looks like this:

/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle/Home/bundle

Directory Alias

I know I can write a function that will compare paths and quit if it seems like I'm going in circles, but is there a simple function that does exactly that that I'm not aware of? E.g. os.isAlias( …pathname… )

Thanks!

adilapapaya
  • 4,765
  • 3
  • 25
  • 26

4 Answers4

4

Here's a version of os.path.realpath that works on Mac aliases as well as on symbolic links under Python 2:

from Carbon import File
def osx_realpath (path):
    return File.FSResolveAliasFile(path, True)[0].as_pathname()

If you call osx_realpath on each directory before you recurse into it you should avoid duplication. Alternatively you could define something like

def is_osx_realpath (path):
    return path == osx_realpath(path)

Here you have to worry a little about false negatives, however. If you filter for is_osx_realpath and the path you start with is an alias, your program will stop without looking at anything.

So far I don't know of a way to do this under Python 3. I have a question here where I'm hoping for an answer. Right now I can't do better than using subprocess.call to invoke something that does the check on the command line.

EDIT: I should add that not only is Carbon.File not available in Python 3, but it is deprecated and so is best avoided in Python 2 as well--however it's the most pragmatic solution I know of for Python 2 at present.

EDIT 2: here is a way to check if a file is an alias that I believe to be Python 3-friendly. However, I don't have code to resolve the alias. I believe you need PyObjC installed.

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)

Community
  • 1
  • 1
kuzzooroo
  • 6,788
  • 11
  • 46
  • 84
  • This returns a false for an alias(yay!), but an error for symlinks: `File.FSResolveAliasFile(path, True)[0].as_pathname()` `MacOS.Error: (-120, 'Directory not found')`. Right now, I'm pretty much resorting to subprocess calls. Specifically, `command = ["mdfind", "kMDItemKind == 'Alias'"]` `subprocess.call(command)` will print out all Alias files which I'm guessing is similar to what you're doing…? (If it isn't and you have no idea what I'm talking about, `mdfind` == spotlight find and `mdls` is roughly analogous to `ls`). – adilapapaya Jan 22 '14 at 17:34
  • God, sorry for the horrible formatting. Double space isn't new-lining things for me. Also, I would upvote your answer but I don't have enough votes to upvote(!) :-/ – adilapapaya Jan 22 '14 at 17:36
  • 1
    Not sure you can get newlines into comments. I confirmed that the code above works with symbolic links on my machine. Have you tried calling `os.path.exists` to make sure the link you're querying on actually exists? At any rate you could catch the exception or check `os.path.islink` first. – kuzzooroo Jan 23 '14 at 02:50
  • `os.path.exists` returns _true_ for alias and _false_ for symlinks. `os.path.islink` returns _false_ for alias and _true for symlinks. I figured that would fix it since I could just write an if-else statement that would catch either occurrence. Alas, .app executables return false for both `os.path.islink` and `os.path.exists` (false for `os.path.isdir` and `os.path.isfile` too!). I haven't yet tried out your Edit2 method above since I'm still in Python 3 but I've accepted your answer anyway since it looks like the closest to a solution so far. Thanks for all the help! :) – adilapapaya Jan 23 '14 at 20:11
  • _"since I'm still in Python 3"_. Sorry, I mean Python 2.7. – adilapapaya Jan 24 '14 at 15:22
  • Tip for people coming here and seeing `AppKit` in the import: it is **not** the Python package of the same name on PyPI. It is, in fact, a different package provided by `pyobjc-framework-AppleScriptObjC`. Do **not** try to get it via `pip install AppKit`. Instead, use `pip install pyobjc-framework-AppleScriptObjC`. – mhucka Dec 24 '20 at 23:33
1

The answer above is incorrect. While it is true that Finder reports symlinks as alias, they are distinct things.

Symlinks are a basic feature of UNIX, but alias are a Apple only feature. If you doubt this create a symlink to a directory and an alias. The symlink will be small typically 50-100 bytes, whereas the alias can be several MB.

os.path.islink( … ) will report symlinks, but not alias.

I am not sure how you would find them in Python, but the following link shows other methods.

https://stackoverflow.com/a/21151368/838253

Community
  • 1
  • 1
Milliways
  • 1,265
  • 1
  • 12
  • 26
  • You are correct! Apparently, the same symbol is used for aliases and symlinks on a Mac and the Home/bundle path I posted above is actually a symlink and not an alias which is why `os.path.islink( … )` worked for me. Thanks for the link. I'll go over it in more detail later tonight and see if it holds the answer. – adilapapaya Jan 18 '14 at 02:47
  • (ugh, sorry. I can't vote up your answer! it says I need at least 15 reps!) – adilapapaya Jan 18 '14 at 02:54
  • so anyone has an idea what an alias really is, and what is it for? – njzk2 Jan 20 '14 at 23:10
0

You can check whether a file or directory is an alias with the GetFileInfo command in Mac OS X. GetFileInfo -aa foo prints a line with "1" if foo is an alias and "0" if not.

import subprocess

def is_alias(path):
    return subprocess.check_output(["GetFileInfo", "-aa", path]) == "1\n"

Seems a little sad to spawn a process for every check, but I think this works with versions of Mac OS X since probably 10.4.4 (2006), 32-bit, 64-bit, Python 2 and Python 3. The version of GetFileInfo I have (from 2009) is a "universal" i386 + PPC binary.

GetFileInfo is part of Xcode, which is large, but you can download the command-line tools separately (see the "Separate Download" section here).

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/GetFileInfo.1.html

Community
  • 1
  • 1
FutureNerd
  • 769
  • 7
  • 9
0

Old question, but I just ran into this myself.

I have no perfect method for checking if the file is an alias, however, if using mimetypes, python will return None for an alias or a symlink. Might be useful in some situations. I've only tested this in python 3.8 on macOS Big Sur.

import mimetypes

for idx, f in enumerate(filepaths):
    type = mimetypes.guess_type(f)[0]
    print(f"type is: {type}")

returns (without my added comments):

type is: None # <-- Folder Alias
type is: None # <-- File Alias
type is: text/x-python
type is: None # <-- Folder Alias
type is: video/mp4
type is: image/png
type is: None # <-- Folder Alias
type is: None # <-- Symlink
type is: image/png
type is: application/zip
type is: image/png
type is: image/jpeg
type is: None # <-- Symlink

I ran some files through exiftool just to see what types they returned, and aliases and symlinks both showed the following:

File Type                       : ALIAS
File Type Extension             : alias
MIME Type                       : application/x-macos

You might be able to init the mimetypes for these, but haven't tested and not sure if it will give false positives if anything else shows up as application/x-macos

liquidRock
  • 327
  • 2
  • 12