github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-tools-src/grumpy_tools/grumprun.py (about)

     1  #!/usr/bin/env python
     2  
     3  # Copyright 2016 Google Inc. All Rights Reserved.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  """grumprun compiles and runs a snippet of Python using Grumpy.
    18  
    19  Usage: $ grumprun -m <module>             # Run the named module.
    20         $ echo 'print "hola!"' | grumprun  # Execute Python code from stdin.
    21  """
    22  
    23  import argparse
    24  import os
    25  import random
    26  import shutil
    27  import string
    28  import subprocess
    29  import sys
    30  import tempfile
    31  import logging
    32  from StringIO import StringIO
    33  
    34  from .compiler import imputil
    35  from .pep_support.pep3147pycache import make_transpiled_module_folders
    36  from . import grumpc
    37  
    38  logger = logging.getLogger(__name__)
    39  
    40  TRACEBACK_DEPENDENCIES = [
    41    '__go__/grumpy',
    42    '__go__/io/ioutil',
    43    '__go__/os',
    44    '__go__/path/filepath',
    45    '__go__/reflect',
    46    '__go__/runtime',
    47    '__go__/sync',
    48    '__go__/syscall',
    49    '__go__/time',
    50    '__go__/unicode',
    51    '_syscall',
    52    'linecache',
    53    'os',
    54    'os/path',
    55    'stat',
    56    'sys',
    57    'traceback',
    58    'types',
    59  ]
    60  
    61  module_tmpl = string.Template("""\
    62  package main
    63  import (
    64  \t"os"
    65  \t"grumpy"
    66  \tmod "$package"
    67  $imports
    68  )
    69  func main() {
    70  \tgrumpy.ImportModule(grumpy.NewRootFrame(), "traceback")
    71  \tos.Exit(grumpy.RunMain(mod.Code))
    72  }
    73  """)
    74  
    75  
    76  def main(stream=None, modname=None, pep3147=False, clean_tempfolder=True, go_action='run'):
    77    assert pep3147, 'It is no longer optional'
    78    assert (stream is None and modname) or (stream.name and not modname)
    79  
    80    gopath = os.environ['GOPATH']
    81  
    82    # CPython does not cache the __main__. Should I?
    83    try:
    84      script = None
    85      if modname and not stream:  # TODO: move all this `if modname` to the CLI handling?
    86        # Find the script associated with the given module.
    87        for d in gopath.split(os.pathsep):
    88          script = imputil.find_script(
    89            os.path.join(d, 'src', '__python__'), modname)
    90          if script:
    91            break
    92        if not script:
    93          for d in sys.path:
    94            script = imputil.find_script(d, modname, main=True)
    95            if script:
    96              break
    97        if not script:
    98          script = imputil.find_script(os.getcwd(), modname, main=True)
    99        if not script:
   100          raise RuntimeError("can't find module '%s'", modname)
   101  
   102        stream = StringIO(open(script).read())
   103        if script.endswith('__main__.py'):
   104          stream.name = script
   105        else:
   106          stream.name = '__main__.py'
   107  
   108      script = os.path.abspath(stream.name)
   109      modname = '__main__'
   110  
   111      pep3147_folders = make_transpiled_module_folders(script, modname)
   112      workdir = pep3147_folders['transpiled_base_folder']
   113  
   114      # Generate a dummy python script on the 'cache_folder'
   115      script_name = os.path.join(pep3147_folders['cache_folder'], os.path.basename(script))
   116      with open(script_name, 'wb') as script_file:
   117        stream.seek(0)
   118        script_file.write(stream.read())
   119  
   120      py_dir = os.path.join(workdir, 'src', '__python__')
   121  
   122      mod_dir = pep3147_folders['transpiled_module_folder']
   123  
   124      ## TODO: Manage the STDIN and `-c` scripts situation
   125      # script = os.path.join(py_dir, 'module.py')
   126      # with open(script, 'w') as f:
   127      #   f.write(stream.read())
   128      ##
   129  
   130      gopath_folder = pep3147_folders['gopath_folder']
   131      os.environ['GOPATH'] = os.pathsep.join([gopath_folder, gopath]) #, workdir])
   132  
   133      # Compile the dummy script to Go using grumpc.
   134      with open(os.path.join(mod_dir, 'module.go'), 'w+') as dummy_file:
   135        result = grumpc.main(stream, modname=modname, pep3147=True, recursive=True, return_deps=True)
   136        transpiled, deps = result['gocode'], result['deps']
   137        dummy_file.write(transpiled)
   138  
   139      # Make sure traceback is available in all Python binaries.
   140      names = sorted(set(['traceback'] + TRACEBACK_DEPENDENCIES).union(deps))
   141  
   142      go_main = os.path.join(workdir, 'main.go')
   143      package = grumpc._package_name(modname)
   144      imports = ''.join('\t_ "' + grumpc._package_name(name) + '"\n' for name in names)
   145      with open(go_main, 'w') as f:
   146        f.write(module_tmpl.substitute(package=package, imports=imports))
   147      logger.info('`go run` GOPATH=%s', os.environ['GOPATH'])
   148      if go_action == 'run':
   149        subprocess_cmd = 'go run ' + go_main
   150      elif go_action == 'build':
   151        subprocess_cmd = 'go build ' + go_main
   152      elif go_action == 'debug':
   153        subprocess_cmd = 'dlv debug --listen=:2345 --log ' + go_main
   154      else:
   155        raise NotImplementedError('Go action "%s" not implemented' % go_action)
   156      logger.debug('Starting subprocess: `%s`', subprocess_cmd)
   157      return subprocess.Popen(subprocess_cmd, shell=True).wait()
   158    finally:
   159      if 'pep3147_folders' in locals():
   160        if clean_tempfolder:
   161          shutil.rmtree(pep3147_folders['cache_folder'], ignore_errors=True)
   162        else:
   163          logger.warning('not cleaning the temporary pycache folder: %s', pep3147_folders['cache_folder'])