Is there a way to use os.chdir() to go to relative user folder?
I'm making a bash and the only issue I found is the cd ~, arg[0] is undefined since I'm using this cd functions:
def cd(args):
os.chdir(args[0])
return current_status
Which I want to change to
def cd(args):
if args[0] == '~':
os.chdir('/home/')
# Here I left it to /home/ since I don't know how
# to get the user's folder name
else:
os.chdir(args[0])
return current_status
No, os.chdir won't do that, since it is just a thin wrapper around a system call. Consider that ~ is actually a legal name for a directory.
You can, however, use os.expanduser to expand ~ in a path.
def cd(path):
os.chdir(os.path.expanduser(path))
Note that this will also expand ~user to the home directory for user.
Related
I'm simply trying to handle an uploaded file and write it in a working dir which name is the system timestamp. The problem is that I want to create that directory with full permission (777) but I can't! Using the following piece of code the created directory with 755 permissions.
def handle_uploaded_file(upfile, cTimeStamp):
target_dir = "path_to_my_working_dir/tmp_files/%s" % (cTimeStamp)
os.makedirs(target_dir, mode=0777)
According to the official python documentation the mode argument of the os.makedirs function may be ignored on some systems, and on systems where it is not ignored the current umask value is masked out.
Either way, you can force the mode to 0o777 (0777 threw up a syntax error) using the os.chmod function.
You are running into problems because os.makedir() honors the umask of current process (see the docs, here). If you want to ignore the umask, you'll have to do something like the following:
import os
try:
original_umask = os.umask(0)
os.makedirs('full/path/to/new/directory', desired_permission)
finally:
os.umask(original_umask)
In your case, you'll want to desired_permission to be 0777 (octal, not string). Most other users would probably want 0755 or 0770.
For Unix systems (when the mode is not ignored) the provided mode is first masked with umask of current user. You could also fix the umask of the user that runs this code. Then you will not have to call os.chmod() method.
Please note, that if you don't fix umask and create more than one directory with os.makedirs method, you will have to identify created folders and apply os.chmod on them.
For me I created the following function:
def supermakedirs(path, mode):
if not path or os.path.exists(path):
return []
(head, tail) = os.path.split(path)
res = supermakedirs(head, mode)
os.mkdir(path)
os.chmod(path, mode)
res += [path]
return res
The other anwsers did not worked for me (with python 2.7).
I had to add os.umask(0) before, to remove the mask for the current user. And I had to change the mode from 0777 to 0o777:
def handle_uploaded_file(upfile, cTimeStamp):
target_dir = "path_to_my_working_dir/tmp_files/%s" % (cTimeStamp)
os.umask(0)
os.makedirs(path, mode=0o777)
Lets assume following directories tree:
/
home/
user/
X/
scripy.py
Y/
a.c
b.c
I have a python script, with many commands using relative path, doing sth like this:
def f1(relative_path_to_a, relative_path_to_c):
abs_path = PATH_TO_PLACE_FROM_SCRIPT_WAS_CALLED
c_abs_path = abs_path + relative_path_to_c
file_a = open(relative_path_to_a)
file_a.write(c_abs_path)
close()
As it found out, I need to use it on another machine, with different directories tree. There should also be a possibility to call script from other location, than the one where it is placed.
Changing all paths to absolute will take some time, but is not impossible. On the other hand, I come up with an idea to change path on the beginning of the script, to /home/user/X (I did it by adding one more python file to X directory and calling inside of it abs_path = os.path.abspath("."), then in script.py I do os.chdir(second_script.abs_path).
What are disadvantages of second solution? Are there any situations, where it can crash?
To be precise, in the second solution, i'm doing sth line this:
/
home/
user/
X/
scripy.py
second_scripy.py
Y/
a.c
b.c
user#user:~$ pwd
/home/
user#user:~$ python user/X/script.py
To make programs reusable, they are expected to work relative to the current working directory, not change the cwd and not relative to the location of the program. So you should use something like:
# relative_path_to_a = 'Y/a.c'
# relative_path_to_c = 'Y/b.c'
def f1(relative_path_to_a, relative_path_to_c):
c_abs_path = os.path.abspath(relative_path_to_c)
a_abs_path = os.path.abspath(relative_path_to_a)
with open(a_abs_path) as file_a:
file_a.write(c_abs_path)
this means for user X you run
(cd /user/X && python script.py) # modifies /user/X/Y/a.c
and for user Z you can reuse the script using
(cd /user/Z && python ../X/script.py) # modifies /user/Z/Y/a.c
I've created a command line program that I would like to distribute to some folks at work. Getting them all to install the python interpreter is simply unrealistic. Hence, I've created a single .exe file using PyInstaller. I am coming to realize however, that most people don't even know how to navigate to the directory where the .exe is, in order to invoke it. (And as of now, I haven't yet figured out how to get the program to run when clicked.) Is there a way to make the program add it's self to the users sys PATH when it is run the first time or would this require an installer? Thanks!
The common trap would be to read the PATH env. variable by using os.environ('PATH'). That would be a big mistake because this variable contains user & system paths mixed together. That's a special case for the PATH variable.
What you need to do is to fetch PATH env variable from the registry (user part), update it if needed, and write it back.
You can achieve that using winreg module, modifying the user PATH environment variable (or create if doesn't exist for this particular user)
read user PATH variable
if exists, tokenize the paths (else, path list defaults to empty)
compute the path of the current module (using os.path.dirname(__file__))
check if already in the path, if so, exit (I print the path list in that case so you can test)
create/update PATH user env. variable with the updated path list if necessary
Code:
import winreg,os
script_directory = os.path.dirname(__file__)
paths = []
key_type = winreg.REG_EXPAND_SZ # default if PATH doesn't exist
try:
keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_QUERY_VALUE)
path_old, key_type = winreg.QueryValueEx(keyQ, "PATH")
winreg.CloseKey(keyQ)
paths = path_old.split(os.pathsep)
except WindowsError:
pass
if script_directory in paths:
# already set, do nothing
print(paths)
else:
# add the new path
paths.append(script_directory)
# change registry
keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_WRITE)
winreg.SetValueEx(keyQ, 'PATH', 0, key_type, os.pathsep.join(paths))
winreg.CloseKey(keyQ)
Note that the user will have to logoff/logon for changes to take effect. Another solution would be to call setx on the PATH variable. System call, ugly, but effective immediately.
# change registry with immediate effect
import subprocess
subprocess.call(["setx","PATH",os.pathsep.join(paths)])
Or, courtesy to eryksun, some python code to propagate the registry changes to new processes. No need to logoff, no need for ugly setx, just call broadcast_change('Environment') using the code below:
import ctypes
user32 = ctypes.WinDLL('user32', use_last_error=True)
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
ERROR_TIMEOUT = 0x05B4
def broadcast_change(lparam):
result = user32.SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE,
0, ctypes.c_wchar_p(lparam), SMTO_ABORTIFHUNG, 1000, None)
if not result:
err = ctypes.get_last_error()
if err != ERROR_TIMEOUT:
raise ctypes.WinError(err)
(seems that I have to refactor some code of my own with that last bit :))
env. variable read code took from here: How to return only user Path in environment variables without access to Registry?
In this path, the directory 'foo' is mounted on an external file system. The directory 'bar' is a subdirectory of 'foo'.
'/Volumes/foo/bar'
Using os.path.ismount('/Volumes/foo'), I can determine correctly that 'foo' is indeed an external mount. However, using os.path.ismount('/Volumes/foo/bar') on 'bar' determines correctly that it is NOT mounted externally.
So, my question is, how can I correctly determine that 'bar' is a subdirectory of an externally mounted file system? I need to be able to determine the same about many directories of varying depth. Any clues would be great!
From the documentation:
Return True if pathname path is a mount point
emphasis mine. A subdirectory of a mount pointed directory is on a mount drive, but not a mount "point".
how can I correctly determine that 'bar' is a subdirectory of an externally mounted file system?
In this case, I would iterate through the parent hierarchy until I reach the root, or I hit a mount-point. Whichever comes first.
Assuming a Unix type filesystem:
def is_on_mount(path):
while True:
if path == os.path.dirname(path):
# we've hit the root dir
return False
elif os.path.ismount(path):
return True
path = os.path.dirname(path)
path = '/mount/one/two/three'
is_on_mount(path)
import os
import subprocess
def is_on_mounted_volume(path):
try:
df = subprocess.check_output(['df', path]).split('\n')
mountpoint = df[1].split()[-1][0]
return os.path.ismount(mountpoint)
except subprocess.CalledProcessError:
pass
In fabric, the cd context manager works like
with cd("dir"):
run("command")
and the command will be run after changing to the dir directory. This works fine, but the issue is that it uses a global state. For example, suppose I have a helper function, which needs to use cd:
def helper():
with cd("foo"):
run("some command")
If I call helper from another function like
def main_function():
helper()
...
it works fine. But if I do something like
def main_function():
with cd("bar"):
helper()
it breaks, because the run("come command") from helper is now run from bar/foo instead of just foo.
Any tips on how to get around this? I tried using absolute paths in cd, but that didn't work. What I really want is for the cd context to only extend to the function scope.
So apparently absolute paths do work. The issue is that paths with ~ do not work (they are treated like relative paths, which IMHO is a bug in fabric), and that was what I was trying. So you have to do (e.g., if you are using vagrant) cd('/home/vagrant/foo').
Probably you can get away with relative paths in nested context managers
def func():
with cd("/home/vagrant/foo"):
stuff()
with cd("bar"): # cd /home/vagrant/foo/bar
more_stuff()
because you know exactly what the current working directory is when you call the cd('bar'). But for top-level cds, if the function can ever be called from within another function (not just directly from fab), you should use absolute paths.