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. |