Import a file from a subdirectory?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Importing a file from a subdirectory is really a question about the module system of a specific language. The most common case is Python, where the right answer depends on whether the subdirectory is part of a package and how the code is executed. The safest general advice is to structure the project as a package and use normal package imports rather than hacking sys.path.
Python Package Example
Suppose your project looks like this:
Then app.py can import from the subdirectory like this:
The __init__.py file tells Python to treat services as a package in the classic package layout.
Why Package Structure Matters
If Python cannot see the directory as part of the importable module layout, imports become fragile. Developers often react by editing sys.path, but that is usually a sign the project structure or run command needs fixing instead.
A good import should work because the package layout is correct, not because the script manually injects directories into module search paths.
Importing from a Nested Subpackage
A deeper structure works the same way.
Then:
This is the standard dotted import path model.
Relative Imports Inside Packages
Within package modules, relative imports can be useful.
Example structure:
Inside emailer.py:
Relative imports are best used inside a package, not from ad hoc top-level scripts.
Running the Module Correctly
A frequent issue is that imports work one way when run from an IDE and another way when run directly from the shell. Execution context matters.
For package-style execution, prefer:
or run from the project root in a way that preserves package structure expectations.
If imports only work when launched from one specific directory, the project layout or run method probably needs cleanup.
The sys.path Escape Hatch
You can modify sys.path, but this is usually a fallback, not the best design.
This can unblock a script, but it makes imports less explicit and more environment-dependent. If the code is part of a real application, package structure is almost always cleaner.
JavaScript and Other Languages
Other languages have their own import rules. For example, in Node.js:
Or with ES modules:
The common pattern across languages is that relative path imports are controlled by the module system, not by some universal “subdirectory import” rule.
Common Python Mistakes
Typical causes of import errors include:
- missing
__init__.pyin older-style package layouts - running a file from the wrong working directory
- mixing relative and absolute imports inconsistently
- naming your own file the same as a standard library module
For example, naming a file email.py or json.py can create shadowing bugs that look like import-path problems even when the directory layout is fine.
Best Practical Advice
For a Python project:
- structure code as packages
- import by package path
- use relative imports only inside package modules when they improve clarity
- avoid
sys.pathedits unless you are solving a very temporary script problem
That is usually the cleanest and most maintainable approach.
Common Pitfalls
The biggest mistake is treating import problems as path hacks instead of project-structure problems. Another is relying on sys.path.append(...) in production code when a proper package layout would solve the issue more cleanly. Developers also often run scripts from inconsistent working directories and then blame the import statement itself. Finally, ambiguous file names can shadow standard modules and create confusing import failures that are not actually about subdirectories at all.
Summary
- In Python, the clean solution is usually to treat the subdirectory as a package and import from it normally.
- Use dotted imports such as
from services.emailer import send_email. - Use relative imports inside packages when appropriate.
- Avoid
sys.pathhacks unless you are fixing a temporary script. - If imports are inconsistent, check package structure, execution context, and name shadowing first.

