2828
2929import distutils .sysconfig
3030
31+ # NOTE: We intentionally use orjson directly here instead of json_encode - orjson.dumps relies
32+ # on config option which we don't parse for the action wrapper since it speeds things down - action
33+ # wrapper should rely on as little imports as possible to make Python runner executions fast -
34+ # that's very important.
35+ import orjson
36+
3137# Note: This work-around is required to fix the issue with other Python modules which live
3238# inside this directory polluting and masking sys.path for Python runner actions.
3339# Since this module is ran as a Python script inside a subprocess, directory where the script
4652 sys .path .insert (0 , distutils .sysconfig .get_python_lib ())
4753
4854import sys
49- import json
5055import argparse
5156
5257import six
@@ -233,19 +238,25 @@ def run(self):
233238 # Special case if result object is not JSON serializable - aka user wanted to return a
234239 # non-simple type (e.g. class instance or other non-JSON serializable type)
235240 try :
236- json .dumps (action_output ["result" ])
237- except TypeError :
241+ orjson .dumps (action_output ["result" ])
242+ except ( TypeError , orjson . JSONDecodeError ) :
238243 action_output ["result" ] = str (action_output ["result" ])
239244
240245 try :
241- print_output = json .dumps (action_output )
246+ print_output = orjson .dumps (action_output )
242247 except Exception :
243- print_output = str (action_output )
248+ print_output = str (action_output ).encode ("utf-8" )
249+
250+ # Data is bytes so we use sys.stdout.buffer which works with bytes and not sys.stdout
251+ # which works with strings / unicodes.
252+ # This way it also works correctly with unicode sequences.
253+ # Technically we could also write to sys.stdout, but this would require additional
254+ # conversion back and forth
244255
245256 # Print output to stdout so the parent can capture it
246- sys .stdout .write (ACTION_OUTPUT_RESULT_DELIMITER )
247- sys .stdout .write (print_output + "\n " )
248- sys .stdout .write (ACTION_OUTPUT_RESULT_DELIMITER )
257+ sys .stdout .buffer . write (ACTION_OUTPUT_RESULT_DELIMITER . encode ( "utf-8" ) )
258+ sys .stdout .buffer . write (print_output + b "\n " )
259+ sys .stdout .buffer . write (ACTION_OUTPUT_RESULT_DELIMITER . encode ( "utf-8" ) )
249260 sys .stdout .flush ()
250261
251262 def _get_action_instance (self ):
@@ -317,9 +328,9 @@ def _get_action_instance(self):
317328 )
318329 args = parser .parse_args ()
319330
320- config = json .loads (args .config ) if args .config else {}
331+ config = orjson .loads (args .config ) if args .config else {}
321332 user = args .user
322- parent_args = json .loads (args .parent_args ) if args .parent_args else []
333+ parent_args = orjson .loads (args .parent_args ) if args .parent_args else []
323334 log_level = args .log_level
324335
325336 if not isinstance (config , dict ):
@@ -330,7 +341,7 @@ def _get_action_instance(self):
330341 if args .parameters :
331342 LOG .debug ("Getting parameters from argument" )
332343 args_parameters = args .parameters
333- args_parameters = json .loads (args_parameters ) if args_parameters else {}
344+ args_parameters = orjson .loads (args_parameters ) if args_parameters else {}
334345 parameters .update (args_parameters )
335346
336347 if args .stdin_parameters :
@@ -349,7 +360,7 @@ def _get_action_instance(self):
349360 stdin_data = sys .stdin .readline ().strip ()
350361
351362 try :
352- stdin_parameters = json .loads (stdin_data )
363+ stdin_parameters = orjson .loads (stdin_data )
353364 stdin_parameters = stdin_parameters .get ("parameters" , {})
354365 except Exception as e :
355366 msg = (
0 commit comments