Skip to content

Commit

Permalink
Refactor is_hidden to address kivy#5873
Browse files Browse the repository at this point in the history
* Added failing test before implementing fix.
* Added super-basic iconview test, because the code is never exercised.
* Refactor is_hidden so it doesn't fail: Use built-in `os.stat` instead of win32file's `GetFileAttributesExW()` because it handles (in use) system files.
* Sort imports according to PEP8.
* Stop (conditionally) importing win32file.
* Add support for macOS's UF_Hidden attributes.
* Confirm new tests are now passing.
  • Loading branch information
Julian-O committed Dec 12, 2023
1 parent 49b81cb commit ff6584c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 42 deletions.
46 changes: 41 additions & 5 deletions kivy/tests/test_filechooser.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
import logging
from os import environ
from os.path import expanduser, exists
from sys import version_info
import unittest

from kivy.tests.common import GraphicUnitTest
from kivy.uix.filechooser import FileChooserListView, FileChooserIconView


class FileChooserTestCase(GraphicUnitTest):

def test_filechooserlistview(self):
from kivy.uix.filechooser import FileChooserListView
from os.path import expanduser
r = self.render
wid = FileChooserListView(path=expanduser('~'))
r(wid, 2)
widget = FileChooserListView(path=expanduser("~"))
r(widget, 2)

def test_filechoosericonview(self):
r = self.render
widget = FileChooserIconView(path=expanduser("~"), show_hidden=True)
r(widget, 2)

@unittest.skipIf(
("SystemDrive" not in environ) or
(not exists(environ["SystemDrive"] + "\\pagefile.sys")),
"Only runs on Windows with example system file present",
)
@unittest.skipIf(
version_info < (3, 10), "assertNoLogs requires in Python 3.10"
)
def test_filechooser_files_in_use_5873(self):
# Per Kivy Issue #5873, initially viewing C:\ on Windows would
# crash.
# Later versions, it merely logged warnings.
# The cause was hidden system files which could not be interrogated to
# find out if they were hidden, because they were in use by the system.

# This variant always worked, because there was no call to is_hidden.
with self.assertNoLogs(logging.getLogger("kivy"), logging.WARNING):
widget = FileChooserListView(
path=environ["SystemDrive"] + "/", show_hidden=True
)
self.render(widget, 1)

# This used to fail, because the call to is_hidden logged errors.
with self.assertNoLogs(logging.getLogger("kivy"), logging.WARNING):
widget = FileChooserListView(path=environ["SystemDrive"] + "/")
self.render(widget, 1)
60 changes: 23 additions & 37 deletions kivy/uix/filechooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,47 +85,34 @@
'FileChooserProgressBase', 'FileSystemAbstract',
'FileSystemLocal')

from weakref import ref
import collections.abc
from fnmatch import fnmatch
import os
from os import listdir
from os.path import (
basename, join, sep, normpath, expanduser, altsep,
splitdrive, realpath, getsize, isdir, abspath, isfile, dirname)
import stat
from time import time
from weakref import ref

from kivy.core.text import DEFAULT_FONT
from kivy.clock import Clock
from kivy.compat import string_types
from kivy.core.text import DEFAULT_FONT
from kivy.factory import Factory
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.utils import platform as core_platform
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import (
StringProperty, ListProperty, BooleanProperty, ObjectProperty,
NumericProperty, AliasProperty)
import collections.abc
from os import listdir
from os.path import (
basename, join, sep, normpath, expanduser, altsep,
splitdrive, realpath, getsize, isdir, abspath, isfile, dirname)
from fnmatch import fnmatch
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.utils import platform as core_platform

platform = core_platform
filesize_units = ('B', 'KB', 'MB', 'GB', 'TB')

_have_win32file = False
if platform == 'win':
# Import that module here as it's not available on non-windows machines.
# See http://bit.ly/i9klJE except that the attributes are defined in
# win32file not win32com (bug on page).
# Note: For some reason this doesn't work after a os.chdir(), no matter to
# what directory you change from where. Windows weirdness.
try:
from win32file import FILE_ATTRIBUTE_HIDDEN, GetFileAttributesExW, \
error
_have_win32file = True
except ImportError:
Logger.error('filechooser: win32file module is missing')
Logger.error('filechooser: we cannot check if a file is hidden or not')


def alphanumeric_folders_first(files, filesystem):
return (sorted(f for f in files if filesystem.is_dir(f)) +
Expand Down Expand Up @@ -174,16 +161,15 @@ def getsize(self, fn):

def is_hidden(self, fn):
if platform == 'win':
if not _have_win32file:
return False
try:
return GetFileAttributesExW(fn)[0] & FILE_ATTRIBUTE_HIDDEN
except error:
# This error can occurred when a file is already accessed by
# someone else. So don't return to True, because we have lot
# of chances to not being able to do anything with it.
Logger.exception('unable to access to <%s>' % fn)
return (
os.path.exists(fn) and
os.stat(fn).st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN)
if platform == 'darwin':
if os.stat(fn).st_flags & stat.UF_HIDDEN:
return True
# Even if not UF_HIDDEN, dot prefix rule applies.
# Other hiding rules on macOS not supported: e.g. CoreFoundation's
# blacklist.

return basename(fn).startswith('.')

Expand Down

0 comments on commit ff6584c

Please sign in to comment.