Fixing up your gits Part 3: Making your python code object-oriented

Brad Johnson
3 min readJan 6, 2020

--

So I went to the trouble of making my Github search-and-clone automator, but I didn’t code it as object-oriented (see Part 1 and Part 2 of “fixing up your gits”). Fortunately, Python makes it easy to make object-oriented code.

In Part 2, I had written the fixorigin.py script that pulled in some functions from githubtools.py.

To review, the fixorigin.py script looked like this:

< docopt usage and configuration code >import os
from github import Github
from githubtools import *
< code to set ACCESS_TOKEN >g=Github(ACCESS_TOKEN)def fix_remote_origin(repo):
try:
repo.remotes['origin']: # check if remote origin exists
except:
print(f"{repo.path} doesn't have a remote origin.")
else:
try:
origin_repo = get_github_repo_from_url(g,repo.remotes['origin'].url, config)
except:
print(f"Can't find {repo.remotes['origin'].url} on Github.")
else:
if repo.remotes['origin'].url != origin_repo.clone_url:
repo.remotes.set_url('origin',origin_repo.clone_url)
for root, dirs, files in os.walk(topDir):
if pygit2.discover_repository(root):
repo = pygit2.Repository(pygit2.discover_repository(root))
fix_remote_origin(repo)
dirs[:] = [] # ignore subdirectories

After making githubtools into an object module, I can instead use this actual code for fixorigin.py:

< docopt usage and configuration code >from githubtools import Githubtoolght = Githubtool(is_verbose=config['--verbose'],
is_test=config['--test'],
root_dir=config['--dir'],
access_token_file=config['-f'],
access_token=config['-t'])
for repo in ght.local_repos:
ght.set_origin(repo)

Wow, that’s so much simpler! We’ve gotten rid of import os and all of the directory traversal code and from github import Github and all of the code connecting to Github.

Of course, we didn’t actually get rid of all that code; it’s all buried in from githubtools import Githubtool.

What did I have to do for this?

  1. Make the Githubtool object with a __init__ method that takes in the configuration variables.
  2. Make the Githubtool attribute .local_repos that is generated from the directory traversal code we had in the previous version of fix_origin.py.
  3. Make the Githubtool method .set_origin that is generated from the origin-setting code we had in the previous version of fix_origin.py.

Step 1: initialize our Githubtool object

Here’s what the Githubtool __init__ method looks like:

def __init__(self,
is_verbose=False,
is_test=False,
root_dir='~/',
access_token_file='~/.oAuth',
access_token=None,
):
self.is_verbose = is_verbose
self.is_test = is_test
self.root_dir = root_dir
# initialize Github objects
_access_token = self.set_access_token(access_token_file, access_token)
self.g = Github(_access_token)
self.local_repos = []
self._load_local_repos()

Step 2: Generate list of local repositories under our root directory

As you can see, _load_local_repos is almost the same as the directory traversal code originally in fixorigin.py.

def _load_local_repos(self):
"""set self.local_repos from traverse of self.root_dir"""
self.local_repos = []
for root, dirs, files in os.walk(self.root_dir):
if self.dir_is_repo(root):
repo = self.local_repo_from_repo_path(root)
self.local_repos.append(repo)
dirs[:] = [] # don't descend into repo

Step 3: Create method to set the origin for a given local repository

The Githubtool method .set_origin looks very much like that of fix_origin.py. Our main difference is that we can pass in the origin_repo as a variable instead of forcing the code to look it up from the local_repo.

This code works either to set the remote origin for a local repo or to fix it.

def set_origin(self, repo, origin_repo=None, ignore_errors=True):
if not origin_repo: # need to set origin_repo
try:
repo.remotes['origin'] # check if remote origin exists
except:
if not ignore_errors:
print(f"{repo.path} does not have an origin remote. Specify origin_repo")
else:
origin_repo = self.get_github_repo_from_url(repo.remotes['origin'].url)
if ('origin' not in repo.remotes) or (repo.remotes['origin'].url != origin_repo.clone_url):
repo.remotes.set_url('origin', origin_repo.clone_url)

What’s exciting for me about this is that our final code for fixorigin.py now looks more like pseudocode:

for repo in ght.local_repos:
ght.set_origin(repo)

--

--

Brad Johnson
Brad Johnson

Written by Brad Johnson

Climate strategist, HillHeat.News. Former Climate Hawks Vote ED, Campaign Manager for Forecast the Facts, ThinkProgress Green Editor.

No responses yet