0

I am working on a load/save module (a GUI written in Python) that will be used with and imported to future programs. My operating system is Windows 10. The problem I've run into is that my get_folders() method is grabbing ALL folder names, including ones that I would rather ignore, such as system folders and hidden folders (best seen on the c-drive).

I have a work around using a hard-coded exclusion list. But this only works for folders already on the list, not for hidden folders that my wizard may come across in the future. I would like to exclude ALL folders that have their 'hidden' attribute set. I would like to avoid methods that require installing new libraries that would have to be re-installed whenever I wipe my system. Also, if the solution is non-Windows specific, yet will work with Windows 10, all the better.

I have searched SO and the web for an answer but have come up empty. The closest I have found is contained in Answer #4 of this thread: Check for a folder, then create a hidden folder in Python, which shows how to set the hidden attribute when creating a new directory, but not how to read the hidden attribute of an existing directory.

Here is my Question: Does anyone know of a way to check if a folder's 'hidden' attribute is set, using either native python, pygame, or os. commands? Or, lacking a native answer, I would accept an imported method from a library that achieves my goal.

The following program demonstrates my get_folders() method, and shows the issue at hand:

# Written for Windows 10
import os
import win32gui

CLS = lambda :os.system('cls||echo -e \\\\033c') # Clear-Command-Console function

def get_folders(path = -1, exclude_list = -1):
    
    if path == -1: path = os.getcwd()    
    if exclude_list == -1: exclude_list = ["Config.Msi","Documents and Settings","System Volume Information","Recovery","ProgramData"]
    
    dir_list = [entry.name for entry in os.scandir(path) if entry.is_dir()] if exclude_list == [] else\
     [entry.name for entry in os.scandir(path) if entry.is_dir() and '$' not in entry.name and entry.name not in exclude_list]
    
    return dir_list

def main():
    
    HWND = win32gui.GetForegroundWindow()                 # Get Command Console Handle.
    win32gui.MoveWindow(HWND,100,50,650,750,False) # Size and Position Command Console.
    CLS()  # Clear Console Screen.
    
    print(''.join(['\n','Folder Names'.center(50),'\n ',('-'*50).center(50)]))
    
    # Example 1: Current Working Directory        
    dirs = get_folders()  # Get folder names in current directory (uses exclude list.)
    for elm in dirs:print(' ',elm)  # Show the folder names.
    print('','-'*50)

    # Examle 2: C Drive, All Folders Included
    dirs = get_folders('c:\\', exclude_list = []) # Get a list of folders in the root c: drive, nothing excluded.
    for elm in dirs: print(' ',elm) # Show the fiolder names
    print('','-'*50)
    
    # Example 3: C Drive, Excluded Folder List Work-Around
    dirs = get_folders('c:\\')      # Get a list of folders in the root c: drive, excluding sytem dirs those named in the exclude_list.
    for elm in dirs:print(' ',elm)   # Show the folder names.

    print("\n Question: Is there a way to identify folders that have the 'hidden' attribute\n\t set to True, rather than using a hard-coded exclusion list?",end='\n\n' )

# ==========================
if __name__ == "__main__": 
    main()
    input(' Press [Enter] to Quit: ')
    CLS()
user10637953
  • 370
  • 1
  • 10
  • 1
    I think this [answer](https://stackoverflow.com/a/40372658/10667016) will help you – Alexander Goryushkin Dec 23 '21 at 05:20
  • Alexander, this is **exactly** what I needed. And the answer is so packed full of wonderful techniques and references that it will be a source of study and inspiration to me for a good long time. Thank You! – user10637953 Dec 23 '21 at 12:49

1 Answers1

0

Here is my revised get_folders() method, with thanks to Alexander.

# Written for Windows 10
# key portions of this code borrowed from:
# https://stackoverflow.com/questions/40367961/how-to-read-or-write-the-a-s-h-r-i-file-attributes-on-windows-using-python-and-c/40372658#40372658
# with thanks to Alexander Goryushkin.

import os
from os import scandir, stat
from stat import (
    FILE_ATTRIBUTE_ARCHIVE as A,
    FILE_ATTRIBUTE_SYSTEM as S,
    FILE_ATTRIBUTE_HIDDEN as H,
    FILE_ATTRIBUTE_READONLY as R,
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED as I
    )
from ctypes import WinDLL, WinError, get_last_error
import win32gui

CLS = lambda :os.system('cls||echo -e \\\\033c') # Clear-Command-Console function

def read_or_write_attribs(kernel32, entry,  a=None, s=None, h=None, r=None, i=None,  update=False):

    # Get the file attributes as an integer.
    if not update: attrs = entry.stat(follow_symlinks=False).st_file_attributes# Fast because we access the stats from the entry
    else:
        # Notice that this will raise a "WinError: Access denied" on some entries,
        # for example C:\System Volume Information\
        attrs = stat(entry.path, follow_symlinks=False).st_file_attributes
        # A bit slower because we re-read the stats from the file path.

    # Construct the new attributes
    newattrs = attrs
    def setattrib(attr, value):
        nonlocal newattrs
        # Use '{0:032b}'.format(number) to understand what this does.
        if value is True: newattrs = newattrs | attr
        elif value is False: 
              newattrs = newattrs & ~attr
              setattrib(A, a)
              setattrib(S, s)
              setattrib(H, h)
              setattrib(R, r)
              setattrib(I, i if i is None else not i)  # Because this attribute is True when the file is _not_ indexed

    # Optional add more attributes here. 
    # See https://docs.python.org/3/library/stat.html#stat.FILE_ATTRIBUTE_ARCHIVE

    # Write the new attributes if they changed
    if newattrs != attrs:
        if not kernel32.SetFileAttributesW(entry.path, newattrs):
            raise WinError(get_last_error())

    # Return an info tuple consisting of bools
    return ( bool(newattrs & A), 
                   bool(newattrs & S), 
                   bool(newattrs & H), 
                   bool(newattrs & R), 
                   not bool(newattrs & I) )# Because this attribute is true when the file is _not_ indexed)

    # Get the file attributes as an integer.
    if not update:
        # Fast because we access the stats from the entry
        attrs = entry.stat(follow_symlinks=False).st_file_attributes
    else:
        # A bit slower because we re-read the stats from the file path.
        # Notice that this will raise a "WinError: Access denied" on some entries,
        # for example C:\System Volume Information\
        attrs = stat(entry.path, follow_symlinks=False).st_file_attributes
    return dir_list

def get_folders(path = -1, show_hidden = False):
        
    if path == -1: path = os.getcwd()    
    dir_list = []
    kernel32 = WinDLL('kernel32', use_last_error=True)
    for entry in scandir(path):
        a,s,hidden,r,i = read_or_write_attribs(kernel32,entry)
        
        if entry.is_dir() and (show_hidden or not hidden): dir_list.append(entry.name)
    return dir_list
    
def main():

    HWND = win32gui.GetForegroundWindow()                 # Get Command Console Handle.
    win32gui.MoveWindow(HWND,100,50,650,750,False) # Size and Position Command Console.
    CLS()  # Clear Console Screen.

    line_len = 36

    # Example 1: C Drive, Exclude Hidden Folders
    print(''.join(['\n','All Folders Not Hidden:'.center(line_len),'\n ',('-'*line_len).center(line_len)]))        
    dirs = get_folders('c:\\')  # Get a list of folders on the c: drive, exclude hidden.
    for elm in dirs:print(' ',elm)  # Show the folder names.
    print('','='*line_len)

    # Examle 2: C Drive, Include Hidden Folders
    print("  All Folders Including Hidden\n "+"-"*line_len)
    dirs = get_folders('c:\\', show_hidden = True) # Get a list of folders on the c: drive, including hidden.
    for elm in dirs: print(' ',elm) # Show the fiolder names
    print('','-'*line_len)
# ==========================
if __name__ == "__main__": 
    main()
    input(' Press [Enter] to Quit: ')
    CLS()
user10637953
  • 370
  • 1
  • 10