我有一个名为foobar的文件(没有.py扩展名)。在同一目录中,我还有另一个python文件试图将其导入:
import foobar
但这仅在我将文件重命名为foobar.py时有效。是否可以导入不带.py扩展名的python模块?
更新:该文件没有扩展名,因为我也将其用作独立脚本,并且我不想键入.py扩展名来运行它。
Update2:我将使用下面提到的symlink解决方案。
我有一个名为foobar的文件(没有.py扩展名)。在同一目录中,我还有另一个python文件试图将其导入:
import foobar
但这仅在我将文件重命名为foobar.py时有效。是否可以导入不带.py扩展名的python模块?
更新:该文件没有扩展名,因为我也将其用作独立脚本,并且我不想键入.py扩展名来运行它。
Update2:我将使用下面提到的symlink解决方案。
pysh
扩展名...
ConfigParser
。wiki.python.org/moin/ConfigParserExamples
Answers:
您可以使用该imp.load_source
功能(来自imp
模块)从给定的文件系统路径动态加载模块。
import imp
foobar = imp.load_source('foobar', '/path/to/foobar')
该SO讨论还显示了一些有趣的选项。
'foobar'
如果在返回值中分配了第一个参数,则有什么用?
__name__
属性;通常这是由文件名确定的,但是由于您使用的是非标准文件名(它甚至可能根本不包含任何有效的python标识符),因此您必须指定模块名称。存储对创建的模块对象的引用的变量名称无关紧要,就像您import foo.bar as baz
通过该变量引用的模块baz
仍然具有原始名称一样__name__
。
这是Python 3.4+的解决方案:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("foobar", SourceFileLoader("foobar", "/path/to/foobar"))
foobar = module_from_spec(spec)
spec.loader.exec_module(foobar)
使用spec_from_loader
并明确指定aSourceFileLoader
将迫使机器将文件作为源加载,而无需尝试从扩展名中找出文件的类型。这意味着即使文件未在中列出,您也可以加载该文件importlib.machinery.SOURCE_SUFFIXES
。
如果要在第一次加载后继续按名称导入文件,请将模块添加到sys.modules
:
sys.modules['foobar'] = foobar
foobar = importlib.nice_import('foobar')
助手。
/some/arbitrary/file.weird_extension
是“不错”。话虽这么说,一旦发现这一点,我就开始对所有配置文件使用python代码。真方便。
globals().update(foobar.__dict__)
,但我建议不要这样做。
就像其他人提到的那样,您可以使用imp.load_source,但是这会使您的代码更难以阅读。我真的只建议您在需要导入其名称或路径在运行时才知道的模块时使用。
您不想使用.py扩展名的原因是什么?不想使用.py扩展名的最常见情况是,因为python脚本也作为可执行文件运行,但是您仍然希望其他模块能够导入它。如果是这种情况,将功能移动到具有类似名称的.py文件中,然后foobar
用作包装器可能会有所帮助。
bin/
目录,则从路径运行该程序将无法工作。
importlib
辅助功能
这里是一个方便,随时可以使用的辅助来代替imp
,用一个例子,基于什么在提到:https://stackoverflow.com/a/43602645/895245
main.py
#!/usr/bin/env python3
import os
import importlib
def import_path(path):
module_name = os.path.basename(path).replace('-', '_')
spec = importlib.util.spec_from_loader(
module_name,
importlib.machinery.SourceFileLoader(module_name, path)
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[module_name] = module
return module
notmain = import_path('not-main')
print(notmain)
print(notmain.x)
非主要
x = 1
跑:
python3 main.py
输出:
<module 'not_main' from 'not-main'>
1
我替换为-
,_
因为我的不带扩展名的可导入Python可执行文件带有连字符。这不是强制性的,但会产生更好的模块名称。
在以下文档中也提到了这种模式:https : //docs.python.org/3.7/library/importlib.html#importing-a-source-file-direct
我最终转向它,因为在更新到Python 3.7后,import imp
打印:
DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
我不知道如何关闭它,有人问到:
在Python 3.7.3中测试。
如果使用程序包管理器(deb或类似程序)安装脚本,则另一种选择是使用setuptools:
“ ...在Windows和POSIX平台上,没有一种简单的方法来使脚本的文件名与本地约定相匹配。另一方面,当实际的“ main”是一个“ main”脚本时,通常必须为“ main”脚本创建一个单独的文件。在某处的模块中起作用... setuptools通过自动为您生成具有正确扩展名的脚本来解决所有这些问题,在Windows上它甚至会创建一个.exe文件...”
https://pythonhosted.org/setuptools/setuptools.html#automatic-script-creation
我已经尝试了上面的所有建议,但在我想对没有.py
扩展名的cli脚本进行pytest的简单情况下,找不到解决方案。
为什么spec.loader.exec_module()
在“ .
”上引发IsADirectoryError ?
我在目录中有两个文件:
% ls
cli_with_no_dot_py test_cli_with_no_dot_py.py
% cat cli_with_no_dot_py
#!/usr/bin/env python3
cool_thing = True
print("cool_thing is", cool_thing)
% ./cli_with_no_dot_py
cool_thing is True
% cat test_cli_with_no_dot_py.py
#!/usr/bin/env python3
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("cli_with_no_dot_py",
SourceFileLoader("cli_with_no_dot_py", "."))
print("spec:", spec)
cli_with_no_dot_py = module_from_spec(spec)
print("cli_with_no_dot_py):", cli_with_no_dot_py)
print("dir(cli_with_no_dot_py:", dir(cli_with_no_dot_py))
print("spec.loader:", spec.loader)
spec.loader.exec_module(cli_with_no_dot_py) # !!! IsADirectoryError !!!
def test_cool_thing():
print("cli_with_no_dot_py.cool_thing is", cli_with_no_dot_py.cool_thing)
assert cli_with_no_dot_py.cool_thing
% pytest
======================================================================== test session starts ========================================================================
platform darwin -- Python 3.8.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /Users/cclauss/Python/pytest_a_cli_tool
collected 0 items / 1 error
============================================================================== ERRORS ===============================================================================
____________________________________________________________ ERROR collecting test_cli_with_no_dot_py.py ____________________________________________________________
test_cli_with_no_dot_py.py:11: in <module>
spec.loader.exec_module(cli_with_no_dot_py)
<frozen importlib._bootstrap_external>:779: in exec_module
???
<frozen importlib._bootstrap_external>:915: in get_code
???
<frozen importlib._bootstrap_external>:972: in get_data
???
E IsADirectoryError: [Errno 21] Is a directory: '.'
-------------------------------------------------------------------------- Captured stdout --------------------------------------------------------------------------
spec: ModuleSpec(name='cli_with_no_dot_py', loader=<_frozen_importlib_external.SourceFileLoader object at 0x10e106f70>, origin='.')
cli_with_no_dot_py: <module 'cli_with_no_dot_py' from '.'>
dir(cli_with_no_dot_py): ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
spec.loader: <_frozen_importlib_external.SourceFileLoader object at 0x10e106f70>
====================================================================== short test summary info ======================================================================
ERROR test_cli_with_no_dot_py.py - IsADirectoryError: [Errno 21] Is a directory: '.'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================================= 1 error in 0.09s ==========================================================================
py
扩展名的python文件?