Как импортировать пакет python из другого каталога? - PullRequest
4 голосов
/ 15 апреля 2020

У меня есть проект, который структурирован следующим образом:

project
├── api
│   ├── __init__.py
│   └── api.py
├── instance
│   ├── __init__.py
│   └── config.py
├── package
│   ├── __init__.py
│   └── app.py
├── requirements.txt
└── tests
    └── __init__.py

Я пытаюсь вызвать файл config.py из package/app.py, как показано ниже:

# package/app.py
from instance import config

# I've also tried
import instance.config
import ..instance.config
from ..instance import config

Но я всегда получаю следующую ошибку:

Traceback (most recent call last):
  File "/home/csymvoul/projects/project/package/app.py", line 1, in <module>
    from instance import config
ModuleNotFoundError: No module named 'instance'

Изменение sys.path - это не то, что я хочу сделать. Я знаю, что на этот вопрос очень много ответов, но ответы, которые были даны, не сработали для меня.

РЕДАКТИРОВАТЬ: При перемещении app.py в папку root он работает просто отлично. Но мне нужно, чтобы он был в папке package.

Ответы [ 2 ]

3 голосов
/ 15 апреля 2020

Вы можете добавить родительский каталог к ​​PYTHONPATH, для этого вы можете использовать путь, зависящий от ОС, в «пути поиска модуля», который указан в sys.path. Таким образом, вы можете легко добавить родительский каталог следующим образом:

import sys
sys.path.insert(0, '..')

from instance import config

Обратите внимание, что предыдущий код использует относительный путь, поэтому вы должны запустить файл в том же месте, иначе он, скорее всего, не будет работать. Для запуска из любого места вы можете использовать модуль pathlib.

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

from instance import config

Однако предыдущий подход - это скорее взлом, чем что-либо другое. Чтобы сделать все правильно, сначала вам нужно изменить свою форму. структура проекта в соответствии с этим очень подробным сообщением в блоге python упаковка , рекомендуемый путь с папкой src.

  • Ваш макет каталога должен выглядеть следующим образом:
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

Обратите внимание, что вам действительно не нужен requirements.txt, потому что вы можете объявить зависимости внутри вашего setup.py. Образец setup.py (адаптированный от здесь ):

#!/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',
        ]
    },
)

Содержимое моего api.py:

from instance import config

def main():
    print("imported")
    config.config()

Содержимое моего config.py :

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

Вы можете найти все предыдущие здесь

  • Необязательно, но рекомендуется: создать виртуальную среду, я использую venv (Python 3.3 <=) для этого внутри root проекта: </li>
python -m venv .

И для активации:

source bin/activate
  • Теперь я могу установить пакет :

Использование команды pip install -e . (с точкой) внутри root проекта

  • Ваш импорт from instance import config теперь работает , для подтверждения вы можете запустить api.py с:
python src/api/api.py
1 голос
/ 15 апреля 2020

В Python 3.3 и далее вам не нужно __init__.py файлов в ваших подкаталогах для целей импорта. Их наличие может вводить в заблуждение, поскольку это вызывает создание пространств имен пакетов в каждой папке, содержащей файл инициализации, как описано здесь .

Удалив все эти __init__.py файлы, вы сможете импортировать файлы в пространство имен package (включая подкаталоги) при запуске app.py, но это по-прежнему нежелательно.

Интерпретатор Python все еще не знает, как добраться до вашего пространства имен instance. Для этого вы можете использовать переменную окружения PYTHONPATH, включая путь, родительский для config.py. Вы можете сделать это, как предложено в ответе @ RMPR с помощью sys.path, или напрямую установив переменную среды, например:

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

Затем импортировать зависимость, например from instance import config.

...