0

I frequently find I'd like to define constants at the package level, mostly for defining paths that are above modules in the filesystem hierarchy. I have the following method:

director structure:

my_project
    tests
    examples
    data
    my_package
        __init__.py
        mod1.py
        mod2.py
        ...  

contents of my_project/my_package/__init__.py:

import my_package
import os

__package_path = os.path.split(my_package.__path__[0])[0]
TESTS_FOLDER = os.path.join(__package_path, 'tests')
EXAMPLES_FOLDER = os.path.join(__package_path, 'examples')
DATA_FOLDER = os.path.join(__package_path, 'local')

This allows for, e.g.:

import my_package
my_package.TESTS_FOLDER #returns path for tests

As a solution to getting the file paths of modules or project files, this seems quite nice - there is a lot of discussion/confusion over how to get valid paths of a script (here and here for example). My question: will import my_package within its own __init__.py result in unusual/undesired behavior? or: what is the idiomatic way to define package level constants?

anon01
  • 8,116
  • 7
  • 25
  • 54

1 Answers1

0

This looks generally accepted, as seen here and here. Here is an improved version of __init__.py:

import my_package
import os

__package_path = os.path.split(my_package.__path__[0])[0]
TESTS_FOLDER = os.path.join(__package_path, 'tests')
EXAMPLES_FOLDER = os.path.join(__package_path, 'examples')
DATA_FOLDER = os.path.join(__package_path, 'local')

del my_package
del os
del __package_path

The del statements prevents unwanted variables from being exposed in the my_package namespace, so you can't do weird things like this:

import my_namespace
print(my_namespace.my_namespace.my_namespace.TESTS_FOLDER)

Elsewhere, the line __all__ = ["TESTS_FOLDER", "EXAMPLES_FOLDER", "DATA_FOLDER"] is sometimes used, but this will only work for the specific import structure from my_namespace import *. For that reason, the del method appears preferable.

anon01
  • 8,116
  • 7
  • 25
  • 54