Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3k Changeset: r87480:76f2e04a21be Date: 2016-09-30 19:30 +0100 http://bitbucket.org/pypy/pypy/changeset/76f2e04a21be/
Log: hg merge default diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -38,3 +38,17 @@ ``lib-python`` and ``lib_pypy``. Of course, you can put a symlink to it from somewhere else. You no longer have to do the same with the ``pypy`` executable, as long as it finds its ``libpypy-c.so`` library. + +.. branch: _warning + +CPython allows warning.warn(('something', 1), Warning), on PyPy this +produced a "expected a readable buffer object" error. Test and fix. + +.. branch: stricter-strip + +CPython rejects 'a'.strip(buffer(' ')); only None, str or unicode are +allowed as arguments. Test and fix for str and unicode + +.. branch: test-cpyext + +Refactor cpyext testing to be more pypy3-friendly. diff --git a/pypy/module/_warnings/interp_warnings.py b/pypy/module/_warnings/interp_warnings.py --- a/pypy/module/_warnings/interp_warnings.py +++ b/pypy/module/_warnings/interp_warnings.py @@ -248,6 +248,10 @@ if space.isinstance_w(w_message, space.w_Warning): w_text = space.str(w_message) w_category = space.type(w_message) + elif (not space.isinstance_w(w_message, space.w_unicode) or + not space.isinstance_w(w_message, space.w_str)): + w_text = space.str(w_message) + w_message = space.call_function(w_category, w_message) else: w_text = w_message w_message = space.call_function(w_category, w_message) diff --git a/pypy/module/_warnings/test/test_warnings.py b/pypy/module/_warnings/test/test_warnings.py --- a/pypy/module/_warnings/test/test_warnings.py +++ b/pypy/module/_warnings/test/test_warnings.py @@ -16,6 +16,7 @@ import _warnings _warnings.warn("some message", DeprecationWarning) _warnings.warn("some message", Warning) + _warnings.warn(("some message",1), Warning) def test_lineno(self): import warnings, _warnings, sys @@ -45,7 +46,10 @@ def test_show_source_line(self): import warnings import sys, io - from test.warning_tests import inner + try: + from test.warning_tests import inner + except ImportError: + skip('no test, -A on cpython?') # With showarning() missing, make sure that output is okay. del warnings.showwarning diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -214,7 +214,9 @@ i = space.int_w(space.index(args_w[0])) j = space.int_w(space.index(args_w[1])) w_y = args_w[2] - return space.wrap(generic_cpy_call(space, func_target, w_self, i, j, w_y)) + res = generic_cpy_call(space, func_target, w_self, i, j, w_y) + if rffi.cast(lltype.Signed, res) == -1: + space.fromcache(State).check_and_raise_exception(always=True) def wrap_lenfunc(space, w_self, w_args, func): func_len = rffi.cast(lenfunc, func) @@ -296,7 +298,10 @@ def wrap_hashfunc(space, w_self, w_args, func): func_target = rffi.cast(hashfunc, func) check_num_args(space, w_args, 0) - return space.wrap(generic_cpy_call(space, func_target, w_self)) + res = generic_cpy_call(space, func_target, w_self) + if res == -1: + space.fromcache(State).check_and_raise_exception(always=True) + return space.wrap(res) class CPyBuffer(Buffer): # Similar to Py_buffer diff --git a/pypy/module/cpyext/test/conftest.py b/pypy/module/cpyext/test/conftest.py --- a/pypy/module/cpyext/test/conftest.py +++ b/pypy/module/cpyext/test/conftest.py @@ -2,6 +2,8 @@ import pytest def pytest_configure(config): + if config.option.runappdirect: + return from pypy.tool.pytest.objspace import gettestobjspace # For some reason (probably a ll2ctypes cache issue on linux64) # it's necessary to run "import time" at least once before any diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -49,6 +49,7 @@ assert arr.tolist() == [1, 21, 22, 23, 4] del arr[slice(1, 3)] assert arr.tolist() == [1, 23, 4] + raises(TypeError, 'arr[slice(1, 3)] = "abc"') def test_buffer(self): import sys diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -5,7 +5,7 @@ import py, pytest from pypy import pypydir -from pypy.interpreter import gateway +from pypy.interpreter.gateway import unwrap_spec, interp2app from rpython.rtyper.lltypesystem import lltype, ll2ctypes from rpython.translator.gensupp import uniquemodulename from rpython.tool.udir import udir @@ -18,6 +18,7 @@ from .support import c_compile +HERE = py.path.local(pypydir) / 'module' / 'cpyext' / 'test' only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" @api.cpython_api([], api.PyObject) @@ -42,19 +43,6 @@ files.append(filename) return files -def create_so(modname, include_dirs, source_strings=None, source_files=None, - compile_extra=None, link_extra=None, libraries=None): - dirname = (udir/uniquemodulename('module')).ensure(dir=1) - if source_strings: - assert not source_files - files = convert_sources_to_files(source_strings, dirname) - source_files = files - soname = c_compile(source_files, outputfilename=str(dirname/modname), - compile_extra=compile_extra, link_extra=link_extra, - include_dirs=include_dirs, - libraries=libraries) - return soname - class SystemCompilationInfo(object): """Bundles all the generic information required to compile extensions. @@ -68,6 +56,96 @@ self.extra_libs = extra_libs self.ext = ext + def compile_extension_module(self, name, include_dirs=None, + source_files=None, source_strings=None): + """ + Build an extension module and return the filename of the resulting + native code file. + + name is the name of the module, possibly including dots if it is a + module inside a package. + + Any extra keyword arguments are passed on to ExternalCompilationInfo to + build the module (so specify your source with one of those). + """ + include_dirs = include_dirs or [] + modname = name.split('.')[-1] + dirname = (udir/uniquemodulename('module')).ensure(dir=1) + if source_strings: + assert not source_files + files = convert_sources_to_files(source_strings, dirname) + source_files = files + soname = c_compile(source_files, outputfilename=str(dirname/modname), + compile_extra=self.compile_extra, + link_extra=self.link_extra, + include_dirs=self.include_extra + include_dirs, + libraries=self.extra_libs) + pydname = soname.new(purebasename=modname, ext=self.ext) + soname.rename(pydname) + return str(pydname) + + def import_module(self, name, init=None, body='', filename=None, + include_dirs=None, PY_SSIZE_T_CLEAN=False): + """ + init specifies the overall template of the module. + + if init is None, the module source will be loaded from a file in this + test directory, give a name given by the filename parameter. + + if filename is None, the module name will be used to construct the + filename. + """ + name = name.encode() + if body or init: + body = body.encode() + if init is None: + init = "return PyModule_Create(&moduledef);" + else: + init = init.encode() + if init is not None: + code = make_source(name, init, body, PY_SSIZE_T_CLEAN) + kwds = dict(source_strings=[code]) + else: + assert not PY_SSIZE_T_CLEAN + if filename is None: + filename = name + filename = HERE / (filename + ".c") + kwds = dict(source_files=[filename]) + mod = self.compile_extension_module( + name, include_dirs=include_dirs, **kwds) + return self.load_module(mod, name) + + def import_extension(self, modname, functions, prologue="", + include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False): + body = prologue + make_methods(functions, modname) + init = """PyObject *mod = PyModule_Create(&moduledef);""" + if more_init: + init += more_init + init += "\nreturn mod;" + return self.import_module( + name=modname, init=init, body=body, include_dirs=include_dirs, + PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN) + + +class ExtensionCompiler(SystemCompilationInfo): + """Extension compiler for appdirect mode""" + def load_module(space, mod, name): + import imp + return imp.load_dynamic(name, mod) + +class SpaceCompiler(SystemCompilationInfo): + """Extension compiler for regular (untranslated PyPy) mode""" + def __init__(self, space, *args, **kwargs): + self.space = space + SystemCompilationInfo.__init__(self, *args, **kwargs) + + def load_module(self, mod, name): + space = self.space + api.load_extension_module(space, mod, name) + return space.getitem( + space.sys.get('modules'), space.wrap(name)) + + def get_cpyext_info(space): from pypy.module.imp.importing import get_so_extension state = space.fromcache(State) @@ -88,7 +166,7 @@ link_extra = ["-g"] else: compile_extra = link_extra = None - return SystemCompilationInfo( + return SpaceCompiler(space, include_extra=api.include_dirs, compile_extra=compile_extra, link_extra=link_extra, @@ -96,30 +174,6 @@ ext=get_so_extension(space)) -def compile_extension_module(sys_info, modname, include_dirs=[], - source_files=None, source_strings=None): - """ - Build an extension module and return the filename of the resulting native - code file. - - modname is the name of the module, possibly including dots if it is a module - inside a package. - - Any extra keyword arguments are passed on to ExternalCompilationInfo to - build the module (so specify your source with one of those). - """ - modname = modname.split('.')[-1] - soname = create_so(modname, - include_dirs=sys_info.include_extra + include_dirs, - source_files=source_files, - source_strings=source_strings, - compile_extra=sys_info.compile_extra, - link_extra=sys_info.link_extra, - libraries=sys_info.extra_libs) - pydname = soname.new(purebasename=modname, ext=sys_info.ext) - soname.rename(pydname) - return str(pydname) - def get_so_suffix(): from imp import get_suffixes, C_EXTENSION for suffix, mode, typ in get_suffixes(): @@ -142,12 +196,58 @@ "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"] link_extra = None ext = get_so_suffix() - return SystemCompilationInfo( + return ExtensionCompiler( include_extra=[get_python_inc()], compile_extra=compile_extra, link_extra=link_extra, ext=get_so_suffix()) +def make_methods(functions, modname): + methods_table = [] + codes = [] + for funcname, flags, code in functions: + cfuncname = "%s_%s" % (modname, funcname) + methods_table.append("{\"%s\", %s, %s}," % + (funcname, cfuncname, flags)) + func_code = """ + static PyObject* %s(PyObject* self, PyObject* args) + { + %s + } + """ % (cfuncname, code) + codes.append(func_code) + + body = "\n".join(codes) + """ + static PyMethodDef methods[] = { + %(methods)s + { NULL } + }; + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "%(modname)s", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + methods, /* m_methods */ + }; + """ % dict(methods='\n'.join(methods_table), modname=modname) + return body + +def make_source(name, init, body, PY_SSIZE_T_CLEAN): + code = """ + %(PY_SSIZE_T_CLEAN)s + #include <Python.h> + + %(body)s + + PyMODINIT_FUNC + PyInit_%(name)s(void) { + %(init)s + } + """ % dict(name=name, init=init, body=body, + PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN' + if PY_SSIZE_T_CLEAN else '') + return code + def freeze_refcnts(self): rawrefcount._dont_free_any_more() @@ -159,22 +259,6 @@ #state.print_refcounts() self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values()) -class FakeSpace(object): - """Like TinyObjSpace, but different""" - def __init__(self, config): - self.config = config - - def passthrough(self, arg): - return arg - listview = passthrough - str_w = passthrough - - def unwrap(self, args): - try: - return args.str_w(None) - except: - return args - class LeakCheckingTest(object): """Base class for all cpyext tests.""" spaceconfig = dict(usemodules=['cpyext', 'thread', '_rawffi', 'array', @@ -267,9 +351,12 @@ state.non_heaptypes_w[:] = [] def setup_method(self, meth): - freeze_refcnts(self) + if not self.runappdirect: + freeze_refcnts(self) def teardown_method(self, meth): + if self.runappdirect: + return self.cleanup_references(self.space) # XXX: like AppTestCpythonExtensionBase.teardown_method: # find out how to disable check_and_print_leaks() if the @@ -295,21 +382,42 @@ skip("Windows Python >= 2.6 only") assert isinstance(sys.dllhandle, int) + +def _unwrap_include_dirs(space, w_include_dirs): + if w_include_dirs is None: + return None + else: + return [space.str_w(s) for s in space.listview(w_include_dirs)] + +def debug_collect(space): + rawrefcount._collect() + class AppTestCpythonExtensionBase(LeakCheckingTest): def setup_class(cls): space = cls.space - space.getbuiltinmodule("cpyext") - # 'import os' to warm up reference counts - w_import = space.builtin.getdictvalue(space, '__import__') - space.call_function(w_import, space.wrap("os")) - #state = cls.space.fromcache(RefcountState) ZZZ - #state.non_heaptypes_w[:] = [] + cls.w_here = space.wrap(str(HERE)) if not cls.runappdirect: cls.w_runappdirect = space.wrap(cls.runappdirect) + space.getbuiltinmodule("cpyext") + # 'import os' to warm up reference counts + w_import = space.builtin.getdictvalue(space, '__import__') + space.call_function(w_import, space.wrap("os")) + #state = cls.space.fromcache(RefcountState) ZZZ + #state.non_heaptypes_w[:] = [] + cls.w_debug_collect = space.wrap(interp2app(debug_collect)) + + def record_imported_module(self, name): + """ + Record a module imported in a test so that it can be cleaned up in + teardown before the check for leaks is done. + + name gives the name of the module in the space's sys.modules. + """ + self.imported_module_names.append(name) def setup_method(self, func): - @gateway.unwrap_spec(name=str) + @unwrap_spec(name=str) def compile_module(space, name, w_source_files=None, w_source_strings=None): @@ -324,178 +432,68 @@ source_strings = space.listview_bytes(w_source_strings) else: source_strings = None - pydname = compile_extension_module( - self.sys_info, name, + pydname = self.sys_info.compile_extension_module( + name, source_files=source_files, source_strings=source_strings) + + # hackish, but tests calling compile_module() always end up + # importing the result + self.record_imported_module(name) + return space.wrap(pydname) - @gateway.unwrap_spec(name=str, init='str_or_None', body=str, - load_it=bool, filename='str_or_None', - PY_SSIZE_T_CLEAN=bool) - def import_module(space, name, init=None, body='', load_it=True, + @unwrap_spec(name=str, init='str_or_None', body=str, + filename='str_or_None', PY_SSIZE_T_CLEAN=bool) + def import_module(space, name, init=None, body='', filename=None, w_include_dirs=None, PY_SSIZE_T_CLEAN=False): - """ - init specifies the overall template of the module. + include_dirs = _unwrap_include_dirs(space, w_include_dirs) + w_result = self.sys_info.import_module( + name, init, body, filename, include_dirs, PY_SSIZE_T_CLEAN) + self.record_imported_module(name) + return w_result - if init is None, the module source will be loaded from a file in this - test direcory, give a name given by the filename parameter. - if filename is None, the module name will be used to construct the - filename. - """ - name = name.encode() - if body or init: - body = body.encode() - if init is None: - init = "return PyModule_Create(&moduledef);" - else: - init = init.encode() - if w_include_dirs is None: - include_dirs = [] - else: - include_dirs = [space.str_w(s) for s in space.listview(w_include_dirs)] - if init is not None: - code = """ - %(PY_SSIZE_T_CLEAN)s - #include <Python.h> + @unwrap_spec(mod=str, name=str) + def load_module(space, mod, name): + return self.sys_info.load_module(mod, name) - %(body)s - - PyMODINIT_FUNC - PyInit_%(name)s(void) { - %(init)s - } - """ % dict(name=name, init=init, body=body, - PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN' - if PY_SSIZE_T_CLEAN else '') - kwds = dict(source_strings=[code]) - else: - assert not PY_SSIZE_T_CLEAN - if filename is None: - filename = name - filename = py.path.local(pypydir) / 'module' \ - / 'cpyext'/ 'test' / (filename + ".c") - kwds = dict(source_files=[filename]) - mod = compile_extension_module(self.sys_info, name, - include_dirs=include_dirs, **kwds) - - if load_it: - if self.runappdirect: - import imp - return imp.load_dynamic(name, mod) - else: - api.load_extension_module(space, mod, name) - self.imported_module_names.append(name) - return space.getitem( - space.sys.get('modules'), - space.wrap(name)) - else: - path = os.path.dirname(mod) - if self.runappdirect: - return path - else: - return space.wrap(path) - - @gateway.unwrap_spec(mod=str, name=str) - def reimport_module(space, mod, name): - if self.runappdirect: - import imp - return imp.load_dynamic(name, mod) - else: - api.load_extension_module(space, mod, name) - return space.getitem( - space.sys.get('modules'), - space.wrap(name)) - - @gateway.unwrap_spec(modname=str, prologue=str, + @unwrap_spec(modname=str, prologue=str, more_init=str, PY_SSIZE_T_CLEAN=bool) def import_extension(space, modname, w_functions, prologue="", w_include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False): functions = space.unwrap(w_functions) - methods_table = [] - codes = [] - for funcname, flags, code in functions: - cfuncname = "%s_%s" % (modname, funcname) - methods_table.append("{\"%s\", %s, %s}," % - (funcname, cfuncname, flags)) - func_code = """ - static PyObject* %s(PyObject* self, PyObject* args) - { - %s - } - """ % (cfuncname, code) - codes.append(func_code) - - body = prologue + "\n".join(codes) + """ - static PyMethodDef methods[] = { - %(methods)s - { NULL } - }; - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "%(modname)s", /* m_name */ - NULL, /* m_doc */ - -1, /* m_size */ - methods, /* m_methods */ - }; - """ % dict(methods='\n'.join(methods_table), modname=modname) - init = """PyObject *mod = PyModule_Create(&moduledef);""" - if more_init: - init += more_init - init += "\nreturn mod;" - return import_module(space, name=modname, init=init, body=body, - w_include_dirs=w_include_dirs, - PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN) - - @gateway.unwrap_spec(name=str) - def record_imported_module(name): - """ - Record a module imported in a test so that it can be cleaned up in - teardown before the check for leaks is done. - - name gives the name of the module in the space's sys.modules. - """ - self.imported_module_names.append(name) - - def debug_collect(space): - rawrefcount._collect() + include_dirs = _unwrap_include_dirs(space, w_include_dirs) + w_result = self.sys_info.import_extension( + modname, functions, prologue, include_dirs, more_init, + PY_SSIZE_T_CLEAN) + self.record_imported_module(modname) + return w_result # A list of modules which the test caused to be imported (in # self.space). These will be cleaned up automatically in teardown. self.imported_module_names = [] if self.runappdirect: - fake = FakeSpace(self.space.config) - def interp2app(func): - def run(*args, **kwargs): - for k in kwargs.keys(): - if k not in func.unwrap_spec and not k.startswith('w_'): - v = kwargs.pop(k) - kwargs['w_' + k] = v - return func(fake, *args, **kwargs) - return run - def wrap(func): - return func self.sys_info = get_sys_info_app() + self.compile_module = self.sys_info.compile_extension_module + self.load_module = self.sys_info.load_module + self.import_module = self.sys_info.import_module + self.import_extension = self.sys_info.import_extension else: - interp2app = gateway.interp2app wrap = self.space.wrap self.sys_info = get_cpyext_info(self.space) - self.w_compile_module = wrap(interp2app(compile_module)) - self.w_import_module = wrap(interp2app(import_module)) - self.w_reimport_module = wrap(interp2app(reimport_module)) - self.w_import_extension = wrap(interp2app(import_extension)) - self.w_record_imported_module = wrap(interp2app(record_imported_module)) - self.w_here = wrap(str(py.path.local(pypydir)) + '/module/cpyext/test/') - self.w_debug_collect = wrap(interp2app(debug_collect)) + self.w_compile_module = wrap(interp2app(compile_module)) + self.w_load_module = wrap(interp2app(load_module)) + self.w_import_module = wrap(interp2app(import_module)) + self.w_import_extension = wrap(interp2app(import_extension)) - # create the file lock before we count allocations - self.space.call_method(self.space.sys.get("stdout"), "flush") + # create the file lock before we count allocations + self.space.call_method(self.space.sys.get("stdout"), "flush") - freeze_refcnts(self) - #self.check_and_print_leaks() + freeze_refcnts(self) + #self.check_and_print_leaks() def unimport_module(self, name): """ @@ -506,6 +504,8 @@ self.space.delitem(w_modules, w_name) def teardown_method(self, func): + if self.runappdirect: + return for name in self.imported_module_names: self.unimport_module(name) self.cleanup_references(self.space) @@ -628,19 +628,15 @@ If `cherry.date` is an extension module which imports `apple.banana`, the latter is added to `sys.modules` for the `"apple.banana"` key. """ - if self.runappdirect: - skip('record_imported_module not supported in runappdirect mode') + import sys, types, os # Build the extensions. banana = self.compile_module( - "apple.banana", source_files=[self.here + 'banana.c']) - self.record_imported_module("apple.banana") + "apple.banana", source_files=[os.path.join(self.here, 'banana.c')]) date = self.compile_module( - "cherry.date", source_files=[self.here + 'date.c']) - self.record_imported_module("cherry.date") + "cherry.date", source_files=[os.path.join(self.here, 'date.c')]) # Set up some package state so that the extensions can actually be # imported. - import sys, types, os cherry = sys.modules['cherry'] = types.ModuleType('cherry') cherry.__path__ = [os.path.dirname(date)] @@ -648,7 +644,6 @@ apple.__path__ = [os.path.dirname(banana)] import cherry.date - import apple.banana assert sys.modules['apple.banana'].__name__ == 'apple.banana' assert sys.modules['cherry.date'].__name__ == 'cherry.date' @@ -1004,7 +999,7 @@ f.write('not again!\n') f.close() m1 = sys.modules['foo'] - m2 = self.reimport_module(m1.__file__, name='foo') + m2 = self.load_module(m1.__file__, name='foo') assert m1 is m2 assert m1 is sys.modules['foo'] diff --git a/pypy/module/cpyext/test/test_import.py b/pypy/module/cpyext/test/test_import.py --- a/pypy/module/cpyext/test/test_import.py +++ b/pypy/module/cpyext/test/test_import.py @@ -1,6 +1,6 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase -from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rtyper.lltypesystem import rffi class TestImport(BaseApiTest): def test_import(self, space, api): @@ -39,9 +39,9 @@ class AppTestImportLogic(AppTestCpythonExtensionBase): def test_import_logic(self): - path = self.import_module(name='test_import_module', load_it=False) - import sys - sys.path.append(path) + import sys, os + path = self.compile_module('test_import_module', + source_files=[os.path.join(self.here, 'test_import_module.c')]) + sys.path.append(os.path.dirname(path)) import test_import_module assert test_import_module.TEST is None - diff --git a/pypy/objspace/std/test/test_bytesobject.py b/pypy/objspace/std/test/test_bytesobject.py --- a/pypy/objspace/std/test/test_bytesobject.py +++ b/pypy/objspace/std/test/test_bytesobject.py @@ -263,6 +263,12 @@ assert b'xyzzyhelloxyzzy'.strip(b'xyz') == b'hello' assert b'xyzzyhelloxyzzy'.lstrip(b'xyz') == b'helloxyzzy' assert b'xyzzyhelloxyzzy'.rstrip(b'xyz') == b'xyzzyhello' + exc = raises(TypeError, s.strip, buffer(' ')) + assert str(exc.value) == 'strip arg must be None, str or unicode' + exc = raises(TypeError, s.rstrip, buffer(' ')) + assert str(exc.value) == 'strip arg must be None, str or unicode' + exc = raises(TypeError, s.lstrip, buffer(' ')) + assert str(exc.value) == 'strip arg must be None, str or unicode' def test_zfill(self): assert b'123'.zfill(2) == b'123' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit