Get Directory of Extension

Hello pyRevit Friends :slight_smile:

Let´s say I have my extension at C:\Folder\Example.extension\

I want to get the Directory of the Extension in the python scripts of this extension, so in this case i want to get C:\Folder.

I need that because the directory will probably change over time so I´ll have to move my whole extension to another drive. I want to put log-files etc in the extension directory and I want my scripts to have correct directorypaths even if i move the whole extendion folder to another drive.

Appreciate any advice!

Kind regards :slight_smile:

I want this as well.
I combed through the pyrevit source code but I couldn’t find anything that provides the root directory of the currently executing extension. The only thing I could find is a way to get the extension name.

If it doesn’t exist, I think adding a way to get the extension path would be a good addition to pyrevit.

Right now I have a path module in the lib folder of my extension which gets the relative path to the root directory.

import os
PROJECT_PATH = os.path.normpath(os.path.join(__file__, '../../../../'))
EXTENSION_PATH = os.path.join(PROJECT_PATH, 'Example.extension')
1 Like

Haven’t tried it but: pyRevit/userconfig.py at b7704eacfe1e5ab4840c1759f5ee3830af38a328 · eirannejad/pyRevit · GitHub should do the trick if you then filter your extension name

    def get_ext_root_dirs(self):
        """Return a list of all extension directories.

        Returns:
            :obj:`list`: list of strings. user extension directories.

        """
        dir_list = set()
        if op.exists(EXTENSIONS_DEFAULT_DIR):
            dir_list.add(EXTENSIONS_DEFAULT_DIR)
        dir_list.update(self.get_thirdparty_ext_root_dirs())
        return list(dir_list)

    def get_ext_sources(self):
        """Return a list of extension definition source files"""
        ext_sources = self.environment.get_option(
            CONSTS.EnvConfigsExtensionLookupSourcesKey,
            default_value=[],
        )
        return list(set(ext_sources))
2 Likes

@Jean-Marc

I saw that option as well, but I think it would be nice to have it defined in the EXEC_PARAMS instead of only having the extension name provided.

I saw where the extension name was provided in the EXEC_PARAMS: pyRevit/__init__.py at b7704eacfe1e5ab4840c1759f5ee3830af38a328 · eirannejad/pyRevit · GitHub and was looking at potentially adding the extension directory as well. I was looking into what that change might effect, but I’m still somewhat unfamiliar with pyrevits runtime. I gave up, because I’m having some trouble finding where the ScriptData that is provided to the executor is defined and what it may affect.

  @property   # read-only
  def command_extension(self):
      """str: Return current command extension name."""
      if '__commandextension__' in __builtins__ \
              and __builtins__['__commandextension__']:
          return __builtins__['__commandextension__']
      elif self.script_runtime:
          return self.script_runtime.ScriptData.CommandExtension

Sorry this is somewhat off-topic. I just wanted to look into a solution for a user to get the extension directory very easily.

Hello @Nicholas.Miles,

I like your method and rebuilt it for myselfe!

Created a “lib” folder with a “path” module:

import os
def path():
    MODULE_PATH = __file__
    DIRECTORY = MODULE_PATH.replace("Name.extension\lib\path\__init__.py","")
    return DIRECTORY 

Works great, so if this method does not have any disadvantage I don´t know yet (like wasting to much time) I´m pretty happy with this solution :slight_smile:

Thanks for your help!

Everything else mentioned in this thread is a little to much for me, when i read “class” and “self” I´m out :smiley:

1 Like

@Gerhard.P

This might be a little opinionated, but I think its best practice to use the path library instead of string manipulation wherever possible. However, in this instance it shouldn’t make any functional difference, assuming you don’t rename any files.

Also, for best practices, you shouldn’t have “MODULE_PATH” and “DIRECTORY” variables as all uppercase. I only had my variables as uppercase, because they are constant variables in the global scope of my path module, which is the convention for python. In your case they are local variables inside of a function, so they should just be “module_path” and “directory”.

1 Like

I didn´t understand what your path joining does, can you explain?

Because i want to remove something from the path, not add something?!

This is from pythons documentation of the path module.
Basically the “…/” that is added signals the normpath function to go up 4 steps in the path.

os.path.normpath(path )
Normalize a pathname by collapsing redundant separators and up-level references so that A//B, A/B/, A/./B and A/foo/../B all become A/B. This string manipulation may change the meaning of a path that contains symbolic links. On Windows, it converts forward slashes to backward slashes. To normalize case, use normcase().>

Also os.path.join is similar to just adding the strings with +, but it leaves the joining to the path module which supports multiple platforms.

Edit:
I was looking through the source code, and saw this. This is how pyrevit gets it’s root directory which is another reference. It uses os.path.dirname instead which may be more intuitive.

# -----------------------------------------------------------------------------
# config environment paths
# -----------------------------------------------------------------------------
# main pyrevit repo folder
try:
    # 3 steps back for <home>/Lib/pyrevit
    HOME_DIR = op.dirname(op.dirname(op.dirname(__file__)))
except NameError:
    raise Exception('Critical Error. Can not find home directory.')

# BIN directory
BIN_DIR = op.join(HOME_DIR, 'bin')

# main pyrevit lib folders
MAIN_LIB_DIR = op.join(HOME_DIR, 'pyrevitlib')
MISC_LIB_DIR = op.join(HOME_DIR, 'site-packages')

# path to pyrevit module
MODULE_DIR = op.join(MAIN_LIB_DIR, 'pyrevit')

# loader directory
LOADER_DIR = op.join(MODULE_DIR, 'loader')

# runtime directory
RUNTIME_DIR = op.join(MODULE_DIR, 'runtime')

# addin directory
ADDIN_DIR = op.join(LOADER_DIR, 'addin')
2 Likes

Hello @Nicholas.Miles :slight_smile:

Sorry for the late reply, had no revit for a few weeks.
Thanks for your explanations! It´s working fine.

I have a UserLog in the same directory as the extension.

image

I have a library that needs the location of this UserLog.

image

I call the location of the library and drop 4 levels, then add the missing piece of path.

PROJECT_PATH = os.path.normpath(os.path.join(__file__, '../../../../'))
LogFilePath = os.path.join(PROJECT_PATH, 'UserLog\LOG.csv')