src/eric7/SystemUtilities/FileSystemUtilities.py

branch
eric7
changeset 11173
d63911a89570
parent 11090
f5f5f5803935
equal deleted inserted replaced
11172:a5bbcf506ef0 11173:d63911a89570
6 """ 6 """
7 Module implementing file system related utility functions. 7 Module implementing file system related utility functions.
8 """ 8 """
9 9
10 import contextlib 10 import contextlib
11 import ctypes
12 import fnmatch 11 import fnmatch
13 import os 12 import os
14 import pathlib 13 import pathlib
15 import shutil 14 import shutil
16 import subprocess # secok 15 import subprocess # secok
531 return dirs 530 return dirs
532 except OSError: 531 except OSError:
533 return [] 532 return []
534 533
535 534
536 def findVolume(volumeName, findAll=False): 535 def findVolume(volumeName, findAll=False, markerFile=None):
537 """ 536 """
538 Function to find the directory belonging to a given volume name. 537 Function to find the directory belonging to a given volume name.
539 538
540 @param volumeName name of the volume to search for 539 @param volumeName name of the volume to search for
541 @type str 540 @type str
542 @param findAll flag indicating to get the directories for all volumes 541 @param findAll flag indicating to get the directories for all volumes
543 starting with the given name (defaults to False) 542 starting with the given name (defaults to False)
544 @type bool (optional) 543 @type bool (optional)
544 @param markerFile name of a file to check for its existence (defaults to None)
545 @type str (optional)
545 @return directory path or list of directory paths for the given volume 546 @return directory path or list of directory paths for the given volume
546 name 547 name
547 @rtype str or list of str 548 @rtype str or list of str
548 """ 549 """
549 volumeDirectories = []
550 volumeDirectory = None
551
552 if OSUtilities.isWindowsPlatform(): 550 if OSUtilities.isWindowsPlatform():
553 # we are on a Windows platform 551 # we are on a Windows platform
554 def getVolumeName(diskName): 552 drives = []
555 """ 553 output = subprocess.run(
556 Local function to determine the volume of a disk or device. 554 [
557 555 "wmic",
558 Each disk or external device connected to windows has an 556 "PATH",
559 attribute called "volume name". This function returns the 557 "Win32_LogicalDisk",
560 volume name for the given disk/device. 558 "get",
561 559 "DeviceID,",
562 Code from http://stackoverflow.com/a/12056414 560 "DriveType,",
563 """ 561 "FileSystem,",
564 volumeNameBuffer = ctypes.create_unicode_buffer(1024) 562 "VolumeName",
565 ctypes.windll.kernel32.GetVolumeInformationW( 563 ],
566 ctypes.c_wchar_p(diskName), 564 check=True,
567 volumeNameBuffer, 565 capture_output=True,
568 ctypes.sizeof(volumeNameBuffer), 566 text=True,
569 None, 567 encoding="utf-8",
570 None, 568 ).stdout.splitlines()
571 None, 569
572 None, 570 for line in output:
573 0, 571 words = line.split()
574 ) 572 if len(words) >= 4 and words[1] == "2" and words[2] == "FAT":
575 return volumeNameBuffer.value 573 drive = words[0]
576 574 volume = " ".join(words[3:])
577 # 575 if findAll:
578 # In certain circumstances, volumes are allocated to USB 576 if volume.startswith(volumeName):
579 # storage devices which cause a Windows popup to raise if their 577 drives.append(f"{drive}\\")
580 # volume contains no media. Wrapping the check in SetErrorMode 578 else:
581 # with SEM_FAILCRITICALERRORS (1) prevents this popup. 579 if volume == volumeName:
582 # 580 return f"{drive}\\"
583 oldMode = ctypes.windll.kernel32.SetErrorMode(1) 581
584 try: 582 return drives
585 for disk in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
586 dirpath = "{0}:\\".format(disk)
587 if os.path.exists(dirpath):
588 if findAll:
589 if getVolumeName(dirpath).startswith(volumeName):
590 volumeDirectories.append(dirpath)
591 else:
592 if getVolumeName(dirpath) == volumeName:
593 volumeDirectory = dirpath
594 break
595 finally:
596 ctypes.windll.kernel32.SetErrorMode(oldMode)
597 else: 583 else:
598 # we are on a Linux, FreeBSD or macOS platform 584 # we are on a Linux, FreeBSD or macOS platform
599 for mountCommand in ["mount", "/sbin/mount", "/usr/sbin/mount"]: 585 # FreeBSD needs a marker file because it does not use the volume name.
600 with contextlib.suppress(FileNotFoundError): 586 directories = []
601 mountOutput = subprocess.run( # secok 587 if OSUtilities.isMacPlatform():
602 mountCommand, check=True, capture_output=True, text=True 588 # macOS
603 ).stdout.splitlines() 589 mountPathStart = "/Volumes"
604 mountedVolumes = [ 590 elif OSUtilities.isLinuxPlatform():
605 x.split(" type", 1)[0].split(" (", 1)[0].split(maxsplit=2)[-1] 591 # Linux
606 for x in mountOutput 592 mountPathStart = "/media/{0}/".format(OSUtilities.getUserName())
607 ] 593 if not os.path.isdir(mountPathStart):
608 if findAll: 594 # no user mount available
609 for volume in mountedVolumes: 595 return [] if findAll else None
610 if os.path.basename(volume).startswith(volumeName): 596 elif OSUtilities.isFreeBsdPlatform():
611 volumeDirectories.append(volume) 597 # FreeBSD
612 if volumeDirectories: 598 mountPathStart = "/media/"
613 break 599 else:
614 else: 600 # unsupported platform
615 for volume in mountedVolumes: 601 return [] if findAll else None
616 if os.path.basename(volume) == volumeName: 602
617 volumeDirectory = volume 603 for d in os.listdir(mountPathStart):
618 break 604 dPath = os.path.join(mountPathStart, d)
619 if volumeDirectory: 605 if findAll:
620 break 606 if d.startswith(volumeName) or (
621 607 markerFile is not None
622 if findAll: 608 and os.path.exists(os.path.join(dPath, markerFile))
623 return volumeDirectories 609 ):
624 else: 610 directories.append(dPath)
625 return volumeDirectory 611 else:
612 if d == volumeName or (
613 markerFile is not None
614 and os.path.exists(os.path.join(dPath, markerFile))
615 ):
616 return dPath
617
618 return directories
626 619
627 620
628 def getUserMounts(): 621 def getUserMounts():
629 """ 622 """
630 Function to determine all available user mounts. 623 Function to determine all available user mounts.
632 Note: On Windows platforms all available drives are returned. 625 Note: On Windows platforms all available drives are returned.
633 626
634 @return list of user mounts or drives 627 @return list of user mounts or drives
635 @rtype list of str 628 @rtype list of str
636 """ 629 """
637 mountedPaths = []
638
639 if OSUtilities.isWindowsPlatform(): 630 if OSUtilities.isWindowsPlatform():
640 # we are on a Windows platform 631 # we are on a Windows platform
641 for disk in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": 632 return [
642 dirpath = "{0}:\\".format(disk) 633 f"{disk}:\\"
643 if os.path.exists(dirpath): 634 for disk in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
644 mountedPaths.append(dirpath) 635 if os.path.exists(f"{disk}:\\")
636 ]
645 else: 637 else:
646 # we are on a Linux, FreeBSD or macOS platform 638 # we are on a Linux, FreeBSD or macOS platform
647 if OSUtilities.isMacPlatform(): 639 if OSUtilities.isMacPlatform():
648 # macOS 640 # macOS
649 mountPathStart = "/Volumes/" 641 mountPathStart = "/Volumes/"
650 elif OSUtilities.isLinuxPlatform(): 642 elif OSUtilities.isLinuxPlatform():
651 # Linux 643 # Linux
652 mountPathStart = "/media/{0}/".format(OSUtilities.getUserName()) 644 mountPathStart = "/media/{0}/".format(OSUtilities.getUserName())
645 if not os.path.isdir(mountPathStart):
646 # no user mount available
647 return []
653 elif OSUtilities.isFreeBsdPlatform(): 648 elif OSUtilities.isFreeBsdPlatform():
654 # FreeBSD 649 # FreeBSD
655 mountPathStart = "/media/" 650 mountPathStart = "/media/"
656 else: 651 else:
657 # unsupported platform 652 # unsupported platform
658 return [] 653 return []
659 654
660 for mountCommand in ["mount", "/sbin/mount", "/usr/sbin/mount"]: 655 return [os.path.join(mountPathStart, d) for d in os.listdir(mountPathStart)]
661 with contextlib.suppress(FileNotFoundError):
662 mountOutput = subprocess.run( # secok
663 mountCommand, check=True, capture_output=True, text=True
664 ).stdout.splitlines()
665 mounts = [
666 x.split(" type", 1)[0].split(" (", 1)[0].split(maxsplit=2)[-1]
667 for x in mountOutput
668 ]
669 mountedPaths = [x for x in mounts if x.startswith(mountPathStart)]
670 break
671
672 return mountedPaths
673 656
674 657
675 def startfile(filePath): 658 def startfile(filePath):
676 """ 659 """
677 Function to open the given file path with the system default application. 660 Function to open the given file path with the system default application.

eric ide

mercurial