2012-06-04 3 views
10

Używam skryptu Python, działającego w Cygwinowej wersji Pythona, do tworzenia poleceń wydanych dla rodzimych narzędzi Windows (nie Cygwin-aware). Wymaga to konwersji parametrów ścieżki z POSIX do WYGRAJ przed wydaniem polecenia.Konwersja ścieżki POSIX-> WIN, w Cygwin Python, bez wywoływania cygpath

Wywołanie narzędzia cygpath jest najmilszym sposobem na zrobienie tego, ponieważ używa Cygwin do robienia tego, co ma do zrobienia, ale jest też trochę przerażające (i powolne).

Mam już zainstalowaną wersję Pythona Cygwin - więc kod do konwersji jest obecny. Wygląda na to, że powinno istnieć specyficzne rozszerzenie Cygwin/Python, które daje mi hak do tej możliwości, bezpośrednio w Pythonie, bez konieczności odpalania zupełnie nowego procesu.

Odpowiedz

1

Po przejrzeniu cygpath source wygląda na to, że cygpath ma nietrywialną implementację i nie udostępnia żadnej wersji biblioteki.

cygpath obsługuje podjęciem wejście z pliku za pomocą opcji -f (lub ze standardowego wejścia, używając -f -) i może trwać wiele ścieżek, wypluwając przebudowanej ścieżka za każdym razem, więc prawdopodobnie można utworzyć pojedynczą instancję cygpath open (przy użyciu Pythona subprocess.Popen) zamiast restartować cygpath za każdym razem.

3

Jest to możliwe po wywołaniu interfejsu API Cygwin przy użyciu ctypes. Poniższy kod działa dla mnie - używam 64-bitowej biblioteki DLL cygwin w wersji 2.5.2 na Windows 2012, a to działa na wersjach Cygwin zarówno Python 2.7.10, jak i Python 3.4.3.

Zasadniczo nazywamy cygwin_create_path z cygwin1.dll, aby wykonać konwersję ścieżki. Ta funkcja przydziela bufor pamięci (używając malloc) zawierający skonwertowaną ścieżkę. Tak więc musimy użyć free z cygwin1.dll, aby zwolnić bufor, który został przydzielony.

Poniżej znajduje się xunicode, która jest alternatywą dla biednego człowieka dla six (biblioteka kompatybilności z Pythonem 2/3); jeśli potrzebujesz obsługi zarówno Python 2, jak i 3, sześć jest o wiele lepszą odpowiedzią, ale chciałem, aby mój przykład był wolny od zależności od wszelkich modułów niezwiązanych z pakietem i dlatego zrobiłem to w ten sposób.

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

Dziękujemy za dzielenie się ctypes kod, ale nie może to działać z WinPython-3.5.2 i 2.7. 10: awarii PY2 na ctypes 'self._handle = _dlopen (self._name tryb,) '' i PY3 w' wynik = cygwin_create_path (CCP_WIN_W_TO_POSIX, xUnikodzie (ścieżki)) OSError: wyjątek dostępu naruszenia pisanie 0x0000000000000000''. Jakieś pomysły? – ankostis

+0

@ankostis, myślę, że problem jest prawdopodobnie w linii 'cygwin = cdll.LoadLibrary (" cygwin1.dll ")'. Z Cygwin Python, 'cygwin1.dll' jest już załadowany do przestrzeni adresowej procesu Python, więc' LoadLibrary' nie ma problemu ze znalezieniem go. Jednakże, ponieważ WinPython jest czystą aplikacją Windows (bez Cygwin), więc 'cygwin1.dll' zwykle nie będzie załadowany, więc musisz podać pełną ścieżkę do' cygwin1.dll'. Ponadto, jeśli twój WinPython jest 64-bitowy, musisz załadować 64-bitowy plik 'cygwin1.dll'; i odwrotnie, jeśli twój WinPython jest 32-bitowy, musisz załadować 32-bitowy plik 'cygwin1.dll'. –

+0

Próbowałem na innym komputerze, jak powiedziałeś, a teraz otrzymuję to: 'Oryginalna ścieżka Win32: C: \ Users \ ankostis \ AppData \ Local 0 [główny] python 772 D: \ Apps \ WinPython-64bit- 3.5.2.1 \ python-3.5.2.amd64 \ python.exe: *** błąd krytyczny - Błąd wewnętrzny: TP_NUM_C_BUFS za mały: 50 1090 [główny] python 772 cygwin_exception :: open_stackdumpfile: Dumping śledzenia stosu do python.exe.stackdump @tokoti (cygwin): ~/Work/gitdb. git $ less python.exe.stackdump' – ankostis

0

Wolałbym napisać Pythona pomocnika, który używa biblioteki 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) 
Powiązane problemy