123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- #
- # Copyright (c) Contributors to the Open 3D Engine Project.
- # For complete copyright and license terms please see the LICENSE at the root of this distribution.
- #
- # SPDX-License-Identifier: Apache-2.0 OR MIT
- #
- #
- import os
- import fnmatch
- import pathlib
- """This module contains FolderSnapshot, a class which can create and compare 'snapshots'
- of folders (The snapshots just store the modtimes / existence of files and folders), and
- also can compare two snapshots to return a SnapshotComparison which represents the diffs
- """
- class SnapshotComparison:
- """ This class just holds the diffs calculated between two folder trees."""
- def __init__(self):
- self.deleted_files = []
- self.added_files = []
- self.changed_files = []
- self.dirs_added = []
- self.dirs_removed = []
-
- def any_changed(self):
- """Returns True if any changes were detected"""
- return self.deleted_files or self.added_files or self.changed_files or self.dirs_added or self.dirs_removed
- def enumerate_changes(self):
- """Enumerates changes, yielding each as a pair of (string, string), that is, (type of change, filename)"""
- for file_entry in self.deleted_files:
- yield ("DELETED", file_entry)
- for file_entry in self.added_files:
- yield ("ADDED", file_entry)
- for file_entry in self.changed_files:
- yield ("CHANGED", file_entry)
- for dir_entry in self.dirs_added:
- yield ("FOLDER_ADDED", dir_entry)
- for dir_entry in self.dirs_removed:
- yield ("FOLDER_DELETED", dir_entry)
- class FolderSnapshot:
- """ This class stores a snapshot of a folder state and has utility functions to compare snapshots"""
- def __init__(self):
- self.file_modtimes = {}
- self.folder_paths = []
- pass
- @staticmethod
- def _matches_ignore_pattern(in_string, ignore_patterns):
- for pattern in ignore_patterns:
- if fnmatch.fnmatch(in_string, pattern):
- return True
- # we also care if the last part is in the patterns
- # this is to cover situatiosn where the pattern has 'build' in it as opposed to *build*
- # and the name of the file is literally 'build'
- _, filepart = os.path.split(in_string)
- if fnmatch.fnmatch(filepart, pattern):
- return True
- return False
-
- @staticmethod
- def CreateSnapshot(root_folder, ignore_patterns):
- """Create a new FolderSnapshot based on a root folder and ignore patterns."""
- folder_snap = FolderSnapshot()
- ignored_folders = []
- for root, dir_names, file_names in os.walk(root_folder, followlinks=False):
- for dir_name in dir_names:
- fullpath = os.path.normpath(os.path.join(root, dir_name)).replace('\\', '/')
- if FolderSnapshot._matches_ignore_pattern(fullpath, ignore_patterns):
- ignored_folders.append(fullpath)
- continue
- if os.path.dirname(fullpath) in ignored_folders:
- # we want to emulate not 'walking' down any folders themselves that have been omitted:
- ignored_folders.append(fullpath)
- continue
- folder_snap.folder_paths.append(fullpath)
-
- for file_name in file_names:
- fullpath = os.path.normpath(os.path.join(root, file_name)).replace('\\', '/')
- if FolderSnapshot._matches_ignore_pattern(fullpath, ignore_patterns):
- continue
- if os.path.dirname(fullpath) in ignored_folders:
- # we want to emulate not 'walking' down any folders themselves that have been omitted:
- continue
- folder_snap.file_modtimes[fullpath] = os.stat(fullpath).st_mtime
-
- return folder_snap
- @staticmethod
- def CompareSnapshots(before, after):
- """Return a SnapshotComparison representing the difference between two FolderShapshot objects"""
- comparison = SnapshotComparison()
- for file_name in before.file_modtimes.keys():
- if file_name not in after.file_modtimes:
- comparison.deleted_files.append(file_name)
- else:
- if before.file_modtimes[file_name] != after.file_modtimes[file_name]:
- comparison.changed_files.append(file_name)
-
- for file_name in after.file_modtimes.keys():
- if file_name not in before.file_modtimes:
- comparison.added_files.append(file_name)
- for folder_path in before.folder_paths:
- if folder_path not in after.folder_paths:
- comparison.dirs_removed.append(folder_path)
-
- for folder_path in after.folder_paths:
- if folder_path not in before.folder_paths:
- comparison.dirs_added.append(folder_path)
- return comparison
|