Periksa apakah jalur valid dalam Python tanpa membuat file di target jalur

98

Saya memiliki jalur (termasuk direktori dan nama file).
Saya perlu menguji apakah nama file itu valid, misalnya jika sistem file mengizinkan saya membuat file dengan nama seperti itu.
Nama file memiliki beberapa karakter unicode di dalamnya.

Aman untuk mengasumsikan segmen direktori dari jalur tersebut valid dan dapat diakses ( saya mencoba membuat pertanyaan lebih dapat diterapkan secara umum, dan tampaknya saya terlalu jauh ).

Saya sangat tidak ingin melarikan diri kecuali jika saya harus .

Saya akan memposting beberapa contoh karakter yang saya hadapi, tetapi tampaknya mereka dihapus secara otomatis oleh sistem pertukaran tumpukan. Bagaimanapun, saya ingin mempertahankan entitas unicode standar seperti ö, dan hanya menghindari hal-hal yang tidak valid dalam nama file.


Ini tangkapannya. Mungkin (atau mungkin tidak) sudah ada file di target jalur. Saya perlu menyimpan file itu jika memang ada, dan tidak membuat file jika tidak ada.

Pada dasarnya saya ingin memeriksa apakah saya dapat menulis ke jalur tanpa benar-benar membuka jalur untuk menulis (dan pembuatan file otomatis / pemogokan file yang biasanya memerlukan).

Dengan demikian:

try:
    open(filename, 'w')
except OSError:
    # handle error here

dari sini

Tidak dapat diterima, karena akan menimpa file yang ada, yang tidak ingin saya sentuh (jika ada), atau membuat file tersebut jika tidak ada.

Saya tahu saya bisa melakukan:

if not os.access(filePath, os.W_OK):
    try:
        open(filePath, 'w').close()
        os.unlink(filePath)
    except OSError:
        # handle error here

Tapi itu akan membuat file di filePath, yang kemudian harus saya lakukan os.unlink.

Pada akhirnya, sepertinya menghabiskan 6 atau 7 baris untuk melakukan sesuatu yang sesederhana os.isvalidpath(filePath)atau serupa.


Selain itu, saya membutuhkan ini untuk berjalan di (setidaknya) Windows dan MacOS, jadi saya ingin menghindari hal-hal khusus platform.

``

Nama palsu
sumber
Jika Anda ingin menguji bahwa jalur tersebut ada dan Anda dapat menulis padanya, maka cukup buat dan hapus beberapa file lain. Beri nama unik (atau seunik yang Anda bisa), untuk menghindari masalah multi pengguna / multi utas. Jika tidak, Anda melihat memeriksa permssions yang akan membawa Anda langsung ke kekacauan khusus OS.
Tony Hopkinson
3
@Tony Hopkinson - Pada dasarnya saya ingin memeriksa apakah saya dapat menulis ke suatu jalur tanpa benar-benar menulis apa pun .
Nama Palsu
Jika Anda tidak memiliki apa pun untuk ditulis ke file, lalu mengapa Anda perlu tahu jika Anda bisa?
Karl Knechtel
@Karl Knechtel - Jika saya menulisnya, dan sudah ada file di sana, itu akan merusak file yang ada.
Nama Palsu
2
@FakeName - Anda akan selalu memiliki kondisi balapan yang halus di sini. Antara memeriksa bahwa file tersebut tidak ada tetapi dapat dibuat, dan kemudian membuat file tersebut, beberapa proses lain dapat membuatnya dan Anda tetap akan memukuli file tersebut. Tentu saja, itu tergantung pada penggunaan Anda apakah ini masalah realistis atau tidak ...
detly

Jawaban:

155

tl; dr

Panggil is_path_exists_or_creatable()fungsi yang ditentukan di bawah ini.

Ketat Python 3. Begitulah cara kami menggulung.

Kisah Dua Pertanyaan

Pertanyaan tentang "Bagaimana cara menguji validitas nama jalur dan, untuk nama jalur yang valid, keberadaan atau kemampuan penulisan jalur tersebut?" jelas merupakan dua pertanyaan terpisah. Keduanya menarik, dan tidak ada yang menerima jawaban yang benar-benar memuaskan di sini ... atau, di mana pun yang bisa saya grep.

vikki 's jawabannya mungkin hews yang paling dekat, namun memiliki kelemahan yang luar biasa:

  • Tidak perlu membuka ( ... dan kemudian gagal menutup dengan andal ) pegangan file.
  • Tidak perlu menulis ( ... dan kemudian gagal menutup atau menghapus secara andal ) file 0-byte.
  • Mengabaikan kesalahan khusus OS yang membedakan antara nama jalur tidak valid yang tidak dapat diabaikan dan masalah sistem file yang dapat diabaikan. Tidak mengherankan, ini sangat penting di bawah Windows. ( Lihat di bawah. )
  • Mengabaikan kondisi balapan yang dihasilkan dari proses eksternal secara bersamaan memindahkan (kembali) direktori induk dari nama jalur yang akan diuji. ( Lihat di bawah. )
  • Mengabaikan waktu tunggu koneksi yang diakibatkan dari nama jalur ini yang berada di sistem file usang, lambat, atau sementara tidak dapat diakses. Ini dapat mengekspos layanan yang dihadapi publik terhadap potensi serangan yang didorong oleh DoS . ( Lihat di bawah. )

Kami akan memperbaiki semua itu.

Pertanyaan # 0: Apa itu Pathname Validity Lagi?

Sebelum melemparkan pakaian daging kita yang rapuh ke dalam moshpits yang penuh dengan ular piton, kita mungkin harus mendefinisikan apa yang kita maksud dengan "validitas nama jalur". Apa sebenarnya yang mendefinisikan validitas?

Yang kami maksud dengan "validitas nama jalur" adalah kebenaran sintaksis dari nama jalur yang terkait dengan sistem file root dari sistem saat ini - terlepas dari apakah jalur tersebut atau direktori induknya secara fisik ada. Sebuah nama jalur secara sintaksis benar di bawah definisi ini jika itu memenuhi semua persyaratan sintaksis dari sistem berkas root.

Yang kami maksud dengan "sistem file root" adalah:

  • Pada sistem yang kompatibel dengan POSIX, sistem file dipasang ke direktori root ( /).
  • Pada Windows, sistem berkas dipasang ke %HOMEDRIVE%, huruf kandar dengan akhiran titik dua yang berisi penginstalan Windows saat ini (biasanya tetapi tidak harus C:).

Arti "kebenaran sintaksis", pada gilirannya, bergantung pada jenis sistem file root. Untuk ext4(dan sebagian besar tetapi tidak semua sistem file yang kompatibel dengan POSIX), nama jalur secara sintaksis benar jika dan hanya jika nama jalur itu:

  • Tidak mengandung byte nol (yaitu, \x00dengan Python). Ini adalah persyaratan yang sulit untuk semua sistem file yang kompatibel dengan POSIX.
  • Tidak berisi komponen jalur yang lebih panjang dari 255 byte (misalnya, 'a'*256dengan Python). Sebuah komponen path adalah substring terpanjang dari pathname yang tidak mengandung /karakter (misalnya, bergtatt, ind, i, dan fjeldkamrenedi pathname /bergtatt/ind/i/fjeldkamrene).

Ketepatan sintaksis. Sistem file root. Itu dia.

Pertanyaan # 1: Bagaimana Sekarang Kita Melakukan Validitas Pathname?

Memvalidasi nama jalur dengan Python ternyata tidak intuitif. Saya sangat setuju dengan Nama Palsu di sini: os.pathpaket resmi harus memberikan solusi out-of-the-box untuk ini. Untuk alasan yang tidak diketahui (dan mungkin tidak menarik), ternyata tidak. Untungnya, membuka gulungan solusi ad-hoc Anda sendiri tidak terlalu memilukan ...

Oke, sebenarnya begitu. Itu berbulu; itu jorok; itu mungkin terkekeh saat menggelegar dan cekikikan saat bersinar. Tapi apa yang akan kamu lakukan? Nuthin '.

Kami akan segera turun ke jurang radioaktif kode tingkat rendah. Tapi pertama-tama, mari kita bicara tentang toko tingkat tinggi. Standar os.stat()dan os.lstat()fungsi memunculkan pengecualian berikut saat meneruskan nama jalur yang tidak valid:

  • Untuk nama jalur yang berada di direktori yang tidak ada, contoh FileNotFoundError.
  • Untuk nama jalur yang berada di direktori yang ada:
    • Di bawah Windows, contoh WindowsErroryang winerroratributnya adalah 123(yaitu, ERROR_INVALID_NAME).
    • Di bawah semua OS lainnya:
    • Untuk nama jalur yang berisi byte nol (yaitu, '\x00'), instance dari TypeError.
    • Untuk nama jalur yang berisi komponen jalur lebih panjang dari 255 byte, contoh OSErroryang errcodeatributnya adalah:
      • Di bawah SunOS dan keluarga OS * BSD errno.ERANGE,. (Ini tampaknya merupakan bug tingkat OS, atau disebut sebagai "interpretasi selektif" dari standar POSIX.)
      • Di bawah semua OS lainnya errno.ENAMETOOLONG,.

Yang terpenting, ini menyiratkan bahwa hanya nama jalur yang berada di direktori yang ada yang dapat divalidasi. Fungsi os.stat()dan os.lstat()memunculkan FileNotFoundErrorpengecualian umum ketika nama jalur yang diteruskan yang berada di direktori yang tidak ada, terlepas dari apakah nama jalur tersebut tidak valid atau tidak. Keberadaan direktori lebih diutamakan daripada ketidakabsahan nama jalur.

Apakah ini berarti bahwa nama path yang berada di direktori yang tidak ada tidak dapat divalidasi? Ya - kecuali kita memodifikasi nama path tersebut agar berada di direktori yang ada. Namun, apakah itu mungkin secara aman? Bukankah memodifikasi nama jalur mencegah kami memvalidasi nama jalur asli?

Untuk menjawab pertanyaan ini, ingat dari atas bahwa nama jalur yang benar secara sintaksis pada sistem ext4file tidak berisi komponen jalur (A) yang berisi byte nol atau (B) dengan panjang lebih dari 255 byte. Karenanya, ext4nama jalur valid jika dan hanya jika semua komponen jalur dalam nama jalur itu valid. Hal ini berlaku untuk sebagian besar sistem file dunia nyata yang menarik.

Apakah wawasan bertele-tele itu benar-benar membantu kita? Iya. Ini mengurangi masalah yang lebih besar dalam memvalidasi nama jalur lengkap dalam satu gerakan ke masalah yang lebih kecil hanya memvalidasi semua komponen jalur dalam nama jalur itu. Nama jalur arbitrer apa pun dapat divalidasi (terlepas dari apakah nama jalur tersebut berada di direktori yang ada atau tidak) secara lintas platform dengan mengikuti algoritme berikut:

  1. Pisahkan nama jalur tersebut menjadi beberapa komponen jalur (misalnya, nama /troldskog/faren/vildjalur ke dalam daftar ['', 'troldskog', 'faren', 'vild']).
  2. Untuk setiap komponen tersebut:
    1. Gabung dengan nama jalur direktori yang dijamin ada dengan komponen itu menjadi nama jalur sementara baru (mis., /troldskog).
    2. Berikan nama jalur itu ke os.stat()atau os.lstat(). Jika nama jalur itu dan karenanya komponen itu tidak valid, panggilan ini dijamin akan memunculkan pengecualian yang mengekspos jenis ketidakabsahan daripada FileNotFoundErrorpengecualian umum . Mengapa? Karena nama jalur itu berada di direktori yang sudah ada. (Logika melingkar adalah melingkar.)

Apakah ada direktori yang dijamin ada? Ya, tetapi biasanya hanya satu: direktori paling atas dari sistem berkas root (seperti yang didefinisikan di atas).

Meneruskan nama jalur yang berada di direktori lain (dan karenanya tidak dijamin ada) ke os.stat()atau os.lstat()mengundang kondisi balapan, meskipun direktori tersebut sebelumnya telah diuji keberadaannya. Mengapa? Karena proses eksternal tidak dapat dicegah untuk secara bersamaan menghapus direktori tersebut setelah pengujian dilakukan, tetapi sebelum nama jalur tersebut diteruskan ke os.stat()atau os.lstat(). Bebaskan anjing kegilaan yang menghancurkan pikiran!

Ada juga manfaat sampingan yang substansial dari pendekatan di atas: keamanan. (Bukankah itu bagus?) Secara khusus:

Aplikasi hadap depan yang memvalidasi nama jalur sewenang-wenang dari sumber yang tidak tepercaya hanya dengan meneruskan nama jalur tersebut ke os.stat()atau os.lstat()rentan terhadap serangan Denial of Service (DoS) dan kejahatan black-hat lainnya. Pengguna jahat mungkin mencoba untuk berulang kali memvalidasi nama path yang berada pada filesystem yang dikenal basi atau lambat (misalnya, NFS Samba share); dalam hal ini, nama jalur masuk yang secara membabi buta dapat menyebabkan kegagalan pada akhirnya dengan batas waktu koneksi atau menghabiskan lebih banyak waktu dan sumber daya daripada kapasitas Anda yang lemah untuk menahan pengangguran.

Pendekatan di atas menghilangkan ini dengan hanya memvalidasi komponen path dari nama path terhadap direktori root dari filesystem root. (Bahkan jika itu basi, lambat, atau tidak dapat diakses, Anda memiliki masalah yang lebih besar daripada validasi nama jalur.)

Kalah? Bagus. Mari kita mulai. (Python 3 diasumsikan. Lihat "What Is Fragile Hope for 300, leycec ?")

import errno, os

# Sadly, Python fails to provide the following magic number for us.
ERROR_INVALID_NAME = 123
'''
Windows-specific error code indicating an invalid pathname.

See Also
----------
https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    Official listing of all such codes.
'''

def is_pathname_valid(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS;
    `False` otherwise.
    '''
    # If this pathname is either not a string or is but is empty, this pathname
    # is invalid.
    try:
        if not isinstance(pathname, str) or not pathname:
            return False

        # Strip this pathname's Windows-specific drive specifier (e.g., `C:\`)
        # if any. Since Windows prohibits path components from containing `:`
        # characters, failing to strip this `:`-suffixed prefix would
        # erroneously invalidate all valid absolute Windows pathnames.
        _, pathname = os.path.splitdrive(pathname)

        # Directory guaranteed to exist. If the current OS is Windows, this is
        # the drive to which Windows was installed (e.g., the "%HOMEDRIVE%"
        # environment variable); else, the typical root directory.
        root_dirname = os.environ.get('HOMEDRIVE', 'C:') \
            if sys.platform == 'win32' else os.path.sep
        assert os.path.isdir(root_dirname)   # ...Murphy and her ironclad Law

        # Append a path separator to this directory if needed.
        root_dirname = root_dirname.rstrip(os.path.sep) + os.path.sep

        # Test whether each path component split from this pathname is valid or
        # not, ignoring non-existent and non-readable path components.
        for pathname_part in pathname.split(os.path.sep):
            try:
                os.lstat(root_dirname + pathname_part)
            # If an OS-specific exception is raised, its error code
            # indicates whether this pathname is valid or not. Unless this
            # is the case, this exception implies an ignorable kernel or
            # filesystem complaint (e.g., path not found or inaccessible).
            #
            # Only the following exceptions indicate invalid pathnames:
            #
            # * Instances of the Windows-specific "WindowsError" class
            #   defining the "winerror" attribute whose value is
            #   "ERROR_INVALID_NAME". Under Windows, "winerror" is more
            #   fine-grained and hence useful than the generic "errno"
            #   attribute. When a too-long pathname is passed, for example,
            #   "errno" is "ENOENT" (i.e., no such file or directory) rather
            #   than "ENAMETOOLONG" (i.e., file name too long).
            # * Instances of the cross-platform "OSError" class defining the
            #   generic "errno" attribute whose value is either:
            #   * Under most POSIX-compatible OSes, "ENAMETOOLONG".
            #   * Under some edge-case OSes (e.g., SunOS, *BSD), "ERANGE".
            except OSError as exc:
                if hasattr(exc, 'winerror'):
                    if exc.winerror == ERROR_INVALID_NAME:
                        return False
                elif exc.errno in {errno.ENAMETOOLONG, errno.ERANGE}:
                    return False
    # If a "TypeError" exception was raised, it almost certainly has the
    # error message "embedded NUL character" indicating an invalid pathname.
    except TypeError as exc:
        return False
    # If no exception was raised, all path components and hence this
    # pathname itself are valid. (Praise be to the curmudgeonly python.)
    else:
        return True
    # If any other exception was raised, this is an unrelated fatal issue
    # (e.g., a bug). Permit this exception to unwind the call stack.
    #
    # Did we mention this should be shipped with Python already?

Selesai. Jangan menyipitkan mata pada kode itu. ( Itu menggigit. )

Pertanyaan # 2: Keberadaan atau Keberadaan Nama Jalur yang Mungkin Tidak Valid, Eh?

Menguji keberadaan atau pembuatan nama jalur yang mungkin tidak valid, mengingat solusi di atas, kebanyakan sepele. Kunci kecil di sini adalah memanggil fungsi yang ditentukan sebelumnya sebelum menguji jalur yang dilewati:

def is_path_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create the passed
    pathname; `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()
    return os.access(dirname, os.W_OK)

def is_path_exists_or_creatable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS _and_
    either currently exists or is hypothetically creatable; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Selesai dan selesai. Kecuali tidak sepenuhnya.

Pertanyaan # 3: Keberadaan Pathname atau Penulisan yang Mungkin Tidak Valid di Windows

Ada peringatan. Tentu saja ada.

Seperti yang diakui oleh os.access()dokumentasi resmi :

Catatan: Operasi I / O mungkin gagal bahkan ketika os.access()menunjukkan bahwa mereka akan berhasil, terutama untuk operasi pada sistem file jaringan yang mungkin memiliki semantik izin di luar model bit izin POSIX biasa.

Tidak mengherankan, Windows adalah tersangka biasa di sini. Berkat penggunaan Access Control List (ACL) yang ekstensif pada sistem file NTFS, model izin-bit POSIX yang sederhana memetakan dengan buruk ke realitas Windows yang mendasarinya. Meskipun ini (bisa dibilang) bukan kesalahan Python, itu mungkin menjadi perhatian untuk aplikasi yang kompatibel dengan Windows.

Jika ini Anda, alternatif yang lebih kuat dibutuhkan. Jika jalur yang diteruskan tidak ada, kami malah mencoba membuat file sementara yang dijamin akan segera dihapus di direktori induk dari jalur tersebut - tes kreasi yang lebih portabel (jika mahal):

import os, tempfile

def is_path_sibling_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create **siblings**
    (i.e., arbitrary files in the parent directory) of the passed pathname;
    `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()

    try:
        # For safety, explicitly close and hence delete this temporary file
        # immediately after creating it in the passed path's parent directory.
        with tempfile.TemporaryFile(dir=dirname): pass
        return True
    # While the exact type of exception raised by the above function depends on
    # the current version of the Python interpreter, all such types subclass the
    # following exception superclass.
    except EnvironmentError:
        return False

def is_path_exists_or_creatable_portable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname on the current OS _and_
    either currently exists or is hypothetically creatable in a cross-platform
    manner optimized for POSIX-unfriendly filesystems; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_sibling_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Perhatikan, bagaimanapun, bahwa ini pun mungkin tidak cukup.

Berkat User Access Control (UAC), Windows Vista yang selalu tidak dapat ditiru dan semua iterasi berikutnya secara terang - terangan berbohong tentang perizinan yang berkaitan dengan direktori sistem. Ketika pengguna non-Administrator mencoba membuat file baik di kanonis C:\Windowsatau C:\Windows\system32direktori, UAC secara dangkal mengizinkan pengguna untuk melakukannya sambil benar-benar mengisolasi semua file yang dibuat ke dalam "Toko Virtual" di profil pengguna tersebut. (Siapa yang menyangka bahwa menipu pengguna akan memiliki konsekuensi jangka panjang yang berbahaya?)

Ini gila. Ini adalah Windows.

Buktikan itu

Berani kita? Saatnya untuk menguji coba tes di atas.

Karena NULL adalah satu-satunya karakter yang dilarang dalam nama path pada sistem file berorientasi UNIX, mari kita manfaatkan itu untuk menunjukkan kebenaran yang dingin dan sulit - mengabaikan kejahatan Windows yang tidak dapat diabaikan, yang terus terang membuat saya bosan dan marah dalam ukuran yang sama:

>>> print('"foo.bar" valid? ' + str(is_pathname_valid('foo.bar')))
"foo.bar" valid? True
>>> print('Null byte valid? ' + str(is_pathname_valid('\x00')))
Null byte valid? False
>>> print('Long path valid? ' + str(is_pathname_valid('a' * 256)))
Long path valid? False
>>> print('"/dev" exists or creatable? ' + str(is_path_exists_or_creatable('/dev')))
"/dev" exists or creatable? True
>>> print('"/dev/foo.bar" exists or creatable? ' + str(is_path_exists_or_creatable('/dev/foo.bar')))
"/dev/foo.bar" exists or creatable? False
>>> print('Null byte exists or creatable? ' + str(is_path_exists_or_creatable('\x00')))
Null byte exists or creatable? False

Di luar kewarasan. Melampaui rasa sakit. Anda akan menemukan masalah portabilitas Python.

Cecil Curry
sumber
3
Yup, itu aku! Mencoba menyatukan regex validasi nama jalur lintas portabel adalah latihan yang sia-sia dan dijamin gagal untuk kasus-kasus umum. Pertimbangkan panjang nama jalur di Windows, misalnya: "Jalur maksimum 32.767 karakter adalah perkiraan, karena awalan '\\? \' Dapat diperluas ke string yang lebih panjang oleh sistem pada waktu proses, dan perluasan ini berlaku untuk total panjang . " Mengingat itu, sebenarnya secara teknis tidak mungkin untuk membuat regex yang hanya cocok dengan nama jalur yang valid. Jauh lebih masuk akal untuk tunduk pada Python.
Cecil Curry
2
Ah. Saya (dengan enggan) melihat. Anda melakukan sesuatu yang bahkan lebih aneh daripada meretas regex. Ya, itu dijamin gagal lebih keras. Itu juga benar-benar gagal menjawab pertanyaan yang dimaksud, yang bukan "Bagaimana cara menghapus substring yang tidak valid dari nama dasar khusus Windows?" (... yang, dengan kelalaian Anda sendiri, Anda gagal untuk menyelesaikannya - sekali lagi karena kasus edge) tetapi "Bagaimana cara saya menguji validitas nama jalur secara lintas portabel dan, untuk nama jalur yang valid, keberadaan atau kemampuan penulisan jalur tersebut?"
Cecil Curry
1
Batasan spesifik sistem file jelas merupakan perhatian yang valid - tetapi memotong dua arah. Untuk aplikasi hadap depan yang mengonsumsi nama jalur sewenang-wenang dari sumber yang tidak tepercaya, melakukan pembacaan secara membabi buta adalah proposisi yang tidak pasti; dalam kasus ini, memaksa penggunaan sistem file root tidak hanya bijaksana tetapi juga bijaksana. Untuk aplikasi lain, bagaimanapun, basis pengguna mungkin cukup dapat dipercaya untuk memberikan akses sistem file tanpa hambatan. Ini cukup bergantung pada konteks, menurut saya. Terima kasih telah mencatat ini dengan cerdik, Tidak ada ! Saya akan menambahkan peringatan di atas.
Cecil Curry
2
Adapun nomenklatur, saya penggemar berat mengawali nama penguji dengan is_ . Ini adalah kekurangan karakter saya. Meskipun demikian, perlu dicatat: Anda tidak bisa menyenangkan semua orang, dan terkadang Anda tidak bisa menyenangkan siapa pun. ;)
Cecil Curry
1
Pada Fedora 24, python 3.5.3, nama jalur dengan karakter null tertanam melempar: ValueError: byte null tertanam ... perlu menambahkan: `` `` kecuali ValueError sebagai exc: return False '' sebelum atau setelah perangkap TypeError.
mMerlin
47
if os.path.exists(filePath):
    #the file is there
elif os.access(os.path.dirname(filePath), os.W_OK):
    #the file does not exists but write privileges are given
else:
    #can not write there

Perhatikan bahwa path.existsbisa gagal karena lebih dari sekedar alasan the file is not theresehingga Anda mungkin harus melakukan tes yang lebih baik seperti pengujian jika direktori yang berisi ada dan seterusnya.


Setelah saya berdiskusi dengan OP ternyata, yang menjadi masalah utama sepertinya, bahwa nama file mungkin mengandung karakter yang tidak diperbolehkan oleh filesystem. Tentu saja mereka perlu dihapus tetapi OP ingin mempertahankan sebanyak mungkin kemudahan yang dapat dibaca oleh sistem file.

Sayangnya saya tidak tahu solusi yang baik untuk ini. Namun , jawaban Cecil Curry melihat lebih dekat pada pendeteksian masalahnya.

Tidak ada yang menjauh dari SE
sumber
Tidak. Saya harus mengembalikan true jika file di jalur ada, atau dapat dibuat . Saya perlu mengembalikan false jika jalur tidak valid (karena mengandung karakter tidak valid di windows).
Nama Palsu
or can be createdbaik saya tidak membacanya dari pertanyaan Anda. Membaca izin akan bergantung pada platfrom sampai batas tertentu.
Tidak ada yang pindah dari SE
1
@Fake Name: Ya, ini akan menghapus beberapa ketergantungan platform tetapi masih beberapa platform menawarkan hal-hal yang tidak dimiliki orang lain dan tidak ada cara mudah untuk membungkusnya untuk semuanya. Saya memperbarui jawaban saya, lihat di sana.
Tidak ada yang pindah dari SE
1
Saya tidak tahu mengapa jawaban ini diberi suara positif. Itu tidak datang jauh berdekatan dengan menangani pertanyaan inti - yang, ringkas, adalah: "? Validasi nama path, silakan" Memvalidasi izin jalur adalah pertanyaan tambahan (dan sebagian besar dapat diabaikan) di sini. Meskipun panggilan ke secara os.path.exists(filePath)teknis memunculkan pengecualian pada nama jalur yang tidak valid, pengecualian tersebut perlu ditangkap dan dibedakan secara eksplisit dari pengecualian lain yang tidak terkait. Selain itu, panggilan yang sama kembali Falseke jalur yang ada di mana pengguna saat ini tidak memiliki izin baca. Singkatnya, kejahatan.
Cecil Curry
1
@CecilCurry: Untuk menjawab pertanyaan Anda: Lihat histori edit pertanyaan tersebut. Seperti kebanyakan pertanyaan, awalnya tidak begitu jelas dan bahkan sekarang kata-kata dari judulnya saja bisa dipahami selain yang Anda katakan.
Tidak ada yang pindah dari SE
9

Dengan Python 3, bagaimana dengan:

try:
    with open(filename, 'x') as tempfile: # OSError if file exists or is invalid
        pass
except OSError:
    # handle error here

Dengan opsi 'x' kami juga tidak perlu khawatir dengan kondisi balapan. Lihat dokumentasi di sini .

Sekarang, ini AKAN membuat file sementara yang berumur sangat pendek jika belum ada - kecuali namanya tidak valid. Jika Anda bisa menerimanya, itu menyederhanakan banyak hal.

Stephen Miller
sumber
2
Pada titik ini, proyek yang membutuhkan ini telah bergerak jauh melampaui titik di mana jawaban bahkan relevan sehingga saya tidak dapat benar-benar menerima jawaban.
Nama Palsu
Ironisnya jawaban praktis tidak cukup baik. Terlepas dari itu, saya kira Anda dapat melihat apakah file itu ada. Jika ya, coba salin file di tempat lain, lalu coba timpa.
Matt
5
open(filename,'r')   #2nd argument is r and not w

akan membuka file atau memberikan kesalahan jika tidak ada. Jika ada kesalahan, maka Anda dapat mencoba menulis ke jalur, jika Anda tidak bisa maka Anda mendapatkan kesalahan kedua

try:
    open(filename,'r')
    return True
except IOError:
    try:
        open(filename, 'w')
        return True
    except IOError:
        return False

Lihat juga di sini tentang izin di windows

Vikki
sumber
1
Untuk menghindari kebutuhan untuk secara eksplisit membatalkan tautan () file pengujian, Anda bisa menggunakan tempfile.TemporaryFile()yang secara otomatis akan menghancurkan tempfile ketika keluar dari ruang lingkup.
D_Bye
@FakeName Kodenya berbeda, saya dapat menggunakan os.access pada bagian kedua tetapi jika Anda mengikuti tautan yang saya berikan, Anda akan melihat bahwa itu bukan ide yang baik, ini membuat Anda memiliki pilihan untuk mencoba benar-benar membuka jalur untuk menulis.
vikki
Saya sedang membangun jalur saya dengan os.path.join, jadi saya tidak memiliki masalah `\` melarikan diri. Selain itu, saya tidak benar-benar mengalami masalah izin direktori . Saya mengalami masalah nama direktori (dan nama file) .
Nama Palsu
@FakeName dalam hal ini Anda hanya perlu mencoba dan membukanya (Anda tidak perlu menulis), python memberikan kesalahan jika filenameberisi karakter yang tidak valid. Saya telah mengedit jawabannya
vikki
1
@HelgaIliashenko Membuka untuk menulis akan menimpa file yang sudah ada (membuatnya kosong) meskipun Anda segera menutupnya tanpa menulisnya. Itu sebabnya saya buka untuk membaca dulu karena begitu, jika Anda tidak mendapatkan kesalahan maka Anda tahu bahwa ada file yang ada.
vikki
-7

coba os.path.existsini akan memeriksa jalur dan mengembalikan Truejika ada dan Falsejika tidak.

Nilesh
sumber
1
Tidak. Saya harus mengembalikan true jika file di jalur ada, atau dapat dibuat . Saya perlu mengembalikan false jika jalur tidak valid (karena mengandung karakter tidak valid di windows).
Nama Palsu
jenis karakter tidak valid yang mana?
Nilesh
Entahlah - itu platform khusus.
Nama Palsu
2
Sebenarnya, sistem file spesifik.
Piotr Kalinowski