1+ # src/treemapper/ignore.py
12import logging
23import os
34from pathlib import Path
4- from typing import List , Dict , Tuple
5+ # ---> ИЗМЕНЕНИЕ: Добавляем импорты из typing <---
6+ from typing import List , Dict , Tuple , Optional
57
68import pathspec
79
@@ -10,11 +12,16 @@ def read_ignore_file(file_path: Path) -> List[str]:
1012 """Read the ignore patterns from the specified ignore file."""
1113 ignore_patterns = []
1214 if file_path .is_file ():
13- with file_path .open ('r' ) as f :
14- ignore_patterns = [line .strip () for line in f
15- if line .strip () and not line .startswith ('#' )]
16- logging .info (f"Using ignore patterns from { file_path } " )
17- logging .debug (f"Read ignore patterns from { file_path } : { ignore_patterns } " )
15+ try :
16+ with file_path .open ('r' , encoding = 'utf-8' ) as f :
17+ ignore_patterns = [line .strip () for line in f
18+ if line .strip () and not line .startswith ('#' )]
19+ logging .info (f"Using ignore patterns from { file_path } " )
20+ logging .debug (f"Read ignore patterns from { file_path } : { ignore_patterns } " )
21+ except IOError as e :
22+ logging .warning (f"Could not read ignore file { file_path } : { e } " )
23+ except UnicodeDecodeError as e :
24+ logging .warning (f"Could not decode ignore file { file_path } as UTF-8: { e } " )
1825 return ignore_patterns
1926
2027
@@ -24,89 +31,110 @@ def load_pathspec(patterns: List[str], syntax='gitwildmatch') -> pathspec.PathSp
2431 logging .debug (f"Loaded pathspec with patterns: { patterns } " )
2532 return spec
2633
27-
34+ # ---> ИЗМЕНЕНИЕ: Заменяем | None на Optional[...] <---
2835def get_ignore_specs (
2936 root_dir : Path ,
30- custom_ignore_file : Path = None ,
37+ custom_ignore_file : Optional [ Path ] = None ,
3138 no_default_ignores : bool = False ,
32- output_file : Path = None
39+ output_file : Optional [ Path ] = None
3340) -> Tuple [pathspec .PathSpec , Dict [Path , pathspec .PathSpec ]]:
3441 """Get combined ignore specs and git ignore specs."""
3542 default_patterns = get_default_patterns (root_dir , no_default_ignores , output_file )
3643 custom_patterns = get_custom_patterns (root_dir , custom_ignore_file )
37- combined_patterns = custom_patterns if no_default_ignores else default_patterns + custom_patterns
44+
45+ if no_default_ignores :
46+ combined_patterns = custom_patterns
47+ if output_file :
48+ try :
49+ resolved_output = output_file .resolve ()
50+ resolved_root = root_dir .resolve ()
51+ if resolved_output .is_relative_to (resolved_root ):
52+ relative_output_str = resolved_output .relative_to (resolved_root ).as_posix ()
53+ output_pattern = f"/{ relative_output_str } "
54+ if output_pattern not in combined_patterns :
55+ combined_patterns .append (output_pattern )
56+ logging .debug (f"Adding output file to ignores (no_default_ignores=True): { output_pattern } " )
57+ except ValueError : pass
58+ except Exception as e :
59+ logging .warning (f"Could not determine relative path for output file { output_file } : { e } " )
60+ else :
61+ combined_patterns = default_patterns + custom_patterns
62+
63+ logging .debug (f"Ignore specs params: no_default_ignores={ no_default_ignores } " )
64+ logging .debug (f"Default patterns (used unless no_default_ignores): { default_patterns } " )
65+ logging .debug (f"Custom patterns (-i): { custom_patterns } " )
66+ logging .debug (f"Combined patterns for spec: { combined_patterns } " )
67+
3868 combined_spec = load_pathspec (combined_patterns )
3969 gitignore_specs = get_gitignore_specs (root_dir , no_default_ignores )
4070
4171 return combined_spec , gitignore_specs
4272
43- def get_default_patterns (root_dir : Path , no_default_ignores : bool , output_file : Path ) -> List [str ]:
44- """Retrieve default ignore patterns."""
73+ # ---> ИЗМЕНЕНИЕ: Заменяем | None на Optional[...] <---
74+ def get_default_patterns (root_dir : Path , no_default_ignores : bool , output_file : Optional [Path ]) -> List [str ]:
75+ """Retrieve default ignore patterns ONLY IF no_default_ignores is FALSE."""
4576 if no_default_ignores :
4677 return []
4778
4879 patterns = []
49- # Add .treemapperignore patterns
5080 treemapper_ignore_file = root_dir / ".treemapperignore"
5181 patterns .extend (read_ignore_file (treemapper_ignore_file ))
5282
53- # Add default git patterns
54- patterns .extend ([".git/" , ".git/**" ])
55-
56- # Add the output file to ignore patterns
5783 if output_file :
5884 try :
59- relative_output = output_file .resolve ().relative_to (root_dir .resolve ())
60- patterns .append (str (relative_output ))
61- if str (relative_output .parent ) != "." :
62- patterns .append (str (relative_output .parent ) + "/" )
63- except ValueError :
64- pass # Output file is outside root_dir; no need to add to ignores
85+ resolved_output = output_file .resolve ()
86+ resolved_root = root_dir .resolve ()
87+ try :
88+ relative_output = resolved_output .relative_to (resolved_root )
89+ output_pattern = f"/{ relative_output .as_posix ()} "
90+ patterns .append (output_pattern )
91+ logging .debug (f"Adding output file to default ignores: { output_pattern } " )
92+ except ValueError :
93+ logging .debug (f"Output file { output_file } is outside root directory { root_dir } , not adding to default ignores." )
94+ except Exception as e :
95+ logging .warning (f"Could not determine relative path for output file { output_file } : { e } " )
6596
6697 return patterns
6798
68- def get_custom_patterns (root_dir : Path , custom_ignore_file : Path ) -> List [str ]:
69- """Retrieve custom ignore patterns."""
99+ # ---> ИЗМЕНЕНИЕ: Заменяем | None на Optional[...] <---
100+ def get_custom_patterns (root_dir : Path , custom_ignore_file : Optional [Path ]) -> List [str ]:
101+ """Retrieve custom ignore patterns from the file specified with -i."""
70102 if not custom_ignore_file :
71103 return []
72104
73- custom_ignore_file = custom_ignore_file if custom_ignore_file .is_absolute () else root_dir / custom_ignore_file
105+ if not custom_ignore_file .is_absolute ():
106+ custom_ignore_file = Path .cwd () / custom_ignore_file
107+
74108 if custom_ignore_file .is_file ():
75109 return read_ignore_file (custom_ignore_file )
110+ else :
111+ logging .warning (f"Custom ignore file '{ custom_ignore_file } ' not found." )
112+ return []
76113
77- logging .warning (f"Custom ignore file '{ custom_ignore_file } ' not found." )
78- return []
79114
80115def get_gitignore_specs (root_dir : Path , no_default_ignores : bool ) -> Dict [Path , pathspec .PathSpec ]:
81- """Retrieve gitignore specs for all .gitignore files in the directory ."""
116+ """Retrieve gitignore specs for all .gitignore files found within root_dir ."""
82117 if no_default_ignores :
83118 return {}
84119
85120 gitignore_specs = {}
86- for dirpath , _ , filenames in os .walk (root_dir ):
87- if ".gitignore" in filenames :
88- gitignore_path = Path (dirpath ) / ".gitignore"
89- patterns = read_ignore_file (gitignore_path )
90- gitignore_specs [Path (dirpath )] = load_pathspec (patterns )
121+ try :
122+ for dirpath_str , dirnames , filenames in os .walk (root_dir , topdown = True ):
123+ if '.git' in dirnames :
124+ dirnames .remove ('.git' )
125+ if ".gitignore" in filenames :
126+ gitignore_path = Path (dirpath_str ) / ".gitignore"
127+ patterns = read_ignore_file (gitignore_path )
128+ if patterns :
129+ gitignore_specs [Path (dirpath_str )] = load_pathspec (patterns )
130+ except OSError as e :
131+ logging .warning (f"Error walking directory { root_dir } to find .gitignore files: { e } " )
91132
92133 return gitignore_specs
93134
94135
95-
96- def should_ignore (file_path : str , combined_spec : pathspec .PathSpec ) -> bool :
136+ def should_ignore (relative_path_str : str , combined_spec : pathspec .PathSpec ) -> bool :
97137 """Check if a file or directory should be ignored based on combined pathspec."""
98- paths_to_check = [file_path ]
99-
100- # Add path variations for checking
101- if file_path .endswith ('/' ):
102- paths_to_check .append (file_path )
103-
104- # Add parent directories with trailing slash
105- for part in Path (file_path ).parents :
106- if part != Path ('.' ):
107- paths_to_check .append (part .as_posix () + '/' )
108-
109- result = any (combined_spec .match_file (path ) for path in paths_to_check )
110- logging .debug (
111- f"Should ignore '{ file_path } ': { result } (checking paths: { paths_to_check } )" )
112- return result
138+ is_ignored = combined_spec .match_file (relative_path_str )
139+ logging .debug (f"Checking combined spec ignore for '{ relative_path_str } ': { is_ignored } " )
140+ return is_ignored
0 commit comments