.py
# We must import the app_info module early to populate it into
# `sys.modules`; otherwise doing `import stripe.app_info` will end up
# importing that module, and not the global `AppInfo` name from below.
import stripe.app_info
from stripe._app_info import AppInfo as AppInfo
from stripe._version import VERSION as VERSION
What is mean by stripe.app_info must be imported early to prevent AppInfo from being imported? AppInfo isnt even located in stripe.app_info but in stripe._app_info?
Tried ChatGPT, they say its something related to defensive programming to prevent bugs.
https://github.com/stripe/stripe-python/blob/master/stripe/__init__.py
# We must import the app_info module early to populate it into
# `sys.modules`; otherwise doing `import stripe.app_info` will end up
# importing that module, and not the global `AppInfo` name from below.
import stripe.app_info
from stripe._app_info import AppInfo as AppInfo
from stripe._version import VERSION as VERSION
What is mean by stripe.app_info must be imported early to prevent AppInfo from being imported? AppInfo isnt even located in stripe.app_info but in stripe._app_info?
Tried ChatGPT, they say its something related to defensive programming to prevent bugs.
Share Improve this question asked Feb 8 at 2:21 Pacific EastPacific East 111 bronze badge New contributor Pacific East is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 3 |1 Answer
Reset to default 0The comment is misleading. The first time stripe.app_info
is imported, app_info.py
is executed and its module is added to sys.modules
as "stripe.app_info" and to the stripe
namespace as app_info
. A user would reference it as stripe.app_info
. These are different namespaces, used in different contexts.
stripe/__init__.py
is the package namespace and is the first thing executed when stripe
is imported. At this point, its guaranteed that stripe.app_info
has not been imported so the import happens and stripe.app_info
references that module.
Now things get confusing. The authors decided to repurpose stripe.app_info
to reference an optional dictionary that can reconfigure the package. This is also done in __init__.py
, so when the package init is complete, stripe.app_info
is a module in sys.modules
and references None
in the package namespace stripe.app_info
. From the comment, the authors seem to think that stripe.app_info
(the dictionary) shadows stripe.app_info
(the module). But that's only partially true.
If you import stripe.app_info
and then try my_info = stripe.app_info.AppInfo(...)
, you'll be disappointed. Python checked sys.modules
for the import and since the module already exists, it didn't do anything. But when referencing in the second statement, that's the package namespace which has the None
reference and AppInfo
doesn't exist.
But if you from stripe.app_info import AppInfo
, that's the module and you will get AppInfo
. my_info = AppInfo(...)
works. my_info = stripe.AppInfo(...)
also works. That's because the real implementation of the class is in stripe._app_info
and both app_info.py
and __init__.py
import from that same module.
Maybe there is some reason for this convoluted code, but personally I'd stay away from it.
app_info.py
file, and these shenanigans impact whatimport stripe.app_info
does for an external user. I'd strongly recommend just never using this import dance unless you're trying to understand python imports in depth (and still never use this in production as confusing and error-prone). – STerliakov Commented Feb 8 at 3:01