@ -20,6 +20,7 @@
from __future__ import ( absolute_import , division , print_function )
from __future__ import ( absolute_import , division , print_function )
__metaclass__ = type
__metaclass__ = type
import ast
import base64
import base64
import json
import json
import os
import os
@ -32,6 +33,7 @@ from ansible import __version__
from ansible import constants as C
from ansible import constants as C
from ansible . errors import AnsibleError
from ansible . errors import AnsibleError
from ansible . utils . unicode import to_bytes , to_unicode
from ansible . utils . unicode import to_bytes , to_unicode
from ansible . plugins . strategy import action_write_locks
try :
try :
from __main__ import display
from __main__ import display
@ -48,7 +50,7 @@ REPLACER_SELINUX = b"<<SELINUX_SPECIAL_FILESYSTEMS>>"
# We could end up writing out parameters with unicode characters so we need to
# We could end up writing out parameters with unicode characters so we need to
# specify an encoding for the python source file
# specify an encoding for the python source file
ENCODING_STRING = b ' # -*- coding: utf-8 -*- '
ENCODING_STRING = u ' # -*- coding: utf-8 -*- '
# we've moved the module_common relative to the snippets, so fix the path
# we've moved the module_common relative to the snippets, so fix the path
_SNIPPET_PATH = os . path . join ( os . path . dirname ( __file__ ) , ' .. ' , ' module_utils ' )
_SNIPPET_PATH = os . path . join ( os . path . dirname ( __file__ ) , ' .. ' , ' module_utils ' )
@ -56,7 +58,7 @@ _SNIPPET_PATH = os.path.join(os.path.dirname(__file__), '..', 'module_utils')
# ******************************************************************************
# ******************************************************************************
ZIPLOADER_TEMPLATE = u ''' %(shebang)s
ZIPLOADER_TEMPLATE = u ''' %(shebang)s
# -*- coding: utf-8 -*-'
% ( coding ) s
# This code is part of Ansible, but is an independent component.
# This code is part of Ansible, but is an independent component.
# The code in this particular templatable string, and this templatable string
# The code in this particular templatable string, and this templatable string
# only, is BSD licensed. Modules which end up using this snippet, which is
# only, is BSD licensed. Modules which end up using this snippet, which is
@ -87,17 +89,49 @@ ZIPLOADER_TEMPLATE = u'''%(shebang)s
import os
import os
import sys
import sys
import base64
import base64
import shutil
import zipfile
import tempfile
import tempfile
import subprocess
import subprocess
if sys . version_info < ( 3 , ) :
if sys . version_info < ( 3 , ) :
bytes = str
bytes = str
PY3 = False
else :
else :
unicode = str
unicode = str
PY3 = True
try :
# Python-2.6+
from io import BytesIO as IOStream
except ImportError :
# Python < 2.6
from StringIO import StringIO as IOStream
ZIPDATA = """ %(zipdata)s """
ZIPDATA = """ %(zipdata)s """
def debug ( command , zipped_mod ) :
def invoke_module ( module , modlib_path , json_params ) :
pythonpath = os . environ . get ( ' PYTHONPATH ' )
if pythonpath :
os . environ [ ' PYTHONPATH ' ] = ' : ' . join ( ( modlib_path , pythonpath ) )
else :
os . environ [ ' PYTHONPATH ' ] = modlib_path
p = subprocess . Popen ( [ ' %(interpreter)s ' , module ] , env = os . environ , shell = False , stdout = subprocess . PIPE , stderr = subprocess . PIPE , stdin = subprocess . PIPE )
( stdout , stderr ) = p . communicate ( json_params )
if not isinstance ( stderr , ( bytes , unicode ) ) :
stderr = stderr . read ( )
if not isinstance ( stdout , ( bytes , unicode ) ) :
stdout = stdout . read ( )
if PY3 :
sys . stderr . buffer . write ( stderr )
sys . stdout . buffer . write ( stdout )
else :
sys . stderr . write ( stderr )
sys . stdout . write ( stdout )
return p . returncode
def debug ( command , zipped_mod , json_params ) :
# The code here normally doesn't run. It's only used for debugging on the
# The code here normally doesn't run. It's only used for debugging on the
# remote machine. Run with ANSIBLE_KEEP_REMOTE_FILES=1 envvar and -vvv
# remote machine. Run with ANSIBLE_KEEP_REMOTE_FILES=1 envvar and -vvv
# to save the module file remotely. Login to the remote machine and use
# to save the module file remotely. Login to the remote machine and use
@ -105,7 +139,7 @@ def debug(command, zipped_mod):
# files. Edit the source files to instrument the code or experiment with
# files. Edit the source files to instrument the code or experiment with
# different values. Then use /path/to/module execute to run the extracted
# different values. Then use /path/to/module execute to run the extracted
# files you've edited instead of the actual zipped module.
# files you've edited instead of the actual zipped module.
#
# Okay to use __file__ here because we're running from a kept file
# Okay to use __file__ here because we're running from a kept file
basedir = os . path . dirname ( __file__ )
basedir = os . path . dirname ( __file__ )
if command == ' explode ' :
if command == ' explode ' :
@ -113,11 +147,11 @@ def debug(command, zipped_mod):
# print the path to the code. This is an easy way for people to look
# print the path to the code. This is an easy way for people to look
# at the code on the remote machine for debugging it in that
# at the code on the remote machine for debugging it in that
# environment
# environment
import zipfile
z = zipfile . ZipFile ( zipped_mod )
z = zipfile . ZipFile ( zipped_mod )
for filename in z . namelist ( ) :
for filename in z . namelist ( ) :
if filename . startswith ( ' / ' ) :
if filename . startswith ( ' / ' ) :
raise Exception ( ' Something wrong with this module zip file: should not contain absolute paths ' )
raise Exception ( ' Something wrong with this module zip file: should not contain absolute paths ' )
dest_filename = os . path . join ( basedir , filename )
dest_filename = os . path . join ( basedir , filename )
if dest_filename . endswith ( os . path . sep ) and not os . path . exists ( dest_filename ) :
if dest_filename . endswith ( os . path . sep ) and not os . path . exists ( dest_filename ) :
os . makedirs ( dest_filename )
os . makedirs ( dest_filename )
@ -128,26 +162,17 @@ def debug(command, zipped_mod):
f = open ( dest_filename , ' w ' )
f = open ( dest_filename , ' w ' )
f . write ( z . read ( filename ) )
f . write ( z . read ( filename ) )
f . close ( )
f . close ( )
print ( ' Module expanded into: ' )
print ( ' Module expanded into: ' )
print ( ' %% s ' % % os . path . join ( basedir , ' ansible ' ) )
print ( ' %% s ' % % os . path . join ( basedir , ' ansible ' ) )
exitcode = 0
elif command == ' execute ' :
elif command == ' execute ' :
# Execute the exploded code instead of executing the module from the
# Execute the exploded code instead of executing the module from the
# embedded ZIPDATA. This allows people to easily run their modified
# embedded ZIPDATA. This allows people to easily run their modified
# code on the remote machine to see how changes will affect it.
# code on the remote machine to see how changes will affect it.
pythonpath = os . environ . get ( ' PYTHONPATH ' )
exitcode = invoke_module ( os . path . join ( basedir , ' ansible_module_ %(ansible_module)s .py ' ) , basedir , json_params )
if pythonpath :
os . environ [ ' PYTHONPATH ' ] = ' : ' . join ( ( basedir , pythonpath ) )
else :
os . environ [ ' PYTHONPATH ' ] = basedir
p = subprocess . Popen ( [ ' %(interpreter)s ' , ' -m ' , ' ansible.module_exec. %(ansible_module)s .__main__ ' ] , env = os . environ , shell = False , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
( stdout , stderr ) = p . communicate ( )
if not isinstance ( stderr , ( bytes , unicode ) ) :
stderr = stderr . read ( )
if not isinstance ( stdout , ( bytes , unicode ) ) :
stdout = stdout . read ( )
sys . stderr . write ( stderr )
sys . stdout . write ( stdout )
sys . exit ( p . returncode )
elif command == ' excommunicate ' :
elif command == ' excommunicate ' :
# This attempts to run the module in-process (by importing a main
# This attempts to run the module in-process (by importing a main
# function and then calling it). It is not the way ansible generally
# function and then calling it). It is not the way ansible generally
@ -157,43 +182,78 @@ def debug(command, zipped_mod):
# when using this that are only artifacts of how we're invoking here,
# when using this that are only artifacts of how we're invoking here,
# not actual bugs (as they don't affect the real way that we invoke
# not actual bugs (as they don't affect the real way that we invoke
# ansible modules)
# ansible modules)
sys . stdin = IOStream ( json_params )
sys . path . insert ( 0 , basedir )
sys . path . insert ( 0 , basedir )
from ansible . module_exec . % ( ansible_module ) s . __main__ import main
from ansible _module_ % ( ansible_module ) s import main
main ( )
main ( )
print ( ' WARNING: Module returned to wrapper instead of exiting ' )
sys . exit ( 1 )
else :
print ( ' WARNING: Unknown debug command. Doing nothing. ' )
exitcode = 0
os . environ [ ' ANSIBLE_MODULE_ARGS ' ] = % ( args ) s
return exitcode
os . environ [ ' ANSIBLE_MODULE_CONSTANTS ' ] = % ( constants ) s
if __name__ == ' __main__ ' :
ZIPLOADER_PARAMS = % ( params ) s
if PY3 :
ZIPLOADER_PARAMS = ZIPLOADER_PARAMS . encode ( ' utf-8 ' )
try :
try :
temp_fd , temp_path = tempfile . mkstemp ( prefix = ' ansible_ ' )
temp_path = tempfile . mkdtemp ( prefix = ' ansible_ ' )
os . write ( temp_fd , base64 . b64decode ( ZIPDATA ) )
zipped_mod = os . path . join ( temp_path , ' ansible_modlib.zip ' )
modlib = open ( zipped_mod , ' wb ' )
modlib . write ( base64 . b64decode ( ZIPDATA ) )
modlib . close ( )
if len ( sys . argv ) == 2 :
if len ( sys . argv ) == 2 :
debug ( sys . argv [ 1 ] , temp_path )
exitcode = debug ( sys . argv [ 1 ] , zipped_mod, ZIPLOADER_PARAMS )
else :
else :
pythonpath = os . environ . get ( ' PYTHONPATH ' )
z = zipfile . ZipFile ( zipped_mod )
if pythonpath :
module = os . path . join ( temp_path , ' ansible_module_ %(ansible_module)s .py ' )
os . environ [ ' PYTHONPATH ' ] = ' : ' . join ( ( temp_path , pythonpath ) )
f = open ( module , ' wb ' )
else :
f . write ( z . read ( ' ansible_module_ %(ansible_module)s .py ' ) )
os . environ [ ' PYTHONPATH ' ] = temp_path
f . close ( )
p = subprocess . Popen ( [ ' %(interpreter)s ' , ' -m ' , ' ansible.module_exec. %(ansible_module)s .__main__ ' ] , env = os . environ , shell = False , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
exitcode = invoke_module ( module , zipped_mod , ZIPLOADER_PARAMS )
( stdout , stderr ) = p . communicate ( )
if not isinstance ( stderr , ( bytes , unicode ) ) :
stderr = stderr . read ( )
if not isinstance ( stdout , ( bytes , unicode ) ) :
stdout = stdout . read ( )
sys . stderr . write ( stderr )
sys . stdout . write ( stdout )
sys . exit ( p . returncode )
finally :
finally :
try :
try :
os . close ( temp_fd )
shutil . rmtree ( temp_path )
os . remove ( temp_path )
except OSError :
except NameError :
# tempdir creation probably failed
# mkstemp failed
pass
pass
sys . exit ( exitcode )
'''
'''
class ModuleDepFinder ( ast . NodeVisitor ) :
# Caveats:
# This code currently does not handle:
# * relative imports from py2.6+ from . import urls
# * python packages (directories with __init__.py in them)
IMPORT_PREFIX_SIZE = len ( ' ansible.module_utils. ' )
def __init__ ( self , * args , * * kwargs ) :
super ( ModuleDepFinder , self ) . __init__ ( * args , * * kwargs )
self . module_files = set ( )
def visit_Import ( self , node ) :
# import ansible.module_utils.MODLIB[.other]
for alias in ( a for a in node . names if a . name . startswith ( ' ansible.module_utils. ' ) ) :
py_mod = alias . name [ self . IMPORT_PREFIX_SIZE : ] . split ( ' . ' , 1 ) [ 0 ]
self . module_files . add ( py_mod )
self . generic_visit ( node )
def visit_ImportFrom ( self , node ) :
if node . module . startswith ( ' ansible.module_utils ' ) :
where_from = node . module [ self . IMPORT_PREFIX_SIZE : ]
# from ansible.module_utils.MODLIB[.other] import foo
if where_from :
py_mod = where_from . split ( ' . ' , 1 ) [ 0 ]
self . module_files . add ( py_mod )
else :
# from ansible.module_utils import MODLIB
for alias in node . names :
self . module_files . add ( alias . name )
self . generic_visit ( node )
def _strip_comments ( source ) :
def _strip_comments ( source ) :
# Strip comments and blank lines from the wrapper
# Strip comments and blank lines from the wrapper
buf = [ ]
buf = [ ]
@ -242,6 +302,28 @@ def _get_facility(task_vars):
facility = task_vars [ ' ansible_syslog_facility ' ]
facility = task_vars [ ' ansible_syslog_facility ' ]
return facility
return facility
def recursive_finder ( data , snippet_names , snippet_data , zf ) :
"""
Using ModuleDepFinder , make sure we have all of the module_utils files that
the module its module_utils files needs .
"""
tree = ast . parse ( data )
finder = ModuleDepFinder ( )
finder . visit ( tree )
new_snippets = set ( )
for snippet_name in finder . module_files . difference ( snippet_names ) :
fname = ' %s .py ' % snippet_name
new_snippets . add ( snippet_name )
if snippet_name not in snippet_data :
snippet_data [ snippet_name ] = _slurp ( os . path . join ( _SNIPPET_PATH , fname ) )
zf . writestr ( os . path . join ( " ansible/module_utils " , fname ) , snippet_data [ snippet_name ] )
snippet_names . update ( new_snippets )
for snippet_name in tuple ( new_snippets ) :
recursive_finder ( snippet_data [ snippet_name ] , snippet_names , snippet_data , zf )
del snippet_data [ snippet_name ]
def _find_snippet_imports ( module_name , module_data , module_path , module_args , task_vars , module_compression ) :
def _find_snippet_imports ( module_name , module_data , module_path , module_args , task_vars , module_compression ) :
"""
"""
Given the source of the module , convert it to a Jinja2 template to insert
Given the source of the module , convert it to a Jinja2 template to insert
@ -280,59 +362,87 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta
if module_style in ( ' old ' , ' non_native_want_json ' ) :
if module_style in ( ' old ' , ' non_native_want_json ' ) :
return module_data , module_style , shebang
return module_data , module_style , shebang
module_args_json = to_bytes ( json . dumps ( module_args ) )
output = BytesIO ( )
output = BytesIO ( )
lines = module_data . split ( b ' \n ' )
snippet_names = set ( )
snippet_names = set ( )
if module_substyle == ' python ' :
if module_substyle == ' python ' :
# ziploader for new-style python classes
# ziploader for new-style python classes
python_repred_args = to_bytes ( repr ( module_args_json ) )
constants = dict (
constants = dict (
SELINUX_SPECIAL_FS = C . DEFAULT_SELINUX_SPECIAL_FS ,
SELINUX_SPECIAL_FS = C . DEFAULT_SELINUX_SPECIAL_FS ,
SYSLOG_FACILITY = _get_facility ( task_vars ) ,
SYSLOG_FACILITY = _get_facility ( task_vars ) ,
)
)
python_repred_constants = to_bytes ( repr ( json . dumps ( constants ) ) , errors = ' strict ' )
params = dict ( ANSIBLE_MODULE_ARGS = module_args ,
ANSIBLE_MODULE_CONSTANTS = constants ,
)
#python_repred_args = to_bytes(repr(module_args_json))
#python_repred_constants = to_bytes(repr(json.dumps(constants)), errors='strict')
python_repred_params = to_bytes ( repr ( json . dumps ( params ) ) , errors = ' strict ' )
try :
try :
compression_method = getattr ( zipfile , module_compression )
compression_method = getattr ( zipfile , module_compression )
except AttributeError :
except AttributeError :
display . warning ( u ' Bad module compression string specified: %s . Using ZIP_STORED (no compression) ' % module_compression )
display . warning ( u ' Bad module compression string specified: %s . Using ZIP_STORED (no compression) ' % module_compression )
compression_method = zipfile . ZIP_STORED
compression_method = zipfile . ZIP_STORED
lookup_path = os . path . join ( C . DEFAULT_LOCAL_TMP , ' ziploader_cache ' )
if not os . path . exists ( lookup_path ) :
os . mkdir ( lookup_path )
cached_module_filename = os . path . join ( lookup_path , " %s - %s " % ( module_name , module_compression ) )
zipdata = None
# Optimization -- don't lock if the module has already been cached
if os . path . exists ( cached_module_filename ) :
zipdata = open ( cached_module_filename , ' rb ' ) . read ( )
# Fool the check later... I think we should just remove the check
snippet_names . add ( ' basic ' )
else :
with action_write_locks [ module_name ] :
# Check that no other process has created this while we were
# waiting for the lock
if not os . path . exists ( cached_module_filename ) :
# Create the module zip data
zipoutput = BytesIO ( )
zipoutput = BytesIO ( )
zf = zipfile . ZipFile ( zipoutput , mode = ' w ' , compression = compression_method )
zf = zipfile . ZipFile ( zipoutput , mode = ' w ' , compression = compression_method )
zf . writestr ( ' ansible/__init__.py ' , b ' ' . join ( ( b " __version__ = ' " , to_bytes ( __version__ ) , b " ' \n " ) ) )
zf . writestr ( ' ansible/__init__.py ' , b ' ' . join ( ( b " __version__ = ' " , to_bytes ( __version__ ) , b " ' \n " ) ) )
zf . writestr ( ' ansible/module_utils/__init__.py ' , b ' ' )
zf . writestr ( ' ansible/module_utils/__init__.py ' , b ' ' )
zf . writestr ( ' ansible/module_exec/__init__.py ' , b ' ' )
zf . writestr ( ' ansible/module_exec/ %s /__init__.py ' % module_name , b " " )
zf . writestr ( ' ansible_module_ %s .py ' % module_name , module_data )
final_data = [ ]
for line in lines :
snippet_data = dict ( )
if line . startswith ( b ' from ansible.module_utils. ' ) :
recursive_finder ( module_data , snippet_names , snippet_data , zf )
tokens = line . split ( b " . " )
snippet_name = tokens [ 2 ] . split ( ) [ 0 ]
snippet_names . add ( snippet_name )
fname = to_unicode ( snippet_name + b " .py " )
zf . writestr ( os . path . join ( " ansible/module_utils " , fname ) , _slurp ( os . path . join ( _SNIPPET_PATH , fname ) ) )
final_data . append ( line )
else :
final_data . append ( line )
zf . writestr ( ' ansible/module_exec/ %s /__main__.py ' % module_name , b " \n " . join ( final_data ) )
zf . close ( )
zf . close ( )
zipdata = base64 . b64encode ( zipoutput . getvalue ( ) )
# Write the assembled module to a temp file (write to temp
# so that no one looking for the file reads a partially
# written file)
with open ( cached_module_filename + ' -part ' , ' w ' ) as f :
f . write ( zipdata )
# Rename the file into its final position in the cache so
# future users of this module can read it off the
# filesystem instead of constructing from scratch.
os . rename ( cached_module_filename + ' -part ' , cached_module_filename )
if zipdata is None :
# Another process wrote the file while we were waiting for
# the write lock. Go ahead and read the data from disk
# instead of re-creating it.
zipdata = open ( cached_module_filename , ' rb ' ) . read ( )
# Fool the check later... I think we should just remove the check
snippet_names . add ( ' basic ' )
shebang , interpreter = _get_shebang ( u ' /usr/bin/python ' , task_vars )
shebang , interpreter = _get_shebang ( u ' /usr/bin/python ' , task_vars )
if shebang is None :
if shebang is None :
shebang = u ' #!/usr/bin/python '
shebang = u ' #!/usr/bin/python '
output . write ( to_bytes ( STRIPPED_ZIPLOADER_TEMPLATE % dict (
output . write ( to_bytes ( STRIPPED_ZIPLOADER_TEMPLATE % dict (
zipdata = base64 . b64encode ( zipoutput . getvalue ( ) ) ,
zipdata = zipdata ,
ansible_module = module_name ,
ansible_module = module_name ,
args = python_repred_args ,
#args=python_repred_args,
constants = python_repred_constants ,
#constants=python_repred_constants,
params = python_repred_params ,
shebang = shebang ,
shebang = shebang ,
interpreter = interpreter ,
interpreter = interpreter ,
coding = ENCODING_STRING ,
) ) )
) ) )
module_data = output . getvalue ( )
module_data = output . getvalue ( )
@ -340,11 +450,12 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta
# modules that use ziploader may implement their own helpers and not
# modules that use ziploader may implement their own helpers and not
# need basic.py. All the constants that we substituted into basic.py
# need basic.py. All the constants that we substituted into basic.py
# for module_replacer are now available in other, better ways.
# for module_replacer are now available in other, better ways.
if b ' basic ' not in snippet_names :
if ' basic ' not in snippet_names :
raise AnsibleError ( " missing required import in %s : Did not import ansible.module_utils.basic for boilerplate helper code " % module_path )
raise AnsibleError ( " missing required import in %s : Did not import ansible.module_utils.basic for boilerplate helper code " % module_path )
elif module_substyle == ' powershell ' :
elif module_substyle == ' powershell ' :
# Module replacer for jsonargs and windows
# Module replacer for jsonargs and windows
lines = module_data . split ( b ' \n ' )
for line in lines :
for line in lines :
if REPLACER_WINDOWS in line :
if REPLACER_WINDOWS in line :
ps_data = _slurp ( os . path . join ( _SNIPPET_PATH , " powershell.ps1 " ) )
ps_data = _slurp ( os . path . join ( _SNIPPET_PATH , " powershell.ps1 " ) )
@ -353,6 +464,8 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta
continue
continue
output . write ( line + b ' \n ' )
output . write ( line + b ' \n ' )
module_data = output . getvalue ( )
module_data = output . getvalue ( )
module_args_json = to_bytes ( json . dumps ( module_args ) )
module_data = module_data . replace ( REPLACER_JSONARGS , module_args_json )
module_data = module_data . replace ( REPLACER_JSONARGS , module_args_json )
# Sanity check from 1.x days. This is currently useless as we only
# Sanity check from 1.x days. This is currently useless as we only
@ -363,11 +476,14 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta
raise AnsibleError ( " missing required import in %s : # POWERSHELL_COMMON " % module_path )
raise AnsibleError ( " missing required import in %s : # POWERSHELL_COMMON " % module_path )
elif module_substyle == ' jsonargs ' :
elif module_substyle == ' jsonargs ' :
module_args_json = to_bytes ( json . dumps ( module_args ) )
# these strings could be included in a third-party module but
# these strings could be included in a third-party module but
# officially they were included in the 'basic' snippet for new-style
# officially they were included in the 'basic' snippet for new-style
# python modules (which has been replaced with something else in
# python modules (which has been replaced with something else in
# ziploader) If we remove them from jsonargs-style module replacer
# ziploader) If we remove them from jsonargs-style module replacer
# then we can remove them everywhere.
# then we can remove them everywhere.
python_repred_args = to_bytes ( repr ( module_args_json ) )
module_data = module_data . replace ( REPLACER_VERSION , to_bytes ( repr ( __version__ ) ) )
module_data = module_data . replace ( REPLACER_VERSION , to_bytes ( repr ( __version__ ) ) )
module_data = module_data . replace ( REPLACER_COMPLEX , python_repred_args )
module_data = module_data . replace ( REPLACER_COMPLEX , python_repred_args )
module_data = module_data . replace ( REPLACER_SELINUX , to_bytes ( ' , ' . join ( C . DEFAULT_SELINUX_SPECIAL_FS ) ) )
module_data = module_data . replace ( REPLACER_SELINUX , to_bytes ( ' , ' . join ( C . DEFAULT_SELINUX_SPECIAL_FS ) ) )
@ -409,17 +525,6 @@ def modify_module(module_name, module_path, module_args, task_vars=dict(), modul
which results in the inclusion of the common code from powershell . ps1
which results in the inclusion of the common code from powershell . ps1
"""
"""
### TODO: Optimization ideas if this code is actually a source of slowness:
# * Fix comment stripping: Currently doesn't preserve shebangs and encoding info (but we unconditionally add encoding info)
# * Use pyminifier if installed
# * comment stripping/pyminifier needs to have config setting to turn it
# off for debugging purposes (goes along with keep remote but should be
# separate otherwise users wouldn't be able to get info on what the
# minifier output)
# * Only split into lines and recombine into strings once
# * Cache the modified module? If only the args are different and we do
# that as the last step we could cache all the work up to that point.
with open ( module_path , ' rb ' ) as f :
with open ( module_path , ' rb ' ) as f :
# read in the module source
# read in the module source
@ -440,7 +545,7 @@ def modify_module(module_name, module_path, module_args, task_vars=dict(), modul
lines [ 0 ] = shebang = new_shebang
lines [ 0 ] = shebang = new_shebang
if os . path . basename ( interpreter ) . startswith ( b ' python ' ) :
if os . path . basename ( interpreter ) . startswith ( b ' python ' ) :
lines . insert ( 1 , ENCODING_STRING)
lines . insert ( 1 , to_bytes( ENCODING_STRING) )
else :
else :
# No shebang, assume a binary module?
# No shebang, assume a binary module?
pass
pass