From bd59e927a17f8c5ee154fe0359e9d1267f3e301a Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Wed, 5 Oct 2022 11:36:12 +0200
Subject: [PATCH] py2/py3: rename function attributes.

---
 .../portal_components/test.erp5.testFields.py     |  4 ++--
 .../test.erp5.testFacebookLogin.py                | 12 ++++++------
 .../test.erp5.testGoogleLogin.py                  | 12 ++++++------
 .../test.erp5.testOpenIdConnectLogin.py           | 12 ++++++------
 product/ERP5/tests/testSecurity.py                | 13 ++++++++-----
 product/ERP5Type/Accessor/Alias.py                |  2 +-
 product/ERP5Type/Cache.py                         |  2 +-
 product/ERP5Type/patches/ExternalMethod.py        | 15 +++++++++++----
 product/ERP5Type/patches/ZopePageTemplateUtils.py |  2 +-
 product/ERP5Type/tests/Sequence.py                |  2 +-
 10 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/bt5/erp5_core_test/TestTemplateItem/portal_components/test.erp5.testFields.py b/bt5/erp5_core_test/TestTemplateItem/portal_components/test.erp5.testFields.py
index 9baebf92a8..5759003cdb 100644
--- a/bt5/erp5_core_test/TestTemplateItem/portal_components/test.erp5.testFields.py
+++ b/bt5/erp5_core_test/TestTemplateItem/portal_components/test.erp5.testFields.py
@@ -67,10 +67,10 @@ class TestRenderViewAPI(ERP5TypeTestCase):
   def test_signature(self):
     for field in FieldRegistry.get_field_classes().itervalues(): # pylint: disable=no-value-for-parameter
       self.assertEqual(('self', 'value', 'REQUEST', 'render_prefix'),
-                        field.render_view.__func__.func_code.co_varnames)
+                        field.render_view.__code__.co_varnames)
       if field is not ProxyField:
         self.assertEqual(('self', 'field', 'value', 'REQUEST'),
-          field.widget.render_view.__func__.func_code.co_varnames[:4], '%s %s' % (field.widget, field.widget.render_view.__func__.func_code.co_varnames[:4]))
+          field.widget.render_view.__code__.co_varnames[:4], '%s %s' % (field.widget, field.widget.render_view.__code__.co_varnames[:4]))
 
 
 class TestFloatField(ERP5TypeTestCase):
diff --git a/bt5/erp5_oauth_facebook_login/TestTemplateItem/portal_components/test.erp5.testFacebookLogin.py b/bt5/erp5_oauth_facebook_login/TestTemplateItem/portal_components/test.erp5.testFacebookLogin.py
index dbfb00d69d..5e097d1998 100644
--- a/bt5/erp5_oauth_facebook_login/TestTemplateItem/portal_components/test.erp5.testFacebookLogin.py
+++ b/bt5/erp5_oauth_facebook_login/TestTemplateItem/portal_components/test.erp5.testFacebookLogin.py
@@ -126,8 +126,8 @@ class TestFacebookLogin(ERP5TypeTestCase):
         'erp5.component.extension.FacebookLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_callbackFacebookLogin(code=CODE)
     getAccessTokenFromCode_mock.assert_called_once()
     getUserEntry_mock.assert_called_once()
@@ -166,8 +166,8 @@ class TestFacebookLogin(ERP5TypeTestCase):
         'erp5.component.extension.FacebookLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_callbackFacebookLogin(code=CODE)
 
     ac_cookie, = [v for (k, v) in response.listHeaders() if k.lower() == 'set-cookie' and '__ac_facebook_hash=' in v]
@@ -232,8 +232,8 @@ return credential_request
         'erp5.component.extension.FacebookLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       response = self.portal.ERP5Site_callbackFacebookLogin(code=CODE)
     facebook_hash = self.portal.REQUEST.RESPONSE.cookies.get("__ac_facebook_hash")["value"]
     self.assertEqual("8cec04e21e927f1023f4f4980ec11a77", facebook_hash)
diff --git a/bt5/erp5_oauth_google_login/TestTemplateItem/portal_components/test.erp5.testGoogleLogin.py b/bt5/erp5_oauth_google_login/TestTemplateItem/portal_components/test.erp5.testGoogleLogin.py
index 17ac256ae2..f8e4ca6a81 100644
--- a/bt5/erp5_oauth_google_login/TestTemplateItem/portal_components/test.erp5.testGoogleLogin.py
+++ b/bt5/erp5_oauth_google_login/TestTemplateItem/portal_components/test.erp5.testGoogleLogin.py
@@ -163,8 +163,8 @@ class TestGoogleLogin(GoogleLoginTestCase):
         'erp5.component.extension.GoogleLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
     getAccessTokenFromCode_mock.assert_called_once()
     getUserEntry_mock.assert_called_once()
@@ -207,8 +207,8 @@ class TestGoogleLogin(GoogleLoginTestCase):
         'erp5.component.extension.GoogleLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
 
     getAccessTokenFromCode_mock.assert_called_once()
@@ -278,8 +278,8 @@ return credential_request
         'erp5.component.extension.GoogleLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       response = self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
     getAccessTokenFromCode_mock.assert_called_once()
     getUserEntry_mock.assert_called_once()
diff --git a/bt5/erp5_openid_connect_client_login/TestTemplateItem/portal_components/test.erp5.testOpenIdConnectLogin.py b/bt5/erp5_openid_connect_client_login/TestTemplateItem/portal_components/test.erp5.testOpenIdConnectLogin.py
index 76aef9f67d..e6d56a9945 100644
--- a/bt5/erp5_openid_connect_client_login/TestTemplateItem/portal_components/test.erp5.testOpenIdConnectLogin.py
+++ b/bt5/erp5_openid_connect_client_login/TestTemplateItem/portal_components/test.erp5.testOpenIdConnectLogin.py
@@ -126,8 +126,8 @@ class TestOpenIdConnectLogin(OpenIdConnectLoginTestCase):
         'erp5.component.extension.OpenIdConnectLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_receiveOpenIdCallback(code=CODE, state=state)
 
     getAccessTokenFromCode_mock.assert_called_once()
@@ -165,8 +165,8 @@ class TestOpenIdConnectLogin(OpenIdConnectLoginTestCase):
         'erp5.component.extension.OpenIdConnectLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_receiveOpenIdCallback(code=CODE, state=state)
     getAccessTokenFromCode_mock.assert_called_once()
     getUserEntry_mock.assert_called_once()
@@ -260,8 +260,8 @@ return credential_request
         'erp5.component.extension.OpenIdConnectLoginUtility.getUserEntry',
         side_effect=getUserEntry
       ) as getUserEntry_mock:
-      getAccessTokenFromCode_mock.func_code = getAccessTokenFromCode.func_code
-      getUserEntry_mock.func_code = getUserEntry.func_code
+      getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
+      getUserEntry_mock.__code__ = getUserEntry.__code__
       self.portal.ERP5Site_receiveOpenIdCallback(code=CODE, state=state)
     getAccessTokenFromCode_mock.assert_called_once()
     getUserEntry_mock.assert_called_once()
diff --git a/product/ERP5/tests/testSecurity.py b/product/ERP5/tests/testSecurity.py
index 44f500f4e0..c122fdfa84 100644
--- a/product/ERP5/tests/testSecurity.py
+++ b/product/ERP5/tests/testSecurity.py
@@ -75,7 +75,7 @@ class TestSecurityMixin(ERP5TypeTestCase):
     allowed_method_id_list = ['om_icons',]
     app = self.portal.aq_parent
     meta_type_set = set([None])
-    error_set = set()
+    error_dict = {}
     for _, obj in app.ZopeFind(app, search_sub=1):
       meta_type = getattr(obj, 'meta_type', None)
       if meta_type in meta_type_set:
@@ -88,17 +88,20 @@ class TestSecurityMixin(ERP5TypeTestCase):
           continue
         method = getattr(obj, method_id)
         if isinstance(method, MethodType) and \
-          getattr(method, 'func_name', None) is not None and \
+          getattr(method, '__name__', None) is not None and \
           method.__doc__ and \
           not hasattr(obj, '%s__roles__' % method_id) and \
+          not hasattr(method, '__roles__') and \
           method.__module__:
-          if method.__module__ == 'Products.ERP5Type.Accessor.WorkflowState' and method.func_code.co_name == 'serialize':
+          if method.__module__ == 'Products.ERP5Type.Accessor.WorkflowState' and method.__code__.co_name == 'serialize':
             continue
           func_code = method.__code__
-          error_set.add((func_code.co_filename, func_code.co_firstlineno, method_id))
+          if not hasattr(func_code, 'co_filename'): # ERP5 Accessor
+            func_code = method.__func__.__class__.__init__.__code__
+          error_dict[(func_code.co_filename, func_code.co_firstlineno)] = method_id
 
     error_list = []
-    for filename, lineno, method_id in sorted(error_set):
+    for (filename, lineno), method_id in sorted(error_dict.items()):
       # ignore security problems with non ERP5 documents, unless running in debug mode.
       if os.environ.get('erp5_debug_mode') or '/erp5/' in filename or '<portal_components' in filename:
         error_list.append('%s:%s %s' % (filename, lineno, method_id))
diff --git a/product/ERP5Type/Accessor/Alias.py b/product/ERP5Type/Accessor/Alias.py
index 3887b50140..bbc5e0b2cf 100644
--- a/product/ERP5Type/Accessor/Alias.py
+++ b/product/ERP5Type/Accessor/Alias.py
@@ -82,7 +82,7 @@ class Dummy(Reindex):
       self._id = id
       self.__name__ = id
       self._accessor_id = accessor_id
-#      self.__code__ = func_code = getattr(instance, self._accessor_id).func_code
+#      self.__code__ = func_code = getattr(instance, self._accessor_id).__code__
 
     def __call__(self, instance, *args, **kw):
       method = getattr(instance, self._accessor_id)
diff --git a/product/ERP5Type/Cache.py b/product/ERP5Type/Cache.py
index 07afa048eb..d878342716 100644
--- a/product/ERP5Type/Cache.py
+++ b/product/ERP5Type/Cache.py
@@ -353,7 +353,7 @@ def transactional_cached(key_method=_default_key_method):
   def decorator(function):
     # Unfornately, we can only check functions (not other callable like class).
     assert (key_method is not _default_key_method or
-            not getattr(function, 'func_defaults', None)), (
+            not getattr(function, '__defaults__', None)), (
       "default 'key_method' of 'transactional_cached' does not work with"
       " functions having default values for parameters")
     key = repr(function)
diff --git a/product/ERP5Type/patches/ExternalMethod.py b/product/ERP5Type/patches/ExternalMethod.py
index 048ef1e179..db6194cc89 100644
--- a/product/ERP5Type/patches/ExternalMethod.py
+++ b/product/ERP5Type/patches/ExternalMethod.py
@@ -83,14 +83,18 @@ class _(PatchClass(ExternalMethod)):
                 return _f
         except AttributeError:
             pass
-        code = f.func_code
+        code = f.__code__
         argument_object = getargs(code)
         # reconstruct back the original names
         arg_list = argument_object.args[:]
         if argument_object.varargs:
             arg_list.append('*' + argument_object.varargs)
-        if argument_object.keywords:
-          arg_list.append('**' + argument_object.keywords)
+        if six.PY2:
+          if argument_object.keywords:
+            arg_list.append('**' + argument_object.keywords)
+        else:
+          if argument_object.varkw:
+            arg_list.append('**' + argument_object.varkw)
 
         i = isinstance(f, MethodType)
         ff = six.get_unbound_function(f) if i else f
@@ -98,7 +102,10 @@ class _(PatchClass(ExternalMethod)):
         i += has_self
         if i:
             code = FuncCode(ff, i)
-        self._v_f = _f = (f, f.func_defaults, code, has_self, arg_list)
+        try: # This fails with mock function
+            self._v_f = _f = (f, f.__defaults__, code, has_self, arg_list)
+        except AttributeError:
+            self._v_f = _f = (f, f.func_defaults, code, has_self, arg_list)
         return _f
 
     def __call__(self, *args, **kw):
diff --git a/product/ERP5Type/patches/ZopePageTemplateUtils.py b/product/ERP5Type/patches/ZopePageTemplateUtils.py
index 1decfee919..8a58fe63d5 100644
--- a/product/ERP5Type/patches/ZopePageTemplateUtils.py
+++ b/product/ERP5Type/patches/ZopePageTemplateUtils.py
@@ -59,4 +59,4 @@ try:
 except TypeError:
     # We need to monkey patch in-place, as it is a top-level function and
     # already imported in other places.
-    convertToUnicode.func_code = patched_convertToUnicode.func_code
+    convertToUnicode.__code__ = patched_convertToUnicode.__code__
diff --git a/product/ERP5Type/tests/Sequence.py b/product/ERP5Type/tests/Sequence.py
index 7d798c6faf..92ea6b3e57 100644
--- a/product/ERP5Type/tests/Sequence.py
+++ b/product/ERP5Type/tests/Sequence.py
@@ -57,7 +57,7 @@ def special_extract_tb(tb, limit = None):
         else: line = None
 
         # display where we failed in the sequence
-        if co == Sequence.play.func_code:
+        if co == Sequence.play.__code__:
           if line is None:
             line = ''
           sequence = f.f_locals['self']
-- 
2.30.9