2012-06-04 7 views
10

Utilizo una secuencia de comandos de Python, ejecutándose en una compilación Cygwin de Python, para crear comandos emitidos a utilidades nativas de Windows (no compatibles con Cygwin). Esto requiere la conversión de parámetros de ruta de POSIX a WIN antes de emitir el comando.Convierta la ruta POSIX-> WIN, en Cygwin Python, sin llamar a cygpath

Llamar a la utilidad cygpath es la mejor manera de hacerlo, ya que utiliza Cygwin para hacer lo que está allí para hacer, pero también es un poco espeluznante (y lento).

Ya estoy ejecutando una compilación Cygwin de Python, por lo que el código para hacer la conversión está presente. Parece que debería haber una extensión específica de Cygwin/Python que me dé un enlace a esta capacidad, directamente en Python, sin tener que iniciar un proceso completamente nuevo.

Respuesta

1

Al navegar por el cygpath source, parece que cygpath tiene una implementación no trivial y no hace disponible ninguna versión de biblioteca.

cygpath hace de soporte de tomar su entrada desde un archivo usando la opción -f (o de la entrada estándar, utilizando -f -) y puede tomar varias rutas, escupiendo un camino convertido cada vez, por lo que probablemente podría crear una sola instancia cygpath abierta (usando Python's subprocess.Popen) en lugar de reiniciar cygpath cada vez.

3

Esto es posible llamando a la API de Cygwin utilizando ctypes. El siguiente código funciona para mí: estoy usando la versión 2.5.2 de cygwin de 64 bits en Windows 2012, y esto funciona en las versiones de Cygwin de Python 2.7.10 y Python 3.4.3.

Básicamente llamamos cygwin_create_path desde cygwin1.dll para realizar la conversión de ruta. Esa función asigna un búfer de memoria (usando malloc) que contiene la ruta convertida. Entonces, necesitamos usar free desde cygwin1.dll para liberar el búfer que asignó.

Tenga en cuenta que xunicode a continuación es una alternativa pobre a six (una biblioteca de compatibilidad Python 2/3); si necesita admitir Python 2 y 3, seis es la respuesta mucho mejor, pero quería que mi ejemplo no tuviese dependencias en ningún módulo no incluido, por eso lo hice de esta manera.

from ctypes import cdll, c_void_p, c_int32, cast, c_char_p, c_wchar_p 
from sys import version_info 

xunicode = str if version_info[0] > 2 else eval("unicode") 

# If running under Cygwin Python, just use DLL name 
# If running under non-Cygwin Windows Python, use full path to cygwin1.dll 
# Note Python and cygwin1.dll must match bitness (i.e. 32-bit Python must 
# use 32-bit cygwin1.dll, 64-bit Python must use 64-bit cygwin1.dll.) 
cygwin = cdll.LoadLibrary("cygwin1.dll") 
cygwin_create_path = cygwin.cygwin_create_path 
cygwin_create_path.restype = c_void_p 
cygwin_create_path.argtypes = [c_int32, c_void_p] 

# Initialise the cygwin DLL. This step should only be done if using 
# non-Cygwin Python. If you are using Cygwin Python don't do this because 
# it has already been done for you. 
cygwin_dll_init = cygwin.cygwin_dll_init 
cygwin_dll_init.restype = None 
cygwin_dll_init.argtypes = [] 
cygwin_dll_init() 

free = cygwin.free 
free.restype = None 
free.argtypes = [c_void_p] 

CCP_POSIX_TO_WIN_A = 0 
CCP_POSIX_TO_WIN_W = 1 
CCP_WIN_A_TO_POSIX = 2 
CCP_WIN_W_TO_POSIX = 3 

def win2posix(path): 
    """Convert a Windows path to a Cygwin path""" 
    result = cygwin_create_path(CCP_WIN_W_TO_POSIX,xunicode(path)) 
    if result is None: 
     raise Exception("cygwin_create_path failed") 
    value = cast(result,c_char_p).value 
    free(result) 
    return value 

def posix2win(path): 
    """Convert a Cygwin path to a Windows path""" 
    result = cygwin_create_path(CCP_POSIX_TO_WIN_W,str(path)) 
    if result is None: 
     raise Exception("cygwin_create_path failed") 
    value = cast(result,c_wchar_p).value 
    free(result) 
    return value 

# Example, convert LOCALAPPDATA to cygwin path and back 
from os import environ 
localAppData = environ["LOCALAPPDATA"] 
print("Original Win32 path: %s" % localAppData) 
localAppData = win2posix(localAppData) 
print("As a POSIX path: %s" % localAppData) 
localAppData = posix2win(localAppData) 
print("Back to a Windows path: %s" % localAppData) 
+0

Gracias por compartir la ctypes de código, pero no puedo hacer que se ejecute con WinPython-3.5.2 y 2.7. 10: accidentes AP2 en ctypes '' self._handle = _dlopen (self._name, modo) '', y AP3 en '' resultado = cygwin_create_path (CCP_WIN_W_TO_POSIX, xunicode (ruta de acceso)) OSError: excepción: violación de acceso escribiendo 0x0000000000000000''. ¿Algunas ideas? – ankostis

+0

@ankostis, creo que el problema es probable en la línea 'cygwin = cdll.LoadLibrary (" cygwin1.dll ")'. Con Cygwin Python, 'cygwin1.dll' ya está cargado en el espacio de direcciones del proceso de Python para que' LoadLibrary' no tenga problemas para encontrarlo. Sin embargo, dado que WinPython es una aplicación pura de Windows (no Cygwin), por lo que 'cygwin1.dll' normalmente no se cargará, por lo que debe proporcionar la ruta completa a su' cygwin1.dll'. Además, si su WinPython es de 64 bits, debe cargar un 'cygwin1.dll' de 64 bits; por el contrario, si su WinPython es de 32 bits, debe cargar un 'cygwin1.dll' de 32 bits. –

+0

Intenté en otra máquina como dijiste, y ahora estoy obteniendo esto: 'Ruta Win32 original: C: \ Users \ ankostis \ AppData \ Local 0 [principal] python 772 D: \ Apps \ WinPython-64bit- 3.5.2.1 \ python-3.5.2.amd64 \ python.exe: *** error fatal - Error interno: TP_NUM_C_BUFS demasiado pequeño: 50 1090 [main] python 772 cygwin_exception :: open_stackdumpfile: volcado de la pila de rastreo a python.exe.stackdump @tokoti (cygwin): ~/Work/gitdb. git $ less python.exe.stackdump' – ankostis

0

yo prefiero escribir este ayudante de Python que utiliza la DLL cygwin:

import errno 
import ctypes 
import enum 
import sys 

class ccp_what(enum.Enum): 
    posix_to_win_a = 0 # from is char *posix, to is char *win32 
    posix_to_win_w = 1 # from is char *posix, to is wchar_t *win32 
    win_a_to_posix = 2 # from is char *win32, to is char *posix 
    win_w_to_posix = 3 # from is wchar_t *win32, to is char *posix 

    convtype_mask = 3 

    absolute = 0   # Request absolute path (default). 
    relative = 0x100  # Request to keep path relative. 
    proc_cygdrive = 0x200 # Request to return /proc/cygdrive path (only with CCP_*_TO_POSIX) 

class CygpathError(Exception): 
    def __init__(self, errno, msg=""): 
     self.errno = errno 
     super(Exception, self).__init__(os.strerror(errno)) 

class Cygpath(object): 
    bufsize = 512 

    def __init__(self): 
     if 'cygwin' not in sys.platform: 
      raise SystemError('Not running on cygwin') 

     self._dll = ctypes.cdll.LoadLibrary("cygwin1.dll") 

    def _cygwin_conv_path(self, what, path, size = None): 
     if size is None: 
      size = self.bufsize 
     out = ctypes.create_string_buffer(size) 
     ret = self._dll.cygwin_conv_path(what, path, out, size) 
     if ret < 0: 
      raise CygpathError(ctypes.get_errno()) 
     return out.value 

    def posix2win(self, path, relative=False): 
     out = ctypes.create_string_buffer(self.bufsize) 
     t = ccp_what.relative.value if relative else ccp_what.absolute.value 
     what = ccp_what.posix_to_win_a.value | t 
     return self._cygwin_conv_path(what, path) 

    def win2posix(self, path, relative=False): 
     out = ctypes.create_string_buffer(self.bufsize) 
     t = ccp_what.relative.value if relative else ccp_what.absolute.value 
     what = ccp_what.win_a_to_posix.value | t 
     return self._cygwin_conv_path(what, path)