4
\$\begingroup\$

I have written a class that locates the relative root project directory path from where I'm calling that class.

E.g., I have this structure:

rootProjectDir
 |_sub
 |__subsub
 |___subsubfile.txt
 |_another_sub
 |__another_sub_file.py

and what I'm doing is when I'm working within another_sub_file.py, I'm calling my class GetRootPath to get /home/user/dev/rootProjectDir, and then I can construct the path to the ./rootProjectDir/sub/subsub/subsubfile.txt to get that subsubfile.txt. I didn't find any better solution. I hope there is, but I wrote this class helper for me to achieve this:

import os
class GetRootPath:
 def __init__(self):
 self.rootFolder = '/rootProjectDir'
 def get_root(self):
 working_file_dir = os.path.dirname(os.path.abspath(__file__))
 i = len(working_file_dir.split('/'))
 root = list(working_file_dir.split('/'))
 new_root = []
 for item in root[1:]:
 new_root.append('/' + str(item))
 while True:
 if self.rootFolder == new_root[-1]:
 builded = ''.join(new_root) + '/'
 return builded
 else:
 new_root.pop()
 i -= 1

Is there any better way to achieve my goal and also, what could be done better with my class to improve performance and stability of creating relative path to root directory of my project? Or is there a better Pythonic way of navigating your project folder to get files, etc., as you need? My GetRootPath class looks like a non-Pythonic way of doing this.

I'm working in PyCharm, and my project is created with it, so it's not just folders, I guess.

toolic
14.6k5 gold badges29 silver badges203 bronze badges
asked Apr 11, 2018 at 3:36
\$\endgroup\$
4
  • \$\begingroup\$ What is the point of the variable i? \$\endgroup\$ Commented Apr 11, 2018 at 5:12
  • \$\begingroup\$ I was thinking of creating a loop that have true while i > 0, where while True, to change to while i > 0:, but didn't knew if that's a good idea, so as for now I lefted it, but if it's not needed then I think I will remove later on :) \$\endgroup\$ Commented Apr 11, 2018 at 5:16
  • \$\begingroup\$ I've thought about this multiple times now and still don't get what you're trying to do. What do you mean by 'I can construct the path to the ./rootProjectDir/sub/subsub/subsubfile.txt to get that subsubfile.txt.'? What problem are you currently experiencing that GetRootPath is supposed to solve? \$\endgroup\$ Commented Apr 11, 2018 at 15:11
  • \$\begingroup\$ Imagine my structure above and that you are working within .py file. Now you need to get that .txt file content from the structure above. How to do that? 1st you can hard-code whole path to the .txt file and that would be fine, but imagine if you have 10 or more .py files in many different structures and differently nested .py files. Now hard-coding whole path in these scattered .py files to get that .txt file looks to much of work. So I decided to create relative path to my .txt file that I could get it by just providing the path from root to .txt file. \$\endgroup\$ Commented Apr 12, 2018 at 3:19

2 Answers 2

3
\$\begingroup\$

As written, your code assumes it will be running on a filesystem which uses / as a directory separator, such as Unix, Linux, and MacOS. It will never work when run on a filesystem with \ as the separator, such as Windows. While you could try to fix this using os.sep, there is an easier way...

pathlib.Path is an object-oriented interface to the filesystem. Using it frees you from needing to worry about which separator needs to be used, and from issues with the file system root which necessitated the unexplained root[1:] in your code, which "seems to" ignore the first component of the filesystem path.

Once you've converted the filename to a Path object, Path.parts will split it into the individual components. Count backwards until you find the required rootProjectDir, and then use Path.parents to retrieve full path to that directory. Eg)

from pathlib import Path
def get_root_path() -> Path:
 path = Path(__file__).absolute()
 idx = path.parts[::-1].index("rootProjectDir")
 return path.parents[idx - 1]

I've replaced the class containing a single method with a simple function. Still, the above still feels wrong. The project root directory doesn't ever change. Moreover, the file itself likely knows its position in the project structure, so it doesn't really need the name of the project directory.

Your project is laid out like ...

rootProjectDir
 |_sub
 | |_subsub
 | |_subsubfile.txt
 |_another_sub
 |_another_sub_file.py

Is another_sub a Python package? If so, when inside the another_sub_file.py module, __name__ will be another_sub.another_sub_file. Since the module name has two components, you can immediately determine the project root directory is the grandparent of this __file__.

from pathlib import Path
ROOT_PATH = Path(__file__).absolute().parents[__name__.count('.')]

Now the project root is computed once when the file is imported, not repeatedly every time you want to access a different file.

answered Aug 11 at 18:25
\$\endgroup\$
2
\$\begingroup\$

DRY

This expression appears on 2 consecutive lines:

working_file_dir.split('/')

You could set it to a variable so that split is only called once.

However, as noted in the comments on the question, the i variable is not needed, which means these 2 lines can be deleted:

i = len(working_file_dir.split('/'))
i -= 1

Simpler

These lines:

builded = ''.join(new_root) + '/'
return builded

can be combined into a single line:

return ''.join(new_root) + '/'

This eliminates the poorly-named builded variable.

Also, there is no need for the else line after the return. here is the simplified while loop:

while True:
 if self.rootFolder == new_root[-1]:
 return ''.join(new_root) + '/'
 new_root.pop()

The code might be simpler without the class since there is really only one function.

Documentation

The PEP 8 style guide recommends adding docstrings for classes and functions.

Naming

The PEP 8 style guide recommends snake_case for function and variable names.

For example, rootFolder would be root_folder.

Array variables are commonly pluralized; root would be better as roots.

answered Aug 7 at 17:13
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.