Fixing up your gits Part 3: Making your python code object-oriented
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?
- Make the Githubtool object with a
__init__
method that takes in the configuration variables. - Make the Githubtool attribute
.local_repos
that is generated from the directory traversal code we had in the previous version offix_origin.py
. - Make the Githubtool method
.set_origin
that is generated from the origin-setting code we had in the previous version offix_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)