diff --git a/setup.py b/setup.py index 3f3d24d..e309436 100644 --- a/setup.py +++ b/setup.py @@ -1,43 +1,45 @@ import sys from os import path from io import open from setuptools import setup, find_packages here = path.abspath(path.dirname(__file__)) src_dir = path.join(here, "src") # When executing the setup.py, we need to be able to import ourselves, this # means that we need to add the src/ directory to the sys.path. sys.path.insert(0, src_dir) # Get the long description from the README file with open(path.join(here, 'README.md'), encoding='utf-8') as f: long_description = f.read() setup( name='pyarcanist', version='0.0.1', description='Pure python cli for Phabricator', long_description=long_description, long_description_content_type='text/markdown', author='David Douard', author_email='david.douard@sdfa3.org', url='https://framagit.org/douardda/pyarcanist', install_requires=[ + 'beaker', 'click', + 'gitpython', 'phabricator', - 'gitpython'], + ], package_dir={"": "src"}, packages=find_packages('src'), entry_points={ 'console_scripts': [ 'pyarc=pyarcanist.cli:pyarc']}, classifiers=[ "Programming Language :: Python :: 3", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", "Development Status :: 3 - Alpha", ], ) diff --git a/src/pyarcanist/__init__.py b/src/pyarcanist/__init__.py index 37a11af..00e8a8d 100644 --- a/src/pyarcanist/__init__.py +++ b/src/pyarcanist/__init__.py @@ -1,4 +1,5 @@ +from .cache import cache from . import whoami from . import diff -__all__ = (whoami, diff) +__all__ = (whoami, diff, cache) diff --git a/src/pyarcanist/cache.py b/src/pyarcanist/cache.py new file mode 100644 index 0000000..5e9a9c7 --- /dev/null +++ b/src/pyarcanist/cache.py @@ -0,0 +1,13 @@ +import os +from beaker.cache import CacheManager +from beaker.util import parse_cache_config_options + +cache_opts = { + 'cache.type': 'file', + 'cache.data_dir': os.path.expanduser('~/.cache/pyarcanist/data'), + 'cache.lock_dir': os.path.expanduser('~/.cache/pyarcanist/lock') +} + +cache = CacheManager(**parse_cache_config_options(cache_opts)) + +__all__ = (cache, ) diff --git a/src/pyarcanist/cli.py b/src/pyarcanist/cli.py index 0c8fa2e..63140c5 100644 --- a/src/pyarcanist/cli.py +++ b/src/pyarcanist/cli.py @@ -1,24 +1,30 @@ import click from phabricator import Phabricator +# we use a global variable to store the Phabricator instance so we do not +# have to add the cnx argument to several utility functions which can then +# be cached by beaker. Not very elegent but it works. +cnx = None + class options(dict): def __getattr__(self, key): return self[key] def __setattr__(self, key, value): self[key] = value @click.group() @click.option('-v', '--verbose/--no-verbose', default=False, envvar='VERBOSE') @click.pass_context def pyarc(ctx, verbose): """Entry point""" + global cnx ctx.ensure_object(dict) - ctx.obj['cnx'] = Phabricator() + ctx.obj['cnx'] = cnx = Phabricator() ctx.obj['options'] = options(verbose=verbose) if __name__ == '__main__': pyarc(obj={}) diff --git a/src/pyarcanist/diff.py b/src/pyarcanist/diff.py index 9068f9b..d77bbf3 100644 --- a/src/pyarcanist/diff.py +++ b/src/pyarcanist/diff.py @@ -1,84 +1,90 @@ from itertools import chain import click import git -from .cli import pyarc +from . import cli from .whoami import get_user from .tools import wrap, object_from_phid +from . import cache -def get_repositories(cnx, uris): +@cache.cache() +def get_repositories(uris): if isinstance(uris, str): uris = [uris] - return cnx.diffusion.repository.search( + return cli.cnx.diffusion.repository.search( constraints={'uris': uris}).data -def repo_from_phid(cnx, phid): - repo = cnx.diffusion.repository.search( +@cache.cache() +def repo_from_phid(phid): + repo = cli.cnx.diffusion.repository.search( constraints={'phids': [phid]}).data return repo and repo[0] or None def format_diff(kw): - kw = kw.copy() + # warning: this function modifies the given dict 'kw' kw['id'] = click.style(str(kw['id']), bold=True) kw['fields']['status']['name'] = click.style( kw['fields']['status']['name'], fg=kw['fields']['status']['color.ansi']) return kw -@pyarc.command() +@cli.pyarc.command() @click.option('-u', '--mine/--all-users', default=False) @click.option('-A', '--all-repos/--current-repo', default=False) @click.option('-s', '--summary/--default', default=False) @click.pass_context def diff(ctx, mine, all_repos, summary): '''List Diffs''' - cnx = ctx.obj['cnx'] - user = get_user(cnx) + cnx = cli.cnx + user = get_user() # options = ctx.obj['options'] query = {'statuses': ['open()']} gitrepo = None repos = None if not all_repos: try: gitrepo = git.Repo() remotes = list(chain(*(r.urls for r in gitrepo.remotes))) - repos = get_repositories(cnx, remotes) + repos = get_repositories(remotes) except git.InvalidGitRepositoryError: pass if repos: query['repositoryPHIDs'] = [r['phid'] for r in repos] if mine: - query['authorPHIDs'] = [user.phid] + query['authorPHIDs'] = [user['phid']] # print('query=', query) diffs = cnx.differential.revision.search(constraints=query).data for diff in sorted(diffs, key=lambda x: int(x['id'])): - fields = diff['fields'] + fdiff = format_diff(diff) if summary: click.echo( '{fields[status][name]:25} D{id}: {fields[title]}'.format( - **format_diff(diff))) + **fdiff)) else: click.echo( wrap('{fields[status][name]:25} D{id}'.format( - **format_diff(diff)))) + **fdiff))) # give a bit more informations - phrepo = repo_from_phid(cnx, fields['repositoryPHID'])['fields'] - author = object_from_phid(cnx, fields['authorPHID']) + fields = fdiff['fields'] + phrepo = repo_from_phid(fields['repositoryPHID'])['fields'] + author = get_user(fields['authorPHID']) + click.echo('{key}: {shortName} ({callsign})'.format( key=click.style('Repo', fg='yellow'), **phrepo)) + click.echo('{key}: {value}'.format( key=click.style('Author', fg='yellow'), value=author['name'])) click.secho('Summary:', fg='yellow') click.secho(' ' + fields['title'], bold=True) click.echo() click.echo('\n'.join(' ' + x for x in fields['summary'].splitlines())) click.echo() diff --git a/src/pyarcanist/tools.py b/src/pyarcanist/tools.py index 24dff9b..c3d0731 100644 --- a/src/pyarcanist/tools.py +++ b/src/pyarcanist/tools.py @@ -1,13 +1,15 @@ import os +from . import cli + ROWS, COLUMNS = map(int, os.popen('stty size', 'r').read().split()) def wrap(msg, width=COLUMNS): if len(msg) > width: return msg[:width-1] + '\u2026' return msg -def object_from_phid(cnx, phid): - return cnx.phid.query(phids=[phid])[phid] +def object_from_phid(phid): + return cli.cnx.phid.query(phids=[phid])[phid] diff --git a/src/pyarcanist/whoami.py b/src/pyarcanist/whoami.py index 1c96eae..1e099cb 100644 --- a/src/pyarcanist/whoami.py +++ b/src/pyarcanist/whoami.py @@ -1,18 +1,22 @@ import click -from .cli import pyarc +from . import cache +from . import cli -def get_user(cnx): - return cnx.user.whoami() +@cache.cache() +def get_user(phid=None): + if phid is None: + return dict(cli.cnx.user.whoami()) + return cli.cnx.phid.query(phids=[phid])[phid] -@pyarc.command() +@cli.pyarc.command() @click.pass_context def whoami(ctx): '''Gives informations on the current user''' - user = get_user(ctx.obj['cnx']) + user = get_user() click.echo("{userName} ({realName})".format(**user)) options = ctx.obj['options'] if options.verbose: for k in ('phid', 'primaryEmail', 'roles', 'uri'): click.echo(" {}: {}".format(k, user[k]))