Accessing the contents of a project’s root directory in Python
This morning I was trying to find a good way of using os.path to load the content of a text file into memory, which exists in the root directory of my current project. This approach strikes me as a bit hackneyed, but after some thought it was exactly what I was about to do, except with os.path.normpath(os.path.join(__file__, ‘..’, ‘..’)) These relative operations on the path to navigate to a static root directory are brittle. Now, if my project layout changes, these associations to the (former) root directory change with it. I wish I could assign the path to find a target, or a destination, instead of following a sequence of navigation operations. I was thinking of making a special __root__.py file. Does this look familiar to anyone, or know of a better implementation?
my_project | | __init__.py | README.md | license.txt | __root__.py | special_resource.txt | ./module |-- load.py
"""__root__.py""" import os def path(): return os.path.dirname(__file__)
"""load.py""" import __root__ def resource(): return open(os.path.join(__root__.path(), 'special_resource.txt'))
3 Answers 3
I never heard of __root__.py and don’t think that is a good idea.
Instead create a files.py in a utils module:
MAIN_DIRECTORY = dirname(dirname(__file__)) def get_full_path(*path): return join(MAIN_DIRECTORY, *path)
You can then import this function from your main.py :
from utils.files import get_full_path path_to_map = get_full_path('res', 'map.png')
So my project directory tree looks like this:
project_main_folder/ utils/ __init__.py (empty) files.py main.py
When you import a module the Python interpreter searches if there’s a built-in module with that name (that’s why your module name should be different or you should use relative imports). If it hasn’t found a module with that name it will (among others in sys.path ) search in the directory containing your script. You can find further information in the documentation.
Open a file as superuser in python
Since privileges work completely differently on Unix-like systems and Windows, you’re going to need to have platform-specific code. In any case, you’ll need to break up your program into two separate programs, one of which runs with elevated permissions and the other of which runs with standard/reduced permissions.
In Unix-like systems (including Linux and Mac OS X), the executable that runs with elevated permissions should do this:
- Assume you’re running as root and open the file for reading. Since you mentioned that the file is very large, you don’t actually read the whole file in, you just keep an open file descriptor. If opening it fails, print an error message and exit.
- Use setreuid(2) and setregid(2) to set your user ID and group ID back to an unprivileged user.
- Use one of the exec(3) functions to execute the unprivileged executable.
- If you want to make it so that you can run this program without using sudo , then make it owned by root and make it a set-user-ID executable with chown root the-program; chmod +s the-program .
The unprivileged program will now be run with normal permissions, but when it starts up, it will have an open file descriptor (file descriptor #3) that can be used to read from your special file.
For Windows, it’s similar but slightly different:
- Assume you’re running as root and open the file for reading using CreateFile . Do not use default security attributes — create a SECURITY_ATTRIBUTES structure with bInheritHandle set to TRUE so that the handle will be inherited by child processes. If opening the file failed, print an error message and exit.
- Use CreateProcess to launch your child process. Pass in the handle above on the command line (e.g. printed as a numerical value); you could also use a shared memory region, but that’s more trouble than it’s worth for this problem.
- Embed a manifest in this executable with requireAdministrator set to true . After you do this, when you run the program, you’ll get a UAC prompt asking you if you want to allow the program to makes changes.
The child process then does grabs the inherited handle by parsing the command line, and it can then read in the data as it pleases.
One problem with this approach is that when you inherit a handle, you have to use the low-level system calls ( read(2) on Unix, ReadFile on Windows) to read from it — you can’t use higher-level functions like C’s fread(3) or C++’s iostream s (ok, Unix has fdopen(3) , but there’s no equivalent on Windows as far as I’m aware).
As I’m sure you’ve noticed by now, everything above has been in C. In Unix, this translates pretty straightforwardly into Python, since the os module has lots of goodies like setreuid , exec* , and fdopen . On Windows, you might be able to do some of this stuff with the ctypes module and/or Pywin32, but it’s probably easier to stick with C.
Solution 2
What you’re looking for is called privilege escalation, and it very much depends on the platform you’re running on. In general, what your program would have to do is run a portion as the superuser. On unix systems, for instance, you might be able to use sudo to read the contents of the file.
But as mentioned, this really depends on what system you’re running on.
Solution 3
I would split the program in two.
- Handles opening the file and accessing the contents. It can assume it’s started with the privileges it needs.
- Everything else that doesn’t require special privileges.
Put a config entry which describes how to exec or subprocess the command that requires extra privileges. ie.
access_special_file: sudo access_special_file
access_special_file: runas /user:AccountWithPrivs access_special_file
This offloads some of the system specifics for privilege escalation to the system shell where there may be more convenient ways of gaining the permissions you need.
Solution 4
On linux it’s a cynch, as @ViktorKerkez showed. This is how I streamed my WiFi passwords files (readable only by root/sudo):
import subprocess import sys # substitute your Windoze/DOS/PowerlessShell command here: cat_wifi_pws = 'sudo cat /etc/NetworkManager/system-connections/*' process = subprocess.Popen(cat_wifi_pws, stdout=subprocess.PIPE, shell=True) # read one line at a time, as it becomes available for line in iter(process.stdout.readline, ''): sys.stdout.write(line)
Of course this will prompt you for your sudo password. And you can use gksudo if you’re on a system that has it and you prefer dialog boxes. As a bonus, if you have a decent timeout_default in /etc/sudoers , and you’ve recently run sudo in the same shell where you launched the python interpreter, you won’t have to enter a password at all.
Python – Open a file as superuser in python
I have to open a system file and read from it. This file is usually only readable by root (the super user). I have a way to ask the user for the superuser password. I would like to use this credentials to open the file and read from it without having my entire program running as a superuser process. Is there a way to achieve this in a multiplatform way?
Best Solution
Since privileges work completely differently on Unix-like systems and Windows, you’re going to need to have platform-specific code. In any case, you’ll need to break up your program into two separate programs, one of which runs with elevated permissions and the other of which runs with standard/reduced permissions.
In Unix-like systems (including Linux and Mac OS X), the executable that runs with elevated permissions should do this:
- Assume you’re running as root and open the file for reading. Since you mentioned that the file is very large, you don’t actually read the whole file in, you just keep an open file descriptor. If opening it fails, print an error message and exit.
- Use setreuid(2) and setregid(2) to set your user ID and group ID back to an unprivileged user.
- Use one of the exec(3) functions to execute the unprivileged executable.
- If you want to make it so that you can run this program without using sudo , then make it owned by root and make it a set-user-ID executable with chown root the-program; chmod +s the-program .
The unprivileged program will now be run with normal permissions, but when it starts up, it will have an open file descriptor (file descriptor #3) that can be used to read from your special file.
For Windows, it’s similar but slightly different:
- Assume you’re running as root and open the file for reading using CreateFile . Do not use default security attributes — create a SECURITY_ATTRIBUTES structure with bInheritHandle set to TRUE so that the handle will be inherited by child processes. If opening the file failed, print an error message and exit.
- Use CreateProcess to launch your child process. Pass in the handle above on the command line (e.g. printed as a numerical value); you could also use a shared memory region, but that’s more trouble than it’s worth for this problem.
- Embed a manifest in this executable with requireAdministrator set to true . After you do this, when you run the program, you’ll get a UAC prompt asking you if you want to allow the program to makes changes.
The child process then does grabs the inherited handle by parsing the command line, and it can then read in the data as it pleases.
One problem with this approach is that when you inherit a handle, you have to use the low-level system calls ( read(2) on Unix, ReadFile on Windows) to read from it — you can’t use higher-level functions like C’s fread(3) or C++’s iostream s (ok, Unix has fdopen(3) , but there’s no equivalent on Windows as far as I’m aware).
As I’m sure you’ve noticed by now, everything above has been in C. In Unix, this translates pretty straightforwardly into Python, since the os module has lots of goodies like setreuid , exec* , and fdopen . On Windows, you might be able to do some of this stuff with the ctypes module and/or Pywin32, but it’s probably easier to stick with C.