Making More to Relative Path to File to Work

alex_ber
Geek Culture
Published in
3 min readApr 11, 2021

--

Without worrying about current working directory (Python).

What if you have some script or application that uses relative path and you want to invoke it from another directory. To get things more complicated. maybe your “external” code also use relative path, but relative to another directory. Here, I will describe the recipe how you can make all of these to work.

Suppose, that you wrote some script or application that is packaged and installed as YourApp (that is, it is installed into site-packages or venv). Something like this:

Code of app.py:

Note: Here I’m using init_app_conf module. You can read about it here.

Typically, at the end of the script/application’s main() function there are following lines:

They are designed to forward the call to the main()function when the file is called as script, typically from the the directory where app.py is laid down (cd YourApp) something like this is called: python app.py.

Now, what if you want to call app.py (or it’s main() function) from another directory?

If you have code such as in line 17, it will just fail. The essential problem their that my code use relative path, so it assumes that my working directory is the same as app.py file.

An alternative approach may be to supply some runner.py file, something like this:

Note, that this file also don’t have:

at the end.

It basically, calls app.py main() function decorated by FixRelCwd context-manager. I will get back to FixRelCwd below.

Now, in some unrelated directory, you should have something like this:

This is “real” main method. If you want to put some extra code before or after the app.py main() function. You can put it in lines 15 and 17.

Here you can read my story that describes my way to configure logging.

Here you can read about fixabscwd() function in mains module or Making relative path to file to work.

To put simple, besides lines 2 and 16 this is typical main() function. I want among many other thing to call app.py script/application that is installed in YourApp package. To make in concrete, the following code was executed:

python -m pip uninstall --yes YourApp
python setup.py sdist bdist_wheel
python -m pip install --find-links=./dist
YourApp

So, because we installed YourApp package we can import (the wrapper of, why it is important that it is wrapper will be clear below) main() function at line 2 and make regular call to it whenever it is appropriate (we can add some code before and/or after it, if we will).

Let’s quickly some-app, what we have. We have YourApp script application. It is installed into site-packages or venv. It has app.py file with main() function that does some computation. We have some “wrapper” runner.py with main() function decorated by FixRelCwd context-manager. And we have some “real” file with “real” main() function as above.

So, the real power is in FixRelCwd.

How FixRelCwd works?

FixRelCwd is context-manager. It takes relPackage and, optionally, logger.
It temporary changes current working directory to the one where relPackage is installed. If logger is not specified, than some default one will be used.

If this function is called in REPL or IPython notebook it does nothing.

This function wasn’t tested from frozen python script (frozen using py2exe), but it should work (by doing nothing).

Basically, first it stores os.chdir for the later use, than it looks for __file__ attribute in the relPackage module, takes directory from there and set cwd to it (by calling toos.chdir).

On the exit, it restores os.chdir, so your “external” code can operate as usual.

The source code you can found here. It is available as part of my AlexBerUtils s project.

You can install AlexBerUtils from PyPi:

python3 -m pip install -U alex-ber-utils

See here for more details explanation on how to install.

--

--