dev_appserver.pyが"TypeError: environment can only contain strings"で動かないのをなんとかする
久々にGoogle AppEngine/PHPのプログラムを修正することになって、ローカルテストしようとdev_appserver.pyを起動したのですが、
ERROR:root:Failure to start PHP with: ['C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\php\\php-5.5-Win32-VC11-x86\\php-cgi.exe', '-d', 'include_path=".;C:\\Users\\FOO\\source\\repos\\appengine-php55;C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\php\\sdk"', '-c', 'C:\\Users\\FOO\\source\\repos\\appengine-php55', '-d', 'zend_extension="C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\php\\php-5.5-Win32-VC11-x86\\php_xdebug.dll"', '-d', 'extension="php_gae_runtime_module.dll"', '-d', 'extension_dir="C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\php\\php-5.5-Win32-VC11-x86"'] Traceback (most recent call last): File "C:\Users\FOO\scoop\apps\gcloud\current\platform\google_appengine\google\appengine\tools\devappserver2\php\runtime\runtime.py", line 269, in __call__ stdout=subprocess.PIPE) INFO 2022-04-09 20:34:10,601 module.py:890] default: "GET /_ah/warmup HTTP/1.1" 500 734 File "C:\Users\FOO\scoop\apps\gcloud\current\platform\google_appengine\google\appengine\tools\devappserver2\safe_subprocess.py", line 83, in start_process shell=shell) File "C:\Users\FOO\scoop\apps\gcloud\current\platform\bundledpython2\lib\subprocess.py", line 390, in __init__ errread, errwrite) startupinfo) TypeError: environment can only contain strings
で起動せず。gcloud componentは最新化したし、「パスに日本語(非ASCII文字)が含まれてませんか?」もNo。 仕方がないのでエラーが出ている runtime.py を解析するハメに。
def __call__(self, environ, start_response): """Handles an HTTP request for the runtime using a PHP executable. Args: environ: An environ dict for the request as defined in PEP-333. start_response: A function with semantics defined in PEP-333. Returns: An iterable over strings containing the body of the HTTP response. """ user_environ = self.make_php_cgi_environ(environ) if 'CONTENT_LENGTH' in environ: content = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) else: content = '' args = [six.ensure_str(arg) for arg in self.make_php_cgi_args()] # Handles interactive request. request_type = environ.pop(http_runtime_constants.REQUEST_TYPE_HEADER, None) if request_type == 'interactive': args.extend(['-d', 'html_errors="0"']) user_environ[http_runtime_constants.REQUEST_TYPE_HEADER] = request_type try: # stderr is not captured here so that it propagates to the parent process # and gets printed out to consle. p = safe_subprocess.start_process( args, input_string=content, env=user_environ, cwd=six.ensure_text(self.config.application_root), stdout=subprocess.PIPE) # ← ここで例外発生 stdout, _ = p.communicate() except Exception as e: logging.exception('Failure to start PHP with: %s', args) start_response('500 Internal Server Error', [(http_runtime_constants.ERROR_CODE_HEADER, '1')]) return ['Failure to start the PHP subprocess with %r:\n%s' % (args, e)]
というコードだったので、まずはuser_environ変数の中身をprint表示してみると
{u'TMP': u'C:\\Users\\FOO\\AppData\\Local\\Temp', u'REQUEST_ID_HASH': u'7CBB4402', u'REDIRECT_STATUS': u'1', u'SERVER_SOFTWARE': u'Development/2.0', u'REQUEST_METHOD': u'GET', u'PATH_INFO': u'/_ah/warmup', u'SERVER_PROTOCOL': u'HTTP/1.1', u'QUERY_STRING': u'', u'SYSTEMROOT': u'C:\\WINDOWS', u'DEFAULT_VERSION_HOSTNAME': u'localhost:8080', u'USER_IS_ADMIN': u'0', u'CONTENT_LENGTH': u'0', u'APPENGINE_RUNTIME': u'php', u'TZ': u'UTC', u'APPLICATION_ROOT': u'C:\\Users\\FOO\\source\\repos\\appengine-php55', u'SERVER_NAME': u'localhost', u'REMOTE_ADDR': u'0.1.0.3', u'INSTANCE_ID': u'a3f67df098d059011b5dafbdc3d41e4aacc2', u'REQUEST_LOG_ID': u'05e41c3d8cc239c03f0c8d4ccdeb797', u'AUTH_DOMAIN': u'gmail.com', u'SERVER_PORT': u'8080', u'CURRENT_MODULE_ID': u'default', u'CURRENT_VERSION_ID': u'None.918090198333333552', u'SCRIPT_FILENAME': u'C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\google\\appengine\\tools\\devappserver2\\php\\runtime\\setup.php', u'USER_ORGANIZATION': u'', u'HTTP_HOST': u'localhost:8080', u'HTTPS': u'off', u'USER_ID': u'', u'REAL_SCRIPT_FILENAME': u'C:\\Users\\FOO\\source\\repos\\appengine-php55\\403.php', u'USER_EMAIL': u'', u'REQUEST_URI': u'/_ah/warmup', u'STDERR_LOG_LEVEL': u'1', u'DATACENTER': u'us1', u'APPLICATION_ID': u'dev~None', u'TEMP': u'C:\\Users\\FOO\\AppData\\Local\\Temp', u'HTTP_X_APPENGINE_COUNTRY': u'ZZ', u'USER_NICKNAME': u'', u'REMOTE_API_PORT': u'52479', u'HTTP_CONTENT_LENGTH': u'0', u'REMOTE_API_HOST': u'localhost', u'REMOTE_REQUEST_ID': u'eRGVBeUVhC'}
という値。「やっぱりどこにも日本語なんか含まれてないじゃん」ということで行き詰りかけたのですが、よくよく見ると
「なんで全部の文字列の前に"u"が付いてるの?」
というわけで、こんなパッチを当ててみました。
--- gcloud/current/platform/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py.orig Tue Jan 1 17:00:00 1980 +++ gcloud/current/platform/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py Sat Apr 9 21:35:32 2022 @@ -259,6 +259,10 @@ user_environ[http_runtime_constants.REQUEST_TYPE_HEADER] = request_type try: + # XXX: fix to convert all env keys and values to str. + # https://stackoverflow.com/a/38476472 + user_environ = { str(key):str(value) for key,value in user_environ.items() } + # stderr is not captured here so that it propagates to the parent process # and gets printed out to consle. p = safe_subprocess.start_process(
これを当てて再度user_environ変数を確認すると
{'TMP': 'C:\\Users\\FOO\\AppData\\Local\\Temp', 'REQUEST_ID_HASH': '7CBB4402', 'REDIRECT_STATUS': '1', 'SERVER_SOFTWARE': 'Development/2.0', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/_ah/warmup', 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': '', 'SYSTEMROOT': 'C:\\WINDOWS', 'DEFAULT_VERSION_HOSTNAME': 'localhost:8080', 'USER_IS_ADMIN': '0', 'CONTENT_LENGTH': '0', 'APPENGINE_RUNTIME': 'php', 'TZ': 'UTC', 'APPLICATION_ROOT': 'C:\\Users\\FOO\\source\\repos\\appengine-php55', 'SERVER_NAME': 'localhost', 'REMOTE_ADDR': '0.1.0.3', 'INSTANCE_ID': 'a3f67df098d059011b5dafbdc3d41e4aacc2', 'REQUEST_LOG_ID': '05e41c3d8cc239c03f0c8d4ccdeb797', 'AUTH_DOMAIN': 'gmail.com', 'SERVER_PORT': '8080', 'CURRENT_MODULE_ID': 'default', 'CURRENT_VERSION_ID': 'None.918090198333333552', 'SCRIPT_FILENAME': 'C:\\Users\\FOO\\scoop\\apps\\gcloud\\current\\platform\\google_appengine\\google\\appengine\\tools\\devappserver2\\php\\runtime\\setup.php', 'USER_ORGANIZATION': '', 'HTTP_HOST': 'localhost:8080', 'HTTPS': 'off', 'USER_ID': '', 'REAL_SCRIPT_FILENAME': 'C:\\Users\\FOO\\source\\repos\\appengine-php55\\403.php', 'USER_EMAIL': '', 'REQUEST_URI': '/_ah/warmup', 'STDERR_LOG_LEVEL': '1', 'DATACENTER': 'us1', 'APPLICATION_ID': 'dev~None', 'TEMP': 'C:\\Users\\FOO\\AppData\\Local\\Temp', 'HTTP_X_APPENGINE_COUNTRY': 'ZZ', 'USER_NICKNAME': '', 'REMOTE_API_PORT': '52479', 'HTTP_CONTENT_LENGTH': '0', 'REMOTE_API_HOST': 'localhost', 'REMOTE_REQUEST_ID': 'eRGVBeUVhC'}
と無事に"u"が外れ、dev_appserver.pyも正常起動するようになりました。めでたし、めでたし。
本来のPHPアプリの修正より、こっちのほうがはるかに時間がかかった...