Changeset 57910


Ignore:
Timestamp:
Oct 1, 2013, 7:55:55 PM (11 years ago)
Author:
wenzeslaus
Message:

handling of scripts on MS Win using Python which (uses PATH) and sys.executable (for grass.script and wxGUI), fixes MAXREPEAT error when running Python script

Location:
grass/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • grass/trunk/gui/wxpython/core/gcmd.py

    r57220 r57910  
    534534
    535535        # TODO: replace ugly hack below
     536        # this cannot be replaced it can be only improved
     537        # also unifying this with 3 other places in code would be nice
     538        # changing from one chdir to get_real_command function
    536539        args = self.cmd
    537540        if sys.platform == 'win32':
    538             ext = os.path.splitext(self.cmd[0])[1] == globalvar.SCT_EXT
    539             if ext or self.cmd[0] in globalvar.grassScripts[globalvar.SCT_EXT]:
    540                 os.chdir(os.path.join(os.getenv('GISBASE'), 'scripts'))
    541                 if not ext:
    542                     args = [sys.executable, self.cmd[0] + globalvar.SCT_EXT] + self.cmd[1:]
    543                 else:
    544                     args = [sys.executable, self.cmd[0]] + self.cmd[1:]
    545        
     541            if os.path.splitext(args[0])[1] == globalvar.SCT_EXT:
     542                args[0] = args[0][:-3]
     543            # using Python executable to run the module if it is a script
     544            # expecting at least module name at first position
     545            # cannot use make_command for this now because it is used in GUI
     546            # The same code is in grass.script.core already twice.
     547            args[0] = grass.get_real_command(args[0])
     548            if args[0].endswith('.py'):
     549                args.insert(0, sys.executable)
     550 
    546551        try:
    547552            self.module = Popen(args,
  • grass/trunk/lib/python/script/core.py

    r57523 r57910  
    143143
    144144    return set(cmd), scripts
     145
     146
     147# replacement for which function from shutil (not available in all versions)
     148# from http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068
     149# added because of Python scripts running Python scripts on MS Windows
     150# see also ticket #2008 which is unrelated but same function was proposed
     151def shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None):
     152    """Given a command, mode, and a PATH string, return the path which
     153    conforms to the given mode on the PATH, or None if there is no such
     154    file.
     155
     156    `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
     157    of os.environ.get("PATH"), or can be overridden with a custom search
     158    path.
     159
     160    """
     161    # Check that a given file can be accessed with the correct mode.
     162    # Additionally check that `file` is not a directory, as on Windows
     163    # directories pass the os.access check.
     164    def _access_check(fn, mode):
     165        return (os.path.exists(fn) and os.access(fn, mode)
     166                and not os.path.isdir(fn))
     167
     168    # If we're given a path with a directory part, look it up directly rather
     169    # than referring to PATH directories. This includes checking relative to the
     170    # current directory, e.g. ./script
     171    if os.path.dirname(cmd):
     172        if _access_check(cmd, mode):
     173            return cmd
     174        return None
     175
     176    if path is None:
     177        path = os.environ.get("PATH", os.defpath)
     178    if not path:
     179        return None
     180    path = path.split(os.pathsep)
     181
     182    if sys.platform == "win32":
     183        # The current directory takes precedence on Windows.
     184        if not os.curdir in path:
     185            path.insert(0, os.curdir)
     186
     187        # PATHEXT is necessary to check on Windows.
     188        pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
     189        # See if the given file matches any of the expected path extensions.
     190        # This will allow us to short circuit when given "python.exe".
     191        # If it does match, only test that one, otherwise we have to try
     192        # others.
     193        if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
     194            files = [cmd]
     195        else:
     196            files = [cmd + ext for ext in pathext]
     197    else:
     198        # On other platforms you don't have things like PATHEXT to tell you
     199        # what file suffixes are executable, so just pass on cmd as-is.
     200        files = [cmd]
     201
     202    seen = set()
     203    for dir in path:
     204        normdir = os.path.normcase(dir)
     205        if not normdir in seen:
     206            seen.add(normdir)
     207            for thefile in files:
     208                name = os.path.join(dir, thefile)
     209                if _access_check(name, mode):
     210                    return name
     211    return None
     212
     213
     214# Added because of scripts calling scripts on MS Windows.
     215# Module name (here cmd) differs from the file name (does not have extension).
     216# Additionally, we don't run scripts using system executable mechanism,
     217# so we need the full path name.
     218# However, scripts are on the PATH and '.PY' in in PATHEXT, so we can use
     219# shutil.which to get the full file path. Addons are on PATH too.
     220# An alternative to which function call would be to check the script path and
     221# addons path. This is proposed improvement for the future.
     222# Another alternative is to check some global list of scripts but this list
     223# needs to be created first. The question is what is less expensive.
     224# Note that getting the full path is only part of the solution,
     225# the other part is to use the right Python as an executable and pass the full
     226# script path as a parameter.
     227# Nevertheless, it is unclear on which places which extensions are added.
     228# This function also could skip the check for platform but depends
     229# how will be used, this is most general but not most effective.
     230def get_real_command(cmd):
     231    """!Returns the real file commad for a module (cmd)
     232
     233    For Python scripts on MS Windows it returns full path to the script
     234    and adds a '.py' extension.
     235    For other cases it just returns a module (name).
     236    So, you can just use this function for all without further check.
     237
     238    >>> get_real_command('g.region')
     239    'g.region'
     240    """
     241    if sys.platform == 'win32':
     242        # we in fact expect pure module name (without extension)
     243        # so, lets remove extension
     244        if os.path.splitext(cmd)[1] == '.py':
     245            cmd = cmd[:-3]
     246        full_path = shutil_which(cmd + '.py')
     247        if full_path:
     248            return full_path
     249
     250    return cmd
    145251
    146252
     
    222328
    223329    args = make_command(prog, flags, overwrite, quiet, verbose, **options)
     330    # using Python executable to run the module if it is a script
     331    # expecting at least module name at first position
     332    # cannot use make_command for this now because it is used in GUI
     333    if sys.platform == "win32":
     334        args[0] = get_real_command(args[0])
     335        if args[0].endswith('.py'):
     336            args.insert(0, sys.executable)
    224337
    225338    if debug_level() > 0:
     
    371484    """
    372485    args = make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
     486    # using Python executable to run the module if it is a script
     487    # expecting at least module name at first position
     488    # cannot use make_command for this now because it is used in GUI
     489    if sys.platform == "win32":
     490        args[0] = get_real_command(args[0])
     491        if args[0].endswith('.py'):
     492            args.insert(0, sys.executable)
     493
    373494    if env == None:
    374495        env = os.environ
  • grass/trunk/lib/python/script/task.py

    r57501 r57910  
    467467       
    468468        # TODO: replace ugly hack bellow
     469        # expecting that cmd is without .py
    469470        if not cmdout and sys.platform == 'win32':
    470             if os.path.splitext(cmd)[1] != '.py':
     471            # we in fact expect pure module name (without extension)
     472            # so, lets remove extension
     473            if os.path.splitext(cmd)[1] == '.py':
     474                cmd = cmd[:-3]
     475            if cmd == 'd.rast3d':
    471476                cmd += '.py'
    472            
    473             if cmd == 'd.rast3d.py':
    474477                os.chdir(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'scripts'))
    475478            else:
    476                 os.chdir(os.path.join(os.getenv('GISBASE'), 'scripts'))
     479                cmd = get_real_command(cmd)
    477480            p = Popen([sys.executable, cmd, '--interface-description'],
    478481                      stdout = PIPE, stderr = PIPE)
Note: See TracChangeset for help on using the changeset viewer.