Skip to content

Instantly share code, notes, and snippets.

@moyashi
Forked from spencerogden/gistcheck.py
Last active December 7, 2016 09:32
Show Gist options
  • Save moyashi/694fbb46e66baaf4e2bb2876c06fd4f9 to your computer and use it in GitHub Desktop.
Save moyashi/694fbb46e66baaf4e2bb2876c06fd4f9 to your computer and use it in GitHub Desktop.
Script for use with Pythonista to allow Github Gists for script storage and retrieval. Copy script in full into a new script in Pythonista called "gitcheck". Run the script and it will create 4 scripts starting with "Gist". These can be added to the action menu. See comments for more details.
# Source: https://gist.github.com/4702275
#
# All-purpose gist tool for Pythonista.
#
# When run directly, this script sets up four other scripts that call various
# functions within this file. Each of these sub-scripts are meant for use as
# action menu items. They are:
#
# Set Gist ID.py - Set the gist id that the current file should be
# associated with.
#
# Download Gist.py - Download the gist from the URL on the clipboard, and
# automatically set the association for it (if possible).
#
# Commit Gist.py - Commits the currently open file to the associated gist.
#
# Pull Gist.py - Replaces the current file with the latest version from
# the associated gist.
# Create Gist.py - create a new gist with the current file
#
#
# Download Gist.py can also be run as a bookmarklet from Mobile Safari (to
# jump from browsing a gist directly to downloading it in Pythonista) by
# creating a bookmark to:
#
# javascript:(function()%7Bif(document.location.href.indexOf('http')===0)document.location.href='pythonista://Gist%20Download?action=run&argv='+document.location.href;%7D)();
#
#
# Credits:
#
# Further building on the previous work of Westacular
# https://gist.github.com/4145515
#
# This combines the gist pull/commit/set id scripts from
# https://gist.github.com/4043334
#
# with the bookmarklet-compatible, multi-gist download script
# https://gist.github.com/5f3f2035d8aa46de42ad
#
# (which in turn is based on https://gist.github.com/4036200,
# which is based on https://gist.github.com/b0644f5ed1d94bd32805)
import clipboard
import console
import editor
import json
import keychain
import os
import re
import requests
import shelve
api_url = 'https://api.github.com/gists/'
class GistDownloadError (Exception): pass
class InvalidGistIDError (Exception): pass
#Perform authorization
def auth(username, password):
data='{"scopes":["gist"],"note":"gistcheck"}'
r = requests.post(
api_url.replace('gist','authorizations'),
data=data,auth=(username,password),
headers={'content-type':'application/json'})
return r.json
#get auth data
def get_token():
token = keychain.get_password('gistcheck','gistcheck')
if token is None:
u, p = console.login_alert('GitHub Login')
token = auth(u, p)['token']
keychain.set_password('gistcheck','gistcheck',token)
return token
def commit_or_create(gist, files, token, message=None):
payload = {"files":{}}
if message is not None: payload['description'] = message
for f, c in files.items():
payload['files'][os.path.basename(f)] = {"content":c}
headers = {
'Content-Type':'application/json',
'Authorization': 'token %s' % token,
}
if gist is None:
# Create a new gist
r = requests.post(api_url[:-1],
data=json.dumps(payload),headers=headers).json
else:
# Commit to existing gist
r = requests.post(api_url + gist,
data=json.dumps(payload),headers=headers).json
return r
def fork(gist,token):
headers = {
'Content-Type':'application/json',
'Authorization': 'token %s' % token,
}
return requests.post(api_url + gist + '/forks',
headers=headers).json
def get_gist_id():
db = shelve.open('gistcheck.db')
gist_id = db.get(editor.get_path(),None)
db.close()
return gist_id
def set_gist_id(gist_id):
gist_id = extract_gist_id(gist_id)
db = shelve.open('gistcheck.db')
db[editor.get_path()] = gist_id
db.close()
def del_gist_id():
db = shelve.open('gistcheck.db')
fpath = editor.get_path()
if fpath in db:
del db[fpath]
db.close()
def extract_gist_id(gist):
if re.match(r'^([0-9a-f]+)$', gist):
return gist
m = re.match(r'^http(s?)://gist.github.com/([^/]+/)?([0-9a-f]*)', gist)
if m:
return m.group(3)
m = re.match(r'^http(s?)://raw.github.com/gist/([0-9a-f]*)', gist)
if m:
return m.group(2)
raise InvalidGistIDError()
#load a file from a gist
def pull():
gist_id = get_gist_id()
if gist_id is None:
console.alert('Error', 'There is no gist id set for this file')
else:
fname = os.path.basename(editor.get_path())
gist_data = requests.get(api_url + gist_id).json
try:
newtext = gist_data['files'][fname]['content']
except:
console.alert('Pull Error', 'There was a problem with the pull',gist_data)
if newtext is not None:
editor.replace_text(0,len(editor.get_text()),newtext)
def commit():
token = get_token()
fpath = editor.get_path()
fname = os.path.basename(fpath)
m = console.input_alert('Edit Description','Enter a new description:','')
if m == '': m = None
gist_id = get_gist_id()
res = commit_or_create(gist_id,{fpath:editor.get_text()},token,m)
try:
id = res['id']
except KeyError:
if gist_id:
f = console.alert('Commit Failed',
'Do you have permission to commit? Would you like to fork?','Fork')
if f == 1:
res = fork(gist_id,token)
try:
id = res['id']
except KeyError:
console.alert('Fork Error', 'There was a problem with the fork')
else:
set_gist_id(id)
res = commit_or_create(id,{fpath:editor.get_text()},token,m)
try:
id = res['id']
except KeyError:
console.alert('Commit Error',
'Commit still failed, maybe fork too')
else:
if gist_id is None:
set_gist_id(id)
def set():
gist = get_gist_id()
if gist == None: gist = ''
gist = console.input_alert('Assign Gist ID','Enter the gist id for this file',gist)
if gist == '':
del_gist_id()
else:
try:
set_gist_id(gist)
except InvalidGistIDError:
console.alert('Invalid Gist ID', 'That does not appear to be a valid gist id')
def download_gist(gist_url):
# Returns a 2-tuple of filename and content
gist_id = extract_gist_id(gist_url)
try:
gist_info = requests.get(api_url + gist_id).json
files = gist_info['files']
except:
raise GistDownloadError()
for file_info in files.values():
lang = file_info.get('language', None)
if lang != 'Python':
continue
yield file_info['filename'],gist_id,file_info['content']
def download(gist_url):
try:
for num, (filename, gist_id, content) in enumerate(download_gist(gist_url), start=1):
if os.path.isfile(filename):
i = console.alert('File exists', 'A file with the name ' + filename +
' already exists in your library.',
'Auto Rename', 'Skip')
if i == 1:
editor.make_new_file(filename, content)
else:
editor.make_new_file(filename, content)
set_gist_id(gist_id)
except InvalidGistIDError:
console.alert('No Gist URL','Invalid Gist URL.','OK')
except GistDownloadError:
console.alert('Error', 'The Gist could not be downloaded.')
if not num:
console.alert('No Python Files', 'This Gist contains no Python files.')
def download_from_args(args):
if len(args) == 2:
url = args[1]
else:
url = clipboard.get()
download(url)
def setup():
script_map={
'Gist Commit' :'gistcheck.commit()',
'Gist Pull' :'gistcheck.pull()',
'Gist Set ID' :'gistcheck.set()',
'Gist Download':'if __name__ == "__main__" : gistcheck.download_from_args( sys.argv )'
}
for s,c in script_map.items():
with open(s+'.py','w') as f:
f.writelines(['import sys\nimport gistcheck\n','%s\n'%c])
if __name__ == '__main__':
setup()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment