From 5e35e6d4c0467d97363313386c4cc7a18484032d Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Wed, 20 Apr 2022 10:55:43 +0200
Subject: [PATCH] py3: No more unbound methods in python3.

---
 product/CMFActivity/tests/testCMFActivity.py  | 21 ++++++++++++++-----
 product/ERP5/ERP5Site.py                      |  6 +++++-
 product/ERP5/Interaction.py                   |  6 +++++-
 product/ERP5/InteractionWorkflow.py           |  8 ++++---
 product/ERP5/mixin/timer_service.py           | 20 ++++++++++++------
 .../ERP5Type/dynamic/persistent_migration.py  |  8 ++++++-
 product/ERP5Type/patches/ExternalMethod.py    |  5 ++++-
 product/ERP5Type/tests/backportUnittest.py    | 10 +++++++--
 8 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/product/CMFActivity/tests/testCMFActivity.py b/product/CMFActivity/tests/testCMFActivity.py
index 2cec1738f6..d0165dfcbd 100644
--- a/product/CMFActivity/tests/testCMFActivity.py
+++ b/product/CMFActivity/tests/testCMFActivity.py
@@ -1883,7 +1883,10 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
       Speed up test by not interrupting the first transaction
       as soon as we have the information we want.
       """
-    original_query = DB.query.__func__
+    original_query = DB.query
+    import six
+    if six.PY2:
+      original_query = original_query.__func__
     def query(self, query_string, *args, **kw):
       if query_string.startswith('INSERT'):
         insert_list.append(len(query_string))
@@ -2054,7 +2057,10 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
   def testTryNotificationSavedOnEventLogWhenNotifyUserRaises(self, activity):
     obj = self.portal.organisation_module.newContent(portal_type='Organisation')
     self.tic()
-    original_notifyUser = Message.notifyUser.im_func
+    original_notifyUser = Message.notifyUser
+    import six
+    if six.PY2:
+      original_notifyUser = original_notifyUser.__func__
     def failSendingEmail(self, *args, **kw):
       raise MailHostError('Mail is not sent')
     activity_unit_test_error = Exception()
@@ -2084,7 +2090,10 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
   def testNotificationFailureIsNotSavedOnEventLogWhenMailNotificationIsDisabled(self, activity):
     obj = self.portal.organisation_module.newContent(portal_type='Organisation')
     self.tic()
-    original_notifyUser = Message.notifyUser.im_func
+    original_notifyUser = Message.notifyUser
+    import six
+    if six.PY2:
+      original_notifyUser = original_notifyUser.__func__
     def failSendingEmail(self, *args, **kw):
       raise MailHostError('Mail is not sent')
     activity_unit_test_error = Exception()
@@ -2152,8 +2161,10 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
     def failingMethod(self):
       raise activity_unit_test_error
     from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
-    original_raising = SiteErrorLog.raising.im_func
-
+    original_raising = SiteErrorLog.raising
+    import six
+    if six.PY2:
+      original_raising = original_raising.__func__
     # Monkey patch Site Error to induce conflict errors artificially.
     def raising(self, info):
       raise AttributeError
diff --git a/product/ERP5/ERP5Site.py b/product/ERP5/ERP5Site.py
index 44cab4b730..ac2775857d 100644
--- a/product/ERP5/ERP5Site.py
+++ b/product/ERP5/ERP5Site.py
@@ -19,6 +19,7 @@ from __future__ import absolute_import
 from DateTime import DateTime
 from six.moves import map
 import thread, threading
+import six
 from weakref import ref as weakref
 from OFS.Application import Application, AppInitializer
 from Products.ERP5Type import Globals
@@ -2437,7 +2438,10 @@ class ERP5Generator(PortalGenerator):
 
 
 # Zope offers no mechanism to extend AppInitializer so let's monkey-patch.
-AppInitializer_initialize = AppInitializer.initialize.__func__
+AppInitializer_initialize = AppInitializer.initialize
+if six.PY2:
+  # No more unbound methods in py3
+  AppInitializer_initialize = AppInitializer_initialize.__func__
 def initialize(self):
   AppInitializer.initialize = AppInitializer_initialize
   self.initialize()
diff --git a/product/ERP5/Interaction.py b/product/ERP5/Interaction.py
index 2af4042ccb..ff5858e2a4 100644
--- a/product/ERP5/Interaction.py
+++ b/product/ERP5/Interaction.py
@@ -304,7 +304,11 @@ class InteractionDefinition (SimpleItem):
 
     def checkGuard(self, *args, **kwargs):
         from Products.ERP5Type.mixin.guardable import GuardableMixin
-        return GuardableMixin.checkGuard.im_func(self, *args, **kwargs)
+        checkGuard = GuardableMixin.checkGuard
+        import six
+        if six.PY2:
+          checkGuard = checkGuard.__func__
+        return checkGuard(self, *args, **kwargs)
 
     def getPortalTypeGroupFilterList(self):
         if self.portal_type_group_filter is None:
diff --git a/product/ERP5/InteractionWorkflow.py b/product/ERP5/InteractionWorkflow.py
index 2b65f56d69..65e6d7ecc8 100644
--- a/product/ERP5/InteractionWorkflow.py
+++ b/product/ERP5/InteractionWorkflow.py
@@ -391,8 +391,10 @@ for method_name, security in (
     ):
   if security is not None:
     security(method_name)
-  setattr(InteractionWorkflowDefinition,
-          method_name,
-          getattr(ERP5InteractionWorkflow, method_name).im_func)
+  func = getattr(ERP5InteractionWorkflow, method_name)
+  import six
+  if six.PY2:
+    func = func.__func__
+  setattr(InteractionWorkflowDefinition, method_name, func)
 
 Globals.InitializeClass(InteractionWorkflowDefinition)
diff --git a/product/ERP5/mixin/timer_service.py b/product/ERP5/mixin/timer_service.py
index b16a2f6cad..55ffbfd1c7 100644
--- a/product/ERP5/mixin/timer_service.py
+++ b/product/ERP5/mixin/timer_service.py
@@ -26,7 +26,7 @@
 #
 ##############################################################################
 
-import warnings
+import warnings, six
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type.Globals import InitializeClass
 from Products.CMFActivity.ActivityTool import ActivityTool
@@ -79,11 +79,19 @@ class TimerServiceMixin(object):
     self.subscribe()
     super(TimerServiceMixin, self).manage_afterAdd(*args, **kw)
 
-  security.declarePublic('getCurrentNode')
-  getCurrentNode = ActivityTool.getCurrentNode.im_func
-  security.declarePublic('getServerAddress')
-  getServerAddress = ActivityTool.getServerAddress.im_func
+  if six.PY2:
+    security.declarePublic('getCurrentNode')
+    getCurrentNode = ActivityTool.getCurrentNode.__func__
+    security.declarePublic('getServerAddress')
+    getServerAddress = ActivityTool.getServerAddress.__func__
+    _isValidNodeName = ActivityTool._isValidNodeName.__func__
+  else:
+    # no more unbound in py3, we got the function directly
+    security.declarePublic('getCurrentNode')
+    getCurrentNode = ActivityTool.getCurrentNode
+    security.declarePublic('getServerAddress')
+    getServerAddress = ActivityTool.getServerAddress
+    _isValidNodeName = ActivityTool._isValidNodeName
 
-  _isValidNodeName = ActivityTool._isValidNodeName.im_func
 
 InitializeClass(TimerServiceMixin)
diff --git a/product/ERP5Type/dynamic/persistent_migration.py b/product/ERP5Type/dynamic/persistent_migration.py
index a3aa70280f..07f60ec284 100644
--- a/product/ERP5Type/dynamic/persistent_migration.py
+++ b/product/ERP5Type/dynamic/persistent_migration.py
@@ -96,7 +96,13 @@ class PickleUpdater(ObjectReader, ObjectWriter, object):
         if _setOb:
           if isinstance(_setOb, WorkflowMethod):
             _setOb = _setOb._m
-          if _setOb.im_func is OFS_Folder._setOb.im_func:
+          import six
+          setOb_func = _setOb
+          OFS_Folder_setOb_func = OFS_Folder._setOb
+          if six.PY2:
+            setOb_func = setOb_func.__func__
+            OFS_Folder_setOb_func = OFS_Folder_setOb.__func__
+          if setOb_func is OFS_Folder_setOb_func:
             self.lazy = Ghost
         elif klass.__module__[:7] == 'BTrees.' and klass.__name__ != 'Length':
           self.lazy = LazyBTree()
diff --git a/product/ERP5Type/patches/ExternalMethod.py b/product/ERP5Type/patches/ExternalMethod.py
index d9f4cfe1c2..0ba5167792 100644
--- a/product/ERP5Type/patches/ExternalMethod.py
+++ b/product/ERP5Type/patches/ExternalMethod.py
@@ -88,7 +88,10 @@ class _(PatchClass(ExternalMethod)):
           arg_list.append('**' + argument_object.keywords)
 
         i = isinstance(f, MethodType)
-        ff = f.__func__ if i else f
+        ff = f
+        import six
+        if six.PY2 and i:
+          ff = f.__func__
         has_self = len(arg_list) > i and arg_list[i] == 'self'
         i += has_self
         if i:
diff --git a/product/ERP5Type/tests/backportUnittest.py b/product/ERP5Type/tests/backportUnittest.py
index 4c47ca09f9..2a679fa50a 100644
--- a/product/ERP5Type/tests/backportUnittest.py
+++ b/product/ERP5Type/tests/backportUnittest.py
@@ -12,7 +12,10 @@ def patch():
     import traceback
     from unittest import TextTestResult, TextTestRunner
 
-    TextTestResult_addError = TextTestResult.addError.__func__
+    TextTestResult_addError = TextTestResult.addError
+    import six
+    if six.PY2:
+      TextTestResult_addError = TextTestResult_addError.__func__
     def addError(self, test, err):
         if isinstance(err[1], SetupSiteError):
             self.errors.append(None)
@@ -40,7 +43,10 @@ def patch():
             self.stream.writeln("SUCCESS: %s" % self.getDescription(test))
     TextTestResult.printErrors = printErrors
 
-    TextTestRunner_run = TextTestRunner.run.__func__
+    TextTestRunner_run = TextTestRunner.run
+    import six
+    if six.PY2:
+        TextTestRunner_run = TextTestRunner_run.__func__
     def run(self, test):
         def t(result):
             try:
-- 
2.30.9