1313
1414
1515class FAL (ABC ):
16- """File Abstraction Layer"""
16+ """
17+ Abstract base class defining the File Abstraction Layer (FAL) interface.
18+
19+ Provides a unified API for file and directory operations used by
20+ RoboticsAcademy exercises. Concrete implementations handle different
21+ storage backends (local filesystem, remote, etc.).
22+ """
1723
1824 def __init__ (self , academy = "" , helper = "" ):
1925 self .academy = academy
2026 self .helper = helper
2127 self .user = None
2228
2329 def set_user (self , user ):
30+ """Set the current user context for file operations."""
2431 self .user = user
2532
2633 @abstractmethod
2734 def academy_path (self ) -> str :
35+ """Return the root path for all academy exercise files."""
2836 pass
2937
3038 def exercise_path (self , exercise_id ) -> str :
39+ """Return the full path for a specific exercise directory."""
3140 return self .path_join (self .academy_path (), exercise_id )
3241
3342 def helpers_path (self , exercise_id ) -> str :
43+ """Return the path to the helpers directory for an exercise."""
3444 return self .path_join (self .helper , exercise_id )
3545
3646 def exercise_helper_path (self , project_id , language ) -> str :
47+ """Return the path to the language-specific template directory for an exercise."""
3748 return self .path_join (self .helpers_path (project_id ), f"{ language } _template/" )
3849
3950 @abstractmethod
4051 def path_join (self , a : str , b : str ) -> str :
52+ """Join two path components and return the result."""
4153 pass
4254
4355 @abstractmethod
4456 def exists (self , path : str ) -> bool :
57+ """Return file size if exists, 0 if directory, -1 if not found."""
4558 pass
4659
4760 @abstractmethod
4861 def isdir (self , path : str ) -> bool :
62+ """Return True if path is an existing directory."""
4963 pass
5064
5165 @abstractmethod
5266 def isfile (self , path : str ) -> bool :
67+ """Return True if path is an existing file."""
5368 pass
5469
5570 @abstractmethod
5671 def create (self , path : str , content ):
72+ """Create a new text file at path with given content. Raises InvalidPath or ResourceAlreadyExists."""
5773 if ".." in path :
5874 raise InvalidPath (path )
5975
@@ -62,6 +78,7 @@ def create(self, path: str, content):
6278
6379 @abstractmethod
6480 def create_binary (self , path : str , content ):
81+ """Create a new binary file at path with given content. Raises InvalidPath or ResourceAlreadyExists."""
6582 if ".." in path :
6683 raise InvalidPath (path )
6784
@@ -70,18 +87,21 @@ def create_binary(self, path: str, content):
7087
7188 @abstractmethod
7289 def write (self , path : str , content ):
90+ """Overwrite an existing text file at path. Raises ResourceNotExists if missing."""
7391 size = self .exists (path )
7492 if size < 0 :
7593 raise ResourceNotExists (path )
7694
7795 @abstractmethod
7896 def write_binary (self , path : str , content ):
97+ """Overwrite an existing binary file at path. Raises ResourceNotExists if missing."""
7998 size = self .exists (path )
8099 if size < 0 :
81100 raise ResourceNotExists (path )
82101
83102 @abstractmethod
84103 def read (self , path : str ):
104+ """Read and return text content of file at path. Raises InvalidPath or ResourceNotExists."""
85105 if ".." in path :
86106 raise InvalidPath (path )
87107
@@ -90,6 +110,7 @@ def read(self, path: str):
90110
91111 @abstractmethod
92112 def read_binary (self , path : str ):
113+ """Read and return binary content of file at path. Raises InvalidPath or ResourceNotExists."""
93114 if ".." in path :
94115 raise InvalidPath (path )
95116
@@ -98,6 +119,7 @@ def read_binary(self, path: str):
98119
99120 @abstractmethod
100121 def listdirs (self , path : str ):
122+ """Return list of subdirectory names at path. Raises InvalidPath or ResourceNotExists."""
101123 if ".." in path :
102124 raise InvalidPath (path )
103125
@@ -109,6 +131,7 @@ def listdirs(self, path: str):
109131
110132 @abstractmethod
111133 def listfiles (self , path : str ):
134+ """Return list of file names at path. Raises InvalidPath or ResourceNotExists."""
112135 if ".." in path :
113136 raise InvalidPath (path )
114137
@@ -120,6 +143,7 @@ def listfiles(self, path: str):
120143
121144 @abstractmethod
122145 def list_formatted (self , path : str , base_group : str ):
146+ """Return formatted directory listing for the file explorer UI."""
123147 if ".." in path :
124148 raise InvalidPath (path )
125149
@@ -131,6 +155,7 @@ def list_formatted(self, path: str, base_group: str):
131155
132156 @abstractmethod
133157 def mkdir (self , path : str ):
158+ """Create a new directory at path. Raises InvalidPath or ResourceAlreadyExists."""
134159 if ".." in path :
135160 raise InvalidPath (path )
136161
@@ -139,6 +164,7 @@ def mkdir(self, path: str):
139164
140165 @abstractmethod
141166 def renamefile (self , old_path : str , new_path : str ):
167+ """Rename a file from old_path to new_path. Raises InvalidPath, ResourceNotExists, or ResourceAlreadyExists."""
142168 if ".." in new_path :
143169 raise InvalidPath (new_path )
144170
@@ -150,6 +176,7 @@ def renamefile(self, old_path: str, new_path: str):
150176
151177 @abstractmethod
152178 def renamedir (self , old_path : str , new_path : str ):
179+ """Rename a directory from old_path to new_path. Raises InvalidPath, ResourceNotExists, or ResourceAlreadyExists."""
153180 if ".." in new_path :
154181 raise InvalidPath (new_path )
155182
@@ -161,6 +188,7 @@ def renamedir(self, old_path: str, new_path: str):
161188
162189 @abstractmethod
163190 def removefile (self , path : str ):
191+ """Delete a file at path. Raises InvalidPath or ResourceNotExists."""
164192 if ".." in path :
165193 raise InvalidPath (path )
166194
@@ -173,6 +201,7 @@ def removefile(self, path: str):
173201
174202 @abstractmethod
175203 def removedir (self , path : str ):
204+ """Delete a directory and all its contents. Raises InvalidPath or ResourceNotExists."""
176205 if ".." in path :
177206 raise InvalidPath (path )
178207
@@ -184,6 +213,7 @@ def removedir(self, path: str):
184213
185214 @abstractmethod
186215 def dir_size (self , path ):
216+ """Return total size in bytes of all files under path. Raises InvalidPath or ResourceNotExists."""
187217 if ".." in path :
188218 raise InvalidPath (path )
189219
@@ -196,11 +226,17 @@ def dir_size(self, path):
196226 raise ResourceNotExists (path )
197227
198228 def filename (self , path : str ) -> str :
229+ """Return the filename without extension from a full path."""
199230 return os .path .splitext (os .path .basename (path ))[0 ]
200231
201232
202233class FAL_RA (FAL ):
203- """File Abstraction Layer"""
234+ """
235+ Concrete FAL implementation for local RoboticsAcademy filesystem.
236+
237+ Stores exercise files under the academy filesystem directory and
238+ uses standard Python os/shutil operations for all file access.
239+ """
204240
205241 def __init__ (self , base , helper ):
206242 FAL .__init__ (self , base , helper )
0 commit comments