How to Import python package from another directory?

后端 未结 2 1720
灰色年华
灰色年华 2021-01-06 09:13

I have a project that is structured as follows:

project
├── api
│   ├── __init__.py
│   └── api.py
├── instance
│   ├── __init__.py
│   └── config.py
├── pac         


        
相关标签:
2条回答
  • 2021-01-06 09:16

    You can add the parent directory to PYTHONPATH, in order to achieve that, you can use OS depending path in the "module search path" which is listed in sys.path. So you can easily add the parent directory like following:

    import sys
    sys.path.insert(0, '..')
    
    from instance import config
    

    Note that the previous code uses a relative path, so you must launch the file inside the same location or it will likely not work. To launch from anywhere, you can use the pathlib module.

    from pathlib import Path
    import sys
    path = str(Path(Path(__file__).parent.absolute()).parent.absolute())
    sys.path.insert(0, path)
    
    from instance import config
    

    However, the previous approach is more a hack than anything, in order to do things right, you'll first need to reshape your project structure according to this very detailed blog post python packaging, going for the recommended way with a src folder.

    • Your directory layout must look like this:
    project
    ├── CHANGELOG.rst
    ├── README.rst
    ├── requirements.txt
    ├── setup.py
    ├── src
    │   ├── api
    │   │   ├── api.py
    │   │   └── __init__.py
    │   ├── instance
    │   │   ├── config.py
    │   │   └── __init__.py
    │   └── package
    │       ├── app.py
    │       └── __init__.py
    └── tests
        └── __init__.py
    

    Note that you don't really need the requirements.txt because you can declare the dependencies inside your setup.py. A sample setup.py (adapted from here):

    #!/usr/bin/env python
    # -*- encoding: utf-8 -*-
    from __future__ import absolute_import
    from __future__ import print_function
    
    import io
    import re
    from glob import glob
    from os.path import basename
    from os.path import dirname
    from os.path import join
    from os.path import splitext
    
    from setuptools import find_packages
    from setuptools import setup
    
    
    def read(*names, **kwargs):
        with io.open(
            join(dirname(__file__), *names),
            encoding=kwargs.get('encoding', 'utf8')
        ) as fh:
            return fh.read()
    
    
    setup(
        name='nameless',
        version='1.644.11',
        license='BSD-2-Clause',
        description='An example package. Generated with cookiecutter-pylibrary.',
        author='mpr',
        author_email='contact@ionelmc.ro',
        packages=find_packages('src'),
        package_dir={'': 'src'},
        include_package_data=True,
        zip_safe=False,
        classifiers=[
            # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers
            'Development Status :: 5 - Production/Stable',
            'Intended Audience :: Developers',
            'License :: OSI Approved :: BSD License',
            'Operating System :: Unix',
            'Operating System :: POSIX',
            'Operating System :: Microsoft :: Windows',
            'Programming Language :: Python',
            'Programming Language :: Python :: 2.7',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.5',
            'Programming Language :: Python :: 3.6',
            'Programming Language :: Python :: 3.7',
            'Programming Language :: Python :: 3.8',
            'Programming Language :: Python :: Implementation :: CPython',
            'Programming Language :: Python :: Implementation :: PyPy',
            # uncomment if you test on these interpreters:
            # 'Programming Language :: Python :: Implementation :: IronPython',
            # 'Programming Language :: Python :: Implementation :: Jython',
            # 'Programming Language :: Python :: Implementation :: Stackless',
            'Topic :: Utilities',
        ],
        keywords=[
            # eg: 'keyword1', 'keyword2', 'keyword3',
        ],
        python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
        install_requires=[
            # eg: 'aspectlib==1.1.1', 'six>=1.7',
        ],
        extras_require={
            # eg:
            #   'rst': ['docutils>=0.11'],
            #   ':python_version=="2.6"': ['argparse'],
        },
        setup_requires=[
            # 'pytest-runner',
        ],
        entry_points={
            'console_scripts': [
                'api = api.api:main',
            ]
        },
    )
    

    The content of my api.py:

    from instance import config
    
    def main():
        print("imported")
        config.config()
    

    The content of my config.py:

    def config():
        print("config imported successfully")
    

    You can find all the previous here

    • Optional but recommended: create a virtual environment, I use venv (Python 3.3 <=) for that, inside the root of the project:
    python -m venv .
    

    And to activate:

    source bin/activate
    
    • Now I can install the package:

    Using pip install -e . (with the dot) command inside the root of the project

    • Your import from instance import config works now, to confirm you can run api.py with:
    python src/api/api.py
    
    0 讨论(0)
  • 2021-01-06 09:36

    In Python 3.3 onwards you don't need __init__.py files in your subdirectories for the purpose of imports. Having them can actually be misleading as it causes the creation of package namespaces in each folder containing an init file, as described here.

    By removing all those __init__.py files you will be able to import files in the namespace package (including subdirectories) when running app.py, but that's still not want we want.

    The Python interpreter still doesn't know how to reach your instance namespace. In order to do that you can use the PYTHONPATH environment variable, including a path that is parent to config.py. You can do that as suggested in @RMPR's answer with sys.path, or by directly setting the environment variable, for instance:

    PYTHONPATH=/home/csymvoul/projects/project python3 /home/csymvoul/projects/project/package/app.py
    

    Then importing the dependency like from instance import config.

    0 讨论(0)
提交回复
热议问题