How to import a Python class that is in a directory above?
Asked Answered
Z

11

312

I want to inherit from a class in a file that lies in a directory above the current one.

Is it possible to relatively import that file?

Zeist answered 28/6, 2009 at 4:56 Comment(0)
G
246

from ..subpkg2 import mod

Per the Python docs: When inside a package hierarchy, use two dots, as the import statement doc says:

When specifying what module to import you do not have to specify the absolute name of the module. When a module or package is contained within another package it is possible to make a relative import within the same top package without having to mention the package name. By using leading dots in the specified module or package after from you can specify how high to traverse up the current package hierarchy without specifying exact names. One leading dot means the current package where the module making the import exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute from . import mod from a module in the pkg package then you will end up importing pkg.mod. If you execute from ..subpkg2 import mod from within pkg.subpkg1 you will import pkg.subpkg2.mod. The specification for relative imports is contained within PEP 328.

PEP 328 deals with absolute/relative imports.

Gaspar answered 28/6, 2009 at 5:1 Comment(6)
up1 = os.path.abspath('..') sys.path.insert(0, up1)Coatbridge
Pep 328 shows only Python-Version: 2.4, 2,5, 2.6. Version 3 is left to more knowledgeable souls .Gaspar
This triggers error ValueError: attempted relative import beyond top-level packagePicard
Results in ImportError: attempted relative import with no known parent packageSappy
@Sappy See https://mcmap.net/q/14223/-relative-imports-for-the-billionth-time. You might need to use python -m to run the script from the parent dir and use absolute imports. There's a variety of tricks you can use, try printing name, package, and sys.path to help you debug.Twitch
I was facing a similar issue, but in my case I have created a module with hyphen in the name like "validation-lib". I had to rename it to "validation_lib" to make it work.Dreda
A
199
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Airlee answered 19/6, 2012 at 8:6 Comment(6)
this works for me. After adding this, I can directly import parent modules, no need to use "..".Subgroup
that only works, roughly speaking, if the application's PWD value - its current directeroy, is a child of the parent. So, even if it works, many kinds of systemic changes can dislodge it.Thom
This worked for me as well for importing modules of higher level. I use this with os.chdir("..") to load other files of higher level.Harken
This seems to trigger the same error as the above answer from gimel.Picard
Please don't do this. If you need to import a higher directory or any other directory reached via .. then what you have done is create a package, so you should build that package, and manage installing it locally on your system with a Virtual Environment. More information: See this answer: #77507889Bunk
Manually editing sys.path is a red flag. For a start it then means that (unless you explicitly remove ".." from sys.path after the operation) that for the rest of your run you app will have the option of looking for files to import from there, if it fails to find a file from an earlier entry in sys.path. So it's overkill and dangerous. And there other ways to accomplish what you want to do.Wye
L
104

@gimel's answer is correct if you can guarantee the package hierarchy he mentions. If you can't -- if your real need is as you expressed it, exclusively tied to directories and without any necessary relationship to packaging -- then you need to work on __file__ to find out the parent directory (a couple of os.path.dirname calls will do;-), then (if that directory is not already on sys.path) prepend temporarily insert said dir at the very start of sys.path, __import__, remove said dir again -- messy work indeed, but, "when you must, you must" (and Pyhon strives to never stop the programmer from doing what must be done -- just like the ISO C standard says in the "Spirit of C" section in its preface!-).

Here is an example that may work for you:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir
Lastex answered 28/6, 2009 at 5:7 Comment(2)
this might add a directory that is inside a Python package to sys.path thus making the same module available under different names and all corresponding bugs. autopath.py in pypy or _preamble.py in twisted solve it by using a search criteria that identifies the top-level package while traversing the directories upwards.Jokjakarta
You might want to do something like sys.path.remove(pathYouJustAdded) after the import you needed so not as to retain this new path.Burkey
A
31

Import module from a directory which is exactly one level above the current directory:

from .. import module
Airlee answered 16/4, 2014 at 8:5 Comment(4)
I got: attempted relative import beyond top-level package :(Recaption
using from .. import module I got error ValueError: Attempted relative import in non-package by following the recommendationDunk
ImportError: attempted relative import with no known parent package Getting this errorOry
That is because you need to run your script as a module @Dunk and SayAz.Rivers
S
12

How to load a module that is a directory up

preface: I did a substantial rewrite of a previous answer with the hopes of helping ease people into python's ecosystem, and hopefully give everyone the best change of success with python's import system.

This will cover relative imports within a package, which I think is the most probable case to OP's question.

Python is a modular system

This is why we write import foo to load a module "foo" from the root namespace, instead of writing:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python isn't coupled to a file-system

This is why we can embed python in environment where there isn't a defacto filesystem without providing a virtual one, such as Jython.

Being decoupled from a filesystem lets imports be flexible, this design allows for things like imports from archive/zip files, import singletons, bytecode caching, cffi extensions, even remote code definition loading.

So if imports are not coupled to a filesystem what does "one directory up" mean? We have to pick out some heuristics but we can do that, for example when working within a package, some heuristics have already been defined that makes relative imports like .foo and ..foo work within the same package. Cool!

If you sincerely want to couple your source code loading patterns to a filesystem, you can do that. You'll have to choose your own heuristics, and use some kind of importing machinery, I recommend importlib

Python's importlib example looks something like so:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

Packaging

There is a great example project available officially here: https://github.com/pypa/sampleproject

A python package is a collection of information about your source code, that can inform other tools how to copy your source code to other computers, and how to integrate your source code into that system's path so that import foo works for other computers (regardless of interpreter, host operating system, etc)

Directory Structure

Lets have a package name foo, in some directory (preferably an empty directory).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

My preference is to create setup.py as sibling to foo.py, because it makes writing the setup.py file simpler, however you can write configuration to change/redirect everything setuptools does by default if you like; for example putting foo.py under a "src/" directory is somewhat popular, not covered here.

some_directory/
    foo.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

"editable" aka -e will yet-again redirect the importing machinery to load the source files in this directory, instead copying the current exact files to the installing-environment's library. This can also cause behavioral differences on a developer's machine, be sure to test your code! There are tools other than pip, however I'd recommend pip be the introductory one :)

I also like to make foo a "package" (a directory containing __init__.py) instead of a module (a single ".py" file), both "packages" and "modules" can be loaded into the root namespace, modules allow for nested namespaces, which is helpful if we want to have a "relative one directory up" import.

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

I also like to make a foo/__main__.py, this allows python to execute the package as a module, eg python3 -m foo will execute foo/__main__.py as __main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

Lets flesh this out with some more modules: Basically, you can have a directory structure like so:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py conventionally holds metadata information about the source code within, such as:

  • what dependencies are needed to install named "install_requires"
  • what name should be used for package management (install/uninstall "name"), I suggest this match your primary python package name in our case foo, though substituting underscores for hyphens is popular
  • licensing information
  • maturity tags (alpha/beta/etc),
  • audience tags (for developers, for machine learning, etc),
  • single-page documentation content (like a README),
  • shell names (names you type at user shell like bash, or names you find in a graphical user shell like a start menu),
  • a list of python modules this package will install (and uninstall)
  • a defacto "run tests" entry point python ./setup.py test

Its very expansive, it can even compile c extensions on the fly if a source module is being installed on a development machine. For a every-day example I recommend the PYPA Sample Repository's setup.py

If you are releasing a build artifact, eg a copy of the code that is meant to run nearly identical computers, a requirements.txt file is a popular way to snapshot exact dependency information, where "install_requires" is a good way to capture minimum and maximum compatible versions. However, given that the target machines are nearly identical anyway, I highly recommend creating a tarball of an entire python prefix. This can be tricky, too detailed to get into here. Check out pip install's --target option, or virtualenv aka venv for leads.

back to the example

how to import a file one directory up:

From foo/spam/eggs.py, if we wanted code from foo/baz we could ask for it by its absolute namespace:

import foo.baz

If we wanted to reserve capability to move eggs.py into some other directory in the future with some other relative baz implementation, we could use a relative import like:

import ..baz
Sybyl answered 11/4, 2020 at 1:40 Comment(2)
Just to be clear, are you saying to use import foo.baz or import ..baz, I would need to run python3 -m pip install --editable FILEPATH on a properly made setup.py file? It's surprising that python needs so much overhead to just do an import from above . . .Vegetative
I did all that and now I can import foo from any python script anywhere on my computer. Is there some way to import from a file above without importlib and all that boiler plate?Vegetative
V
6

Here's a three-step, somewhat minimalist version of ThorSummoner's answer for the sake of clarity. It doesn't quite do what I want (I'll explain at the bottom), but it works okay.

Step 1: Make directory and setup.py

filepath_to/project_name/
    setup.py

In setup.py, write:

import setuptools

setuptools.setup(name='project_name')

Step 2: Install this directory as a package

Run this code in console:

python -m pip install --editable filepath_to/project_name

Instead of python, you may need to use python3 or something, depending on how your python is installed. Also, you can use -e instead of --editable.

Now, your directory will look more or less like this. I don't know what the egg stuff is.

filepath_to/project_name/
    setup.py
    test_3.egg-info/
        dependency_links.txt
        PKG-INFO
        SOURCES.txt
        top_level.txt

This folder is considered a python package and you can import from files in this parent directory even if you're writing a script anywhere else on your computer.

Step 3. Import from above

Let's say you make two files, one in your project's main directory and another in a sub directory. It'll look like this:

filepath_to/project_name/
    top_level_file.py
    subdirectory/
        subfile.py

    setup.py          |
    test_3.egg-info/  |----- Ignore these guys
        ...           |

Now, if top_level_file.py looks like this:

x = 1

Then I can import it from subfile.py, or really any other file anywhere else on your computer.

# subfile.py  OR  some_other_python_file_somewhere_else.py

import random # This is a standard package that can be imported anywhere.
import top_level_file # Now, top_level_file.py works similarly.

print(top_level_file.x)

This is different than what I was looking for: I hoped python had a one-line way to import from a file above. Instead, I have to treat the script like a module, do a bunch of boilerplate, and install it globally for the entire python installation to have access to it. It's overkill. If anyone has a simpler method than doesn't involve the above process or importlib shenanigans, please let me know.

Vegetative answered 24/8, 2020 at 9:8 Comment(1)
you could always do this inside of a virtual environment, instead of installing globally (the virtual environment your project is using ) by using env\Scripts\python ... or env/bin/pythonHulking
C
4

Polished answer of @alex-martelli with pathlib:

import pathlib
import sys

_parentdir = pathlib.Path(__file__).parent.parent.resolve()
sys.path.insert(0, str(_parentdir))

import module_in_parent_dir

sys.path.remove(str(_parentdir))
Costello answered 3/5, 2021 at 1:1 Comment(0)
W
3

It is 2022 and none of the answers really worked for me. Here is what worked in the end

import sys
sys.path.append('../my_class')
import my_class

My directory structure:

src
--my_class.py
notebooks
-- mynotebook.ipynb

I imported my_class from mynotebook.ipynb.

Wickham answered 15/12, 2022 at 14:31 Comment(0)
H
2

To run python /myprogram/submodule/mymodule.py which imports /myprogram/mainmodule.py, e.g., via

from mainmodule import *

on Linux (e.g., in the python Docker image), I had to add the program root directory to PYTHONPATH:

export PYTHONPATH=/myprogram
Herald answered 11/8, 2021 at 16:35 Comment(0)
G
0

You can use the sys.path.append() method to add the directory containing the package to the list of paths searched for modules. For example, if the package is located two directories above the current directory, you can use the following code:

import sys
sys.path.append("../../")

if the package is location one directory above the current directory, you can use below code:

import sys
sys.path.append("..")
Glister answered 19/1, 2023 at 13:7 Comment(0)
S
-6

Python is a modular system

Python doesn't rely on a file system

To load python code reliably, have that code in a module, and that module installed in python's library.

Installed modules can always be loaded from the top level namespace with import <name>


There is a great sample project available officially here: https://github.com/pypa/sampleproject

Basically, you can have a directory structure like so:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

Be sure to declare your setuptools.setup() in setup.py,

official example: https://github.com/pypa/sampleproject/blob/master/setup.py

In our case we probably want to export bar.py and foo/__init__.py, my brief example:

setup.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

Now we can install our module into the python library; with pip, you can install the_foo_project into your python library in edit mode, so we can work on it in real time

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

Now from any python context, we can load our shared py_modules and packages

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
Sybyl answered 16/6, 2015 at 7:10 Comment(2)
should mention I always install my module while I work on it with pip install --edit foo, almost always inside a virtualenv. I almost never write a module that is not intended to be installed. if I misunderstand something I would like to know.Sybyl
I should also mention that using a venv-auto-creation test suite, like tox is very helpful as editable egg links and installed python modules are not exactly the same in every way; for example, adding a new namespace to an editable module will be found in your path lookup, but if that isn't exported in your setup.py file it won't be packaged/installed! Test your use case :)Sybyl

© 2022 - 2024 — McMap. All rights reserved.