Python searches the paths in sys.path in order (see http://docs.python.org/tutorial/modules.html#the-module-search-path). easy_install changes this list directly (see the last line in your easy-install.pth file):

import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

This basically takes whatever directories are added and inserts them at the beginning of the list.

Also see Eggs in path before PYTHONPATH environment variable.

Answer from cwa on Stack Overflow
Top answer
1 of 4
25

Python searches the paths in sys.path in order (see http://docs.python.org/tutorial/modules.html#the-module-search-path). easy_install changes this list directly (see the last line in your easy-install.pth file):

import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

This basically takes whatever directories are added and inserts them at the beginning of the list.

Also see Eggs in path before PYTHONPATH environment variable.

2 of 4
21

This page is a high Google result for "Python import order", so here's a hopefully clearer explanation:

  • https://docs.python.org/library/sys.html#sys.path
  • https://docs.python.org/tutorial/modules.html#the-module-search-path

As both of those pages explain, the import order is:

  1. Built-in python modules. You can see the list in the variable sys.modules.
  2. The sys.path entries.
  3. The installation-dependent default locations.

And as the sys.path doc page explains, it is populated as follows:

  1. The first entry is the FULL PATH TO THE DIRECTORY of the file which python was started with (so /someplace/on/disk/> $ python /path/to/the/run.py means the first path is /path/to/the/, and likewise the path would be the same if you're in /path/to/> $ python the/run.py (it is still ALWAYS going to be set to the FULL PATH to the directory no matter if you gave python a relative or absolute file)), or it will be an empty string if python was started without a file aka interactive mode (an empty string means "current working directory for the python process"). In other words, Python assumes that the file you started wants to be able to do relative imports of package/-folders and blah.py modules that exist within the same location as the file you started python with.
  2. The other entries in sys.path are populated from the PYTHONPATH environment variable. Basically your global pip folders where your third-party python packages are installed (things like requests and numpy and tensorflow).

So, basically: Yes, you can trust that Python will find your local package-folders and module files first, before any globally installed pip stuff.

Here's an example to explain further:

myproject/ # <-- This is not a package (no __init__.py file).
  modules/ # <-- This is a package (has an __init__.py file).
    __init__.py
    foo.py
  run.py
  second.py

executed with: python /path/to/the/myproject/run.py
will cause sys.path[0] to be "/path/to/the/myproject/"

run.py contents:
import modules.foo as foo # will import "/path/to/the/myproject/" + "modules/foo.py"
import second # will import "/path/to/the/myproject/" + "second.py"

second.py contents:
import modules.foo as foo # will import "/path/to/the/myproject/" + "modules/foo.py"

EDIT:

You can run the following command to print a sorted list of all built-in module names. These are the things that load before ANY custom files/module folders in your projects. Basically these are names you must avoid in your own custom files:

python -c "import sys, json; print(json.dumps(sorted(list(sys.modules.keys())), indent=4))"

List as of Python 3.9.0:

"__main__",
"_abc",
"_bootlocale",
"_codecs",
"_collections",
"_collections_abc",
"_frozen_importlib",
"_frozen_importlib_external",
"_functools",
"_heapq",
"_imp",
"_io",
"_json",
"_locale",
"_operator",
"_signal",
"_sitebuiltins",
"_sre",
"_stat",
"_thread",
"_warnings",
"_weakref",
"abc",
"builtins",
"codecs",
"collections",
"copyreg",
"encodings",
"encodings.aliases",
"encodings.cp1252",
"encodings.latin_1",
"encodings.utf_8",
"enum",
"functools",
"genericpath",
"heapq",
"io",
"itertools",
"json",
"json.decoder",
"json.encoder",
"json.scanner",
"keyword",
"marshal",
"nt",
"ntpath",
"operator",
"os",
"os.path",
"pywin32_bootstrap",
"re",
"reprlib",
"site",
"sre_compile",
"sre_constants",
"sre_parse",
"stat",
"sys",
"time",
"types",
"winreg",
"zipimport"

So NEVER use any of those names for you .py files or your project module subfolders.

Discussions

Difference in sys.path order, depending on how run
In the coverage.py test suite, I have tests that check various environmental values to see that they are they same for coverage run myprog.py as they are for python myprog.py. One of the values is ... More on github.com
🌐 github.com
5
August 24, 2016
python - Is the order reported by 'sys.path' the order in which packages are searched? - Stack Overflow
Does the order in which entries occur in the Python sys.path correspond to the order in which packages are found? For example I have >>> from pprint import pprint >>> pprint... More on stackoverflow.com
🌐 stackoverflow.com
python - PYTHONPATH order on Ubuntu 14.04 - Stack Overflow
I have two computers running Ubuntu 14.04 server (let's call them A and B). B was initially a 10.04 but it has received two upgrades to 12.04 and 14.04. I do not understand why the python path is More on stackoverflow.com
🌐 stackoverflow.com
October 3, 2016
python - change the priority of pythonpath - Stack Overflow
If you just need to control it per command line execution then you should probably use the PYTHONPATH env var. ... Ordering the entries in PYTHONPATH is partly right, but the system paths don't seem to get included until you run python, and then they get put at the end. More on stackoverflow.com
🌐 stackoverflow.com
🌐
Python.org
discuss.python.org › ideas
PYTHONPATH priority after standard library? - Ideas - Discussions on Python.org
November 11, 2020 - Would it be appropriate/acceptable to put sys.path inserts such as PYTHONPATH and/or '' when that is added at a lower priority than the standard library, instead of always top priority? This is related to Python flag/envvar not to put current directory to sys.path (but don't ignore PYTHONPATH) (and several of the historical links in this comment) in that it involves the behavior of PYTHONPATH and/or current directory (when that happens) being added to the front of sys.path, but I haven’t seen t...
🌐
Leemendelowitz
leemendelowitz.github.io › blog › how-does-python-find-packages.html
How does python find packages? // Lee On Coding // My blog about coding and stuff.
As the docs explain, sys.path is populated using the current working directory, followed by directories listed in your PYTHONPATH environment variable, followed by installation-dependent default paths, which are controlled by the site module.
🌐
GitHub
github.com › pypa › setuptools › issues › 686
Difference in sys.path order, depending on how run · Issue #686 · pypa/setuptools
August 24, 2016 - In the coverage.py test suite, I have tests that check various environmental values to see that they are they same for coverage run myprog.py as they are for python myprog.py. One of the values is sys.path. Recently, these tests started ...
Author: pypa
🌐
JetBrains
youtrack.jetbrains.com › issue › PY-52471 › Allow-choosing-order-in-which-PYTHONPATH-is-constructed
Allow choosing order in which PYTHONPATH is constructed
Our website uses some cookies and records your IP address for the purposes of accessibility, security, and managing your access to the telecommunication network. You can disable data collection and cookies by changing your browser settings, but it may affect how this website functions.
🌐
Thomas Stringer
trstringer.com › python-module-import-precedence
Module Import Precedence in Python | Thomas Stringer
May 12, 2017 - If Python doesn’t find the module in the local directory, it’ll then move onto the paths specified in $PYTHONPATH.
Find elsewhere
Top answer
1 of 5
6

As we cannot explore into your system, I am trying to analysis your first question by illustrating how sys.path is initialized. Available references are where-does-sys-path-starts and pyco-reverse-engineering(python2.6).

The sys.path comes from the following variables(in order):

  1. $PYTHONPATH (highest priority)
  2. sys.prefix-ed stdlib
  3. sys.exec_prefix-ed stdlib
  4. site-packages
  5. *.pth in site-packages (lowest priority)

Now let's describe each of these variables:

  1. $PYTHONPATH, this is just a system environment variable.
  2. & 3. sys.prefix and sys.exec_prefix are determined before any python script is executed. It is actually coded in the source Module/getpath.c.

The logic is like this:

IF $PYTHONHOME IS set:
    RETURN sys.prefix AND sys.exec_prefix as $PYTHONHOME
ELSE:
    current_dir = directory of python executable;
    DO:
        current_dir = parent(current_dir)
        IF FILE 'lib/pythonX.Y/os.py' EXSITS:
            sys.prefix = current_dir
        IF FILE 'lib/pythonX.Y/lib-dynload' EXSITS:
            sys.exec_prefix = current_dir
        IF current_dir IS '/':
            BREAK
    WHILE(TRUE)
    IF sys.prefix IS NOT SET:
       sys.prefix = BUILD_PREFIX
    IF sys.exec_prefix IS NOT SET:
       sys.exec_prefix = BUILD_PREFIX
  1. & 5. site-packages and *.pth are added by import of site.py. In this module you will find the docs:

    This will append site-specific paths to the module search path. On Unix (including Mac OSX), it starts with sys.prefix and sys.exec_prefix (if different) and appends lib/python/site-packages as well as lib/site-python. ... ...

    For Debian and derivatives, this sys.path is augmented with directories for packages distributed within the distribution. Local addons go into /usr/local/lib/python/dist-packages, Debian addons install into /usr/{lib,share}/python/dist-packages. /usr/lib/python/site-packages is not used.

    A path configuration file is a file whose name has the form .pth; its contents are additional directories (one per line) to be added to sys.path. ... ...

And a code snippet for important function getsitepackages:

sitepackages.append(os.path.join(prefix, "local/lib",
                            "python" + sys.version[:3],
                            "dist-packages"))
sitepackages.append(os.path.join(prefix, "lib",
                            "python" + sys.version[:3],
                            "dist-packages"))

Now I try to fig out where may be this odd problem comes from:

  1. $PYTHONPATH, impossible, because it is empty both A and B
  2. sys.prefix and sys.exec_prefix, maybe, please check them and as well as $PYTHONHOME
  3. site.py, maybe, check the file.

The sys.path output of B is quite odd, dist-package (site-package) goes before sys.exec_prefix (lib-dynload). Please try to investigate each step of sys.path initialization of machine B, you may find out something.

Very sorry that I cannot replicate your problem. By the way, about your question title, I think SYS.PATH is better than PYTHONPATH, which makes me misinterpretation as $PYTHONPATH at first glance.

2 of 5
2

If you peek into Python's site.py, which you can by opening /usr/lib/python2.7/site.py in a text editor.

The sys.path is augmented with directories for packages distributed within the distribution. Local addons go into /usr/local/lib/python/dist-packages, the global addons install into /usr/{lib,share}/python/dist-packages.

You can change the order by overriding this:

def getsitepackages():
    """Returns a list containing all global site-packages directories
    (and possibly site-python).

    For each directory present in the global ``PREFIXES``, this function
    will find its `site-packages` subdirectory depending on the system
    environment, and will return a list of full paths.
    """
    sitepackages = []
    seen = set()

    for prefix in PREFIXES:
        if not prefix or prefix in seen:
            continue
        seen.add(prefix)

        if sys.platform in ('os2emx', 'riscos'):
            sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
        elif os.sep == '/':
            sitepackages.append(os.path.join(prefix, "local/lib",
                                        "python" + sys.version[:3],
                                        "dist-packages"))
            sitepackages.append(os.path.join(prefix, "lib",
                                        "python" + sys.version[:3],
                                        "dist-packages"))
        else:
            sitepackages.append(prefix)
            sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
        if sys.platform == "darwin":
            # for framework builds *only* we add the standard Apple
            # locations.
            from sysconfig import get_config_var
            framework = get_config_var("PYTHONFRAMEWORK")
            if framework:
                sitepackages.append(
                        os.path.join("/Library", framework,
                            sys.version[:3], "site-packages"))
    return sitepackages
🌐
Microsoft Learn
learn.microsoft.com › en-us › visualstudio › python › search-paths
Use Python folders in search paths - Visual Studio (Windows) | Microsoft Learn
April 18, 2024 - In a typical Python program, the PYTHONPATH environment variable (or IRONPYTHONPATH, and so on) provides the default search path for module files. The from <name> import... or import <name> statements instruct Python to search the specific locations for files that match the specified <name>. The locations are searched in the following order:
🌐
sqlpey
sqlpey.com › python › python-path-methods
Python PATH: Methods to Add Custom Directories for Module Imports
November 4, 2025 - Virtual Environments: When using ... might interfere with other projects or the system Python installation. Order of Precedence: Python checks paths in the order they appear in sys.path....
Top answer
1 of 2
9

As you may know, sys.path is initialized from:

  • the current directory
  • your PYTHONPATH
  • an installation-dependent default

However unfortunately that is only part of the story: setuptools creates easy-install.pth files, which also modify sys.path and worst of all they prepend packages and therefore totally mess up the order of directories.

In particular (at least on my system), there is /usr/local/lib/python2.7/dist-packages/easy-install.pth with the following contents:

import sys; sys.__plen = len(sys.path)
/usr/lib/python2.7/dist-packages
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

This causes /usr/lib/python2.7/dist-packages to be prepended even before your PYTHONPATH!

What you could do is simply change the 2nd line in this file to

/usr/local/lib/python2.7/dist-packages

and you will get your desired priority.

However beware this file might be overwritten or changed again by a future setuptools invocation!

2 of 2
2

We encountered an almost identical situation and wanted to expand upon @kynan's response which is spot-on. In the case where you have such an easy-install.pth that you want to overcome, but which you cannot modify it (say you are a user with no root/admin access), you can do the following:

  • Set up an alternate python installation scheme

    • e.g. we use a PYTHON HOME install (setting PYTHONUSERBASE)
  • Create a user/home site-packages

    • You can do this by installing a package into the user env: pip install <package> --user
  • Create a pth to set sys.__egginsert to workaround the system/distribution easy-install.pth

    • Create a $PYTHONUSERBASE/lib/python2.7/site-packages/fix_easy_install.pth
    • Containing: import sys; sys.__egginsert = len(sys.path);

This will set sys.__egginsert to point to the end of your sys.path including your usersite paths. When the nefarious system/dist easy-install.pth will then insert its items at the end of the system path.

🌐
LinuxVox
linuxvox.com › blog › python-path-linux
Mastering Python Paths on Linux — linuxvox.com
It is initialized when the Python interpreter starts using the following order: The directory containing the input script (or the current directory when running interactively, via -c, or via -m). Directories listed in the PYTHONPATH environment ...
🌐
GitHub
github.com › Autodesk › maya-usd › issues › 23
USD and maya-usd paths insertion in "PYTHONPATH" order dependency. · Issue #23 · Autodesk/maya-usd
August 21, 2019 - Hi, I came across a quite annoying one: if USD is inserted before maya-usd in PYTHONPATH, the UsdMaya package cannot be imported. Changing the order of the paths fixed the issue. The problem reside...
Author: Autodesk
🌐
TutorialsPoint
tutorialspoint.com › article › What-is-PYTHONPATH-environment-variable-in-Python
What is PYTHONPATH environment variable in Python?
August 26, 2023 - PYTHONPATH is an environment variable that extends Python's module search path. When Python imports a module, it searches directories in this order:
🌐
Real Python
realpython.com › add-python-to-path
How to Add Python to PATH – Real Python
July 23, 2026 - Reordering PATH on Windows is relatively straightforward. You open the GUI control panel and adjust the order using the Move Up and Move Down buttons. If you’re on a UNIX-based operating system, however, the process is more involved.
🌐
Medium
medium.com › @trstringer › module-import-precedence-in-python-acad7e0bd57
Module Import Precedence in Python | by Thomas Stringer | Medium
June 5, 2020 - This blog post has permanently moved to https://trstringer.com/python-module-import-precedence/. “Module Import Precedence in Python” is published by Thomas Stringer.