aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pym/repoman/utilities.py')
-rw-r--r--pym/repoman/utilities.py146
1 files changed, 112 insertions, 34 deletions
diff --git a/pym/repoman/utilities.py b/pym/repoman/utilities.py
index 013858a6d..aec61fe2f 100644
--- a/pym/repoman/utilities.py
+++ b/pym/repoman/utilities.py
@@ -1,11 +1,11 @@
# repoman: Utilities
-# Copyright 2007-2012 Gentoo Foundation
+# Copyright 2007-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
"""This module contains utility functions to help repoman find ebuilds to
scan"""
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
__all__ = [
"detect_vcs_conflicts",
@@ -25,6 +25,7 @@ __all__ = [
"UpdateChangeLog"
]
+import collections
import errno
import io
from itertools import chain
@@ -33,18 +34,20 @@ import pwd
import re
import stat
import sys
+import subprocess
import time
import textwrap
import difflib
from tempfile import mkstemp
+import portage
from portage import os
from portage import shutil
-from portage import subprocess_getstatusoutput
from portage import _encodings
from portage import _unicode_decode
from portage import _unicode_encode
from portage import output
+from portage.const import BASH_BINARY
from portage.localization import _
from portage.output import red, green
from portage.process import find_binary
@@ -71,22 +74,31 @@ def detect_vcs_conflicts(options, vcs):
Returns:
None (calls sys.exit on fatal problems)
"""
- retval = ("","")
+
+ cmd = None
if vcs == 'cvs':
logging.info("Performing a " + output.green("cvs -n up") + \
" with a little magic grep to check for updates.")
- retval = subprocess_getstatusoutput("cvs -n up 2>/dev/null | " + \
+ cmd = "cvs -n up 2>/dev/null | " + \
"egrep '^[^\?] .*' | " + \
- "egrep -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'")
+ "egrep -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'"
if vcs == 'svn':
logging.info("Performing a " + output.green("svn status -u") + \
" with a little magic grep to check for updates.")
- retval = subprocess_getstatusoutput("svn status -u 2>&1 | " + \
+ cmd = "svn status -u 2>&1 | " + \
"egrep -v '^. +.*/digest-[^/]+' | " + \
- "head -n-1")
-
- if vcs in ['cvs', 'svn']:
- mylines = retval[1].splitlines()
+ "head -n-1"
+
+ if cmd is not None:
+ # Use Popen instead of getstatusoutput(), in order to avoid
+ # unicode handling problems (see bug #310789).
+ args = [BASH_BINARY, "-c", cmd]
+ args = [_unicode_encode(x) for x in args]
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out = _unicode_decode(proc.communicate()[0])
+ proc.wait()
+ mylines = out.splitlines()
myupdates = []
for line in mylines:
if not line:
@@ -98,7 +110,7 @@ def detect_vcs_conflicts(options, vcs):
logging.error(red("!!! Please fix the following issues reported " + \
"from cvs: ")+green("(U,P,M,A,R,D are ok)"))
logging.error(red("!!! Note: This is a pretend/no-modify pass..."))
- logging.error(retval[1])
+ logging.error(out)
sys.exit(1)
elif vcs == 'cvs' and line[0] in "UP":
myupdates.append(line[2:])
@@ -298,12 +310,12 @@ def format_qa_output(formatter, stats, fails, dofull, dofail, options, qawarning
# we only want key value pairs where value > 0
for category, number in \
filter(lambda myitem: myitem[1] > 0, iter(stats.items())):
- formatter.add_literal_data(_unicode_decode(" " + category.ljust(30)))
+ formatter.add_literal_data(" " + category.ljust(30))
if category in qawarnings:
formatter.push_style("WARN")
else:
formatter.push_style("BAD")
- formatter.add_literal_data(_unicode_decode(str(number)))
+ formatter.add_literal_data("%s" % number)
formatter.pop_style()
formatter.add_line_break()
if not dofull:
@@ -314,10 +326,54 @@ def format_qa_output(formatter, stats, fails, dofull, dofail, options, qawarning
if not full and len(fails_list) > 12:
fails_list = fails_list[:12]
for failure in fails_list:
- formatter.add_literal_data(_unicode_decode(" " + failure))
+ formatter.add_literal_data(" " + failure)
formatter.add_line_break()
+def format_qa_output_column(formatter, stats, fails, dofull, dofail, options, qawarnings):
+ """Helper function that formats output in a machine-parseable column format
+
+ @param formatter: an instance of Formatter
+ @type formatter: Formatter
+ @param path: dict of qa status items
+ @type path: dict
+ @param fails: dict of qa status failures
+ @type fails: dict
+ @param dofull: Whether to print full results or a summary
+ @type dofull: boolean
+ @param dofail: Whether failure was hard or soft
+ @type dofail: boolean
+ @param options: The command-line options provided to repoman
+ @type options: Namespace
+ @param qawarnings: the set of warning types
+ @type qawarnings: set
+ @return: None (modifies formatter)
+ """
+ full = options.mode == 'full'
+ for category, number in stats.items():
+ # we only want key value pairs where value > 0
+ if number < 1:
+ continue
+
+ formatter.add_literal_data("NumberOf " + category + " ")
+ if category in qawarnings:
+ formatter.push_style("WARN")
+ else:
+ formatter.push_style("BAD")
+ formatter.add_literal_data("%s" % number)
+ formatter.pop_style()
+ formatter.add_line_break()
+ if not dofull:
+ if not full and dofail and category in qawarnings:
+ # warnings are considered noise when there are failures
+ continue
+ fails_list = fails[category]
+ if not full and len(fails_list) > 12:
+ fails_list = fails_list[:12]
+ for failure in fails_list:
+ formatter.add_literal_data(category + " " + failure)
+ formatter.add_line_break()
+
def editor_is_executable(editor):
"""
Given an EDITOR string, validate that it refers to
@@ -367,10 +423,11 @@ def get_commit_message_with_editor(editor, message=None):
if not (os.WIFEXITED(retval) and os.WEXITSTATUS(retval) == os.EX_OK):
return None
try:
- mylines = io.open(_unicode_encode(filename,
+ with io.open(_unicode_encode(filename,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'], errors='replace'
- ).readlines()
+ ) as f:
+ mylines = f.readlines()
except OSError as e:
if e.errno != errno.ENOENT:
raise
@@ -427,7 +484,7 @@ def FindPortdir(settings):
portdir = None
portdir_overlay = None
location = os.getcwd()
- pwd = os.environ.get('PWD', '')
+ pwd = _unicode_decode(os.environ.get('PWD', ''), encoding=_encodings['fs'])
if pwd and pwd != location and os.path.realpath(pwd) == location:
# getcwd() returns the canonical path but that makes it hard for repoman to
# orient itself if the user has symlinks in their portage tree structure.
@@ -449,7 +506,7 @@ def FindPortdir(settings):
if location[-1] != "/":
location += "/"
- for overlay in settings["PORTDIR_OVERLAY"].split():
+ for overlay in portage.util.shlex_split(settings["PORTDIR_OVERLAY"]):
overlay = os.path.realpath(overlay)
try:
s = os.stat(overlay)
@@ -509,6 +566,28 @@ def FindPortdir(settings):
return [normalize_path(x) for x in (portdir, portdir_overlay, location)]
+_vcs_type = collections.namedtuple('_vcs_type',
+ 'name dir_name')
+
+_FindVCS_data = (
+ _vcs_type(
+ name = 'git',
+ dir_name = '.git'
+ ),
+ _vcs_type(
+ name = 'bzr',
+ dir_name = '.bzr'
+ ),
+ _vcs_type(
+ name = 'hg',
+ dir_name = '.hg'
+ ),
+ _vcs_type(
+ name = 'svn',
+ dir_name = '.svn'
+ )
+)
+
def FindVCS():
""" Try to figure out in what VCS' working tree we are. """
@@ -520,14 +599,13 @@ def FindVCS():
pathprep = ''
while depth is None or depth > 0:
- if os.path.isdir(os.path.join(pathprep, '.git')):
- retvcs.append('git')
- if os.path.isdir(os.path.join(pathprep, '.bzr')):
- retvcs.append('bzr')
- if os.path.isdir(os.path.join(pathprep, '.hg')):
- retvcs.append('hg')
- if os.path.isdir(os.path.join(pathprep, '.svn')): # >=1.7
- retvcs.append('svn')
+ for vcs_type in _FindVCS_data:
+ vcs_dir = os.path.join(pathprep, vcs_type.dir_name)
+ if os.path.isdir(vcs_dir):
+ logging.debug('FindVCS: found %(name)s dir: %(vcs_dir)s' %
+ {'name': vcs_type.name,
+ 'vcs_dir': os.path.abspath(vcs_dir)})
+ retvcs.append(vcs_type.name)
if retvcs:
break
@@ -763,7 +841,7 @@ def UpdateChangeLog(pkgdir, user, msg, skel_path, category, package,
line = line.replace('<PACKAGE_NAME>', package)
line = _update_copyright_year(year, line)
header_lines.append(line)
- header_lines.append(_unicode_decode('\n'))
+ header_lines.append('\n')
clskel_file.close()
# write new ChangeLog entry
@@ -773,10 +851,10 @@ def UpdateChangeLog(pkgdir, user, msg, skel_path, category, package,
if not fn.endswith('.ebuild'):
continue
ebuild = fn.split(os.sep)[-1][0:-7]
- clnew_lines.append(_unicode_decode('*%s (%s)\n' % (ebuild, date)))
+ clnew_lines.append('*%s (%s)\n' % (ebuild, date))
newebuild = True
if newebuild:
- clnew_lines.append(_unicode_decode('\n'))
+ clnew_lines.append('\n')
trivial_files = ('ChangeLog', 'Manifest')
display_new = ['+' + elem for elem in new
if elem not in trivial_files]
@@ -803,19 +881,19 @@ def UpdateChangeLog(pkgdir, user, msg, skel_path, category, package,
for line in textwrap.wrap(mesg, 80, \
initial_indent=' ', subsequent_indent=' ', \
break_on_hyphens=False):
- clnew_lines.append(_unicode_decode('%s\n' % line))
+ clnew_lines.append('%s\n' % line)
for line in textwrap.wrap(msg, 80, \
initial_indent=' ', subsequent_indent=' '):
- clnew_lines.append(_unicode_decode('%s\n' % line))
+ clnew_lines.append('%s\n' % line)
# Don't append a trailing newline if the file is new.
if clold_file is not None:
- clnew_lines.append(_unicode_decode('\n'))
+ clnew_lines.append('\n')
f = io.open(f, mode='w', encoding=_encodings['repo.content'],
errors='backslashreplace')
for line in clnew_lines:
- f.write(_unicode_decode(line))
+ f.write(line)
# append stuff from old ChangeLog
if clold_file is not None: