8181True
8282"""
8383
84+ import contextlib
85+ import dataclasses
8486import errno
8587import heapq
8688import os
101103)
102104from typing import (
103105 List ,
104- Optional ,
105106 Callable ,
106107 Union ,
107108 Any ,
111112 AnyStr ,
112113 overload ,
113114 NoReturn ,
115+ Optional ,
114116)
115117
116118from pyfakefs import fake_file , fake_path , fake_io , fake_os , helpers , fake_open
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,30 +271,71 @@ 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 self .fs_type == FSType .WINDOWS or (
281+ self .fs_type == FSType .DEFAULT and self ._is_windows_fs
282+ )
270283
271284 @is_windows_fs .setter
272285 def is_windows_fs (self , value : bool ) -> None :
273286 if self ._is_windows_fs != value :
274287 self ._is_windows_fs = value
288+ if value :
289+ self ._is_macos = False
275290 self .reset ()
276291 FakePathModule .reset (self )
277292
278293 @property
279294 def is_macos (self ) -> bool :
295+ """Returns `True` in a real or faked macOS file system."""
280296 return self ._is_macos
281297
282298 @is_macos .setter
283299 def is_macos (self , value : bool ) -> None :
284300 if self ._is_macos != value :
285301 self ._is_macos = value
302+ if value :
303+ self ._is_windows_fs = False
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,9 @@ 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+ alternative_path_separator = self .alternative_path_separator
1207+ if alternative_path_separator is not None :
1208+ altsep = alternative_path_separator .encode ()
11491209 colon = b":"
11501210 unc_prefix = b"\\ \\ ?\\ UNC\\ "
11511211 empty = b""
@@ -1292,7 +1352,11 @@ def _path_components(self, path: AnyStr) -> List[AnyStr]:
12921352 if not path or path == self .get_path_separator (path ):
12931353 return []
12941354 drive , path = self .splitdrive (path )
1295- path_components = path .split (self .get_path_separator (path ))
1355+ sep = self .get_path_separator (path )
1356+ # handle special case of Windows emulated under POSIX
1357+ if self .is_windows_fs and sys .platform != "win32" :
1358+ path = path .replace (matching_string (sep , "\\ " ), sep )
1359+ path_components = path .split (sep )
12961360 assert drive or path_components
12971361 if not path_components [0 ]:
12981362 if len (path_components ) > 1 and not path_components [1 ]:
@@ -1438,7 +1502,7 @@ def exists(self, file_path: AnyPath, check_link: bool = False) -> bool:
14381502 raise TypeError
14391503 if not path :
14401504 return False
1441- if path == self .dev_null . name :
1505+ if path == self .devnull :
14421506 return not self .is_windows_fs or sys .version_info >= (3 , 8 )
14431507 try :
14441508 if self .is_filepath_ending_with_separator (path ):
@@ -1515,7 +1579,7 @@ def resolve_path(self, file_path: AnyStr, allow_fd: bool = False) -> AnyStr:
15151579 path = self .replace_windows_root (path )
15161580 if self ._is_root_path (path ):
15171581 return path
1518- if path == matching_string (path , self .dev_null . name ):
1582+ if path == matching_string (path , self .devnull ):
15191583 return path
15201584 path_components = self ._path_components (path )
15211585 resolved_components = self ._resolve_components (path_components )
@@ -1661,7 +1725,7 @@ def get_object_from_normpath(
16611725 path = make_string_path (file_path )
16621726 if path == matching_string (path , self .root .name ):
16631727 return self .root
1664- if path == matching_string (path , self .dev_null . name ):
1728+ if path == matching_string (path , self .devnull ):
16651729 return self .dev_null
16661730
16671731 path = self ._original_path (path )
0 commit comments