diff -r df2fdd42b375 Lib/random.py --- a/Lib/random.py Mon Aug 26 22:28:21 2013 +0200 +++ b/Lib/random.py Sun Sep 01 10:46:08 2013 -0400 @@ -43,6 +43,9 @@ from os import urandom as _urandom from collections.abc import Set as _Set, Sequence as _Sequence from hashlib import sha512 as _sha512 +from itertools import accumulate as _accumulate +from bisect import bisect as _bisect +from functools import lru_cache __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", @@ -246,14 +249,52 @@ ## -------------------- sequence methods ------------------- - def choice(self, seq): - """Choose a random element from a non-empty sequence.""" - try: - i = self._randbelow(len(seq)) - except ValueError: - raise IndexError('Cannot choose from an empty sequence') - return seq[i] + @lru_cache(maxsize=4, typed=False) + def _weighted_choice_gen(self, weights): + # helper function for choice + def _add(cumulative, partial): + try: + partial = float(partial) + except ValueError: + raise TypeError("All weights must be numeric") + if partial < 0: + raise ValueError("All weights must be non-negative") + return cumulative + partial + + cumulative_dist = list(_accumulate(weights, _add)) + + if not any(weights): + raise ValueError("At least one weight must be greater than zero") + + while True: + x = self.random() * cumulative_dist[-1] + i = _bisect(cumulative_dist, x) + yield i + + def choice(self, seq, weights=None): + """Choose a random element from a non-empty sequence. + + Optional argument weights is a sequence of non-negative numbers used to + alters the probability that element at index i in the sequence is + selected. + """ + + if not weights: + try: + i = self._randbelow(len(seq)) + except ValueError: + raise IndexError('Cannot choose from an empty sequence') + return seq[i] + + # #weights argument supplied + + if len(weights) != len(seq): + raise ValueError("Length of weights must equal length of sequence") + + choice_gen = self._weighted_choice_gen(tuple(weights)) + return seq[next(choice_gen)] + def shuffle(self, x, random=None, int=int): """Shuffle list x in place, and return None. diff -r df2fdd42b375 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Mon Aug 26 22:28:21 2013 +0200 +++ b/Lib/test/test_argparse.py Sun Sep 01 10:46:08 2013 -0400 @@ -4464,6 +4464,25 @@ "add_help=True)" % argparse.HelpFormatter) self.assertStringEqual(parser, string) + def test_positional(self): + _get_positionals = mock.MagicMock(return_value=(['--foo', '-a', '-b'], 'b')) + with mock.patch('argparse.Action._get_args', _get_positionals): + option = argparse.Action( + ['--foo', '-a', '-b'], + 'b', + type='int', + nargs='+', + default=42, + choices=[1, 2, 3], + help='HELP', + metavar='METAVAR') + string = ( + "Action(['--foo', '-a', '-b'], 'b', " + "option_strings=['--foo', '-a', '-b'], dest='b', " + "nargs='+', const=None, default=42, type='int', " + "choices=[1, 2, 3], help='HELP', metavar='METAVAR')") + self.assertStringEqual(option, string) + # =============== # Namespace tests # =============== diff -r df2fdd42b375 Lib/test/test_random.py --- a/Lib/test/test_random.py Mon Aug 26 22:28:21 2013 +0200 +++ b/Lib/test/test_random.py Sun Sep 01 10:46:08 2013 -0400 @@ -96,6 +96,11 @@ choice([]) self.assertEqual(choice([50]), 50) self.assertIn(choice([25, 75]), [25, 75]) + self.assertIn(choice([25, 75], [.25, .75]), [25, 75]) + self.assertRaises(ValueError, choice, [25, 75], [.25]) + self.assertRaises(ValueError, choice, [25, 75], [-1, -2]) + self.assertRaises(ValueError, choice, [25, 75], [0, 0]) + self.assertRaises(TypeError, choice, [25, 75], ['str1', 'str2']) def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that diff -r df2fdd42b375 Lib/test/test_re.py --- a/Lib/test/test_re.py Mon Aug 26 22:28:21 2013 +0200 +++ b/Lib/test/test_re.py Sun Sep 01 10:46:08 2013 -0400 @@ -4,6 +4,7 @@ import re from re import Scanner import sre_constants +import sre_compile import sys import string import traceback @@ -19,6 +20,19 @@ class ReTests(unittest.TestCase): + def assertAllEqual(self, *args): + for arg in args[1:]: + self.assertEqual(arg, args[0]) + + def test_re_aliases(self): + self.assertAllEqual(re.A, re.ASCII, sre_compile.SRE_FLAG_ASCII) + self.assertAllEqual(re.I, re.IGNORECASE, sre_compile.SRE_FLAG_IGNORECASE) + self.assertAllEqual(re.L, re.LOCALE, sre_compile.SRE_FLAG_LOCALE) + self.assertAllEqual(re.U, re.UNICODE, sre_compile.SRE_FLAG_UNICODE) + self.assertAllEqual(re.M, re.MULTILINE, sre_compile.SRE_FLAG_MULTILINE) + self.assertAllEqual(re.S, re.DOTALL, sre_compile.SRE_FLAG_DOTALL) + self.assertAllEqual(re.X, re.VERBOSE, sre_compile.SRE_FLAG_VERBOSE) + def test_keep_buffer(self): # See bug 14212 b = bytearray(b'x')