Unusual import of a class in Python - python

There is a file exceptions.py present in kubernetes.client folder where ApiException class is defined. So I can write the following line in my own file say myfile.py and use the ApiException for raising exception.
some_folder.myfile.py code snippet:
from kubernetes.client.exceptions import ApiException
.....
.....
try:
.....
except ApiException as e:
.....
That is fine.
Also in rest.py present in kubernetes.client folder is importing the same class ApiException and raising some exception.
kubernetes.client.rest.py code snippet:
from kubernetes.client.exceptions import ApiException
.....
.....
if not 200 <= r.status <= 299:
raise ApiException(http_resp=r)
That is also fine. But I am pretty much confused to see the below things as ApiException is imported from kubernetes.client.rest in some_file.py file (see below), not from kubernetes.client.exceptions where actual class definition for ApiException is present.
some_folder.some_file.py code snippet:
from kubernetes.client.rest import ApiException
.....
.....
try:
.....
except ApiException as e:
.....
The above code is working but I am really surprised. Can somebody explain me what is happening here. Sorry I am new to Python.
Note:
ApiException class is not defined in kubernetes.client.rest, it is only defined in kubernetes.client.exceptions
I have searched many articles at online but did not get much information.

The name ApiException is also defined in kubernetes.client.rest, because it's been imported there. kubernetes.client.rest is using it, so it exists there. Any name that exists at the top level of a module is an attribute of that module and can be imported from elsewhere. It doesn't matter how that name got to be defined there.
Arguably the class should be imported from its canonical location where it has been defined, but it doesn't have to be. some_folder.some_file.py might not know where the exception has been originally defined, if it only interacts with kubernetes.client.rest and just needs to catch exceptions raised there.
You will often see this technique used in __init__.py files to simply re-export some classes defined in submodules under a simpler name, e.g.:
# foo/__init__.py
from .submodule import Foo
from .othermodule import Bar
This allows users of foo to from foo import Foo, instead of having to do from foo.submodule import Foo, but it still keeps the implementation of foo clean and separated into multiple files.

Related

How do I define custom exceptions across multiple files?

So I have a Python project that is split across multiple files (to preserve my sanity) and I'm trying to use a custom exception defined in one file in a different file. Basically, I have a.py and b.py. a.py contains a custom exception (customException) that is raised by a function within a.py.
a.py:
class customException(Exception):
[irrelevant error handling]
def aFunction():
if [something]:
raise customException
return [stuff]
The surrounding code is irrelevant (I hope, otherwise I have a much weirder issue) so I didn't include it. I then import a.py into b.py (they're in the same directory) to use aFunction:
b.py:
import a.py
try:
var = a.aFunction()
except customException:
var = [something else]
When I run b.py in a situation where a.py would raise customException, I get the expected a.customException error, but I also get a NameError: name 'customException' is not defined error.
How do I make it so that customException is defined in b.py?
Edit: Paul M.'s solution worked for me just in case anyone else finds this when looking for a solution. Thanks again Paul and everyone else who answered!
You just need to qualify customException by calling it as a.customException from modules where you imported a, like so:
a.py:
class customException(Exception):
[irrelevant error handling]
def aFunction():
if [something]:
raise customException
return [stuff]
b.py:
import a
try:
var = a.aFunction()
except a.customException:
var = [something else]

Python : Mock a module that raises an exception

I need to test a function in a module that import another module which raises an exception when imported.
#a.py
raise ValueError("hello")
my_const = 'SOMETHING'
#b.py
from a import my_const
def foo():
# do something with my_const
return "expected_result"
#test_foo.py
def test_foo():
from b import foo
assert foo() == "expected_result"
Here when I import foo in test_foo.py, a.py get imported in b.py, an exception is raised and the import is never completed so my_const is not available in b.py.
I'm not allowed to modify neither a.py or b.py. Also, using unittest.patch and #patch('a', 'my_const') does import a.py so it doens't work.
It is possible create the module dynamically with the import lib and add it to sys.modules, but is there another solution that doesn't require importlib ?
As far as I know, you can create and importe the module dynamically. Here is a code inspired from the
"Approximating importlib.import_module()" section in the import lib documentation
from importlib.util import module_from_spec, find_spec
import sys
def patched_import(name, **kwargs):
    spec = find_spec(name)
    m = module_from_spec(spec)
    for k in kwargs:
     setattr(m, k, kwargs[k])
    sys.modules[name] = m
Edit: My solution should be ok for a mock-up but be careful as manipulation of referential can have side effects.
To use it, just do:
patched_import('a', my_const='stuff')
Before importing b.py.

How to unittest a function that use app.logger of Flask

For sure I'm missing something in Flask and unit test integration (or logger configuration maybe)
But when I'm trying to unittest some class methods that have some app.logger I'm having troubles with RuntimeError: working outside of the application context
So a practical example:
utils.py
import boto3
from flask import current_app as app
class CustomError(BaseException):
type = "boto"
class BotoManager:
def upload_to_s3(self, file):
try:
# do something that can trigger a boto3 error
except boto3.exceptions.Boto3Error as e:
app.logger.error(e)
raise CustomError()
test_utils.py
import pytest
from utils.py import CustomError, BotoManager
def test_s3_manager_trigger_error():
boto_manager = BotoManager()
with pytest.raises(CustomError):
boto_manager.upload_to_s3('file.txt') # file doesn't exist so trigger error
So the thing is that when I run it show me the error:
RuntimeError: Working outside of application context.
Becuase the app is not created and I'm not working with the app, so have sense.
So I only see two possible solutions (spoiler I don't like any of them):
Don't log anything with app.logger outside of the views (I think I can use the python logging system, but this is not the desired behaviour)
Don't unittest the parts that use app.logger
Did someone face this problem already? How did you solve it? Any other possible solution?

AttributeError: 'module' object has no attribute 'ConnectError' with Seperate Exceptions file

I am getting an error when I run my python code
This is my exceptions class (exceptions.py):
#!/usr/bin/env python
class ConnectError(Exception):
def __init__(self, arg):
# Set some exception infomation
self.msg = arg
Here is my connector class (connector.py):
#!/usr/bin/env python
import exceptions
class CardReader():
def __init__(self):
raise exceptions.ConnectError("ABC")
Here is my test file(test.py):
#!/usr/bin/env python
import connector
connect = connector()
This is what my code sorta looks like, I know that I should use a try and except in my test file, but before I get to that I get an error in the connector class (AttributeError). I have tried using
from exceptions import ConnectError
but that gives me an ImportError: cannot import name ConnectError, I have tried using this:
from exceptions import *
then I get NameError: name 'exceptions' is not defined, then I tried:
from exceptions import *
import exceptions
I still get AttributeError: 'module' object has no attribute 'ConnectError', I have flipped the import statements and I am stuck. I've been searching online, but I can't find anything that has helped.
While ConnectError is a subclass of Exception, it is not in the same module. So importing exceptions does not have the effect that you want. You need to import the file in which you declared ConnectError.
Don't raise exceptions.ConnectError. Just raise ConnectError.
I solved my issue, i renamed my exceptions.py to expection.py, i'm not sure why but i think the name was effecting something, i used the regular import exception. thanks for the help :D

Custom Exception handling in Python 2.6.5 and importing modules

I had one script with custom exception classes in the form of:
class DirectionError(Exception):
pass
I had my functions in the same script in the form of:
def func1(x):
if x == 1:
raise DirectionError
I put my function calls into a try/except/except block in the form of:
try:
func1(2)
except DirectionError:
logging.debug("Custom error message")
sys.exit()
except:
logging.debug(traceback.format_exc())
I subsequently moved the functions into a seperate mytools.py file. I import the mytools.py file into my main python script.
I moved the custom exception classes into the mytools.py file but exception is not reaching the main python script.
How do I get those functions in the mytools.py file to send the exception back to the try/except block in my main python script?
Thanks.
It depends on how did you import mytools.
If you imported it as
import mytools
then changing:
except DirectionError:
to:
except mytools.DirectionError:
should work.
If you imported only your function with:
from mytools import func1
change it to:
from mytools import func1, DirectionError
Basically, you need to import the DirectionError class into your main code and reference it correctly.
Besides, your exception raise only when you call func1(1), and you are calling func1(2).
Define the exception in its own scriptfile and then import that file into both mytools.py and your main script

Categories