[docs]classSourceCode:"""Source code iterable."""gitignore_filter:igittigitt.IgnoreParser"""Filter to use when zipping dependencies. If file/folder matches the filter, it should be ignored. """project_root:Path"""Top-level directory containing the project metadata files and source code root directory. The value can be the same as ``root_directory``. If it is not, it must be a parent of ``root_directory``. """root_directory:Path"""The root directory containing the source code."""
[docs]def__init__(self,root_directory:StrPath,*,gitignore_filter:igittigitt.IgnoreParser|None=None,include_files_in_hash:Sequence[Path]|None=None,project_root:StrPath|None=None,)->None:"""Instantiate class. Args: root_directory: The root directory containing the source code. gitignore_filter: Object that has been pre-populated with rules/patterns to determine if a file should be ignored. include_files_in_hash: Files that should be included in hash calculation even if they are filtered by gitignore (e.g. ``poetry.lock``). project_root: Optional project root if the source code is located within a larger project. This should only be used if the contents of value of ``include_files_in_hash`` contains paths that exist outside of the root directory. If this is provided, it must be a parent of the root directory. """self._include_files_in_hash=include_files_in_hashor[]self.gitignore_filter=gitignore_filterorigittigitt.IgnoreParser()self.root_directory=(root_directoryifisinstance(root_directory,Path)elsePath(root_directory))self.project_root=(# defaults to root_directory if project_root not providedproject_rootifisinstance(project_root,Path)else(Path(project_root)ifproject_rootelseself.root_directory))ifnotgitignore_filter:self.gitignore_filter.parse_rule_files(self.root_directory)self.gitignore_filter.add_rule(".git/",self.root_directory)self.gitignore_filter.add_rule(".gitignore",self.root_directory)
@cached_propertydefmd5_hash(self)->str:"""Calculate the md5 hash of the directory contents. This can be resource intensive depending on the size of the project. """sorted_files=list(self.sorted())forinclude_fileinself._include_files_in_hash:ifinclude_filenotinsorted_files:sorted_files.append(include_file)file_hash=FileHash(hashlib.md5())# noqa: S324file_hash.add_files(sorted(sorted_files),relative_to=self.project_root)returnfile_hash.hexdigest
[docs]defadd_filter_rule(self,pattern:str)->None:"""Add rule to ignore filter. Args: pattern: The gitignore pattern to add to the filter. """self.gitignore_filter.add_rule(pattern=pattern,base_path=self.root_directory)
[docs]defsorted(self,*,reverse:bool=False)->list[Path]:"""Sorted list of source code files. Args: reverse: Sort the list in reverse. Returns: Sorted list of source code files excluding those that match the ignore filter. """returnsorted(self,reverse=reverse)
[docs]def__eq__(self,other:object)->bool:"""Compare if self is equal to another object."""ifisinstance(other,SourceCode):returnself.root_directory==other.root_directoryreturnFalse
[docs]def__fspath__(self)->str|bytes:"""Return the file system path representation of the object."""returnstr(self.root_directory)
[docs]def__iter__(self)->Iterator[Path]:"""Iterate over the source code files. Yields: Files that do not match the ignore filter. Order in arbitrary. """forchildinself.root_directory.rglob("*"):ifchild.is_dir():continue# ignore directoriesifself.gitignore_filter.match(child):continue# ignore files that match the filteryieldchild
[docs]def__str__(self)->str:"""Return the string representation of the object."""returnstr(self.root_directory)
[docs]def__truediv__(self,other:StrPath)->Path:"""Create a new path object from source code's root directory."""returnself.root_directory/other