Skip to content

Commit

Permalink
Merge pull request #108 from zopefoundation/dataflake/issue_107
Browse files Browse the repository at this point in the history
Fix two bytes/str issues under Python 3
  • Loading branch information
dataflake committed Jul 2, 2021
2 parents 3544a86 + f0b7e7b commit f2f95a5
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 15 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Changelog
2.1.2 (unreleased)
------------------

- Document and fix behavior of methods that open/read/write filesystem files
(`#107 <https://github.com/zopefoundation/Products.GenericSetup/issues/107>`_)

- Fix snapshot comparisons under Python 3
(`#85 <https://github.com/zopefoundation/Products.GenericSetup/issues/85>`_)


2.1.1 (2021-03-02)
------------------
Expand Down
16 changes: 14 additions & 2 deletions src/Products/GenericSetup/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from csv import reader
from csv import writer

import six
from six import BytesIO
from six.moves import cStringIO
from six.moves.configparser import ConfigParser

Expand Down Expand Up @@ -143,6 +145,9 @@ def import_(self, import_context, subdir, root=False):
if not preserve:
preserve = []
else:
# Make sure ``preserve`` is a native string
if six.PY3 and not isinstance(preserve, str):
preserve = preserve.decode('UTF-8')
preserve = _globtest(preserve, prior)

preserve.extend([x[0] for x in must_preserve])
Expand All @@ -156,6 +161,8 @@ def import_(self, import_context, subdir, root=False):
return

dialect = 'excel'
if six.PY3 and not isinstance(objects, str):
objects = objects.decode('UTF-8')
stream = cStringIO(objects)

rowiter = reader(stream, dialect)
Expand Down Expand Up @@ -274,7 +281,7 @@ def import_(self, import_context, subdir, root=False):
logger = import_context.getLogger('CSAFA')
logger.info('no .csv file for %s/%s' % (subdir, cid))
else:
stream = cStringIO(data)
stream = BytesIO(data)
self.context.put_csv(stream)


Expand Down Expand Up @@ -339,6 +346,11 @@ def put_ini(self, text):
"""
context = self.context
parser = ConfigParser()

# read_file/readfp expect text, not bytes
if isinstance(text, six.binary_type):
text = text.decode('UTF-8')

try:
parser.read_file(cStringIO(text))
except AttributeError: # Python 2
Expand Down Expand Up @@ -412,6 +424,6 @@ def import_(self, import_context, subdir, root=False):
logger = import_context.getLogger('SGAIFA')
logger.info('no .ini file for %s/%s' % (subdir, cid))
else:
request = FauxDAVRequest(BODY=data, BODYFILE=cStringIO(data))
request = FauxDAVRequest(BODY=data, BODYFILE=BytesIO(data))
response = FauxDAVResponse()
self.context.PUT(request, response)
14 changes: 8 additions & 6 deletions src/Products/GenericSetup/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def readDataFile(filename, subdir=None):
o 'subdir' is an optional subdirectory; if not supplied, search
only the "root" directory.
o Return the file contents as a string, or None if the
file cannot be found.
o Return the file contents as bytes (``str`` on Python 2 and ``bytes``
on Python 3), or None if the file cannot be found.
"""

def getLastModified(path):
Expand Down Expand Up @@ -138,8 +138,9 @@ def openDataFile(filename, subdir=None):
which to find the file; if not passed, write the file to the
"root" of the target.
o Return a readable file-like object; the caller is responsible
for calling 'close' on it.
o Return a readable file-like object in binary mode that will return
bytes when read from (``str`` on Python 2 and ``bytes`` on Python 3);
the caller is responsible for calling 'close' on it.
"""


Expand Down Expand Up @@ -186,8 +187,9 @@ def openDataFile(filename, content_type, subdir=None):
which to write the file; if not passed, write the file to the
"root" of the target.
o Return a writeable file-like object; the caller is responsible
for calling 'close' on it.
o Return a writeable file-like object in binary mode that expects
bytes data (``str`` on Python 2 and ``bytes`` on Python 3);
the caller is responsible for calling 'close' on it.
"""


Expand Down
7 changes: 6 additions & 1 deletion src/Products/GenericSetup/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,12 @@ def readDataFile(self, filename, subdir=None):
if subdir is not None:
filename = '/'.join((subdir, filename))

return self._files.get(filename)
# readDataFile must return bytes, so we make sure it does.
contents = self._files.get(filename)
if isinstance(contents, six.text_type):
contents = contents.encode(self._encoding or 'UTF-8')

return contents

def shouldPurge(self):

Expand Down
8 changes: 4 additions & 4 deletions src/Products/GenericSetup/tests/faux_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def put_ini(self, text):
self._was_put = text


KNOWN_DAV = """\
KNOWN_DAV = b"""\
Title: %s
Description: %s
Expand All @@ -79,9 +79,9 @@ def put_ini(self, text):
@implementer(IDAVAware)
class TestDAVAware(SimpleItem):
_was_put = None
title = 'DAV title'
description = 'DAV description'
body = 'DAV body'
title = b'DAV title'
description = b'DAV description'
body = b'DAV body'

def manage_FTPget(self):
return KNOWN_DAV % (self.title, self.description, self.body)
Expand Down
11 changes: 9 additions & 2 deletions src/Products/GenericSetup/tests/test_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import unittest

import six

from .conformance import ConformsToIFilesystemExporter
from .conformance import ConformsToIFilesystemImporter
from .conformance import ConformsToIINIAware
Expand Down Expand Up @@ -766,7 +768,7 @@ def test_import_with_known_CSV(self):
one,two,three
four,five,six
"""
NEW_CSV = """\
NEW_CSV = b"""\
four,five,six
one,two,three
"""
Expand Down Expand Up @@ -857,7 +859,7 @@ def test_export_dav_file(self):
def test_import_dav_file(self):
from .common import DummyImportContext
from .faux_objects import KNOWN_DAV
VALUES = ('Title: dav_file', 'Description: abc', 'body goes here')
VALUES = (b'Title: dav_file', b'Description: abc', b'body goes here')
dav_file = _makeDAVAware('dav_file.html')
adapter = self._makeOne(dav_file)
context = DummyImportContext(None)
Expand Down Expand Up @@ -939,6 +941,11 @@ def _parseINI(text):
from six.moves import cStringIO
from six.moves.configparser import ConfigParser
parser = ConfigParser()

# read_file/readfp expect text, not bytes
if isinstance(text, six.binary_type):
text = text.decode('UTF-8')

try:
parser.read_file(cStringIO(text))
except AttributeError: # Python 2
Expand Down
4 changes: 4 additions & 0 deletions src/Products/GenericSetup/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ def markupComparison(self, lines):

for line in lines.splitlines():

# All the operations below expect native strings
if six.PY3 and not isinstance(line, str):
line = line.decode('UTF-8')

if line.startswith('** '):

if line.find('File') > -1:
Expand Down

0 comments on commit f2f95a5

Please sign in to comment.