Python - How to use relative paths with os.replace? - python

I am using the os.replace function to rename a folder. The folder will remain in the same parent directory.
parent_dir = '/Users/my_Username/Desktop/'
old_name = 'foo'
new_name = 'bar'
os.replace(parent_dir + old_name, parent_dir + new_name)
This code works, but feels a little redundant, especially when using long variable names and when calling this function multiple times.
According to the docs,
This function can support specifying src_dir_fd and/or dst_dir_fd to supply paths relative to directory descriptors.
However, I cannot figure out how to pass in the relative path of both folders. I thought it would be something like this:
os.rename(old_name, new_name, src_dir_fd=parent_dir)
But that doesn't work.
How do I pass in a relative path?

You could write something like this:
import os
parent_dir = '/Users/my_Username/Desktop/'
old_name = 'foo.txt'
new_name = 'bar.txt'
with os.open(parent_dir, os.O_RDONLY) as fd:
os.replace(old_name, new_name, src_dir_fd=fd)
Option src_dir_fd accepts a file descriptor (fd), intead of actual path. There is good description in the documentation.

Related

How get path of specific parent directory in 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")

name ' ' is not defined

I have a function which is stored in builtins. This is used to load python modules with relative paths from the projects base directory. The projecs base directory is stored under builtins.absolute
Function below:
def projectRelativeImport(fileName, projectRelativePath, moduleName = None):
# if moduleName not set, set it to file name with first letter capatilised
if moduleName is None:
moduleName = fileName[:1].capitalize() + fileName[1:]
# we shouldn't be passing fileName with an extension unless moduleName is set due to previous if. So in those cases we add .py
if len(fileName) >= 3 and fileName[-3:] != '.py':
fileName = fileName + '.py'
dir = os.path.join(builtins.absolute, projectRelativePath)
full = os.path.join(dir, fileName)
sys.path.append(dir)
imp.load_source(moduleName, full)
sys.path.remove(dir)
On one of my other files I use projectRelativeImport('inputSaveHandler', 'app/util', 'SaveHandler') to import SaveHandler from app/util/inputSaveHandler.py. This runs through the project RelativeImport absolutely fine. Correct strings are being used by imp, I've printed to check.
But a couple of lines after that execution I have a line
handler = SaveHandler.ConfHandler()
Which throws NameError: name 'SaveHandler' is not defined
I realise my project relative import function is a bit odd, especially since I have it globally saved using builtins (there's probably a better way but I only started using python over the last two days). But I'm just a bit confused as to why the name isn't being recognised. Do I need to return something from imp due to scope being rubbish as the project relative import function is in a different file?
I fixed this by returning from projectRelativeImport() what was passed back from imp.load_source as shown below:
sys.path.append(dir)
submodule = imp.load_source(moduleName, full)
sys.path.remove(dir)
return submodule
Then when I used the import function the returned value now goes to a variable with the same name as that I gave to the module (all very strange)
SaveHandler = projectRelativeImport('inputSaveHandler', 'app/util', 'SaveHandler')
I got to this because it worked no problem from the file projectRelativeImport was defined in but not in any others. So it was clearly a scope issue to me, so I figured I'd just try returning whatever imp gave me and it worked

How to get full path to python program including filename within the program?

I have a python program, and I want to get the path to the program from within the program, but INCLUDING the file name itself. The name of my file is PyWrapper.py. Right now I'm doing this:
import sys,os
pathname = os.path.dirname(sys.argv[0])
fullpath = os.path.abspath(pathname)
print fullpath
The output is:
/home/mrpickles/Desktop/HANSTUFF/securesdk/src/
This is the path to the directory in which my file is saved, but I would like it to output:
/home/mrpickles/Desktop/HANSTUFF/securesk/src/PyWrapper.py/
Which is the path, including the filename itself. Is this possible? Thanks.
Take a look at __file__. This gives you a filename where the code actually is.
Also, there's another option:
import __main__ as main
print(main.__file__)
This gives you a filename of a script being run (similar to what your argv does).
The difference comes into play when the code is imported by another script.
print __file__
should work.
__file__ returns the path of the executed file
Just in case you would like to find the absolute path for other files, not just the one you are currently running, in that same directory, a general approach could look like this:
import sys,os
pathname = os.path.dirname(sys.argv[0])
fullpath = os.path.abspath(pathname)
for root, dirs, files in os.walk(fullpath):
for name in files:
name = str(name)
name = os.path.realpath(os.path.join(root,name))
print name
As others are mentioning, you could take advantage of the __file__ attribute. You can use the __file__ attribute to return several different paths relevant to the currently loaded Python module (copied from another StackOverflow answer):
When a module is loaded in Python, file is set to its name. You can then use that with other functions to find the directory that the file is located in.
# The parent directory of the directory where program resides.
print os.path.join(os.path.dirname(__file__), '..')
# The canonicalised (?) directory where the program resides.
print os.path.dirname(os.path.realpath(__file__))
# The absolute path of the directory where the program resides.
print os.path.abspath(os.path.dirname(__file__))
Remember to be wary of where the module you are loading came from. It could affect the contents of the __file__ attribute (copied from Python 3 Data model documentation):
__file__ is the pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute may be missing for certain types of modules, such as C modules that are statically linked into the interpreter; for extension modules loaded dynamically from a shared library, it is the pathname of the shared library file.
try using __file__. as long as the module is ran from a file, i.e. not code in an editor, __file__ will be the abs path to the module!
print __file__
Or for command line.
def get_my_name():
if __name__ == '__main__':
return os.path.abspath(os.path.join(os.curdir, __file__))
else:
return __file__.replace('.pyc', '.py')

Get path for every file python loads when importing modules

I'm trying to make a directory containing all the modules a python script depends on. Rather than manually tracking down all those files, I would like to automatically find these files as python imports them. To do this, I've added a module finder to sys.meta_path:
import sys, imp
class ImportPrint(object):
def find_module(self, name, path=None):
toks = name.split(".")
pre, loc = ".".join(toks[:-1]), toks[-1]
try:
module_info = imp.find_module(loc, path)
except ImportError:
module_info = imp.find_module(loc)
if module_info[0]: module_info[0].close
print "A", name, module_info[1]
return None
sys.meta_path = [ImportPrint()]
import mymod1, mymod2, etc..
This almost works, but the __init__.py files are not found this way. Is there a better way to find them, or should I just hackily add them whenever the file found is a directory? Will this method miss any other files.
According to the documentation for sys.meta_path, your find_module method will be called with the path argument set to the path of a package if it is one. Why not use os.path.join(path, '__init__.py') for when path exists?

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