I am trying to do the following:
In CMD:
python __main__.py 127.0.0.1
The in main.py:
address = sys.argv[1]
Then in my config.py file, I am importing the address like this:
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:
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:
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. 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. E.g. 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
:
$ 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:
...
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
:
$ 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".
BONUS QUESTION: Why did I run python -c 'import a'
and not just python a.py
? If you try the latter, the previous version will actually work and will appear to import a.py
twice. This is because when you run python somemodule.py
it is not imported initially as somemodule
, but rather as __main__
. So from the import system's perspective the a
module has not been imported yet when running from a import a_func
. A very confusing caveat.
So in your case if you have something like __main__.py
:
import config
address = 1
and in 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
.
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.
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 (e.g. 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).
So instead make runner(...)
take arguments for whatever options it requires. Then __main__.py
would just take those arguments from command-line arguments. E.g.:
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)