8181True
8282"""
8383
84+ import contextlib
85+ import dataclasses
8486import errno
8587import heapq
8688import os
122124 matching_string ,
123125 AnyPath ,
124126 AnyString ,
127+ WINDOWS_PROPERTIES ,
128+ POSIX_PROPERTIES ,
129+ FSType ,
125130)
126131
127132if sys .platform .startswith ("linux" ):
@@ -179,10 +184,6 @@ class FakeFilesystem:
179184 """Provides the appearance of a real directory tree for unit testing.
180185
181186 Attributes:
182- path_separator: The path separator, corresponds to `os.path.sep`.
183- alternative_path_separator: Corresponds to `os.path.altsep`.
184- is_windows_fs: `True` in a real or faked Windows file system.
185- is_macos: `True` under MacOS, or if we are faking it.
186187 is_case_sensitive: `True` if a case-sensitive file system is assumed.
187188 root: The root :py:class:`FakeDirectory<pyfakefs.fake_file.FakeDirectory>` entry
188189 of the file system.
@@ -217,12 +218,8 @@ def __init__(
217218 >>> filesystem = FakeFilesystem(path_separator='/')
218219
219220 """
220- self .path_separator : str = path_separator
221- self .alternative_path_separator : Optional [str ] = os .path .altsep
222221 self .patcher = patcher
223222 self .create_temp_dir = create_temp_dir
224- if path_separator != os .sep :
225- self .alternative_path_separator = None
226223
227224 # is_windows_fs can be used to test the behavior of pyfakefs under
228225 # Windows fs on non-Windows systems and vice verse;
@@ -235,7 +232,19 @@ def __init__(
235232
236233 # is_case_sensitive can be used to test pyfakefs for case-sensitive
237234 # file systems on non-case-sensitive systems and vice verse
238- self .is_case_sensitive : bool = not (self .is_windows_fs or self ._is_macos )
235+ self .is_case_sensitive : bool = not (self ._is_windows_fs or self ._is_macos )
236+
237+ # by default, we use the configured filesystem
238+ self .fs_type = FSType .DEFAULT
239+ base_properties = (
240+ WINDOWS_PROPERTIES if self ._is_windows_fs else POSIX_PROPERTIES
241+ )
242+ self .fs_properties = [
243+ dataclasses .replace (base_properties ),
244+ POSIX_PROPERTIES ,
245+ WINDOWS_PROPERTIES ,
246+ ]
247+ self .path_separator = path_separator
239248
240249 self .root : FakeDirectory
241250 self ._cwd = ""
@@ -262,21 +271,30 @@ def __init__(
262271
263272 @property
264273 def is_linux (self ) -> bool :
274+ """Returns `True` in a real or faked Linux file system."""
265275 return not self .is_windows_fs and not self .is_macos
266276
267277 @property
268278 def is_windows_fs (self ) -> bool :
269- return self ._is_windows_fs
279+ """Returns `True` in a real or faked Windows file system."""
280+ return (
281+ self .fs_type == FSType .WINDOWS
282+ or self .fs_type == FSType .DEFAULT
283+ and self ._is_windows_fs
284+ )
270285
271286 @is_windows_fs .setter
272287 def is_windows_fs (self , value : bool ) -> None :
273288 if self ._is_windows_fs != value :
274289 self ._is_windows_fs = value
290+ if value :
291+ self ._is_macos = False
275292 self .reset ()
276293 FakePathModule .reset (self )
277294
278295 @property
279296 def is_macos (self ) -> bool :
297+ """Returns `True` in a real or faked macOS file system."""
280298 return self ._is_macos
281299
282300 @is_macos .setter
@@ -286,6 +304,38 @@ def is_macos(self, value: bool) -> None:
286304 self .reset ()
287305 FakePathModule .reset (self )
288306
307+ @property
308+ def path_separator (self ) -> str :
309+ """Returns the path separator, corresponds to `os.path.sep`."""
310+ return self .fs_properties [self .fs_type .value ].sep
311+
312+ @path_separator .setter
313+ def path_separator (self , value : str ) -> None :
314+ self .fs_properties [0 ].sep = value
315+ if value != os .sep :
316+ self .alternative_path_separator = None
317+
318+ @property
319+ def alternative_path_separator (self ) -> Optional [str ]:
320+ """Returns the alternative path separator, corresponds to `os.path.altsep`."""
321+ return self .fs_properties [self .fs_type .value ].altsep
322+
323+ @alternative_path_separator .setter
324+ def alternative_path_separator (self , value : Optional [str ]) -> None :
325+ self .fs_properties [0 ].altsep = value
326+
327+ @property
328+ def devnull (self ) -> str :
329+ return self .fs_properties [self .fs_type .value ].devnull
330+
331+ @property
332+ def pathsep (self ) -> str :
333+ return self .fs_properties [self .fs_type .value ].pathsep
334+
335+ @property
336+ def line_separator (self ) -> str :
337+ return self .fs_properties [self .fs_type .value ].linesep
338+
289339 @property
290340 def cwd (self ) -> str :
291341 """Return the current working directory of the fake filesystem."""
@@ -334,8 +384,11 @@ def os(self, value: OSType) -> None:
334384 self ._is_windows_fs = value == OSType .WINDOWS
335385 self ._is_macos = value == OSType .MACOS
336386 self .is_case_sensitive = value == OSType .LINUX
337- self .path_separator = "\\ " if value == OSType .WINDOWS else "/"
338- self .alternative_path_separator = "/" if value == OSType .WINDOWS else None
387+ self .fs_type = FSType .DEFAULT
388+ base_properties = (
389+ WINDOWS_PROPERTIES if self ._is_windows_fs else POSIX_PROPERTIES
390+ )
391+ self .fs_properties [0 ] = base_properties
339392 self .reset ()
340393 FakePathModule .reset (self )
341394
@@ -358,6 +411,15 @@ def reset(self, total_size: Optional[int] = None, init_pathlib: bool = True):
358411
359412 fake_pathlib .init_module (self )
360413
414+ @contextlib .contextmanager
415+ def use_fs_type (self , fs_type : FSType ):
416+ old_fs_type = self .fs_type
417+ try :
418+ self .fs_type = fs_type
419+ yield
420+ finally :
421+ self .fs_type = old_fs_type
422+
361423 def _add_root_mount_point (self , total_size ):
362424 mount_point = "C:" if self .is_windows_fs else self .path_separator
363425 self ._cwd = mount_point
@@ -403,9 +465,6 @@ def clear_cache(self) -> None:
403465 if self .patcher :
404466 self .patcher .clear_cache ()
405467
406- def line_separator (self ) -> str :
407- return "\r \n " if self .is_windows_fs else "\n "
408-
409468 def raise_os_error (
410469 self ,
411470 err_no : int ,
@@ -1144,8 +1203,8 @@ def splitroot(self, path: AnyStr):
11441203 if isinstance (p , bytes ):
11451204 sep = self .path_separator .encode ()
11461205 altsep = None
1147- if self .alternative_path_separator :
1148- altsep = self .alternative_path_separator .encode ()
1206+ if self .alternative_path_separator is not None :
1207+ altsep = self .alternative_path_separator .encode () # type: ignore[attribute-error]
11491208 colon = b":"
11501209 unc_prefix = b"\\ \\ ?\\ UNC\\ "
11511210 empty = b""
@@ -1438,7 +1497,7 @@ def exists(self, file_path: AnyPath, check_link: bool = False) -> bool:
14381497 raise TypeError
14391498 if not path :
14401499 return False
1441- if path == self .dev_null . name :
1500+ if path == self .devnull :
14421501 return not self .is_windows_fs or sys .version_info >= (3 , 8 )
14431502 try :
14441503 if self .is_filepath_ending_with_separator (path ):
@@ -1515,7 +1574,7 @@ def resolve_path(self, file_path: AnyStr, allow_fd: bool = False) -> AnyStr:
15151574 path = self .replace_windows_root (path )
15161575 if self ._is_root_path (path ):
15171576 return path
1518- if path == matching_string (path , self .dev_null . name ):
1577+ if path == matching_string (path , self .devnull ):
15191578 return path
15201579 path_components = self ._path_components (path )
15211580 resolved_components = self ._resolve_components (path_components )
@@ -1661,7 +1720,7 @@ def get_object_from_normpath(
16611720 path = make_string_path (file_path )
16621721 if path == matching_string (path , self .root .name ):
16631722 return self .root
1664- if path == matching_string (path , self .dev_null . name ):
1723+ if path == matching_string (path , self .devnull ):
16651724 return self .dev_null
16661725
16671726 path = self ._original_path (path )
0 commit comments