Проверьте, не является ли файловая система нечувствительной к регистру в Python

Есть ли простой способ проверить Python, если файловая система нечувствительна к регистру? Я имею в виду, в частности, файловые системы, такие как HFS + (OSX) и NTFS (Windows), где вы можете получить доступ к тому же файлу, что и foo, Foo или FOO, даже если файл сохраняется.

Ответы

Ответ 1

import os
import tempfile

# By default mkstemp() creates a file with
# a name that begins with 'tmp' (lowercase)
tmphandle, tmppath = tempfile.mkstemp()
if os.path.exists(tmppath.upper()):
    # Case insensitive.
else:
    # Case sensitive.

Ответ 2

Ответ, предоставленный Amber, оставит временные мусорные файлы, если закрытие и удаление не будут обработаны явно. Чтобы этого избежать, я использую:

import os
import tempfile

def is_fs_case_sensitive():
    #
    # Force case with the prefix
    #
    with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
        return(not os.path.exists(tmp_file.name.lower()))

Хотя мои примеры использования обычно проверяют это более одного раза, поэтому я нажимаю результат, чтобы не касаться файловой системы более одного раза.

def is_fs_case_sensitive():
    if not hasattr(is_fs_case_sensitive, 'case_sensitive'):
        with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
            setattr(is_fs_case_sensitive,
                    'case_sensitive',
                    not os.path.exists(tmp_file.name.lower()))
    return(is_fs_case_sensitive.case_sensitive)

Это немного медленнее, если вы вызываете только один раз и значительно быстрее в каждом другом случае.

Ответ 3

Начиная с ответа Amber, я придумал этот код. Я не уверен, что он полностью надежный, но он пытается решить некоторые проблемы в оригинале (о чем я расскажу ниже).

import os
import sys
import tempfile
import contextlib


def is_case_sensitive(path):
    with temp(path) as tmppath:
        head, tail = os.path.split(tmppath)
        testpath = os.path.join(head, tail.upper())
        return not os.path.exists(testpath)


@contextlib.contextmanager
def temp(path):
    tmphandle, tmppath = tempfile.mkstemp(dir=path)
    os.close(tmphandle)
    try:
        yield tmppath
    finally:
        os.unlink(tmppath)


if __name__ == '__main__':
    path = os.path.abspath(sys.argv[1])
    print(path)
    print('Case sensitive: ' + str(is_case_sensitive(path)))

Без указания параметра dir в mkstemp вопрос о чувствительности к регистру нечеткий. Вы проверяете чувствительность к регистру везде, где находится временный каталог, но вы можете знать об определенном пути.

Если вы конвертируете полный путь, возвращенный из mkstemp в верхний регистр, вы можете пропустить переход где-то в пути. Например, у меня есть USB-накопитель на Linux, установленный с помощью vfat в /media/FLASH. Тестирование существования чего-либо под /media/FLASH всегда будет терпеть неудачу, потому что /media находится на секционном диске ext4 (с учетом регистра), но сам флеш-диск не учитывает регистр. Установленные сетевые ресурсы могут быть другой ситуацией.

Наконец, и, может быть, это само собой разумеется в ответе Amber, вы захотите очистить временный файл, созданный mkstemp.

Ответ 4

Хорошая точка в разных файловых системах и т.д., Эрик Смит. Но почему бы не использовать tempfile.NamedTemporaryFile с параметром dir и избежать того, чтобы сделать все, что менеджер контекста поднял?

def is_fs_case_sensitive(path):
    #
    # Force case with the prefix
    #
    with tempfile.NamedTemporaryFile(prefix='TmP',dir=path) as tmp_file:
        return(not os.path.exists(tmp_file.name.lower()))

Следует также упомянуть, что ваше решение не гарантирует, что вы на самом деле проверяете чувствительность к регистру. Если вы не проверите префикс по умолчанию (используйте tempfile.gettempprefix()), чтобы убедиться, что он содержит строчный символ. Поэтому включение префикса здесь не является обязательным.

Ваше решение очищает временный файл. Я согласен с тем, что это казалось очевидным, но никто не знает, сделайте одно?

Ответ 5

import os

if os.path.normcase('A') == os.path.normcase('a'):
    # case insensitive
else:
    # case sensitive