Skip to content

Commit

Permalink
Drop Python 2 support Part 1 (#699)
Browse files Browse the repository at this point in the history
* Grep for `2.7` and remove places for Python 2 support.

Additionally removed also a bit of Zope 2.7 support code.

* Drop usage of code referencing `PY2`.
  • Loading branch information
Michael Howitz committed Sep 11, 2019
1 parent a225e5a commit d2bbf33
Show file tree
Hide file tree
Showing 38 changed files with 107 additions and 409 deletions.
13 changes: 1 addition & 12 deletions docs/INSTALL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ available:
- A supported version of Python, including the development support if
installed from system-level packages. Supported versions include:

* 2.7
* 3.5 - 3.8
* 3.5 up to 3.8

- Zope needs the Python ``zlib`` module to be importable. If you are
building your own Python from source, please be sure that you have the
Expand Down Expand Up @@ -72,11 +71,6 @@ Built-in standard buildout configuration
$ bin/pip install -U pip zc.buildout
$ bin/buildout
.. note::

When using Python 2.7 instead of calling ``python3.7 -m venv .`` you have to
install `virtualenv` and then call ``python2.7 -m virtualenv .``.

Custom buildout configurations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -175,11 +169,6 @@ version you find on https://zopefoundation.github.io/Zope/:
$ bin/pip install Zope==4.1 \
-c https://zopefoundation.github.io/Zope/releases/4.1/constraints.txt
.. note::

When using Python 2.7 instead of calling ``python3.7 -m venv zope`` you have
to install `virtualenv` and then call ``python2.7 -m virtualenv zope``.

You can also install Zope using a single requirements file. Note that this
installation method might install packages that are not actually needed (i. e.
more than are listed in the ``install_requires`` section of ``setup.py``):
Expand Down
2 changes: 1 addition & 1 deletion docs/operation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ done.
Troubleshooting
---------------

- This version of Zope requires Python 2.7 or Python 3.5 and later.
- This version of Zope requires Python 3.5 and later.
It will *not* run with any version of PyPy.

- To build Python extensions you need to have Python configuration
Expand Down
11 changes: 5 additions & 6 deletions docs/zope4/news.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ found in this release.

Extended Python version support
-------------------------------
Zope 4 supports Python 2.7 and Python 3.5 up to Python 3.8.
Zope 5 supports Python 3.5 up to Python 3.8.

The Python 3 support currently covers the core dependencies shipped
with Zope and is limited to the new WSGI based publisher. The new
external ZServer project is currently limited to Python 2.7 compatibility
and likely to stay that way.
with Zope and is limited to the new WSGI based publisher.

Migrating an existing ZODB to Python 3 is not an automated process. Please
consult :ref:`zope4zodbmigration` for details.
Migrating an existing ZODB to Python 3 is not an automated process. You have
to update to Zope 4 first, see
`Zope 4 migration <https://zope.readthedocs.io/en/4.x/zope4/migration/index.html>`_.


WSGI as the new default server type
Expand Down
17 changes: 6 additions & 11 deletions src/App/Management.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""Standard management interface support
"""

import html
import itertools

import six
Expand All @@ -32,12 +33,6 @@
from zope.interface import implementer


try:
from html import escape
except ImportError: # PY2
from cgi import escape


class Tabs(Base):
"""Mix-in provides management folder tab support."""

Expand Down Expand Up @@ -99,20 +94,20 @@ def tabs_path_default(self, REQUEST):
steps = REQUEST._steps[:-1]
script = REQUEST['BASEPATH1']
linkpat = '{}/manage_workspace'
yield {'url': linkpat.format(escape(script, True)),
yield {'url': linkpat.format(html.escape(script, True)),
'title': 'Root',
'last': not bool(steps)}
if not steps:
return
last = steps.pop()
for step in steps:
script = '%s/%s' % (script, step)
yield {'url': linkpat.format(escape(script, True)),
'title': escape(unquote(step)),
yield {'url': linkpat.format(html.escape(script, True)),
'title': html.escape(unquote(step)),
'last': False}
script = '%s/%s' % (script, last)
yield {'url': linkpat.format(escape(script, True)),
'title': escape(unquote(last)),
yield {'url': linkpat.format(html.escape(script, True)),
'title': html.escape(unquote(last)),
'last': True}

def tabs_path_info(self, script, path):
Expand Down
10 changes: 2 additions & 8 deletions src/OFS/CopySupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,10 +671,7 @@ def _cb_encode(d):
json_bytes = dumps(d).encode('utf-8')
squashed_bytes = compress(json_bytes, 2) # -> bytes w/ useful encoding
# quote for embeding in cookie
if six.PY2:
return quote(squashed_bytes)
else:
return quote(squashed_bytes.decode('latin-1'))
return quote(squashed_bytes.decode('latin-1'))


def _cb_decode(s, maxsize=8192):
Expand All @@ -686,10 +683,7 @@ def _cb_decode(s, maxsize=8192):
Return a list of text IDs.
"""
dec = decompressobj()
if six.PY2:
squashed = unquote(s)
else:
squashed = unquote(s).encode('latin-1')
squashed = unquote(s).encode('latin-1')
data = dec.decompress(squashed, maxsize)
if dec.unconsumed_tail:
raise ValueError
Expand Down
4 changes: 0 additions & 4 deletions src/OFS/DTMLDocument.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""DTML Document objects.
"""

import six
from six.moves.urllib.parse import quote

from AccessControl import getSecurityManager
Expand Down Expand Up @@ -103,9 +102,6 @@ def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
c = self.content_type
else:
encoding = getattr(self, 'encoding', default_encoding)
if six.PY2 and not isinstance(r, six.text_type):
# Prevent double-encoding edge cases under Python 2
r = r.decode(encoding)
c, e = guess_content_type(self.getId(), r.encode(encoding))
RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE)
Expand Down
7 changes: 0 additions & 7 deletions src/OFS/DTMLMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
"""
import re

from six import PY2
from six import PY3
from six import binary_type
from six import text_type
from six.moves.urllib.parse import quote

from AccessControl import getSecurityManager
Expand Down Expand Up @@ -190,9 +188,6 @@ def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
c = self.content_type
else:
encoding = getattr(self, 'encoding', default_encoding)
if PY2 and not isinstance(r, text_type):
# Prevent double-encoding edge cases under Python 2
r = r.decode(encoding)
c, e = guess_content_type(self.getId(), r.encode(encoding))
RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE)
Expand Down Expand Up @@ -430,8 +425,6 @@ def safe_file_data(data):
data = data.read()
if PY3 and isinstance(data, binary_type):
data = data.decode('utf-8')
if PY2 and isinstance(data, text_type):
data = data.encode('utf-8')
return data


Expand Down
24 changes: 4 additions & 20 deletions src/OFS/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"""Image object
"""

import html
import struct
from email.generator import _make_boundary
from io import BytesIO

from six import PY2
from six import binary_type
from six import text_type

Expand Down Expand Up @@ -49,12 +49,6 @@
from ZPublisher.HTTPRequest import FileUpload


try:
from html import escape
except ImportError: # PY2
from cgi import escape


manage_addFileForm = DTMLFile(
'dtml/imageAdd',
globals(),
Expand Down Expand Up @@ -669,10 +663,7 @@ def __bytes__(self):
return bytes(self.data)

def __str__(self):
if PY2:
return str(self.data)
else:
return self.data.decode(self._get_encoding())
return self.data.decode(self._get_encoding())

def __bool__(self):
return True
Expand Down Expand Up @@ -944,11 +935,11 @@ def tag(

if alt is None:
alt = getattr(self, 'alt', '')
result = '%s alt="%s"' % (result, escape(alt, True))
result = '%s alt="%s"' % (result, html.escape(alt, True))

if title is None:
title = getattr(self, 'title', '')
result = '%s title="%s"' % (result, escape(title, True))
result = '%s title="%s"' % (result, html.escape(title, True))

if height:
result = '%s height="%s"' % (result, height)
Expand Down Expand Up @@ -989,10 +980,6 @@ class Pdata(Persistent, Implicit):
def __init__(self, data):
self.data = data

if PY2:
def __getslice__(self, i, j):
return self.data[i:j]

def __getitem__(self, key):
return self.data[key]

Expand All @@ -1012,6 +999,3 @@ def __bytes__(self):
_next = self.next

return b''.join(r)

if PY2:
__str__ = __bytes__
21 changes: 10 additions & 11 deletions src/OFS/ObjectManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""Object Manager
"""

import html
import os
import re
from io import BytesIO
Expand Down Expand Up @@ -63,12 +64,6 @@
from zope.lifecycleevent import ObjectRemovedEvent


try:
from html import escape
except ImportError: # PY2
from cgi import escape


# Constants: __replaceable__ flags:
NOT_REPLACEABLE = 0
REPLACEABLE = 1
Expand All @@ -94,12 +89,12 @@ def checkValidId(self, id, allow_dup=0):
# set to false before the object is added.
if not id or not isinstance(id, str):
if isinstance(id, text_type):
id = escape(id, True)
id = html.escape(id, True)
raise BadRequest('Empty or invalid id specified', id)
if bad_id(id) is not None:
raise BadRequest(
('The id "%s" contains characters '
'illegal in URLs.' % escape(id, True)))
'illegal in URLs.' % html.escape(id, True)))
if id in ('.', '..'):
raise BadRequest(
'The id "%s" is invalid because it is not traversable.' % id)
Expand Down Expand Up @@ -557,7 +552,8 @@ def manage_delObjects(self, ids=[], REQUEST=None):
pass

if v is self:
raise BadRequest('%s does not exist' % escape(ids[-1], True))
raise BadRequest('%s does not exist' %
html.escape(ids[-1], True))
self._delObject(id)
del ids[-1]
if REQUEST is not None:
Expand Down Expand Up @@ -642,14 +638,17 @@ def manage_importObject(self, file, REQUEST=None, set_owner=1,
"""Import an object from a file"""
dirname, file = os.path.split(file)
if dirname:
raise BadRequest('Invalid file name %s' % escape(file, True))
raise BadRequest('Invalid file name %s' % html.escape(file, True))

for impath in self._getImportPaths():
filepath = os.path.join(impath, 'import', file)
if os.path.exists(filepath):
break
else:
raise BadRequest('File does not exist: %s' % escape(file, True))
raise BadRequest(
'File does not exist: %s' %
html.escape(
file, True))

imported = self._importObjectFromFile(
filepath, verify=bool(REQUEST), set_owner=set_owner,
Expand Down
18 changes: 7 additions & 11 deletions src/OFS/PropertyManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"""Property management
"""

import html

import six

from AccessControl.class_init import InitializeClass
Expand All @@ -30,12 +32,6 @@
from ZPublisher.Converters import type_converters


try:
from html import escape
except ImportError: # PY2
from cgi import escape


@implementer(IPropertyManager)
class PropertyManager(Base):
"""
Expand Down Expand Up @@ -138,7 +134,7 @@ def valid_property_id(self, id):
id[:3] == 'aq_' or \
' ' in id or \
hasattr(aq_base(self), id) or \
escape(id, True) != id:
html.escape(id, True) != id:
return 0
return 1

Expand Down Expand Up @@ -215,7 +211,7 @@ def _updateProperty(self, id, value):
self._wrapperCheck(value)
if not self.hasProperty(id):
raise BadRequest(
'The property %s does not exist' % escape(id, True))
'The property %s does not exist' % html.escape(id, True))
if isinstance(value, (six.string_types, six.binary_type)):
proptype = self.getPropertyType(id) or 'string'
if proptype in type_converters:
Expand All @@ -225,7 +221,7 @@ def _updateProperty(self, id, value):
def _delProperty(self, id):
if not self.hasProperty(id):
raise ValueError(
'The property %s does not exist' % escape(id, True))
'The property %s does not exist' % html.escape(id, True))
self._delPropValue(id)
self._properties = tuple(i for i in self._properties if i['id'] != id)

Expand Down Expand Up @@ -339,7 +335,7 @@ def manage_changeProperties(self, REQUEST=None, **kw):
if self.hasProperty(name):
if 'w' not in propdict[name].get('mode', 'wd'):
raise BadRequest(
'%s cannot be changed' % escape(name, True))
'%s cannot be changed' % html.escape(name, True))
self._updateProperty(name, value)

if REQUEST:
Expand Down Expand Up @@ -385,7 +381,7 @@ def manage_delProperties(self, ids=None, REQUEST=None):
if not hasattr(aq_base(self), id):
raise BadRequest(
('The property <em>%s</em> '
'does not exist' % escape(id, True)))
'does not exist' % html.escape(id, True)))
if ('d' not in propdict[id].get('mode', 'wd')) or (id in nd):
raise BadRequest('Cannot delete %s' % id)
self._delProperty(id)
Expand Down
Loading

0 comments on commit d2bbf33

Please sign in to comment.