简体   繁体   English

Python - 提取为字符串的命令行参数 ImportError

[英]Python - command line argument extracted as a string ImportError

I am trying to do the following:我正在尝试执行以下操作:

In CMD:在 CMD 中:

python __main__.py 127.0.0.1

The in main .py:.py:

address = sys.argv[1]

Then in my config.py file, I am importing the address like this:然后在我的 config.py 文件中,我像这样导入地址:

from __main__.py import address

...

EXAMPLE_URL = f"http://{address}/login"

And I am using the URL in a simple test scenario, which is imported from config and I get the following error:我在一个简单的测试场景中使用 URL,它是从 config 导入的,我收到以下错误:

ImportError: cannot import name 'address' from '__main__' (__main__.py)

This is my directory structure:这是我的目录结构:

QA System/
├── config/
│   ├── config.py
│   ├── __init__.py
├── .... some other unneccessary stuff
└── tests/
    ├── test_scenarios
       ├── test_scenario_01.py
       ├── test_scenario_02.py
       ├── __init__.py
    |── test_suite.py
    |── __init__.py
|
|-- __main__.py   < --- I launch tests from here
|-- __init__.py

It seems the error is in config file during the imports, but I cannot understand where the error is.在导入过程中,错误似乎在配置文件中,但我不明白错误在哪里。 Thanks in advance!提前致谢!

main .py file:.py 文件:

import argparse
import sys

from tests.test_suite import runner

if __name__ == "__main__":
    address = str(sys.argv[1])
    runner()   # This runs the tests, and the tests also use config.py for
                 various settings, I am worried something happens with the
                 imports there.

You have a circular import你有一个循环导入

When you import a module in Python, say, import __main__ as in your example, a module object is created for the namespace of the module, which is initially empty.当您在 Python 中导入一个模块时,例如像您的示例中那样import __main__ ,会为module的名称空间创建一个module对象,该名称空间最初是空的。 Then, as the code in the body of the module is executed--variables assigned, functions and classes defined, etc. that namespace gets filled in in order.然后,随着模块主体中的代码被执行——分配的变量、定义的函数和类等,命名空间按顺序填充。 Eg take the following script:例如,采用以下脚本:

$ cat a.py
print(locals())
an_int = 1

print("after an_int = 1")
print(locals())

def a_func(): pass
print("after def a_func(): pass")
print(locals())

Then run it:然后运行它:

$ python a.py
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__file__': 'a.py', '__doc__': None, '__package__': None}
after an_int = 1
{'an_int': 1, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}
after def a_func(): pass
{'an_int': 1, 'a_func': <function a_func at 0x6ffffeed758>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}

You can see the namespace gets filled in line-by-line.您可以看到命名空间被逐行填充。

Now say we modify it like:现在说我们修改它:

$ cat a.py
print(locals())
an_int = 1

print("after an_int = 1")
print(locals())

import b
print("after import b")

def a_func(): pass
print("after def a_func(): pass")
print(locals())

and add b.py :并添加b.py

$ cat b.py
import sys
print('a is in progress of being imported:', sys.modules['a'])
print("is a_func defined in a? `'a_func' in sys.modules['a'].__dict__`:",
      'a_func' in sys.modules['a'].__dict__)
from a import a_func

and run it like:并运行它:

python -c 'import a'

You'll get some output ending in a Traceback:你会得到一些以 Traceback 结尾的输出:

...
after an_int = 1
{'an_int': 1, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}
a is in progress of being imported: <module 'a' from '/path/to/a.py'>
is a_func defined in a? `'a_func' in sys.modules['a'].__dict__`: False
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "a.py", line 7, in <module>
    import b
  File "b.py", line 1, in <module>
    from a import a_func
ImportError: cannot import name 'a_func'

If however you move import b to after the definition of a_func :但是,如果您将import b移动到a_func的定义a_func

$ cat a.py
print(locals())
an_int = 1

print("after an_int = 1")
print(locals())

def a_func(): pass
print("after def a_func(): pass")
print(locals())

import b
print("after import b")

And again run python -c 'import a' you'll see it works, and the output ends with "after import b".再次运行python -c 'import a'你会看到它起作用了,输出以“after import b”结尾。

BONUS QUESTION : Why did I run python -c 'import a' and not just python a.py ?额外问题:为什么我运行python -c 'import a'而不仅仅是python a.py If you try the latter, the previous version will actually work and will appear to import a.py twice.如果您尝试后者,以前的版本实际上可以工作,并且会出现两次导入a.py This is because when you run python somemodule.py it is not imported initially as somemodule , but rather as __main__ .这是因为当您运行python somemodule.py它最初不是作为somemodule导入的,而是作为__main__导入的。 So from the import system's perspective the a module has not been imported yet when running from a import a_func .因此,从导入系统的角度来看, from a import a_func运行from a import a_func没有导入a模块。 A very confusing caveat.一个非常令人困惑的警告。


So in your case if you have something like __main__.py :所以在你的情况下,如果你有类似__main__.py东西:

import config
address = 1

and in config.py :config.py

from __main__ import address

when you run python __main__.py , by the time it runs import config , address isn't assigned yet, so the code in config that tries to import address from __main__ results in an ImportError .当您运行python __main__.py ,当它运行import configaddress尚未分配,因此config中尝试从__main__导入address的代码导致ImportError

In your case it's a little more complicated because you aren't importing config directly in __main__ from what it looks like, but indirectly that's still what's happening.在您的情况下,它有点复杂,因为您没有直接在__main__从它的外观导入config ,但间接地仍然是这样。

In this case, you shouldn't be passing variables around between modules by way of import statements.在这种情况下,您不应该通过导入语句在模块之间传递变量。 In fact, __main__ should really just be a command-line front-end to your code, and the rest of your code should be able to work independently from it (eg a good design would allow you to run from tests.test_runner import runner and call runner() from an interactive Python prompt, in principle, even if you never actually use it that way).事实上, __main__应该只是你代码的命令行前端,你的其余代码应该能够独立于它工作(例如,一个好的设计将允许你from tests.test_runner import runner和原则上从交互式 Python 提示符调用runner() ,即使您从未真正使用过它)。

So instead make runner(...) take arguments for whatever options it requires.所以相反,让runner(...)为它需要的任何选项接受参数 Then __main__.py would just take those arguments from command-line arguments.然后__main__.py只会从命令行参数中获取这些参数。 Eg:例如:

def runner(address=None):
    # Or maybe just runner(address) if you don't want to make the
    # address argument optional

Then然后

if __name__ == '__main__':
    address = sys.argv[1]  # Use argparse instead if you can
    runner(address=address)

I think you have a circular dependency here.我认为你在这里有一个循环依赖。 Your __main__ isn't directly importing config but my guess is that the runner is.你的__main__不是直接导入config但我猜是亚军。 basically if you import config before you hit the address = str(sys.argv[1]) line your going to hit this issue.基本上,如果您在遇到address = str(sys.argv[1])行之前导入config ,则会遇到此问题。

I think a better way to do this is to go with a structure like this:我认为更好的方法是使用这样的结构:

config.py配置文件

URL_TEMPLATE = ""http://{}/login"

then in __main__.py然后在__main__.py

from config import URL_TEMPLATE

url = URL_TEMPLATE.format(address)

this keeps only the static stuff in config and the dynamic runtime generated final url in your main这仅保留配置中的静态内容和动态运行时在您的主文件中生成的最终 url

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM