Skip to content

Commit

Permalink
🚧 Improved displaying of lists items
Browse files Browse the repository at this point in the history
added a generator logic for displaying lists,
that's following this algorithm:

- yield the format
- yield the header (as tuple matching the format)
- yield content line by line (as tuple matching the format)

and do not display the header when not sending to a tty
and do not display the "cursor display" character voodoo when not
sending to a tty.

fixes #114

Signed-off-by: Guyzmo <[email protected]>
  • Loading branch information
guyzmo committed Feb 3, 2017
1 parent 244d189 commit 583734f
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 194 deletions.
46 changes: 7 additions & 39 deletions git_repo/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
from .exceptions import ArgumentError, ResourceNotFoundError
from .services.service import RepositoryService

from .tools import print_tty, print_iter, loop_input, confirm
from .kwargparse import KeywordArgumentParser, store_parameter, register_action

from git import Repo, Git
Expand All @@ -146,28 +147,6 @@

EXTRACT_URL_RE = re.compile('[^:]*(://|@)[^/]*/')

def loop_input(*args, method=input, **kwarg):
out = ''
while len(out) == 0:
out = method(*args, **kwarg)
return out

def confirm(what, where):
'''
Method to show a CLI based confirmation message, waiting for a yes/no answer.
"what" and "where" are used to better define the message.
'''
ans = input('Are you sure you want to delete the '
'{} {} from the service?\n[yN]> '.format(what, where))
if 'y' in ans:
ans = loop_input('Are you really sure? there\'s no coming back!\n'
'[type \'burn!\' to proceed]> ')
if 'burn!' != ans:
return False
else:
return False
return True


class GitRepoRunner(KeywordArgumentParser):

Expand Down Expand Up @@ -289,8 +268,7 @@ def store_gitconfig(self, val):
@register_action('ls')
@register_action('list')
def do_list(self):
service = self.get_service(False)
service.list(self.user, self.long)
print_iter(self.get_service(False).list(self.user, self.long))
return 0

@register_action('add')
Expand Down Expand Up @@ -409,10 +387,8 @@ def do_open(self):
@register_action('request', 'list')
def do_request_list(self):
service = self.get_service(lookup_repository=self.repo_slug == None)
log.info('List of open requests to merge:')
log.info(" {}\t{}\t{}".format('id', 'title'.ljust(60), 'URL'))
for pr in service.request_list(self.user_name, self.repo_name):
print("{}\t{}\t{}".format(pr[0].rjust(3), pr[1][:60].ljust(60), pr[2]))
print_tty('List of open requests to merge:')
print_iter(service.request_list(self.user_name, self.repo_name))
return 0

@register_action('request', 'create')
Expand Down Expand Up @@ -494,16 +470,7 @@ def do_request_fetch(self):
@register_action('snippet', 'list')
def do_gist_list(self):
service = self.get_service(lookup_repository=False)
if 'github' == service.name and self.gist_ref:
log.info("{:15}\t{:>7}\t{}".format('language', 'size', 'name'))
else:
log.info("{:56}\t{}".format('id', 'title'.ljust(60)))
if self.gist_ref:
for gist_file in service.gist_list(self.gist_ref):
print("{:15}\t{:7}\t{}".format(*gist_file))
else:
for gist in service.gist_list():
print( "{:56}\t{}".format(gist[0], gist[1]))
print_iter(service.gist_list(self.gist_ref or None))
return 0

@register_action('gist', 'clone')
Expand Down Expand Up @@ -638,7 +605,8 @@ def cli(): #pragma: no cover
sys.exit(main(docopt(__doc__.format(self=sys.argv[0].split('/')[-1], version=__version__))))
finally:
# Whatever happens, make sure that the cursor reappears with some ANSI voodoo
sys.stdout.write('\033[?25h')
if sys.stdout.isatty():
sys.stdout.write('\033[?25h')

if __name__ == '__main__': #pragma: no cover
cli()
52 changes: 21 additions & 31 deletions git_repo/services/ext/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

from ..service import register_target, RepositoryService, os
from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError, ArgumentError
from ...tools import columnize

import github3

from git.exc import GitCommandError

from datetime import datetime

@register_target('hub', 'github')
class GithubService(RepositoryService):
fqdn = 'github.com'
Expand Down Expand Up @@ -75,37 +78,18 @@ def delete(self, repo, user=None):
raise ResourceError('Unhandled exception: {}'.format(err)) from err

def list(self, user, _long=False):
import shutil, sys
from datetime import datetime
term_width = shutil.get_terminal_size((80, 20)).columns
def col_print(lines, indent=0, pad=2):
# prints a list of items in a fashion similar to the dir command
# borrowed from https://gist.github.com/critiqjo/2ca84db26daaeb1715e1
n_lines = len(lines)
if n_lines == 0:
return
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len : i*col_len + col_len] for i in range(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
rows.extend(rows_missed)
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row))

if not self.gh.user(user):
raise ResourceNotFoundError("User {} does not exists.".format(user))

repositories = self.gh.iter_user_repos(user)
if not _long:
repositories = list(repositories)
col_print(["/".join([user, repo.name]) for repo in repositories])
repositories = list(["/".join([user, repo.name]) for repo in repositories])
yield "{}"
yield "Total repositories: {}".format(len(repositories))
yield from columnize(repositories)
else:
print('Status\tCommits\tReqs\tIssues\tForks\tCoders\tWatch\tLikes\tLang\tModif\t\tName', file=sys.stderr)
yield "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}"
yield ['Status', 'Commits', 'Reqs', 'Issues', 'Forks', 'Coders', 'Watch', 'Likes', 'Lang', 'Modif', 'Name']
for repo in repositories:
try:
if repo.updated_at.year < datetime.now().year:
Expand All @@ -119,7 +103,7 @@ def col_print(lines, indent=0, pad=2):
])
nb_pulls = len(list(repo.iter_pulls()))
nb_issues = len(list(repo.iter_issues())) - nb_pulls
print('\t'.join([
yield [
# status
status,
# stats
Expand All @@ -134,10 +118,10 @@ def col_print(lines, indent=0, pad=2):
repo.language or '?', # language
repo.updated_at.strftime(date_fmt), # date
'/'.join([user, repo.name]), # name
]))
]
except Exception as err:
if 'Git Repository is empty.' == err.args[0].json()['message']:
print('\t'.join([
yield [
# status
'E',
# stats
Expand All @@ -152,7 +136,7 @@ def col_print(lines, indent=0, pad=2):
'?', # language
repo.updated_at.strftime(date_fmt), # date
'/'.join([user, repo.name]), # name
]))
]
else:
print("Cannot show repository {}: {}".format('/'.join([user, repo.name]), err))

Expand All @@ -167,12 +151,16 @@ def _format_gist(self, gist):

def gist_list(self, gist=None):
if not gist:
yield "{:45.45} {}"
yield 'title', 'url'
for gist in self.gh.iter_gists(self.gh.user().login):
yield (gist.html_url, gist.description)
yield gist.description, gist.html_url
else:
gist = self.gh.gist(self._format_gist(gist))
if gist is None:
raise ResourceNotFoundError('Gist does not exists.')
yield "{:15}\t{:7}\t{}"
yield 'language', 'size', 'name'
for gist_file in gist.iter_files():
yield (gist_file.language if gist_file.language else 'Raw text',
gist_file.size,
Expand Down Expand Up @@ -285,8 +273,10 @@ def request_create(self, user, repo, from_branch, onto_branch, title=None, descr

def request_list(self, user, repo):
repository = self.gh.repository(user, repo)
yield "{}\t{:<60}\t{}"
yield 'id', 'title', 'URL'
for pull in repository.iter_pulls():
yield ( str(pull.number), pull.title, pull.links['html'] )
yield str(pull.number), pull.title, pull.links['html']

def request_fetch(self, user, repo, request, pull=False, force=False):
if pull:
Expand Down
43 changes: 14 additions & 29 deletions git_repo/services/ext/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from ..service import register_target, RepositoryService
from ...exceptions import ArgumentError, ResourceError, ResourceExistsError, ResourceNotFoundError
from ...tools import columnize

import gitlab
from gitlab.exceptions import GitlabListError, GitlabCreateError, GitlabGetError
Expand Down Expand Up @@ -74,37 +75,17 @@ def delete(self, repo, user=None):
raise ResourceError("Unhandled exception: {}".format(err)) from err

def list(self, user, _long=False):
import shutil, sys
from datetime import datetime
term_width = shutil.get_terminal_size((80, 20)).columns
def col_print(lines, indent=0, pad=2):
# prints a list of items in a fashion similar to the dir command
# borrowed from https://gist.github.com/critiqjo/2ca84db26daaeb1715e1
n_lines = len(lines)
if n_lines == 0:
return
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len : i*col_len + col_len] for i in range(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
rows.extend(rows_missed)
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row))

if not self.gl.users.search(user):
raise ResourceNotFoundError("User {} does not exists.".format(user))

repositories = self.gl.projects.list(author=user)
if not _long:
repositories = list(repositories)
col_print([repo.path_with_namespace for repo in repositories])
repositories = list([repo.path_with_namespace for repo in repositories])
yield "{}"
yield "Total repositories: {}".format(len(repositories))
yield from columnize(repositories)
else:
print('Status\tCommits\tReqs\tIssues\tForks\tCoders\tWatch\tLikes\tLang\tModif\t\tName', file=sys.stderr)
yield ['Status', 'Commits', 'Reqs', 'Issues', 'Forks', 'Coders', 'Watch', 'Likes', 'Lang', 'Modif\t', 'Name']
for repo in repositories:
time.sleep(0.5)
# if repo.last_activity_at.year < datetime.now().year:
Expand All @@ -116,7 +97,7 @@ def col_print(lines, indent=0, pad=2):
'F' if False else ' ', # is a fork?
'P' if repo.visibility_level == 0 else ' ', # is private?
])
print('\t'.join([
yield [
# status
status,
# stats
Expand All @@ -131,7 +112,7 @@ def col_print(lines, indent=0, pad=2):
'N.A.', # language
repo.last_activity_at, # date
repo.name_with_namespace, # name
]))
]

def get_repository(self, user, repo):
try:
Expand Down Expand Up @@ -163,10 +144,12 @@ def _deconstruct_snippet_uri(self, uri):
return (user, project_name, snippet_id)

def gist_list(self, project=None):
yield "{:45.45} {}"
yield 'title', 'url'
if not project:
try:
for snippet in self.gl.snippets.list():
yield (snippet.web_url, snippet.title)
yield snippet.title, snippet.web_url
except GitlabListError as err:
if err.response_code == 404:
raise ResourceNotFoundError('Feature not available, please upgrade your gitlab instance.') from err
Expand All @@ -177,7 +160,7 @@ def gist_list(self, project=None):
try:
project = self.gl.projects.get(project)
for snippet in project.snippets.list():
yield (snippet.web_url, 0, snippet.title)
yield (snippet.web_url, snippet.title)
except GitlabGetError as err:
raise ResourceNotFoundError('Could not retrieve project "{}".'.format(project)) from err

Expand Down Expand Up @@ -303,6 +286,8 @@ def request_create(self, user, repo, local_branch, remote_branch, title, descrip

def request_list(self, user, repo):
project = self.gl.projects.get('/'.join([user, repo]))
yield "{:>3}\t{:<60}\t{:2}"
yield ('id', 'title', 'URL')
for mr in self.gl.project_mergerequests.list(project_id=project.id):
yield ( str(mr.iid),
mr.title,
Expand Down
31 changes: 9 additions & 22 deletions git_repo/services/ext/gogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

from ..service import register_target, RepositoryService, os
from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError
from ...tools import columnize

from gogs_client import GogsApi, GogsRepo, Token, UsernamePassword, ApiFailure
from requests import Session, HTTPError
from urllib.parse import urlparse, urlunparse
from datetime import datetime
import functools

from git import config as git_config
Expand Down Expand Up @@ -152,32 +154,17 @@ def list(self, user, _long=False):
import shutil, sys
from datetime import datetime
term_width = shutil.get_terminal_size((80, 20)).columns
def col_print(lines, indent=0, pad=2):
# prints a list of items in a fashion similar to the dir command
# borrowed from https://gist.github.com/critiqjo/2ca84db26daaeb1715e1
n_lines = len(lines)
if n_lines == 0:
return
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len : i*col_len + col_len] for i in range(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
rows.extend(rows_missed)
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row))

repositories = self.gg.repositories(user)
if user != self.username and not repositories and user not in self.orgs:
raise ResourceNotFoundError("Unable to list namespace {} - only authenticated user and orgs available for listing.".format(user))
if not _long:
col_print([repo['full_name'] for repo in repositories])
repositories = list([repo['full_name'] for repo in repositories])
yield "{}"
yield "Total repositories: {}".format(len(repositories))
yield from columnize(repositories)
else:
print('Status\tCommits\tReqs\tIssues\tForks\tCoders\tWatch\tLikes\tLang\tModif\t\t\t\tName', file=sys.stderr)
yield ['Status', 'Commits', 'Reqs', 'Issues', 'Forks', 'Coders', 'Watch', 'Likes', 'Lang', 'Modif\t', 'Name']
for repo in repositories:
status = ''.join([
'F' if repo['fork'] else ' ', # is a fork?
Expand All @@ -187,7 +174,7 @@ def col_print(lines, indent=0, pad=2):
issues = self.gg._check_ok(self.gg._get('/repos/{}/issues'.format(repo['full_name']), auth=self.auth)).json()
except Exception:
issues = []
print('\t'.join([
yield [
# status
status,
# stats
Expand All @@ -202,7 +189,7 @@ def col_print(lines, indent=0, pad=2):
repo.get('language') or '?', # language
repo['updated_at'], # date
repo['full_name'], # name
]))
]

def get_repository(self, user, repo):
try:
Expand Down
Loading

0 comments on commit 583734f

Please sign in to comment.