Skip to content

Instantly share code, notes, and snippets.

@cmattoon
Last active May 31, 2017 02:01
Show Gist options
  • Save cmattoon/a78166721c6b38fbc0ec19014de27656 to your computer and use it in GitHub Desktop.
Save cmattoon/a78166721c6b38fbc0ec19014de27656 to your computer and use it in GitHub Desktop.
Install R packages on ShinyServerPro
#!/usr/bin/env python
"""
Install R Packages from the command line
Usage: (parse app.R)
./rpkg app.R
Usage: (install R packages)
./rpkg shiny V8 plyr
"""
import os
import pwd
import shlex
import subprocess
import sys
E_OK = 0
E_BADARGS = 1
# The name of the shiny user
SHINY_USER_NAME = 'shiny'
def loadfile(filename):
"""
:param filename:
:return:
"""
with open(filename, 'r') as fd:
return filter(lambda l: l.startswith('require('),
[line.strip() for line in fd.readlines()])
def change_user(uid, gid):
"""
Returns a pre-exec function to pass to subprocess.Popen
This allows the script to run as the 'shiny' user
(defined in SHINY_USER_NAME)
:param uid: int
:param gid: int
:return: function
"""
def wrapper():
os.setgid(gid)
os.setuid(uid)
return wrapper
def get_install_command(package):
return shlex.split("R -e \'install.packages(\"%s\", repos=\"http://cran.rstudio.com/\")\'".format(package)
def get_new_env(username):
"""
Build up an environment for the new user
:param username:
:return:
"""
info = pwd.getpwnam(username)
env = os.environ.copy()
env['HOME'] = info.pw_dir
env['LOGNAME'] = info.pw_name
env['PWD'] = os.getcwd()
env['USER'] = info.pw_name
cwd = os.getcwd()
preexec = change_user(info.pw_uid, info.pw_gid)
return env, cwd, preexec
def install_package(package):
"""
Installs an R package
:param package: The name of the package to install
:type package: str
:return:
"""
print(">> Installing '{}'".format(package))
env, cwd, preexec = get_new_env(SHINY_USER_NAME)
ps = subprocess.Popen(get_install_command(package),
preexec_fn=preexec,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
_out, _err = ps.communicate()
success_str = "* DONE ({})".format(package)
if success_str not in _err:
print("\033[31mERROR")
print(_err)
print("\033[0m")
return False
print(" [\033[32mOK\033[0m]")
return True
def install_packages(packages):
"""
Mostly unnecessary, unless you run into exceptions
Add the install_package call inside a try:except block
to install *most* packages
:param packages:
:return:
"""
for package in packages:
install_package(package)
def main(filename):
"""
:param filename:
:return:
"""
packages = []
if os.path.exists(filename):
# Parse a file
lines = loadfile(filename)
for require in lines:
packages.append(require.replace('require(', '').replace(')', ''))
else:
# Install a list of packages
for arg in sys.argv[1:]:
packages.append(arg)
install_packages(packages)
if __name__ == '__main__':
try:
infile = sys.argv[1]
except IndexError:
print("Usage: {0} <path/to/app.R>\nOr: {1} list of packages".format(sys.argv[0], sys.argv[0]))
exit(E_BADARGS)
exit(main(infile))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment