I am looking to see if someone is willing to take the challenge in optimizing this noob Python script.
The purpose of the script is to:
- Check if Google Drive File Stream is installed
- Check if Google Drive is installed
- Check if user's Google Drive folder has been renamed
Then the actions begin:
- Install Google Drive File Stream
- Quit Google Drive if running
- Uninstall Google Drive
- Rename user's Google Drive folder
Logging is needed, of course, and some double checks for various commands you'll see in the script.
import commands
import subprocess
import os, logging, sys, signal, time, shutil
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
##################################################################################
# Variables
##################################################################################
userName = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]
userName = [userName,""][userName in [u"loginwindow", None, u""]]
googleDriveDir = ""
gdfsInstaller = "/Library/Scripts/Company/GoogleDriveFileStream.dmg"
# Logging File
#logFile = "/Library/Logs/Company/googleDrive_Migration.log"
logFile = "/Users/" + userName + "/Library/Logs/Company/googleDrive_Migration.log"
basedir = os.path.dirname(logFile)
if not os.path.exists(basedir):
os.makedirs(basedir)
if not os.path.exists(logFile):
with open(logFile, 'a'):
os.utime(logFile, None)
logging.basicConfig(filename=logFile, level=logging.DEBUG, format='%(asctime)s - %(levelname)s: %(message)s')
# Console Handler
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
gstream = "/Applications/Google Drive File Stream.app"
gdrive = "/Applications/Google Drive.app"
##################################################################################
# Functions
##################################################################################
def appExist(appName):
logging.info("Checking for the existence of: %s", appName)
return os.path.exists(appName)
def appProcessID(appPath, action):
# the "action" item takes either a "kill" or "check" action.
global appName
#appName = os.path.splitext(os.path.basename(appPath))[0]
appName = os.path.basename(appPath)
logging.debug('AppName variable and action to be taken is: %s, %s', appName, action)
if action == "check":
logging.info('Looking for Process ID(s) for %s', appName)
process = subprocess.Popen(['pgrep', '-i', appName], stdout=subprocess.PIPE)
global my_pid
my_pid, err = process.communicate()
# Check if the application is running and log status
#my_pid = appProcessID(gdrive)
if my_pid:
logging.debug('Application %s is running with process ID(s): %s', gdrive, my_pid)
else:
logging.debug('Application %s, is not running. Could not find any process id(s)', appName)
return my_pid
elif action == "kill":
if not my_pid:
appExit('No Process ID was found.')
try:
pid = int(my_pid)
logging.debug('Converted %s to an integar acceptable for signal killing.', pid)
#print "that worked"
except ValueError:
appExit('Could not confirm ' + my_pid + 'as an integar value acceptable for signal kill.')
#print "did not work"
os.kill(pid, signal.SIGKILL)
else:
appExit('Could not run the function appProcessID wihtout an action argument.')
def googleDirLookup(directory):
#UsersGoogleDriveDir = "/Users/" + userName + "/" + appName + "/"
logging.info('Verifying if this path exists: %s', directory)
if os.path.exists(directory):
googleDriveDir = directory
if os.path.islink(googleDriveDir):
dir = os.path.realpath(googleDriveDir)
googleDriveDir = dir + "/"
logging.info('The path was a symlink and has been updated to use the real path.')
else:
googleDriveDir = directory + "/"
logging.info('Found the directory %s', googleDriveDir)
return googleDriveDir
else:
logging.info('Could not find the path: %s', directory)
def renameDir(olddir, newdir):
logging.info('Renaming %s to %s', olddir, newdir)
os.rename(olddir, newdir)
time.sleep(3)
def Uninstall(appName):
logging.info('Killing any processes from %s', appName)
appProcessID(appName, "kill")
logging.info('Checking if process was killed successfully...')
appProcessID(appName, "check")
logging.info('Deleting %s...', appName)
time.sleep(5)
if os.path.exists(appName):
shutil.rmtree(appName)
else:
logging.info('Could not find the path: %s', appName)
# Rename Google Drive folder if it exists
if not os.path.exists(RenamedUsersGoogleDriveDir):
renameDir(UsersGoogleDriveDir, RenamedUsersGoogleDriveDir)
appExit('Successful! New path is set to ' + RenamedUsersGoogleDriveDir)
else:
appExit('Failed! Could not find the path: ' + RenamedUsersGoogleDriveDir)
def openFileCheck(directory):
p1=subprocess.Popen(['lsof', '-Fn'], stdout=subprocess.PIPE)
p2=subprocess.Popen(['grep', directory + "/"], stdin=p1.stdout, stdout=subprocess.PIPE)
p3=subprocess.Popen(['grep', '-v', '.DS_Store'], stdin=p2.stdout, stdout=subprocess.PIPE)
process = p3.communicate()[0]
if process:
return process
def installApp(appName):
if not appExist(appName):
print appName
appExit('FAILED to find the installer from ' + appName)
mountedGDFS = "/Volumes/GoogleDriveFileStream"
logging.info('Found %s...mounting to %s...', appName, mountedGDFS)
# Mount DMG
p1=subprocess.Popen(['hdiutil', 'attach', '-mountpoint', mountedGDFS, appName], stdout=subprocess.PIPE).wait()
if not os.path.exists(mountedGDFS + "/GoogleDriveFileStream.pkg"):
appExit('FAILED to mount directory')
gdfsPKG = mountedGDFS + "/GoogleDriveFileStream.pkg"
logging.info('Successfully mounted %s.', mountedGDFS)
logging.info('Installing PKG from %s...', gdfsPKG)
p1=subprocess.Popen(['installer', '-pkg', gdfsPKG, '-target', '/'], stdout=subprocess.PIPE).wait()
if not appExist(gstream):
p2=subprocess.Popen(['hdiutil', 'attach', '-mountpoint', mountedGDFS, appName], stdout=subprocess.PIPE).wait()
appExit('FAILED could not find Google Drive File Stream in ' + gstream)
# Detach mounted DMG
logging.info('Unmounting Installer DMG...')
d1=subprocess.Popen(['df', '-h'], stdout=subprocess.PIPE)
d2=subprocess.Popen(['grep', mountedGDFS], stdin=d1.stdout, stdout=subprocess.PIPE)
diskDrive = d2.stdout.readline()[0:12]
detachCommand=subprocess.Popen(['hdiutil', 'detach', diskDrive], stderr=subprocess.PIPE, stdout=subprocess.PIPE).wait()
if not os.path.exists(mountedGDFS):
logging.info('Successfully unmounted %s', appName)
else:
logging.info('Failed to unmount %s', appName)
def appExit(exitstatement):
logging.info("%s", exitstatement)
logging.info('==============END==========================')
logging.info(' ')
sys.exit()
##################################################################################
# Check Logic
##################################################################################
# Check and create log files if they do not exist.
logging.info('==============START========================')
logging.info('Location of logs: %s', logFile)
logging.info('Current logged in user: %s', userName)
# Check for the existence of the application before installing.
logging.info('Checking for the existence of application: %s', gstream)
gStreamExist = "FALSE"
if os.path.exists(gstream):
logging.info('Found the application %s', gstream)
gStreamExist="TRUE"
# DO NOT EXIT HERE FOR PRODUCTION
#appExit(gstream + ", is installed on this system, no need to continue.")
else:
logging.info('Could not find the application: %s', gstream)
# Check if Google Drive is installed.
logging.info('Checking for the existence of application: %s', gdrive)
gDriveExist = "TRUE"
if not os.path.exists(gdrive):
gDriveExist="FALSE"
# DO NOT EXIT HERE FOR PRODUCTION
#appExit(gdrive + ", is installed on this system")
else:
logging.info('Found the application: %s', gdrive)
# Check if the application is running and log status
appProcessID(gdrive, "check")
appName = os.path.splitext(os.path.basename(appName))[0]
# Check for Google Drive folder and for any open files from the application's directory.
UsersGoogleDriveDir = "/Users/" + userName + "/" + appName
RenamedUsersGoogleDriveDir = os.path.dirname(UsersGoogleDriveDir) + "/.RENAMED_" + appName
folderRenamed = "FALSE"
if googleDirLookup(UsersGoogleDriveDir):
logging.info('Checking to see if any open files exist for %s in %s', appName, UsersGoogleDriveDir)
openFiles = openFileCheck(UsersGoogleDriveDir)
if openFiles:
logging.debug('Currently open files include: %s', openFiles)
appExit("Found actively open files in the directory. Cannot continue at this time. Quitting script...")
logging.info('No open files found, continuing...')
elif googleDirLookup(RenamedUsersGoogleDriveDir):
logging.info('Already renamed user\'s home folder. No need to continue.')
folderRenamed = "TRUE"
else:
logging.info('No Google Drive folder found in users home directory.')
##################################################################################
# Action Logic
##################################################################################
if gStreamExist =='FALSE' and gDriveExist =='FALSE' and folderRenamed =='FALSE':
# Install Google Drive File Stream
# 1) Check if installer is in place
# 2) Install if found
logging.info('== Taking action to Install Google Drive File Stream ==')
installApp(gdfsInstaller)
appExit('Successful!')
elif gStreamExist =='FALSE' and gDriveExist =='TRUE' and folderRenamed =='FALSE':
logging.info('== Taking action to Install GDFS & Uninstall GDrive ==')
# Install Google Drive File Stream
# 1) Check if installer is in place
# 2) Install if found
installApp(gdfsInstaller)
# Uninstall Google Drive
# 1) Kill Process
# 2) Remove delete application from Applications folder
# 3) Rename Google Drive folder in user's home directory
Uninstall(gdrive)
appExit('Successful!')
elif gStreamExist =='TRUE' and gDriveExist =='TRUE' and folderRenamed =='FALSE':
logging.info('== Taking action to Uninstall Gdrive ==')
# Uninstall Google Drive
# 1) Kill Process
# 2) Remove delete application from Applications folder
# 3) Rename Google Drive folder in user's home directory
Uninstall(gdrive)
appExit('Successful!')
elif gStreamExist =='TRUE' and gDriveExist =='FALSE' and folderRenamed =='FALSE':
logging.info('== Taking action to Rename Gdrive Directory ==')
# 3) Rename Google Drive folder in user's home directory
renameDir(UsersGoogleDriveDir, RenamedUsersGoogleDriveDir)
appExit('Successful!')
#else gStreamExist =='TRUE' and gDriveExist =='FALSE' and folderRenamed =='TRUE':
# Ideal scenario, do nothing!
else:
appExit('== NO ACTION TAKEN: Google Drive File Stream is installed, Google Drive is uninstalled, and ' + userName + " Google Drive folder is renamed.")
1 Answer 1
Portability
I realize this question was posted many years ago when Python version 2.x was prevalent, but now that it is deprecated, consider porting to 3.x.
Change this line:
print appName
to:
print(appName)
There are also syntax errors; perhaps your version of Python was more forgiving.
In the googleDirLookup
function, there is illegal indentation:
googleDriveDir = dir + "/"
logging.info('The path was a symlink and has been updated to use the real path.')
it should be:
googleDriveDir = dir + "/"
logging.info('The path was a symlink and has been updated to use the real path.')
In the appProcessID
function, there is an else
followed by an elif
:
if my_pid:
logging.debug('Application %s is running with process ID(s): %s', gdrive, my_pid)
else:
logging.debug('Application %s, is not running. Could not find any process id(s)', appName)
return my_pid
elif action == "kill":
This is illegal. If you did not get a syntax error there, I assume the code
between the elif
and the end of the function was unreachable. In any case,
that should be cleaned up.
Tools
You could run code development tools to automatically find some style issues with your code.
For example, ruff
identifies unused imports. This line can be deleted:
import commands
Remove the unused signal
import. Also, split imports onto multiple lines. Change:
import os, logging, sys, signal, time, shutil
to:
import os
import logging
import sys
import time
import shutil
You could also use ruff
to automatically reorder the import
lines (similar to the isort
tool) as follows:
ruff check --select I --fix
Comments
All commented-out code should be deleted to reduce clutter. For example:
#logFile = "/Library/Logs/Company/googleDrive_Migration.log"
#appName = os.path.splitext(os.path.basename(appPath))[0]
#my_pid = appProcessID(gdrive)
Simpler
Since you only set the gStreamExist
variable to the 2 strings "TRUE"
and "FALSE", it would be more natural to make the variable a boolean
type instead of a string type by assigning boolean values:
gDriveExist = True
if not os.path.exists(gdrive):
gDriveExist = False
Then comparisons like:
elif gStreamExist =='TRUE'
can be simplified as:
elif gStreamExist
Naming
The PEP 8 style guide recommends snake_case for function and variable names.
For example, appExist
would be app_exist
, and appName
would be app_name
.
The variable named dir
is the same name as a Python built-in function.
This can be confusing. To eliminate the confusion, rename the variable
as something like google_dir
. The first clue is that "dir" has special
coloring (syntax highlighting) in the question, as it does when I copy the code
into my editor.
Documentation
The PEP 8 style guide recommends adding docstrings for classes and functions.