blob: bf8a2652580fe8709fdc668f939f49ffa6551796 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Adds third_party packages to their respective package namespaces."""
6
7import os
8import six
9import sys
10
11
12def FixImports():
13 """Adds third_party packages to their respective package namespaces."""
14 _AddThirdPartyToPath()
15 _FixProtorpcPackage()
16 _FixDefaultApiStub()
17 _ImportProtocolBuffer()
18 _FixMox3()
19
20
21def _AddThirdPartyToPath():
22 """Adds third_party/ to sys.path.
23
24 This lets us find endpoints."""
25 sys.path.append(_ThirdPartyDir())
26
27
28def _FixProtorpcPackage():
29 """Adds third_party/protorpc/ to protorpc.__path__.
30
31 protorpc generally supports Python 3, except for a few minor issues. protorpc
32 has been unmaintained and archived for years, and will not take pull requests.
33 So, we have a local copy of a few of the files with Python 3 modifications,
34 and update the package __path__ to use our local copy.
35 """
36 import protorpc
37 package_path = os.path.join(_ThirdPartyDir(), 'protorpc')
38 protorpc.__path__.insert(0, package_path)
39
40
41def _FixDefaultApiStub():
42 """Fixes "Attempted RPC call without active security ticket" error.
43
44 In appengine-python-standard==1.0.0, default_api_stub throws an error when
45 trying to access NDB outside of a Flask request. This was fixed in commit
46 cc19a2e on Juy 21, 2022, but wasn't included in the 1.0.1rc1 release on
47 Sep 6, 2022. It's been months since that release, so instead of waiting on
48 another release, we'll just monkeypatch the file here.
49 """
50 if not six.PY3:
51 return
52 sys.path.append(os.path.join(_ThirdPartyDir(), 'appengine-python-standard'))
53 import default_api_stub as fixed_default_api_stub
54 from google.appengine.runtime import default_api_stub
55 default_api_stub.DefaultApiRPC = fixed_default_api_stub.DefaultApiRPC
56
57
58def _ImportProtocolBuffer():
59 """Adds google.net.proto.ProtocolBuffer to the importable packages.
60
61 The appengine-python-standard package doesn't include
62 google.net.proto.ProtocolBuffer. So, we include a local copy in
63 third_party/, and modify the package __path__ to use our local copy.
64 """
65 # Add third_party/google/ to the google namespace.
66 # This makes Python look in this additional location for google.net.proto.
67 import google
68 package_path = os.path.join(_ThirdPartyDir(), 'google')
69 google.__path__.append(package_path)
70
71
72def _FixMox3():
73 """Fixes a Python 3 warning with the mox3 library.
74
75 mox3 uses `inspect.getargspec()`, which is deprecated since Python 3.0.
76 This throws a warning when running unit tests. Update the method to use
77 `inspect.getfullargspec()` instead.
78 """
79 from mox3 import mox
80 mox.MethodSignatureChecker.__init__ = _MethodSignatureChecker
81
82
83def _ThirdPartyDir():
84 return os.path.join(os.path.dirname(__file__), 'third_party')
85
86
87def _MethodSignatureChecker(self, method, class_to_bind=None):
88 """Creates a checker.
89
90 Args:
91 # method: A method to check.
92 # class_to_bind: optionally, a class used to type check first
93 # method parameter, only used with unbound methods
94 method: function
95 class_to_bind: type or None
96
97 Raises:
98 ValueError: method could not be inspected, so checks aren't
99 possible. Some methods and functions like built-ins
100 can't be inspected.
101 """
102 import inspect
103 try:
104 self._args, varargs, varkw, defaults, _, _, _ = inspect.getfullargspec(
105 method)
106 except TypeError:
107 raise ValueError('Could not get argument specification for %r' % (method,))
108 if (inspect.ismethod(method) or class_to_bind or
109 (hasattr(self, '_args') and len(self._args) > 0 and
110 self._args[0] == 'self')):
111 self._args = self._args[1:] # Skip 'self'.
112 self._method = method
113 self._instance = None # May contain the instance this is bound to.
114 self._instance = getattr(method, "__self__", None)
115
116 # _bounded_to determines whether the method is bound or not
117 if self._instance:
118 self._bounded_to = self._instance.__class__
119 else:
120 self._bounded_to = class_to_bind or getattr(method, "im_class", None)
121
122 self._has_varargs = varargs is not None
123 self._has_varkw = varkw is not None
124 if defaults is None:
125 self._required_args = self._args
126 self._default_args = []
127 else:
128 self._required_args = self._args[:-len(defaults)]
129 self._default_args = self._args[-len(defaults):]