You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@steve.apache.org by ad...@apache.org on 2013/06/13 07:57:20 UTC

svn commit: r1492532 - in /steve/trunk: bin/ cmdline/make_issue.py cmdline/votegroup.py docs/make.bat lib/steve.py requirements.txt setup.py src/steve/

Author: adc
Date: Thu Jun 13 05:57:19 2013
New Revision: 1492532

URL: http://svn.apache.org/r1492532
Log:
Cleaned up per suggestions

Added:
    steve/trunk/cmdline/votegroup.py
      - copied, changed from r1492040, steve/trunk/bin/votegroup
Removed:
    steve/trunk/bin/
    steve/trunk/docs/make.bat
    steve/trunk/src/steve/
Modified:
    steve/trunk/cmdline/make_issue.py
    steve/trunk/lib/steve.py
    steve/trunk/requirements.txt
    steve/trunk/setup.py

Modified: steve/trunk/cmdline/make_issue.py
URL: http://svn.apache.org/viewvc/steve/trunk/cmdline/make_issue.py?rev=1492532&r1=1492531&r2=1492532&view=diff
==============================================================================
--- steve/trunk/cmdline/make_issue.py (original)
+++ steve/trunk/cmdline/make_issue.py Thu Jun 13 05:57:19 2013
@@ -115,18 +115,18 @@ def augment_args(args, config):
   if args.group is None:
     args.group = steve.get_input_line('group name for voters on this issue', True)
   if not re.match(r'^\w+$', args.group):
-    die('group name must be an alphanumeric token')
+    steve.die('group name must be an alphanumeric token')
 
   if args.start is None:
     args.start = steve.get_input_line('YYYYMMDD date that voting starts', True)
   if not re.match(r'^[2-9]\d\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$',
                   args.start):
-    die('start date must be formatted as YYYYMMDD, like 20090930')
+    steve.die('start date must be formatted as YYYYMMDD, like 20090930')
 
   if args.issue is None:
     args.issue = steve.get_input_line('short issue name to append to date', True)
   if not re.match(r'^\w+$', args.issue):
-    die('issue name must be an alphanumeric token')
+    steve.die('issue name must be an alphanumeric token')
 
   if args.file is None:
     args.file = steve.get_input_line('file pathname of issue info on %s'
@@ -134,17 +134,17 @@ def augment_args(args, config):
                                      True)
   args.file = os.path.realpath(args.file)
   if not os.path.exists(args.file):
-    die('info file does not exist: %s', args.file)
+    steve.die('info file does not exist: %s', args.file)
   if args.file.startswith('/etc/'):
-    die('forbidden to read info files from: /etc')
+    steve.die('forbidden to read info files from: /etc')
   issue_dir = os.path.realpath(config.issue_dir)
   if args.file.startswith(issue_dir + '/'):
-    die('forbidden to read info files from: %s', issue_dir)
+    steve.die('forbidden to read info files from: %s', issue_dir)
 
   if args.monitors is None:
     args.monitors = steve.get_input_line('e-mail address(es) for vote monitors', True)
   if '@' not in args.monitors:
-    die('vote monitor must be an Internet e-mail address')
+    steve.die('vote monitor must be an Internet e-mail address')
 
   if args.votetype is None:
     args.votetype = steve.get_input_line('vote type; yna, stvN, or selectN (N=1-9)',
@@ -160,25 +160,25 @@ def augment_args(args, config):
     args.selector = int(args.votetype[6:])
     args.style = 'select %d of the candidates labeled [a-z0-9]' % (args.selector,)
   else:
-    die('vote type must be yna, stvN, or selectN (N=[1-9])')
+    steve.die('vote type must be yna, stvN, or selectN (N=[1-9])')
 
 
 def get_voters(args, config):
   if not os.path.isdir(config.issue_dir):
-    die('cannot find: %s', config.issue_dir)
+    steve.die('cannot find: %s', config.issue_dir)
 
   config.issue_dir += '/' + args.group
   if not os.path.isdir(config.issue_dir):
-    die('group "%s" has not been created yet, see votegroup', args.group)
+    steve.die('group "%s" has not been created yet, see votegroup', args.group)
   if not os.access(config.issue_dir, os.R_OK | os.W_OK | os.X_OK):
-    die('you lack permissions on: %s', config.issue_dir)
+    steve.die('you lack permissions on: %s', config.issue_dir)
   uid = os.stat(config.issue_dir).st_uid
   if uid != os.geteuid():
-    die('you are not the effective owner of: %s', config.issue_dir)
+    steve.die('you are not the effective owner of: %s', config.issue_dir)
 
   voters = steve.get_group(config.issue_dir + '/voters')
   if not voters:
-    die('"%s" must be an existing voter group: see votegroup', args.group)
+    steve.die('"%s" must be an existing voter group: see votegroup', args.group)
 
   return voters
 
@@ -189,7 +189,7 @@ def create_issue_dir(issue_name, args, c
   # Note that .issue_dir already has the group.
   config.issue_dir += '/%s-%s' % (args.start, args.issue)
   if os.path.exists(config.issue_dir):
-    die('already exists: %s', config.issue_dir)
+    steve.die('already exists: %s', config.issue_dir)
   os.mkdir(config.issue_dir, 0700)
 
 
@@ -328,15 +328,6 @@ def _use_template(template_fname, key, i
   return buf.getvalue()
 
 
-### keep this? not sure that exceptions would be helpful since there is likely
-### no intent to trap them.
-def die(msg, *args):
-  "Print an error message and exit with failure."
-
-  print '%s: %s' % (steve.PROG, msg % args)
-  sys.exit(1)
-
-
 if __name__ == '__main__':
   os.umask(0077)
   main()

Copied: steve/trunk/cmdline/votegroup.py (from r1492040, steve/trunk/bin/votegroup)
URL: http://svn.apache.org/viewvc/steve/trunk/cmdline/votegroup.py?p2=steve/trunk/cmdline/votegroup.py&p1=steve/trunk/bin/votegroup&r1=1492040&r2=1492532&rev=1492532&view=diff
==============================================================================
--- steve/trunk/bin/votegroup (original)
+++ steve/trunk/cmdline/votegroup.py Thu Jun 13 05:57:19 2013
@@ -17,51 +17,133 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-"""
-A tool for creating a list of voters in the given issue group.
-"""
+# votegroup
+# A program for creating a list of voters in the given issue group
+#
+# o  must be run by voter user (see wrapsuid.c for setuid wrapper)
+#
+# o  creates a group directory (/home/voter/issues/group/),
+#    and adds to it a "voters" file containing a list of e-mail addresses.
+#
+# Originally created by Roy Fielding
+#
+from collections import Counter
 import os
+import re
+import subprocess
 import sys
+import argparse
+
+import steve
+
+
+def accept_or_exit():
+  while True:
+    answer = steve.get_input_line('"ok" to accept, or "abort" to exit', False)
+    answer = answer.lower()
+    if answer == 'abort':
+      sys.exit(1)
+    if answer == 'ok':
+      return
+
+
+def main():
+  args = parse_argv()
+
+  # Expand the set of arguments (as a side-effect), if they were not provided
+  # on the cmdline.
+  augment_args(args)
+
+  voters = steve.get_group(args.file)
+  if not voters:
+    steve.die('No valid e-mail addresses were found in %s' % (args.file,))
+
+  print 'Here is the list of voter e-mail addresses:'
+  print '=============================================================='
+  for voter in voters:
+    print voter
+  print '=============================================================='
+
+  duplicates = len(voters) - len(set(voters))
+  if duplicates:
+    print 'Found duplicates:\n  %s' % '\n  '.join([x for x, y in Counter(voters).items() if y > 1])
+    steve.die('%s duplicate voter%s must be removed from the list' % (duplicates, 's' if duplicates > 1 else ''))
+
+  if not args.batch:
+    accept_or_exit()
+
+  diff_voters_file(args)
+
+  create_voters_file(args)
+
+
+def parse_argv():
+  parser = argparse.ArgumentParser(
+    prog=steve.PROG,
+    description='Make an issue for managing an on-line, '
+                'anonymous voting process',
+  )
+  parser.add_argument('-b', '--batch', action='store_true',
+                      help='batch processing: assume "ok" unless errors')
+  parser.add_argument('-g', '--group',
+                      help='create an issue for an existing group of voters')
+  parser.add_argument('-f', '--file',
+                      help='contains the list of voter e-mail addresses, one per line')
+
+  return parser.parse_args()
+
+
+def augment_args(args):
+  "Update ARGS in-place with missing values."
+
+  if args.group is None:
+    args.group = steve.get_input_line('group name for voters on this issue', True)
+  if not re.match(r'^\w+$', args.group):
+    steve.die('group name must be an alphanumeric token')
+
+  if args.file is None:
+    args.file = steve.get_input_line('file pathname voter e-mail addresses', True)
+  args.file = os.path.realpath(args.file)
+  if not os.path.exists(args.file):
+    steve.die('info file does not exist: %s', args.file)
+  if args.file.startswith('/etc/'):
+    steve.die('forbidden to read info files from: /etc')
+  if args.file.startswith(steve.ISSUE_DIR + os.path.sep):
+    steve.die('forbidden to read info files from: %s', steve.ISSUE_DIR)
+
+
+def _run_cmd(cmd, cwd=None):
+  process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  (stdout, stderr) = process.communicate()
+
+  return process.returncode, stdout + stderr
+
+
+def diff_voters_file(args):
+  VOTERS_FILE = os.path.join(steve.ISSUE_DIR, 'voters')
+  VOTERS_FILE = os.path.join('.', 'voters')
+  if os.path.exists(VOTERS_FILE):
+    _, stdout = _run_cmd(['diff', '-u', VOTERS_FILE, args.file])
+
+    print 'Differences from existing list of voters:'
+    print '=============================================================='
+    print stdout
+    print '=============================================================='
+
+    if not args.batch:
+      accept_or_exit()
+
+    _run_cmd(['mv', '-f', VOTERS_FILE, VOTERS_FILE + os.path.extsep + 'old'])
+
 
-from asf.cli import entrypoint, prompt_yes_no
-from brownie.datastructures import Counter
+def create_voters_file(args):
+  VOTERS_FILE = os.path.join(steve.ISSUE_DIR, 'voters')
+  VOTERS_FILE = os.path.join('.', 'voters')
 
-from steve.voters import get_group, hash_file
+  _run_cmd(['cp', args.file, VOTERS_FILE])
 
+  print '\n\n%s: %s' % (steve.hash_file(VOTERS_FILE), VOTERS_FILE)
 
-@entrypoint
-def main(cli):
-    cli.add_argument('group', help='group name for the voters')
-    cli.add_argument('file', help='File containing the list of voter e-mail addresses, one per line')
-    cli.add_argument('--non-interactive', action='store_true', dest='no_user_prompt', help='Disables interactive prompting. Assumes \'ok\' unless there are errors.')
-
-    cli.register_exception(ValueError, cli.log.error)
-
-    with cli.run():
-        if not cli.args.group.isalnum():
-            raise ValueError('Group name must be an alphanumeric token')
-        if not os.path.exists(cli.args.file):
-            raise ValueError('Voters list %s does not exist' % (cli.args.file,))
-        if cli.args.file.startswith('/etc/'):
-            raise ValueError('Must not use files in the /etc directory')
-
-        voters = get_group(cli.args.file)
-        if not voters:
-            raise ValueError('No valid e-mail addresses were found in %s' % (cli.args.file,))
-
-        print 'Here is the list of voter e-mail addresses:'
-        print '=============================================================='
-        for voter in voters:
-            print voter
-        print '=============================================================='
-
-        duplicates = len(voters) - len(set(voters))
-        if duplicates:
-            print 'Found duplicates:\n  %s' % '\n  '.join([x for x, y in Counter(voters).items() if y > 1])
-            raise ValueError('%s duplicate voters must be removed from the list' % (duplicates,))
-
-        if not cli.args.no_user_prompt:
-            if not prompt_yes_no('Proceed?', default=True):
-                sys.exit(1)
 
-        print '\n\n%s: %s' % (hash_file(cli.args.file), cli.args.file)
+if __name__ == '__main__':
+  main()

Modified: steve/trunk/lib/steve.py
URL: http://svn.apache.org/viewvc/steve/trunk/lib/steve.py?rev=1492532&r1=1492531&r2=1492532&view=diff
==============================================================================
--- steve/trunk/lib/steve.py (original)
+++ steve/trunk/lib/steve.py Thu Jun 13 05:57:19 2013
@@ -26,6 +26,9 @@ import time
 import random
 import ConfigParser
 
+HOME_DIR = os.path.join('home', 'voter')
+ISSUE_DIR = os.path.join(HOME_DIR, 'issues')
+
 # Strip the .py extension, producing the setuid program name.
 PROG = os.path.splitext(os.path.basename(sys.argv[0]))[0]
 
@@ -45,11 +48,14 @@ def get_input_line(prompt, quittable=Fal
     # loop until we get an answer
 
 
-def get_group(fname):
-  "Return the group of voters, as a set of email addresses."
+def get_group(filename):
+  """ Return the group of voters, as a list of email addresses
+      :param str filename: name of file that contains the group of voter email addresses
+      :rtype list(str): list of voter email addresses
+  """
 
-  group = set()
-  for line in open(fname).readlines():
+  group = []
+  for line in open(filename).readlines():
     i = line.find('#')
     if i >= 0:
       line = line[:i]
@@ -58,7 +64,7 @@ def get_group(fname):
       continue
     if '@' not in line:
       raise ValueError('%s: voter must be an Internet e-mail address.' % (line,))
-    group.add(line)
+    group.append(line)
 
   return group
 
@@ -166,3 +172,10 @@ def load_config(fname):
     def __init__(self, items):
       vars(self).update(items)
   return _config(parser.items('general'))
+
+
+def die(msg, *args):
+  "Print an error message and exit with failure."
+
+  print '%s: %s' % (PROG, msg % args)
+  sys.exit(1)

Modified: steve/trunk/requirements.txt
URL: http://svn.apache.org/viewvc/steve/trunk/requirements.txt?rev=1492532&r1=1492531&r2=1492532&view=diff
==============================================================================
--- steve/trunk/requirements.txt (original)
+++ steve/trunk/requirements.txt Thu Jun 13 05:57:19 2013
@@ -1,4 +1 @@
-mock
 nose
-
-asf-incubator-tools

Modified: steve/trunk/setup.py
URL: http://svn.apache.org/viewvc/steve/trunk/setup.py?rev=1492532&r1=1492531&r2=1492532&view=diff
==============================================================================
--- steve/trunk/setup.py (original)
+++ steve/trunk/setup.py Thu Jun 13 05:57:19 2013
@@ -102,11 +102,11 @@ setup(
     # don't ever depend on refcounting to close files anywhere else
     long_description=open('README', encoding='utf-8').read(),
 
-    scripts=["bin/votegroup"],
+  #  scripts=["bin/votegroup"],
 
-    namespace_packages=['steve'],
-    package_dir={'': 'src'},
-    packages=find_packages('src'),
+  #  namespace_packages=['steve'],
+    package_dir={'': 'lib'},
+    packages=find_packages('lib'),
 
     zip_safe=False,
     platforms='any',