兄弟包导入

发布于 2024-11-15 05:32:49 字数 652 浏览 3 评论 0原文

我尝试阅读有关兄弟导入的问题,甚至 包文档,但我还没有找到答案。

结构如下:

├── LICENSE.md
├── README.md
├── api
│   ├── __init__.py
│   ├── api.py
│   └── api_key.py
├── examples
│   ├── __init__.py
│   ├── example_one.py
│   └── example_two.py
└── tests
│   ├── __init__.py
│   └── test_one.py

examplestests 目录下的脚本如何从 api 模块并从命令行运行?

另外,我想避免对每个文件进行丑陋的 sys.path.insert hack。一定 这可以用 Python 完成,对吗?

I've tried reading through questions about sibling imports and even the
package documentation, but I've yet to find an answer.

With the following structure:

├── LICENSE.md
├── README.md
├── api
│   ├── __init__.py
│   ├── api.py
│   └── api_key.py
├── examples
│   ├── __init__.py
│   ├── example_one.py
│   └── example_two.py
└── tests
│   ├── __init__.py
│   └── test_one.py

How can the scripts in the examples and tests directories import from the
api module and be run from the commandline?

Also, I'd like to avoid the ugly sys.path.insert hack for every file. Surely
this can be done in Python, right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

软糯酥胸 2024-11-22 05:32:50

厌倦了 sys.path 黑客攻击?

有很多可用的 sys.path.append -hacks,但我找到了解决当前问题的替代方法。

摘要

  • 将代码打包到一个文件夹中(例如 packaged_stuff
  • 创建 pyproject.toml 文件来描述您的包(请参阅下面的最小 pyproject.toml
  • Pip使用 pip install -e 以可编辑状态安装软件包
  • 使用 from packaged_stuff.modulename import function_name

设置

起点是您的文件结构已提供,包装在名为 myproject 的文件夹中。

.
└── myproject
    ├── api
    │   ├── api_key.py
    │   ├── api.py
    │   └── __init__.py
    ├── examples
    │   ├── example_one.py
    │   ├── example_two.py
    │   └── __init__.py
    ├── LICENCE.md
    ├── README.md
    └── tests
        ├── __init__.py
        └── test_one.py

我将 . 称为根文件夹,在我的示例中,它位于 C:\tmp\test_imports\

api.py

作为测试用例,让我们使用以下 ./api/api.py

def function_from_api():
    return 'I am the return value from api.api!'

test_one.py

from api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

尝试运行 test_one:

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\myproject\tests\test_one.py", line 1, in <module>
    from api.api import function_from_api
ModuleNotFoundError: No module named 'api'

同时尝试相对导入也不起作用:

使用 from ..api.api import function_from_api将导致

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\tests\test_one.py", line 1, in <module>
    from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package

步骤

1) 在根级别目录中创​​建一个 pyproject.toml 文件

(以前人们使用 setup.py 文件)

最小 pyproject.toml 的内容将是*

[project]
name = "myproject"
version = "0.1.0"
description = "My small project"

[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]

2) 使用虚拟环境

如果您熟悉虚拟环境,请激活一个虚拟环境,然后跳到下一步。使用虚拟环境并不是绝对必需的,但它们会从长远来看,确实可以帮助您(当您有超过 1 个项目正在进行时......)。最基本的步骤是(在根文件夹中运行)

  • 创建虚拟环境
    • python -m venv venv
  • 激活虚拟环境
    • 源 ./venv/bin/activate(Linux、macOS)或 ./venv/Scripts/activate (Win)

要了解更多信息,只需 Google 出“python virtual env 教程”或类似内容。除了创建、激活和停用之外,您可能不需要任何其他命令。

创建并激活虚拟环境后,您的控制台应在括号中给出虚拟环境的名称

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

,您的文件夹树应如下所示**

.
├── myproject
│   ├── api
│   │   ├── api_key.py
│   │   ├── api.py
│   │   └── __init__.py
│   ├── examples
│   │   ├── example_one.py
│   │   ├── example_two.py
│   │   └── __init__.py
│   ├── LICENCE.md
│   ├── README.md
│   └── tests
│       ├── __init__.py
│       └── test_one.py
├── pyproject.toml
└── venv
    ├── Include
    ├── Lib
    ├── pyvenv.cfg
    └── Scripts [87 entries exceeds filelimit, not opening dir]

3) pip install your project in editable state

安装您的顶级包 myproject使用pip。诀窍是在安装时使用 -e 标志。这样它就会以可编辑状态安装,并且对 .py 文件所做的所有编辑都将自动包含在已安装的包中。 使用 pyproject.toml 和 -e 标志需要 pip >= 21.3

在根目录中,运行

pip install -e . (注意点,它代表“当前目录” ")

您还可以看到它是使用 pip freeze 安装的

Obtaining file:///home/user/projects/myproject
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: myproj
  Building editable for myproj (pyproject.toml) ... done
  Created wheel for myproj: filename=myproj-0.1.0-py2.py3-none-any.whl size=903 sha256=f19858b080d4e770c2a172b9a73afcad5f33f4c43c86e8eb9bdacbe50a627064
  Stored in directory: /tmp/pip-ephem-wheel-cache-qohzx1u0/wheels/55/5f/e4/507fdeb40cdef333e3e0a8c50c740a430b8ce84cbe17ae5875
Successfully built myproject
Installing collected packages: myproject
Successfully installed myproject-0.1.0
(venv) PS C:\tmp\test_imports> pip freeze
myproject==0.1.0

4) 将 myproject. 添加到您的导入中

请注意,您必须添加 myproject.代码>仅进入导入否则无法工作。无需 pyproject.toml & 即可导入pip install 仍然可以正常工作。请参阅下面的示例。


测试解决方案

现在,让我们使用上面定义的 api.py 和下面定义的 test_one.py 来测试该解决方案。

test_one.py

from myproject.api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

在这里使用 flit 作为构建后端运行测试

(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!

。还存在其他替代方案。

** 实际上,您可以将虚拟环境放置在硬盘上的任何位置。

Tired of sys.path hacks?

There are plenty of sys.path.append -hacks available, but I found an alternative way of solving the problem in hand.

Summary

  • Wrap the code into one folder (e.g. packaged_stuff)
  • Create pyproject.toml file to describe your package (see minimal pyproject.toml below)
  • Pip install the package in editable state with pip install -e <myproject_folder>
  • Import using from packaged_stuff.modulename import function_name

Setup

The starting point is the file structure you have provided, wrapped in a folder called myproject.

.
└── myproject
    ├── api
    │   ├── api_key.py
    │   ├── api.py
    │   └── __init__.py
    ├── examples
    │   ├── example_one.py
    │   ├── example_two.py
    │   └── __init__.py
    ├── LICENCE.md
    ├── README.md
    └── tests
        ├── __init__.py
        └── test_one.py

I will call the . the root folder, and in my example case it is located at C:\tmp\test_imports\.

api.py

As a test case, let's use the following ./api/api.py

def function_from_api():
    return 'I am the return value from api.api!'

test_one.py

from api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

Try to run test_one:

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\myproject\tests\test_one.py", line 1, in <module>
    from api.api import function_from_api
ModuleNotFoundError: No module named 'api'

Also trying relative imports wont work:

Using from ..api.api import function_from_api would result into

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\tests\test_one.py", line 1, in <module>
    from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package

Steps

1) Make a pyproject.toml file to the root level directory

(previously people used a setup.py file)

The contents for a minimal pyproject.toml would be*

[project]
name = "myproject"
version = "0.1.0"
description = "My small project"

[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]

2) Use a virtual environment

If you are familiar with virtual environments, activate one, and skip to the next step. Usage of virtual environments are not absolutely required, but they will really help you out in the long run (when you have more than 1 project ongoing..). The most basic steps are (run in the root folder)

  • Create virtual env
    • python -m venv venv
  • Activate virtual env
    • source ./venv/bin/activate (Linux, macOS) or ./venv/Scripts/activate (Win)

To learn more about this, just Google out "python virtual env tutorial" or similar. You probably never need any other commands than creating, activating and deactivating.

Once you have made and activated a virtual environment, your console should give the name of the virtual environment in parenthesis

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

and your folder tree should look like this**

.
├── myproject
│   ├── api
│   │   ├── api_key.py
│   │   ├── api.py
│   │   └── __init__.py
│   ├── examples
│   │   ├── example_one.py
│   │   ├── example_two.py
│   │   └── __init__.py
│   ├── LICENCE.md
│   ├── README.md
│   └── tests
│       ├── __init__.py
│       └── test_one.py
├── pyproject.toml
└── venv
    ├── Include
    ├── Lib
    ├── pyvenv.cfg
    └── Scripts [87 entries exceeds filelimit, not opening dir]

3) pip install your project in editable state

Install your top level package myproject using pip. The trick is to use the -e flag when doing the install. This way it is installed in an editable state, and all the edits made to the .py files will be automatically included in the installed package. Using pyproject.toml and -e flag requires pip >= 21.3

In the root directory, run

pip install -e . (note the dot, it stands for "current directory")

You can also see that it is installed by using pip freeze

Obtaining file:///home/user/projects/myproject
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: myproj
  Building editable for myproj (pyproject.toml) ... done
  Created wheel for myproj: filename=myproj-0.1.0-py2.py3-none-any.whl size=903 sha256=f19858b080d4e770c2a172b9a73afcad5f33f4c43c86e8eb9bdacbe50a627064
  Stored in directory: /tmp/pip-ephem-wheel-cache-qohzx1u0/wheels/55/5f/e4/507fdeb40cdef333e3e0a8c50c740a430b8ce84cbe17ae5875
Successfully built myproject
Installing collected packages: myproject
Successfully installed myproject-0.1.0
(venv) PS C:\tmp\test_imports> pip freeze
myproject==0.1.0

4) Add myproject. into your imports

Note that you will have to add myproject. only into imports that would not work otherwise. Imports that worked without the pyproject.toml & pip install will work still work fine. See an example below.


Test the solution

Now, let's test the solution using api.py defined above, and test_one.py defined below.

test_one.py

from myproject.api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

running the test

(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!

* here using flit as build backend. Other alternatives exist.

** In reality, you could put your virtual environment anywhere on your hard disk.

尛丟丟 2024-11-22 05:32:50

自从我写下答案七年后

,修改 sys.path 仍然是一个快速而肮脏的技巧,对于私有脚本效果很好,但已经有了一些改进

  • 安装包(无论是否在virtualenv中)都会给你你想要的,尽管我建议使用pip来完成它而不是直接使用setuptools(并且使用setup.cfg 来存储元数据)
  • 使用 -m 标志 并且作为包运行也可以(但如果您想将工作目录转换为可安装包,则会有点尴尬)。
  • 具体来说,对于测试, pytest 能够在这种情况下找到 api 包并处理 sys. path hacks for you

所以这实际上取决于你想做什么。不过,就您而言,由于您的目标似乎是在某个时候制作一个合适的软件包,因此通过 pip -e 安装可能是您最好的选择,即使它还不完美。

旧答案

正如其他地方已经指出的,可怕的事实是,您必须进行丑陋的黑客攻击才能允许从 __main__ 模块从兄弟模块或父包导入。 PEP 366 中详细介绍了该问题。 PEP 3122 试图以更合理的方式处理导入,但 Guido 拒绝了一个帐户

唯一的用例似乎是运行发生的脚本
位于模块的目录中,我一直将其视为
反模式。

此处

不过,我在定期使用

# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
    from sys import path
    from os.path import dirname as dir

    path.append(dir(path[0]))
    __package__ = "examples"

import api

这里 path[0] 是您正在运行的脚本的父文件夹,dir(path[0]) 是您的顶级文件夹。

不过,我仍然无法使用相对导入,但它确实允许从顶层(在您的示例 api 的父文件夹中)进行绝对导入。

Seven years after

Since I wrote the answer below, modifying sys.path is still a quick-and-dirty trick that works well for private scripts, but there has been several improvements

  • Installing the package (in a virtualenv or not) will give you what you want, though I would suggest using pip to do it rather than using setuptools directly (and using setup.cfg to store the metadata)
  • Using the -m flag and running as a package works too (but will turn out a bit awkward if you want to convert your working directory into an installable package).
  • For the tests, specifically, pytest is able to find the api package in this situation and takes care of the sys.path hacks for you

So it really depends on what you want to do. In your case, though, since it seems that your goal is to make a proper package at some point, installing through pip -e is probably your best bet, even if it is not perfect yet.

Old answer

As already stated elsewhere, the awful truth is that you have to do ugly hacks to allow imports from siblings modules or parents package from a __main__ module. The issue is detailed in PEP 366. PEP 3122 attempted to handle imports in a more rational way but Guido has rejected it one the account of

The only use case seems to be running scripts that happen
to be living inside a module's directory, which I've always seen as an
antipattern.

(here)

Though, I use this pattern on a regular basis with

# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
    from sys import path
    from os.path import dirname as dir

    path.append(dir(path[0]))
    __package__ = "examples"

import api

Here path[0] is your running script's parent folder and dir(path[0]) your top level folder.

I have still not been able to use relative imports with this, though, but it does allow absolute imports from the top level (in your example api's parent folder).

思慕 2024-11-22 05:32:50

这是我在 tests 文件夹中的 Python 文件顶部插入的另一个替代方案:

# Path hack.
import sys, os
sys.path.insert(0, os.path.abspath('..'))

Here is another alternative that I insert at top of the Python files in tests folder:

# Path hack.
import sys, os
sys.path.insert(0, os.path.abspath('..'))
南…巷孤猫 2024-11-22 05:32:50

您不需要也不应该破解 sys.path 除非有必要,但在本例中则不然。使用:

import api.api_key # in tests, examples

从项目目录运行:python -mtests.test_one

您可能应该将 tests (如果它们是 api 的单元测试)移到 api 内并运行 python -m api.test 来运行所有测试(假设有是 __main__.py) 或 python -m api.test.test_one 来运行 test_one

您还可以从 examples (它不是 Python 包)中删除 __init__.py 并在安装了 api 的 virtualenv 中运行示例,例如如果您有正确的 setup.py,virtualenv 中的 pip install -e . 将安装就地 api 包。

You don't need and shouldn't hack sys.path unless it is necessary and in this case it is not. Use:

import api.api_key # in tests, examples

Run from the project directory: python -m tests.test_one.

You should probably move tests (if they are api's unittests) inside api and run python -m api.test to run all tests (assuming there is __main__.py) or python -m api.test.test_one to run test_one instead.

You could also remove __init__.py from examples (it is not a Python package) and run the examples in a virtualenv where api is installed e.g., pip install -e . in a virtualenv would install inplace api package if you have proper setup.py.

情话墙 2024-11-22 05:32:50

对于 2023 年的读者:如果您对 pip install -e 没有信心:

TL;DR:脚本(通常是入口点)只能导入任何相同或低于其水平。

考虑这个层次结构,正如 Python 3 中的相对导入的答案所建议的那样:

MyProject
├── src
│   ├── bot
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── sib1.py
│   └── mod
│       ├── __init__.py
│       └── module1.py
└── main.py

从起点运行我们的程序 python main.py,我们在main.py中使用绝对导入(无前导点):

from src.bot import main


if __name__ == '__main__':
    main.magic_tricks()

简单的命令 bot/main.py,它利用显式相对导入来显示我们正在导入的内容,如下所示:

from .sib1 import my_drink                # Both are explicit-relative-imports.
from ..mod.module1 import relative_magic

def magic_tricks():
    # Using sub-magic
    relative_magic(in=["newbie", "pain"], advice="cheer_up")
    
    my_drink()
    # Do your work
    ...

原因如下:

  • 我们不想当我们想要运行我们的 Python 程序时,真诚地对“好吧,所以这是一个模块”感到害怕。
    • 因此我们使用绝对导入作为入口点main.py,这样我们就可以通过python main.py来运行我们的程序.
    • 在幕后,Python 将使用 sys.path 为我们解析包,但这也意味着我们想要导入的包可能会被任何其他包所取代由于 sys.path 中路径的顺序,名称相同,例如尝试 import test
  • 为了避免这些冲突,我们使用显式相对导入。
    • from ..mod 语法非常清楚地表明“我们正在导入我们自己的本地包”。
    • 但缺点是,当你想将模块作为脚本运行时,你需要再次考虑“好吧,所以这是一个模块”。
  • 最后,from ..mod部分意味着它将上升一级到MyProject/src

结论

  1. 将您的 main.py 脚本放在所有包 MyProject/src 的根目录旁边,并在 python main 中使用绝对导入.py 导入任何内容。没有人会创建名为 src 的包。
  2. 这些显式的相对导入将会起作用。
  3. 要运行模块,请使用 python -m ... 。

附录:有关将 src/ 下的任何文件作为脚本运行的更多信息?

那么你应该使用语法 python -m 并看看我的另一篇文章: ModuleNotFoundError: No module named 'sib1'

For readers in 2023: If you're not confident with pip install -e:

TL;DR: A script(usually an entry point) can only import anything the same or below its level.

Consider this hierarchy, as recommended by an answer from Relative imports in Python 3:

MyProject
├── src
│   ├── bot
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── sib1.py
│   └── mod
│       ├── __init__.py
│       └── module1.py
└── main.py

To run our program from the starting point with the simple command python main.py, we use absolute import (no leading dot(s)) in main.py here:

from src.bot import main


if __name__ == '__main__':
    main.magic_tricks()

The content of bot/main.py, which takes advantage of explicit relative imports to show what we're importing, looks like this:

from .sib1 import my_drink                # Both are explicit-relative-imports.
from ..mod.module1 import relative_magic

def magic_tricks():
    # Using sub-magic
    relative_magic(in=["newbie", "pain"], advice="cheer_up")
    
    my_drink()
    # Do your work
    ...

These are the reasonings:

  • We don't want to give "OK, so this is a module" a funk when we want to run our Python program, sincerely.
    • So we use absolute import for the entry point main.py, this way we can run our program by simply python main.py.
    • Behind the scene, Python will use sys.path to resolve packages for us, but this also means that the package we want to import can probably be superseded by any other package of the same name due to the ordering of paths in sys.path e.g. try import test.
  • To avoid those conflicts, we use explicit relative import.
    • The from ..mod syntax makes it very clear about "we're importing our own local package".
    • But the drawback is that you need to think about "OK, so this is a module" again when you want to run the module as a script.
  • Finally, the from ..mod part means that it will go up one level to MyProject/src.

Conclusion

  1. Put your main.py script next to the root of all your packages MyProject/src, and use absolute import in python main.py to import anything. No one will create a package named src.
  2. Those explicit relative imports will just work.
  3. To run a module, use python -m ....

Appendix: More about running any file under src/ as a script?

Then you should use the syntax python -m and take a look at my other post: ModuleNotFoundError: No module named 'sib1'

手心的海 2024-11-22 05:32:50

我还没有对 Pythonology 的理解,无法了解在不相关的项目之间共享代码而无需兄弟/相对导入 hack 的预期方式。直到那一天,这就是我的解决方案。对于从 ..\api 导入内容的 examplestests ,它看起来像:

import sys.path
import os.path
# Import from sibling directory ..\api
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..")
import api.api
import api.api_key

I don't yet have the comprehension of Pythonology necessary to see the intended way of sharing code amongst unrelated projects without a sibling/relative import hack. Until that day, this is my solution. For examples or tests to import stuff from ..\api, it would look like:

import sys.path
import os.path
# Import from sibling directory ..\api
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..")
import api.api
import api.api_key
晚风撩人 2024-11-22 05:32:50

对于兄弟包导入,您可以使用 [sys.path][2] 模块的 insertappend 方法:

if __name__ == '__main__' and if __package__ is None:
    import sys
    from os import path
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    import api

这将如果您按如下方式启动脚本,则可以工作:

python examples/example_one.py
python tests/test_one.py

另一方面,您也可以使用相对导入:

if __name__ == '__main__' and if __package__ is not None:
    import ..api.api

在这种情况下,您必须使用 '-m' 参数(请注意,在这种情况下,您不能给出 '.py' 扩展名):

python -m packageName.examples.example_one
python -m packageName.tests.test_one

当然,您可以混合使用两者方法,以便您的脚本无论如何调用都可以工作:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        import api
    else:
        import ..api.api

For siblings package imports, you can use either the insert or the append method of the [sys.path][2] module:

if __name__ == '__main__' and if __package__ is None:
    import sys
    from os import path
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    import api

This will work if you are launching your scripts as follows:

python examples/example_one.py
python tests/test_one.py

On the other hand, you can also use the relative import:

if __name__ == '__main__' and if __package__ is not None:
    import ..api.api

In this case you will have to launch your script with the '-m' argument (note that, in this case, you must not give the '.py' extension):

python -m packageName.examples.example_one
python -m packageName.tests.test_one

Of course, you can mix the two approaches, so that your script will work no matter how it is called:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        import api
    else:
        import ..api.api
我要还你自由 2024-11-22 05:32:50

你需要看看相关代码中的导入语句是如何编写的。如果examples/example_one.py使用以下导入语句:

import api.api

...那么它期望项目的根目录位于系统路径中。

在没有任何黑客攻击的情况下支持这一点的最简单方法(如您所说)是从顶级目录运行示例,如下所示:

PYTHONPATH=$PYTHONPATH:. python examples/example_one.py 

You need to look to see how the import statements are written in the related code. If examples/example_one.py uses the following import statement:

import api.api

...then it expects the root directory of the project to be in the system path.

The easiest way to support this without any hacks (as you put it) would be to run the examples from the top level directory, like this:

PYTHONPATH=$PYTHONPATH:. python examples/example_one.py 
输什么也不输骨气 2024-11-22 05:32:50

以防万一有人在 Eclipse 上使用 Pydev 到这里:您可以使用 Project->Properties 并设置 将同级的父路径(以及调用模块的父路径)添加为外部库文件夹左侧菜单下的外部库Pydev-PYTHONPATH。然后你可以从你的同级导入,例如fromsibling import some_class

Just in case someone using Pydev on Eclipse end up here: you can add the sibling's parent path (and thus the calling module's parent) as an external library folder using Project->Properties and setting External Libraries under the left menu Pydev-PYTHONPATH. Then you can import from your sibling, e. g. from sibling import some_class.

弥繁 2024-11-22 05:32:50

我想对 np8 提供的 解决方案发表评论,但我没有足够的声誉所以我只想提一下,您可以完全按照他们的建议创建一个 setup.py 文件,然后从项目根目录执行 pipenv install --dev -e .将其变成可编辑的依赖项。然后你的绝对导入将起作用,例如 from api.api import foo 并且你不必搞乱系统范围的安装。

文档

I wanted to comment on the solution provided by np8 but I don't have enough reputation so I'll just mention that you can create a setup.py file exactly as they suggested, and then do pipenv install --dev -e . from the project root directory to turn it into an editable dependency. Then your absolute imports will work e.g. from api.api import foo and you don't have to mess around with system-wide installations.

Documentation

哀由 2024-11-22 05:32:50

如果您使用 pytest,则 pytest 文档 描述了如何从单独的测试包引用源包的方法。

建议的项目目录结构是:

setup.py
src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

setup.py 文件的内容:

from setuptools import setup, find_packages

setup(name="PACKAGENAME", packages=find_packages())

可编辑模式安装软件包:

pip install -e .

pytest 文章引用Ionel Cristian Mărieş 的这篇博文

If you're using pytest then the pytest docs describe a method of how to reference source packages from a separate test package.

The suggested project directory structure is:

setup.py
src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

Contents of the setup.py file:

from setuptools import setup, find_packages

setup(name="PACKAGENAME", packages=find_packages())

Install the packages in editable mode:

pip install -e .

The pytest article references this blog post by Ionel Cristian Mărieș.

森罗 2024-11-22 05:32:50

我制作了一个示例项目来演示我如何处理这个问题,这确实是另一个 sys.path hack,如上所述。 Python 同级导入示例,依赖于:

if __name__ == '__main__': import os import sys sys.path.append(os.getcwd())

只要您的工作目录保留在 Python 项目的根目录下,这似乎就非常有效。

I made a sample project to demonstrate how I handled this, which is indeed another sys.path hack as indicated above. Python Sibling Import Example, which relies on:

if __name__ == '__main__': import os import sys sys.path.append(os.getcwd())

This seems to be pretty effective so long as your working directory remains at the root of the Python project.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文