Skip to content

Instantly share code, notes, and snippets.

@gesquive
Last active February 18, 2024 20:36
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save gesquive/8363131 to your computer and use it in GitHub Desktop.
Save gesquive/8363131 to your computer and use it in GitHub Desktop.
Stick this in a python script to self update the script from an online source
def update(dl_url, force_update=False):
"""
Attempts to download the update url in order to find if an update is needed.
If an update is needed, the current script is backed up and the update is
saved in its place.
"""
import urllib
import re
from subprocess import call
def compare_versions(vA, vB):
"""
Compares two version number strings
@param vA: first version string to compare
@param vB: second version string to compare
@author <a href="http_stream://sebthom.de/136-comparing-version-numbers-in-jython-pytho/">Sebastian Thomschke</a>
@return negative if vA < vB, zero if vA == vB, positive if vA > vB.
"""
if vA == vB: return 0
def num(s):
if s.isdigit(): return int(s)
return s
seqA = map(num, re.findall('\d+|\w+', vA.replace('-SNAPSHOT', '')))
seqB = map(num, re.findall('\d+|\w+', vB.replace('-SNAPSHOT', '')))
# this is to ensure that 1.0 == 1.0.0 in cmp(..)
lenA, lenB = len(seqA), len(seqB)
for i in range(lenA, lenB): seqA += (0,)
for i in range(lenB, lenA): seqB += (0,)
rc = cmp(seqA, seqB)
if rc == 0:
if vA.endswith('-SNAPSHOT'): return -1
if vB.endswith('-SNAPSHOT'): return 1
return rc
# dl the first 256 bytes and parse it for version number
try:
http_stream = urllib.urlopen(dl_url)
update_file = http_stream.read(256)
http_stream.close()
except IOError, (errno, strerror):
print "Unable to retrieve version data"
print "Error %s: %s" % (errno, strerror)
return
match_regex = re.search(r'__version__ *= *"(\S+)"', update_file)
if not match_regex:
print "No version info could be found"
return
update_version = match_regex.group(1)
if not update_version:
print "Unable to parse version data"
return
if force_update:
print "Forcing update, downloading version %s..." \
% update_version
else:
cmp_result = compare_versions(__version__, update_version)
if cmp_result < 0:
print "Newer version %s available, downloading..." % update_version
elif cmp_result > 0:
print "Local version %s newer then available %s, not updating." \
% (__version__, update_version)
return
else:
print "You already have the latest version."
return
# dl, backup, and save the updated script
app_path = os.path.realpath(sys.argv[0])
if not os.access(app_path, os.W_OK):
print "Cannot update -- unable to write to %s" % app_path
dl_path = app_path + ".new"
backup_path = app_path + ".old"
try:
dl_file = open(dl_path, 'w')
http_stream = urllib.urlopen(dl_url)
total_size = None
bytes_so_far = 0
chunk_size = 8192
try:
total_size = int(http_stream.info().getheader('Content-Length').strip())
except:
# The header is improper or missing Content-Length, just download
dl_file.write(http_stream.read())
while total_size:
chunk = http_stream.read(chunk_size)
dl_file.write(chunk)
bytes_so_far += len(chunk)
if not chunk:
break
percent = float(bytes_so_far) / total_size
percent = round(percent*100, 2)
sys.stdout.write("Downloaded %d of %d bytes (%0.2f%%)\r" %
(bytes_so_far, total_size, percent))
if bytes_so_far >= total_size:
sys.stdout.write('\n')
http_stream.close()
dl_file.close()
except IOError, (errno, strerror):
print "Download failed"
print "Error %s: %s" % (errno, strerror)
return
try:
os.rename(app_path, backup_path)
except OSError, (errno, strerror):
print "Unable to rename %s to %s: (%d) %s" \
% (app_path, backup_path, errno, strerror)
return
try:
os.rename(dl_path, app_path)
except OSError, (errno, strerror):
print "Unable to rename %s to %s: (%d) %s" \
% (dl_path, app_path, errno, strerror)
return
try:
import shutil
shutil.copymode(backup_path, app_path)
except:
os.chmod(app_path, 0755)
print "New version installed as %s" % app_path
print "(previous version backed up to %s)" % (backup_path)
return
@roboteck
Copy link

roboteck commented Jul 4, 2017

add this two lines of code after "line number 117"

        if os.path.isfile(backup_path):
            os.remove(backup_path)
        os.rename(app_path, backup_path)

Because in windows it cannot rename the file if it already exists

@BNTFryingPan
Copy link

Will this work in python 3.6 after changing the print statements to Python 3 form?

@fighterhit
Copy link

Why not use shutil.copy directly at the end?

@MuhammadAamer
Copy link

Can SomeOne Tell Me What this Script Will do And Why Should One Use It

@IanSapp128
Copy link

Can SomeOne Tell Me What this Script Will do And Why Should One Use It

It'll look online at a URL you provided and compare the version the user is running with the version online. If the one online is newer, this will download that newer version and replace the script so that its update to date. Whenever a new version is pushed out, the application can always fetch the most up to date version. You could have it auto do this or you could give your end users the option to "check for updates". Just makes it easier for them to stay up to date.

@fbtoolo
Copy link

fbtoolo commented Mar 18, 2022

T

@Mainakdey1
Copy link

This needs to get updated. Almost everything has changed since this was last updated even though the method is still accurate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment