Skip to content

Instantly share code, notes, and snippets.

@Haaruun-I
Forked from gesquive/self-update-script.py
Last active August 12, 2018 22:58
Show Gist options
  • Save Haaruun-I/7742df86770cc7c2066b99dca05186e9 to your computer and use it in GitHub Desktop.
Save Haaruun-I/7742df86770cc7c2066b99dca05186e9 to your computer and use it in GitHub Desktop.
Stick this in a python script to self update the script from an online source
__version__ = "1"
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
import os
import sys
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
update("https://gist.githubusercontent.com/Haaruun-I/7742df86770cc7c2066b99dca05186e9/raw/6d1c687b311978b71d083ee0914d2e56938b20e5/self-update-script.py")
def main():
# Here is the first line of the main function's code.
root = tkinter.Tk()
root.title("Draw!")
cv = tkinter.Canvas(root,width=600,height=600)
cv.pack(side = tkinter.LEFT)
# This is how we create a turtle to draw on the canvas we created above.
t = turtle.RawTurtle(cv)
screen = t.getscreen()
# With the lines below, the "turtle" will look like a pencil.
screen.register_shape("pencil.gif")
t.shape("pencil.gif")
# This sets the lower left corner to 0,0 and the upper right corner to 600,600.
screen.setworldcoordinates(0,0,600,600)
screen.bgcolor("white")
# A frame is an invisible widget that holds other widgets. This frame goes
# on the right hand side of the window and holds the buttons and Entry widgets.
frame = tkinter.Frame(root)
frame.pack(side = tkinter.RIGHT,fill=tkinter.BOTH)
pointLabel = tkinter.Label(frame,text="Width")
pointLabel.pack()
# This entry widget allows the user to pick a width for their lines.
# With the pointSize variable below you can write pointSize.get() to to
# the contents of the entry widget and pointSize.set(val) to set the value
# of the entry widget to val. Initially the pointSize is set to 1. str(1) is needed because
# the entry widget must be given a string.
pointSize = tkinter.StringVar()
pointEntry = tkinter.Entry(frame,textvariable=pointSize)
pointEntry.pack()
pointSize.set(str(1))
# This is an event handler. Handling the quit button press results in destroying the window
# and quitting the application.
def quitHandler():
root.destroy()
root.quit()
# This is how a button is created in the frame. The quitHandler is the event handler for button
# presses of the "Quit" button.
quitButton = tkinter.Button(frame, text = "Quit", command=quitHandler)
quitButton.pack()
# Here is another event handler. This one handles mouse clicks on the screen.
def clickHandler(x,y):
# When a mouse click occurs, get the pointSize entry value and set the width of the
# turtle called "t" to the pointSize value. The int(pointSize.get()) is needed because
# the width is an integer, but the entry widget stores it as a string.
t.width(int(pointSize.get()))
t.goto(x,y)
# Here is how we tie the clickHandler to mouse clicks.
screen.onclick(clickHandler)
# Finally, this code is last. It tells the application to enter its event processing loop
# so the application will respond to events.
tkinter.mainloop()
# Python jumps right here after executing the def main() line. These two lines tell
# Python to jump to the first line of the main function above. Seems a little strange,
# but there are good reasons for this which you'll learn if you take some computer
# science.
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment