Commit 20afa613 authored by Jérome Perrin's avatar Jérome Perrin

syncml: support python3

parent 41156e9a
......@@ -28,6 +28,7 @@
##############################################################################
from hashlib import md5
import six
from AccessControl import ClassSecurityInfo
......@@ -98,7 +99,8 @@ class SyncMLSignature(XMLObject):
Set the XML corresponding to the object
"""
if value:
# convert the string to Pdata
assert isinstance(value, bytes)
# convert the bytes to Pdata
pdata_wrapper = PdataHelper(self.getPortalObject(), value)
self._setData(pdata_wrapper)
self.setTemporaryData(None) # We make sure that the data will not be erased
......@@ -113,7 +115,7 @@ class SyncMLSignature(XMLObject):
Get the XML corresponding to the object
"""
if self.hasData():
return str(self._baseGetData())
return bytes(self._baseGetData())
elif default is _MARKER:
return self._baseGetData()
else:
......@@ -139,7 +141,7 @@ class SyncMLSignature(XMLObject):
Return the temp xml as string
"""
if self.hasTemporaryData():
return str(self._baseGetTemporaryData())
return bytes(self._baseGetTemporaryData())
elif default is _MARKER:
return self._baseGetTemporaryData()
else:
......@@ -153,7 +155,7 @@ class SyncMLSignature(XMLObject):
changed or not
Returns 1 if MD5 are equals, else it returns 0
"""
if isinstance(xml_string, unicode):
if six.PY2 and isinstance(xml_string, six.text_type):
xml_string = xml_string.encode('utf-8')
return md5(xml_string).hexdigest() == self.getContentMd5()
......@@ -189,7 +191,7 @@ class SyncMLSignature(XMLObject):
Return the patial xml as string
"""
if self.hasPartialData():
return str(self._baseGetPartialData())
return bytes(self._baseGetPartialData())
elif default is _MARKER:
return self._baseGetPartialData()
else:
......@@ -223,7 +225,7 @@ class SyncMLSignature(XMLObject):
rest_in_queue = partial_data[max_len:]
if rest_in_queue is not None:
self.setPartialData(rest_in_queue)
return str(chunk)
return bytes(chunk)
security.declareProtected(Permissions.ModifyPortalContent,
'setSubscriberXupdate')
......@@ -244,7 +246,7 @@ class SyncMLSignature(XMLObject):
Return the patial xml as string
"""
if self.hasSubscriberXupdate():
return str(self._baseGetSubscriberXupdate())
return bytes(self._baseGetSubscriberXupdate())
elif default is _MARKER:
return self._baseGetSubscriberXupdate()
else:
......@@ -269,7 +271,7 @@ class SyncMLSignature(XMLObject):
Return the partial xml as string
"""
if self.hasPublisherXupdate():
return str(self._baseGetPublisherXupdate())
return bytes(self._baseGetPublisherXupdate())
elif default is _MARKER:
return self._baseGetPublisherXupdate()
else:
......
......@@ -29,9 +29,10 @@
from base64 import b16encode, b16decode
from logging import getLogger
from urlparse import urlparse
from six.moves.urllib.parse import urlparse
from lxml import etree
from copy import deepcopy
import six
from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import newSecurityManager
......@@ -53,6 +54,7 @@ from erp5.component.module.SyncMLTransportERP5 import ERP5Transport
from erp5.component.module.SyncMLConstant import MAX_LEN, ADD_ACTION, \
REPLACE_ACTION
from erp5.component.module.XMLSyncUtils import cutXML
from six.moves import range
transport_scheme_dict = {
"http" : HTTPTransport(),
......@@ -160,7 +162,7 @@ class SyncMLSubscription(XMLObject):
activate = self.activate
callback_method = getattr(activate(**activate_kw), callback)
if generated_other_activity:
for i in xrange(0, result_count, packet_size):
for i in range(0, result_count, packet_size):
syncml_logger.info("-- getAndIndex : recursive call, generating for %s",
r[i:i+packet_size])
callback_method(path_list=r[i:i+packet_size],
......@@ -168,7 +170,7 @@ class SyncMLSubscription(XMLObject):
**method_kw)
else:
if result_count > packet_size and limit:
for i in xrange(0, result_count-packet_size, packet_size):
for i in range(0, result_count-packet_size, packet_size):
syncml_logger.info("-- getAndIndex : i %s, call, generating for %s : %s",
i, r[i:i+packet_size], activate_kw)
callback_method(path_list=r[i:i+packet_size],
......@@ -205,7 +207,7 @@ class SyncMLSubscription(XMLObject):
Return the path of the subscription that will be used in sql table
_ char must be escaped because of the LIKE behaviour
"""
return "%s/%%" % (self.getSourceValue().getPath().replace("_","\_"),) # pylint: disable=anomalous-backslash-in-string
return "%s/%%" % (self.getSourceValue().getPath().replace("_", r"\_"),)
security.declarePrivate('sendSyncCommand')
def sendSyncCommand(self, min_gid, max_gid, message_id, activate_kw):
......@@ -225,7 +227,7 @@ class SyncMLSubscription(XMLObject):
# transport failure
# activate_kw["group_method_id"] = None
# activate_kw["group_method_cost"] = .05
self.activate(**activate_kw).sendMessage(xml=str(syncml_response))
self.activate(**activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('applySyncCommand')
def applySyncCommand(self, response_message_id, activate_kw, **kw):
......@@ -250,7 +252,7 @@ class SyncMLSubscription(XMLObject):
self.activate(activity="SQLQueue",
# group_method_id=None,
# group_method_cost=.05,
tag=activate_kw).sendMessage(xml=str(syncml_response))
tag=activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('getAndActivate')
......@@ -310,7 +312,7 @@ class SyncMLSubscription(XMLObject):
if generated_other_activity:
# XXX Can be factorized with following code
# upper_limit of xrange + some check ???
for i in xrange(0, result_count, packet_size):
for i in range(0, result_count, packet_size):
if first_call:
min_gid = None
first_call = False
......@@ -330,7 +332,7 @@ class SyncMLSubscription(XMLObject):
else:
i = 0
if result_count > packet_size:
for i in xrange(0, result_count-packet_size, packet_size):
for i in range(0, result_count-packet_size, packet_size):
if first_call:
min_gid = None
first_call = False
......@@ -531,7 +533,7 @@ class SyncMLSubscription(XMLObject):
domain=self))
xml_document = incoming_data
if not isinstance(xml_document, basestring):
if not isinstance(xml_document, bytes):
# XXX using deepcopy to remove parent link - must be done elsewhere
xml_document = deepcopy(xml_document)
# Remove useless namespace
......@@ -539,7 +541,7 @@ class SyncMLSubscription(XMLObject):
xml_document = etree.tostring(xml_document, encoding='utf-8',
pretty_print=True)
if isinstance(xml_document, unicode):
if six.PY2 and isinstance(xml_document, unicode):
xml_document = xml_document.encode('utf-8')
# Link the signature to the document
if signature:
......@@ -603,12 +605,12 @@ class SyncMLSubscription(XMLObject):
signature.changeToConflict()
# Register the data received which generated the diff
# XXX Why ?
if not isinstance(incoming_data, basestring):
if not isinstance(incoming_data, bytes):
incoming_data = etree.tostring(incoming_data,
encoding='utf-8')
signature.setPartialData(incoming_data)
else:
signature.setData(str(xml_document))
signature.setData(bytes(xml_document))
signature.synchronize()
syncml_logger.info("change state of signature to %s with %s",
signature.getValidationState(), signature.getData())
......@@ -667,7 +669,7 @@ class SyncMLSubscription(XMLObject):
}
syncml_logger.info("Sending final message for modificationson on %s",
self.getRelativeUrl())
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
self.activate(**final_activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('getDeletedSyncMLData')
......@@ -700,11 +702,10 @@ class SyncMLSubscription(XMLObject):
}
syncml_logger.info("Sending final message for modificationson on %s",
self.getRelativeUrl())
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
self.activate(**final_activate_kw).sendMessage(xml=bytes(syncml_response))
def getSearchablePath(self):
return "%s%%" %(self.getPath().replace('_', '\_'),) # pylint: disable=anomalous-backslash-in-string
return "%s%%" %(self.getPath().replace('_', r'\_'),)
def _generateSyncCommand(self, action, signature, data_diff ,document_data, gid,
conduit, syncml_response):
......@@ -973,10 +974,12 @@ class SyncMLSubscription(XMLObject):
# old way using the conduit
conduit = self.getConduit()
raw_gid = conduit.getGidFromObject(object)
if isinstance(raw_gid, unicode):
if isinstance(raw_gid, six.text_type):
raw_gid = raw_gid.encode('ascii', 'ignore')
if encoded:
gid = b16encode(raw_gid)
if six.PY3:
gid = gid.decode()
else:
gid = raw_gid
return gid
......@@ -1146,7 +1149,7 @@ class SyncMLSubscription(XMLObject):
"""
object_id_list = list(self.getObjectIds())
object_list_len = len(object_id_list)
for i in xrange(0, object_list_len, MAX_OBJECTS):
for i in range(0, object_list_len, MAX_OBJECTS):
current_id_list = object_id_list[i:i+MAX_OBJECTS]
self.activate(activity='SQLQueue',
priority=ACTIVITY_PRIORITY).manage_delObjects(current_id_list)
......
......@@ -25,6 +25,7 @@
#
##############################################################################
import six
from logging import getLogger
from AccessControl import ClassSecurityInfo, getSecurityManager
......@@ -337,8 +338,11 @@ class SyncMLEngineMixin(object):
if syncml_request.credentials['type'] == publication.getAuthenticationType():
decoded = decode(syncml_request.credentials['format'],
syncml_request.credentials['data'])
if decoded and ':' in decoded:
login, password = decoded.split(':')
if decoded and b':' in decoded:
login, password = decoded.split(b':')
if six.PY3:
login = login.decode()
password = password.decode()
# TODO: make it work for users existing anywhere
user_folder = publication.getPortalObject().acl_users
for _, plugin in user_folder._getOb('plugins')\
......@@ -463,8 +467,8 @@ class SyncMLEngineMixin(object):
# Wait for all reset to be done
# before starting sync
priority=ACTIVITY_PRIORITY,
tag=publication.getRelativeUrl()).sendMessage(xml=str(syncml_response))
tag=publication.getRelativeUrl()).sendMessage(xml=bytes(syncml_response))
else:
subscriber.sendMessage(xml=str(syncml_response))
subscriber.sendMessage(xml=bytes(syncml_response))
return syncml_response
......@@ -42,12 +42,14 @@ from xml.sax.saxutils import unescape
import re
from lxml import etree
from lxml.etree import Element
import six
parser = etree.XMLParser(remove_blank_text=True)
from xml_marshaller.xml_marshaller import Unmarshaller
from xupdate_processor import xuproc
from base64 import standard_b64decode
from zope.interface import implementer
from copy import deepcopy
from six import string_types as basestring
import logging
syncml_logger = logging.getLogger('ERP5SyncML')
......@@ -229,10 +231,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# /erp5/object[@gid='313730']/../workflow_action[@id=SHA(TIME + ACTOR)]
wf_action_id = EXTRACT_ID_FROM_XPATH.findall(xpath_expression)[-1][-1]
def deleteWorkflowNode():
for wf_id, wf_history_tuple in object.workflow_history.iteritems():
for wf_id, wf_history_tuple in six.iteritems(object.workflow_history):
for wf_history_index, wf_history in enumerate(wf_history_tuple):
if sha1(wf_id + str(wf_history['time']) +
wf_history['actor']).hexdigest() == wf_action_id:
if sha1((wf_id + str(wf_history['time']) +
wf_history['actor']).encode('utf-8')).hexdigest() == wf_action_id:
object.workflow_history[wf_id] = (
object.workflow_history[wf_id][:wf_history_index] +
object.workflow_history[wf_id][wf_history_index + 1:])
......@@ -426,21 +428,21 @@ class ERP5Conduit(XMLSyncUtilsMixin):
def getFormatedArgs(self, args=None):
"""
This lookd inside the args dictionnary and then
convert any unicode string to string
convert any unicode string to string ( on python 2 )
"""
new_args = {}
for keyword in args.keys():
data = args[keyword]
if isinstance(keyword, unicode):
if six.PY2 and isinstance(keyword, six.text_type):
keyword = keyword.encode(self.getEncoding())
if isinstance(data, (tuple, list)):
new_data = []
for item in data:
if isinstance(item, unicode):
if six.PY2 and isinstance(item, six.text_type):
item = item.encode(self.getEncoding())
new_data.append(item)
data = new_data
if isinstance(data, unicode):
if six.PY2 and isinstance(data, six.text_type):
data = data.encode(self.getEncoding())
new_args[keyword] = data
return new_args
......@@ -561,7 +563,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
XXX name of method is not good, because content is not necessarily XML
return a xml with id replaced by a new id
"""
if isinstance(xml, str):
if isinstance(xml, bytes):
xml = etree.XML(xml, parser=parser)
else:
# copy of xml object for modification
......@@ -623,9 +625,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
"""
if xml is a string, convert it to a node
"""
if xml is None: return None
if isinstance(xml, (str, unicode)):
if isinstance(xml, unicode):
if xml is None:
return None
if isinstance(xml, six.string_types + (bytes, )):
if six.PY2 and isinstance(xml, six.text_type):
xml = xml.encode('utf-8')
xml = etree.XML(xml, parser=parser)
# If we have the xml from the node erp5, we just take the subnode
......@@ -778,7 +781,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if data_type == NONE_TYPE:
return None
data = node.text
if data is not None and isinstance(data, unicode):
if data is not None and six.PY2 and isinstance(data, six.text_type):
data = data.encode('utf-8')
elif data is None and data_type in TEXT_TYPE_LIST:
return ''
......@@ -797,7 +800,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
elif data_type in DATA_TYPE_LIST:
if data is None:
# data is splitted inside block_data nodes
data = ''.join([standard_b64decode(block.text) for\
data = b''.join([standard_b64decode(block.text) for\
block in node.iterchildren()])
elif data_type == DATE_TYPE:
data = DateTime(data)
......@@ -812,7 +815,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
Parse the xupdate and then it will call the conduit
"""
conflict_list = []
if isinstance(xupdate, (str, unicode)):
if isinstance(xupdate, six.string_types + (bytes, )):
xupdate = etree.XML(xupdate, parser=parser)
#LOG("applyXupdate", INFO, etree.tostring(xupdate, pretty_print=True))
xupdate_builded = False
......@@ -830,14 +833,16 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xupdate_builded = True
# Find the prefix used by marshaller.
for prefix, namespace_uri in subnode.nsmap.iteritems():
for prefix, namespace_uri in six.iteritems(subnode.nsmap):
if namespace_uri == MARSHALLER_NAMESPACE_URI:
break
# TODO add support of etree objects for xuproc to avoid
# serializing tree into string
if not isinstance(previous_xml, str):
previous_xml = etree.tostring(previous_xml)
xupdated_tree = xuproc.applyXUpdate(xml_xu_string=etree.tostring(xupdate),
if isinstance(previous_xml, bytes):
previous_xml = previous_xml.decode('utf-8')
if not isinstance(previous_xml, six.text_type):
previous_xml = etree.tostring(previous_xml, encoding='unicode')
xupdated_tree = xuproc.applyXUpdate(xml_xu_string=etree.tostring(xupdate, encoding='unicode'),
xml_doc_string=previous_xml)
if MARSHALLER_NAMESPACE_URI in subnode.nsmap.values():
xpath_expression = original_xpath_expression
......@@ -888,7 +893,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
previous_xml=previous_xml, **kw)
# Now apply collected xupdated_node
for update_dict in xpath_expression_update_dict.itervalues():
for update_dict in six.itervalues(xpath_expression_update_dict):
update_dict.update(kw)
conflict_list += self.updateNode(previous_xml=previous_xml,
**update_dict)
......@@ -913,7 +918,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if time <= action.get('time'):
# action in the past are not appended
addable = WORKFLOW_ACTION_INSERTABLE
key_list = action.keys()
key_list = list(action.keys())
key_list.remove("time")
for key in key_list:
if status[key] != action[key]:
......@@ -1100,8 +1105,12 @@ class ERP5Conduit(XMLSyncUtilsMixin):
"""
# XXX xuproc does not support passing
# etree objetcs
if not isinstance(diff, basestring):
diff = etree.tostring(diff)
if isinstance(diff, bytes):
diff = diff.decode('utf-8')
elif not isinstance(diff, basestring):
diff = etree.tostring(diff, encoding='unicode')
if not isinstance(original_data, six.text_type):
original_data = six.text_type(original_data, 'utf-8')
return etree.tostring(xuproc.applyXUpdate(xml_xu_string=diff,
xml_doc_string=original_data),
encoding='utf-8')
......
......@@ -75,7 +75,7 @@ class ERP5DocumentConduit(ERP5Conduit):
# if time <= action.get('time'):
# # action in the past are not appended
# addable = WORKFLOW_ACTION_INSERTABLE
key_list = action.keys()
key_list = list(action.keys())
# key_list.remove("time")
# XXX-AUREL For document it seems that checking time != is required
# I don't know why ?
......
......@@ -29,7 +29,7 @@
from xml.dom.ext import PrettyPrint
import random
from cStringIO import StringIO
from six.moves import cStringIO as StringIO
from AccessControl import ClassSecurityInfo
from zLOG import LOG
......
......@@ -30,6 +30,7 @@ from logging import getLogger
from erp5.component.mixin.SyncMLEngineMixin import SyncMLEngineMixin
from erp5.component.module.SyncMLConstant import ACTIVITY_PRIORITY
from Products.ERP5.ERP5Site import getSite
from six.moves import range
syncml_logger = getLogger('ERP5SyncML')
......@@ -113,7 +114,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
(subscription.getPath(),
'applySyncCommand'),
after_tag=tag,).sendMessage(
xml=str(syncml_response))
xml=bytes(syncml_response))
# Synchronization process is now finished
syncml_logger.info("\tClient finished processing messages from server")
subscription.finish()
......@@ -137,7 +138,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
# transport failure
syncml_logger.info("....client sending message....")
subscription.activate(activity="SQLQueue").sendMessage(
xml=str(syncml_response))
xml=bytes(syncml_response))
def processServerSynchronization(self, subscriber, syncml_request):
......@@ -241,7 +242,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
subscriber.activate(activity="SQLQueue",
after_method_id=after_method_id,
after_tag=tag).sendMessage(
xml=str(syncml_response))
xml=bytes(syncml_response))
def runGetAndActivate(self, subscription, tag, after_method_id=None):
"""
......@@ -282,7 +283,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
response_id_list.reverse()
else:
response_id_list = [None for _ in
xrange(len(syncml_request.sync_command_list))]
range(len(syncml_request.sync_command_list))]
split = getSite().portal_preferences.getPreferredSyncActionPerActivityCount()
if not split: # We do not use activities
if send_response:
......@@ -294,7 +295,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
subscription.activate(
activity="SQLQueue",
priority=ACTIVITY_PRIORITY,
tag=subscription.getRelativeUrl()).sendMessage(xml=str(syncml_response))
tag=subscription.getRelativeUrl()).sendMessage(xml=bytes(syncml_response))
else:
# XXX For now always split by one
activate = subscription.activate
......
......@@ -123,8 +123,9 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin):
subscription._edit(authenticated_user=None)
# Send the message
subscription.sendMessage(xml=str(syncml_response))
return str(syncml_response)
subscription.sendMessage(xml=bytes(syncml_response))
return bytes(syncml_response)
def processServerSynchronization(self, subscriber, syncml_request):
......@@ -222,10 +223,10 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin):
subscriber.logout()
subscriber._edit(authenticated_user=None,
remaining_object_path_list=None)
syncml_response = "" # XXX This is expected by unit test only
syncml_response = b"" # XXX This is expected by unit test only
# Body must be sent even when there is no data to notify client
subscriber.sendMessage(xml=str(syncml_response))
subscriber.sendMessage(xml=bytes(syncml_response))
# Return message for unit test purpose
return str(syncml_response)
return bytes(syncml_response)
......@@ -28,6 +28,7 @@
from lxml.builder import ElementMaker
from lxml.etree import Element
from lxml import etree
import six
from erp5.component.module.XMLSyncUtils import resolveSyncmlStatusCode, \
encode, resolveSyncmlAlertCode
......@@ -60,13 +61,13 @@ class SyncMLResponse(object):
def __len__(self):
# To check if it has to be done on whole message or only the body
return len(etree.tostring(self.body, encoding='utf-8',
xml_declaration=True,
pretty_print=True))
return len(bytes(self))
def __str__(self):
def __bytes__(self):
return etree.tostring(self.data, encoding='utf-8', xml_declaration=True,
pretty_print=True)
if six.PY2:
__str__ = __bytes__
def _getNextCommandId(self):
"""
......@@ -117,7 +118,7 @@ class SyncMLResponse(object):
if authentication_type == 'syncml:auth-basic':
# base64 formating of "userid:password"
credential = "%s:%s" % (user_id, password)
credential = encode(authentication_format, credential)
credential = encode(authentication_format, credential).decode()
elif authentication_type == "syncml:auth-md5":
# base64 coded md5 for user "XXX", password "XXX", nonce "XXX"
raise NotImplementedError("MD5 authentication not supported")
......@@ -345,7 +346,7 @@ class SyncMLResponse(object):
data_node = E.Data()
# XXX to be remove later to use only CDATA
if media_type == 'text/xml':
if isinstance(data, basestring):
if isinstance(data, bytes):
data_node.append(etree.XML(data, parser=parser))
elif isinstance(data, etree.CDATA):
# data could be Data element if partial XML
......@@ -421,7 +422,7 @@ class SyncMLRequest(object):
""" SyncMLRequest represent a message received by the client or server"""
def __init__(self, xml):
if isinstance(xml, basestring):
if isinstance(xml, bytes):
self.data = etree.XML(xml, parser=parser)
else:
raise ValueError("Do not know how to initialize message with data %r"
......@@ -435,9 +436,11 @@ class SyncMLRequest(object):
self.isFinal = False
self.parse()
def __str__(self):
def __bytes__(self):
return etree.tostring(self.data, encoding='utf-8', xml_declaration=True,
pretty_print=True)
if six.PY2:
__str__ = __bytes__
def parse(self):
"""
......@@ -590,7 +593,8 @@ class SyncMLRequest(object):
parser_ = etree.XMLParser(strip_cdata=False)
cdata = etree.XML(data, parser_)
data = cdata.text
# XXX this is unicode and can be a problem for activity
if six.PY3:
data = data.encode()
sync_command_kw["raw_data"] = data
append(sync_command_kw)
......@@ -32,8 +32,7 @@ class FileTransport:
def send(self, to_url, data, sync_id, content_type):
filename = to_url[len('file:/'):]
try:
stream = open(filename, 'w')
stream.write(data)
stream.close()
with open(filename, 'wb') as stream:
stream.write(data)
except IOError:
raise ConnectionError
......@@ -38,7 +38,7 @@ from hashlib import md5
from ZPublisher.HTTPRequest import FileUpload
from OFS.Image import Pdata
from StringIO import StringIO
from io import BytesIO
import transaction
class PdataHelper(persistent.Persistent):
......@@ -68,14 +68,14 @@ class PdataHelper(persistent.Persistent):
n = self._max_len
if isinstance(value, (str, unicode)):
if isinstance(value, unicode):
if isinstance(value, six.string_types + (six.binary_type, )):
if not isinstance(value, six.binary_type):
value = value.encode('utf-8')
size=len(value)
size = len(value)
if size < n:
return Pdata(value), size
# Big string: cut it into smaller chunks
value = StringIO(value)
# Big data: cut it into smaller chunks
value = BytesIO(value)
if isinstance(value, FileUpload) and not value:
raise ValueError('File not specified')
......@@ -161,11 +161,13 @@ class PdataHelper(persistent.Persistent):
"""
return self.size
def __str__(self):
def __bytes__(self):
"""Return string concatenation
of all Pdata parts
"""
return str(self._data)
return bytes(self._data)
if six.PY2:
__str__ = __bytes__
def getContentMd5(self):
"""
......@@ -176,26 +178,20 @@ class PdataHelper(persistent.Persistent):
self.md5sum = md5sum
return md5sum
def __getslice__(self, i, j):
def __getitem__(self, i):
"""XXX Could be improved to avoid loading
into memory all Pdata objects
"""
return self.__str__()[i:j]
return self.__bytes__()[i]
def getLastPdata(self):
"""return the last Pdata element
of a Pdata chains
"""
pdata = self._data
if six.PY2:
next_ = pdata.next
else:
next_ = pdata.__next__
next_ = pdata.next
while next_ is not None:
pdata = next_
if six.PY2:
next_ = pdata.next
else:
next_ = pdata.__next__
next_ = pdata.next
return pdata
......@@ -31,6 +31,7 @@ from erp5.component.module.ERP5Conduit import ERP5Conduit
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
import difflib
import six
from zLOG import LOG
......@@ -56,7 +57,7 @@ class VCardConduit(ERP5Conduit):
"""
#LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \
#xml:\n%s' % (str(object), str(object_id), str(sub_object), xml))
if not isinstance(xml, str):
if not isinstance(xml, bytes):
xml = self.nodeToString(xml)
portal_type = 'Person' #the VCard can just use Person
if sub_object is None:
......@@ -161,7 +162,9 @@ class VCardConduit(ERP5Conduit):
convert_dict['N'] = 'last_name'
convert_dict['TEL'] = 'default_telephone_text'
edit_dict = {}
vcard_list = vcard.split('\n')
if isinstance(vcard, bytes):
vcard = vcard.decode('utf-8')
vcard_list = vcard.splitlines()
for vcard_line in vcard_list:
if ':' in vcard_line:
property_, property_value = vcard_line.split(':')
......@@ -192,12 +195,12 @@ class VCardConduit(ERP5Conduit):
else:
property_name=property_
if isinstance(property_name, unicode):
if six.PY2 and isinstance(property_name, six.text_type):
property_name = property_name.encode('utf-8')
tmp = []
for property_value in property_value_list:
if isinstance(property_value, unicode):
if six.PY2 and isinstance(property_value, six.text_type):
property_value = property_value.encode('utf-8')
tmp.append(property_value)
property_value_list = tmp
......@@ -226,6 +229,10 @@ class VCardConduit(ERP5Conduit):
def generateDiff(self, new_data, former_data):
"""return unified diff for plain-text documents
"""
if isinstance(new_data, bytes):
new_data = new_data.decode('utf-8')
if isinstance(former_data, bytes):
former_data = former_data.decode('utf-8')
diff = '\n'.join(difflib.unified_diff(new_data.splitlines(),
former_data.splitlines()))
return diff
......
......@@ -29,6 +29,7 @@
import smtplib
import os
import six
from base64 import b64encode, b64decode
from lxml import etree
from lxml.builder import ElementMaker
......@@ -58,7 +59,10 @@ def encode(format, string_to_encode): # pylint: disable=redefined-builtin
if not format:
return string_to_encode
if format == 'b64':
return b64encode(string_to_encode)
if not isinstance(string_to_encode, six.binary_type):
string_to_encode = string_to_encode.encode()
encoded = b64encode(string_to_encode)
return encoded
#elif format is .... put here the other formats
else:#if there is no format corresponding with format, raise an error
LOG('encode : unknown or not implemented format : ', INFO, format)
......@@ -180,11 +184,10 @@ def getXupdateObject(object_xml=None, old_xml=None):
"""
erp5diff = ERP5Diff()
erp5diff.compare(old_xml, object_xml)
#Upper version of ERP5Diff produce valid XML.
if erp5diff._getResultRoot():
xupdate = erp5diff.outputString()
#omit xml declaration
xupdate = xupdate[xupdate.find('<xupdate:modifications'):]
xupdate = erp5diff.outputBytes(encoding="utf-8")
# omit xml declaration
xupdate = xupdate[xupdate.find(b'<xupdate:modifications'):]
return xupdate
def cutXML(xml_string, length=None):
......
......@@ -3,13 +3,6 @@ first_name = context.getFirstName()
last_name = context.getLastName()
tel = context.getDefaultTelephoneTelephoneNumber()
if same_type(first_name, u'a'):
first_name = first_name.encode('utf-8')
if same_type(last_name, u'a'):
last_name = last_name.encode('utf-8')
if same_type(tel, u'a') :
tel = tel.encode('utf-8')
parameters_FN = ''
parameters_N = ''
......
from six.moves import range
if not len(path_list):
return
restrictedTraverse = context.getPortalObject().restrictedTraverse
......@@ -35,7 +36,7 @@ for path in path_list:
object_list = obj()
else:
object_list = [obj,]
for x in xrange(0, len(object_list), MAX_PER_QUERY):
for x in range(0, len(object_list), MAX_PER_QUERY):
parameter_dict, parameter_append_list = generateParameterList()
for obj in object_list[x:x+MAX_PER_QUERY]:
for value_list, getter in parameter_append_list:
......
from six.moves import range
sub_path = method_kw.get("subscription_path")
sub = context.getPortalObject().restrictedTraverse(sub_path)
search_kw = dict(kw)
......@@ -17,7 +18,7 @@ if result_count:
r = [x.getId() for x in r]
callback_method = getattr(sub.activate(**activate_kw), callback)
for i in xrange(0, result_count, packet_size):
for i in range(0, result_count, packet_size):
callback_method(id_list=r[i:i+packet_size],
**method_kw)
......
......@@ -55,9 +55,9 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
nb_objects = 10
#for objects
ids = range(1, nb_objects+1)
ids = list(range(1, nb_objects+1))
#id_max_text : number of document text
id_max_text = nb_objects/2
id_max_text = nb_objects // 2
id1 = '2'
id2 = '3'
#for documents (encoding in unicode for utf-8)
......@@ -644,7 +644,7 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
publication = portal_sync[self.pub_id]
self.assertEqual(len(publication['1']), nb_document)
gid = self.reference1 + '-' + self.version1 + '-' + self.language1 # ie the title ''
gid = b16encode(gid)
gid = b16encode(gid.encode()).decode()
document_c1 = subscription1.getDocumentFromGid(gid)
document_s = publication.getSubscriber(self.subscription_url['two_way']).getDocumentFromGid(gid)
id_s = document_s.getId()
......
......@@ -31,6 +31,7 @@ import unittest
from base64 import b64encode, b64decode, b16encode
from lxml import etree
from unittest import expectedFailure
from six import string_types as basestring
from AccessControl.SecurityManagement import newSecurityManager
from ERP5Diff import ERP5Diff
......@@ -43,6 +44,7 @@ from erp5.component.module.SyncMLConstant import MAX_LEN
from erp5.component.document import SyncMLSubscription
from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin \
as TestMixin
from six.moves import range
class TestERP5SyncMLMixin(TestMixin):
......@@ -255,7 +257,7 @@ class TestERP5SyncMLMixin(TestMixin):
# only first call will return an answer
result = portal_sync.processServerSynchronization(publication.getPath())
self.tic()
for _ in xrange(2):
for _ in range(2):
portal_sync.processServerSynchronization(publication.getPath())
self.tic()
nb_message += 1
......@@ -263,7 +265,7 @@ class TestERP5SyncMLMixin(TestMixin):
break
result = portal_sync.processClientSynchronization(subscription.getPath())
self.tic()
for _ in xrange(2):
for _ in range(2):
portal_sync.processClientSynchronization(subscription.getPath())
self.tic()
nb_message += 1
......@@ -1095,7 +1097,7 @@ return [context[%r]]
publication = portal_sync[self.pub_id]
self.assertEqual(len(publication.getDocumentList()), nb_person)
gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin'
gid = b16encode(gid)
gid = b16encode(gid.encode()).decode()
person_c1 = subscription1.getDocumentFromGid(gid)
person_s = publication.getSubscriber(self.subscription_url1).getDocumentFromGid(gid)
id_s = person_s.getId()
......@@ -1620,7 +1622,7 @@ return [context[%r]]
# Check same person on client & server side
client_person_module = self.getPersonClient1()
server_person_module = self.getPersonServer()
for x in xrange(1, 61):
for x in range(1, 61):
client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getFirstName(), self.first_name1)
......@@ -1643,7 +1645,7 @@ return [context[%r]]
self.assertEqual(len(subscription1), 0)
self.assertEqual(len(subscriber), 0)
for x in xrange(1, 61):
for x in range(1, 61):
client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getFirstName(), self.first_name2)
......@@ -1663,7 +1665,7 @@ return [context[%r]]
self.assertEqual(len(subscription1), 0)
self.assertEqual(len(subscriber), 0)
for x in xrange(1, 61):
for x in range(1, 61):
client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getLastName(), self.last_name2)
......@@ -1687,31 +1689,26 @@ return [context[%r]]
self.test_08_FirstSynchronization()
#define some strings :
python = 'www.python.org'
awaited_result_python = "d3d3LnB5dGhvbi5vcmc="
long_string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO\
PQRSTUVWXYZéèçà@^~µ&²0123456789!@#0^&*();:<>,. []{}\xc3\xa7sdf__\
sdf\xc3\xa7\xc3\xa7\xc3\xa7_df___&&\xc3\xa9]]]\xc2\xb0\xc2\xb0\xc2\
\xb0\xc2\xb0\xc2\xb0\xc2\xb0"
#= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²012345
#6789!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'"
awaited_result_long_string = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH\
SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpOzo8Pi\
wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=="
python = b'www.python.org'
awaited_result_python = b"d3d3LnB5dGhvbi5vcmc="
long_string = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²0123456789"\
u"!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'".encode('utf-8')
awaited_result_long_string = b'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH'\
b'SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpO'\
b'zo8PiwuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsCc='
#test just b64encode
self.assertEqual(b64encode(python), awaited_result_python)
self.assertEqual(b64encode(""), "")
self.assertEqual(b64encode(b""), b"")
self.assertEqual(b64encode(long_string), awaited_result_long_string)
self.assertEqual(b64decode(awaited_result_python), python)
self.assertEqual(b64decode(""), "")
self.assertEqual(b64decode(b""), b"")
self.assertEqual(b64decode(awaited_result_long_string), long_string)
# test with the ERP5 functions
string_encoded = encode('b64', python)
self.assertEqual(string_encoded, awaited_result_python)
string_decoded = decode('b64', awaited_result_python)
string_decoded = decode('b64', awaited_result_python.decode())
self.assertEqual(string_decoded, python)
self.assertTrue(isDecodeEncodeTheSame(string_encoded,
python, 'b64'))
......@@ -1720,17 +1717,17 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=="
string_encoded = encode('b64', long_string)
self.assertEqual(string_encoded, awaited_result_long_string)
string_decoded = decode('b64', awaited_result_long_string)
string_decoded = decode('b64', awaited_result_long_string.decode())
self.assertEqual(string_decoded, long_string)
self.assertTrue(isDecodeEncodeTheSame(string_encoded,
long_string, 'b64'))
self.assertTrue(isDecodeEncodeTheSame(string_encoded,
string_decoded, 'b64'))
self.assertEqual(encode('b64', ''), '')
self.assertEqual(decode('b64', ''), '')
self.assertEqual(encode('b64', b''), b'')
self.assertEqual(decode('b64', ''), b'')
self.assertTrue(isDecodeEncodeTheSame(
encode('b64', ''), '', 'b64'))
encode('b64', ''), b'', 'b64'))
def test_35_authentication(self):
"""
......
......@@ -26,6 +26,7 @@
##############################################################################
from logging import getLogger
import six
from AccessControl import ClassSecurityInfo
......@@ -184,8 +185,7 @@ class SynchronizationTool(BaseTool):
#
security.declarePublic('readResponse')
def readResponse(self, text='', sync_id=None, from_url=None):
"""
We will look at the url and we will see if we need to send mail, http
"""We will look at the url and we will see if we need to send mail, http
response, or just copy to a file.
"""
syncml_logger.info('readResponse sync_id %s, text %s', sync_id, text)
......@@ -193,6 +193,9 @@ class SynchronizationTool(BaseTool):
# we are still anonymous at this time, use unrestrictedSearchResults
# to fetch the Subcribers
catalog_tool = self.getPortalObject().portal_catalog.unrestrictedSearchResults
if isinstance(text, six.text_type):
text = text.encode('utf-8')
syncml_request = SyncMLRequest(text)
# It is assumed that client & server does not share the same database ID
......@@ -233,12 +236,12 @@ class SynchronizationTool(BaseTool):
% (sync_id, syncml_request.header['target']))
# we use from only if we have a file
elif isinstance(from_url, basestring):
elif isinstance(from_url, six.string_types):
if from_url.startswith('file:'):
filename = from_url[len('file:'):]
xml = None
try:
stream = open(filename, 'r')
stream = open(filename, 'rb')
except IOError:
# XXX-Aurel : Why raising here make unit tests to fail ?
# raise ValueError("Impossible to read file %s, error is %s"
......@@ -313,7 +316,7 @@ class SynchronizationTool(BaseTool):
raise NotImplementedError("Starting sync process from server is forbidden")
# Return message for unit test purpose
return str(syncml_response)
return bytes(syncml_response)
#
# Following methods are related to client (subscription)
......@@ -362,10 +365,10 @@ class SynchronizationTool(BaseTool):
'getAndIndex',
'SQLCatalog_indexSyncMLDocumentList'),
priority=ACTIVITY_PRIORITY,
tag=subscription.getRelativeUrl()).sendMessage(str(syncml_response))
tag=subscription.getRelativeUrl()).sendMessage(bytes(syncml_response))
else:
subscription.sendMessage(str(syncml_response))
subscription.sendMessage(bytes(syncml_response))
return str(syncml_response)
return bytes(syncml_response)
InitializeClass(SynchronizationTool)
......@@ -27,6 +27,7 @@
from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin
from six.moves import range
class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
......@@ -100,7 +101,7 @@ class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
def _fillModule(self, module, nb_objects):
self.title_list = []
append = self.title_list.append
for x in xrange(nb_objects):
for x in range(nb_objects):
module.newContent(title=str(x))
append(str(x))
......
......@@ -123,7 +123,7 @@ def Base_asXML(object, root=None):
# Create blocks to represent data
# <data><block>ZERD</block><block>OEJJM</block></data>
size_block = 60
if isinstance(value, str):
if isinstance(value, bytes):
for index in xrange(0, len(value), size_block):
content = value[index:index + size_block]
data_encoded = standard_b64encode(content)
......@@ -136,7 +136,10 @@ def Base_asXML(object, root=None):
for word in value]
sub_object.append(marshaller(value))
elif prop_type in ('text', 'string',):
sub_object.text = six.text_type(escape(value), 'utf-8')
value = escape(value)
if six.PY2:
value = six.text_type(value, 'utf-8')
sub_object.text = value
elif prop_type != 'None':
sub_object.text = str(value)
......@@ -160,15 +163,22 @@ def Base_asXML(object, root=None):
variable_node = SubElement(workflow_node, workflow_variable,
attrib=dict(type=variable_type))
if variable_type != 'None':
variable_node.text = six.text_type(str(variable_node_text), 'utf-8')
variable_node_text = str(variable_node_text)
if six.PY2:
variable_node_text = six.text_type(str(variable_node_text), 'utf-8')
variable_node.text = variable_node_text
if workflow_variable == 'time':
time = variable_node.text
elif workflow_variable == 'actor':
actor = variable_node.text
workflow_node.attrib['id'] = sha1(workflow_id + time +
str(actor.encode('utf-8'))).hexdigest()
if six.PY2 and isinstance(actor, six.text_type):
actor = actor.encode('utf-8')
workflow_transition_id = workflow_id + time + actor
if six.PY3:
workflow_transition_id = workflow_transition_id.encode()
workflow_node.attrib['id'] = sha1(workflow_transition_id).hexdigest()
# We should now describe security settings
for user_role in self.get_local_roles():
......@@ -177,7 +187,7 @@ def Base_asXML(object, root=None):
#convert local_roles in string because marshaller can't do it
role_list = []
for role in user_role[1]:
if isinstance(role, six.text_type):
if six.PY2 and isinstance(role, six.text_type):
role = role.encode('utf-8')
role_list.append(role)
local_role_node.append(marshaller(tuple(role_list)))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment