Relative Imports#
Relative imports allow modules within a package to reference each other using . (current) and .. (parent) notation.
Prefer Absolute Imports
Relative imports can be ambiguous. We recommend absolute imports in most cases. Use relative imports only for tightly coupled internal package code.
Relative Import Syntax#
| Syntax | Meaning |
|---|---|
.module |
Same directory |
..module |
Parent directory |
...module |
Grandparent directory |
Same-Level Imports (.)#
Import from modules in the same directory.
import from pkg.sibling { SIBLING_VALUE, sibling_func }
import from pkg.base { BASE_VALUE }
with entry {
print(f"Relative import - BASE_VALUE: {BASE_VALUE}");
print(f"Relative import - SIBLING_VALUE: {SIBLING_VALUE}");
print(f"Relative import - sibling_func(): {sibling_func()}");
}
glob BASE_VALUE = "Base value";
def base_func -> str {
return "Base function";
}
import from .base { BASE_VALUE, base_func }
glob SIBLING_VALUE = f"Sibling uses {BASE_VALUE}";
def sibling_func -> str {
return f"Sibling calls: {base_func()}";
}
relative_sibling/
├── main.jac
└── pkg/
├── base.jac # Defines BASE_VALUE, base_func
└── sibling.jac # Uses .base to import
Output
```
Relative import - BASE_VALUE: Base value Relative import - SIBLING_VALUE: Sibling uses Base value Relative import - sibling_func(): Sibling calls: Base function ```
Parent-Level Imports (..)#
Import from the parent directory.
import from project.sub.deep { DEEP_VALUE, deep_func }
import from project.config { CONFIG_VALUE }
with entry {
print(f"Parent relative - CONFIG_VALUE: {CONFIG_VALUE}");
print(f"Parent relative - DEEP_VALUE: {DEEP_VALUE}");
print(f"Parent relative - deep_func(): {deep_func()}");
}
import from ..config { CONFIG_VALUE, DEBUG }
glob DEEP_VALUE = f"Deep module using config: {CONFIG_VALUE}";
def deep_func -> str {
return f"Deep function, DEBUG={DEBUG}";
}
relative_parent/
├── main.jac
└── project/
├── config.jac # Defines CONFIG_VALUE, DEBUG
└── sub/
└── deep.jac # Uses ..config to reach parent
Output
```
Parent relative - CONFIG_VALUE: Project config Parent relative - DEEP_VALUE: Deep module using config: Project config Parent relative - deep_func(): Deep function, DEBUG=True ```
Mixed Absolute and Relative#
You can combine both import styles, but prefer absolute imports for clarity.
import from library.base { BASE_ID }
import from library.extended { EXTENDED_ID, get_extended_id }
import library.base as lib_base;
with entry {
print(f"Mixed - BASE_ID (from import): {BASE_ID}");
print(f"Mixed - EXTENDED_ID: {EXTENDED_ID}");
print(f"Mixed - get_extended_id(): {get_extended_id()}");
print(f"Mixed - lib_base.BASE_ID (alias): {lib_base.BASE_ID}");
}
import from .base { BASE_ID, get_base_id }
glob EXTENDED_ID = BASE_ID + 1;
def get_extended_id -> str {
return f"Base: {get_base_id()}, Extended: {EXTENDED_ID}";
}
Output
```
Mixed - BASE_ID (from import): 1000 Mixed - EXTENDED_ID: 1001 Mixed - get_extended_id(): Base: 1000, Extended: 1001 Mixed - lib_base.BASE_ID (alias): 1000 ```
Relative Import Boundaries
Relative imports only work within packages. You cannot use .. to escape beyond your project's root.
Best Practice
Default to absolute imports. They're explicit and don't break when you reorganize code.