Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/tools/dev/contribulyze.py  view on Meta::CPAN

except AttributeError:
  my_getopt = getopt.getopt
try:
  # Python >=3.0
  from urllib.parse import quote as urllib_parse_quote
except ImportError:
  # Python <3.0
  from urllib import quote as urllib_parse_quote


# Warnings and errors start with these strings.  They are typically
# followed by a colon and a space, as in "%s: " ==> "WARNING: ".
warning_prefix = 'WARNING'
error_prefix = 'ERROR'

def complain(msg, fatal=False):
  """Print MSG as a warning, or if FATAL is true, print it as an error
  and exit."""
  prefix = 'WARNING: '
  if fatal:
    prefix = 'ERROR: '
  sys.stderr.write(prefix + msg + '\n')
  if fatal:
    sys.exit(1)


def html_spam_guard(addr, entities_only=False):
  """Return a spam-protected version of email ADDR that renders the
  same in HTML as the original address.  If ENTITIES_ONLY, use a less
  thorough mangling scheme involving entities only, avoiding the use
  of tags."""
  if entities_only:
    def mangle(x):
      return "&#%d;" % ord (x)
  else:
    def mangle(x):
      return "<span>&#%d;</span>" % ord(x)
  return "".join(map(mangle, addr))


def escape_html(str):
  """Return an HTML-escaped version of STR."""
  return str.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')


_spam_guard_in_html_block_re = re.compile(r'&lt;([^&]*@[^&]*)&gt;')
def _spam_guard_in_html_block_func(m):
  return "&lt;%s&gt;" % html_spam_guard(m.group(1))
def spam_guard_in_html_block(str):
  """Take a block of HTML data, and run html_spam_guard() on parts of it."""
  return _spam_guard_in_html_block_re.subn(_spam_guard_in_html_block_func,
                                           str)[0]

def html_header(title, page_heading=None, highlight_targets=False):
  """Write HTML file header.  TITLE and PAGE_HEADING parameters are
  expected to already by HTML-escaped if needed.  If HIGHLIGHT_TARGETS
is true, then write out a style header that causes anchor targets to be
surrounded by a red border when they are jumped to."""
  if not page_heading:
    page_heading = title
  s  = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n'
  s += ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
  s += '<html><head>\n'
  s += '<meta http-equiv="Content-Type"'
  s += ' content="text/html; charset=UTF-8" />\n'
  if highlight_targets:
    s += '<style type="text/css">\n'
    s += ':target { border: 2px solid red; }\n'
    s += '</style>\n'
  s += '<title>%s</title>\n' % title
  s += '</head>\n\n'
  s += '<body style="text-color: black; background-color: white">\n\n'
  s += '<h1 style="text-align: center">%s</h1>\n\n' % page_heading
  s += '<hr />\n\n'
  return s


def html_footer():
  return '\n</body>\n</html>\n'


class Contributor(object):
  # Map contributor names to contributor instances, so that there
  # exists exactly one instance associated with a given name.
  # Fold names with email addresses.  That is, if we see someone
  # listed first with just an email address, but later with a real
  # name and that same email address together, we create only one
  # instance, and store it under both the email and the real name.
  all_contributors = { }

  def __init__(self, username, real_name, email):
    """Instantiate a contributor.  Don't use this to generate a
    Contributor for an external caller, though, use .get() instead."""
    self.real_name = real_name
    self.username  = username
    self.email     = email
    self.is_committer = False       # Assume not until hear otherwise.
    self.is_full_committer = False  # Assume not until hear otherwise.
    # Map verbs (e.g., "Patch", "Suggested", "Review") to lists of
    # LogMessage objects.  For example, the log messages stored under
    # "Patch" represent all the revisions for which this contributor
    # contributed a patch.
    self.activities = { }

  def add_activity(self, field_name, log):
    """Record that this contributor was active in FIELD_NAME in LOG."""
    logs = self.activities.get(field_name)
    if not logs:
      logs = [ ]
      self.activities[field_name] = logs
    if not log in logs:
      logs.append(log)

  @staticmethod
  def get(username, real_name, email):
    """If this contributor is already registered, just return it;
    otherwise, register it then return it.  Hint: use parse() to
    generate the arguments."""
    c = None
    for key in username, real_name, email:
      if key and key in Contributor.all_contributors:



( run in 0.551 second using v1.01-cache-2.11-cpan-119454b85a5 )