dataforseo_client/__init__.py recurses infinitely for any attribute name it can't resolve to a submodule.
Because the fallback calls getattr() on the package itself, a missing name re-enters __getattr__ with the same name, looping until stack overflows (which initially looks like indefinite hang of a python program).
Repro:
import dataforseo_client
# Any of these hang and eventually segfault instead of resolving / raising AttributeError:
print(dataforseo_client.__version__)
print(getattr(dataforseo_client, "DoesNotExist", None)) # expected: returns None
print(hasattr(dataforseo_client, "DoesNotExist")) # expected: returns False
print("ok")
Real life example:
import dataforseo_client # SDK in sys.modules
import logfire
logfire.configure() # scans sys.modules for versions -- hangs
Suspected root cause:
# dataforseo_client/__init__.py
def __getattr__(name):
file_name = camel_to_snake(name)
modules_to_try = ['dataforseo_client', 'dataforseo_client.api', 'dataforseo_client.models']
for module in modules_to_try:
try:
imported_module = importlib.import_module(f'{module}.{file_name}')
model = getattr(imported_module, name)
globals()[name] = model
return model
except:
try:
imported_module = importlib.import_module(f'{module}') # == this package when module == 'dataforseo_client'
model = getattr(imported_module, name) # getattr(self_package, missing_name) -- __getattr__(name) again
globals()[name] = model
return model
except:
continue
raise ImportError(f"Cannot find {name} in any of the specified modules")
When module == 'dataforseo_client', import_module('dataforseo_client') returns this same package, so getattr(imported_module, name) for an unresolved name re-invokes the module-level __getattr__(name) = recursion. Deep recursion overflows the stack rather than raising RecursionError.
Two secondary problems:
__getattr__ should raise AttributeError, not ImportError
- Clauses swallow
KeyboardInterrupt, SystemExit, and RecursionError
Impact
Any environment that imports dataforseo-client alongside a library that introspects modules (observability tooling, type/validation libraries, IDE debuggers) can hang or crash with no actionable error.
Workarounds
- monkeypatching
__getattr__
- Ensuring module-scanning tools run before importing the SDK
dataforseo_client/__init__.pyrecurses infinitely for any attribute name it can't resolve to a submodule.Because the fallback calls getattr() on the package itself, a missing name re-enters
__getattr__with the same name, looping until stack overflows (which initially looks like indefinite hang of a python program).Repro:
Real life example:
Suspected root cause:
When module == 'dataforseo_client',
import_module('dataforseo_client')returns this same package, sogetattr(imported_module, name)for an unresolved name re-invokes the module-level__getattr__(name)= recursion. Deep recursion overflows the stack rather than raisingRecursionError.Two secondary problems:
__getattr__should raiseAttributeError, notImportErrorKeyboardInterrupt,SystemExit, andRecursionErrorImpact
Any environment that imports
dataforseo-clientalongside a library that introspects modules (observability tooling, type/validation libraries, IDE debuggers) can hang or crash with no actionable error.Workarounds
__getattr__