diff --git a/setup.py b/setup.py index 871361f..4469f74 100644 --- a/setup.py +++ b/setup.py @@ -103,7 +103,7 @@ def header_dirs_for_dep(distname, headername): # 3 and 4 ] -class IncludeDirs(object): +class IncludeDirs: dirs = None def __getattribute__(self, name): @@ -118,19 +118,14 @@ def __iter__(self): return iter(self.dirs) -if str is bytes and hasattr(sys, 'pypy_version_info'): - # zope.proxy, as of 4.3.5, cannot compile on PyPy2 7.3.0 - # because it uses cl_dict in a PyClassObject, which does not exist. - ext_modules = [] -else: - ext_modules = [ - Extension( - "zope.container._zope_container_contained", - [os.path.join("src", "zope", "container", - "_zope_container_contained.c")], - include_dirs=IncludeDirs(), - ), - ] +ext_modules = [ + Extension( + "zope.container._zope_container_contained", + [os.path.join("src", "zope", "container", + "_zope_container_contained.c")], + include_dirs=IncludeDirs(), + ), +] setup_requires = [ 'persistent >= 4.1.0', @@ -140,7 +135,6 @@ def __iter__(self): install_requires = setup_requires + [ 'BTrees', - 'six', 'zope.cachedescriptors', 'zope.component', 'zope.deferredimport', @@ -199,11 +193,7 @@ def __iter__(self): 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', @@ -234,12 +224,5 @@ def __iter__(self): tests_require=extras['test'], include_package_data=True, zip_safe=False, - python_requires=', '.join([ - '>=2.7', - '!=3.0.*', - '!=3.1.*', - '!=3.2.*', - '!=3.3.*', - '!=3.4.*', - ]), + python_requires='>=3.7', ) diff --git a/src/zope/container/_compat.py b/src/zope/container/_compat.py deleted file mode 100644 index 9e9c417..0000000 --- a/src/zope/container/_compat.py +++ /dev/null @@ -1,6 +0,0 @@ -try: - # PY2 - text_type = unicode -except NameError: - # PY3 - text_type = str diff --git a/src/zope/container/_proxy.py b/src/zope/container/_proxy.py index 62b7f75..aa7c41e 100644 --- a/src/zope/container/_proxy.py +++ b/src/zope/container/_proxy.py @@ -36,13 +36,13 @@ class ContainedProxyBase(AbstractPyProxyBase, Persistent): __slots__ = ('_wrapped', '__parent__', '__name__', '__weakref__') def __new__(cls, obj): - inst = super(ContainedProxyBase, cls).__new__(cls, obj) + inst = super().__new__(cls, obj) inst.__parent__ = None inst.__name__ = None return inst def __init__(self, obj): - super(ContainedProxyBase, self).__init__(obj) + super().__init__(obj) self.__parent__ = None self.__name__ = None @@ -94,7 +94,7 @@ def __getattribute__(self, name): ): return object.__getattribute__(self, name) - return super(ContainedProxyBase, self).__getattribute__(name) + return super().__getattribute__(name) def __setattr__(self, name, value): if _special_name(name): @@ -103,7 +103,7 @@ def __setattr__(self, name, value): # themselves return Persistent.__setattr__(self, name, value) - return super(ContainedProxyBase, self).__setattr__(name, value) + return super().__setattr__(name, value) @use_c_impl diff --git a/src/zope/container/_util.py b/src/zope/container/_util.py index 4fe98f4..775c0dd 100644 --- a/src/zope/container/_util.py +++ b/src/zope/container/_util.py @@ -157,19 +157,9 @@ def find_impl(): if not isinstance(v, types.FunctionType): continue - # Somewhat surprisingly, on Python 2, while - # ``Class.function`` results in a - # ``types.UnboundMethodType`` (``instancemethed``) object, - # ``Class.__dict__["function"]`` returns a - # ``types.FunctionType``, just like ``Class.function`` - # (and the dictionary access, of course) does on Python 3. - # The upshot is, we don't need different version-dependent - # code. Hooray! if new_globals is None: new_globals = v.__globals__.copy() new_globals[py_impl.__name__] = py_impl - # On Python 2, all arguments are optional, but an Python 3, all - # are required. v = types.FunctionType( v.__code__, new_globals, diff --git a/src/zope/container/_zope_container_contained.c b/src/zope/container/_zope_container_contained.c index d21f741..7794c60 100644 --- a/src/zope/container/_zope_container_contained.c +++ b/src/zope/container/_zope_container_contained.c @@ -78,13 +78,8 @@ typedef struct { /* Incude the proxy C source */ #include "_zope_proxy_proxy.c" -#ifdef PY3K - #define MAKE_PYSTRING(s) PyUnicode_FromString(s) - #define MAKE_STRING(name) PyBytes_AS_STRING(PyUnicode_AsUTF8String(name)) -#else - #define MAKE_PYSTRING(s) PyString_FromString(s) - #define MAKE_STRING(name) PyString_AS_STRING(name) -#endif +#define MAKE_PYSTRING(s) PyUnicode_FromString(s) +#define MAKE_STRING(name) PyBytes_AS_STRING(PyUnicode_AsUTF8String(name)) #define SPECIAL(NAME) ( \ *(NAME) == '_' && \ @@ -316,13 +311,8 @@ MOD_INIT(_zope_container_contained) empty_tuple = PyTuple_New(0); /* Initialize the PyPersist_C_API and the type objects. */ -#ifdef PY3K - cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import( - "persistent.cPersistence.CAPI", 0); -#else - cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCObject_Import( - "persistent.cPersistence", "CAPI"); -#endif + cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import( + "persistent.cPersistence.CAPI", 0); if (cPersistenceCAPI == NULL) return MOD_ERROR_VAL; diff --git a/src/zope/container/constraints.py b/src/zope/container/constraints.py index 2020df2..1c07731 100644 --- a/src/zope/container/constraints.py +++ b/src/zope/container/constraints.py @@ -47,7 +47,7 @@ >>> checkObject(c1, "Zbob", None) Traceback (most recent call last): ... - Invalid: Names can not start with Z + zope.interface.exceptions.Invalid: Names can not start with Z We can also express constaints on the containers an object can be added to. We do this by setting a field constraint on an object's @@ -76,7 +76,7 @@ >>> checkObject(c1, "bob", O()) Traceback (most recent call last): ... - ConstraintNotSatisfied: (C1, '__parent__') + zope.schema._bootstrapinterfaces.ConstraintNotSatisfied: (C1, '__parent__') Note that the validation error isn't very informative. For that reason, it's better for constraints to raise Invalid errors when they @@ -98,7 +98,7 @@ >>> checkObject(c1, "bob", O()) Traceback (most recent call last): ... - Invalid: What, no x? + zope.interface.exceptions.Invalid: What, no x? >>> c1.x = 1 >>> checkObject(c1, "bob", O()) @@ -246,7 +246,7 @@ def factory(container, name, factory): """ -class _TypesBased(object): +class _TypesBased: @readproperty def types(self): diff --git a/src/zope/container/constraints.rst b/src/zope/container/constraints.rst index 07beb99..4cbd83e 100644 --- a/src/zope/container/constraints.rst +++ b/src/zope/container/constraints.rst @@ -59,7 +59,7 @@ If we try to use other containers or folders, we'll get errors: >>> checkObject(Container(), 'x', Buddy()) ... # doctest: +ELLIPSIS Traceback (most recent call last): - InvalidContainerType: ... + zope.container.interfaces.InvalidContainerType: ... >>> checkFactory(Container(), 'x', Factory(Buddy)) False @@ -67,7 +67,7 @@ If we try to use other containers or folders, we'll get errors: >>> checkObject(BuddyFolder(), 'x', Contained()) ... # doctest: +ELLIPSIS Traceback (most recent call last): - InvalidItemType: ... + zope.container.interfaces.InvalidItemType: ... >>> checkFactory(BuddyFolder(), 'x', Factory(Contained)) False @@ -90,7 +90,7 @@ could have defined these in the opposite order: >>> checkObject(Contacts(), 'x', Buddy()) ... # doctest: +ELLIPSIS Traceback (most recent call last): - InvalidItemType: ... + zope.container.interfaces.InvalidItemType: ... >>> checkFactory(Contacts(), 'x', Factory(Buddy)) False diff --git a/src/zope/container/contained.py b/src/zope/container/contained.py index 8ab95cd..f1aa61a 100644 --- a/src/zope/container/contained.py +++ b/src/zope/container/contained.py @@ -14,7 +14,6 @@ """Classes to support implementing `IContained` """ # pylint:disable=too-many-lines -from six import text_type import zope.component from zope.event import notify @@ -50,7 +49,7 @@ class IBroken(Interface): # pylint:disable=inherit-non-class @zope.interface.implementer(IContained) -class Contained(object): +class Contained: """ Simple mix-in that defines ``__parent__`` and ``__name__`` attributes and implements `IContained`. @@ -157,7 +156,7 @@ def dispatchToSublocations(object, event): # pylint:disable=redefined-builtin zope.component.handle(sub, event) -class ContainerSublocations(object): +class ContainerSublocations: """Get the sublocations for a container Obviously, this is the container values: @@ -200,7 +199,7 @@ def containedEvent(object, container, name=None): >>> container = {} >>> item = Contained() - >>> x, event = containedEvent(item, container, u'foo') + >>> x, event = containedEvent(item, container, 'foo') >>> x is item True >>> item.__parent__ is container @@ -223,7 +222,7 @@ def containedEvent(object, container, name=None): Now if we call contained again: - >>> x2, event = containedEvent(item, container, u'foo') + >>> x2, event = containedEvent(item, container, 'foo') >>> x2 is item True >>> item.__parent__ is container @@ -238,7 +237,7 @@ def containedEvent(object, container, name=None): If the object already had a parent but the parent or name was different, we get a moved event: - >>> x, event = containedEvent(item, container, u'foo2') + >>> x, event = containedEvent(item, container, 'foo2') >>> event.__class__.__name__ 'ObjectMovedEvent' >>> event.object is item @@ -353,7 +352,7 @@ def checkAndConvertName(name): name = name.decode('ascii') except UnicodeError: raise TypeError("name not unicode or ascii string") - elif not isinstance(name, text_type): + elif not isinstance(name, str): raise TypeError("name not unicode or ascii string") if not name: @@ -391,8 +390,8 @@ def setitem(container, setitemf, name, object): >>> item = Item() >>> container = {} - >>> setitem(container, container.__setitem__, u'c', item) - >>> container[u'c'] is item + >>> setitem(container, container.__setitem__, 'c', item) + >>> container['c'] is item True >>> item.__parent__ is container True @@ -438,7 +437,7 @@ def setitem(container, setitemf, name, object): >>> item = Item() >>> item.__parent__, item.__name__ = container, 'c2' - >>> setitem(container, container.__setitem__, u'c2', item) + >>> setitem(container, container.__setitem__, 'c2', item) >>> len(container) 2 >>> len(getEvents(IObjectAddedEvent)) @@ -452,7 +451,7 @@ def setitem(container, setitemf, name, object): If the item had a parent or name (as in a move or rename), we generate a move event, rather than an add event: - >>> setitem(container, container.__setitem__, u'c3', item) + >>> setitem(container, container.__setitem__, 'c3', item) >>> len(container) 3 >>> len(getEvents(IObjectAddedEvent)) @@ -474,14 +473,14 @@ def setitem(container, setitemf, name, object): If we try to replace an item without deleting it first, we'll get an error: - >>> setitem(container, container.__setitem__, u'c', []) + >>> setitem(container, container.__setitem__, 'c', []) Traceback (most recent call last): ... - KeyError: u'c' + KeyError: 'c' - >>> del container[u'c'] - >>> setitem(container, container.__setitem__, u'c', []) + >>> del container['c'] + >>> setitem(container, container.__setitem__, 'c', []) >>> len(getEvents(IObjectAddedEvent)) 2 >>> len(getEvents(IObjectModifiedEvent)) @@ -496,13 +495,13 @@ def setitem(container, setitemf, name, object): >>> item = Location() >>> IContained.providedBy(item) 0 - >>> setitem(container, container.__setitem__, u'l', item) - >>> container[u'l'] is item + >>> setitem(container, container.__setitem__, 'l', item) + >>> container['l'] is item 1 >>> item.__parent__ is container 1 >>> item.__name__ - u'l' + 'l' >>> IContained.providedBy(item) 1 @@ -517,16 +516,16 @@ def setitem(container, setitemf, name, object): `ContainedProxy` around it: >>> item = [] - >>> setitem(container, container.__setitem__, u'i', item) - >>> container[u'i'] + >>> setitem(container, container.__setitem__, 'i', item) + >>> container['i'] [] - >>> container[u'i'] is item + >>> container['i'] is item 0 - >>> item = container[u'i'] + >>> item = container['i'] >>> item.__parent__ is container 1 >>> item.__name__ - u'i' + 'i' >>> IContained.providedBy(item) 1 @@ -559,7 +558,7 @@ def setitem(container, setitemf, name, object): ... ValueError: empty names are not allowed - >>> setitem(container, container.__setitem__, u'', item) + >>> setitem(container, container.__setitem__, '', item) Traceback (most recent call last): ... ValueError: empty names are not allowed @@ -601,16 +600,16 @@ def uncontained(object, container, name=None): ... pass >>> item = Item() - >>> container = {u'foo': item} - >>> x, event = containedEvent(item, container, u'foo') + >>> container = {'foo': item} + >>> x, event = containedEvent(item, container, 'foo') >>> item.__parent__ is container 1 >>> item.__name__ - u'foo' + 'foo' Now we'll remove the item. It's parent and name are cleared: - >>> uncontained(item, container, u'foo') + >>> uncontained(item, container, 'foo') >>> item.__parent__ >>> item.__name__ @@ -624,7 +623,7 @@ def uncontained(object, container, name=None): >>> event.oldParent is container 1 >>> event.oldName - u'foo' + 'foo' >>> event.newParent >>> event.newName @@ -637,7 +636,7 @@ def uncontained(object, container, name=None): Now if we call uncontained again: - >>> uncontained(item, container, u'foo') + >>> uncontained(item, container, 'foo') We won't get any new events, because __parent__ and __name__ are None: @@ -651,14 +650,14 @@ def uncontained(object, container, name=None): event. >>> item.__parent__, item.__name__ = container, None - >>> uncontained(item, container, u'foo') + >>> uncontained(item, container, 'foo') >>> len(getEvents(IObjectRemovedEvent)) 1 >>> len(getEvents(IObjectModifiedEvent)) 2 - >>> item.__parent__, item.__name__ = None, u'bar' - >>> uncontained(item, container, u'foo') + >>> item.__parent__, item.__name__ = None, 'bar' + >>> uncontained(item, container, 'foo') >>> len(getEvents(IObjectRemovedEvent)) 1 >>> len(getEvents(IObjectModifiedEvent)) @@ -670,10 +669,10 @@ def uncontained(object, container, name=None): >>> class Broken(object): ... __Broken_state__ = {} >>> broken = Broken() - >>> broken.__Broken_state__['__name__'] = u'bar' + >>> broken.__Broken_state__['__name__'] = 'bar' >>> broken.__Broken_state__['__parent__'] = container - >>> container[u'bar'] = broken - >>> uncontained(broken, container, u'bar') + >>> container['bar'] = broken + >>> uncontained(broken, container, 'bar') >>> len(getEvents(IObjectRemovedEvent)) 2 @@ -709,7 +708,7 @@ def uncontained(object, container, name=None): @zope.interface.implementer(INameChooser) -class NameChooser(object): +class NameChooser: def __init__(self, context): self.context = context @@ -736,14 +735,14 @@ def checkName(self, name, object): # pylint:disable=redefined-builtin >>> NameChooser(container).checkName('foo', object()) Traceback (most recent call last): ... - KeyError: u'The given name is already being used' + KeyError: 'The given name is already being used' A name must be a string or unicode string: >>> NameChooser(container).checkName(2, object()) Traceback (most recent call last): ... - TypeError: ('Invalid name type', ) + TypeError: ('Invalid name type', ) A correct name returns True: @@ -766,12 +765,12 @@ def checkName(self, name, object): # pylint:disable=redefined-builtin >>> NameChooser(container).checkName('reserved', None) Traceback (most recent call last): ... - NameReserved: reserved + zope.container.interfaces.NameReserved: reserved """ if isinstance(name, bytes): name = name.decode('ascii') - elif not isinstance(name, text_type): + elif not isinstance(name, str): raise TypeError("Invalid name type", type(name)) if not name: @@ -811,27 +810,27 @@ def chooseName(self, name, object): # pylint:disable=redefined-builtin the suggested name is converted to unicode: - >>> NameChooser(container).chooseName(u'foobar', object()) - u'foobar' + >>> NameChooser(container).chooseName('foobar', object()) + 'foobar' >>> NameChooser(container).chooseName(b'foobar', object()) - u'foobar' + 'foobar' If it already exists, a number is appended but keeps the same extension: >>> NameChooser(container).chooseName('foobar.old', object()) - u'foobar-2.old' + 'foobar-2.old' Bad characters are turned into dashes: >>> NameChooser(container).chooseName('foo/foo', object()) - u'foo-foo' + 'foo-foo' If no name is suggested, it is based on the object type: >>> NameChooser(container).chooseName('', []) - u'list' + 'list' """ @@ -841,11 +840,11 @@ def chooseName(self, name, object): # pylint:disable=redefined-builtin # allow if isinstance(name, bytes): name = name.decode('ascii') - if not isinstance(name, text_type): + if not isinstance(name, str): try: - name = text_type(name) + name = str(name) except Exception: - name = u'' + name = '' name = name.replace('/', '-').lstrip('+@') if not name: @@ -866,7 +865,7 @@ def chooseName(self, name, object): # pylint:disable=redefined-builtin i = 1 while n in container: i += 1 - n = name + u'-' + str(i) + suffix + n = name + '-' + str(i) + suffix # Make sure the name is valid. We may have started with something bad. self.checkName(n, object) @@ -935,7 +934,7 @@ def __get__(self, inst, cls=None): return Provides(cls, provided) -class DecoratedSecurityCheckerDescriptor(object): +class DecoratedSecurityCheckerDescriptor: """ Descriptor for a Decorator that provides a decorated security checker. diff --git a/src/zope/container/directory.py b/src/zope/container/directory.py index 540bf3e..fa64a70 100644 --- a/src/zope/container/directory.py +++ b/src/zope/container/directory.py @@ -23,7 +23,6 @@ """ __docformat__ = 'restructuredtext' -from six.moves import map import zope.filerepresentation.interfaces from zope.component.interfaces import ISite @@ -46,7 +45,7 @@ def noop(container): @implementer(zope.filerepresentation.interfaces.IDirectoryFactory) -class Cloner(object): +class Cloner: """ `IContainer` to :class:`zope.filerepresentation.interfaces.IDirectoryFactory` adapter @@ -71,7 +70,7 @@ def __call__(self, name): return removeSecurityProxy(self.context).__class__() -class RootDirectoryFactory(object): +class RootDirectoryFactory: def __init__(self, context): pass @@ -80,7 +79,7 @@ def __call__(self, name): return Folder() -class ReadDirectory(object): +class ReadDirectory: """Adapter to provide a file-system rendition of folders.""" def __init__(self, context): diff --git a/src/zope/container/find.py b/src/zope/container/find.py index 780e2fe..b585b5e 100644 --- a/src/zope/container/find.py +++ b/src/zope/container/find.py @@ -24,7 +24,7 @@ @implementer(IFind) -class FindAdapter(object): +class FindAdapter: """Adapts :class:`zope.container.interfaces.IReadContainer`""" __used_for__ = IReadContainer @@ -68,7 +68,7 @@ def _find_helper(id, object, container, id_filters, object_filters, result): @implementer(IIdFindFilter) -class SimpleIdFindFilter(object): +class SimpleIdFindFilter: """Filter objects by ID""" def __init__(self, ids): @@ -80,7 +80,7 @@ def matches(self, id): @implementer(IObjectFindFilter) -class SimpleInterfacesFindFilter(object): +class SimpleInterfacesFindFilter: """Filter objects on the provided interfaces""" def __init__(self, *interfaces): diff --git a/src/zope/container/folder.py b/src/zope/container/folder.py index 6cce846..aced5de 100644 --- a/src/zope/container/folder.py +++ b/src/zope/container/folder.py @@ -35,7 +35,7 @@ def data(self, value): self._SampleContainer__data = value def __getstate__(self): - state = super(Folder, self).__getstate__() + state = super().__getstate__() data = state.pop('_SampleContainer__data') state['data'] = data return state @@ -43,4 +43,4 @@ def __getstate__(self): def __setstate__(self, state): if 'data' in state and '_SampleContainer__data' not in state: state['_SampleContainer__data'] = state.pop('data') - super(Folder, self).__setstate__(state) + super().__setstate__(state) diff --git a/src/zope/container/interfaces.py b/src/zope/container/interfaces.py index 304c9e3..86bfa4e 100644 --- a/src/zope/container/interfaces.py +++ b/src/zope/container/interfaces.py @@ -223,8 +223,8 @@ class IReservedNames(Interface): """A sequence of names that are reserved for that container""" reservedNames = Set( - title=_(u'Reserved Names'), - description=_(u'Names that are not allowed for addable content'), + title=_('Reserved Names'), + description=_('Names that are not allowed for addable content'), required=True, ) diff --git a/src/zope/container/ordered.py b/src/zope/container/ordered.py index d4a37d7..14acdd5 100644 --- a/src/zope/container/ordered.py +++ b/src/zope/container/ordered.py @@ -189,7 +189,7 @@ def __setitem__(self, key, object): >>> oc['foo'] = 'baz' Traceback (most recent call last): ... - KeyError: u'foo' + KeyError: 'foo' >>> oc._order ['foo', 'baz'] """ diff --git a/src/zope/container/size.py b/src/zope/container/size.py index 4b82af5..34457cd 100644 --- a/src/zope/container/size.py +++ b/src/zope/container/size.py @@ -1,4 +1,3 @@ - ############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. @@ -23,7 +22,7 @@ @implementer(ISized) -class ContainerSized(object): +class ContainerSized: """ Implements :class:`zope.size.interfaces.ISize` for :class:`zope.container.interfaces.IReadContainer` diff --git a/src/zope/container/testing.py b/src/zope/container/testing.py index 6a2c70d..316bb26 100644 --- a/src/zope/container/testing.py +++ b/src/zope/container/testing.py @@ -13,13 +13,10 @@ ############################################################################## """Unit test logic for setting up and tearing down basic infrastructure """ -import re - import zope.interface import zope.traversing.testing from zope.component.eventtesting import PlacelessSetup as EventPlacelessSetup from zope.component.testing import PlacelessSetup as CAPlacelessSetup -from zope.testing import renormalizing from zope.traversing.interfaces import IContainmentRoot from zope.traversing.interfaces import ITraversable @@ -32,35 +29,12 @@ from zope.container.traversal import ContainerTraversable -checker = renormalizing.RENormalizing([ - # Python 3 unicode removed the "u". - (re.compile("u('.*?')"), - r"\1"), - (re.compile('u(".*?")'), - r"\1"), - # Python 3 renamed type to class. - (re.compile('