How get path of specific parent directory in python - python

I have path = "dir1/dir2/dir3/file.py"
I need a way to get the full path to dir2 i.e. dir1/dir2.
something like findparent(path, 'dir2').

You can split the path by the target directory, take the first element from the list, and then add the target directory to the target path.
path = "dir1/dir2/dir3/file.py"
def findparent(path: str, dir_: str) -> str:
return path.split(dir_)[0] + dir_
print(findparent(path, 'dir2'))
# dir1/dir2

If you use pathlib and the path actually exists:
path.resolve().parent
Just path.parent also works, purely syntactically, but has some caveats as mentioned in the docs.
To find one specific part of the parent hierarchy, you could iteratively call parent, or search path.parents for the name you need.

Check this out! How to get the parent dir location
My favorite is
from pathlib import Path
Path(__file__).parent.parent.parent # ad infinitum
You can even write a loop to get to dir2, something like this..
from pathlib import Path
goal_dir = "dir2"
current_dir = Path(__file__)
for i in range(10):
if current_dir == goal_dir:
break
current_dir = current_dir.parent
Note: This solution is not the best, you might want to use a while-loop instead and check if there is actually a parent. If you are at root level and there is no parent, then it doesn't exist. But, assuming it exists and you don't have a tree deeper than 10 levels, this works.

Assuming your current work directory is at the same location as your dir1, you can do:
import os
os.path.abspath("dir1/dir2")

Related

How to change to a specific folder using cmd without caring about the whole path?

I am at a specific folder lets say users/personal/project/scripts. Is there a way to go to the project folder without caring about what is before /project, that is without caring about the users/personal/ in a python script?
I am looking for a way, other than
import os
os.chdir('..')
You can use split of os.path module.
import os
import os.path
some_path = '/users/personal/project/scripts'
base_path, child = os.path.split(some_path)
os.chdir(base_path)
You can use the os module with the os.cwd() and os.path.abspath(os.path.join(path, os.pardir)) in order to achieve this. GeeksforGeeks has a great article, which has multiple answers to your question:
https://www.geeksforgeeks.org/get-parent-of-current-directory-using-python/
METHOD 1:
import os
# get current directory
path = os.getcwd()
print("Current Directory", path)
print()
# parent directory
parent = os.path.dirname(path)
print("Parent directory", parent)
METHOD 2:
import os
# get current directory
path = os.getcwd()
print("Current Directory", path)
# prints parent directory
print(os.path.abspath(os.path.join(path, os.pardir)))
METHOD 3:
import os.path
# function to get parent
def getParent(path, levels = 1):
common = path
# Using for loop for getting
# starting point required for
# os.path.relpath()
for i in range(levels + 1):
# Starting point
common = os.path.dirname(common)
# Parent directory upto specified
# level
return os.path.relpath(path, common)
path = 'D:/Pycharm projects / GeeksforGeeks / Nikhil / gfg.txt'
print(getParent(path, 2))
Keep in mind, that you can put these into functions and repeat them the number of parent directories up you need to go, for example you could turn method 1 into:
import os
def get_parent_directory(num_of_parent_directories_to_go_up=1): # Defaults the number of parent directories to go up to one, which mean go one directory up
current_path = os.getcwd()
for i in range(num_of_parent_directories_to_go_up):
current_path = os.path.dirname(current_path)
return current_path
This will output the parent directory of how many times you put into it, which defaults to one.
Example 1:
INPUT: users/personal/project/scripts
CODE: get_parent_directory()
OUTPUT: users/personal/project/
Example 2:
INPUT: users/personal/project/scripts
CODE: get_parent_directory(2)
OUTPUT: users/personal/
Ans you can see how this would go on. Furthermore, this does not error if you give a number larger than parent directories exist and will instead return the root path.
In the function or with what is returned from the example function I provided, you can use os.chdir(path_from_function_here) to change your working directory.
you can use os.getcwd() to get the current path, then find out how many level to go back to project folder, then goto project folder using os.chdir
this will works across variable level deep inside project directory.
for example, current directory either in /users/personal/project/scripts or /users/personal/project/scripts/lib also will back to /users/personal/project
os.path.sep ensure this will works in both windows/linux
curdir = os.getcwd().split(os.path.sep)
project_back_idx = len(curdir) - curdir.index('project') - 1
os.chdir(os.path.sep.join(['..'] * project_back_idx))
# 1 level back - os.chdir('..')
# 2 level back - os.chdir('..//..') and so on

Check whether or not directory is in path

I'm trying to write a Python function to accomplish the following: given a path and directory, return True only if directory appears somewhere in path.
For example consider the following example:
path = 'Documents/Pictures/random/old/test_image.jpg'
dir = 'random'
This should return True, since the directory random/ occurs somewhere along the path. On the other hand, the following example should return False:
path = 'Documents/Pictures/random_pictures/old/test_image.jpg'
dir = 'random`
This is because the directory random/ does not appear in the path, random_pictures/ does.
Is there a smarter way to do this than simply doing something like this:
def is_in_directory(path, dir):
return '/{0}/'.format(dir) in path
Perhaps with an os or os.path module?
You can use os.path.split to get the directory path then split them and check for existence :
>>> dir = 'random'
>>> dir in os.path.split(path)[0].split('/')
True
And as #LittleQ suggested as a better way you can split your base path with os.path.sep
>>> dir in os.path.split(path)[0].split(s.path.sep)
True
split using os.path.sep os.path.dirname:
from os.path import sep,dirname
def is_in_directory(p, d):
return d in dirname(p).split(sep)
os.path.dirname(path)ΒΆ
Return the directory name of pathname path. This is the first element of the pair returned by passing path to the function split().

Recursively create directories in python while skipping existing directories

I'm trying to create the following directories:
/autofs/homes/008/gwarner/test1/test2/
/autofs/homes/008/gwarner/test1/test3/
where /autofs/homes/008/gwarner/ already exists and I don't have write access for all of /autofs/homes/008/. When I try running:
dir = '/autofs/homes/008/gwarner/test/test1/test4/test5/'
for root, dirs, files in os.walk(dir, topdown = True):
print root
I get no output at all.
I assume you've tried os.makedirs(), right? Perhaps I'm misunderstanding your requirements, but you say you want to:
Recursively create directories
And the docs for os.makedirs() starts with:
Recursive directory creation function.
You could use the os.path.exists modules.
I would be careful and use both os.path.isdir and os.path.exists to check if the path is a directory before trying to write inside the directory and os.path.exists before overwriting a path.
For example:
>>> import os
>>> os.path.isdir('/home')
True
>>> os.path.isdir('/usr/bin')
True
>>> os.path.isdir('/usr/bin/python')
False
# writing a single, non-recursive path
>>> if not os.path.exists('/home/cinnamon'):
... os.mkdir('/home/cinnamon')
# writing a single, recursive path
>>> if not os.path.exists('/home/alex/is/making/a/really/long/path'):
... os.makedirs('/home/alex/is/making/a/really/long/path')
# now to script the latter
>>> paths = ['/home/alex/path/one', ...]
>>> for path in paths:
>>> if not os.path.exists(path):
>>> os.makedirs(path)
This way, you do not overwrite anything exists, you check if something is a directory before you write in the directory. By design, the system throws an OSError if the path exists, because it does not know HOW you want to handle it.
Do you want to overwrite the path (shutil.rmtree), do you want to store that the path has already been set, or do you want to just skip it? This is for you, the coder, to decide.

Can you set cwd for abspath?

I am trying to expand out a relative path into an absolute path. The relative path will sometimes in include a . which will need to be expanded out from the current working directory. I was wondering if the any standard functions in Python accept a cwd kwarg, like subprocess.popen does.
Optimal Solution
abs_path = os.path.abspath(rel_path, cwd=special_cwd)
Current Solution
# Capture current working directory
previous_cwd = os.getcwd()
# Change to the new working directory
os.chdir(new_cwd)
# Convert relative path to absolute path
abs_path = os.path.abspath(rel_path)
# Change back to previous working directory
os.chdir(previous_cwd)
The current solution seems clunky, is there a better way to accomplish this?
The current solution seems clunky, is there a better way to accomplish this?
You can write your own code as a context manager which changes the directory and then changes back:
>>> from contextlib import contextmanager
>>> #contextmanager
... def cwd(path):
... from os import getcwd, chdir
... cwd = getcwd()
... chdir(path)
... yield
... chdir(cwd)
then the actual code will look much cleaner:
>>> os.getcwd()
'/home/user'
>>> with cwd('/usr/share'):
... print(os.path.abspath('./test'))
...
/usr/share/test
>>> os.getcwd()
'/home/user'

Importing python modules from multiple directories

I have a python 2.6 Django app which has a folder structure like this:
/foo/bar/__init__.py
I have another couple directories on the filesystem full of python modules like this:
/modules/__init__.py
/modules/module1/__init__.py
/other_modules/module2/__init__.py
/other_modules/module2/file.py
Each module __init__ has a class. For example module1Class() and module2Class() respectively. In module2, file.py contains a class called myFileClass().
What I would like to do is put some code in /foo/bar/__init__.py so I can import in my Django project like this:
from foo.bar.module1 import module1Class
from foo.bar.module2 import module2Class
from foo.bar.module2.file import myFileClass
The list of directories which have modules is contained in a tuple in a Django config which looks like this:
module_list = ("/modules", "/other_modules",)
I've tried using __import__ and vars() to dynamically generate variables like this:
import os
import sys
for m in module_list:
sys.path.insert(0, m)
for d in os.listdir(m):
if os.path.isdir(d):
vars()[d] = getattr(__import__(m.split("/")[-1], fromlist=[d], d)
But that doesn't seem to work. Is there any way to do this?
Thanks!
I can see at least one problem with your code. The line...
if os.path.isdir(d):
...won't work, because os.listdir() returns relative pathnames, so you'll need to convert them to absolute pathnames, otherwise the os.path.isdir() will return False because the path doesn't exist (relative to the current working directory), rather than raising an exception (which would make more sense, IMO).
The following code works for me...
import sys
import os
# Directories to search for packages
root_path_list = ("/modules", "/other_modules",)
# Make a backup of sys.path
old_sys_path = sys.path[:]
# Add all paths to sys.path first, in case one package imports from another
for root_path in root_path_list:
sys.path.insert(0, root_path)
# Add new packages to current scope
for root_path in root_path_list:
filenames = os.listdir(root_path)
for filename in filenames:
full_path = os.path.join(root_path, filename)
if os.path.isdir(full_path):
locals()[filename] = __import__(filename)
# Restore sys.path
sys.path[:] = old_sys_path
# Clean up locals
del sys, os, root_path_list, old_sys_path, root_path, filenames, filename, full_path
Update
Thinking about it, it might be safer to check for the presence of __init__.py, rather than using os.path.isdir() in case you have subdirectories which don't contain such a file, otherwise the __import__() will fail.
So you could change the lines...
full_path = os.path.join(root_path, filename)
if os.path.isdir(full_path):
locals()[filename] = __import__(filename)
...to...
full_path = os.path.join(root_path, filename, '__init__.py')
if os.path.exists(full_path):
locals()[filename] = __import__(filename)
...but it might be unnecessary.
We wound up biting the bullet and changing how we do things. Now the list of directories to find modules is passed in the Django config and each one is added to sys.path (similar to a comment Aya mentioned and something I did before but wasn't too happy with). Then for each module inside of it, we check for an __init__.py and if it exists, attempt to treat it as a module to use inside of the app without using the foo.bar piece.
This required some adjustment on how we interact with the modules and how developers code their modules (they now need to use relative imports within their module instead of the full path imports they used before) but I think this will be an easier design for developers to use long-term.
We didn't add these to INSTALLED_APPS because we do some exception handling where if we cannot import a module due to dependency issues or bad code our software will continue running just without that module. If they were in INSTALLED_APPS we wouldn't be able to leverage that flexibility on when/how to deal with those exceptions.
Thanks for all of the help!

Categories