@@ -347,7 +347,7 @@ def __init__(self, name, st_mode=stat.S_IFREG | PERM_DEF_FILE,
347
347
contents: the contents of the filesystem object; should be a string or byte object for
348
348
regular files, and a list of other FakeFile or FakeDirectory objects
349
349
for FakeDirectory objects
350
- filesystem: if set, the fake filesystem where the file is created.
350
+ filesystem: the fake filesystem where the file is created.
351
351
New in pyfakefs 2.9.
352
352
encoding: if contents is a unicode string, the encoding used for serialization
353
353
errors: the error mode used for encoding/decoding errors
@@ -361,8 +361,12 @@ def __init__(self, name, st_mode=stat.S_IFREG | PERM_DEF_FILE,
361
361
self ._byte_contents = self ._encode_contents (contents )
362
362
self .stat_result .st_size = (
363
363
len (self ._byte_contents ) if self ._byte_contents is not None else 0 )
364
+ # to be backwards compatible regarding argument order, we raise on None
365
+ if filesystem is None :
366
+ raise ValueError ('filesystem shall not be None' )
364
367
self .filesystem = filesystem
365
368
self .epoch = 0
369
+ self .parent_dir = None
366
370
367
371
@property
368
372
def byte_contents (self ):
@@ -400,8 +404,7 @@ def SetLargeFileSize(self, st_size):
400
404
self .name )
401
405
if self .st_size :
402
406
self .SetSize (0 )
403
- if self .filesystem :
404
- self .filesystem .ChangeDiskUsage (st_size , self .name , self .st_dev )
407
+ self .filesystem .ChangeDiskUsage (st_size , self .name , self .st_dev )
405
408
self .st_size = st_size
406
409
self ._byte_contents = None
407
410
@@ -433,8 +436,7 @@ def _set_initial_contents(self, contents):
433
436
if self ._byte_contents :
434
437
self .SetSize (0 )
435
438
current_size = self .st_size or 0
436
- if self .filesystem :
437
- self .filesystem .ChangeDiskUsage (st_size - current_size , self .name , self .st_dev )
439
+ self .filesystem .ChangeDiskUsage (st_size - current_size , self .name , self .st_dev )
438
440
self ._byte_contents = contents
439
441
self .st_size = st_size
440
442
self .epoch += 1
@@ -465,6 +467,16 @@ def GetSize(self):
465
467
"""
466
468
return self .st_size
467
469
470
+ def GetPath (self ):
471
+ """Return the full path of the current object."""
472
+ names = []
473
+ obj = self
474
+ while obj :
475
+ names .insert (0 , obj .name )
476
+ obj = obj .parent_dir
477
+ sep = self .filesystem ._path_separator (self .name )
478
+ return self .filesystem .NormalizePath (sep .join (names [1 :]))
479
+
468
480
def SetSize (self , st_size ):
469
481
"""Resizes file content, padding with nulls if new size exceeds the old.
470
482
@@ -483,8 +495,7 @@ def SetSize(self, st_size):
483
495
self .name )
484
496
485
497
current_size = self .st_size or 0
486
- if self .filesystem :
487
- self .filesystem .ChangeDiskUsage (st_size - current_size , self .name , self .st_dev )
498
+ self .filesystem .ChangeDiskUsage (st_size - current_size , self .name , self .st_dev )
488
499
if self ._byte_contents :
489
500
if st_size < current_size :
490
501
self ._byte_contents = self ._byte_contents [:st_size ]
@@ -620,12 +631,25 @@ def AddEntry(self, path_object):
620
631
"""Adds a child FakeFile to this directory.
621
632
622
633
Args:
623
- path_object: FakeFile instance to add as a child of this directory.
634
+ path_object: FakeFile instance to add as a child of this directory.
635
+
636
+ Raises:
637
+ OSError: if the directory has no write permission (Posix only)
638
+ OSError: if the file or directory to be added already exists
624
639
"""
640
+ if not self .st_mode & PERM_WRITE and not self .filesystem .is_windows_fs :
641
+ raise OSError (errno .EACCES , 'Permission Denied' , self .GetPath ())
642
+
643
+ if path_object .name in self .contents :
644
+ raise OSError (errno .EEXIST ,
645
+ 'Object already exists in fake filesystem' ,
646
+ self .GetPath ())
647
+
625
648
self .contents [path_object .name ] = path_object
649
+ path_object .parent_dir = self
626
650
path_object .st_nlink += 1
627
651
path_object .st_dev = self .st_dev
628
- if self . filesystem and path_object .st_nlink == 1 :
652
+ if path_object .st_nlink == 1 :
629
653
self .filesystem .ChangeDiskUsage (path_object .GetSize (), path_object .name , self .st_dev )
630
654
631
655
def GetEntry (self , pathname_name ):
@@ -659,12 +683,12 @@ def RemoveEntry(self, pathname_name, recursive=True):
659
683
if entry .st_mode & PERM_WRITE == 0 :
660
684
raise OSError (errno .EACCES , 'Trying to remove object without write permission' ,
661
685
pathname_name )
662
- if self .filesystem and self . filesystem .is_windows_fs and self .filesystem .HasOpenFile (entry ):
686
+ if self .filesystem .is_windows_fs and self .filesystem .HasOpenFile (entry ):
663
687
raise OSError (errno .EACCES , 'Trying to remove an open file' , pathname_name )
664
688
if recursive and isinstance (entry , FakeDirectory ):
665
689
while entry .contents :
666
690
entry .RemoveEntry (list (entry .contents )[0 ])
667
- elif self . filesystem and entry .st_nlink == 1 :
691
+ elif entry .st_nlink == 1 :
668
692
self .filesystem .ChangeDiskUsage (- entry .GetSize (), pathname_name , entry .st_dev )
669
693
670
694
entry .st_nlink -= 1
@@ -1872,15 +1896,22 @@ def CreateDirectory(self, directory_path, perm_bits=PERM_DEF):
1872
1896
path_components = self .GetPathComponents (directory_path )
1873
1897
current_dir = self .root
1874
1898
1899
+ new_dirs = []
1875
1900
for component in path_components :
1876
1901
directory = self ._DirectoryContent (current_dir , component )[1 ]
1877
1902
if not directory :
1878
- new_dir = FakeDirectory (component , perm_bits , filesystem = self )
1903
+ new_dir = FakeDirectory (component , filesystem = self )
1904
+ new_dirs .append (new_dir )
1879
1905
current_dir .AddEntry (new_dir )
1880
1906
current_dir = new_dir
1881
1907
else :
1882
1908
current_dir = directory
1883
1909
1910
+ # set the permission after creating the directories
1911
+ # to allow directory creation inside a read-only directory
1912
+ for new_dir in new_dirs :
1913
+ new_dir .st_mode = stat .S_IFDIR | perm_bits
1914
+
1884
1915
self .last_ino += 1
1885
1916
current_dir .SetIno (self .last_ino )
1886
1917
return current_dir
@@ -2197,12 +2228,9 @@ def MakeDirectory(self, dir_name, mode=PERM_DEF):
2197
2228
if self .Exists (dir_name ):
2198
2229
raise OSError (errno .EEXIST , 'Fake object already exists' , dir_name )
2199
2230
head , tail = self .SplitPath (dir_name )
2200
- directory_object = self .GetObject (head )
2201
- if not directory_object .st_mode & PERM_WRITE :
2202
- raise OSError (errno .EACCES , 'Permission Denied' , dir_name )
2203
2231
2204
2232
self .AddObject (
2205
- head , FakeDirectory (tail , mode & ~ self .umask ))
2233
+ head , FakeDirectory (tail , mode & ~ self .umask , filesystem = self ))
2206
2234
2207
2235
def MakeDirectories (self , dir_name , mode = PERM_DEF , exist_ok = False ):
2208
2236
"""Create a leaf Fake directory and create any non-existent parent dirs.
@@ -2228,10 +2256,7 @@ def MakeDirectories(self, dir_name, mode=PERM_DEF, exist_ok=False):
2228
2256
current_dir = self .root
2229
2257
for component in path_components :
2230
2258
if component not in current_dir .contents :
2231
- if not current_dir .st_mode & PERM_WRITE :
2232
- raise OSError (errno .EACCES , 'Permission Denied' , dir_name )
2233
- else :
2234
- break
2259
+ break
2235
2260
else :
2236
2261
current_dir = current_dir .contents [component ]
2237
2262
try :
0 commit comments