@@ -26,14 +26,20 @@ class FileSystemStats:
2626class FileSystemNode (Source ): # pylint: disable=too-many-instance-attributes
2727 """Base class for filesystem nodes (files, directories, symlinks)."""
2828
29- name : str = ""
30- path_str : str = ""
31- path : Path | None = None
29+ # Required fields - no default values to prevent missing data initialization
30+ name : str
31+ path_str : str
32+ path : "Path"
33+
34+ # Optional fields with sensible defaults
3235 size : int = 0
3336 file_count : int = 0
3437 dir_count : int = 0
3538 depth : int = 0
3639 children : list [FileSystemNode ] = field (default_factory = list )
40+
41+ # Class attribute for display type name (instead of fragile string manipulation)
42+ _display_type : str = "NODE"
3743
3844 @property
3945 def tree (self ) -> str :
@@ -75,7 +81,8 @@ def content_string(self) -> str:
7581 A string representation of the node's content.
7682
7783 """
78- type_name = self .__class__ .__name__ .upper ().replace ("FILESYSTEM" , "" )
84+ # Use class attribute instead of fragile string manipulation
85+ type_name = self ._display_type
7986
8087 parts = [
8188 SEPARATOR ,
@@ -87,14 +94,44 @@ def content_string(self) -> str:
8794 return "\n " .join (parts ) + "\n \n "
8895
8996 def get_content (self ) -> str :
90- """Return file content. Override in subclasses for specific behavior."""
91- if self .path is None :
92- return "Error: No path specified"
97+ """Return file content with proper encoding detection."""
98+ from gitingest .utils .file_utils import _decodes , _get_preferred_encodings , _read_chunk
99+ from gitingest .utils .notebook import process_notebook
100+
101+ # Handle notebook files specially
102+ if self .path .suffix == ".ipynb" :
103+ try :
104+ return process_notebook (self .path )
105+ except Exception as exc :
106+ return f"Error processing notebook: { exc } "
107+
108+ # Read a chunk to check if it's binary or text
109+ chunk = _read_chunk (self .path )
110+
111+ if chunk is None :
112+ return "Error reading file"
113+
114+ if chunk == b"" :
115+ return "[Empty file]"
116+
117+ # Check if it's binary
118+ if not _decodes (chunk , "utf-8" ):
119+ return "[Binary file]"
120+
121+ # Find the first encoding that decodes the sample
122+ good_enc : str | None = next (
123+ (enc for enc in _get_preferred_encodings () if _decodes (chunk , encoding = enc )),
124+ None ,
125+ )
126+
127+ if good_enc is None :
128+ return "Error: Unable to decode file with available encodings"
93129
94130 try :
95- return self .path .read_text (encoding = "utf-8" )
96- except Exception as e :
97- return f"Error reading content of { self .name } : { e } "
131+ with self .path .open (encoding = good_enc ) as fp :
132+ return fp .read ()
133+ except (OSError , UnicodeDecodeError ) as exc :
134+ return f"Error reading file with { good_enc !r} : { exc } "
98135
99136 def get_summary_info (self ) -> str :
100137 """Return summary information. Override in subclasses."""
@@ -112,10 +149,6 @@ def get_display_name(self) -> str:
112149 """Get display name for tree view. Override in subclasses."""
113150 return self .name
114151
115- def has_children (self ) -> bool :
116- """Return whether this node has children to display."""
117- return False
118-
119152 @property
120153 def content (self ) -> str :
121154 """Return file content (simplified version for backward compatibility)."""
@@ -125,6 +158,8 @@ def content(self) -> str:
125158@dataclass
126159class FileSystemFile (FileSystemNode ):
127160 """Represents a file in the filesystem."""
161+
162+ _display_type : str = "FILE"
128163
129164 def get_sort_priority (self ) -> int :
130165 """Files have priority 0 for sorting."""
@@ -149,6 +184,7 @@ class FileSystemDirectory(FileSystemNode):
149184 """Represents a directory in the filesystem."""
150185
151186 file_count_total : int = 0
187+ _display_type : str = "DIRECTORY"
152188
153189 def get_content (self ) -> str :
154190 """Directories cannot have content."""
@@ -167,10 +203,6 @@ def get_display_name(self) -> str:
167203 """Directories get a trailing slash."""
168204 return self .name + "/"
169205
170- def has_children (self ) -> bool :
171- """Directories have children if the list is not empty."""
172- return bool (self .children )
173-
174206 def render_tree (self , prefix : str = "" , * , is_last : bool = True ) -> list [str ]:
175207 """Render the tree representation of this directory."""
176208 lines = []
@@ -195,6 +227,7 @@ class GitRepository(FileSystemDirectory):
195227 """A directory that contains a .git folder, representing a Git repository."""
196228
197229 git_info : dict = field (default_factory = dict ) # Store git metadata like branch, commit, etc.
230+ _display_type : str = "GIT_REPOSITORY"
198231
199232 def render_tree (self , prefix : str = "" , * , is_last : bool = True ) -> list [str ]:
200233 """Render the tree representation of this git repository."""
@@ -216,7 +249,7 @@ class FileSystemSymlink(FileSystemNode):
216249 """Represents a symbolic link in the filesystem."""
217250
218251 target : str = ""
219- # Add symlink-specific fields if needed
252+ _display_type : str = "SYMLINK"
220253
221254 def get_content (self ) -> str :
222255 """Symlinks content is what they point to."""
0 commit comments