2012-06-04 9 views
10

Io uso uno script Python, in esecuzione in una build Cygwin di Python, per creare comandi emessi da utilità nativi di Windows (non Cygwin-aware). Ciò richiede la conversione dei parametri del percorso da POSIX a WIN prima di emettere il comando.Converti POSIX-> percorso WIN, in Cygwin Python, senza chiamare cygpath

Chiamando l'utilità cygpath è il modo più bello per fare questo, dal momento che utilizza Cygwin di fare ciò che c'è da fare, ma è anche un po 'orribile (e lento).

Sono già in esecuzione una versione Cygwin di Python, quindi è presente il codice per eseguire la conversione. Sembra che ci dovrebbe essere un'estensione Cygwin/Python specifica che mi dà un aggancio a questa capacità, direttamente in Python, senza dover avviare un intero processo.

risposta

1

Dal browsing dello cygpath source, sembra cygpath ha un'implementazione non banale e non rende disponibile alcuna versione di libreria.

cygpath fa supporto di prendere il suo input da un file utilizzando l'opzione -f (o dallo standard input, utilizzando -f -) e può prendere percorsi multipli, sputando un percorso convertito ogni volta, quindi probabilmente si potrebbe creare una singola istanza cygpath aperta (usando Python subprocess.Popen anziché riavviare cygpath ogni volta.

3

Ciò è possibile chiamando l'API Cygwin utilizzando i tipi. Il codice seguente funziona per me: sto usando la versione 2.5.2 Cygwin DLL a 64 bit su Windows 2012, e questo funziona sulle versioni Cygwin sia di Python 2.7.10 che di Python 3.4.3.

In pratica si chiama cygwin_create_path da cygwin1.dll per eseguire la conversione del percorso. Quella funzione alloca un buffer di memoria (usando malloc) contenente il percorso convertito. Quindi, è necessario utilizzare free da cygwin1.dll per rilasciare il buffer assegnato.

Nota che xunicode è un'alternativa di scarsa qualità a six (una libreria di compatibilità Python 2/3); se hai bisogno di supportare sia Python 2 e 3, sei è la risposta molto migliore, ma volevo che il mio esempio fosse libero da dipendenze su qualsiasi modulo non raggruppato ed è per questo che l'ho fatto in questo modo.

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

Grazie per aver condiviso il ctypes-code, ma non riesco a farlo funzionare con WinPython-3.5.2 e 2.7. 10: PY2 si blocca su ctypes '' self._handle = _dlopen (self._name, mode) '', e PY3 su '' risultato = cygwin_create_path (CCP_WIN_W_TO_POSIX, xunicode (percorso)) OSError: eccezione: violazione di accesso scrivendo 0x0000000000000000''. Qualche idea? – ankostis

+0

@ankostis, penso che il problema sia probabilmente alla riga 'cygwin = cdll.LoadLibrary (" cygwin1.dll ")'. Con Cygwin Python, 'cygwin1.dll' è già stato caricato nello spazio di indirizzamento del processo Python quindi' LoadLibrary' non ha problemi a trovarlo. Tuttavia, dal momento che WinPython è una pura app per Windows (senza Cygwin), quindi 'cygwin1.dll' non verrà normalmente caricato, quindi è necessario fornire il percorso completo al tuo' cygwin1.dll'. Inoltre, se il tuo WinPython è a 64 bit, devi caricare un 'cygwin1.dll 'a 64 bit; viceversa, se il tuo WinPython è a 32 bit, devi caricare un 'cygwin1.dll 'a 32 bit. –

+0

Ho provato in un'altra macchina come hai detto, e ora sto ottenendo questo: 'Percorso originale Win32: C: \ Users \ ankostis \ AppData \ Local 0 [main] python 772 D: \ Apps \ WinPython-64bit- 3.5.2.1 \ python-3.5.2.amd64 \ python.exe: *** errore fatale - Errore interno: TP_NUM_C_BUFS troppo piccolo: 50 1090 [main] python 772 cygwin_exception :: open_stackdumpfile: scarica la traccia di stack in python.exe.stackdump @tokoti (cygwin): ~/Work/gitdb. git $ less python.exe.stackdump' – ankostis

0

avrei preferito scrivere questo Python supporto che utilizza il cygwin dll:

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) 
Problemi correlati