Import Built-in and Custom Modules in Python

Learn how Python resolves imports for both standard library modules and your own custom modules, and how to avoid conflicts when names overlap.

6 min read

Python ships with a large collection of built-in and standard library modules, and at the same time every project creates custom modules specific to that application. To import built-in and custom modules in Python, you use the same import statement regardless of whether the module came with Python or you wrote it yourself. Understanding how Python distinguishes between built-in and custom modules, how it resolves import names when both exist, and how to organize your project so that custom modules coexist cleanly with the standard library is essential for avoiding confusing import errors.

The import system does not treat built-in and custom modules differently at the syntax level. The same statement that imports the standard library math module also imports your own utils module. What differs is where Python looks for the module file and what happens when a name conflict arises between a module you wrote and a module that ships with Python. The search path determines which module wins in a conflict, and understanding that path gives you control. For the foundational mechanics behind this, see the article on importing modules in Python.

How Python resolves module names during import

When Python encounters an import statement, it searches for the module in a specific order. First it checks sys.modules, the cache of already-loaded modules, to see if the module has been imported previously in this session. If not, it searches through the directories listed in sys.path, testing each one in order for a matching file or package directory. The search stops at the first match, and that file becomes the module. This first-match behavior is the root cause of the most common import confusion: a local file with the same name as a standard library module will shadow the built-in version.

The search path order on a typical system puts the current directory first, followed by the directories specified in the PYTHONPATH environment variable, then the standard library directories, and finally the site-packages directories where third-party libraries are installed. This order means that a file called json.py in your project directory will be found before Python's built-in json module, and every import of json in your project will use your file instead of the standard library version.

This first-match behavior is easy to verify. Open a Python interpreter and print the search path to see the exact lookup order on your system:

pythonpython
import sys
 
for i, path in enumerate(sys.path):
    print(f"{i}: {path}")

This output tells you whether your current working directory is on the path, whether any environment variables are contributing directories, and where the standard library and third-party packages live. When an import fails, printing the search path is the first diagnostic step.

Importing standard library modules correctly

The standard library is a collection of modules and packages that ship with every Python installation. You import them the same way you import any other module: by name, with no special prefix or configuration. Modules like math, datetime, json, csv, os, and sys are available in every Python program without installing anything extra. They are part of the language's promise that a working Python installation comes with batteries included.

The standard library is organized into a mix of top-level modules and packages with submodules. Some modules like math are single files that you import directly. Others like collections provide both the top-level package and specific names that you can import individually. Still others like urllib are packages with submodules that you import with dotted names. The organizational pattern varies by module, but the import syntax is consistent: you write the import statement, Python searches the standard library directories, and the module loads.

Large standard library packages like email and xml contain many submodules, but you should import only the specific submodules you need rather than the entire package. Importing the top-level email package does not automatically load all of its submodules, and accessing a submodule you have not explicitly imported will fail. This lazy loading behavior is by design to keep import times fast and memory usage low.

Importing your own custom modules

Custom modules are .py files that you create in your project directory or in a package structure that you have set up. Importing them works identically to importing standard library modules, with the important difference that Python finds your modules through the search path rather than through the standard library directories. As long as your module file or package directory is in a location that appears in sys.path, the import statement will find it.

The simplest case is a custom module in the same directory as the script that imports it. Because the script's directory is automatically added to the search path, any .py file in that directory is importable by name. This is the pattern you used when you created your first module: a greetings.py file in the same folder as main.py, imported with a plain import statement. The same directory import works for any number of modules and is the right approach for small projects.

For custom modules in a different directory, you have three options. You can add the directory to sys.path at runtime, which is a quick fix for development but not suitable for production code. You can set the PYTHONPATH environment variable before launching Python, which makes the directory available to all scripts in that session. Or you can structure your project as an installable package so that the package directory is added to the search path automatically during installation. The installable package approach, covered in the article on building a multi-module Python project, is the professional standard for code that other people will use.

Avoiding name collisions with standard library modules

The most important rule for naming your custom modules is to never use a name that conflicts with a standard library module. If you create a file called math.py, every import of math in your project will find your file instead of Python's math module. The same applies to json.py, csv.py, email.py, and hundreds of other module names that Python provides out of the box. The Python documentation publishes a complete module index that lists every standard library module name, and you should check that list before naming your own modules.

If you are unsure whether a name conflicts, test it in a clean Python interpreter. Open a terminal, run Python, and type an import statement with the name you are considering. If the import succeeds without any prior installation, the name belongs to a standard library module and you should pick a different name for your own file. Common names like utils, helpers, and config are safe because they are not standard library modules, but names like test, types, and code are taken and should be avoided.

You can also confirm which file an import actually loaded by printing its __file__ attribute. This is useful when you suspect a local file is shadowing a standard library module and want proof instead of a guess:

pythonpython
import json
 
print(json.__file__)

If this prints a path inside your own project instead of a path inside Python's installation directory, you have confirmed that a local json.py is shadowing the standard library module, and you know exactly which file to rename.

When you absolutely must use a name that conflicts, you can rename your module with a project-specific prefix or suffix. Instead of json.py, name the file project_json.py or json_utils.py. The import statement uses the filename, so the new name avoids the conflict while still being descriptive about the module's purpose. This is a better solution than manipulating the search path to make your module take priority, because path manipulation is fragile and breaks when the script is run from a different directory or on a different machine.

Rune AI

Rune AI

Key Insights

  • Python searches sys.path in order and uses the first matching module, so local files take priority over standard library modules.
  • Never name your own modules after standard library modules; check the official module index if you are unsure.
  • You can inspect sys.path to see the exact search order on your system.
  • Custom modules must be on the search path to be importable; use the same directory, PYTHONPATH, or a proper package installation.
  • The import statement works identically for built-in, standard library, third-party, and custom modules.
RunePowered by Rune AI

Frequently Asked Questions

How does Python decide whether to import my module or the standard library version?

Python searches sys.path in order and uses the first match it finds. Since the current directory is usually first in sys.path, a local module with the same name as a standard library module will shadow the built-in one. This is why you should never name your own modules after standard library modules like json.py, math.py, or email.py. If you need to use both, rename your local module to avoid the conflict.

Can I import a module from a different directory without moving it?

Yes, you can add the directory to sys.path at runtime by appending to the list, or you can set the PYTHONPATH environment variable before running Python. The proper long-term solution is to structure your project as an installable package so that the package directory is automatically on the search path after installation. Runtime path manipulation should be reserved for development workflows and one-off scripts.

How do I know which modules are built into Python?

The Python documentation maintains a complete module index that lists every standard library module. You can also check by running import module_name in a clean Python interpreter: if it works without any prior installation, it is either a built-in or a standard library module. Third-party modules installed with pip live in the site-packages directory, which is also on the search path but comes after the standard library locations.

Conclusion

Importing built-in and custom modules uses the same import statement and the same search path. The only difference is where the module file lives on disk. Understanding the search order helps you avoid accidentally shadowing standard library modules, troubleshoot import errors, and design projects where custom modules coexist cleanly with the modules Python provides.