diff options
-rw-r--r-- | Doc/howto/advocacy.rst | 2 | ||||
-rw-r--r-- | Doc/howto/curses.rst | 2 | ||||
-rw-r--r-- | Doc/howto/regex.rst | 2 | ||||
-rw-r--r-- | Doc/howto/unicode.rst | 2 | ||||
-rw-r--r-- | Doc/library/curses.rst | 4 | ||||
-rw-r--r-- | Doc/library/re.rst | 3 | ||||
-rw-r--r-- | Lib/DocXMLRPCServer.py | 4 | ||||
-rw-r--r-- | Lib/_abcoll.py | 1 | ||||
-rw-r--r-- | Lib/distutils/sysconfig.py | 8 | ||||
-rwxr-xr-x | Lib/pydoc.py | 2 | ||||
-rw-r--r-- | Lib/re.py | 2 | ||||
-rw-r--r-- | Lib/tarfile.py | 18 | ||||
-rw-r--r-- | Lib/test/output/test_profile | 97 | ||||
-rw-r--r-- | Lib/test/profilee.py | 115 | ||||
-rw-r--r-- | Lib/test/test_docxmlrpc.py | 6 | ||||
-rwxr-xr-x[-rw-r--r--] | Lib/test/test_profile.py | 284 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 9 | ||||
-rw-r--r-- | Lib/test/test_trace.py | 61 | ||||
-rw-r--r-- | Lib/test/test_wave.py | 61 | ||||
-rw-r--r-- | Misc/python-mode.el | 544 | ||||
-rw-r--r-- | Objects/classobject.c | 24 | ||||
-rw-r--r-- | Objects/dictobject.c | 16 | ||||
-rw-r--r-- | Objects/frameobject.c | 7 | ||||
-rw-r--r-- | Objects/listobject.c | 24 | ||||
-rw-r--r-- | Objects/methodobject.c | 22 | ||||
-rw-r--r-- | Objects/setobject.c | 22 | ||||
-rw-r--r-- | Objects/tupleobject.c | 58 | ||||
-rw-r--r-- | Objects/unicodeobject.c | 36 | ||||
-rw-r--r-- | Python/compile.c | 28 |
29 files changed, 905 insertions, 559 deletions
diff --git a/Doc/howto/advocacy.rst b/Doc/howto/advocacy.rst index 7d7706e..669ce72 100644 --- a/Doc/howto/advocacy.rst +++ b/Doc/howto/advocacy.rst @@ -265,7 +265,7 @@ the organizations that use Python. **What are the restrictions on Python's use?** They're practically nonexistent. Consult the :file:`Misc/COPYRIGHT` file in the -source distribution, or http://www.python.org/doc/Copyright.html for the full +source distribution, or the section :ref:`history-and-license` for the full language, but it boils down to three conditions. * You have to leave the copyright notice on the software; if you don't include diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst index 12fb936..841a030 100644 --- a/Doc/howto/curses.rst +++ b/Doc/howto/curses.rst @@ -1,3 +1,5 @@ +.. _curses-howto: + ********************************** Curses Programming with Python ********************************** diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 794c945..406ce1c 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -1,3 +1,5 @@ +.. _regex-howto: + **************************** Regular Expression HOWTO **************************** diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index 40c77d6..67aa2b2 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -276,7 +276,7 @@ Unicode result). The following examples show the differences:: Encodings are specified as strings containing the encoding's name. Python comes with roughly 100 different encodings; see the Python Library Reference at -<http://docs.python.org/lib/standard-encodings.html> for a list. Some encodings +:ref:`standard-encodings` for a list. Some encodings have multiple names; for example, 'latin-1', 'iso_8859_1' and '8859' are all synonyms for the same encoding. diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 7f82bca..4135182 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -45,9 +45,9 @@ Linux and the BSD variants of Unix. Convenience function to ensure proper terminal setup and resetting on application entry and exit. - `Curses Programming with Python <http://www.python.org/doc/howto/curses/curses.html>`_ + :ref:`curses-howto` Tutorial material on using curses with Python, by Andrew Kuchling and Eric - Raymond, is available on the Python Web site. + Raymond. The :file:`Demo/curses/` directory in the Python source distribution contains some example programs using the curses bindings provided by this module. diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 5c2935b..0c64c72 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -65,8 +65,7 @@ and implementation of regular expressions, consult the Friedl book referenced above, or almost any textbook about compiler construction. A brief explanation of the format of regular expressions follows. For further -information and a gentler presentation, consult the Regular Expression HOWTO, -accessible from http://www.python.org/doc/howto/. +information and a gentler presentation, consult the :ref:`regex-howto`. Regular expressions can contain both special and ordinary characters. Most ordinary characters, like ``'A'``, ``'a'``, or ``'0'``, are the simplest regular diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py index 504b669..246fd74 100644 --- a/Lib/DocXMLRPCServer.py +++ b/Lib/DocXMLRPCServer.py @@ -30,7 +30,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): results = [] here = 0 - # XXX Note that this regular expressions does not allow for the + # XXX Note that this regular expression does not allow for the # hyperlinking of arbitrary strings being used as method # names. Only methods with names consisting of word characters # and '.'s are hyperlinked. @@ -52,7 +52,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) results.append('<a href="%s">%s</a>' % (url, escape(all))) elif pep: - url = 'http://www.python.org/peps/pep-%04d.html' % int(pep) + url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('<a href="%s">%s</a>' % (url, escape(all))) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) diff --git a/Lib/_abcoll.py b/Lib/_abcoll.py index ba084aa..90a78cb 100644 --- a/Lib/_abcoll.py +++ b/Lib/_abcoll.py @@ -385,6 +385,7 @@ class Mapping(metaclass=ABCMeta): def __ne__(self, other): return not (self == other) + class MappingView(metaclass=ABCMeta): def __init__(self, mapping): diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 4d790cc..39216a5 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -37,8 +37,12 @@ if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): # different (hard-wired) directories. # Setup.local is available for Makefile builds including VPATH builds, # Setup.dist is available on Windows -python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) - for fn in ("Setup.dist", "Setup.local")) +def _python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(project_base, "Modules", fn)): + return True + return False +python_build = _python_build() def get_python_version(): """Return a string containing the major and minor Python version, diff --git a/Lib/pydoc.py b/Lib/pydoc.py index de8d193..c6ec68e 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -536,7 +536,7 @@ class HTMLDoc(Doc): url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) results.append('<a href="%s">%s</a>' % (url, escape(all))) elif pep: - url = 'http://www.python.org/peps/pep-%04d' % int(pep) + url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('<a href="%s">%s</a>' % (url, escape(all))) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) @@ -294,8 +294,8 @@ class Scanner: p.append(sre_parse.SubPattern(s, [ (SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))), ])) + s.groups = len(p)+1 p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) - s.groups = len(p) self.scanner = sre_compile.compile(p) def scan(self, string): result = [] diff --git a/Lib/tarfile.py b/Lib/tarfile.py index b184ed8..b789cca 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2005,15 +2005,11 @@ class TarFile(object): for tarinfo in members: if tarinfo.isdir(): - # Extract directory with a safe mode, so that - # all files below can be extracted as well. - try: - os.makedirs(os.path.join(path, tarinfo.name), 0o700) - except EnvironmentError: - pass + # Extract directories with a safe mode. directories.append(tarinfo) - else: - self.extract(tarinfo, path) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + self.extract(tarinfo, path) # Reverse sort directories. directories.sort(key=lambda a: a.name) @@ -2118,6 +2114,8 @@ class TarFile(object): # Create all upper directories. upperdirs = os.path.dirname(targetpath) if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. os.makedirs(upperdirs) if tarinfo.islnk() or tarinfo.issym(): @@ -2154,7 +2152,9 @@ class TarFile(object): """Make a directory called targetpath. """ try: - os.mkdir(targetpath) + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) except EnvironmentError as e: if e.errno != errno.EEXIST: raise diff --git a/Lib/test/output/test_profile b/Lib/test/output/test_profile deleted file mode 100644 index 0582611..0000000 --- a/Lib/test/output/test_profile +++ /dev/null @@ -1,97 +0,0 @@ -test_profile - 125 function calls (105 primitive calls) in 1.000 CPU seconds - - Ordered by: standard name - - ncalls tottime percall cumtime percall filename:lineno(function) - 4 0.000 0.000 0.000 0.000 :0(append) - 4 0.000 0.000 0.000 0.000 :0(exc_info) - 1 0.000 0.000 1.000 1.000 :0(exec) - 12 0.000 0.000 0.012 0.001 :0(hasattr) - 1 0.000 0.000 0.000 0.000 :0(setprofile) - 1 0.000 0.000 1.000 1.000 <string>:1(<module>) - 2 0.000 0.000 0.000 0.000 io.py:1213(flush) - 1 0.000 0.000 0.000 0.000 io.py:269(flush) - 1 0.000 0.000 0.000 0.000 io.py:656(closed) - 1 0.000 0.000 0.000 0.000 io.py:874(flush) - 0 0.000 0.000 profile:0(profiler) - 1 0.000 0.000 1.000 1.000 profile:0(testfunc()) - 8 0.064 0.008 0.080 0.010 test_profile.py:103(subhelper) - 28 0.028 0.001 0.028 0.001 test_profile.py:115(__getattr__) - 1 0.270 0.270 1.000 1.000 test_profile.py:30(testfunc) - 23/3 0.150 0.007 0.170 0.057 test_profile.py:40(factorial) - 20 0.020 0.001 0.020 0.001 test_profile.py:53(mul) - 2 0.040 0.020 0.600 0.300 test_profile.py:60(helper) - 4 0.116 0.029 0.120 0.030 test_profile.py:78(helper1) - 2 0.000 0.000 0.140 0.070 test_profile.py:89(helper2_indirect) - 8 0.312 0.039 0.400 0.050 test_profile.py:93(helper2) - - - Ordered by: standard name - -Function called... -:0(append) -> -:0(exc_info) -> -:0(exec) -> <string>:1(<module>)(1) 1.000 - io.py:1213(flush)(2) 0.000 -:0(hasattr) -> test_profile.py:115(__getattr__)(12) 0.028 -:0(setprofile) -> -<string>:1(<module>) -> test_profile.py:30(testfunc)(1) 1.000 -io.py:1213(flush) -> io.py:269(flush)(1) 0.000 - io.py:874(flush)(1) 0.000 -io.py:269(flush) -> -io.py:656(closed) -> -io.py:874(flush) -> io.py:656(closed)(1) 0.000 -profile:0(profiler) -> profile:0(testfunc())(1) 1.000 -profile:0(testfunc()) -> :0(exec)(1) 1.000 - :0(setprofile)(1) 0.000 -test_profile.py:103(subhelper) -> test_profile.py:115(__getattr__)(16) 0.028 -test_profile.py:115(__getattr__) -> -test_profile.py:30(testfunc) -> test_profile.py:40(factorial)(1) 0.170 - test_profile.py:60(helper)(2) 0.600 -test_profile.py:40(factorial) -> test_profile.py:40(factorial)(20) 0.170 - test_profile.py:53(mul)(20) 0.020 -test_profile.py:53(mul) -> -test_profile.py:60(helper) -> test_profile.py:78(helper1)(4) 0.120 - test_profile.py:89(helper2_indirect)(2) 0.140 - test_profile.py:93(helper2)(6) 0.400 -test_profile.py:78(helper1) -> :0(append)(4) 0.000 - :0(exc_info)(4) 0.000 - :0(hasattr)(4) 0.012 -test_profile.py:89(helper2_indirect) -> test_profile.py:40(factorial)(2) 0.170 - test_profile.py:93(helper2)(2) 0.400 -test_profile.py:93(helper2) -> :0(hasattr)(8) 0.012 - test_profile.py:103(subhelper)(8) 0.080 - - - Ordered by: standard name - -Function was called by... -:0(append) <- test_profile.py:78(helper1)(4) 0.120 -:0(exc_info) <- test_profile.py:78(helper1)(4) 0.120 -:0(exec) <- profile:0(testfunc())(1) 1.000 -:0(hasattr) <- test_profile.py:78(helper1)(4) 0.120 - test_profile.py:93(helper2)(8) 0.400 -:0(setprofile) <- profile:0(testfunc())(1) 1.000 -<string>:1(<module>) <- :0(exec)(1) 1.000 -io.py:1213(flush) <- :0(exec)(2) 1.000 -io.py:269(flush) <- io.py:1213(flush)(1) 0.000 -io.py:656(closed) <- io.py:874(flush)(1) 0.000 -io.py:874(flush) <- io.py:1213(flush)(1) 0.000 -profile:0(profiler) <- -profile:0(testfunc()) <- profile:0(profiler)(1) 0.000 -test_profile.py:103(subhelper) <- test_profile.py:93(helper2)(8) 0.400 -test_profile.py:115(__getattr__) <- :0(hasattr)(12) 0.012 - test_profile.py:103(subhelper)(16) 0.080 -test_profile.py:30(testfunc) <- <string>:1(<module>)(1) 1.000 -test_profile.py:40(factorial) <- test_profile.py:30(testfunc)(1) 1.000 - test_profile.py:40(factorial)(20) 0.170 - test_profile.py:89(helper2_indirect)(2) 0.140 -test_profile.py:53(mul) <- test_profile.py:40(factorial)(20) 0.170 -test_profile.py:60(helper) <- test_profile.py:30(testfunc)(2) 1.000 -test_profile.py:78(helper1) <- test_profile.py:60(helper)(4) 0.600 -test_profile.py:89(helper2_indirect) <- test_profile.py:60(helper)(2) 0.600 -test_profile.py:93(helper2) <- test_profile.py:60(helper)(6) 0.600 - test_profile.py:89(helper2_indirect)(2) 0.140 - - diff --git a/Lib/test/profilee.py b/Lib/test/profilee.py new file mode 100644 index 0000000..6ad2c83 --- /dev/null +++ b/Lib/test/profilee.py @@ -0,0 +1,115 @@ +""" +Input for test_profile.py and test_cprofile.py. + +IMPORTANT: This stuff is touchy. If you modify anything above the +test class you'll have to regenerate the stats by running the two +test files. + +*ALL* NUMBERS in the expected output are relevant. If you change +the formatting of pstats, please don't just regenerate the expected +output without checking very carefully that not a single number has +changed. +""" + +import sys + +# In order to have reproducible time, we simulate a timer in the global +# variable 'TICKS', which represents simulated time in milliseconds. +# (We can't use a helper function increment the timer since it would be +# included in the profile and would appear to consume all the time.) +TICKS = 42000 + +def timer(): + return TICKS + +def testfunc(): + # 1 call + # 1000 ticks total: 270 ticks local, 730 ticks in subfunctions + global TICKS + TICKS += 99 + helper() # 300 + helper() # 300 + TICKS += 171 + factorial(14) # 130 + +def factorial(n): + # 23 calls total + # 170 ticks total, 150 ticks local + # 3 primitive calls, 130, 20 and 20 ticks total + # including 116, 17, 17 ticks local + global TICKS + if n > 0: + TICKS += n + return mul(n, factorial(n-1)) + else: + TICKS += 11 + return 1 + +def mul(a, b): + # 20 calls + # 1 tick, local + global TICKS + TICKS += 1 + return a * b + +def helper(): + # 2 calls + # 300 ticks total: 20 ticks local, 260 ticks in subfunctions + global TICKS + TICKS += 1 + helper1() # 30 + TICKS += 2 + helper1() # 30 + TICKS += 6 + helper2() # 50 + TICKS += 3 + helper2() # 50 + TICKS += 2 + helper2() # 50 + TICKS += 5 + helper2_indirect() # 70 + TICKS += 1 + +def helper1(): + # 4 calls + # 30 ticks total: 29 ticks local, 1 tick in subfunctions + global TICKS + TICKS += 10 + hasattr(C(), "foo") # 1 + TICKS += 19 + lst = [] + lst.append(42) # 0 + sys.exc_info() # 0 + +def helper2_indirect(): + helper2() # 50 + factorial(3) # 20 + +def helper2(): + # 8 calls + # 50 ticks local: 39 ticks local, 11 ticks in subfunctions + global TICKS + TICKS += 11 + hasattr(C(), "bar") # 1 + TICKS += 13 + subhelper() # 10 + TICKS += 15 + +def subhelper(): + # 8 calls + # 10 ticks total: 8 ticks local, 2 ticks in subfunctions + global TICKS + TICKS += 2 + for i in range(2): # 0 + try: + C().foo # 1 x 2 + except AttributeError: + TICKS += 3 # 3 x 2 + +class C: + def __getattr__(self, name): + # 28 calls + # 1 tick, local + global TICKS + TICKS += 1 + raise AttributeError diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index 1a056f8..c9984b3 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -117,11 +117,11 @@ b"""<dl><dt><a name="-<lambda>"><strong><lambda></strong></a>(x, y)< The documentation for the "add" method contains the test material. """ self.client.request("GET", "/") - response = self.client.getresponse() + response = self.client.getresponse().read() self.assert_( # This is ugly ... how can it be made better? -b"""<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd><tt>Add two instances together. This follows <a href="http://www.python.org/peps/pep-0008.html">PEP008</a>, but has nothing<br>\nto do with <a href="http://www.rfc-editor.org/rfc/rfc1952.txt">RFC1952</a>. Case should matter: pEp008 and rFC1952. Things<br>\nthat start with http and ftp should be auto-linked, too:<br>\n<a href="http://google.com">http://google.com</a>.</tt></dd></dl>""" - in response.read()) +b"""<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd><tt>Add two instances together. This follows <a href="http://www.python.org/dev/peps/pep-0008/">PEP008</a>, but has nothing<br>\nto do with <a href="http://www.rfc-editor.org/rfc/rfc1952.txt">RFC1952</a>. Case should matter: pEp008 and rFC1952. Things<br>\nthat start with http and ftp should be auto-linked, too:<br>\n<a href="http://google.com">http://google.com</a>.</tt></dd></dl>""" + in response, response) def test_system_methods(self): """Test the precense of three consecutive system.* methods. diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 95ad8d2..632911d 100644..100755 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -1,123 +1,179 @@ """Test suite for the profile module.""" -import profile, pstats, sys +import os +import sys +import pstats +import unittest +from difflib import unified_diff +from io import StringIO +from test.test_support import run_unittest -# In order to have reproducible time, we simulate a timer in the global -# variable 'ticks', which represents simulated time in milliseconds. -# (We can't use a helper function increment the timer since it would be -# included in the profile and would appear to consume all the time.) -ticks = 0 +import profile +from test.profilee import testfunc, timer + + +class ProfileTest(unittest.TestCase): + + profilerclass = profile.Profile + methodnames = ['print_stats', 'print_callers', 'print_callees'] + expected_output = {} + + @classmethod + def do_profiling(cls): + results = [] + prof = cls.profilerclass(timer, 0.001) + prof.runctx("testfunc()", globals(), locals()) + results.append(timer()) + for methodname in cls.methodnames: + s = StringIO() + stats = pstats.Stats(prof, stream=s) + stats.strip_dirs().sort_stats("stdname") + getattr(stats, methodname)() + results.append(s.getvalue()) + return results + + def test_cprofile(self): + results = self.do_profiling() + self.assertEqual(results[0], 43000) + for i, method in enumerate(self.methodnames): + if results[i+1] != self.expected_output[method]: + print("Stats.%s output for %s doesn't fit expectation!" % + (method, self.profilerclass.__name__)) + print('\n'.join(unified_diff( + results[i+1].split('\n'), + self.expected_output[method].split('\n')))) + + +def regenerate_expected_output(filename, cls): + filename = filename.rstrip('co') + print('Regenerating %s...' % filename) + results = cls.do_profiling() + + newfile = [] + with open(filename, 'r') as f: + for line in f: + newfile.append(line) + if line[:6] == '#--cut': + break + + with open(filename, 'w') as f: + f.writelines(newfile) + for i, method in enumerate(cls.methodnames): + f.write('%s.expected_output[%r] = """\\\n%s"""\n' % ( + cls.__name__, method, results[i+1])) + f.write('\nif __name__ == "__main__":\n main()\n') -# IMPORTANT: this is an output test. *ALL* NUMBERS in the expected -# output are relevant. If you change the formatting of pstats, -# please don't just regenerate output/test_profile without checking -# very carefully that not a single number has changed. def test_main(): - global ticks - ticks = 42000 - prof = profile.Profile(timer) - prof.runctx("testfunc()", globals(), locals()) - assert ticks == 43000, ticks - st = pstats.Stats(prof) - st.strip_dirs().sort_stats('stdname').print_stats() - st.print_callees() - st.print_callers() - -def timer(): - return ticks*0.001 - -def testfunc(): - # 1 call - # 1000 ticks total: 270 ticks local, 730 ticks in subfunctions - global ticks - ticks += 99 - helper() # 300 - helper() # 300 - ticks += 171 - factorial(14) # 130 - -def factorial(n): - # 23 calls total - # 170 ticks total, 150 ticks local - # 3 primitive calls, 130, 20 and 20 ticks total - # including 116, 17, 17 ticks local - global ticks - if n > 0: - ticks += n - return mul(n, factorial(n-1)) + run_unittest(ProfileTest) + +def main(): + if '-r' not in sys.argv: + test_main() else: - ticks += 11 - return 1 - -def mul(a, b): - # 20 calls - # 1 tick, local - global ticks - ticks += 1 - return a * b - -def helper(): - # 2 calls - # 300 ticks total: 20 ticks local, 260 ticks in subfunctions - global ticks - ticks += 1 - helper1() # 30 - ticks += 2 - helper1() # 30 - ticks += 6 - helper2() # 50 - ticks += 3 - helper2() # 50 - ticks += 2 - helper2() # 50 - ticks += 5 - helper2_indirect() # 70 - ticks += 1 - -def helper1(): - # 4 calls - # 30 ticks total: 29 ticks local, 1 tick in subfunctions - global ticks - ticks += 10 - hasattr(C(), "foo") # 1 - ticks += 19 - lst = [] - lst.append(42) # 0 - sys.exc_info() # 0 - -def helper2_indirect(): - helper2() # 50 - factorial(3) # 20 - -def helper2(): - # 8 calls - # 50 ticks local: 39 ticks local, 11 ticks in subfunctions - global ticks - ticks += 11 - hasattr(C(), "bar") # 1 - ticks += 13 - subhelper() # 10 - ticks += 15 - -def subhelper(): - # 8 calls - # 10 ticks total: 8 ticks local, 2 ticks in subfunctions - global ticks - ticks += 2 - for i in range(2): # 0 - try: - C().foo # 1 x 2 - except AttributeError: - ticks += 3 # 3 x 2 - -class C: - def __getattr__(self, name): - # 28 calls - # 1 tick, local - global ticks - ticks += 1 - raise AttributeError + regenerate_expected_output(__file__, ProfileTest) + + +# Don't remove this comment. Everything below it is auto-generated. +#--cut-------------------------------------------------------------------------- +ProfileTest.expected_output['print_stats'] = """\ + 126 function calls (106 primitive calls) in 999.751 CPU seconds + + Ordered by: standard name + + ncalls tottime percall cumtime percall filename:lineno(function) + 4 -0.004 -0.001 -0.004 -0.001 :0(append) + 4 -0.004 -0.001 -0.004 -0.001 :0(exc_info) + 1 -0.004 -0.004 999.753 999.753 :0(exec) + 12 -0.024 -0.002 11.964 0.997 :0(hasattr) + 1 0.000 0.000 0.000 0.000 :0(setprofile) + 1 -0.002 -0.002 999.767 999.767 <string>:1(<module>) + 2 -0.004 -0.002 -0.010 -0.005 io.py:1213(flush) + 2 -0.002 -0.001 -0.002 -0.001 io.py:656(closed) + 2 -0.004 -0.002 -0.006 -0.003 io.py:874(flush) + 0 0.000 0.000 profile:0(profiler) + 1 -0.002 -0.002 999.751 999.751 profile:0(testfunc()) + 28 27.972 0.999 27.972 0.999 profilee.py:110(__getattr__) + 1 269.996 269.996 999.769 999.769 profilee.py:25(testfunc) + 23/3 149.937 6.519 169.917 56.639 profilee.py:35(factorial) + 20 19.980 0.999 19.980 0.999 profilee.py:48(mul) + 2 39.986 19.993 599.830 299.915 profilee.py:55(helper) + 4 115.984 28.996 119.964 29.991 profilee.py:73(helper1) + 2 -0.006 -0.003 139.946 69.973 profilee.py:84(helper2_indirect) + 8 311.976 38.997 399.912 49.989 profilee.py:88(helper2) + 8 63.976 7.997 79.960 9.995 profilee.py:98(subhelper) + + +""" +ProfileTest.expected_output['print_callers'] = """\ + Ordered by: standard name + +Function was called by... +:0(append) <- profilee.py:73(helper1)(4) 119.964 +:0(exc_info) <- profilee.py:73(helper1)(4) 119.964 +:0(exec) <- profile:0(testfunc())(1) 999.751 +:0(hasattr) <- profilee.py:73(helper1)(4) 119.964 + profilee.py:88(helper2)(8) 399.912 +:0(setprofile) <- profile:0(testfunc())(1) 999.751 +<string>:1(<module>) <- :0(exec)(1) 999.753 +io.py:1213(flush) <- :0(exec)(2) 999.753 +io.py:656(closed) <- io.py:874(flush)(2) -0.006 +io.py:874(flush) <- io.py:1213(flush)(2) -0.010 +profile:0(profiler) <- +profile:0(testfunc()) <- profile:0(profiler)(1) 0.000 +profilee.py:110(__getattr__) <- :0(hasattr)(12) 11.964 + profilee.py:98(subhelper)(16) 79.960 +profilee.py:25(testfunc) <- <string>:1(<module>)(1) 999.767 +profilee.py:35(factorial) <- profilee.py:25(testfunc)(1) 999.769 + profilee.py:35(factorial)(20) 169.917 + profilee.py:84(helper2_indirect)(2) 139.946 +profilee.py:48(mul) <- profilee.py:35(factorial)(20) 169.917 +profilee.py:55(helper) <- profilee.py:25(testfunc)(2) 999.769 +profilee.py:73(helper1) <- profilee.py:55(helper)(4) 599.830 +profilee.py:84(helper2_indirect) <- profilee.py:55(helper)(2) 599.830 +profilee.py:88(helper2) <- profilee.py:55(helper)(6) 599.830 + profilee.py:84(helper2_indirect)(2) 139.946 +profilee.py:98(subhelper) <- profilee.py:88(helper2)(8) 399.912 + + +""" +ProfileTest.expected_output['print_callees'] = """\ + Ordered by: standard name + +Function called... +:0(append) -> +:0(exc_info) -> +:0(exec) -> <string>:1(<module>)(1) 999.767 + io.py:1213(flush)(2) -0.010 +:0(hasattr) -> profilee.py:110(__getattr__)(12) 27.972 +:0(setprofile) -> +<string>:1(<module>) -> profilee.py:25(testfunc)(1) 999.769 +io.py:1213(flush) -> io.py:874(flush)(2) -0.006 +io.py:656(closed) -> +io.py:874(flush) -> io.py:656(closed)(2) -0.002 +profile:0(profiler) -> profile:0(testfunc())(1) 999.751 +profile:0(testfunc()) -> :0(exec)(1) 999.753 + :0(setprofile)(1) 0.000 +profilee.py:110(__getattr__) -> +profilee.py:25(testfunc) -> profilee.py:35(factorial)(1) 169.917 + profilee.py:55(helper)(2) 599.830 +profilee.py:35(factorial) -> profilee.py:35(factorial)(20) 169.917 + profilee.py:48(mul)(20) 19.980 +profilee.py:48(mul) -> +profilee.py:55(helper) -> profilee.py:73(helper1)(4) 119.964 + profilee.py:84(helper2_indirect)(2) 139.946 + profilee.py:88(helper2)(6) 399.912 +profilee.py:73(helper1) -> :0(append)(4) -0.004 + :0(exc_info)(4) -0.004 + :0(hasattr)(4) 11.964 +profilee.py:84(helper2_indirect) -> profilee.py:35(factorial)(2) 169.917 + profilee.py:88(helper2)(2) 399.912 +profilee.py:88(helper2) -> :0(hasattr)(8) 11.964 + profilee.py:98(subhelper)(8) 79.960 +profilee.py:98(subhelper) -> profilee.py:110(__getattr__)(16) 27.972 + + +""" if __name__ == "__main__": - test_main() + main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 11251b7..d8d74d0 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -339,11 +339,14 @@ class SysModuleTest(unittest.TestCase): # freed blocks shouldn't change self.assertEqual(r[0][2], 0) # fill freelists - floats = [float(i) for i in range(12000)] + ints = list(range(10000)) + floats = [float(i) for i in ints] + del ints del floats - # should free more than 200 blocks + # should free more than 100 blocks r = sys._compact_freelists() - self.assert_(r[0][2] > 200, r[0][2]) + self.assert_(r[0][1] > 100, r[0][1]) + self.assert_(r[0][2] > 100, r[0][2]) def test_main(): test.test_support.run_unittest(SysModuleTest) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index a0f76c6..635e1bc 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -252,14 +252,16 @@ class TraceTestCase(unittest.TestCase): "\n".join(difflib.ndiff([str(x) for x in expected_events], [str(x) for x in events]))) - - def run_test(self, func): + def run_and_compare(self, func, events): tracer = Tracer() sys.settrace(tracer.trace) func() sys.settrace(None) self.compare_events(func.__code__.co_firstlineno, - tracer.events, func.events) + tracer.events, events) + + def run_test(self, func): + self.run_and_compare(func, func.events) def run_test2(self, func): tracer = Tracer() @@ -321,6 +323,59 @@ class TraceTestCase(unittest.TestCase): self.compare_events(generator_example.__code__.co_firstlineno, tracer.events, generator_example.events) + def test_14_onliner_if(self): + def onliners(): + if True: False + else: True + return 0 + self.run_and_compare( + onliners, + [(0, 'call'), + (1, 'line'), + (3, 'line'), + (3, 'return')]) + + def test_15_loops(self): + # issue1750076: "while" expression is skipped by debugger + def for_example(): + for x in range(2): + pass + self.run_and_compare( + for_example, + [(0, 'call'), + (1, 'line'), + (2, 'line'), + (1, 'line'), + (2, 'line'), + (1, 'line'), + (1, 'return')]) + + def while_example(): + # While expression should be traced on every loop + x = 2 + while x > 0: + x -= 1 + self.run_and_compare( + while_example, + [(0, 'call'), + (2, 'line'), + (3, 'line'), + (4, 'line'), + (3, 'line'), + (4, 'line'), + (3, 'line'), + (3, 'return')]) + + def test_16_blank_lines(self): + namespace = {} + exec("def f():\n" + "\n" * 256 + " pass", namespace) + self.run_and_compare( + namespace["f"], + [(0, 'call'), + (257, 'line'), + (257, 'return')]) + + class RaisingTraceFuncTestCase(unittest.TestCase): def trace(self, frame, event, arg): """A trace function that raises an exception in response to a diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index 271f0a8..c37323a 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,32 +1,45 @@ -from test.test_support import TestFailed, TESTFN +from test.test_support import TESTFN, run_unittest import os import wave - -def check(t, msg=None): - if not t: - raise TestFailed(msg) +import unittest nchannels = 2 sampwidth = 2 framerate = 8000 nframes = 100 -f = wave.open(TESTFN, 'wb') -f.setnchannels(nchannels) -f.setsampwidth(sampwidth) -f.setframerate(framerate) -f.setnframes(nframes) -output = b'\0' * nframes * nchannels * sampwidth -f.writeframes(output) -f.close() - -f = wave.open(TESTFN, 'rb') -check(nchannels == f.getnchannels(), "nchannels") -check(sampwidth == f.getsampwidth(), "sampwidth") -check(framerate == f.getframerate(), "framerate") -check(nframes == f.getnframes(), "nframes") -input = f.readframes(nframes) -check(input == output, "data") -f.close() - -os.remove(TESTFN) +class TestWave(unittest.TestCase): + + def setUp(self): + self.f = None + + def tearDown(self): + if self.f is not None: + self.f.close() + try: + os.remove(TESTFN) + except OSError: + pass + + def test_it(self): + self.f = wave.open(TESTFN, 'wb') + self.f.setnchannels(nchannels) + self.f.setsampwidth(sampwidth) + self.f.setframerate(framerate) + self.f.setnframes(nframes) + output = b'\0' * nframes * nchannels * sampwidth + self.f.writeframes(output) + self.f.close() + + self.f = wave.open(TESTFN, 'rb') + self.assertEqual(nchannels, self.f.getnchannels()) + self.assertEqual(sampwidth, self.f.getsampwidth()) + self.assertEqual(framerate, self.f.getframerate()) + self.assertEqual(nframes, self.f.getnframes()) + self.assertEqual(self.f.readframes(nframes), output) + +def test_main(): + run_unittest(TestWave) + +if __name__ == '__main__': + test_main() diff --git a/Misc/python-mode.el b/Misc/python-mode.el index 5d9af67..1ad818f 100644 --- a/Misc/python-mode.el +++ b/Misc/python-mode.el @@ -2,7 +2,8 @@ ;; Copyright (C) 1992,1993,1994 Tim Peters -;; Author: 1995-2002 Barry A. Warsaw +;; Author: 2003-2007 http://sf.net/projects/python-mode +;; 1995-2002 Barry A. Warsaw ;; 1992-1994 Tim Peters ;; Maintainer: python-mode@python.org ;; Created: Feb 1992 @@ -19,19 +20,38 @@ ;;; Commentary: -;; This is a major mode for editing Python programs. It was developed -;; by Tim Peters after an original idea by Michael A. Guravage. Tim -;; subsequently left the net; in 1995, Barry Warsaw inherited the mode -;; and is the current maintainer. Tim's now back but disavows all -;; responsibility for the mode. Smart Tim :-) +;; This is a major mode for editing Python programs. It was developed by Tim +;; Peters after an original idea by Michael A. Guravage. Tim subsequently +;; left the net and in 1995, Barry Warsaw inherited the mode. Tim's now back +;; but disavows all responsibility for the mode. In fact, we suspect he +;; doesn't even use Emacs any more. In 2003, python-mode.el was moved to its +;; own SourceForge project apart from the Python project, and now is +;; maintained by the volunteers at the python-mode@python.org mailing list. -;; pdbtrack support contributed by Ken Manheimer, April 2001. +;; pdbtrack support contributed by Ken Manheimer, April 2001. Skip Montanaro +;; has also contributed significantly to python-mode's development. ;; Please use the SourceForge Python project to submit bugs or ;; patches: ;; ;; http://sourceforge.net/projects/python +;; INSTALLATION: + +;; To install, just drop this file into a directory on your load-path and +;; byte-compile it. To set up Emacs to automatically edit files ending in +;; ".py" using python-mode add the following to your ~/.emacs file (GNU +;; Emacs) or ~/.xemacs/init.el file (XEmacs): +;; (setq auto-mode-alist (cons '("\\.py$" . python-mode) auto-mode-alist)) +;; (setq interpreter-mode-alist (cons '("python" . python-mode) +;; interpreter-mode-alist)) +;; (autoload 'python-mode "python-mode" "Python editing mode." t) +;; +;; In XEmacs syntax highlighting should be enabled automatically. In GNU +;; Emacs you may have to add these lines to your ~/.emacs file: +;; (global-font-lock-mode t) +;; (setq font-lock-maximum-decoration t) + ;; FOR MORE INFORMATION: ;; There is some information on python-mode.el at @@ -60,6 +80,7 @@ (require 'custom) (require 'cl) (require 'compile) +(require 'ansi-color) ;; user definable variables @@ -70,34 +91,41 @@ :group 'languages :prefix "py-") +(defcustom py-tab-always-indent t + "*Non-nil means TAB in Python mode should always reindent the current line, +regardless of where in the line point is when the TAB command is used." + :type 'boolean + :group 'python) + (defcustom py-python-command "python" "*Shell command used to start Python interpreter." :type 'string :group 'python) -(defcustom py-jpython-command "jpython" - "*Shell command used to start the JPython interpreter." +(make-obsolete-variable 'py-jpython-command 'py-jython-command) +(defcustom py-jython-command "jython" + "*Shell command used to start the Jython interpreter." :type 'string :group 'python - :tag "JPython Command") + :tag "Jython Command") (defcustom py-default-interpreter 'cpython "*Which Python interpreter is used by default. -The value for this variable can be either `cpython' or `jpython'. +The value for this variable can be either `cpython' or `jython'. When the value is `cpython', the variables `py-python-command' and `py-python-command-args' are consulted to determine the interpreter and arguments to use. -When the value is `jpython', the variables `py-jpython-command' and -`py-jpython-command-args' are consulted to determine the interpreter +When the value is `jython', the variables `py-jython-command' and +`py-jython-command-args' are consulted to determine the interpreter and arguments to use. Note that this variable is consulted only the first time that a Python mode buffer is visited during an Emacs session. After that, use \\[py-toggle-shells] to change the interpreter shell." :type '(choice (const :tag "Python (a.k.a. CPython)" cpython) - (const :tag "JPython" jpython)) + (const :tag "Jython" jython)) :group 'python) (defcustom py-python-command-args '("-i") @@ -105,11 +133,12 @@ mode buffer is visited during an Emacs session. After that, use :type '(repeat string) :group 'python) -(defcustom py-jpython-command-args '("-i") - "*List of string arguments to be used when starting a JPython shell." +(make-obsolete-variable 'py-jpython-command-args 'py-jython-command-args) +(defcustom py-jython-command-args '("-i") + "*List of string arguments to be used when starting a Jython shell." :type '(repeat string) :group 'python - :tag "JPython Command Args") + :tag "Jython Command Args") (defcustom py-indent-offset 4 "*Amount of offset per level of indentation. @@ -248,7 +277,7 @@ Otherwise, all modified buffers are saved without asking." :type 'function :group 'python) -(defcustom py-imenu-show-method-args-p nil +(defcustom py-imenu-show-method-args-p nil "*Controls echoing of arguments of functions & methods in the Imenu buffer. When non-nil, arguments are printed." :type 'boolean @@ -275,19 +304,20 @@ as gud-mode does for debugging C programs with gdb." 20000 "Maximum number of characters to search for a Java-ish import statement. When `python-mode' tries to calculate the shell to use (either a -CPython or a JPython shell), it looks at the so-called `shebang' line +CPython or a Jython shell), it looks at the so-called `shebang' line -- i.e. #! line. If that's not available, it looks at some of the file heading imports to see if they look Java-like." :type 'integer :group 'python ) -(defcustom py-jpython-packages +(make-obsolete-variable 'py-jpython-packages 'py-jython-packages) +(defcustom py-jython-packages '("java" "javax" "org" "com") - "Imported packages that imply `jpython-mode'." + "Imported packages that imply `jython-mode'." :type '(repeat string) :group 'python) - + ;; Not customizable (defvar py-master-file nil "If non-nil, execute the named file instead of the buffer's file. @@ -317,16 +347,39 @@ buffer is prepended to come up with a file name.") :tag "Pychecker Command Args") (defvar py-shell-alist - '(("jpython" . 'jpython) - ("jython" . 'jpython) + '(("jython" . 'jython) ("python" . 'cpython)) "*Alist of interpreters and python shells. Used by `py-choose-shell' to select the appropriate python interpreter mode for a file.") +(defcustom py-shell-input-prompt-1-regexp "^>>> " + "*A regular expression to match the input prompt of the shell." + :type 'string + :group 'python) + +(defcustom py-shell-input-prompt-2-regexp "^[.][.][.] " + "*A regular expression to match the input prompt of the shell after the + first line of input." + :type 'string + :group 'python) + +(defcustom py-shell-switch-buffers-on-execute t + "*Controls switching to the Python buffer where commands are + executed. When non-nil the buffer switches to the Python buffer, if + not no switching occurs." + :type 'boolean + :group 'python) + ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT +(defvar py-line-number-offset 0 + "When an exception occurs as a result of py-execute-region, a +subsequent py-up-exception needs the line number where the region +started, in order to jump to the correct file line. This variable is +set in py-execute-region and used in py-jump-to-exception.") + (defconst py-emacs-features (let (features) features) @@ -339,9 +392,31 @@ support for features needed by `python-mode'.") "Face for pseudo keywords in Python mode, like self, True, False, Ellipsis.") (make-face 'py-pseudo-keyword-face) +;; PEP 318 decorators +(defvar py-decorators-face 'py-decorators-face + "Face method decorators.") +(make-face 'py-decorators-face) + +;; Face for builtins +(defvar py-builtins-face 'py-builtins-face + "Face for builtins like TypeError, object, open, and exec.") +(make-face 'py-builtins-face) + +;; XXX, TODO, and FIXME comments and such +(defvar py-XXX-tag-face 'py-XXX-tag-face + "Face for XXX, TODO, and FIXME tags") +(make-face 'py-XXX-tag-face) + (defun py-font-lock-mode-hook () (or (face-differs-from-default-p 'py-pseudo-keyword-face) - (copy-face 'font-lock-keyword-face 'py-pseudo-keyword-face))) + (copy-face 'font-lock-keyword-face 'py-pseudo-keyword-face)) + (or (face-differs-from-default-p 'py-builtins-face) + (copy-face 'font-lock-keyword-face 'py-builtins-face)) + (or (face-differs-from-default-p 'py-decorators-face) + (copy-face 'py-pseudo-keyword-face 'py-decorators-face)) + (or (face-differs-from-default-p 'py-XXX-tag-face) + (copy-face 'font-lock-comment-face 'py-XXX-tag-face)) + ) (add-hook 'font-lock-mode-hook 'py-font-lock-mode-hook) (defvar python-font-lock-keywords @@ -352,7 +427,7 @@ support for features needed by `python-mode'.") "from" "global" "if" "import" "in" "is" "lambda" "not" "or" "pass" "print" "raise" - "return" "while" "yield" + "return" "while" "with" "yield" ) "\\|")) (kw2 (mapconcat 'identity @@ -391,26 +466,52 @@ support for features needed by `python-mode'.") "super" "tuple" "type" "unichr" "unicode" "vars" "zip") "\\|")) + (kw4 (mapconcat 'identity + ;; Exceptions and warnings + '("ArithmeticError" "AssertionError" + "AttributeError" "DeprecationWarning" "EOFError" + "EnvironmentError" "Exception" + "FloatingPointError" "FutureWarning" "IOError" + "ImportError" "IndentationError" "IndexError" + "KeyError" "KeyboardInterrupt" "LookupError" + "MemoryError" "NameError" "NotImplemented" + "NotImplementedError" "OSError" "OverflowError" + "OverflowWarning" "PendingDeprecationWarning" + "ReferenceError" "RuntimeError" "RuntimeWarning" + "StandardError" "StopIteration" "SyntaxError" + "SyntaxWarning" "SystemError" "SystemExit" + "TabError" "TypeError" "UnboundLocalError" + "UnicodeDecodeError" "UnicodeEncodeError" + "UnicodeError" "UnicodeTranslateError" + "UserWarning" "ValueError" "Warning" + "ZeroDivisionError") + "\\|")) ) (list + '("^[ \t]*\\(@.+\\)" 1 'py-decorators-face) ;; keywords - (cons (concat "\\b\\(" kw1 "\\)\\b[ \n\t(]") 1) + (cons (concat "\\<\\(" kw1 "\\)\\>[ \n\t(]") 1) ;; builtins when they don't appear as object attributes - (cons (concat "\\(\\b\\|[.]\\)\\(" kw3 "\\)\\b[ \n\t(]") 2) + (list (concat "\\([^. \t]\\|^\\)[ \t]*\\<\\(" kw3 "\\)\\>[ \n\t(]") 2 + 'py-builtins-face) ;; block introducing keywords with immediately following colons. ;; Yes "except" is in both lists. - (cons (concat "\\b\\(" kw2 "\\)[ \n\t(]") 1) - ;; `as' but only in "import foo as bar" - '("[ \t]*\\(\\bfrom\\b.*\\)?\\bimport\\b.*\\b\\(as\\)\\b" . 2) + (cons (concat "\\<\\(" kw2 "\\)[ \n\t(]") 1) + ;; Exceptions + (list (concat "\\<\\(" kw4 "\\)[ \n\t:,(]") 1 'py-builtins-face) + ;; `as' but only in "import foo as bar" or "with foo as bar" + '("[ \t]*\\(\\<from\\>.*\\)?\\<import\\>.*\\<\\(as\\)\\>" . 2) + '("[ \t]*\\<with\\>.*\\<\\(as\\)\\>" . 1) ;; classes - '("\\bclass[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" - 1 font-lock-type-face) + '("\\<class[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" 1 font-lock-type-face) ;; functions - '("\\bdef[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" + '("\\<def[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" 1 font-lock-function-name-face) ;; pseudo-keywords - '("\\b\\(self\\|None\\|True\\|False\\|Ellipsis\\)\\b" + '("\\<\\(self\\|None\\|True\\|False\\|Ellipsis\\)\\>" 1 py-pseudo-keyword-face) + ;; XXX, TODO, and FIXME tags + '("XXX\\|TODO\\|FIXME" 0 py-XXX-tag-face t) )) "Additional expressions to highlight in Python mode.") (put 'python-mode 'font-lock-defaults '(python-font-lock-keywords)) @@ -421,13 +522,7 @@ support for features needed by `python-mode'.") Currently-active file is at the head of the list.") (defvar py-pdbtrack-is-tracking-p nil) -(defvar py-pdbtrack-last-grubbed-buffer nil - "Record of the last buffer used when the source path was invalid. -This buffer is consulted before the buffer-list history for satisfying -`py-pdbtrack-grub-for-buffer', since it's the most often the likely -prospect as debugging continues.") -(make-variable-buffer-local 'py-pdbtrack-last-grubbed-buffer) (defvar py-pychecker-history nil) @@ -461,7 +556,7 @@ prospect as debugging continues.") "\\(" "[^#'\"\n\\]" "\\|" py-stringlit-re "\\)*" "\\\\$") "Regular expression matching Python backslash continuation lines.") - + (defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)" "Regular expression matching a blank or comment line.") @@ -474,7 +569,7 @@ prospect as debugging continues.") "\\|") "\\)") "Regular expression matching statements to be dedented one level.") - + (defconst py-block-closing-keywords-re "\\(return\\|raise\\|break\\|continue\\|pass\\)" "Regular expression matching keywords which typically close a block.") @@ -495,30 +590,17 @@ prospect as debugging continues.") "\\)") "Regular expression matching lines not to dedent after.") -(defconst py-defun-start-re - "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*=" - ;; If you change this, you probably have to change py-current-defun - ;; as well. This is only used by py-current-defun to find the name - ;; for add-log.el. - "Regular expression matching a function, method, or variable assignment.") - -(defconst py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)" - ;; If you change this, you probably have to change py-current-defun - ;; as well. This is only used by py-current-defun to find the name - ;; for add-log.el. - "Regular expression for finding a class name.") - -(defconst py-traceback-line-re +(defvar py-traceback-line-re "[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\)" "Regular expression that describes tracebacks.") -;; pdbtrack contants +;; pdbtrack constants (defconst py-pdbtrack-stack-entry-regexp ; "^> \\([^(]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()" "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()" "Regular expression pdbtrack uses to find a stack trace entry.") -(defconst py-pdbtrack-input-prompt "\n[(<]*pdb[>)]+ " +(defconst py-pdbtrack-input-prompt "\n[(<]*[Pp]db[>)]+ " "Regular expression pdbtrack uses to recognize a pdb prompt.") (defconst py-pdbtrack-track-range 10000 @@ -536,8 +618,9 @@ prospect as debugging continues.") (defvar python-mode-hook nil "*Hook called by `python-mode'.") -(defvar jpython-mode-hook nil - "*Hook called by `jpython-mode'. `jpython-mode' also calls +(make-obsolete-variable 'jpython-mode-hook 'jython-mode-hook) +(defvar jython-mode-hook nil + "*Hook called by `jython-mode'. `jython-mode' also calls `python-mode-hook'.") (defvar py-shell-hook nil @@ -560,8 +643,6 @@ prospect as debugging continues.") (define-key py-mode-map "\C-c\C-r" 'py-shift-region-right) (define-key py-mode-map "\C-c<" 'py-shift-region-left) (define-key py-mode-map "\C-c>" 'py-shift-region-right) - ;; paragraph and string filling - (define-key py-mode-map "\eq" 'py-fill-paragraph) ;; subprocess commands (define-key py-mode-map "\C-c\C-c" 'py-execute-buffer) (define-key py-mode-map "\C-c\C-m" 'py-execute-import-or-reload) @@ -624,7 +705,7 @@ prospect as debugging continues.") ;; expect RET to do a `py-newline-and-indent' and any Emacsers who ;; dislike this are probably knowledgeable enough to do a rebind. ;; However, we do *not* change C-j since many Emacsers have already - ;; swapped RET and C-j and they don't want C-j bound to `newline' to + ;; swapped RET and C-j and they don't want C-j bound to `newline' to ;; change. (define-key py-mode-map "\C-m" 'py-newline-and-indent) ) @@ -740,8 +821,8 @@ This function does not modify point or mark." (cond ((eq position 'bol) (beginning-of-line)) ((eq position 'eol) (end-of-line)) - ((eq position 'bod) (py-beginning-of-def-or-class)) - ((eq position 'eod) (py-end-of-def-or-class)) + ((eq position 'bod) (py-beginning-of-def-or-class 'either)) + ((eq position 'eod) (py-end-of-def-or-class 'either)) ;; Kind of funny, I know, but useful for py-up-exception. ((eq position 'bob) (beginning-of-buffer)) ((eq position 'eob) (end-of-buffer)) @@ -849,7 +930,7 @@ package. Note that the latest X/Emacs releases contain this package.") (defvar py-imenu-method-regexp (concat ; <<methods and functions>> - "\\(" ; + "\\(" ; "^[ \t]*" ; new line and maybe whitespace "\\(def[ \t]+" ; function definitions start with def "\\([a-zA-Z0-9_]+\\)" ; name is here @@ -885,7 +966,7 @@ information.") ;; it. (defvar py-imenu-generic-expression (cons - (concat + (concat py-imenu-class-regexp "\\|" ; or... py-imenu-method-regexp @@ -954,7 +1035,7 @@ of the first definition found." looking-p def-name prev-name cur-indent def-pos - (class-paren (first py-imenu-generic-parens)) + (class-paren (first py-imenu-generic-parens)) (def-paren (second py-imenu-generic-parens))) (setq looking-p (re-search-forward py-imenu-generic-regexp (point-max) t)) @@ -1009,7 +1090,7 @@ of the first definition found." (cons save-elmt sub-method-alist)) index-alist)))) ;; found less indented expression, we're done. - (t + (t (setq looking-p nil) (re-search-backward py-imenu-generic-regexp (point-min) t))) ;; end-cond @@ -1023,7 +1104,7 @@ of the first definition found." (defun py-choose-shell-by-shebang () - "Choose CPython or JPython mode by looking at #! on the first line. + "Choose CPython or Jython mode by looking at #! on the first line. Returns the appropriate mode function. Used by `py-choose-shell', and similar to but distinct from `set-auto-mode', though it uses `auto-mode-interpreter-regexp' (if available)." @@ -1047,10 +1128,10 @@ Used by `py-choose-shell', and similar to but distinct from (defun py-choose-shell-by-import () - "Choose CPython or JPython mode based imports. -If a file imports any packages in `py-jpython-packages', within + "Choose CPython or Jython mode based imports. +If a file imports any packages in `py-jython-packages', within `py-import-check-point-max' characters from the start of the file, -return `jpython', otherwise return nil." +return `jython', otherwise return nil." (let (mode) (save-excursion (goto-char (point-min)) @@ -1058,14 +1139,14 @@ return `jpython', otherwise return nil." (search-forward-regexp "^\\(\\(from\\)\\|\\(import\\)\\) \\([^ \t\n.]+\\)" py-import-check-point-max t)) - (setq mode (and (member (match-string 4) py-jpython-packages) - 'jpython + (setq mode (and (member (match-string 4) py-jython-packages) + 'jython )))) mode)) (defun py-choose-shell () - "Choose CPython or JPython mode. Returns the appropriate mode function. + "Choose CPython or Jython mode. Returns the appropriate mode function. This does the following: - look for an interpreter with `py-choose-shell-by-shebang' - examine imports using `py-choose-shell-by-import' @@ -1114,6 +1195,7 @@ py-beep-if-tab-change\t\tring the bell if `tab-width' is changed" (make-local-variable 'indent-region-function) (make-local-variable 'indent-line-function) (make-local-variable 'add-log-current-defun-function) + (make-local-variable 'fill-paragraph-function) ;; (set-syntax-table py-mode-syntax-table) (setq major-mode 'python-mode @@ -1132,6 +1214,8 @@ py-beep-if-tab-change\t\tring the bell if `tab-width' is changed" indent-line-function 'py-indent-line ;; tell add-log.el how to find the current function/method/variable add-log-current-defun-function 'py-current-defun + + fill-paragraph-function 'py-fill-paragraph ) (use-local-map py-mode-map) ;; add the menu @@ -1171,17 +1255,18 @@ py-beep-if-tab-change\t\tring the bell if `tab-width' is changed" (py-toggle-shells (py-choose-shell)))) -(defun jpython-mode () - "Major mode for editing JPython/Jython files. +(make-obsolete 'jpython-mode 'jython-mode) +(defun jython-mode () + "Major mode for editing Jython/Jython files. This is a simple wrapper around `python-mode'. -It runs `jpython-mode-hook' then calls `python-mode.' +It runs `jython-mode-hook' then calls `python-mode.' It is added to `interpreter-mode-alist' and `py-choose-shell'. " (interactive) (python-mode) - (py-toggle-shells 'jpython) - (when jpython-mode-hook - (run-hooks 'jpython-mode-hook))) + (py-toggle-shells 'jython) + (when jython-mode-hook + (run-hooks 'jython-mode-hook))) ;; It's handy to add recognition of Python files to the @@ -1189,16 +1274,16 @@ It is added to `interpreter-mode-alist' and `py-choose-shell'. ;; can specify different `derived-modes' based on the #! line, but ;; with the latter, we can't. So we just won't add them if they're ;; already added. -(let ((modes '(("jpython" . jpython-mode) - ("jython" . jpython-mode) +;;;###autoload +(let ((modes '(("jython" . jython-mode) ("python" . python-mode)))) (while modes (when (not (assoc (car modes) interpreter-mode-alist)) (push (car modes) interpreter-mode-alist)) (setq modes (cdr modes)))) - +;;;###autoload (when (not (or (rassq 'python-mode auto-mode-alist) - (rassq 'jpython-mode auto-mode-alist))) + (rassq 'jython-mode auto-mode-alist))) (push '("\\.py$" . python-mode) auto-mode-alist)) @@ -1283,12 +1368,13 @@ comint believe the user typed this string so that (defun py-comint-output-filter-function (string) "Watch output for Python prompt and exec next file waiting in queue. This function is appropriate for `comint-output-filter-functions'." - ;; TBD: this should probably use split-string - (when (and (or (string-equal string ">>> ") - (and (>= (length string) 5) - (string-equal (substring string -5) "\n>>> "))) - py-file-queue) - (pop-to-buffer (current-buffer)) + ;;remove ansi terminal escape sequences from string, not sure why they are + ;;still around... + (setq string (ansi-color-filter-apply string)) + (when (and (string-match py-shell-input-prompt-1-regexp string) + py-file-queue) + (if py-shell-switch-buffers-on-execute + (pop-to-buffer (current-buffer))) (py-safe (delete-file (car py-file-queue))) (setq py-file-queue (cdr py-file-queue)) (if py-file-queue @@ -1344,7 +1430,7 @@ script, and set to python-mode, and pdbtrack will find it.)" (- procmark py-pdbtrack-track-range)) procmark)) - target target_fname target_lineno) + target target_fname target_lineno target_buffer) (if (not (string-match (concat py-pdbtrack-input-prompt "$") block)) (py-pdbtrack-overlay-arrow nil) @@ -1372,8 +1458,7 @@ script, and set to python-mode, and pdbtrack will find it.)" We look first to visit the file indicated in the trace. Failing that, we look for the most recently visited python-mode buffer -with the same name or having -having the named function. +with the same name or having the named function. If we're unable find the source code we return a string describing the problem as best as we can determine." @@ -1417,11 +1502,10 @@ problem as best as we can determine." (defun py-pdbtrack-grub-for-buffer (funcname lineno) "Find most recent buffer itself named or having function funcname. -We first check the last buffer this function found, if any, then walk -throught the buffer-list history for python-mode buffers that are +We walk the buffer-list history for python-mode buffers that are named for funcname or define a function funcname." (let ((buffers (buffer-list)) - curbuf + buf got) (while (and buffers (not got)) (setq buf (car buffers) @@ -1436,7 +1520,7 @@ named for funcname or define a function funcname." (buffer-substring (point-min) (point-max)))))) (setq got buf))) - (setq py-pdbtrack-last-grubbed-buffer got))) + got)) (defun py-postprocess-output-buffer (buf) "Highlight exceptions found in BUF. @@ -1466,7 +1550,7 @@ If an exception occurred return t, otherwise return nil. BUF must exist." (defconst py-output-buffer "*Python Output*") (make-variable-buffer-local 'py-output-buffer) -;; for toggling between CPython and JPython +;; for toggling between CPython and Jython (defvar py-which-shell nil) (defvar py-which-args py-python-command-args) (defvar py-which-bufname "Python") @@ -1475,14 +1559,14 @@ If an exception occurred return t, otherwise return nil. BUF must exist." (make-variable-buffer-local 'py-which-bufname) (defun py-toggle-shells (arg) - "Toggles between the CPython and JPython shells. + "Toggles between the CPython and Jython shells. With positive argument ARG (interactively \\[universal-argument]), -uses the CPython shell, with negative ARG uses the JPython shell, and +uses the CPython shell, with negative ARG uses the Jython shell, and with a zero argument, toggles the shell. Programmatically, ARG can also be one of the symbols `cpython' or -`jpython', equivalent to positive arg and negative arg respectively." +`jython', equivalent to positive arg and negative arg respectively." (interactive "P") ;; default is to toggle (if (null arg) @@ -1495,7 +1579,7 @@ Programmatically, ARG can also be one of the symbols `cpython' or (setq arg -1) (setq arg 1))) ((equal arg 'cpython) (setq arg 1)) - ((equal arg 'jpython) (setq arg -1))) + ((equal arg 'jython) (setq arg -1))) (let (msg) (cond ((< 0 arg) @@ -1503,14 +1587,16 @@ Programmatically, ARG can also be one of the symbols `cpython' or (setq py-which-shell py-python-command py-which-args py-python-command-args py-which-bufname "Python" - msg "CPython" - mode-name "Python")) + msg "CPython") + (if (string-equal py-which-bufname "Jython") + (setq mode-name "Python"))) ((> 0 arg) - (setq py-which-shell py-jpython-command - py-which-args py-jpython-command-args - py-which-bufname "JPython" - msg "JPython" - mode-name "JPython")) + (setq py-which-shell py-jython-command + py-which-args py-jython-command-args + py-which-bufname "Jython" + msg "Jython") + (if (string-equal py-which-bufname "Python") + (setq mode-name "Jython"))) ) (message "Using the %s shell" msg) (setq py-output-buffer (format "*%s Output*" py-which-bufname)))) @@ -1532,9 +1618,9 @@ prompt). This argument is ignored when this function is called programmatically, or when running in Emacs 19.34 or older. Note: You can toggle between using the CPython interpreter and the -JPython interpreter by hitting \\[py-toggle-shells]. This toggles +Jython interpreter by hitting \\[py-toggle-shells]. This toggles buffer local variables which control whether all your subshell -interactions happen to the `*JPython*' or `*Python*' buffers (the +interactions happen to the `*Jython*' or `*Python*' buffers (the latter is the name used for the CPython buffer). Warning: Don't use an interactive Python if you change sys.ps1 or @@ -1568,10 +1654,14 @@ filter." (concat (mapconcat 'identity py-which-args " ") " ") )))) - (switch-to-buffer-other-window - (apply 'make-comint py-which-bufname py-which-shell nil args)) + (if (not (equal (buffer-name) "*Python*")) + (switch-to-buffer-other-window + (apply 'make-comint py-which-bufname py-which-shell nil args)) + (apply 'make-comint py-which-bufname py-which-shell nil args)) (make-local-variable 'comint-prompt-regexp) - (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ") + (setq comint-prompt-regexp (concat py-shell-input-prompt-1-regexp "\\|" + py-shell-input-prompt-2-regexp "\\|" + "^([Pp]db) ")) (add-hook 'comint-output-filter-functions 'py-comint-output-filter-function) ;; pdbtrack @@ -1642,11 +1732,13 @@ is inserted at the end. See also the command `py-clear-queue'." (setq start (point)) (or (< start end) (error "Region is empty")) + (setq py-line-number-offset (count-lines 1 start)) (let ((needs-if (/= (py-point 'bol) (py-point 'boi)))) (set-buffer buf) (python-mode) (when needs-if - (insert "if 1:\n")) + (insert "if 1:\n") + (setq py-line-number-offset (- py-line-number-offset 1))) (insert-buffer-substring cur start end) ;; Set the shell either to the #! line command, or to the ;; py-which-shell buffer local variable. @@ -1683,8 +1775,9 @@ is inserted at the end. See also the command `py-clear-queue'." (setq py-exception-buffer (cons file (current-buffer)))) (t ;; TBD: a horrible hack, but why create new Custom variables? - (let ((cmd (concat shell (if (string-equal py-which-bufname "JPython") - " -" "")))) + (let ((cmd (concat py-which-shell (if (string-equal py-which-bufname + "Jython") + " -" "")))) ;; otherwise either run it synchronously in a subprocess (save-excursion (set-buffer buf) @@ -1718,12 +1811,14 @@ sent. A trailing newline will be supplied if needed. See the `\\[py-execute-region]' docs for an account of some subtleties, including the use of the optional ASYNC argument." (interactive "P") - (if py-master-file - (let* ((filename (expand-file-name py-master-file)) - (buffer (or (get-file-buffer filename) - (find-file-noselect filename)))) - (set-buffer buffer))) - (py-execute-region (point-min) (point-max) async)) + (let ((old-buffer (current-buffer))) + (if py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (py-execute-region (point-min) (point-max) async) + (pop-to-buffer old-buffer))) (defun py-execute-import-or-reload (&optional async) "Import the current buffer's file in a Python interpreter. @@ -1819,6 +1914,9 @@ subtleties, including the use of the optional ASYNC argument." (t (find-file (read-file-name "Exception file: " nil file t)))))) + ;; Fiddle about with line number + (setq line (+ py-line-number-offset line)) + (pop-to-buffer buffer) ;; Force Python mode (if (not (eq major-mode 'python-mode)) @@ -1999,16 +2097,29 @@ This function is normally bound to `indent-line-function' so (interactive "P") (let* ((ci (current-indentation)) (move-to-indentation-p (<= (current-column) ci)) - (need (py-compute-indentation (not arg)))) - ;; see if we need to dedent - (if (py-outdent-p) - (setq need (- need py-indent-offset))) - (if (/= ci need) - (save-excursion - (beginning-of-line) - (delete-horizontal-space) - (indent-to need))) - (if move-to-indentation-p (back-to-indentation)))) + (need (py-compute-indentation (not arg))) + (cc (current-column))) + ;; dedent out a level if previous command was the same unless we're in + ;; column 1 + (if (and (equal last-command this-command) + (/= cc 0)) + (progn + (beginning-of-line) + (delete-horizontal-space) + (indent-to (* (/ (- cc 1) py-indent-offset) py-indent-offset))) + (progn + ;; see if we need to dedent + (if (py-outdent-p) + (setq need (- need py-indent-offset))) + (if (or py-tab-always-indent + move-to-indentation-p) + (progn (if (/= ci need) + (save-excursion + (beginning-of-line) + (delete-horizontal-space) + (indent-to need))) + (if move-to-indentation-p (back-to-indentation))) + (insert-tab)))))) (defun py-newline-and-indent () "Strives to act like the Emacs `newline-and-indent'. @@ -2052,39 +2163,23 @@ dedenting." ((py-continuation-line-p) (let ((startpos (point)) (open-bracket-pos (py-nesting-level)) - endpos searching found state) + endpos searching found state cind cline) (if open-bracket-pos (progn - ;; align with first item in list; else a normal - ;; indent beyond the line with the open bracket - (goto-char (1+ open-bracket-pos)) ; just beyond bracket - ;; is the first list item on the same line? - (skip-chars-forward " \t") - (if (null (memq (following-char) '(?\n ?# ?\\))) - ; yes, so line up with it - (current-column) - ;; first list item on another line, or doesn't exist yet - (forward-line 1) - (while (and (< (point) startpos) - (looking-at "[ \t]*[#\n\\\\]")) ; skip noise - (forward-line 1)) - (if (and (< (point) startpos) - (/= startpos - (save-excursion - (goto-char (1+ open-bracket-pos)) - (forward-comment (point-max)) - (point)))) - ;; again mimic the first list item - (current-indentation) - ;; else they're about to enter the first item - (goto-char open-bracket-pos) - (setq placeholder (point)) - (py-goto-initial-line) - (py-goto-beginning-of-tqs - (save-excursion (nth 3 (parse-partial-sexp - placeholder (point))))) - (+ (current-indentation) py-indent-offset)))) - + (setq endpos (py-point 'bol)) + (py-goto-initial-line) + (setq cind (current-indentation)) + (setq cline cind) + (dolist (bp + (nth 9 (save-excursion + (parse-partial-sexp (point) endpos))) + cind) + (if (search-forward "\n" bp t) (setq cline cind)) + (goto-char (1+ bp)) + (skip-chars-forward " \t") + (setq cind (if (memq (following-char) '(?\n ?# ?\\)) + (+ cline py-indent-offset) + (current-column))))) ;; else on backslash continuation line (forward-line -1) (if (py-continuation-line-p) ; on at least 3rd line in block @@ -2832,7 +2927,7 @@ pleasant." ;; ripped from cc-mode (defun py-forward-into-nomenclature (&optional arg) "Move forward to end of a nomenclature section or word. -With \\[universal-argument] (programmatically, optional argument ARG), +With \\[universal-argument] (programmatically, optional argument ARG), do it that many times. A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." @@ -2886,6 +2981,11 @@ A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." ;; Pychecker + +;; hack for FSF Emacs +(unless (fboundp 'read-shell-command) + (defalias 'read-shell-command 'read-string)) + (defun py-pychecker-run (command) "*Run pychecker (default on the file currently visited)." (interactive @@ -3410,7 +3510,7 @@ multi-line statement we need to skip over the continuation lines." (defun py-statement-opens-block-p () "Return t iff the current statement opens a block. -I.e., iff it ends with a colon that is not in a comment. Point should +I.e., iff it ends with a colon that is not in a comment. Point should be at the start of a statement." (save-excursion (let ((start (point)) @@ -3494,8 +3594,8 @@ does not include blank lines, comments, or continuation lines." KEY is a regular expression describing a Python keyword. Skip blank lines and non-indenting comments. If the statement found starts with KEY, then stop, otherwise go back to first enclosing block starting -with KEY. If successful, leave point at the start of the KEY line and -return t. Otherwise, leav point at an undefined place and return nil." +with KEY. If successful, leave point at the start of the KEY line and +return t. Otherwise, leave point at an undefined place and return nil." ;; skip blanks and non-indenting # (py-goto-initial-line) (while (and @@ -3503,7 +3603,7 @@ return t. Otherwise, leav point at an undefined place and return nil." (zerop (forward-line -1))) ; go back nil) (py-goto-initial-line) - (let* ((re (concat "[ \t]*" key "\\b")) + (let* ((re (concat "[ \t]*" key "\\>")) (case-fold-search nil) ; let* so looking-at sees this (found (looking-at re)) (dead nil)) @@ -3529,7 +3629,7 @@ Prefix with \"...\" if leading whitespace was skipped." `Keyword' is defined (essentially) as the regular expression ([a-z]+). Returns nil if none was found." (let ((case-fold-search nil)) - (if (looking-at "[ \t]*\\([a-z]+\\)\\b") + (if (looking-at "[ \t]*\\([a-z]+\\)\\>") (intern (buffer-substring (match-beginning 1) (match-end 1))) nil))) @@ -3537,14 +3637,49 @@ Prefix with \"...\" if leading whitespace was skipped." "Python value for `add-log-current-defun-function'. This tells add-log.el how to find the current function/method/variable." (save-excursion - (if (re-search-backward py-defun-start-re nil t) - (or (match-string 3) - (let ((method (match-string 2))) - (if (and (not (zerop (length (match-string 1)))) - (re-search-backward py-class-start-re nil t)) - (concat (match-string 1) "." method) - method))) - nil))) + + ;; Move back to start of the current statement. + + (py-goto-initial-line) + (back-to-indentation) + (while (and (or (looking-at py-blank-or-comment-re) + (py-in-literal)) + (not (bobp))) + (backward-to-indentation 1)) + (py-goto-initial-line) + + (let ((scopes "") + (sep "") + dead assignment) + + ;; Check for an assignment. If this assignment exists inside a + ;; def, it will be overwritten inside the while loop. If it + ;; exists at top lever or inside a class, it will be preserved. + + (when (looking-at "[ \t]*\\([a-zA-Z0-9_]+\\)[ \t]*=") + (setq scopes (buffer-substring (match-beginning 1) (match-end 1))) + (setq assignment t) + (setq sep ".")) + + ;; Prepend the name of each outer socpe (def or class). + + (while (not dead) + (if (and (py-go-up-tree-to-keyword "\\(class\\|def\\)") + (looking-at + "[ \t]*\\(class\\|def\\)[ \t]*\\([a-zA-Z0-9_]+\\)[ \t]*")) + (let ((name (buffer-substring (match-beginning 2) (match-end 2)))) + (if (and assignment (looking-at "[ \t]*def")) + (setq scopes name) + (setq scopes (concat name sep scopes)) + (setq sep ".")))) + (setq assignment nil) + (condition-case nil ; Terminate nicely at top level. + (py-goto-block-up 'no-mark) + (error (setq dead t)))) + (if (string= scopes "") + nil + scopes)))) + (defconst py-help-address "python-mode@python.org" @@ -3586,7 +3721,7 @@ non-nil) just submit an enhancement request." "Dear Barry,") ;salutation (if enhancement-p nil (set-mark (point)) - (insert + (insert "Please replace this text with a sufficiently large code sample\n\ and an exact recipe so that I can reproduce your problem. Failure\n\ to do so may mean a greater delay in fixing your bug.\n\n") @@ -3606,7 +3741,7 @@ These are Python temporary files awaiting execution." (add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file) ;; Add a designator to the minor mode strings -(or (assq 'py-pdbtrack-minor-mode-string minor-mode-alist) +(or (assq 'py-pdbtrack-is-tracking-p minor-mode-alist) (push '(py-pdbtrack-is-tracking-p py-pdbtrack-minor-mode-string) minor-mode-alist)) @@ -3745,20 +3880,35 @@ and initial `#'s. If point is inside a string, narrow to that string and fill. " (interactive "P") - (let* ((bod (py-point 'bod)) - (pps (parse-partial-sexp bod (point)))) - (cond - ;; are we inside a comment or on a line with only whitespace before - ;; the comment start? - ((or (nth 4 pps) - (save-excursion (beginning-of-line) (looking-at "[ \t]*#"))) - (py-fill-comment justify)) - ;; are we inside a string? - ((nth 3 pps) - (py-fill-string (nth 8 pps))) - ;; otherwise use the default - (t - (fill-paragraph justify))))) + ;; fill-paragraph will narrow incorrectly + (save-restriction + (widen) + (let* ((bod (py-point 'bod)) + (pps (parse-partial-sexp bod (point)))) + (cond + ;; are we inside a comment or on a line with only whitespace before + ;; the comment start? + ((or (nth 4 pps) + (save-excursion (beginning-of-line) (looking-at "[ \t]*#"))) + (py-fill-comment justify)) + ;; are we inside a string? + ((nth 3 pps) + (py-fill-string (nth 8 pps))) + ;; are we at the opening quote of a string, or in the indentation? + ((save-excursion + (forward-word 1) + (eq (py-in-literal) 'string)) + (save-excursion + (py-fill-string (py-point 'boi)))) + ;; are we at or after the closing quote of a string? + ((save-excursion + (backward-word 1) + (eq (py-in-literal) 'string)) + (save-excursion + (py-fill-string (py-point 'boi)))) + ;; otherwise use the default + (t + (fill-paragraph justify)))))) diff --git a/Objects/classobject.c b/Objects/classobject.c index d9f7219..be7ba2d 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -5,6 +5,15 @@ #define TP_DESCR_GET(t) ((t)->tp_descr_get) +/* Free list for method objects to safe malloc/free overhead + * The im_self element is used to chain the elements. + */ +static PyMethodObject *free_list; +static int numfree = 0; +#ifndef PyMethod_MAXFREELIST +#define PyMethod_MAXFREELIST 256 +#endif + PyObject * PyMethod_Function(PyObject *im) { @@ -30,8 +39,6 @@ PyMethod_Self(PyObject *im) function. */ -static PyMethodObject *free_list; - PyObject * PyMethod_New(PyObject *func, PyObject *self) { @@ -48,6 +55,7 @@ PyMethod_New(PyObject *func, PyObject *self) if (im != NULL) { free_list = (PyMethodObject *)(im->im_self); PyObject_INIT(im, &PyMethod_Type); + numfree--; } else { im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); @@ -165,8 +173,14 @@ method_dealloc(register PyMethodObject *im) PyObject_ClearWeakRefs((PyObject *)im); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); - im->im_self = (PyObject *)free_list; - free_list = im; + if (numfree < PyMethod_MAXFREELIST) { + im->im_self = (PyObject *)free_list; + free_list = im; + numfree++; + } + else { + PyObject_GC_Del(im); + } } static PyObject * @@ -375,7 +389,9 @@ PyMethod_Fini(void) PyMethodObject *im = free_list; free_list = (PyMethodObject *)(im->im_self); PyObject_GC_Del(im); + numfree--; } + assert(numfree == 0); } /* ------------------------------------------------------------------------ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 6a1938e..1bc7184 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -184,9 +184,11 @@ show_counts(void) } while(0) /* Dictionary reuse scheme to save calls to malloc, free, and memset */ -#define MAXFREEDICTS 80 -static PyDictObject *free_dicts[MAXFREEDICTS]; -static int num_free_dicts = 0; +#ifndef PyDict_MAXFREELIST +#define PyDict_MAXFREELIST 80 +#endif +static PyDictObject *free_list[PyDict_MAXFREELIST]; +static int numfree = 0; PyObject * PyDict_New(void) @@ -200,8 +202,8 @@ PyDict_New(void) Py_AtExit(show_counts); #endif } - if (num_free_dicts) { - mp = free_dicts[--num_free_dicts]; + if (numfree) { + mp = free_list[--numfree]; assert (mp != NULL); assert (Py_TYPE(mp) == &PyDict_Type); _Py_NewReference((PyObject *)mp); @@ -897,8 +899,8 @@ dict_dealloc(register PyDictObject *mp) } if (mp->ma_table != mp->ma_smalltable) PyMem_DEL(mp->ma_table); - if (num_free_dicts < MAXFREEDICTS && Py_TYPE(mp) == &PyDict_Type) - free_dicts[num_free_dicts++] = mp; + if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type) + free_list[numfree++] = mp; else Py_TYPE(mp)->tp_free((PyObject *)mp); Py_TRASHCAN_SAFE_END(mp) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index deda244..658ce1d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -401,14 +401,15 @@ static PyGetSetDef frame_getsetlist[] = { call depth of more than 20 or 30 is probably already exceptional unless the program contains run-away recursion. I hope. - Later, MAXFREELIST was added to bound the # of frames saved on + Later, PyFrame_MAXFREELIST was added to bound the # of frames saved on free_list. Else programs creating lots of cyclic trash involving frames could provoke free_list into growing without bound. */ static PyFrameObject *free_list = NULL; static int numfree = 0; /* number of frames currently in free_list */ -#define MAXFREELIST 200 /* max value for numfree */ +/* max value for numfree */ +#define PyFrame_MAXFREELIST 200 static void frame_dealloc(PyFrameObject *f) @@ -441,7 +442,7 @@ frame_dealloc(PyFrameObject *f) co = f->f_code; if (co->co_zombieframe == NULL) co->co_zombieframe = f; - else if (numfree < MAXFREELIST) { + else if (numfree < PyFrame_MAXFREELIST) { ++numfree; f->f_back = free_list; free_list = f; diff --git a/Objects/listobject.c b/Objects/listobject.c index a36a29e..cb0609a 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -64,18 +64,20 @@ list_resize(PyListObject *self, Py_ssize_t newsize) } /* Empty list reuse scheme to save calls to malloc and free */ -#define MAXFREELISTS 80 -static PyListObject *free_lists[MAXFREELISTS]; -static int num_free_lists = 0; +#ifndef PyList_MAXFREELIST +#define PyList_MAXFREELIST 80 +#endif +static PyListObject *free_list[PyList_MAXFREELIST]; +static int numfree = 0; void PyList_Fini(void) { PyListObject *op; - while (num_free_lists) { - num_free_lists--; - op = free_lists[num_free_lists]; + while (numfree) { + numfree--; + op = free_list[numfree]; assert(PyList_CheckExact(op)); PyObject_GC_Del(op); } @@ -95,9 +97,9 @@ PyList_New(Py_ssize_t size) /* Check for overflow */ if (nbytes / sizeof(PyObject *) != (size_t)size) return PyErr_NoMemory(); - if (num_free_lists) { - num_free_lists--; - op = free_lists[num_free_lists]; + if (numfree) { + numfree--; + op = free_list[numfree]; _Py_NewReference((PyObject *)op); } else { op = PyObject_GC_New(PyListObject, &PyList_Type); @@ -265,8 +267,8 @@ list_dealloc(PyListObject *op) } PyMem_FREE(op->ob_item); } - if (num_free_lists < MAXFREELISTS && PyList_CheckExact(op)) - free_lists[num_free_lists++] = op; + if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) + free_list[numfree++] = op; else Py_TYPE(op)->tp_free((PyObject *)op); Py_TRASHCAN_SAFE_END(op) diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 73e0790..7a82d89 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -4,7 +4,14 @@ #include "Python.h" #include "structmember.h" +/* Free list for method objects to safe malloc/free overhead + * The m_self element is used to chain the objects. + */ static PyCFunctionObject *free_list = NULL; +static int numfree = 0; +#ifndef PyCFunction_MAXFREELIST +#define PyCFunction_MAXFREELIST 256 +#endif PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) @@ -14,6 +21,7 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) if (op != NULL) { free_list = (PyCFunctionObject *)(op->m_self); PyObject_INIT(op, &PyCFunction_Type); + numfree--; } else { op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type); @@ -116,8 +124,14 @@ meth_dealloc(PyCFunctionObject *m) _PyObject_GC_UNTRACK(m); Py_XDECREF(m->m_self); Py_XDECREF(m->m_module); - m->m_self = (PyObject *)free_list; - free_list = m; + if (numfree < PyCFunction_MAXFREELIST) { + m->m_self = (PyObject *)free_list; + free_list = m; + numfree++; + } + else { + PyObject_GC_Del(m); + } } static PyObject * @@ -312,14 +326,16 @@ PyCFunction_Fini(void) PyCFunctionObject *v = free_list; free_list = (PyCFunctionObject *)(v->m_self); PyObject_GC_Del(v); + numfree--; } + assert(numfree == 0); } /* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(), but it's part of the API so we need to keep a function around that existing C extensions can call. */ - + #undef PyCFunction_New PyAPI_FUNC(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *); diff --git a/Objects/setobject.c b/Objects/setobject.c index c827434..fcc152a 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -52,9 +52,11 @@ _PySet_Dummy(void) } while(0) /* Reuse scheme to save calls to malloc, free, and memset */ -#define MAXFREESETS 80 -static PySetObject *free_sets[MAXFREESETS]; -static int num_free_sets = 0; +#ifndef PySet_MAXFREELIST +#define PySet_MAXFREELIST 80 +#endif +static PySetObject *free_list[PySet_MAXFREELIST]; +static int numfree = 0; /* @@ -561,8 +563,8 @@ set_dealloc(PySetObject *so) } if (so->table != so->smalltable) PyMem_DEL(so->table); - if (num_free_sets < MAXFREESETS && PyAnySet_CheckExact(so)) - free_sets[num_free_sets++] = so; + if (numfree < PySet_MAXFREELIST && PyAnySet_CheckExact(so)) + free_list[numfree++] = so; else Py_TYPE(so)->tp_free(so); Py_TRASHCAN_SAFE_END(so) @@ -975,9 +977,9 @@ make_new_set(PyTypeObject *type, PyObject *iterable) } /* create PySetObject structure */ - if (num_free_sets && + if (numfree && (type == &PySet_Type || type == &PyFrozenSet_Type)) { - so = free_sets[--num_free_sets]; + so = free_list[--numfree]; assert (so != NULL && PyAnySet_CheckExact(so)); Py_TYPE(so) = type; _Py_NewReference((PyObject *)so); @@ -1045,9 +1047,9 @@ PySet_Fini(void) { PySetObject *so; - while (num_free_sets) { - num_free_sets--; - so = free_sets[num_free_sets]; + while (numfree) { + numfree--; + so = free_list[numfree]; PyObject_GC_Del(so); } Py_CLEAR(dummy); diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index c9d91e5..c212345 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -4,19 +4,19 @@ #include "Python.h" /* Speed optimization to avoid frequent malloc/free of small tuples */ -#ifndef MAXSAVESIZE -#define MAXSAVESIZE 20 /* Largest tuple to save on free list */ +#ifndef PyTuple_MAXSAVESIZE +#define PyTuple_MAXSAVESIZE 20 /* Largest tuple to save on free list */ #endif -#ifndef MAXSAVEDTUPLES -#define MAXSAVEDTUPLES 2000 /* Maximum number of tuples of each size to save */ +#ifndef PyTuple_MAXFREELIST +#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */ #endif -#if MAXSAVESIZE > 0 -/* Entries 1 up to MAXSAVESIZE are free lists, entry 0 is the empty +#if PyTuple_MAXSAVESIZE > 0 +/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty tuple () of which at most one instance will be allocated. */ -static PyTupleObject *free_tuples[MAXSAVESIZE]; -static int num_free_tuples[MAXSAVESIZE]; +static PyTupleObject *free_list[PyTuple_MAXSAVESIZE]; +static int numfree[PyTuple_MAXSAVESIZE]; #endif #ifdef COUNT_ALLOCS int fast_tuple_allocs; @@ -32,18 +32,18 @@ PyTuple_New(register Py_ssize_t size) PyErr_BadInternalCall(); return NULL; } -#if MAXSAVESIZE > 0 - if (size == 0 && free_tuples[0]) { - op = free_tuples[0]; +#if PyTuple_MAXSAVESIZE > 0 + if (size == 0 && free_list[0]) { + op = free_list[0]; Py_INCREF(op); #ifdef COUNT_ALLOCS tuple_zero_allocs++; #endif return (PyObject *) op; } - if (size < MAXSAVESIZE && (op = free_tuples[size]) != NULL) { - free_tuples[size] = (PyTupleObject *) op->ob_item[0]; - num_free_tuples[size]--; + if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) { + free_list[size] = (PyTupleObject *) op->ob_item[0]; + numfree[size]--; #ifdef COUNT_ALLOCS fast_tuple_allocs++; #endif @@ -71,10 +71,10 @@ PyTuple_New(register Py_ssize_t size) } for (i=0; i < size; i++) op->ob_item[i] = NULL; -#if MAXSAVESIZE > 0 +#if PyTuple_MAXSAVESIZE > 0 if (size == 0) { - free_tuples[0] = op; - ++num_free_tuples[0]; + free_list[0] = op; + ++numfree[0]; Py_INCREF(op); /* extra INCREF so that this is never freed */ } #endif @@ -167,14 +167,14 @@ tupledealloc(register PyTupleObject *op) i = len; while (--i >= 0) Py_XDECREF(op->ob_item[i]); -#if MAXSAVESIZE > 0 - if (len < MAXSAVESIZE && - num_free_tuples[len] < MAXSAVEDTUPLES && +#if PyTuple_MAXSAVESIZE > 0 + if (len < PyTuple_MAXSAVESIZE && + numfree[len] < PyTuple_MAXFREELIST && Py_TYPE(op) == &PyTuple_Type) { - op->ob_item[0] = (PyObject *) free_tuples[len]; - num_free_tuples[len]++; - free_tuples[len] = op; + op->ob_item[0] = (PyObject *) free_list[len]; + numfree[len]++; + free_list[len] = op; goto done; /* return */ } #endif @@ -756,16 +756,16 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) void PyTuple_Fini(void) { -#if MAXSAVESIZE > 0 +#if PyTuple_MAXSAVESIZE > 0 int i; - Py_XDECREF(free_tuples[0]); - free_tuples[0] = NULL; + Py_XDECREF(free_list[0]); + free_list[0] = NULL; - for (i = 1; i < MAXSAVESIZE; i++) { + for (i = 1; i < PyTuple_MAXSAVESIZE; i++) { PyTupleObject *p, *q; - p = free_tuples[i]; - free_tuples[i] = NULL; + p = free_list[i]; + free_list[i] = NULL; while (p) { q = p; p = (PyTupleObject *)(p->ob_item[0]); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 1b35d4e..4f0de1e 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -54,7 +54,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* Limit for the Unicode object free list */ -#define MAX_UNICODE_FREELIST_SIZE 1024 +#define PyUnicode_MAXFREELIST 1024 /* Limit for the Unicode object free list stay alive optimization. @@ -62,7 +62,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. all objects on the free list having a size less than this limit. This reduces malloc() overhead for small Unicode objects. - At worst this will result in MAX_UNICODE_FREELIST_SIZE * + At worst this will result in PyUnicode_MAXFREELIST * (sizeof(PyUnicodeObject) + KEEPALIVE_SIZE_LIMIT + malloc()-overhead) bytes of unused garbage. @@ -106,8 +106,8 @@ extern "C" { static PyObject *interned; /* Free list for Unicode objects */ -static PyUnicodeObject *unicode_freelist; -static int unicode_freelist_size; +static PyUnicodeObject *free_list; +static int numfree; /* The empty Unicode object is shared to improve performance. */ static PyUnicodeObject *unicode_empty; @@ -313,10 +313,10 @@ PyUnicodeObject *_PyUnicode_New(Py_ssize_t length) } /* Unicode freelist & memory allocation */ - if (unicode_freelist) { - unicode = unicode_freelist; - unicode_freelist = *(PyUnicodeObject **)unicode; - unicode_freelist_size--; + if (free_list) { + unicode = free_list; + free_list = *(PyUnicodeObject **)unicode; + numfree--; if (unicode->str) { /* Keep-Alive optimization: we only upsize the buffer, never downsize it. */ @@ -386,7 +386,7 @@ void unicode_dealloc(register PyUnicodeObject *unicode) } if (PyUnicode_CheckExact(unicode) && - unicode_freelist_size < MAX_UNICODE_FREELIST_SIZE) { + numfree < PyUnicode_MAXFREELIST) { /* Keep-Alive optimization */ if (unicode->length >= KEEPALIVE_SIZE_LIMIT) { PyMem_DEL(unicode->str); @@ -398,9 +398,9 @@ void unicode_dealloc(register PyUnicodeObject *unicode) unicode->defenc = NULL; } /* Add to free list */ - *(PyUnicodeObject **)unicode = unicode_freelist; - unicode_freelist = unicode; - unicode_freelist_size++; + *(PyUnicodeObject **)unicode = free_list; + free_list = unicode; + numfree++; } else { PyMem_DEL(unicode->str); @@ -8033,7 +8033,7 @@ unicode_zfill(PyUnicodeObject *self, PyObject *args) static PyObject* unicode_freelistsize(PyUnicodeObject *self) { - return PyLong_FromLong(unicode_freelist_size); + return PyLong_FromLong(numfree); } #endif @@ -9090,8 +9090,8 @@ void _PyUnicode_Init(void) }; /* Init the implementation */ - unicode_freelist = NULL; - unicode_freelist_size = 0; + free_list = NULL; + numfree = 0; unicode_empty = _PyUnicode_New(0); if (!unicode_empty) return; @@ -9127,7 +9127,7 @@ _PyUnicode_Fini(void) } } - for (u = unicode_freelist; u != NULL;) { + for (u = free_list; u != NULL;) { PyUnicodeObject *v = u; u = *(PyUnicodeObject **)u; if (v->str) @@ -9135,8 +9135,8 @@ _PyUnicode_Fini(void) Py_XDECREF(v->defenc); PyObject_Del(v); } - unicode_freelist = NULL; - unicode_freelist_size = 0; + free_list = NULL; + numfree = 0; } void diff --git a/Python/compile.c b/Python/compile.c index b256198..35ec10a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -652,11 +652,16 @@ compiler_next_instr(struct compiler *c, basicblock *b) return b->b_iused++; } -/* Set the i_lineno member of the instruction at offse off if the - line number for the current expression/statement (?) has not +/* Set the i_lineno member of the instruction at offset off if the + line number for the current expression/statement has not already been set. If it has been set, the call has no effect. - Every time a new node is b + The line number is reset in the following cases: + - when entering a new scope + - on each statement + - on each expression that start a new line + - before the "except" clause + - before the "for" and "while" expressions */ static void @@ -1750,9 +1755,8 @@ compiler_for(struct compiler *c, stmt_ty s) VISIT(c, expr, s->v.For.iter); ADDOP(c, GET_ITER); compiler_use_next_block(c, start); - /* XXX(nnorwitz): is there a better way to handle this? - for loops are special, we want to be able to trace them - each time around, so we need to set an extra line number. */ + /* for expressions must be traced on each iteration, + so we need to set an extra line number. */ c->u->u_lineno_set = 0; ADDOP_JREL(c, FOR_ITER, cleanup); VISIT(c, expr, s->v.For.target); @@ -1799,6 +1803,9 @@ compiler_while(struct compiler *c, stmt_ty s) if (!compiler_push_fblock(c, LOOP, loop)) return 0; if (constant == -1) { + /* while expressions must be traced on each iteration, + so we need to set an extra line number. */ + c->u->u_lineno_set = 0; VISIT(c, expr, s->v.While.test); ADDOP_JREL(c, JUMP_IF_FALSE, anchor); ADDOP(c, POP_TOP); @@ -1979,8 +1986,8 @@ compiler_try_except(struct compiler *c, stmt_ty s) s->v.TryExcept.handlers, i); if (!handler->type && i < n-1) return compiler_error(c, "default 'except:' must be last"); - c->u->u_lineno_set = 0; - c->u->u_lineno = handler->lineno; + c->u->u_lineno_set = 0; + c->u->u_lineno = handler->lineno; except = compiler_new_block(c); if (except == NULL) return 0; @@ -3762,10 +3769,7 @@ assemble_lnotab(struct assembler *a, struct instr *i) assert(d_bytecode >= 0); assert(d_lineno >= 0); - /* XXX(nnorwitz): is there a better way to handle this? - for loops are special, we want to be able to trace them - each time around, so we need to set an extra line number. */ - if (d_lineno == 0 && i->i_opcode != FOR_ITER) + if(d_bytecode == 0 && d_lineno == 0) return 1; if (d_bytecode > 255) { |