I'm trying to create a directory using os.mkdir() or os.makedirs() as follows:
if not os.path.exists(directory):
os.mkdir(directory)
This code runs fine, but I could see no directory created in the 'directory' path.
If I only write:
os.mkdir(directory)
it gives error message that directory already exists.
Try the following for a little more robust handling -- similar to how mkdir -p works on Linux:
def _mkdir(_dir):
if os.path.isdir(_dir): pass
elif os.path.isfile(_dir):
raise OSError("%s exists as a regular file." % _dir)
else:
parent, directory = os.path.split(_dir)
if parent and not os.path.isdir(parent): _mkdir(parent)
if directory: os.mkdir(_dir)
If you try making a dir over a file, complain, otherwise, just make sure the dir exists.
Related
I am using an observer to watch a directory structure (project directory). My application uses a wx.TreeCtrl to display a tree of the watched directory. So far everything works on both Windows and Linux: newly created files in the watched directory are added to the wx.TreeCtrl, deleted files are removed, renamed files are renamed, etc.
The user has the option to select another directory to watch (read: project directory). This then calls the function DrawDocumentTree, which will populate the wx.TreeCtrl and update the observer. Following this, the observer happily watches the newly selected directory and performs its tasks accordingly. Again, this works on Windows and Linux.
My code gets stuck (program not responding) under Windows when the entire watched directory is deleted. Following the deletion, the previous project directory is selected and the DrawDocumentTree function is called. With the code as I have now, this only works on Linux. Here is what the DrawDocumentTree function looks like.
def DrawDocumentTree (self, fullpath):
logger = init_logger('Common')
logger.debug('Drawing document tree')
self.document_tree.SetFocus()
self.document_tree.DeleteAllItems()
self.document_tree.Refresh()
root = fullpath
self.ids = {root : self.document_tree.AddRoot(root, self.folderidx)}
for dirpath, dirnames, filenames in os.walk(root):
for dirpath, dirnames, filenames in os.walk(root):
for dirname in dirnames:
fullpath = os.path.join(dirpath, dirname)
file_type = 'directory'
self.ids[fullpath] = self.document_tree.AppendItem(self.ids[dirpath], dirname, get_icon(self, fullpath, file_type))
self.document_tree.SetItemData(self.ids[fullpath], { 'type' : file_type})
for filename in filenames:
fullpath = os.path.join(dirpath, filename)
file_type = get_filetype(fullpath)
self.ids[fullpath] = self.document_tree.AppendItem(self.ids[dirpath], filename, get_icon(self, fullpath, file_type))
self.document_tree.SetItemData(self.ids[fullpath], { 'type' : file_type})
try:
# ~ logger.debug('Stop observer')
# ~ self.observer.stop()
# ~ logger.debug('Join observer')
# ~ self.observer.join()
# ~ logger.debug('Start observer')
# ~ self.observer.start()
# ~ logger.debug('Unschedule all watchers')
# ~ self.observer.unschedule_all()
logger.info('Set observer to watch project directory: ' + root)
self.observer.schedule(self.event_handler, root, recursive=True)
except:
logger.error('Unable to watch the project directory.')
logger.debug('Done drawing document tree')
You can see towards the bottom of the function that I have tried various things, like stopping, joining and then starting the observer. None of this works. If anything, it makes it stop working under Linux.
When running on Windows, the last entry in the log is Set observer to watch project directory: xyz (where xyz is the path to the directory to be watched). The exception message does not make it to the log.
Another thing I have tried is: the moment the user deletes the project directory, I immediately (before the current project directory is actually deleted) tell the observer to watch a temporary directory. This in an attempt to prevent the observer from possibly crashing due to it trying to watch a directory that no longer exists. Once everything has been deleted, the DrawDocumentTree function is called and will attempt to point the observer to watch the then current project directory.
I am running this code on Python 3.9.0 and watchdog version 2.1.9 (in both Windows and Linux).
Any ideas on what I can do to ensure this code works on Windows like it does on Linux?
Thank you.
Marc.
I am running a script to uninstall a program and to finish the process, I am checking if the applicable directories get deleted as expected. I have the following:
D_PATHS = (
r'C:\ProgramFiles\D1\FolderA',
r'C:\ProgramFiles\D1\FolderB',
r'C:\ProgramFiles\D1\FolderC',
)
for path in D_PATHS:
self.info('Deleting %s', path)
if os.path.exists:
warnings.warn(f'The following directory still exists: {path}')
else:
print(f'Removed all required directories')
When I run the script, it always throws the warning that the directories still exist, even if they don't. What am I doing wrong? Please excuse my very limited knowledge of coding. I know there's probably an easy answer that I am not understanding.
os.path.exists is a function, you need to give it an argument:
...
if os.path.exists(path):
print("Still exists")
...
If you want to print a warning for each directory that exists, then you should set a boolean flag to decide at the end whether to print the "removed all required directories" message.
dirs_exist = False
for path in D_PATHS:
self.info('Deleting %s', path)
if os.path.exists(path):
warnings.warn(f'The following directory still exists: {path}')
dirs_exist = True
if not dirs_exist:
print(f'Removed all required directories')
The alternative, if you only want to warn about the first directory that still exists, is to break from the for loop, in which case you can use an else clause on your for loop for printing the message if no break was encountered:
for path in D_PATHS:
self.info('Deleting %s', path)
if os.path.exists(path):
warnings.warn(f'The following directory still exists: {path}')
break
else:
print(f'Removed all required directories')
Note that despite the messages saying "Deleting", the code that you have shown here is not actually deleting anything. Presumably you have done so earlier in your code.
It may be possible that you dont have the permission so you can not delete them.
1.Generate a python.exe with pyinstaller .
2.Right Click and click the option "run as administrator"
In a python test function
def test_something(tmpdir):
with tmpdir.as_cwd() as p:
print('here', p)
print(os.getcwd())
I was expecting p and the os.getcwd() would give the same result. But in reality, p points to the directory of the test file whereas os.getcwd() points to the expected temporary file.
Is this expected behavior?
Take a look at the docs of py.path.as_cwd:
return context manager which changes to current dir during the managed "with" context. On __enter__ it returns the old dir.
The behaviour you are observing is thus correct:
def test_something(tmpdir):
print('current directory where you are before changing it:', os.getcwd())
# the current directory will be changed now
with tmpdir.as_cwd() as old_dir:
print('old directory where you were before:', old_dir)
print('current directory where you are now:', os.getcwd())
print('you now returned to the old current dir', os.getcwd())
Just remember that p in your example is not the "new" current dir you are changing to, it's the "old" one you changed from.
From the documentation:
You can use the tmpdir fixture which will provide a temporary
directory unique to the test invocation, created in the base temporary
directory.
Whereas, getcwd stands for Get Current Working directory, and returns the directory from which your python process has been started.
If I wanted to specify a path to save files to and make directories that don’t exist in that path, is it possible to do this using the pathlib library in one line of code?
Yes, that is Path.mkdir:
pathlib.Path('/tmp/sub1/sub2').mkdir(parents=True, exist_ok=True)
From the docs:
If parents is true, any missing parents of this path are created as
needed; they are created with the default permissions without taking
mode into account (mimicking the POSIX mkdir -p command).
If parents is false (the default), a missing parent raises
FileNotFoundError.
If exist_ok is false (the default), FileExistsError is raised if the
target directory already exists.
If exist_ok is true, FileExistsError exceptions will be ignored (same
behavior as the POSIX mkdir -p command), but only if the last path
component is not an existing non-directory file.
This gives additional control for the case that the path is already there:
path = Path.cwd() / 'new' / 'hi' / 'there'
try:
path.mkdir(parents=True, exist_ok=False)
except FileExistsError:
print("Folder is already there")
else:
print("Folder was created")
Adding to Wim's answer. If your path has a file on the end that you do not want made as a directory.
ie.
'/existing_dir/not_existing_dir/another_dir/a_file'
Then you use PurePath.parents. But the nice thing is that because Paths inherit the attributes of Pure Paths, then you can simply do
filepath = '/existing_dir/not_existing_dir/another_dir/a_file'
pathlib.Path(filepath).parents[0].mkdir(parents=True, exist_ok=True)
I chrooted directory using following commands:
os.chroot("/mydir")
How to return to directory to previous - before chrooting?
Maybe it is possible to unchroot directory?
SOLUTION:
Thanks to Phihag. I found a solution. Simple example:
import os
os.mkdir('/tmp/new_dir')
dir1 = os.open('.', os.O_RDONLY)
dir2 = os.open('/tmp/new_dir', os.O_RDONLY)
os.getcwd() # we are in 'tmp'
os.chroot('/tmp/new_dir') # chrooting 'new_dir' directory
os.fchdir(dir2)
os.getcwd() # we are in chrooted directory, but path is '/'. It's OK.
os.fchdir(dir1)
os.getcwd() # we came back to not chrooted 'tmp' directory
os.close(dir1)
os.close(dir2)
More info
If you haven't changed your current working directory, you can simply call
os.chroot('../..') # Add '../' as needed
Of course, this requires the CAP_SYS_CHROOT capability (usually only given to root).
If you have changed your working directory, you can still escape, but it's harder:
os.mkdir('tmp')
os.chroot('tmp')
os.chdir('../../') # Add '../' as needed
os.chroot('.')
If chroot changes the current working directory, you can get around that by opening the directory, and using fchdir to go back.
Of course, if you intend to go out of a chroot in the course of a normal program (i.e. not a demonstration or security exploit), you should rethink your program. First of all, do you really need to escape the chroot? Why can't you just copy the required info into it beforehand?
Also, consider using a second process that stays outside of the chroot and answers to the requests of the chrooted one.