Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Pulling from github with pythonista
-
Hey, I'm trying to get scripts from my github repository into pythonista to run them from my iOS. I've installed the CodeHub and Pythonista on my iPhone, but still struggling to find how it should work. I know it must be a really trivial thing, but I'm very new to this topic and can't figure that out. Any suggestions?
-
Pythonista doesn't have built-in Git integration, and also doesn't integrate with any iOS Git apps on its own - this is not possible, because Apple doesn't allow apps to execute downloaded code. I recommend installing Stash (https://github.com/ywangd/stash), it's a bash-like shell for Pythonista and includes a
git
command. Then you can rungit clone https://github.com/username/reponame.git
in a folder to download your repo there. You can also commit, push, etc., although Stash's git is not a full port of the realgit
commands, so there are subtle differences to real git, and some advanced features aren't available. The most important features exist though. -
And also the 2nd question, probably quite related- how can I import non-standard packages into Pythonista? E.g I need to use functions from NLTK package in my scripts.
-
Hi,
This "new from gist" script, really helped me download scripts from github in one go, please try it :) -
My personal favorite in this category is https://github.com/cclauss/Ten-lines-or-less/blob/master/read_zipfile_from_github.py
-
i use this extraordinary script from ? to load full repos
call it repo.py, and load next post for the ui#! python2 # coding: utf-8 # gist https://gist.github.com/1efe1a9e7866523112151c2270b09eb2 import clipboard, console, requests, ui, urlparse, zipfile, re, os try: from cStringIO import StringIO except ImportError: from StringIO import StringIO class Delegate (object): def __init__(self): self.selected_item = None def tableview_did_select(self, tableview, section, row): self.selected_item = tableview.data_source.items[row] tableview.superview.close() repolink = "https://github.com/{}/{}/archive/master.zip" gistslink = "https://api.github.com/users/{}/gists" browselink = "https://api.github.com/users/{}/repos" releaselink = "https://api.github.com/repos/{}/{}/releases" parselink = re.compile(r'<[\S]*?page=(\d+)>; rel="last"').search def error_alert(msg="General error"): console.alert("Error", msg, "OK", hide_cancel_button=True) def get_page_num(link_header): return int(parselink(link_header).group(1)) @ui.in_background def save_zip(data, name, unzip): if unzip: io = StringIO(data) with zipfile.ZipFile(io) as zp: zp.extractall() else: with open(name + ".zip", "wb") as zp: zp.write(data) @ui.in_background def download_repo(username, repo, unzip): url = repolink.format(username, repo) data = requests.get(url) if isinstance(data, dict) and data["message"] == "Not Found": return error_alert("User '{}' not found".format(username)) elif not data: return error_alert("Repo '{}' not found".format(repo)) try: save_zip(data.content, repo, unzip) except Exception as err: return error_alert("Error downloading repo: {}".format(err)) console.hud_alert("Done!") @ui.in_background def download_release(username, repo, unzip): url = releaselink.format(username, repo) data = requests.get(url).json() if not data: return error_alert("Repo '{}' has no releases".format(repo)) elif "message" in data and data["message"] == "Not Found": return error_alert("Repo '{}' not found".format(repo)) vers = sorted([i["tag_name"] for i in data]) rview = data_view("release", vers) rview.present("sheet") rview.wait_modal() tapped_text = rview["rtable"].delegate.selected_item if tapped_text: for d in data: if d["tag_name"] == tapped_text: zipurl = d["zipball_url"] save_zip(requests.get(zipurl).content, tapped_text, unzip) return console.hud_alert("Done!") @ui.in_background def download_gist(username, gist): url = gistslink.format(username, gist) req = requests.get(url) data = req.json() req.close() info = [i for i in data if i["id"] == gist] if not info: return error_alert("Gist '{}' not found".format(gist)) info = info[0] files = info["files"] try: os.mkdir(gist) except: pass for fpinfo in files.values(): data = requests.get(fpinfo["raw_url"]).content with open(os.path.join(gist, fpinfo["filename"]), "wb") as fp: fp.write(data) return console.hud_alert("Done!") @ui.in_background def gitdownload(button): index = view["sgcontrol"].selected_index username = view["username"].text = view["username"].text.strip() reponame = view["reponame"].text = view["reponame"].text.strip() unzip = view["dounzip"].value if not username: return error_alert("Please enter username") if not reponame: return error_alert("Please enter repo name") console.show_activity() if index == 0: download_repo(username, reponame, unzip) elif index == 1: download_release(username, reponame, unzip) elif index == 2: download_gist(username, reponame) console.hide_activity() @ui.in_background def gitbrowse(sender): username = view["username"].text = view["username"].text.strip() if not username: return error_alert("Please enter username") index = view["sgcontrol"].selected_index if index == 2: url = gistslink.format(username) else: url = browselink.format(username) try: req = requests.get(url) data = req.json() # normally returns a list of dicts except requests.HTTPError as err: return error_alert("User '{}' not found".format(username)) except Exception as err: return error_alert("Error downloading metadata: {}".format(err)) if isinstance(data, dict) and data["message"] == "Not Found": return error_alert("User '{}' not found".format(username)) finaldata = data if "link" in req.headers: pages = get_page_num(req.headers["link"]) for lnk in range(2, pages + 1): link = browselink.format(username) + "?page=%d" % lnk req = requests.get(link) finaldata += req.json() if index == 2: nameid_dict = {} for fpinfo in finaldata: nameid_dict[", ".join(fpinfo["files"].keys())] = fpinfo["id"] names = nameid_dict.keys() else: names = sorted([i["name"] for i in finaldata]) name = {0: "repos", 1: "repos", 2: "gists"}[index] if not names: return error_alert("User '{}' has no {}".format(username, name)) rview = data_view(name[:-1], names) rview.present("sheet") rview.wait_modal() tapped_text = rview["rtable"].delegate.selected_item if tapped_text: if index == 2: view["reponame"].text = nameid_dict[tapped_text] else: view["reponame"].text = tapped_text def data_view(name, data): rview = ui.View(name="Choose a " + name) table = ui.TableView() table.name = "rtable" table.flex = "WH" table.data_source = ui.ListDataSource(data) table.data_source.delete_enabled = False table.delegate = Delegate() rview.add_subview(table) return rview def segchange(sender): index = sender.selected_index if index == 2: # Gist view["repolabel"].text = "Gist ID:" view["bbutton"].title = "Browse gists" else: view["repolabel"].text = "Repo:" view["bbutton"].title = "Browse repos" view = ui.load_view('_gitrepo') for name in 'username reponame'.split(): view[name].autocapitalization_type = ui.AUTOCAPITALIZE_NONE parse = urlparse.urlparse(clipboard.get().strip()) if parse.netloc in "www.github.com github.com".split(): path = [i for i in parse.path.split("/") if i] if len(path) >= 2: view["username"].text, view["reponame"].text = path[:2] view["sgcontrol"].action = segchange view.present('popover')
-
copy the next code and paste it into repo.pyui
when you run repo.py with a github repo in the clipboard, you'll have the repo name decoded. Then just tap download and a new directory appears with everthing. Fantastic![{"class":"View","attributes":{"name":"gitrepo","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {341, 250}}","nodes":[{"class":"SegmentedControl","attributes":{"name":"sgcontrol","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"308A3491-ABAA-43BC-8D2C-1319469FF3C9","enabled":true,"segments":"Repo|Release|Gist","flex":"LR"},"frame":"{{16, 6}, {196.5, 29}}","nodes":[]},{"class":"Label","attributes":{"font_size":20,"enabled":true,"text":"Username:","flex":"","name":"label1","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"7C45FE1B-8084-4257-A1D1-DE573BC8299A"},"frame":"{{16.5, 57.5}, {96, 35.5}}","nodes":[]},{"class":"TextField","attributes":{"uuid":"18AD549C-48DF-40CF-B008-5E2C283AD3EA","alignment":"left","autocorrection_type":"no","font_size":17,"font_name":"Avenir-Book","enabled":true,"flex":"","border_color":"RGBA(0.571429,0.571429,0.571429,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","corner_radius":2,"secure":false,"border_width":1,"border_style":3,"name":"username","spellchecking_type":"no"},"frame":"{{120.5, 57.5}, {200, 36}}","nodes":[]},{"class":"Label","attributes":{"font_size":20,"enabled":true,"text":"Repo:","flex":"","name":"repolabel","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"97359D8F-3BD5-4D8F-8711-14B4565F9601"},"frame":"{{16.5, 102}, {96, 35}}","nodes":[]},{"class":"TextField","attributes":{"name":"reponame","alignment":"left","autocorrection_type":"no","font_size":17,"font_name":"Avenir-Book","enabled":true,"flex":"","border_color":"RGBA(0.571429,0.571429,0.571429,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","corner_radius":2,"secure":false,"uuid":"7BFD908D-CA67-4976-94DD-5CF0DB5EC360","border_style":3,"border_width":1,"spellchecking_type":"no"},"frame":"{{120, 101.5}, {200, 36}}","nodes":[]},{"class":"Button","attributes":{"font_size":16,"enabled":true,"flex":"","font_bold":false,"name":"dbutton","corner_radius":9,"border_color":"RGBA(0.244898,0.520408,0.857143,1.000000)","border_width":1,"action":"gitdownload","uuid":"7C58793E-E2CA-4724-B2FC-3E779FD71890","title":"Download"},"frame":"{{16.5, 201}, {96, 31}}","nodes":[]},{"class":"Label","attributes":{"font_size":20,"enabled":true,"text":"Unzip archive:","flex":"","name":"label3","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"3A801AA2-D086-48E9-9262-E9880135E8EA"},"frame":"{{16.5, 145}, {137, 35}}","nodes":[]},{"class":"Switch","attributes":{"enabled":true,"flex":"","name":"dounzip","value":true,"alpha":1,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"40756EA8-214B-4247-9A47-F7D519C0BE88"},"frame":"{{161.5, 147}, {51, 31}}","nodes":[]},{"class":"Button","attributes":{"font_size":16,"enabled":true,"flex":"","font_bold":false,"name":"bbutton","corner_radius":9,"border_color":"RGBA(0.244898,0.520408,0.857143,1.000000)","border_width":1,"action":"gitbrowse","uuid":"127F82F8-97A9-4835-AD60-DF5FCDC942E3","title":"Browse repos"},"frame":"{{139, 201}, {119.5, 31}}","nodes":[]}]}]
-
Thanks @all for your great hints, seems that I can progress now from this point.
-
# download an entire github repo. # # either copy the url to clipboard, and run script, or run following bookmarklet. # will unzip to repo-branch (so be careful if downloading same branch name from multiple users) # ## javascript:(function()%7Bif(document.location.href.indexOf('http')===0)document.location.href='pythonista://GitHubGet?action=run&argv='+document.location.href;%7D)(); import urllib.request, urllib.parse, urllib.error,zipfile,sys, clipboard, functools, re, os, tempfile def extract_git_id(git): print(git) m = re.match((r'^http(s?)://([\w-]*\.)?github\.com/(?P<user>[\w-]+)/(?P<repo>[\w-]*)' '((/tree|/blob)/(?P<branch>[\w-]*))?'), git) # print m.groupdict() return m def git_download_from_args(args): if len(args) == 2: url = args[1] else: url = clipboard.get() git_download(url) def dlProgress(filename, count, blockSize, totalSize): if count*blockSize > totalSize: percent=100 else: percent = max(min(int(count*blockSize*100/totalSize),100),0) sys.stdout.write("\r" + filename + "...%d%%" % percent) sys.stdout.flush() def git_download(url): base='https://codeload.github.com' archive='zip' m=extract_git_id(url) if m: g=m.groupdict() if not g['branch']: g['branch']='master' u= '/'.join((base,g['user'],g['repo'],archive, g['branch'])) #print u try: with tempfile.NamedTemporaryFile(mode='w+b',suffix='.zip') as f: urllib.request.urlretrieve(u,f.name,reporthook=functools.partial(dlProgress,u)) z=zipfile.ZipFile(f) z.extractall() print(z.namelist()) except: print('git url did not return zip file') else: print('could not determine repo url from clipboard or argv') if __name__=='__main__': git_download_from_args(sys.argv)
Copy the link to the repot and run the script above. It can download entire repos.
-
@dgelessus, regarding those subtle differences and how to commit new repo and changes to the repo, I have been struggling to find a how to for doing this in StaSH. Any pointers would be highly appreciated.
-
@mikael The most important things I can think of right now:
- Cloning a repo (
git clone https://github.com/dgelessus/myrepo.git
) with Stashgit
puts the repo into the current directory. Realgit
creates a new subdirectory (in this casemyrepo
) and clones the repo in there. - If you use certain commands in a freshly
git init
ed repo with no commits, you'll get an obscure error message that theHEAD
is missing. To fix this, you just need to commit some files. - If you get "Broken pipe" errors when pushing, that means your local copy of the repo is outdated and you need to
git pull
first (I think). - Before checking out a different branch in Stash
git
, make sure that you have committed (or otherwise manually backed up) all your changes, i. e. you should have no unstaged or staged changes. Stash'sgit checkout
doesn't always check if there are uncommitted changes, and simply overwrites/discards them.
There are also some minor syntax differences for a few commands. When in doubt, run
git something --help
. Though I think for some commands that don't expect any arguments (likegit init
) that just runs the command instead of printing a help message.Also please note that some of these issues might be fixed in newer Stash versions.
- Cloning a repo (
-
@dgelessus, thanks a lot!
Also, just found this cheatsheet, but have not done anything yet.
-
There is also this, which attempted to go through the basics...
https://github.com/jsbain/stash_git_tutorial/blob/master/stash_git_tutorial.md -
@JonB, looks just the ticket, thanks. Inconvenient how Google did not turn this up earlier, even with all the magic keywords.
-
@JonB, I was successful with the cheatsheet, but completely failed with your tutorial, with some KeyError. I think the missing bit is the intro on how to do the authentication.
-
Oh right, in my tutorial it is assumed you have previously pushed to a github repo using stash git, since that saves your login info. I will update the gh command to do that prompt for user info