224 lines
5.9 KiB
Python
224 lines
5.9 KiB
Python
|
||
"""Interactive python shell with port_stats
|
||
|
||
What's in the box:
|
||
|
||
- load() -- (re)loads the task and packages information
|
||
- use(repo) -- switch to using <repo>
|
||
- load(repo) -- load stuff and start using the given repo instead of default
|
||
- pt(name) -- prints information about the package tasks
|
||
- spt(name) -- prints information about the package tasks, short version
|
||
- spi(name) -- prints the short package information
|
||
- gspi(pattern) -- prints the short package information for
|
||
all the packages with name matching pattern ('g' for 'grep')
|
||
- list_spi(pkgs_list) -- same as gspi, but for python lists
|
||
of strings instead of patterns
|
||
- ti(num) -- prints information about the task #num
|
||
- fti(num) -- prints information about the task #num, full version (a dict)
|
||
- logs(num, [idx]) -- print logs (logs/events.X.Y.log) for task #num
|
||
- next_tasks() -- display all non-done tasks that would update packages
|
||
- stats() -- prints packages statistics
|
||
|
||
You can also enjoy autocompletion with <TAB>.
|
||
"""
|
||
|
||
from __future__ import print_function
|
||
|
||
import atexit
|
||
import json
|
||
import logging
|
||
import os
|
||
import re
|
||
import readline
|
||
import rlcompleter
|
||
import sys
|
||
|
||
from pydoc import pager
|
||
|
||
from port_stats import colorize
|
||
from port_stats import lists
|
||
from port_stats import tasks
|
||
from port_stats import reports
|
||
from port_stats import utils
|
||
|
||
|
||
|
||
LOG = logging.getLogger('port_stats.interactive')
|
||
|
||
|
||
def get_task(taskid, task_list):
|
||
for task in task_list:
|
||
if int(task['taskid']) == int(taskid):
|
||
return task
|
||
|
||
|
||
def package_tasks(pkg, data):
|
||
ts = sorted(data[pkg], key=lambda t: int(t['taskid']))
|
||
return '\n'.join(tasks.format_task(t) for t in ts)
|
||
|
||
|
||
def task_event_logs(num, task_list):
|
||
info = get_task(num, task_list)
|
||
if info is None:
|
||
return []
|
||
path = os.path.join(info['task_path'], 'logs')
|
||
res = sorted((f for f in os.listdir(path) if f.startswith('events')),
|
||
key=lambda f: [utils.maybe_int(x) for x in f.split('.')])
|
||
return [os.path.join(path, x) for x in res]
|
||
|
||
|
||
## To targets ##
|
||
|
||
def dump(string):
|
||
print(string)
|
||
|
||
|
||
def append_to(filename):
|
||
if os.path.exists(filename):
|
||
LOG.info('File %s already exists, appending', filename)
|
||
def _write(string):
|
||
with open(filename, 'a') as f:
|
||
f.write(string)
|
||
f.write('\n')
|
||
return _write
|
||
|
||
|
||
## The Interactive Part ##
|
||
|
||
CONFIG = None
|
||
CURRENT = None
|
||
REPOS = {}
|
||
TASKS = []
|
||
PACKAGE_TASKS = {}
|
||
BY_NAME = {}
|
||
BY_COLOR = {}
|
||
|
||
|
||
def use(repo=None):
|
||
global PACKAGE_TASKS, BY_NAME, BY_COLOR, CURRENT
|
||
if repo is not None:
|
||
try:
|
||
CURRENT = (c for c in CONFIG['colorize'] if c['name'] == repo).next()
|
||
except StopIteration:
|
||
raise ValueError('Unknown repo: ' + repo)
|
||
elif CURRENT is None:
|
||
CURRENT = CONFIG['colorize'][0]
|
||
LOG.info("Updating data structures for %s...", CURRENT['name'])
|
||
sys.ps1 = CURRENT['name'] + '>>> '
|
||
PACKAGE_TASKS = tasks.tasks_by_package(
|
||
t for t in TASKS if t['repo'] == CURRENT['new'])
|
||
BY_NAME, BY_COLOR = colorize.colorize(REPOS[CURRENT['base']],
|
||
REPOS[CURRENT['new']])
|
||
LOG.info("DONE")
|
||
|
||
|
||
def load(repo=None):
|
||
global TASKS, REPOS
|
||
TASKS = tasks.load_tasks(CONFIG['tasks'])
|
||
REPOS = lists.read_all_srclists(CONFIG['repos'])
|
||
LOG.info("Got %s tasks, %s repositories", len(TASKS), len(REPOS))
|
||
for name in sorted(REPOS):
|
||
LOG.info(" %s: %s srpms", name, len(REPOS[name]))
|
||
use(repo)
|
||
|
||
|
||
def pt(pkg, to=print):
|
||
to(package_tasks(pkg, PACKAGE_TASKS))
|
||
|
||
|
||
def spt(pkg, to=print):
|
||
to(tasks.format_tasks_short(PACKAGE_TASKS.get(pkg)))
|
||
|
||
|
||
def spi(pkg, to=print):
|
||
to(reports.package_one_line(pkg, BY_NAME, PACKAGE_TASKS))
|
||
|
||
|
||
def _spi_by_predicate(pred, colors, to):
|
||
colors = colors or colorize.COLORS
|
||
lines = '\n'.join(
|
||
reports.package_one_line(name, BY_NAME, PACKAGE_TASKS)
|
||
for name in sorted(BY_NAME)
|
||
if pred(name) and BY_NAME[name][0] in colors)
|
||
to(lines)
|
||
|
||
|
||
def gspi(pattern, colors=None, to=print):
|
||
p = re.compile(pattern)
|
||
_spi_by_predicate(p.search, colors, to)
|
||
|
||
|
||
def list_spi(pkgs, colors=None, to=print):
|
||
pset = frozenset(pkgs)
|
||
_spi_by_predicate(pset.__contains__, colors, to)
|
||
|
||
|
||
def _ti_str(task):
|
||
return tasks.format_task(
|
||
task,
|
||
extra_info=lambda p: colorize.package_color(p, BY_NAME) or 'NONE')
|
||
|
||
|
||
def ti(num, to=print):
|
||
to(_ti_str(get_task(num, TASKS)))
|
||
|
||
|
||
def next_tasks(include=None, exclude=None, to=pager):
|
||
tasks = reports.next_tasks(TASKS, BY_NAME, CURRENT['new'])
|
||
if include:
|
||
p = re.compile(include)
|
||
tasks = [t for t in tasks if p.search(t)]
|
||
if exclude:
|
||
p = re.compile(exclude)
|
||
tasks = [t for t in tasks if not p.search(t)]
|
||
to("%s tasks\n\n%s" % (len(tasks), '\n\n'.join(tasks)))
|
||
|
||
|
||
def fti(num, to=print):
|
||
to(utils.format_dict(get_task(num, TASKS), indent=True))
|
||
|
||
|
||
def logs(num, idx=-1, to=pager):
|
||
log_file = task_event_logs(num, TASKS)[idx]
|
||
with open(log_file, 'r') as f:
|
||
log = f.read()
|
||
to(log_file + ':\n\n' + log)
|
||
|
||
|
||
def stats(to=dump):
|
||
to('\n'.join('%10s:%6s' % (color, len(BY_COLOR[color]))
|
||
for color in colorize.COLORS))
|
||
|
||
|
||
def interactive_setup():
|
||
# Bind ‘TAB’ to complete
|
||
readline.parse_and_bind('tab:complete')
|
||
|
||
# Set history file – ~\.pythonhistory
|
||
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
|
||
|
||
# Attempt read of histfile
|
||
try:
|
||
readline.read_history_file(histfile)
|
||
except IOError:
|
||
pass
|
||
|
||
# Write history file at shell exit
|
||
atexit.register(readline.write_history_file, histfile)
|
||
|
||
# Configure logging
|
||
logging.basicConfig(
|
||
format='%(asctime)s %(levelname)-5s %(name)s - %(message)s',
|
||
datefmt='%Y-%m-%d %H:%M:%S',
|
||
stream=sys.stderr, level=logging.INFO)
|
||
|
||
config = sys.argv[1]
|
||
LOG.info("Loading configuraition file: %s", config)
|
||
with open(config, 'r') as f:
|
||
global CONFIG
|
||
CONFIG = json.load(f)
|
||
|
||
if __name__ == '__main__':
|
||
interactive_setup()
|
||
print(__doc__)
|