Here’s a quick script I created today for creating and mounting a ramdisk in OSX:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python

import os, sys
from subprocess import call
from getopt import gnu_getopt, GetoptError

def createRamdisk(vol_name, disk_size):
    dsize = (int(disk_size)*1024)*2
    cmd = 'diskutil quiet erasevolume HFS+ "%s" $(hdiutil attach -nomount ram://%i)' %(vol_name, dsize)
    print "Creating %sMB Ramdisk...\n" %disk_size
    res = call(cmd, shell=True)
    if res == 0:
        print 'Ramdisk "%s" Successfully Created and Mounted.' %vol_name
    else:
        print 'Error creating Ramdisk'

def usage():
    sys.exit( "Usage: %s -n <Volume Name> -s <size in Megabytes>\n" %(sys.argv[0]) )

def main():
    SIZE = VOLNAME = None

    try:
        options, remainder = gnu_getopt(sys.argv[1:], 's:n:h')
        for opt, arg in options:
            if opt in '-s':
                SIZE = arg
            elif opt in '-n':
                VOLNAME = arg
            elif opt in '-h':
                usage()

    except GetoptError, err:
        print "\nWarning:",err.msg,'\n'
        usage()


    if SIZE and VOLNAME:
        createRamdisk(VOLNAME, SIZE)
    else:
        usage()

if __name__ == '__main__':
    main()

EDIT: Here is a simplified version, using ‘optparse’ intead of ‘getopt’:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python

import os, sys
from subprocess import call
import optparse

def createRamdisk(vol_name, disk_size):
    dsize = (int(disk_size)*1024)*2
    cmd = 'diskutil quiet erasevolume HFS+ "%s" $(hdiutil attach -nomount ram://%i)' %(vol_name, dsize)
    print "Creating %sMB Ramdisk...\n" %disk_size
    res = call(cmd, shell=True)
    if res == 0:
        print 'Ramdisk "%s" Successfully Created and Mounted.' %vol_name
    else:
        print 'Error creating Ramdisk'

def main():
    p = optparse.OptionParser()
    p.add_option('--volname', '-n', default='RamDisk')
    p.add_option('--volsize','-s', default=256)
    options, arguments = p.parse_args()

    if options.volsize and options.volname:
        createRamdisk(options.volname, options.volsize)

if __name__ == '__main__':
    main()

‘optparse’ is the easier approach, because it handles errors for you, and also automatically displays help via the ‘-h’ switch.

It provides both with no additional code required on the part of the programmer.

This is a follow-up to my previous post, which required logging out before the changes took effect.

This simple method changes the wallpaper on the fly, which is better.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python2.5

from ScriptingBridge import *
from Foundation import CFURLCreateWithFileSystemPath, CFURLCopyFileSystemPath


def changeWallpaper(wp_path):
    finder = SBApplication.applicationWithBundleIdentifier_("com.apple.finder")
    file_ref = CFURLCreateWithFileSystemPath(None,wp_path,0,False)
    finder.setDesktopPicture_(CFURLCopyFileSystemPath(file_ref,1))


if __name__ == "__main__":  
    from time import sleep
    wp = "/Users/riveraa/Pictures/ee45987c573c14b873658a0ac1ce0421.jpg"
    wp2 = "/Users/riveraa/Pictures/Snow_Leopard_Wallpaper_by_maddog1138.jpg"
   
    changeWallpaper(wp)
    sleep(5)
    changeWallpaper(wp2)

You’ll need to logout for the change to take effect. This is more of a deployment script…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python

from Foundation import NSUserDefaults
import os
"""Provides Wallpaper, a class for changing the wallpaper path
  in the com.apple.desktop.plist
"""


__author__ = "Armando I. Rivera"
__copyright__ = "Copyright 2011, RMG Enterprises"
__credits__ = ["Armando I. Rivera"]
__license__ = "BSD Simplified"
__version__ = "1.0.0"
__maintainer__ = "Armando I. Rivera"
__status__ = "Development"

class WallPaper():
   def __init__(self):
       """ Loads the com.apple.desktop.plist file, for processing"""
       self.UserDefaults = NSUserDefaults.standardUserDefaults()
       self.deskTop = self.UserDefaults.persistentDomainForName_("com.apple.desktop")
       self.backGrounds = self.deskTop.objectForKey_("Background")

   def update(self, new_wallpaper):
       """ Updates the wallpaper in the desktop.plist file.
           'new_wallpaper' is the full path to the replacement wallpaper.
       """

       for a in self.backGrounds:
           try:
               b = self.backGrounds[a]["ImageFilePath"]
               if b:
                   self.backGrounds[a]["ImageFilePath"] = new_wallpaper
                   self.backGrounds[a]["NewImageFilePath"] = new_wallpaper
           except KeyError:
               pass

   def write(self):
       """Writes the updated plist file to disk"""
       self.UserDefaults.setObject_forKey_(self.backGrounds,"Background")
       self.UserDefaults.setPersistentDomain_forName_(self.deskTop,"com.apple.desktop")
       self.UserDefaults.synchronize()

if __name__ == "__main__":
   wp = WallPaper()
   wp.update("</path/to/my/wallpaper.jpg>")
   wp.write()

Here’s a way to get the size of an OSX user’s Entourage mail store without using the ScriptingBridge or NSApplescript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python2.5
import os
from SystemConfiguration import SCDynamicStoreCopyConsoleUser as GetUserName

def getEntourageIdentityFolder():
  current_user=GetUserName(None,None,None)[0]
  MAIL_LOC="/Users/%s/Documents/Microsoft User Data/Office 2008 Identities/" %current_user
  f = open("/Users/%s/Library/Preferences/Microsoft/Office 2008/Entourage Preferences" %current_user)
  g = f.read()
  f.close()
 
  ident = ''.join([char for char in g if char.isalnum() or char.isspace()])
  return "%s%s" %(MAIL_LOC,ident.strip())

def getEntourageDatabaseSize():
    return os.path.getsize(os.path.join(getEntourageIdentityFolder(),"Database"))>>20
   
print getEntourageDatabaseSize()

I needed a function to get the size of a folder, for a project that I’m currently working on.

I created three possible methods to accomplish this, and when I timed them I was surprised at some of the results.

The three methods consist of:

  • Method getFolderSize1, using Pure Python
  • Method getFolderSize2, using a mixture of Python and NSFileManager from the Foundation module
  • Method getFolderSize3, using only NSFileManager from the Foundation module

The folder I chose to work with is the “Main Identity” folder from my Outlook 2011 mail store.

Here’s a screenshot of the info I’ll be parsing:

In order to accurately time each function call, I used the “timeit” Python module, which only returns the elapsed time for each function (no overhead from other things like importing modules, etc)

Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env python2.5

from Foundation import *
from timeit import Timer
import os



def getFolderSize1(path_to_folder):
    fileSize=0
    for (path, dirs,files) in os.walk(path_to_folder):
        for theFile in files:
            fileSize += os.path.getsize(os.path.join(path,theFile))
    return (fileSize>>20)

def getFolderSize2(path_to_folder):
    fileSize=0
    mgr = NSFileManager.defaultManager()
    contents = mgr.subpathsAtPath_(thePath)

    for msg in contents:
        theFile = os.path.join(thePath,msg)
        fileSize += os.path.getsize(theFile)
    return (fileSize>>20)

def getFolderSize3(path_to_folder):
    fileSize=0
    mgr = NSFileManager.defaultManager()
    contents = mgr.subpathsAtPath_(thePath)
    enumerator = contents.objectEnumerator()
    filePath = enumerator.nextObject()

    while filePath:
        fmgr = NSFileManager.defaultManager()
        fattrib = fmgr.fileAttributesAtPath_traverseLink_(os.path.join(thePath, filePath),False)
        fileSize += fattrib.fileSize()
        filePath = enumerator.nextObject()
    return (fileSize>>20)



if __name__ == "__main__":

    thePath = os.path.join(os.path.expanduser("~"),"Documents/Microsoft User Data/Office 2011 Identities/Main Identity")

    t = Timer( "getFolderSize1(thePath)","from __main__ import getFolderSize1, thePath")
    print "Native Method completed in %0.2f seconds" %(t.timeit(1))

    t = Timer( "getFolderSize2(thePath)","from __main__ import getFolderSize2, thePath")
    print "Mixed Native and NSFileManager Method completed in %0.2f seconds" %(t.timeit(1))

    t = Timer( "getFolderSize3(thePath)","from __main__ import getFolderSize3, thePath")
    print "NSFileManager Method completed in %0.2f seconds" %(t.timeit(1))

The functions are timed in order, from Native Only, Mixed Native and Foundation, and Foundation Only.

Here are the results:

1
2
3
Native Method completed in 10.90 seconds
Mixed Native and NSFileManager Method completed in 21.75 seconds
NSFileManager Method completed in 36.59 seconds

I really expected the Foundation Only function implementation to perform better than that, but apparently there are two choke points within it: the call to .subpathsAtPath and using the Foundation enumerator to loop through the files. Also, I think the bridge between Python and ObjC introduces additional overhead.

I had read a LOT of posts elsewhere about Python’s os.walk() call being slow, but 10 seconds to get size information on 230,730 files in 582 folders is not bad for only 5 lines of code.

I am thinking that a recursive function using os.listdir() would be faster, and I might add that to this test at some point….

Easy way to get available space on a Volume in OSX:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python2.5

from Foundation import *
import locale

def getFreeSpace(path_to_volume):
    mgr=NSFileManager.defaultManager()
    freespace=mgr.fileSystemAttributesAtPath_(path_to_volume).objectForKey_("NSFileSystemFreeSize")
    locale.setlocale(locale.LC_ALL, "en_US")
    return locale.format("%d",freespace,grouping=True)

print "%s bytes" %(getFreeSpace("/Volumes/Lion"))

On my laptop loaded with OSX Lion GM, I was running Xcode 4.2 DP, and with the official release of Xcode 4.1 for Lion wanted to switch to the current released version.

To make sure that I didn’t have any conflicts or weirdness, I uninstalled the 4.2 version with the following command:

1
sudo /Developer/Library/uninstall-devtools --mode=all

“–mode=all” removes every bit of Xcode, except for the unixdev parts.

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python

import os

def getAvailableDriveSpace(path = "/"):
  s = os.statvfs(path)
  return round((s.f_frsize * s.f_bavail)/1.073741824e9)

 
print "Available Disk Space is: %iGB" %(getAvailableDriveSpace())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def removeLoginItem(app_label):
  from Foundation import NSUserDefaults

  UserDefaults = NSUserDefaults.standardUserDefaults()
  domain = UserDefaults.persistentDomainForName_("loginwindow")
  apps = domain.objectForKey_("AutoLaunchedApplicationDictionary")

  for item in apps:
    if item["Path"].find(app_label) > 0:
      apps.remove(item)
      UserDefaults.setObject_forKey_(apps,"AutoLaunchedApplicationDictionary")
      UserDefaults.setPersistentDomain_forName_(domain,"loginwindow")
      UserDefaults.synchronize()
      return

Here’s a function to retrieve the default application handler for a file:

1
2
3
4
5
6
7
8
from Foundation import NSURL
from LaunchServices import LSGetApplicationForURL, kLSRolesAll

def getDefaultApplication(path):
    url = NSURL.fileURLWithPath_(path)
    os_status, app_ref, appurl = LSGetApplicationForURL(url, kLSRolesAll, None, None)
    if os_status != 0: return ""
    return app_ref.as_pathname()
© 2012 Recursive Media Suffusion theme by Sayontan Sinha