Commit 4c42f95e authored by Rafael Monnerat's avatar Rafael Monnerat

Update from upstream/master

parents ebcd7ade 6e6eb5f0
...@@ -64,11 +64,11 @@ line_list = context.AccountingTransaction_getAccountingTransactionLineList( ...@@ -64,11 +64,11 @@ line_list = context.AccountingTransaction_getAccountingTransactionLineList(
if not cancellation_amount: if not cancellation_amount:
line_list.reverse() line_list.reverse()
# guess portal_type to create lines
if line_list: if line_list:
# guess portal_type to create lines
line_portal_type = line_list[0].getPortalType() line_portal_type = line_list[0].getPortalType()
for line in line_list: for line in line_list:
new_line = reversal.newContent( portal_type=line_portal_type ) new_line = reversal.newContent( portal_type=line_portal_type )
new_line.edit( new_line.edit(
source=line.getSource(portal_type='Account'), source=line.getSource(portal_type='Account'),
......
...@@ -55,6 +55,7 @@ shopping_cart_items = context.SaleOrder_getShoppingCartItemList() ...@@ -55,6 +55,7 @@ shopping_cart_items = context.SaleOrder_getShoppingCartItemList()
# get category like size and variation # get category like size and variation
category = request.form.get('field_variation_box_your_category', '') category = request.form.get('field_variation_box_your_category', '')
base_category = ''
if category: if category:
[base_category, category] = category.split('/', 1) [base_category, category] = category.split('/', 1)
variation = request.form.get('field_variation_box_your_variation', None) variation = request.form.get('field_variation_box_your_variation', None)
......
...@@ -37,8 +37,7 @@ else: ...@@ -37,8 +37,7 @@ else:
variation_dict["start_date"] = date variation_dict["start_date"] = date
variation_dict["stop_date"] = date+0.00001 variation_dict["stop_date"] = date+0.00001
if web_site_value is not None: price_currency_value = web_site_value.WebSite_getShoppingCartDefaultCurrency()
price_currency_value = web_site_value.WebSite_getShoppingCartDefaultCurrency()
movement = context.newContent( movement = context.newContent(
temp_object=True, temp_object=True,
......
...@@ -263,7 +263,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -263,7 +263,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
[x.getPassword() for x in self._getPasswordEventList(login)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (edit)... # other methods (edit)...
login.edit(password = '123456789-4') login.edit(password='123456789-4')
self.tic() self.tic()
old_password5 = login.getPassword() old_password5 = login.getPassword()
self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
# #
############################################################################## ##############################################################################
import contextlib
import re, zipfile import re, zipfile
from io import BytesIO from io import BytesIO
from warnings import warn from warnings import warn
...@@ -178,7 +179,8 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi ...@@ -178,7 +179,8 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi
warn('Your oood version is too old, using old method ' warn('Your oood version is too old, using old method '
'getAllowedTargets instead of getAllowedTargetList', 'getAllowedTargets instead of getAllowedTargetList',
DeprecationWarning) DeprecationWarning)
finally:
server_proxy.close()
# tuple order is reversed to be compatible with ERP5 Form # tuple order is reversed to be compatible with ERP5 Form
return [(y, x) for x, y in allowed] return [(y, x) for x, y in allowed]
...@@ -208,8 +210,8 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi ...@@ -208,8 +210,8 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi
cs.close() cs.close()
z.close() z.close()
return 'text/plain', s return 'text/plain', s
server_proxy = DocumentConversionServerProxy(self)
orig_format = self.getBaseContentType() orig_format = self.getBaseContentType()
with contextlib.closing(DocumentConversionServerProxy(self)) as server_proxy:
generate_result = server_proxy.run_generate(self.getId(), generate_result = server_proxy.run_generate(self.getId(),
bytes2str(enc(bytes(self.getBaseData()))), bytes2str(enc(bytes(self.getBaseData()))),
None, None,
...@@ -382,7 +384,7 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi ...@@ -382,7 +384,7 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi
by invoking the conversion server. Store the result by invoking the conversion server. Store the result
on the object. Update metadata information. on the object. Update metadata information.
""" """
server_proxy = DocumentConversionServerProxy(self) with contextlib.closing(DocumentConversionServerProxy(self)) as server_proxy:
response_code, response_dict, response_message = server_proxy.run_convert( response_code, response_dict, response_message = server_proxy.run_convert(
self.getFilename() or self.getId(), self.getFilename() or self.getId(),
bytes2str(enc(bytes(self.getData()))), bytes2str(enc(bytes(self.getData()))),
...@@ -421,7 +423,7 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi ...@@ -421,7 +423,7 @@ class OOoDocument(OOoDocumentExtensibleTraversableMixin, BaseConvertableFileMixi
# XXX please pass a meaningful description of error as argument # XXX please pass a meaningful description of error as argument
raise NotConvertedError() raise NotConvertedError()
server_proxy = DocumentConversionServerProxy(self) with contextlib.closing(DocumentConversionServerProxy(self)) as server_proxy:
response_code, response_dict, response_message = \ response_code, response_dict, response_message = \
server_proxy.run_setmetadata(self.getId(), server_proxy.run_setmetadata(self.getId(),
bytes2str(enc(bytes(self.getBaseData()))), bytes2str(enc(bytes(self.getBaseData()))),
......
...@@ -36,10 +36,12 @@ from Acquisition import aq_base ...@@ -36,10 +36,12 @@ from Acquisition import aq_base
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from erp5.component.interface.IEncryptedPassword import IEncryptedPassword from erp5.component.interface.IEncryptedPassword import IEncryptedPassword
from Products.ERP5Type.Globals import PersistentMapping from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type.Utils import bytes2str
from Products.CMFCore.utils import _checkPermission from Products.CMFCore.utils import _checkPermission
from Products.CMFCore.exceptions import AccessControl_Unauthorized from Products.CMFCore.exceptions import AccessControl_Unauthorized
from six import string_types as basestring from six import string_types as basestring
@zope.interface.implementer(IEncryptedPassword,) @zope.interface.implementer(IEncryptedPassword,)
class EncryptedPasswordMixin(object): class EncryptedPasswordMixin(object):
"""Encrypted Password Mixin """Encrypted Password Mixin
...@@ -103,7 +105,7 @@ class EncryptedPasswordMixin(object): ...@@ -103,7 +105,7 @@ class EncryptedPasswordMixin(object):
# workflows on this method. # workflows on this method.
self.password = PersistentMapping() self.password = PersistentMapping()
if value: if value:
self._setEncodedPassword(pw_encrypt(value)) self._setEncodedPassword(bytes2str(pw_encrypt(value)))
def _setPassword(self, value): def _setPassword(self, value):
self.checkPasswordValueAcceptable(value) self.checkPasswordValueAcceptable(value)
......
...@@ -28,6 +28,8 @@ elif aggregation_level == "week": ...@@ -28,6 +28,8 @@ elif aggregation_level == "week":
sql_format = "%Y-%u" sql_format = "%Y-%u"
elif aggregation_level == "day": elif aggregation_level == "day":
sql_format = "%Y-%m-%d" sql_format = "%Y-%m-%d"
else:
raise ValueError("Unsupported aggregation level %s" % aggregation_level)
if to_date is not None: if to_date is not None:
to_date = atTheEndOfPeriod(to_date, period=aggregation_level) to_date = atTheEndOfPeriod(to_date, period=aggregation_level)
count_kw = {} count_kw = {}
......
...@@ -30,7 +30,9 @@ import re ...@@ -30,7 +30,9 @@ import re
import six import six
if six.PY3: if six.PY3:
long = int # pylint:disable=redefined-builtin long_or_int = int
else:
long_or_int = long # pylint:disable=undefined-variable
class BigFile(File): class BigFile(File):
""" """
...@@ -189,13 +191,13 @@ class BigFile(File): ...@@ -189,13 +191,13 @@ class BigFile(File):
else: else:
# Date # Date
date = if_range.split( ';')[0] date = if_range.split( ';')[0]
try: mod_since=long(DateTime(date).timeTime()) try: mod_since=long_or_int(DateTime(date).timeTime())
except Exception: mod_since=None except Exception: mod_since=None
if mod_since is not None: if mod_since is not None:
last_mod = self._data_mtime() last_mod = self._data_mtime()
if last_mod is None: if last_mod is None:
last_mod = 0 last_mod = 0
last_mod = long(last_mod) last_mod = long_or_int(last_mod)
if last_mod > mod_since: if last_mod > mod_since:
# Modified, so send a normal response. We delete # Modified, so send a normal response. We delete
# the ranges, which causes us to skip to the 200 # the ranges, which causes us to skip to the 200
......
...@@ -81,6 +81,8 @@ class CategoriesSpreadsheetConfiguratorItem(ConfiguratorItemMixin, XMLObject): ...@@ -81,6 +81,8 @@ class CategoriesSpreadsheetConfiguratorItem(ConfiguratorItemMixin, XMLObject):
"Base Category %s should be created" % bc_id)) "Base Category %s should be created" % bc_id))
if fixit: if fixit:
bc = ctool.newContent(id=bc_id) bc = ctool.newContent(id=bc_id)
else:
continue
if fixit: if fixit:
for category_info in category_list: for category_info in category_list:
......
...@@ -226,6 +226,7 @@ class ConfiguratorTool(BaseTool): ...@@ -226,6 +226,7 @@ class ConfiguratorTool(BaseTool):
return response return response
## show next form in transitions ## show next form in transitions
html = None
rendered = False rendered = False
while rendered is False: while rendered is False:
if need_validation == 1: if need_validation == 1:
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
</item> </item>
<item> <item>
<key> <string>decimal_rounding_option</string> </key> <key> <string>decimal_rounding_option</string> </key>
<value> <string>ROUND_DOWN</string> </value> <value> <string>ROUND_HALF_UP</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -208,6 +208,7 @@ class TestPinDateTime(ERP5TypeTestCase): ...@@ -208,6 +208,7 @@ class TestPinDateTime(ERP5TypeTestCase):
self.pinDateTime(datetime) self.pinDateTime(datetime)
self.assertEqual(DateTime(), datetime) self.assertEqual(DateTime(), datetime)
self.assertEqual(DateTime(None), datetime)
self.assertEqual(DateTime('2002/02/02 02:02:02 GMT+2'), fixed_date_with_timezone) self.assertEqual(DateTime('2002/02/02 02:02:02 GMT+2'), fixed_date_with_timezone)
self.unpinDateTime() self.unpinDateTime()
...@@ -221,6 +222,24 @@ class TestPinDateTime(ERP5TypeTestCase): ...@@ -221,6 +222,24 @@ class TestPinDateTime(ERP5TypeTestCase):
self.assertEqual(DateTime(), datetime) self.assertEqual(DateTime(), datetime)
self.assertGreaterEqual(DateTime(), actual_begin_date) self.assertGreaterEqual(DateTime(), actual_begin_date)
def test_pinDateTime_date_time_methods(self):
with self.pinDateTime(DateTime('2001/01/01 01:01:01')):
self.assertTrue(DateTime('2000').isPast())
self.assertTrue(DateTime('2002').isFuture())
self.assertTrue(DateTime('2001').isCurrentYear())
self.assertTrue(DateTime('2001/01/02 01:01:01').isCurrentMonth())
self.assertTrue(DateTime('2001/01/01 02:01:01').isCurrentDay())
self.assertTrue(DateTime().strftime('%Y'), '2001')
self.assertTrue(DateTime('2002').strftime('%Y'), '2001')
def test_pinDateTime_timezone(self):
with self.pinDateTime(DateTime('2001/01/01 01:01:01 GMT+9')):
self.assertEqual(DateTime().timezone(), 'GMT+9')
self.assertEqual(DateTime('2001/01/01 01:01:01 GMT+4').timezone(), 'GMT+4')
with self.pinDateTime(DateTime('2001/01/01 01:01:01 Europe/Paris')):
self.assertEqual(DateTime().timezone(), 'Europe/Paris')
self.assertEqual(DateTime('2001/01/01 01:01:01 GMT+4').timezone(), 'GMT+4')
class TestTimeZoneContext(ERP5TypeTestCase): class TestTimeZoneContext(ERP5TypeTestCase):
def afterSetUp(self): def afterSetUp(self):
......
...@@ -58,18 +58,16 @@ class TestERP5TypeInterfaces(unittest.TestCase): ...@@ -58,18 +58,16 @@ class TestERP5TypeInterfaces(unittest.TestCase):
def makeTestMethod(import_tuple, interface): def makeTestMethod(import_tuple, interface):
"""Common method which checks if documents implements interface""" """Common method which checks if documents implements interface"""
def testMethod(self): def testMethod(self):
Klass = getattr( from importlib import import_module
__import__(import_tuple[0], globals(), locals(), [import_tuple[0]]),
import_tuple[1]) Klass = getattr(import_module(import_tuple[0]), import_tuple[1])
import Products.ERP5Type.interfaces import Products.ERP5Type.interfaces
try: try:
Interface = getattr(Products.ERP5Type.interfaces, interface) Interface = getattr(Products.ERP5Type.interfaces, interface)
except AttributeError: except AttributeError:
InterfaceModuleName = 'erp5.component.interface.%s' % interface InterfaceModuleName = 'erp5.component.interface.%s' % interface
Interface = getattr( Interface = getattr(import_module(InterfaceModuleName), interface)
__import__(InterfaceModuleName, globals(), locals(), [InterfaceModuleName]),
interface)
verifyClass(Interface, Klass) verifyClass(Interface, Klass)
......
...@@ -11,6 +11,7 @@ Upgrade link for the specific type of display ...@@ -11,6 +11,7 @@ Upgrade link for the specific type of display
import re import re
link_href = re.findall("href=['\"](.*?)['\"]", link_string)[0] link_href = re.findall("href=['\"](.*?)['\"]", link_string)[0]
link_title = ''
# XXX flag if broken link # XXX flag if broken link
if link_href.find("http") == -1: if link_href.find("http") == -1:
......
...@@ -29,6 +29,7 @@ from Products.PythonScripts.standard import html_quote ...@@ -29,6 +29,7 @@ from Products.PythonScripts.standard import html_quote
from base64 import b64encode from base64 import b64encode
blank = '' blank = ''
slide_content = blank
flags = re.MULTILINE|re.DOTALL|re.IGNORECASE flags = re.MULTILINE|re.DOTALL|re.IGNORECASE
details_separator = '</section><section class="ci-notes-continue"><section><h1>cont.</h1></section>' details_separator = '</section><section class="ci-notes-continue"><section><h1>cont.</h1></section>'
pref = context.getPortalObject().portal_preferences pref = context.getPortalObject().portal_preferences
......
...@@ -2,12 +2,12 @@ getResultValue = context.portal_catalog.getResultValue ...@@ -2,12 +2,12 @@ getResultValue = context.portal_catalog.getResultValue
from Products.ERP5Type.Utils import Email_parseAddressHeader from Products.ERP5Type.Utils import Email_parseAddressHeader
result = [] result = set()
for _, recipient in Email_parseAddressHeader(text): for _, recipient in set(Email_parseAddressHeader(text)):
if recipient: if recipient:
email = getResultValue(url_string={'query':recipient, 'key':'ExactMatch'}, portal_type='Email', parent_portal_type='Person') email = getResultValue(url_string={'query':recipient, 'key':'ExactMatch'}, portal_type='Email', parent_portal_type='Person')
if email is None: if email is None:
email = getResultValue(url_string={'query':recipient, 'key':'ExactMatch'}, portal_type='Email', parent_portal_type='Organisation') email = getResultValue(url_string={'query':recipient, 'key':'ExactMatch'}, portal_type='Email', parent_portal_type='Organisation')
if email is not None: if email is not None:
result.append(email.getParentValue()) result.add(email.getParentValue())
return result return list(result)
...@@ -735,6 +735,8 @@ class TestCRMMailIngestion(BaseTestCRM): ...@@ -735,6 +735,8 @@ class TestCRMMailIngestion(BaseTestCRM):
('me@erp5.org', ['person_module/me']), ('me@erp5.org', ['person_module/me']),
('me@erp5.org, he@erp5.org', ['person_module/me', 'person_module/he']), ('me@erp5.org, he@erp5.org', ['person_module/me', 'person_module/he']),
('Sender <sender@customer.com>', ['person_module/sender']), ('Sender <sender@customer.com>', ['person_module/sender']),
# title is also an email, it should return the person once, not twice
('sender@customer.com <sender@customer.com>', ['person_module/sender']),
# tricks to confuse the e-mail parser: # tricks to confuse the e-mail parser:
# a comma in the name # a comma in the name
('"Sender," <sender@customer.com>, he@erp5.org', ['person_module/sender', ('"Sender," <sender@customer.com>, he@erp5.org', ['person_module/sender',
...@@ -750,9 +752,10 @@ class TestCRMMailIngestion(BaseTestCRM): ...@@ -750,9 +752,10 @@ class TestCRMMailIngestion(BaseTestCRM):
for header, expected_paths in expected_values: for header, expected_paths in expected_values:
paths = [entity.getRelativeUrl() paths = [entity.getRelativeUrl()
for entity in portal.Base_getEntityListFromFromHeader(header)] for entity in portal.Base_getEntityListFromFromHeader(header)]
self.assertEqual(paths, expected_paths, self.assertEqual(
'%r should return %r, but returned %r' % sorted(paths), sorted(expected_paths),
(header, expected_paths, paths)) '%r should return %r, but returned %r' % (header, expected_paths, paths)
)
def test_document_creation(self): def test_document_creation(self):
# CRM email ingestion creates a Mail Message in event_module # CRM email ingestion creates a Mail Message in event_module
......
# type: () -> bytes # type: () -> str
if context.getId() == 'test_ERP5_Logo_Encrypted_PDF': if context.getId() == 'test_ERP5_Logo_Encrypted_PDF':
return 'secret' return 'secret'
return context.skinSuper('erp5_dms_ui_test', 'PDF_getContentPassword')(REQUEST=REQUEST) return context.skinSuper('erp5_dms_ui_test', 'PDF_getContentPassword')(REQUEST=REQUEST)
...@@ -22,6 +22,7 @@ def Listbox_getBrainValue(self, brain, obj, select, can_check_local_property, ed ...@@ -22,6 +22,7 @@ def Listbox_getBrainValue(self, brain, obj, select, can_check_local_property, ed
ListBox.py / getValueList ListBox.py / getValueList
""" """
tales = False tales = False
default_field_value = None
# Use a widget, if any. # Use a widget, if any.
if editable_field is not None: if editable_field is not None:
......
...@@ -4,8 +4,7 @@ Base_translateString = portal.Base_translateString ...@@ -4,8 +4,7 @@ Base_translateString = portal.Base_translateString
preserved_parameter_dict = {} preserved_parameter_dict = {}
Base_doAction = select_action.split() Base_doAction = select_action.split()
if len(Base_doAction) != 0: doAction0 = Base_doAction[0]
doAction0 = Base_doAction[0]
kw['keep_items'] = preserved_parameter_dict kw['keep_items'] = preserved_parameter_dict
......
...@@ -202,7 +202,7 @@ def generateDomainTreeList(url_tool, domain_tool, domain, depth, domain_list): ...@@ -202,7 +202,7 @@ def generateDomainTreeList(url_tool, domain_tool, domain, depth, domain_list):
def getDomainSelection(domain_list): def getDomainSelection(domain_list):
root_dict = {} root_dict = {}
if len(domain_list) > 0: if domain_list:
category_tool = portal.portal_categories category_tool = portal.portal_categories
domain_tool = portal.portal_domains domain_tool = portal.portal_domains
preference_tool = portal.portal_preferences preference_tool = portal.portal_preferences
...@@ -2169,7 +2169,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -2169,7 +2169,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# XXX If only available on brains, maybe better to call on aq_self # XXX If only available on brains, maybe better to call on aq_self
getBrainListItemUrlDict = getattr(brain, 'getListItemUrlDict', None) getBrainListItemUrlDict = getattr(brain, 'getListItemUrlDict', None)
is_getListItemUrlDict_calculated = True is_getListItemUrlDict_calculated = True
if getBrainListItemUrlDict is not None: if getBrainListItemUrlDict is not None: # pylint:disable=possibly-used-before-assignment
# Check if we can get URL result from the brain # Check if we can get URL result from the brain
try: try:
url_parameter_dict = getBrainListItemUrlDict( url_parameter_dict = getBrainListItemUrlDict(
......
...@@ -44,7 +44,6 @@ delivery_count = len(source_section_list) ...@@ -44,7 +44,6 @@ delivery_count = len(source_section_list)
for item in object_list: for item in object_list:
source_section = item.Item_getCurrentOwnerValue() source_section = item.Item_getCurrentOwnerValue()
if source_section is not None: if source_section is not None:
if source_section.getUid() is not None:
pl_value = pl_dict[str(source_section.getUid())] pl_value = pl_dict[str(source_section.getUid())]
else: else:
pl_value = pl_dict['UID'] pl_value = pl_dict['UID']
......
...@@ -72,6 +72,10 @@ ...@@ -72,6 +72,10 @@
<string>reference</string> <string>reference</string>
<string>Reference</string> <string>Reference</string>
</tuple> </tuple>
<tuple>
<string>translated_portal_type</string>
<string>Type</string>
</tuple>
<tuple> <tuple>
<string>translated_validation_state_title</string> <string>translated_validation_state_title</string>
<string>State</string> <string>State</string>
......
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
<key> <string>right</string> </key> <key> <string>right</string> </key>
<value> <value>
<list> <list>
<string>my_translated_portal_type</string>
<string>my_reference</string> <string>my_reference</string>
<string>my_translated_validation_state_title</string> <string>my_translated_validation_state_title</string>
</list> </list>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_translated_portal_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_translated_workflow_state_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Type</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
<key> <string>right</string> </key> <key> <string>right</string> </key>
<value> <value>
<list> <list>
<string>my_translated_portal_type</string>
<string>my_reference</string> <string>my_reference</string>
<string>my_translated_validation_state_title</string> <string>my_translated_validation_state_title</string>
</list> </list>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_translated_portal_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_translated_workflow_state_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Type</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -184,7 +184,14 @@ ...@@ -184,7 +184,14 @@
return RSVP.hash({ return RSVP.hash({
url_list: gadget.getUrlForList([ url_list: gadget.getUrlForList([
{command: 'display'}, {command: 'display'},
{command: 'display', options: {jio_key: "support_request_module"}}, {
command: 'display_stored_state',
options: {
jio_key: "support_request_module",
page: "form",
view: "view"
}
},
{command: 'display', options: {page: "supportrequest_preference"}}, {command: 'display', options: {page: "supportrequest_preference"}},
{command: 'display', options: {page: "logout"}} {command: 'display', options: {page: "logout"}}
]), ]),
......
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>supertitouan</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -242,7 +242,7 @@ ...@@ -242,7 +242,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1017.970.56971.14218</string> </value> <value> <string>1017.64209.22264.37956</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -262,7 +262,7 @@ ...@@ -262,7 +262,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1717413839.14</float> <float>1721121747.19</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
<key> <string>delegated_list</string> </key> <key> <string>delegated_list</string> </key>
<value> <value>
<list> <list>
<string>title</string>
<string>description</string> <string>description</string>
<string>precision</string> <string>precision</string>
<string>title</string>
</list> </list>
</value> </value>
</item> </item>
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
</item> </item>
<item> <item>
<key> <string>precision</string> </key> <key> <string>precision</string> </key>
<value> <string></string> </value> <value> <int>2</int> </value>
</item> </item>
<item> <item>
<key> <string>target</string> </key> <key> <string>target</string> </key>
......
...@@ -70,7 +70,7 @@ def groupSameReportSectionLine(line_to_group_list): ...@@ -70,7 +70,7 @@ def groupSameReportSectionLine(line_to_group_list):
tmp2_base_dict[new_key]['employee_total_price'] = tmp2_base_dict[new_key]['employee_total_price'] + value['employee_total_price'] tmp2_base_dict[new_key]['employee_total_price'] = tmp2_base_dict[new_key]['employee_total_price'] + value['employee_total_price']
new_value_list = [] new_value_list = []
# recalculate for rounding issue # recalculate for rounding issue
for value_dict in tmp2_base_dict.values(): for _, value_dict in sorted(tmp2_base_dict.items()):
value_dict['employer_total_price'] = value_dict['base'] * value_dict['employer_price'] value_dict['employer_total_price'] = value_dict['base'] * value_dict['employer_price']
value_dict['employee_total_price'] = value_dict['base'] * value_dict['employee_price'] value_dict['employee_total_price'] = value_dict['base'] * value_dict['employee_price']
new_value_list.append(value_dict) new_value_list.append(value_dict)
......
...@@ -52,12 +52,10 @@ for requirement_item in requirements_items: ...@@ -52,12 +52,10 @@ for requirement_item in requirements_items:
has_1st_level_requirement = True has_1st_level_requirement = True
new_1st_level_requirement = [] new_1st_level_requirement = []
new_1st_level_requirement_title = requirement_item['title'] new_1st_level_requirement_title = requirement_item['title']
description_dict[new_1st_level_requirement_title] = ''
else: else:
has_1st_level_requirement = False has_1st_level_requirement = False
if has_1st_level_requirement:
description_dict[new_1st_level_requirement_title] = ''
# the item has a second level requirement, built it # the item has a second level requirement, built it
if requirement_item['sub_title'] not in ('', None): if requirement_item['sub_title'] not in ('', None):
has_2nd_level_requirement = True has_2nd_level_requirement = True
......
...@@ -2,8 +2,8 @@ DROP TABLE IF EXISTS worklist_cache ...@@ -2,8 +2,8 @@ DROP TABLE IF EXISTS worklist_cache
<dtml-var sql_delimiter> <dtml-var sql_delimiter>
CREATE TABLE `worklist_cache` ( CREATE TABLE `worklist_cache` (
`count` INT UNSIGNED NOT NULL, `count` INT UNSIGNED NOT NULL,
`owner` VARCHAR(32) DEFAULT '', `owner` VARCHAR(255) binary DEFAULT '',
`viewable_owner` VARCHAR(32) NOT NULL DEFAULT '', `viewable_owner` VARCHAR(255) binary NOT NULL DEFAULT '',
`security_uid` INT UNSIGNED NOT NULL, `security_uid` INT UNSIGNED NOT NULL,
`alternate_security_uid` INT UNSIGNED, `alternate_security_uid` INT UNSIGNED,
`other_security_uid` INT UNSIGNED, `other_security_uid` INT UNSIGNED,
......
...@@ -477,6 +477,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -477,6 +477,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if get_target_parent: if get_target_parent:
result_list = result_list[:-1] result_list = result_list[:-1]
first_object = True first_object = True
sub_context = None
while result_list: while result_list:
object_block = result_list[0][0] object_block = result_list[0][0]
sub_context_id = result_list[0][3] sub_context_id = result_list[0][3]
......
...@@ -128,18 +128,19 @@ def getConduitByName(conduit_name): ...@@ -128,18 +128,19 @@ def getConduitByName(conduit_name):
Conduit can also be defined as Extension to have it editable through the web, in this Conduit can also be defined as Extension to have it editable through the web, in this
case its definition must be Extensions.<Conduit Module> case its definition must be Extensions.<Conduit Module>
""" """
from importlib import import_module
if conduit_name.startswith('Products'): if conduit_name.startswith('Products'):
path = conduit_name path = conduit_name
conduit_name = conduit_name.split('.')[-1] conduit_name = conduit_name.split('.')[-1]
conduit_module = __import__(path, globals(), locals(), ['']) conduit_module = import_module(path)
elif conduit_name.startswith('Extensions'): elif conduit_name.startswith('Extensions'):
conduit_module = __import__(conduit_name, globals(), locals(), ['']) conduit_module = import_module(conduit_name)
conduit_name = conduit_name.split('.')[-1] conduit_name = conduit_name.split('.')[-1]
elif conduit_name.startswith('extension.'): elif conduit_name.startswith('extension.'):
conduit_module = __import__("erp5.component."+conduit_name, globals(), locals(), ['']) conduit_module = import_module("erp5.component." + conduit_name)
conduit_name = conduit_name.split('.')[-1] conduit_name = conduit_name.split('.')[-1]
else: else:
conduit_module = __import__('erp5.component.module.'+conduit_name, globals(), locals(), ['']) conduit_module = import_module('erp5.component.module.' + conduit_name)
conduit_instance = getattr(conduit_module, conduit_name)() conduit_instance = getattr(conduit_module, conduit_name)()
return conduit_instance return conduit_instance
......
...@@ -21,7 +21,7 @@ if line_portal_type in portal.getPortalSaleTypeList(): ...@@ -21,7 +21,7 @@ if line_portal_type in portal.getPortalSaleTypeList():
section_uid = context.getSourceSectionUid() section_uid = context.getSourceSectionUid()
elif line_portal_type in portal.getPortalPurchaseTypeList(): elif line_portal_type in portal.getPortalPurchaseTypeList():
section_uid = context.getDestinationSectionUid() section_uid = context.getDestinationSectionUid()
elif line_portal_type in portal.getPortalInternalTypeList() + portal.getPortalInventoryMovementTypeList(): else:
section_uid = None section_uid = None
len_line_list = len(line_list) len_line_list = len(line_list)
used_id = [] # list use to make sure we do not generate two line with same id/uid used_id = [] # list use to make sure we do not generate two line with same id/uid
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
input information.It should take into account any trade document line input information.It should take into account any trade document line
which were already created so that they are not duplicated. which were already created so that they are not duplicated.
""" """
# pylint:disable=possibly-used-before-assignment
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
portal = context.getPortalObject() portal = context.getPortalObject()
......
...@@ -43,6 +43,7 @@ elif line_portal_type in portal.getPortalInternalTypeList(): ...@@ -43,6 +43,7 @@ elif line_portal_type in portal.getPortalInternalTypeList():
elif line_portal_type in portal.getPortalInventoryMovementTypeList(): elif line_portal_type in portal.getPortalInventoryMovementTypeList():
section_uid = None section_uid = None
no_inventory = True no_inventory = True
supply_cell_portal_type = supply_line_id = None
use_list = portal.portal_preferences.getPreferredPurchaseUseList() \ use_list = portal.portal_preferences.getPreferredPurchaseUseList() \
+ portal.portal_preferences.getPreferredSaleUseList() + portal.portal_preferences.getPreferredSaleUseList()
else: else:
......
...@@ -51,6 +51,8 @@ elif aggregation_level == "week": ...@@ -51,6 +51,8 @@ elif aggregation_level == "week":
date_format = "%Y-%U" date_format = "%Y-%U"
elif aggregation_level == "day": elif aggregation_level == "day":
date_format = "%Y-%m-%d" date_format = "%Y-%m-%d"
else:
raise ValueError("Unsupported aggregation_level: %s" % aggregation_level)
if from_date is not None and at_date is not None: if from_date is not None and at_date is not None:
catalog_params['delivery.start_date'] = { catalog_params['delivery.start_date'] = {
......
...@@ -247,6 +247,7 @@ if force_base_url: ...@@ -247,6 +247,7 @@ if force_base_url:
root_url = "/".join(base_url.split("/", 3)[:3]) root_url = "/".join(base_url.split("/", 3)[:3])
if root_url != base_url: if root_url != base_url:
base_url = "/".join(base_url.split("/")[:-1]) base_url = "/".join(base_url.split("/")[:-1])
request_protocol = 'https:'
else: else:
request_protocol = context.REQUEST.SERVER_URL.split(":", 1)[0] + ":" request_protocol = context.REQUEST.SERVER_URL.split(":", 1)[0] + ":"
root_url = base_url_root_object.absolute_url() root_url = base_url_root_object.absolute_url()
......
...@@ -55,15 +55,14 @@ handler_module_dict = { ...@@ -55,15 +55,14 @@ handler_module_dict = {
'sql' : "SQLConnection", 'sql' : "SQLConnection",
'document' : "DocumentConnection", 'document' : "DocumentConnection",
} }
from importlib import import_module
for handler_id, module_id in handler_module_dict.iteritems(): for handler_id, module_id in handler_module_dict.iteritems():
# Ignore non-functionnal plugins. # Ignore non-functionnal plugins.
# This is done to avoid adding strict dependencies. # This is done to avoid adding strict dependencies.
# Code relying on the presence of a plugin will fail upon # Code relying on the presence of a plugin will fail upon
# WebServiceTool.connect . # WebServiceTool.connect .
try: try:
module = __import__( module = import_module('erp5.component.module.' + module_id)
'erp5.component.module.%s' % (module_id, ),
globals(), {}, [module_id])
except ImportError: except ImportError:
LOG('WebServiceTool', WARNING, LOG('WebServiceTool', WARNING,
'Unable to import module %r. %r transport will not be available.' % \ 'Unable to import module %r. %r transport will not be available.' % \
......
...@@ -2,8 +2,8 @@ DROP TABLE IF EXISTS worklist_cache ...@@ -2,8 +2,8 @@ DROP TABLE IF EXISTS worklist_cache
<dtml-var sql_delimiter> <dtml-var sql_delimiter>
CREATE TABLE `worklist_cache` ( CREATE TABLE `worklist_cache` (
`count` INT UNSIGNED NOT NULL, `count` INT UNSIGNED NOT NULL,
`owner` VARCHAR(32) DEFAULT '', `owner` VARCHAR(255) binary DEFAULT '',
`viewable_owner` VARCHAR(32) NOT NULL DEFAULT '', `viewable_owner` VARCHAR(255) binary NOT NULL DEFAULT '',
`security_uid` INT UNSIGNED NOT NULL, `security_uid` INT UNSIGNED NOT NULL,
`portal_type` VARCHAR(255) NOT NULL, `portal_type` VARCHAR(255) NOT NULL,
`validation_state` VARCHAR(255) NULL, `validation_state` VARCHAR(255) NULL,
......
...@@ -90,6 +90,7 @@ from xml.sax.saxutils import escape ...@@ -90,6 +90,7 @@ from xml.sax.saxutils import escape
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
from six.moves.urllib.parse import quote, unquote, urlparse from six.moves.urllib.parse import quote, unquote, urlparse
from difflib import unified_diff from difflib import unified_diff
from importlib import import_module
import posixpath import posixpath
import transaction import transaction
import inspect import inspect
...@@ -6858,9 +6859,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6858,9 +6859,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
if component_portal_type in ('Document Component', if component_portal_type in ('Document Component',
'Tool Component'): 'Tool Component'):
try: try:
klass = getattr( klass = getattr(import_module(source_reference), subsubmodule_name)
__import__(source_reference, {}, {}, [source_reference]),
subsubmodule_name)
except ImportError as e: except ImportError as e:
LOG("BusinessTemplate", WARNING, LOG("BusinessTemplate", WARNING,
"Skipping %s: Cannot be imported (%s)" % (filepath, e), "Skipping %s: Cannot be imported (%s)" % (filepath, e),
...@@ -6890,7 +6889,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6890,7 +6889,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Generally: foo_bar.py => IFooBar, but to avoid quirks (such as # Generally: foo_bar.py => IFooBar, but to avoid quirks (such as
# 'sql_foo.py' => 'ISQLFoo'), get the Interface class __name__ # 'sql_foo.py' => 'ISQLFoo'), get the Interface class __name__
try: try:
interface_module = __import__(source_reference, {}, {}, source_reference) interface_module = import_module(source_reference)
except ImportError as e: except ImportError as e:
LOG("BusinessTemplate", WARNING, LOG("BusinessTemplate", WARNING,
"Skipping %s: Cannot be imported (%s)" % (filepath, e), "Skipping %s: Cannot be imported (%s)" % (filepath, e),
...@@ -6919,7 +6918,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6919,7 +6918,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# TODO-arnau: Refactor with 'Interface Component' # TODO-arnau: Refactor with 'Interface Component'
elif component_portal_type == 'Mixin Component': elif component_portal_type == 'Mixin Component':
try: try:
mixin_module = __import__(source_reference, {}, {}, source_reference) mixin_module = import_module(source_reference)
except ImportError as e: except ImportError as e:
LOG("BusinessTemplate", WARNING, LOG("BusinessTemplate", WARNING,
"Skipping %s: Cannot be imported (%s)" % (filepath, e), "Skipping %s: Cannot be imported (%s)" % (filepath, e),
......
...@@ -132,6 +132,8 @@ class TrashTool(BaseTool): ...@@ -132,6 +132,8 @@ class TrashTool(BaseTool):
LOG("Trash Tool backupObject", WARNING, LOG("Trash Tool backupObject", WARNING,
"Can't backup object %s" % object_path) "Can't backup object %s" % object_path)
return {} return {}
finally:
copy.close()
subobjects_dict = {} subobjects_dict = {}
......
...@@ -27,8 +27,9 @@ ...@@ -27,8 +27,9 @@
# #
############################################################################## ##############################################################################
import logging
import re import re
from zLOG import LOG, WARNING from zLOG import LOG
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Acquisition import aq_base from Acquisition import aq_base
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
...@@ -102,6 +103,7 @@ class DocumentConversionServerProxy(): ...@@ -102,6 +103,7 @@ class DocumentConversionServerProxy():
""" """
def __init__(self, context): def __init__(self, context):
self._serverproxy_list = [] self._serverproxy_list = []
self._logger = logging.getLogger(__name__)
preference_tool = getToolByName(context, 'portal_preferences') preference_tool = getToolByName(context, 'portal_preferences')
self._ooo_server_retry = ( self._ooo_server_retry = (
preference_tool.getPreferredDocumentConversionServerRetry() or preference_tool.getPreferredDocumentConversionServerRetry() or
...@@ -113,9 +115,9 @@ class DocumentConversionServerProxy(): ...@@ -113,9 +115,9 @@ class DocumentConversionServerProxy():
if not (address and port): if not (address and port):
raise ConversionError('OOoDocument: cannot proceed with conversion:' raise ConversionError('OOoDocument: cannot proceed with conversion:'
' conversion server url is not defined in preferences') ' conversion server url is not defined in preferences')
self._logger.warning(
LOG('Document', WARNING, 'PreferredOoodocServer{Address,PortNumber}' + \ 'PreferredOoodocServer{Address,PortNumber} are DEPRECATED '
' are DEPRECATED please use PreferredDocumentServerUrl instead', error=True) 'please use PreferredDocumentServerUrl instead')
uri_list = ['%s://%s:%s' % ('http', address, port)] uri_list = ['%s://%s:%s' % ('http', address, port)]
...@@ -209,6 +211,18 @@ class DocumentConversionServerProxy(): ...@@ -209,6 +211,18 @@ class DocumentConversionServerProxy():
def __getattr__(self, attr): def __getattr__(self, attr):
return partial(self._proxy_function, attr) return partial(self._proxy_function, attr)
def close(self):
error_list = []
for server_addr, proxy in self._serverproxy_list:
try:
proxy.__call__('close')()
except Exception as e:
self._logger.exception('Error closing %s', server_addr)
error_list.append(e)
for e in error_list:
raise e
from erp5.component.mixin.DocumentExtensibleTraversableMixin import DocumentExtensibleTraversableMixin from erp5.component.mixin.DocumentExtensibleTraversableMixin import DocumentExtensibleTraversableMixin
from erp5.component.interface.IConvertable import IConvertable from erp5.component.interface.IConvertable import IConvertable
from erp5.component.interface.ITextConvertable import ITextConvertable from erp5.component.interface.ITextConvertable import ITextConvertable
......
...@@ -989,7 +989,8 @@ class ImmobilisableItem(Item, Amount): ...@@ -989,7 +989,8 @@ class ImmobilisableItem(Item, Amount):
raw_annuity_price = annuity_start_price * current_ratio raw_annuity_price = annuity_start_price * current_ratio
elif price_calculation_basis == "period recalculated start price": elif price_calculation_basis == "period recalculated start price":
raw_annuity_price = local_period_start_price * current_ratio raw_annuity_price = local_period_start_price * current_ratio
else:
raise ValueError("Unsupported price_calculation_basis: %s" % price_calculation_basis)
# Apply the prorata temporis on the raw annuity value # Apply the prorata temporis on the raw annuity value
if annuity_number and \ if annuity_number and \
price_calculation_basis == 'period recalculated start price' and \ price_calculation_basis == 'period recalculated start price' and \
......
...@@ -422,7 +422,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin): ...@@ -422,7 +422,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin):
quantity = self.getQuantity() quantity = self.getQuantity()
if quantity : if quantity :
source_asset_price = self.getSourceAssetPrice() source_asset_price = self.getSourceAssetPrice()
if source_asset_price : if source_asset_price is not None:
return source_asset_price * - quantity return source_asset_price * - quantity
return None return None
...@@ -466,7 +466,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin): ...@@ -466,7 +466,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin):
quantity = self.getQuantity() quantity = self.getQuantity()
if quantity : if quantity :
destination_asset_price = self.getDestinationAssetPrice() destination_asset_price = self.getDestinationAssetPrice()
if destination_asset_price : if destination_asset_price is not None:
return destination_asset_price * quantity return destination_asset_price * quantity
return None return None
...@@ -520,7 +520,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin): ...@@ -520,7 +520,7 @@ class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin):
def _getAssetPrice(self,section,date): def _getAssetPrice(self,section,date):
price = self.getPrice() price = self.getPrice()
if section is None or not price or getattr( if section is None or price is None or getattr(
section.aq_base, 'getPriceCurrencyValue', None section.aq_base, 'getPriceCurrencyValue', None
) is None: ) is None:
return price return price
......
...@@ -271,6 +271,7 @@ class AmountGeneratorMixin: ...@@ -271,6 +271,7 @@ class AmountGeneratorMixin:
- is rounding really well supported (ie. before and after aggregation) - is rounding really well supported (ie. before and after aggregation)
very likely not - proxying before or after must be decided very likely not - proxying before or after must be decided
""" """
# pylint:disable=self-cls-assignment,possibly-used-before-assignment
# It is the only place where we can import this # It is the only place where we can import this
portal = self.getPortalObject() portal = self.getPortalObject()
getRoundingProxy = portal.portal_roundings.getRoundingProxy getRoundingProxy = portal.portal_roundings.getRoundingProxy
......
...@@ -85,7 +85,7 @@ class _Policy(object): ...@@ -85,7 +85,7 @@ class _Policy(object):
if attr == 'merge_parent': if attr == 'merge_parent':
self.merge_parent = value = self.context.getRootAppliedRule().getPath() self.merge_parent = value = self.context.getRootAppliedRule().getPath()
else: else:
object.__getattribute__(self, attr) value = object.__getattribute__(self, attr)
return value return value
def deferAll(self): def deferAll(self):
......
...@@ -10,13 +10,15 @@ else: ...@@ -10,13 +10,15 @@ else:
now = DateTime() now = DateTime()
kw['expires'] = (now + expire_interval).toZone('GMT').rfc822() kw['expires'] = (now + expire_interval).toZone('GMT').rfc822()
ac_renew = (now + expire_interval / 2).millis() ac_renew = (now + expire_interval / 2).millis()
portal.portal_sessions[
cookie_authentication = getattr(portal, 'cookie_authentication', None)
if cookie_authentication is not None \
and cookie_authentication.getProperty('auth_cookie') == cookie_name:
portal.portal_sessions[
portal.Base_getAutoLogoutSessionKey( portal.Base_getAutoLogoutSessionKey(
username=portal.Base_getUsernameFromAuthenticationCookie( username=portal.Base_getUsernameFromAuthenticationCookie(cookie_value)
cookie_value,
)
) )
]['ac_renew'] = ac_renew ]['ac_renew'] = ac_renew
REQUEST = portal.REQUEST REQUEST = portal.REQUEST
parse_dict = urlparse(REQUEST.other.get('ACTUAL_URL')) parse_dict = urlparse(REQUEST.other.get('ACTUAL_URL'))
......
portal = context.getPortalObject() portal = context.getPortalObject()
if DateTime().millis() >= portal.portal_sessions[
cookie_authentication = getattr(portal, 'cookie_authentication', None)
if cookie_authentication is not None \
and cookie_authentication.getProperty('auth_cookie') == cookie_name \
and DateTime().millis() >= portal.portal_sessions[
portal.Base_getAutoLogoutSessionKey( portal.Base_getAutoLogoutSessionKey(
username=portal.Base_getUsernameFromAuthenticationCookie( username=portal.Base_getUsernameFromAuthenticationCookie(
cookie_value, cookie_value
) )
) )
].get('ac_renew', 0): ].get('ac_renew', 0):
......
...@@ -25,6 +25,7 @@ The returned mapping has the following structure: ...@@ -25,6 +25,7 @@ The returned mapping has the following structure:
This scripts guarantees that the list of category info is sorted in such a This scripts guarantees that the list of category info is sorted in such a
way that parent always precedes their children. way that parent always precedes their children.
""" """
import six
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
from Products.ERP5OOo.OOoUtils import OOoParser from Products.ERP5OOo.OOoUtils import OOoParser
parser = OOoParser() parser = OOoParser()
...@@ -41,41 +42,7 @@ if invalid_spreadsheet_error_handler is None: ...@@ -41,41 +42,7 @@ if invalid_spreadsheet_error_handler is None:
property_id_set = portal.portal_types.Category.getInstancePropertySet() property_id_set = portal.portal_types.Category.getInstancePropertySet()
property_id_set.update(getattr(portal.portal_types, 'Base Category').getInstancePropertySet()) property_id_set.update(getattr(portal.portal_types, 'Base Category').getInstancePropertySet())
def getIDFromString(string=None): getIDFromString = portal.Base_getSafeIdFromString
"""
This function transform a string to a safe and beautiful ID.
It is used here to create a safe category ID from a string.
But the code is not really clever...
"""
if string is None:
return None
clean_id = ''
translation_map = { 'a' : [u'\xe0', u'\xe3']
, 'e' : [u'\xe9', u'\xe8']
, 'i' : [u'\xed']
, 'u' : [u'\xf9']
, '_' : [' ', '+']
, '-' : ['-', u'\u2013']
, 'and': ['&']
}
# Replace odd chars by safe ascii
string = string.lower()
string = string.strip()
for (safe_char, char_list) in translation_map.items():
for char in char_list:
string = string.replace(char, safe_char)
# Exclude all non alphanumeric chars
for char in string:
if char.isalnum() or char in translation_map.keys():
clean_id += char
# Delete leading and trailing char which are not alpha-numerics
# This prevent having IDs with starting underscores
while len(clean_id) > 0 and not clean_id[0].isalnum():
clean_id = clean_id[1:]
while len(clean_id) > 0 and not clean_id[-1].isalnum():
clean_id = clean_id[:-1]
return clean_id
# if the file is not an open office format, try to convert it using oood # if the file is not an open office format, try to convert it using oood
# FIXME: use portal_transforms # FIXME: use portal_transforms
...@@ -129,7 +96,7 @@ for table_name in spreadsheet_list.keys(): ...@@ -129,7 +96,7 @@ for table_name in spreadsheet_list.keys():
else: else:
# If there is a new column with a header and the path definition has # If there is a new column with a header and the path definition has
# started, that seems the path definition has ended # started, that seems the path definition has ended
property_map[column_index] = column_id.encode('utf8') property_map[column_index] = column_id.encode('utf8') if six.PY2 else column_id
column_index += 1 column_index += 1
# Construct categories data (with absolute path) from table lines # Construct categories data (with absolute path) from table lines
...@@ -137,9 +104,9 @@ for table_name in spreadsheet_list.keys(): ...@@ -137,9 +104,9 @@ for table_name in spreadsheet_list.keys():
# 1 table = 1 base category # 1 table = 1 base category
base_category_name = table_name base_category_name = table_name
base_category_id = getIDFromString(base_category_name) base_category_id = getIDFromString(base_category_name)
if same_type(base_category_name, u''): if six.PY2 and isinstance(base_category_name, unicode):
base_category_name = base_category_name.encode('utf8') base_category_name = base_category_name.encode('utf8')
if same_type(base_category_id, u''): if six.PY2 and isinstance(base_category_id, unicode):
base_category_id = base_category_id.encode('utf8') base_category_id = base_category_id.encode('utf8')
category_list = category_list_spreadsheet_mapping.setdefault(base_category_id, []) category_list = category_list_spreadsheet_mapping.setdefault(base_category_id, [])
category_list.append({ 'path' : base_category_id category_list.append({ 'path' : base_category_id
...@@ -198,7 +165,7 @@ for table_name in spreadsheet_list.keys(): ...@@ -198,7 +165,7 @@ for table_name in spreadsheet_list.keys():
if cell_id not in ('', None): if cell_id not in ('', None):
# Handle normal properties # Handle normal properties
if not property_id.startswith('path_'): if not property_id.startswith('path_'):
if same_type(cell_data, u''): if six.PY2 and same_type(cell_data, u''):
cell_data = cell_data.encode('utf8') cell_data = cell_data.encode('utf8')
category_property_list[property_id] = cell_data category_property_list[property_id] = cell_data
# Handle 'path' property # Handle 'path' property
...@@ -218,7 +185,7 @@ for table_name in spreadsheet_list.keys(): ...@@ -218,7 +185,7 @@ for table_name in spreadsheet_list.keys():
# Get the next depth # Get the next depth
break break
path = '/'.join([base_category_id,] + absolute_path_element_list[::-1]) path = '/'.join([base_category_id,] + absolute_path_element_list[::-1])
if same_type(path, u''): if six.PY2 and same_type(path, u''):
path = path.encode('utf8') path = path.encode('utf8')
category_property_list['path'] = path category_property_list['path'] = path
......
...@@ -2,27 +2,32 @@ ...@@ -2,27 +2,32 @@
This function transform a string to a safe id. This function transform a string to a safe id.
It is used here to create a safe category id from a string. It is used here to create a safe category id from a string.
""" """
translation_map = { "a": ['\xe0']
, "e": ['\xe9', '\xe8']
}
clean_id = ''
if s is None: if s is None:
return None return None
clean_id = ''
translation_map = { 'a' : [u'\xe0', u'\xe3']
, 'e' : [u'\xe9', u'\xe8']
, 'i' : [u'\xed']
, 'u' : [u'\xf9']
, '_' : [' ', '+']
, '-' : ['-', u'\u2013']
, 'and': ['&']
}
# Replace odd chars by safe ascii
s = s.lower() s = s.lower()
s = s.strip() s = s.strip()
# oocalc inserts some strange chars when you press - key in a text cell. for (safe_char, char_list) in translation_map.items():
# Following line is a workaround for this, because \u2013 does not exist in latin1 for char in char_list:
s = s.replace(u'\u2013', '-') s = s.replace(char, safe_char)
for char in s.encode('iso8859_1'): # Exclude all non alphanumeric chars
if char == '_' or char.isalnum(): for char in s:
if char.isalnum() or char in translation_map.keys():
clean_id += char clean_id += char
elif char.isspace() or char in ('+', '-'): # Delete leading and trailing char which are not alpha-numerics
clean_id += '_' # This prevent having IDs with starting underscores
else: while len(clean_id) > 0 and not clean_id[0].isalnum():
for (safe_char, char_list) in translation_map.items(): clean_id = clean_id[1:]
if char in char_list: while len(clean_id) > 0 and not clean_id[-1].isalnum():
clean_id += safe_char clean_id = clean_id[:-1]
break
return clean_id return clean_id
...@@ -8,6 +8,7 @@ request=context.REQUEST ...@@ -8,6 +8,7 @@ request=context.REQUEST
# We stop doing this # We stop doing this
#base_category = context.getBaseCategoryId() #base_category = context.getBaseCategoryId()
base_category = None base_category = None
redirect_url = None
o = context.restrictedTraverse(object_path) o = context.restrictedTraverse(object_path)
......
...@@ -42,7 +42,7 @@ skin_id_list = context.getTemplateSkinIdList() ...@@ -42,7 +42,7 @@ skin_id_list = context.getTemplateSkinIdList()
if skin_id_list: if skin_id_list:
if bt_title in skin_id_list: if bt_title in skin_id_list:
main_skin_id = bt_title main_skin_id = bt_title
elif skin_id_list: else:
main_skin_id = skin_id_list[0] main_skin_id = skin_id_list[0]
form_path = '%s/%s' % (main_skin_id, field_library_id) form_path = '%s/%s' % (main_skin_id, field_library_id)
form = getForm(portal.portal_skins[main_skin_id], field_library_id) form = getForm(portal.portal_skins[main_skin_id], field_library_id)
......
...@@ -27,6 +27,13 @@ ...@@ -27,6 +27,13 @@
# #
############################################################################## ##############################################################################
import six
# pylint:disable=import-error,no-name-in-module
if six.PY3:
from collections.abc import Set
else:
from collections import Set
# pylint:enable=import-error,no-name-in-module
import difflib import difflib
import warnings import warnings
try: try:
...@@ -144,7 +151,7 @@ class PortalPatch(Explicit): ...@@ -144,7 +151,7 @@ class PortalPatch(Explicit):
# Flatten the list of DiffValues # Flatten the list of DiffValues
for key, subset in tree_diff.items(): for key, subset in tree_diff.items():
if isinstance(subset, set): if isinstance(subset, Set):
sublist = list(subset) sublist = list(subset)
for item in sublist: for item in sublist:
# XXX: This is important as the subsets with iterable item removed # XXX: This is important as the subsets with iterable item removed
...@@ -192,9 +199,9 @@ class PortalPatch(Explicit): ...@@ -192,9 +199,9 @@ class PortalPatch(Explicit):
else: else:
old_value = val.t1 old_value = val.t1
new_value = val.t2 new_value = val.t2
if (val.t1 == None) or isinstance(val.t1, deepdiff.helper.NotPresent): if (val.t1 is None) or isinstance(val.t1, deepdiff.helper.NotPresent):
old_value = '' old_value = ''
if (val.t2 == None) or isinstance(val.t2, deepdiff.helper.NotPresent): if (val.t2 is None) or isinstance(val.t2, deepdiff.helper.NotPresent):
new_value = '' new_value = ''
# Deepdiff doesn't creates diff for anything other than string, thus for all other cases, # Deepdiff doesn't creates diff for anything other than string, thus for all other cases,
......
...@@ -1568,8 +1568,8 @@ class SimulationTool(BaseTool): ...@@ -1568,8 +1568,8 @@ class SimulationTool(BaseTool):
try: try:
# We must copy the path so that getObject works # We must copy the path so that getObject works
setattr(result, 'path', line_a.path) setattr(result, 'path', line_a.path)
except ValueError: # XXX: ValueError ? really ? except (AttributeError, ValueError):
# getInventory return no object, so no path available # getInventory returned no object, so no path available
pass pass
if parent is not None: if parent is not None:
result = result.__of__(parent) result = result.__of__(parent)
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
CREATE TABLE `catalog` ( CREATE TABLE `catalog` (
`uid` BIGINT UNSIGNED NOT NULL, `uid` BIGINT UNSIGNED NOT NULL,
`security_uid` INT UNSIGNED, `security_uid` INT UNSIGNED,
`owner` varbinary(255) NOT NULL default '', `owner` varchar(255) binary NOT NULL default '',
`viewable_owner` varbinary(255) NOT NULL default '', `viewable_owner` varchar(255) binary NOT NULL default '',
`path` varchar(255) NOT NULL default '', `path` varchar(255) NOT NULL default '',
`relative_url` varchar(255) NOT NULL default '', `relative_url` varchar(255) NOT NULL default '',
`parent_uid` BIGINT UNSIGNED default '0', `parent_uid` BIGINT UNSIGNED default '0',
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
# #
############################################################################## ##############################################################################
import six
import sys import sys
import traceback import traceback
...@@ -84,6 +85,11 @@ class TestERP5PythonScript(ERP5TypeTestCase): ...@@ -84,6 +85,11 @@ class TestERP5PythonScript(ERP5TypeTestCase):
self.script.setBody('return "Hello " + who') self.script.setBody('return "Hello " + who')
self.assertEqual(self.script("world"), "Hello world") self.assertEqual(self.script("world"), "Hello world")
if six.PY2:
filename = 'ERP5 Python Script'
else:
filename = 'ERP5 Python Script:%s' % self.script.getPath()
try: try:
self.script(123) self.script(123)
except TypeError: except TypeError:
...@@ -91,8 +97,8 @@ class TestERP5PythonScript(ERP5TypeTestCase): ...@@ -91,8 +97,8 @@ class TestERP5PythonScript(ERP5TypeTestCase):
# python script code is visible in traceback # python script code is visible in traceback
self.assertEqual( self.assertEqual(
traceback.format_tb(tb)[-1], traceback.format_tb(tb)[-1],
' File "ERP5 Python Script", line 1, in %s\n' ' File "%s", line 1, in %s\n'
' return "Hello " + who\n' % self.id() ' return "Hello " + who\n' % (filename, self.id())
) )
else: else:
self.fail('Exception not raised') self.fail('Exception not raised')
...@@ -126,6 +132,11 @@ class TestERP5WorkflowScript(ERP5TypeTestCase): ...@@ -126,6 +132,11 @@ class TestERP5WorkflowScript(ERP5TypeTestCase):
self.script.setBody('return "Hello " + state_change') self.script.setBody('return "Hello " + state_change')
self.assertEqual(self.script("world"), "Hello world") self.assertEqual(self.script("world"), "Hello world")
if six.PY2:
filename = 'ERP5 Workflow Script'
else:
filename = 'ERP5 Workflow Script:%s' % self.script.getPath()
try: try:
self.script(123) self.script(123)
except TypeError: except TypeError:
...@@ -133,8 +144,8 @@ class TestERP5WorkflowScript(ERP5TypeTestCase): ...@@ -133,8 +144,8 @@ class TestERP5WorkflowScript(ERP5TypeTestCase):
# python script code is visible in traceback # python script code is visible in traceback
self.assertEqual( self.assertEqual(
traceback.format_tb(tb)[-1], traceback.format_tb(tb)[-1],
' File "ERP5 Workflow Script", line 1, in script_test_script\n' (' File "%s", line 1, in script_test_script\n'
' return "Hello " + state_change\n' ' return "Hello " + state_change\n') % filename
) )
else: else:
self.fail('Exception not raised') self.fail('Exception not raised')
...@@ -1708,7 +1708,7 @@ class TestMovementHistoryList(InventoryAPITestCase): ...@@ -1708,7 +1708,7 @@ class TestMovementHistoryList(InventoryAPITestCase):
# default is an empty list # default is an empty list
self.assertEqual(0, len(mvt_history_list)) self.assertEqual(0, len(mvt_history_list))
def testDefault0(self): def testDefaultNone(self):
self._makeMovement() self._makeMovement()
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
mvt_history_list = getMovementHistoryList( mvt_history_list = getMovementHistoryList(
...@@ -1718,6 +1718,32 @@ class TestMovementHistoryList(InventoryAPITestCase): ...@@ -1718,6 +1718,32 @@ class TestMovementHistoryList(InventoryAPITestCase):
# If a movement have no price, None is returned # If a movement have no price, None is returned
self.assertEqual(None, mvt_history_list[0].total_price) self.assertEqual(None, mvt_history_list[0].total_price)
def testPriceZero(self):
self._makeMovement(quantity=1, price=0)
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self.assertEqual(
[(b.total_quantity, b.total_price) for b in getMovementHistoryList(section_uid=self.section.getUid())],
[(1, 0), ]
)
self.assertEqual(
[(b.total_quantity, b.total_price) for b in getMovementHistoryList(section_uid=self.mirror_section.getUid())],
[(-1, 0), ]
)
def testPriceNone(self):
self._makeMovement(quantity=1, price=None)
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
mvt_history_list = getMovementHistoryList(
section_uid=self.section.getUid(),)
self.assertEqual(
[(b.total_quantity, b.total_price) for b in getMovementHistoryList(section_uid=self.section.getUid())],
[(1, None), ]
)
self.assertEqual(
[(b.total_quantity, b.total_price) for b in getMovementHistoryList(section_uid=self.mirror_section.getUid())],
[(-1, None), ]
)
def testMovementBothSides(self): def testMovementBothSides(self):
"""Movement History List returns movement from both sides""" """Movement History List returns movement from both sides"""
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
......
...@@ -667,6 +667,12 @@ class OFSFolder2(OFSFolder): ...@@ -667,6 +667,12 @@ class OFSFolder2(OFSFolder):
except AttributeError as exc: except AttributeError as exc:
raise KeyError(exc.args) raise KeyError(exc.args)
def _cleanup(self):
# Keep compatibility with BTreeFolder2 API, despite it is not reaquired.
# This api is required by the [check,fix]Consistency implementation.
LOG("OFSFolder2._cleanup", WARNING, "This folder class do not implement _cleanup, skip.")
return True
OFS_HANDLER = 0 OFS_HANDLER = 0
BTREE_HANDLER = 1 BTREE_HANDLER = 1
HBTREE_HANDLER = 2 HBTREE_HANDLER = 2
......
...@@ -1076,7 +1076,8 @@ def importLocalDocument(class_id, path=None, class_path=None): ...@@ -1076,7 +1076,8 @@ def importLocalDocument(class_id, path=None, class_path=None):
if class_path: if class_path:
assert path is None assert path is None
module_path = class_path.rsplit('.', 1)[0] module_path = class_path.rsplit('.', 1)[0]
module = __import__(module_path, {}, {}, (module_path,)) from importlib import import_module
module = import_module(module_path)
try: try:
klass = getattr(module, class_id) klass = getattr(module, class_id)
except AttributeError: except AttributeError:
......
...@@ -393,10 +393,12 @@ def save_record(parser, tag, data): ...@@ -393,10 +393,12 @@ def save_record(parser, tag, data):
import xml.parsers.expat import xml.parsers.expat
def importXML(jar, file, clue=''): def importXML(jar, file, clue=''):
if type(file) is str: if isinstance(file, str):
file=open(file, 'rb') with open(file, 'rb') as f:
outfile=TemporaryFile() data = f.read()
data=file.read() else:
data = file.read()
with TemporaryFile() as outfile:
F=ppml.xmlPickler() F=ppml.xmlPickler()
F.end_handlers['record'] = save_record F.end_handlers['record'] = save_record
F.end_handlers['ZopeData'] = save_zopedata F.end_handlers['ZopeData'] = save_zopedata
...@@ -416,7 +418,7 @@ def importXML(jar, file, clue=''): ...@@ -416,7 +418,7 @@ def importXML(jar, file, clue=''):
p.EndElementHandler=F.unknown_endtag p.EndElementHandler=F.unknown_endtag
r=p.Parse(data) r=p.Parse(data)
outfile.seek(0) outfile.seek(0)
return jar.importFile(outfile,clue) return jar.importFile(outfile, clue)
customImporters = { customImporters = {
magic: importXML magic: importXML
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
# There is absolutely no reason to use relative imports when loading a Component # There is absolutely no reason to use relative imports when loading a Component
from __future__ import absolute_import from __future__ import absolute_import
import errno
import os
import six import six
import sys import sys
import imp import imp
...@@ -222,6 +224,15 @@ class ComponentDynamicPackage(ModuleType): ...@@ -222,6 +224,15 @@ class ComponentDynamicPackage(ModuleType):
if import_lock_held: if import_lock_held:
imp.acquire_lock() imp.acquire_lock()
def find_spec(self, name, path=None, target=None):
"""PEP-0451
"""
assert six.PY3
if self.find_module(name, path) is None:
return None
import importlib.util
return importlib.util.spec_from_loader(name, self)
def _getVersionPackage(self, version): def _getVersionPackage(self, version):
""" """
Get the version package (NAMESPACE.VERSION_version) for the given version Get the version package (NAMESPACE.VERSION_version) for the given version
...@@ -321,11 +332,27 @@ class ComponentDynamicPackage(ModuleType): ...@@ -321,11 +332,27 @@ class ComponentDynamicPackage(ModuleType):
component = getattr(site.portal_components, component_id) component = getattr(site.portal_components, component_id)
relative_url = component.getRelativeUrl() relative_url = component.getRelativeUrl()
if six.PY2:
module_file = '<' + relative_url + '>'
else:
module_file = 'erp5://' + relative_url
module_fullname = '%s.%s_version.%s' % (self._namespace, version, name) module_fullname = '%s.%s_version.%s' % (self._namespace, version, name)
module = ModuleType(module_fullname, component.getDescription()) module = ModuleType(module_fullname, component.getDescription())
source_code_str = component.getTextContent(validated_only=True) source_code_str = component.getTextContent(validated_only=True)
for override_path in os.environ.get('ERP5_COMPONENT_OVERRIDE_PATH', '').split(os.pathsep):
try:
local_override_path = os.path.join(override_path, component.getId() + '.py')
with open(local_override_path) as f:
source_code_str = f.read()
module_file = local_override_path
LOG("component_package", WARNING, "Using local override %s" % local_override_path)
break
except IOError as e:
if e.errno != errno.ENOENT:
raise
version_package = self._getVersionPackage(version) version_package = self._getVersionPackage(version)
# All the required objects have been loaded, acquire import lock to modify # All the required objects have been loaded, acquire import lock to modify
...@@ -341,7 +368,7 @@ class ComponentDynamicPackage(ModuleType): ...@@ -341,7 +368,7 @@ class ComponentDynamicPackage(ModuleType):
sys.modules[module_fullname_filesystem] = module sys.modules[module_fullname_filesystem] = module
# This must be set for imports at least (see PEP 302) # This must be set for imports at least (see PEP 302)
module.__file__ = '<' + relative_url + '>' module.__file__ = module_file
if coverage.Coverage.current(): if coverage.Coverage.current():
if hasattr(component, '_erp5_coverage_filename'): if hasattr(component, '_erp5_coverage_filename'):
module.__file__ = component._erp5_coverage_filename module.__file__ = component._erp5_coverage_filename
......
...@@ -41,6 +41,17 @@ class PackageType(ModuleType): ...@@ -41,6 +41,17 @@ class PackageType(ModuleType):
""" """
__path__ = [] __path__ = []
def __init__(self, name, doc=None):
super(PackageType, self).__init__(name=name, doc=doc)
if six.PY3:
# PEP-0451
import importlib.machinery
self.__spec__ = importlib.machinery.ModuleSpec(
name=self.__name__,
loader=None,
)
class RefManager(dict): class RefManager(dict):
""" """
self[ComponentTool.last_sync] = (HTTP_REQUEST_WEAKSET, self[ComponentTool.last_sync] = (HTTP_REQUEST_WEAKSET,
......
...@@ -22,18 +22,13 @@ from . import persistent_migration ...@@ -22,18 +22,13 @@ from . import persistent_migration
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import six import six
class ERP5BaseBroken(Broken, ERP5Base, PersistentBroken):
# PersistentBroken can't be reused directly
# because its « layout differs from 'GhostPortalType' »
# This prevents serialize (ZODB) from reloading the class during commit class PersistentBrokenMetaClass(type):
# (which would look for __Broken_newargs__ which is not present) def __new__(cls, name, bases, d):
__getnewargs__ = None
def __metaclass__(name, base, d):
d = dict(PersistentBroken.__dict__, **d) d = dict(PersistentBroken.__dict__, **d)
for x in '__dict__', '__metaclass__', '__weakref__': del d['__dict__']
del d[x] del d['__weakref__']
def get(x): def get(x):
def get(self): def get(self):
d = self.__dict__ d = self.__dict__
...@@ -44,7 +39,17 @@ class ERP5BaseBroken(Broken, ERP5Base, PersistentBroken): ...@@ -44,7 +39,17 @@ class ERP5BaseBroken(Broken, ERP5Base, PersistentBroken):
return property(get) return property(get)
for x in 'id', 'title': for x in 'id', 'title':
d[x] = get(x) d[x] = get(x)
return type(name, base, d) return type(name, bases, d)
@six.add_metaclass(PersistentBrokenMetaClass)
class ERP5BaseBroken(Broken, ERP5Base, PersistentBroken):
# PersistentBroken can't be reused directly
# because its « layout differs from 'GhostPortalType' »
# This prevents serialize (ZODB) from reloading the class during commit
# (which would look for __Broken_newargs__ which is not present)
__getnewargs__ = None
def __getattr__(self, name): def __getattr__(self, name):
try: try:
......
...@@ -63,9 +63,10 @@ ACQUIRE_LOCAL_ROLE_GETTER_DICT = { ...@@ -63,9 +63,10 @@ ACQUIRE_LOCAL_ROLE_GETTER_DICT = {
def _importFilesystemClass(classpath): def _importFilesystemClass(classpath):
from importlib import import_module
try: try:
module_path, class_name = classpath.rsplit('.', 1) module_path, class_name = classpath.rsplit('.', 1)
module = __import__(module_path, {}, {}, (module_path,)) module = import_module(module_path)
klass = getattr(module, class_name) klass = getattr(module, class_name)
# XXX is this required? (here?) # XXX is this required? (here?)
......
...@@ -380,9 +380,9 @@ class ComponentMixin(with_metaclass(RecordablePropertyMetaClass, type('NewBase', ...@@ -380,9 +380,9 @@ class ComponentMixin(with_metaclass(RecordablePropertyMetaClass, type('NewBase',
if source_reference is None or not source_reference.startswith('Products'): if source_reference is None or not source_reference.startswith('Products'):
path = os.path.join(cls._getFilesystemPath(), reference + '.py') path = os.path.join(cls._getFilesystemPath(), reference + '.py')
else: else:
from importlib import import_module
module_obj = import_module(source_reference)
import inspect import inspect
module_obj = __import__(source_reference, globals(), {},
level=0, fromlist=[source_reference])
path = inspect.getsourcefile(module_obj) path = inspect.getsourcefile(module_obj)
with open(path) as f: with open(path) as f:
......
...@@ -277,11 +277,9 @@ def getObjectMeta(original_function): ...@@ -277,11 +277,9 @@ def getObjectMeta(original_function):
def getObject(module, name, reload=0): def getObject(module, name, reload=0):
# Modified version that ignore errors as long as the module can be be # Modified version that ignore errors as long as the module can be be
# imported, which is enough to use a ZODB Extension as a brain. # imported, which is enough to use a ZODB Extension as a brain.
from importlib import import_module
try: try:
m = __import__('erp5.component.extension.%s' % module, globals(), o = getattr(import_module('erp5.component.extension.%s' % module), name, None)
{}, 'erp5.component.extension')
o = getattr(m, name, None)
if o is None: if o is None:
raise ImportError( raise ImportError(
"Cannot get %s from erp5.component.extension.%s" % (name, module)) "Cannot get %s from erp5.component.extension.%s" % (name, module))
......
...@@ -22,14 +22,14 @@ from io import BytesIO ...@@ -22,14 +22,14 @@ from io import BytesIO
from zExceptions import Forbidden from zExceptions import Forbidden
def getImageInfo_with_svg_fix(data): def getImageInfo_with_svg_fix(data):
data = str(data) data = bytes(data)
size = len(data) size = len(data)
height = -1 height = -1
width = -1 width = -1
content_type = '' content_type = ''
# handle GIFs # handle GIFs
if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'): if (size >= 10) and data[:6] in (b'GIF87a', b'GIF89a'):
# Check to see if content_type is correct # Check to see if content_type is correct
content_type = 'image/gif' content_type = 'image/gif'
w, h = struct.unpack("<HH", data[6:10]) w, h = struct.unpack("<HH", data[6:10])
...@@ -39,15 +39,16 @@ def getImageInfo_with_svg_fix(data): ...@@ -39,15 +39,16 @@ def getImageInfo_with_svg_fix(data):
# See PNG v1.2 spec (http://www.cdrom.com/pub/png/spec/) # See PNG v1.2 spec (http://www.cdrom.com/pub/png/spec/)
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR' # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
# and finally the 4-byte width, height # and finally the 4-byte width, height
elif ((size >= 24) and (data[:8] == '\211PNG\r\n\032\n') elif (size >= 24
and (data[12:16] == 'IHDR')): and data[:8] == b'\211PNG\r\n\032\n'
and data[12:16] == b'IHDR'):
content_type = 'image/png' content_type = 'image/png'
w, h = struct.unpack(">LL", data[16:24]) w, h = struct.unpack(">LL", data[16:24])
width = int(w) width = int(w)
height = int(h) height = int(h)
# Maybe this is for an older PNG version. # Maybe this is for an older PNG version.
elif (size >= 16) and (data[:8] == '\211PNG\r\n\032\n'): elif (size >= 16) and (data[:8] == b'\211PNG\r\n\032\n'):
# Check to see if we have the right content type # Check to see if we have the right content type
content_type = 'image/png' content_type = 'image/png'
w, h = struct.unpack(">LL", data[8:16]) w, h = struct.unpack(">LL", data[8:16])
...@@ -55,29 +56,32 @@ def getImageInfo_with_svg_fix(data): ...@@ -55,29 +56,32 @@ def getImageInfo_with_svg_fix(data):
height = int(h) height = int(h)
# handle JPEGs # handle JPEGs
elif (size >= 2) and (data[:2] == '\377\330'): elif (size >= 2) and (data[:2] == b'\377\330'):
content_type = 'image/jpeg' content_type = 'image/jpeg'
jpeg = BytesIO(data) jpeg = BytesIO(data)
jpeg.read(2) jpeg.read(2)
b = jpeg.read(1) b = jpeg.read(1)
try: try:
while (b and ord(b) != 0xDA): while (b and ord(b) != 0xDA):
while (ord(b) != 0xFF): b = jpeg.read(1) while (ord(b) != 0xFF):
while (ord(b) == 0xFF): b = jpeg.read(1) b = jpeg.read(1)
while (ord(b) == 0xFF):
b = jpeg.read(1)
if (ord(b) >= 0xC0 and ord(b) <= 0xC3): if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
jpeg.read(3) jpeg.read(3)
h, w = struct.unpack(">HH", jpeg.read(4)) h, w = struct.unpack(">HH", jpeg.read(4))
break break
else: else:
jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2) jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0]) - 2)
b = jpeg.read(1) b = jpeg.read(1)
width = int(w) width = int(w)
height = int(h) height = int(h)
except: pass except Exception:
pass
# MONKEY PATCH START HERE # MONKEY PATCH START HERE
# Handle SVG # Handle SVG
elif ("</svg>" in data): elif (b"</svg>" in data):
content_type = 'image/svg+xml' content_type = 'image/svg+xml'
# MONKEY PATCH ENDS HERE # MONKEY PATCH ENDS HERE
......
...@@ -124,8 +124,14 @@ class TypeAccessChecker: ...@@ -124,8 +124,14 @@ class TypeAccessChecker:
as "a method which returing a method" because we can not know what is the as "a method which returing a method" because we can not know what is the
type until it is actually called. So the three ways are simulated the type until it is actually called. So the three ways are simulated the
function returned by this method. function returned by this method.
We don't return a simple function, but a class instance with a __bool__ method
to accomodate the two cases where this is called by SecurityManager.validate when
checking access on the class (then only the bool is used) or by guarded_getattr
when checking access on the instance (the __call__ is used).
""" """
def factory(inst, name): class _AccessChecker:
def __call__(self, inst, name):
""" """
Check function used with ContainerAssertions checked by cAccessControl. Check function used with ContainerAssertions checked by cAccessControl.
""" """
...@@ -143,7 +149,12 @@ class TypeAccessChecker: ...@@ -143,7 +149,12 @@ class TypeAccessChecker:
# fallback to default security # fallback to default security
aq_acquire(inst, name, aq_validate, getSecurityManager().validate) aq_acquire(inst, name, aq_validate, getSecurityManager().validate)
return v return v
return factory
def __bool__(self):
return False
__nonzero__ = __bool__ # six.PY2
return _AccessChecker()
def __bool__(self): def __bool__(self):
# If Containers(type(x)) is true, ZopeGuard checks will short circuit, # If Containers(type(x)) is true, ZopeGuard checks will short circuit,
...@@ -177,8 +188,9 @@ import past.builtins # six.PY2 ...@@ -177,8 +188,9 @@ import past.builtins # six.PY2
allow_module('past.builtins') allow_module('past.builtins')
ModuleSecurityInfo('past.builtins').declarePublic('cmp') ModuleSecurityInfo('past.builtins').declarePublic('cmp')
def guarded_sorted(seq, cmp=None, key=None, reverse=False): if six.PY2:
if cmp is not None: # six.PY2 def guarded_sorted(seq, cmp=None, key=None, reverse=False):
if cmp is not None:
from functools import cmp_to_key from functools import cmp_to_key
key = cmp_to_key(cmp) key = cmp_to_key(cmp)
...@@ -186,7 +198,11 @@ def guarded_sorted(seq, cmp=None, key=None, reverse=False): ...@@ -186,7 +198,11 @@ def guarded_sorted(seq, cmp=None, key=None, reverse=False):
for i, x in enumerate(seq): for i, x in enumerate(seq):
guard(seq, x, i) guard(seq, x, i)
return sorted(seq, key=key, reverse=reverse) return sorted(seq, key=key, reverse=reverse)
safe_builtins['sorted'] = guarded_sorted safe_builtins['sorted'] = guarded_sorted
def guarded_enumerate(seq, start=0):
return NullIter(enumerate(guarded_iter(seq), start=start))
safe_builtins['enumerate'] = guarded_enumerate
def guarded_reversed(seq): def guarded_reversed(seq):
return SafeIter(reversed(seq)) return SafeIter(reversed(seq))
...@@ -195,9 +211,6 @@ ContainerAssertions[reversed] = 1 ...@@ -195,9 +211,6 @@ ContainerAssertions[reversed] = 1
# listreverseiterator is a special type, returned by list.__reversed__ # listreverseiterator is a special type, returned by list.__reversed__
ContainerAssertions[type(reversed([]))] = 1 ContainerAssertions[type(reversed([]))] = 1
def guarded_enumerate(seq, start=0):
return NullIter(enumerate(guarded_iter(seq), start=start))
safe_builtins['enumerate'] = guarded_enumerate
def get_set_pop(s, name): def get_set_pop(s, name):
def guarded_pop(): def guarded_pop():
...@@ -357,6 +370,8 @@ allow_class_attribute(datetime.tzinfo) ...@@ -357,6 +370,8 @@ allow_class_attribute(datetime.tzinfo)
# This prevents both importing _strptime with level=0, and accessing __doc__, # This prevents both importing _strptime with level=0, and accessing __doc__,
# when calling datetime.datetime.strptime(). # when calling datetime.datetime.strptime().
import _strptime import _strptime
# on python3 it seems we actually need to call strptime for this.
datetime.datetime.strptime('', '')
# Allow dict.fromkeys, Only this method is a class method in dict module. # Allow dict.fromkeys, Only this method is a class method in dict module.
allow_class_attribute(dict, {'fromkeys': 1}) allow_class_attribute(dict, {'fromkeys': 1})
...@@ -432,6 +447,9 @@ except ImportError: ...@@ -432,6 +447,9 @@ except ImportError:
import_default_level = -1 import_default_level = -1
def guarded_import(mname, globals=None, locals=None, fromlist=None, def guarded_import(mname, globals=None, locals=None, fromlist=None,
level=import_default_level): level=import_default_level):
# XXX workaround C-code calling PyImport_Import
if mname in ('numpy.core._dtype',):
return __import__(mname, globals, locals, fromlist)
for fromname in fromlist or (): for fromname in fromlist or ():
if fromname[:1] == '_': if fromname[:1] == '_':
raise Unauthorized(fromname) raise Unauthorized(fromname)
...@@ -494,6 +512,7 @@ for dtype in ('int8', 'int16', 'int32', 'int64', \ ...@@ -494,6 +512,7 @@ for dtype in ('int8', 'int16', 'int32', 'int64', \
'uint8', 'uint16', 'uint32', 'uint64', \ 'uint8', 'uint16', 'uint32', 'uint64', \
'float16', 'float32', 'float64', \ 'float16', 'float32', 'float64', \
'complex64', 'complex128'): 'complex64', 'complex128'):
allow_type(type(np.dtype(dtype)))
z = np.array([0,], dtype = dtype) z = np.array([0,], dtype = dtype)
allow_type(type(z[0])) allow_type(type(z[0]))
allow_type(type(z)) allow_type(type(z))
...@@ -509,7 +528,6 @@ for dtype in ('int8', 'int16', 'int32', 'int64', \ ...@@ -509,7 +528,6 @@ for dtype in ('int8', 'int16', 'int32', 'int64', \
allow_type(np.dtype) allow_type(np.dtype)
allow_type(np.timedelta64) allow_type(np.timedelta64)
allow_type(type(np.c_)) allow_type(type(np.c_))
allow_type(type(np.dtype('int16')))
sz = np.array([('2017-07-12T12:30:20',)], dtype=[('date', 'M8[s]')]) sz = np.array([('2017-07-12T12:30:20',)], dtype=[('date', 'M8[s]')])
allow_type(type(sz[0]['date'])) allow_type(type(sz[0]['date']))
...@@ -556,20 +574,26 @@ else: ...@@ -556,20 +574,26 @@ else:
allow_class(pd.DataFrame) allow_class(pd.DataFrame)
# Note: These black_list methods are for pandas 0.19.2 # Note: These black_list methods are for pandas 0.19.2 on PY2 and 1.4.0 on PY3
series_black_list = ('to_csv', 'to_json', 'to_pickle', 'to_hdf', series_black_list = ('to_csv', 'to_json', 'to_pickle', 'to_hdf',
'to_sql', 'to_msgpack') 'to_sql',)
if six.PY2:
series_black_list += ('to_msgpack', )
ContainerAssertions[pd.Series] = _check_access_wrapper( ContainerAssertions[pd.Series] = _check_access_wrapper(
pd.Series, dict.fromkeys(series_black_list, restrictedMethod)) pd.Series, dict.fromkeys(series_black_list, restrictedMethod))
pandas_black_list = ('read_pickle', 'read_hdf', pandas_black_list = ('read_pickle', 'read_hdf',
'read_excel', 'read_html', 'read_msgpack', 'read_excel', 'read_html',
'read_gbq', 'read_sas', 'read_stata') 'read_gbq', 'read_sas', 'read_stata')
if six.PY2:
pandas_black_list += ('read_msgpack', )
ModuleSecurityInfo(MNAME_MAP['pandas']).declarePrivate(*pandas_black_list) ModuleSecurityInfo(MNAME_MAP['pandas']).declarePrivate(*pandas_black_list)
dataframe_black_list = ('to_csv', 'to_json', 'to_pickle', 'to_hdf', dataframe_black_list = ('to_csv', 'to_json', 'to_pickle', 'to_hdf',
'to_excel', 'to_html', 'to_sql', 'to_msgpack', 'to_excel', 'to_html', 'to_sql',
'to_latex', 'to_gbq', 'to_stata') 'to_latex', 'to_gbq', 'to_stata')
if six.PY2:
dataframe_black_list += ('to_msgpack', )
ContainerAssertions[pd.DataFrame] = _check_access_wrapper( ContainerAssertions[pd.DataFrame] = _check_access_wrapper(
pd.DataFrame, dict.fromkeys(dataframe_black_list, restrictedMethod)) pd.DataFrame, dict.fromkeys(dataframe_black_list, restrictedMethod))
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Code based on python-memcached-1.53 # Code based on python-memcached-1.58
try: try:
from memcache import _Host, Client, _Error from memcache import _Host, Client, _Error
except ImportError: except ImportError:
...@@ -15,9 +15,11 @@ else: ...@@ -15,9 +15,11 @@ else:
pass pass
Client.MemcachedConnectionError = _ConnectionDeadError Client.MemcachedConnectionError = _ConnectionDeadError
import six
import socket import socket
def _get(self, cmd, key): def _get(self, cmd, key):
if getattr(self, 'do_check_key', True): key = self._encode_key(key)
if self.do_check_key:
self.check_key(key) self.check_key(key)
server, key = self._get_server(key) server, key = self._get_server(key)
if not server: if not server:
...@@ -29,25 +31,22 @@ else: ...@@ -29,25 +31,22 @@ else:
self._statlog(cmd) self._statlog(cmd)
try: try:
server.send_cmd("%s %s" % (cmd, key)) cmd_bytes = cmd.encode('utf-8') if six.PY3 else cmd
fullcmd = b''.join((cmd_bytes, b' ', key))
server.send_cmd(fullcmd)
rkey = flags = rlen = cas_id = None rkey = flags = rlen = cas_id = None
if cmd == 'gets': if cmd == 'gets':
try: rkey, flags, rlen, cas_id, = self._expect_cas_value(
rkey, flags, rlen, cas_id, = self._expect_cas_value(server, server, raise_exception=True
raise_exception=True) )
except TypeError:
# BBB
rkey, flags, rlen, cas_id, = self._expect_cas_value(server)
if rkey and self.cache_cas: if rkey and self.cache_cas:
self.cas_ids[rkey] = cas_id self.cas_ids[rkey] = cas_id
else: else:
try: rkey, flags, rlen, = self._expectvalue(
rkey, flags, rlen, = self._expectvalue(server, server, raise_exception=True
raise_exception=True) )
except TypeError:
# BBB
rkey, flags, rlen, = self._expectvalue(server)
if not rkey: if not rkey:
# (patch) # (patch)
# return None # return None
...@@ -55,13 +54,10 @@ else: ...@@ -55,13 +54,10 @@ else:
try: try:
value = self._recv_value(server, flags, rlen) value = self._recv_value(server, flags, rlen)
finally: finally:
try: server.expect(b"END", raise_exception=True)
server.expect("END", raise_exception=True)
except TypeError:
# BBB
server.expect("END")
except (_Error, socket.error) as msg: except (_Error, socket.error) as msg:
if isinstance(msg, tuple): msg = msg[1] if isinstance(msg, tuple):
msg = msg[1]
server.mark_dead(msg) server.mark_dead(msg)
# (patch) # (patch)
# return None # return None
......
...@@ -23,6 +23,7 @@ import six ...@@ -23,6 +23,7 @@ import six
import sys import sys
import types import types
import warnings import warnings
import importlib
from Products.ERP5Type import IS_ZOPE2 from Products.ERP5Type import IS_ZOPE2
# TODO: make sure that trying to use it does not import isort, because the # TODO: make sure that trying to use it does not import isort, because the
...@@ -245,10 +246,7 @@ def _getattr(self, name, *args, **kw): ...@@ -245,10 +246,7 @@ def _getattr(self, name, *args, **kw):
# XXX actually maybe we don't need this branch at all on py3 # XXX actually maybe we don't need this branch at all on py3
): ):
raise raise
real_module = __import__( real_module = importlib.import_module(self.name)
self.name,
fromlist=[self.name] if six.PY2 else [name],
level=0)
try: try:
attr = getattr(real_module, name) attr = getattr(real_module, name)
except AttributeError: except AttributeError:
...@@ -462,7 +460,7 @@ def fail_hook_BTrees(modname): ...@@ -462,7 +460,7 @@ def fail_hook_BTrees(modname):
if modname not in _inspected_modules: if modname not in _inspected_modules:
try: try:
modcode = build_stub( modcode = build_stub(
__import__(modname, {}, {}, [modname], level=0), importlib.import_module(modname),
# Exclude all classes ending with 'Py' (no reason to not call the # Exclude all classes ending with 'Py' (no reason to not call the
# C version and not part of public API anyway) # C version and not part of public API anyway)
identifier_re=r'^[A-Za-z_]\w*(?<!Py)$') identifier_re=r'^[A-Za-z_]\w*(?<!Py)$')
...@@ -507,7 +505,7 @@ for filename in os.listdir(os.path.dirname(lxml.__file__)): ...@@ -507,7 +505,7 @@ for filename in os.listdir(os.path.dirname(lxml.__file__)):
module_name = 'lxml.' + filename.split('.', 1)[0] module_name = 'lxml.' + filename.split('.', 1)[0]
_register_module_extender_from_live_module( _register_module_extender_from_live_module(
module_name, module_name,
__import__(module_name, fromlist=[module_name], level=0)) importlib.import_module(module_name))
# Wendelin and XLTE are special namespace packages which pylint fails to recognize, and so # Wendelin and XLTE are special namespace packages which pylint fails to recognize, and so
# complains about things like `from wendelin.bigarray.array_zodb import ZBigArray` # complains about things like `from wendelin.bigarray.array_zodb import ZBigArray`
...@@ -528,7 +526,6 @@ def register_xpkg(pkgname): ...@@ -528,7 +526,6 @@ def register_xpkg(pkgname):
return m return m
MANAGER.register_transform(Module, xpkg_transform, lambda node: node.name == pkgname) MANAGER.register_transform(Module, xpkg_transform, lambda node: node.name == pkgname)
else: else:
import importlib
def fail_hook_xpkg(modname): def fail_hook_xpkg(modname):
if modname.split('.')[0] == pkgname: if modname.split('.')[0] == pkgname:
return MANAGER.ast_from_module(importlib.import_module(modname)) return MANAGER.ast_from_module(importlib.import_module(modname))
......
...@@ -155,9 +155,10 @@ def patch_linecache(): ...@@ -155,9 +155,10 @@ def patch_linecache():
data = get_source(name) data = get_source(name)
except (ImportError, AttributeError): except (ImportError, AttributeError):
pass pass
return data.splitlines(True) if data is not None else () return data.splitlines(True) if data is not None else ()
if module_globals is not None:
# in-ZODB python scripts
if basename(filename) in ('Script (Python)', 'ERP5 Python Script', 'ERP5 Workflow Script'): if basename(filename) in ('Script (Python)', 'ERP5 Python Script', 'ERP5 Workflow Script'):
try: try:
script = module_globals['script'] script = module_globals['script']
...@@ -166,6 +167,8 @@ def patch_linecache(): ...@@ -166,6 +167,8 @@ def patch_linecache():
except Exception: except Exception:
pass pass
return () return ()
# TALES expressions
x = expr_search(filename) x = expr_search(filename)
if x: if x:
return x.groups() return x.groups()
...@@ -173,4 +176,5 @@ def patch_linecache(): ...@@ -173,4 +176,5 @@ def patch_linecache():
linecache.getlines = getlines linecache.getlines = getlines
patch_linecache() if sys.version_info[:3] < (3, ):
patch_linecache()
...@@ -189,6 +189,7 @@ def profile_if_environ(environment_var_name): ...@@ -189,6 +189,7 @@ def profile_if_environ(environment_var_name):
assert getattr(DateTime, '_original_parse_args', None) is None assert getattr(DateTime, '_original_parse_args', None) is None
DateTime._original_parse_args = DateTime._parse_args DateTime._original_parse_args = DateTime._parse_args
_datetime_system_time_patcher = None
_pinned_date_time = None _pinned_date_time = None
def _parse_args(self, *args, **kw): def _parse_args(self, *args, **kw):
...@@ -368,26 +369,38 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase, functional.F ...@@ -368,26 +369,38 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase, functional.F
if not uf.getUserById(user_name): if not uf.getUserById(user_name):
uf._doAddUser(user_name, self.newPassword(), ['Member'], []) uf._doAddUser(user_name, self.newPassword(), ['Member'], [])
def pinDateTime(self, date_time): @classmethod
def pinDateTime(cls, date_time):
# pretend time has stopped at a certain date (i.e. the test runs # pretend time has stopped at a certain date (i.e. the test runs
# infinitely fast), for example to avoid errors on tests that are started # infinitely fast), for example to avoid errors on tests that are started
# just before midnight. # just before midnight.
# This can be used as a context manager, otherwise use unpinDateTime to # This is best used as a context manager, otherwise use unpinDateTime to
# reset. # reset.
global _pinned_date_time global _pinned_date_time, _datetime_system_time_patcher
assert date_time is None or isinstance(date_time, DateTime) assert date_time is None or isinstance(date_time, DateTime)
_pinned_date_time = date_time _pinned_date_time = date_time
unpinDateTime = self.unpinDateTime if _datetime_system_time_patcher is not None:
_datetime_system_time_patcher.stop()
if date_time is not None:
_datetime_system_time_patcher = mock.patch.object(
sys.modules['DateTime.DateTime'],
'_system_time',
return_value=date_time.timeTime())
_datetime_system_time_patcher.start()
unpinDateTime = cls.unpinDateTime
class UnpinContextManager(object): class UnpinContextManager(object):
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, *args): def __exit__(self, *args):
unpinDateTime() unpinDateTime()
_datetime_system_time_patcher.stop()
return UnpinContextManager() return UnpinContextManager()
def unpinDateTime(self): @classmethod
self.pinDateTime(None) def unpinDateTime(cls):
cls.pinDateTime(None)
def setTimeZoneToUTC(self): def setTimeZoneToUTC(self):
# Deprecated, prefer using `timeZoneContext` context manager instead. # Deprecated, prefer using `timeZoneContext` context manager instead.
......
...@@ -62,6 +62,11 @@ class ERP5TypeTestSuite(TestSuite): ...@@ -62,6 +62,11 @@ class ERP5TypeTestSuite(TestSuite):
assert len(marker_connection_string) == len(actual_connection_string) assert len(marker_connection_string) == len(actual_connection_string)
with open(os.path.join(instance_home, 'var', 'Data.fs'), 'rb') as f: with open(os.path.join(instance_home, 'var', 'Data.fs'), 'rb') as f:
data_fs = f.read() data_fs = f.read()
# XXX adjust FileStorage "magic" number so that python3 ZODB accepts reading a
# ZODB for python2, we'll handle the data migration ourselves.
from ZODB._compat import FILESTORAGE_MAGIC
data_fs = FILESTORAGE_MAGIC + data_fs[len(FILESTORAGE_MAGIC):]
with open(os.path.join(instance_home, 'var', 'Data.fs'), 'wb') as f: with open(os.path.join(instance_home, 'var', 'Data.fs'), 'wb') as f:
f.write(data_fs.replace(marker_connection_string, actual_connection_string)) f.write(data_fs.replace(marker_connection_string, actual_connection_string))
......
#!/usr/bin/env python2.7 #!/usr/bin/env python2.7
from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
import os import os
import sys import sys
...@@ -298,10 +299,9 @@ class ERP5TypeTestLoader(unittest.TestLoader): ...@@ -298,10 +299,9 @@ class ERP5TypeTestLoader(unittest.TestLoader):
self._loading_packages = set() self._loading_packages = set()
def _importZodbTestComponent(self, name): def _importZodbTestComponent(self, name):
from importlib import import_module
import erp5.component.test import erp5.component.test
module = __import__('erp5.component.test.' + name, module = import_module('erp5.component.test.' + name)
fromlist=['erp5.component.test'],
level=0)
try: try:
self._test_component_ref_list.append(module) self._test_component_ref_list.append(module)
except AttributeError: except AttributeError:
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
# 02110-1301, USA. # 02110-1301, USA.
# #
############################################################################## ##############################################################################
from __future__ import absolute_import
import gc import gc
import os import os
...@@ -37,6 +38,7 @@ import unittest ...@@ -37,6 +38,7 @@ import unittest
import warnings import warnings
import re import re
import sys import sys
from importlib import import_module
import transaction import transaction
from persistent import Persistent from persistent import Persistent
...@@ -1449,8 +1451,7 @@ class TestZodbModuleComponent(SecurityTestCase): ...@@ -1449,8 +1451,7 @@ class TestZodbModuleComponent(SecurityTestCase):
def afterSetUp(self): def afterSetUp(self):
self._component_tool = self.portal.portal_components self._component_tool = self.portal.portal_components
self._module = __import__(self._document_class._getDynamicModuleNamespace(), self._module = import_module(self._document_class._getDynamicModuleNamespace())
fromlist=['erp5.component'])
self._component_tool.reset(force=True, self._component_tool.reset(force=True,
reset_portal_type_at_transaction_boundary=True) reset_portal_type_at_transaction_boundary=True)
...@@ -1520,7 +1521,7 @@ class TestZodbModuleComponent(SecurityTestCase): ...@@ -1520,7 +1521,7 @@ class TestZodbModuleComponent(SecurityTestCase):
if expected_default_version is not None: if expected_default_version is not None:
top_module_name = self._document_class._getDynamicModuleNamespace() top_module_name = self._document_class._getDynamicModuleNamespace()
top_module = __import__(top_module_name, level=0, fromlist=[top_module_name]) top_module = import_module(top_module_name)
# The module must be available in its default version # The module must be available in its default version
self.assertHasAttribute(top_module, expected_default_version) self.assertHasAttribute(top_module, expected_default_version)
...@@ -1549,10 +1550,7 @@ class TestZodbModuleComponent(SecurityTestCase): ...@@ -1549,10 +1550,7 @@ class TestZodbModuleComponent(SecurityTestCase):
def _importModule(self, module_name): def _importModule(self, module_name):
module_name = self._getComponentFullModuleName(module_name) module_name = self._getComponentFullModuleName(module_name)
module = __import__( module = import_module(module_name)
module_name,
fromlist=[self._document_class._getDynamicModuleNamespace()],
level=0)
self.assertIn(module_name, sys.modules) self.assertIn(module_name, sys.modules)
return module return module
...@@ -2043,8 +2041,7 @@ def bar(*args, **kwargs): ...@@ -2043,8 +2041,7 @@ def bar(*args, **kwargs):
# later that the module has not been added to the top-level package # later that the module has not been added to the top-level package
self.assertModuleImportable('erp5_version.%s' % imported_reference) self.assertModuleImportable('erp5_version.%s' % imported_reference)
top_module = __import__(top_module_name, level=0, top_module = import_module(top_module_name)
fromlist=[top_module_name])
self._importModule('erp5_version.%s' % imported_reference) self._importModule('erp5_version.%s' % imported_reference)
...@@ -2106,8 +2103,7 @@ def function_foo(*args, **kwargs): ...@@ -2106,8 +2103,7 @@ def function_foo(*args, **kwargs):
self.failIfModuleImportable('foo_version.%s' % reference) self.failIfModuleImportable('foo_version.%s' % reference)
top_module_name = self._document_class._getDynamicModuleNamespace() top_module_name = self._document_class._getDynamicModuleNamespace()
top_module = __import__(top_module_name, level=0, top_module = import_module(top_module_name)
fromlist=[top_module_name])
self._importModule(reference) self._importModule(reference)
module = getattr(top_module, reference) module = getattr(top_module, reference)
...@@ -3401,13 +3397,18 @@ break_at_import() ...@@ -3401,13 +3397,18 @@ break_at_import()
return self._component_tool.readTestOutput() return self._component_tool.readTestOutput()
output = runLiveTest('testRunLiveTestImportError') output = runLiveTest('testRunLiveTestImportError')
relative_url = 'portal_components/test.erp5.testRunLiveTestImportError'
if six.PY2:
module_file = '<' + relative_url + '>'
else:
module_file = 'erp5://' + relative_url
self.assertIn(''' self.assertIn('''
File "<portal_components/test.erp5.testRunLiveTestImportError>", line 4, in <module> File "%(module_file)s", line 4, in <module>
break_at_import() break_at_import()
File "<portal_components/test.erp5.testRunLiveTestImportError>", line 3, in break_at_import File "%(module_file)s", line 3, in break_at_import
import non.existing.module # pylint:disable=import-error import non.existing.module # pylint:disable=import-error
ImportError: No module named non.existing.module ImportError: No module named non.existing.module
''', output) ''' % dict(module_file=module_file), output)
output = runLiveTest('testDoesNotExist_import_error_because_module_does_not_exist') output = runLiveTest('testDoesNotExist_import_error_because_module_does_not_exist')
self.assertIn( self.assertIn(
......
This diff is collapsed.
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from zLOG import ERROR from zLOG import ERROR
from six.moves import UserDict from six.moves import UserDict
from importlib import import_module
from zope.interface import implementer from zope.interface import implementer
...@@ -21,7 +22,7 @@ from Products.PortalTransforms.transforms.broken import BrokenTransform ...@@ -21,7 +22,7 @@ from Products.PortalTransforms.transforms.broken import BrokenTransform
def import_from_name(module_name): def import_from_name(module_name):
""" import and return a module by its name """ """ import and return a module by its name """
return __import__(module_name, {}, {}, module_name) return import_module(module_name)
def make_config_persistent(kwargs): def make_config_persistent(kwargs):
""" iterates on the given dictionnary and replace list by persistent list, """ iterates on the given dictionnary and replace list by persistent list,
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from six import unichr from six import unichr
from zLOG import ERROR from zLOG import ERROR
from six.moves.html_parser import HTMLParser, HTMLParseError from six.moves.html_parser import HTMLParser
import re import re
from Products.PythonScripts.standard import html_quote from Products.PythonScripts.standard import html_quote
import codecs import codecs
...@@ -17,6 +17,11 @@ from lxml.etree import HTMLParser as LHTMLParser ...@@ -17,6 +17,11 @@ from lxml.etree import HTMLParser as LHTMLParser
from lxml.html import tostring from lxml.html import tostring
import six import six
if six.PY2:
from six.moves.html_parser import HTMLParseError
else:
HTMLParseError = AssertionError
try: try:
from lxml.html.soupparser import fromstring as soupfromstring from lxml.html.soupparser import fromstring as soupfromstring
except ImportError: except ImportError:
...@@ -365,7 +370,7 @@ def scrubHTML(html, valid=VALID_TAGS, nasty=NASTY_TAGS, ...@@ -365,7 +370,7 @@ def scrubHTML(html, valid=VALID_TAGS, nasty=NASTY_TAGS,
# As suggested by python developpers: # As suggested by python developpers:
# "Python 3.0 implicitly rejects non-unicode strings" # "Python 3.0 implicitly rejects non-unicode strings"
# We try to decode strings against provided codec first # We try to decode strings against provided codec first
if isinstance(html, str): if isinstance(html, bytes):
try: try:
html = html.decode(default_encoding) html = html.decode(default_encoding)
except UnicodeDecodeError: except UnicodeDecodeError:
......
...@@ -33,7 +33,11 @@ class ZSQLBrain(Acquisition.Implicit): ...@@ -33,7 +33,11 @@ class ZSQLBrain(Acquisition.Implicit):
""" """
if name.startswith('__') : if name.startswith('__') :
return None return None
return getattr(self.getObject(), name, None) try:
obj = self.getObject()
except ValueError:
return None
return getattr(obj, name, None)
def getURL(self): def getURL(self):
return self.path return self.path
......
...@@ -4,6 +4,7 @@ import os, subprocess, re ...@@ -4,6 +4,7 @@ import os, subprocess, re
# test_suite is provided by 'run_test_suite' # test_suite is provided by 'run_test_suite'
from test_suite import ERP5TypeTestSuite from test_suite import ERP5TypeTestSuite
import sys import sys
import six
from itertools import chain from itertools import chain
HERE = os.path.dirname(__file__) HERE = os.path.dirname(__file__)
...@@ -46,6 +47,14 @@ class _ERP5(ERP5TypeTestSuite): ...@@ -46,6 +47,14 @@ class _ERP5(ERP5TypeTestSuite):
component_re_match.group(2)) component_re_match.group(2))
else: else:
test_case = test_path.split(os.sep)[-1][:-3] # remove .py test_case = test_path.split(os.sep)[-1][:-3] # remove .py
if six.PY3:
# disable tests that are not compatible with Python 3.
if test_case in (
# using legacy workflow
'erp5_workflow_test:testWorkflowAndDCWorkflow',
'testUpgradeInstanceWithOldDataFsLegacyWorkflow'
):
continue
product = test_path.split(os.sep)[-3] product = test_path.split(os.sep)[-3]
# don't test 3rd party products # don't test 3rd party products
if product in ('PortalTransforms', 'MailTemplates', 'Zelenium'): if product in ('PortalTransforms', 'MailTemplates', 'Zelenium'):
...@@ -231,15 +240,24 @@ class ERP5BusinessTemplateCodingStyleTestSuite(_ERP5): ...@@ -231,15 +240,24 @@ class ERP5BusinessTemplateCodingStyleTestSuite(_ERP5):
"""Run coding style test on all business templates. """Run coding style test on all business templates.
""" """
def getTestList(self): def getTestList(self):
def skip_business_template(path):
# we skip coding style check for business templates having this marker
# property. Since the property is not exported (on purpose), modified business templates
# will be candidate for coding style test again.
if os.path.exists(path + '/bt/skip_coding_style_test'):
return True
if six.PY3 and os.path.basename(path) in (
'erp5_workflow_test', # uses legacy DCWorkflow
):
return True
return False
test_list = [ test_list = [
os.path.basename(path) os.path.basename(path)
for path in chain( for path in chain(
glob(HERE + '/../bt5/*'), glob(HERE + '/../bt5/*'),
glob(HERE + '/../product/ERP5/bootstrap/*')) glob(HERE + '/../product/ERP5/bootstrap/*'))
# we skip coding style check for business templates having this marker if os.path.isdir(path) and not skip_business_template(path)
# property. Since the property is not exported (on purpose), modified business templates
# will be candidate for coding style test again.
if not os.path.exists(path + '/bt/skip_coding_style_test') and os.path.isdir(path)
] ]
for path in chain(glob(HERE + '/../product/*'), for path in chain(glob(HERE + '/../product/*'),
glob(HERE + '/../bt5')): glob(HERE + '/../bt5')):
......
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