Python: 3 Error Handling inside a function - python

I created a class and function. the function is creating a new object and executing other operations and returns the object.
class DoublyLinkedList:
def __init__(self, element):
self.item = element
self.before = None
self.after = None
def __str__(self):
if self.before is None:
return f'Previous node : {None}, item : {self.item}, Next node : {self.after.item};'
elif self.after is None:
return f'Previous node : {self.before.item}, item : {self.item}, Next node : {None};'
else:
return f'Previous node : {self.before.item}, item : {self.item}, Next node : {self.after.item};'
def addToHeadDLL(element, head):
try:
name = DoublyLinkedList(element)
except NameError:
print('Please enter correct parameters')
else:
head.before = name
name.after = head
print('List added to Head')
return name
a = DoublyLinkedList(1)
b = DoublyLinkedList(2)
a.after = b
b.before = a
c = addToHeadDLL(3, a) #Works
d = addToHeadDLL(4, e) #Produces NameError
When NameError occurs it has to print "Please enter correct parameters". But the output is like this,
List added to Head
Traceback (most recent call last):
File "c:/Users/JBallipalli/Desktop/Codes/dll.py", line 43, in <module>
d = addToHeadDLL(4, e)
NameError: name 'e' is not defined
Please help me solve this...
EDIT:
It's not that try-except- else not run in function. Check the following code. it does run.
import os
def openfolder(dir):
"""Opens folder in file explorer
Args:
dir (str): path of the folder
"""
os.startfile(dir)
def access(dir):
"""Checking access to the given directory
Args:
dir (str): directory path
Returns:
bool, list: status, list of files & folders in the directory path
"""
try:
filesList = os.listdir(dir)
except PermissionError:
print(f'No permission to access {os.path.basename(dir)}')
print(f'Please delete the file in {os.path.basename(dir)} manually')
folder = input('Do you wanna open folder in file explorer? type y/n : ')
if folder[0].lower() == 'y':
openfolder(dir)
return False, None
else:
return True, filesList
path = r'C:\Users\JBallipalli\Recent' # raises PermissionError
permission, files = access(path)
and check the output:
No permission to access Recent
Please delete the file in Recent manually
Do you wanna open folder in file explorer? type y/n :
The only difference between these two functions is above (DoublyLinkedList) code calls class inside a function and other doesn't. I want to know why its behaviour like that?
my Python version : 3.7.6

NameError is returned when you're calling function, as python doesn't know what to pass as e.
It never gets to the exception handling part in addToHeadDLL.

Do not use try and except in function,
If You want NameError then call function addToHeadDLL in try block.
Like that
try:
d = addToHeadDLL(4, e) #Produces NameError
except:
print("Name error")

To add to what everyone have said, you could modify you code like the sample below to fit your use case...
This time, you handle the error at function level...
def addToHeadDLL(element, head):
name = DoublyLinkedList(element)
if not isinstance(head, type(name)):
raise NameError('Please enter correct parameters')
head.before = name
name.after = head
print('List added to Head')
return name
a = DoublyLinkedList(1)
b = DoublyLinkedList(2)
a.after = b
b.before = a
try:
addToHeadDLL(4, e)
except NameError as e:
print(e)

Related

Python validate Folders are in directory and validate an integer value

The goal of this Python program is to:
Take in 2 Folder paths
Take in 1 Integer Value
Validate both Folders are in the directory and that the Integer value is an int
I'm using default values (Folder paths and int) as placeholders for testing but will be using CLI arguments at a later time.
My issue and questions are:
def validate_folders_arg(folder):
When both folders are present in the specified directory the program still prints "Folders cannot be found", Is this an issue with the "return None" statement? When 1 of the 2 or both folders is not in the specified directory the program should quit.
def validate_number_arg(number1):
This method is supposed to check an integer value was passed, if no integer value was found the program should automatically set the value to 10. Am I doing that correctly?
Main:
def main():
test()
Test method:
def test():
path1 = r"Path1"
path2 = r"Path2"
main_folder = validate_folders_arg(path1)
archive_folder = validate_folders_arg(path2)
kept_files = validate_number_arg(5)
# if any of the inputs is not valid return None
if main_folder is None or archive_folder is None:
print("Folders cannot be found")
Validate Folders:
def validate_folders_arg(folder):
"""validates folders in working directory"""
try:
isDir = os.path.isdir(folder)
print(isDir)
# return isDir
except Exception as e:
print(e)
return None
Validate int:
def validate_number_arg(number1):
input = number1
try:
int(input)
it_is = True
# return
except ValueError:
it_is = False
default_value = 10
return default_value
print(it_is)
Output:
True
True
True
Folders cannot be found

How can I implement a static attribute in Python?

How can I implement a static property or method-pair in Python using a sharable and accessible static attribute?
class StaticClass:
__static_attr: str
#classmethod
def set(cls, input__):
cls.__static_class = input__
#classmethod
def get(cls):
return cls.__static_attr
StaticClass.set("Hello!")
print(StaticClass.get())
Output:
C:\Users\pc\AppData\Local\Microsoft\WindowsApps\python3.7.exe C:/Users/pc/source/repos/main.py
Traceback (most recent call last):
File "C:/Users/pc/source/repos/main.py", line 15, in <module>
print(StaticClass.get())
File "C:/Users/pc/source/repos/main.py", line 11, in get
return cls.__static_attr
AttributeError: type object 'StaticClass' has no attribute '_StaticClass__static_attr'
Process finished with exit code 1
Edit: The above source code has a typo in set(). If that typo is fixed, the code runs perfectly.
However, in the case of my original source code, that attribute was a List[str]. In that case, the program only runs only if the attribute is initialized through a pair of square brackets [].
import os
from typing import List
class SearchFilesInTheDirectory:
__file_list: List[str] = []
#classmethod
def do_indexing_of_files(cls, path, file_extension):
for root, dirs, files in os.walk(path):
for file in files:
if file.endswith(file_extension):
cls.__file_list.append(os.path.join(root, file))
#classmethod
def get_files_list(cls):
return cls.__file_list
#classmethod
def printf(cls):
if cls.__file_list is not None:
for file in cls.__file_list:
print(file)
else:
print("directory is empty")
#classmethod
def write_to_file(cls, file_name):
if cls.__file_list is not None:
with open(file_name, 'w') as f:
for f_name in cls.__file_list:
f.write("%s\t%s\n" % f_name)
else:
print("directory is empty")
It would be a better idea is to initialize the class static variable, not just declare its type. This way you would be able to call get even before set, and have it return a default value:
class StaticClass:
__static_attr = ""
#classmethod
def set(cls, input__):
cls.__static_attr = input__
#classmethod
def get(cls):
return cls.__static_attr
StaticClass.set("Hello!")
print(StaticClass.get())
Also, it seems like your original code had a typo, as a result of which set method was assigning to a variable other than the one you were declaring (__static_class instead of __static_attr). Because of that, and because the static variable was not initialized, your get method failed.

Cannot access variable from outer scope (Python)

So I'm trying to make a CLI but I'm having some errors with the 'cd' command. I can't access the 'target_path' variable from my cd command. Here's the code for the CLI:
import os, colorama
ERROR_COLOR = colorama.Back.RED + colorama.Fore.BLACK
user = os.getlogin()
target_path = f'C:\\Users\\{user}'
class Commands:
def __init__(self):
raise RuntimeError("Cannot instantiate class 'Commands'")
#staticmethod
def help(cmd=None):
cmd_help_msgs = {
'help': \
"""
HELP:
USAGE:
help [command: str]
DESCRIPTION:
Prints out the description and usage of a command.
If command is not passed, it prints out the help messages of all commands
""",
'cls': \
"""
CLS:
USAGE:
cls
DESCRIPTION:
Clears the terminal
""",
'exit': \
"""
EXIT:
USAGE:
exit
DESCRITPION:
Quits the program
""",
'echo': \
"""
ECHO:
USAGE:
echo <msg: str>
DESCRIPTION:
Prints out msg
"""
}
help_msg = f"""
IMPORTANT: [] = optional parameter, <> = mandatory parameter
COMMANDS:
"""
if cmd is None:
print(help_msg, end='')
for val in cmd_help_msgs.values():
print(val, end='')
else:
try:
print(help_msg, end='')
print(cmd_help_msgs[cmd])
except:
print(f"{ERROR_COLOR}'{cmd}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}")
return
print('\n')
#staticmethod
def cls():
os.system('cls')
#staticmethod
def exit():
exit(0)
#staticmethod
def echo(msg=''):
if msg == '':
print(ERROR_COLOR + "Missing required argument 'msg'" + colorama.Style.RESET_ALL)
return
print(msg)
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
command_list = [func for func in dir(Commands) if callable(getattr(Commands, func)) and not func.startswith('__')]
os.system('cls')
while True:
command_has_kws = False
command = input(f'[{target_path}]{colorama.Fore.CYAN} # {colorama.Fore.LIGHTBLUE_EX}').strip().split(' ')
print(colorama.Style.RESET_ALL, end='')
for i in range(len(command)):
command[i] = command[i].strip()
if command[i].startswith('-'):
command_has_kws = True
if command[0] in command_list:
try:
if not command_has_kws:
getattr(Commands, command[0])(*command[1:])
else:
# REMINDER: Type 'getattr(Commands, command[0]).__code__.co_varnames[:getattr(Commands, command[0]).__code__.co_argcount]' to get the parameters of a command
pass # TODO: Implement keywords
except TypeError:
print(f"{ERROR_COLOR}Too many or too little arguments were passed to '{command[0]}'{colorama.Style.RESET_ALL}")
else:
print(f"{ERROR_COLOR}'{command[0]}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}\n")
Here's 2 things you should focus on:
target_path = f'C:\\Users\\{user}'
And:
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
Also here's the error message:
Traceback (most recent call last):
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 132, in <module>
getattr(Commands, command[0])(*command[1:])
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 102, in cd
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
UnboundLocalError: local variable 'target_path' referenced before assignment
INFORMATION:
Interpreter - Python 3.8
OS - Windows 10
Editor - PyCharm Community Edition
local variable 'target_path' referenced before assignment this error suggests that you are trying to access target_path in one of your functions before assigning any value and if my understanding is correct, you want to use the global target_path, that you have set in top, within those functions.
If this is correct, then you need to tell your functions to use global variables.
Solution :
Within each function that uses the 'target_path' variable, in the beginning, put this line
def abc():
# add this line
global target_path
...
...
# now use `target_path` here

I got aTypeError: 'NoneType' object is not iterable

in fucntion getLink(urls), I have return (cloud,parent,children)
in main function, I have (cloud,parent,children) = getLink(urls) and I got error of this line: TypeError: 'NoneType' object is not iterable
parent and children are all list of http links. since, it is not able to paste them here, parent is a list contains about 30 links; children is a list contains about 30 items, each item is about 10-100 links which is divide by ",".
cloud is a list contain about 100 words, like that: ['official store', 'Java Applets Centre', 'About Google', 'Web History'.....]
I didnot know why I get an error. Is there anything wrong in passing parameter? Or because the list take too much space?
#crawler url: read webpage and return a list of url and a list of its name
def crawler(url):
try:
m = urllib.request.urlopen(url)
msg = m.read()
....
return (list(set(list(links))),list(set(list(titles))) )
except Exception:
print("url wrong!")
#this is the function has gone wrong: it throw an exception here, also the error I mentioned, also it will end while before len(parent) reach 100.
def getLink(urls):
try:
newUrl=[]
parent = []
children =[]
cloud =[]
i=0
while len(parent)<=100:
url = urls[i]
if url in parent:
i += 1
continue
(links, titles) = crawler(url)
parent.append(url)
children.append(",".join(links))
cloud = cloud + titles
newUrl= newUrl+links
print ("links: ",links)
i += 1
if i == len(urls):
urls = list(set(newUrl))
newUrl = []
i = 0
return (cloud,parent,children)
except Exception:
print("can not get links")
def readfile(file):
#not related, this function will return a list of url
def main():
file='sampleinput.txt'
urls=readfile(file)
(cloud,parent,children) = getLink(urls)
if __name__=='__main__':
main()
There might be a way that your function ends without reaching the explicit return statement.
Look at the following example code.
def get_values(x):
if x:
return 'foo', 'bar'
x, y = get_values(1)
x, y = get_values(0)
When the function is called with 0 as parameter the return is skipped and the function will return None.
You could add an explicit return as the last line of your function. In the example given in this answer it would look like this.
def get_values(x):
if x:
return 'foo', 'bar'
return None, None
Update after seing the code
When the exception is triggered in get_link you just print something and return from the function. You have no return statement, so Python will return None. The calling function now tries to expand None into three values and that fails.
Change your exception handling to return a tuple with three values like you do it when everything is fine. Using None for each value is a good idea for it shows you, that something went wrong. Additionally I wouldn't print anything in the function. Don't mix business logic and input/output.
except Exception:
return None, None, None
Then in your main function use the following:
cloud, parent, children = getLink(urls)
if cloud is None:
print("can not get links")
else:
# do some more work

Faking a traceback in Python

I'm writing a test runner. I have an object that can catch and store exceptions, which will be formatted as a string later as part of the test failure report. I'm trying to unit-test the procedure that formats the exception.
In my test setup, I don't want to actually throw an exception for my object to catch, mainly because it means that the traceback won't be predictable. (If the file changes length, the line numbers in the traceback will change.)
How can I attach a fake traceback to an exception, so that I can make assertions about the way it's formatted? Is this even possible? I'm using Python 3.3.
Simplified example:
class ExceptionCatcher(object):
def __init__(self, function_to_try):
self.f = function_to_try
self.exception = None
def try_run(self):
try:
self.f()
except Exception as e:
self.exception = e
def format_exception_catcher(catcher):
pass
# No implementation yet - I'm doing TDD.
# This'll probably use the 'traceback' module to stringify catcher.exception
class TestFormattingExceptions(unittest.TestCase):
def test_formatting(self):
catcher = ExceptionCatcher(None)
catcher.exception = ValueError("Oh no")
# do something to catcher.exception so that it has a traceback?
output_str = format_exception_catcher(catcher)
self.assertEquals(output_str,
"""Traceback (most recent call last):
File "nonexistent_file.py", line 100, in nonexistent_function
raise ValueError("Oh no")
ValueError: Oh no
""")
Reading the source of traceback.py pointed me in the right direction. Here's my hacky solution, which involves faking the frame and code objects which the traceback would normally hold references to.
import traceback
class FakeCode(object):
def __init__(self, co_filename, co_name):
self.co_filename = co_filename
self.co_name = co_name
class FakeFrame(object):
def __init__(self, f_code, f_globals):
self.f_code = f_code
self.f_globals = f_globals
class FakeTraceback(object):
def __init__(self, frames, line_nums):
if len(frames) != len(line_nums):
raise ValueError("Ya messed up!")
self._frames = frames
self._line_nums = line_nums
self.tb_frame = frames[0]
self.tb_lineno = line_nums[0]
#property
def tb_next(self):
if len(self._frames) > 1:
return FakeTraceback(self._frames[1:], self._line_nums[1:])
class FakeException(Exception):
def __init__(self, *args, **kwargs):
self._tb = None
super().__init__(*args, **kwargs)
#property
def __traceback__(self):
return self._tb
#__traceback__.setter
def __traceback__(self, value):
self._tb = value
def with_traceback(self, value):
self._tb = value
return self
code1 = FakeCode("made_up_filename.py", "non_existent_function")
code2 = FakeCode("another_non_existent_file.py", "another_non_existent_method")
frame1 = FakeFrame(code1, {})
frame2 = FakeFrame(code2, {})
tb = FakeTraceback([frame1, frame2], [1,3])
exc = FakeException("yo").with_traceback(tb)
print(''.join(traceback.format_exception(FakeException, exc, tb)))
# Traceback (most recent call last):
# File "made_up_filename.py", line 1, in non_existent_function
# File "another_non_existent_file.py", line 3, in another_non_existent_method
# FakeException: yo
Thanks to #User for providing FakeException, which is necessary because real exceptions type-check the argument to with_traceback().
This version does have a few limitations:
It doesn't print the lines of code for each stack frame, as a real
traceback would, because format_exception goes off to look for the
real file that the code came from (which doesn't exist in our case).
If you want to make this work, you need to insert fake data into
linecache's
cache (because traceback uses linecache to get hold of the source
code), per #User's answer
below.
You also can't actually raise exc and expect the fake traceback
to survive.
More generally, if you have client code that traverses tracebacks in
a different manner than traceback does (such as much of the inspect
module), these fakes probably won't work. You'd need to add whatever
extra attributes the client code expects.
These limitations are fine for my purposes - I'm just using it as a test double for code that calls traceback - but if you want to do more involved traceback manipulation, it looks like you might have to go down to the C level.
EDIT2:
That is the code of linecache.. I will comment on it.
def updatecache(filename, module_globals=None): # module_globals is a dict
# ...
if module_globals and '__loader__' in module_globals:
name = module_globals.get('__name__')
loader = module_globals['__loader__']
# module_globals = dict(__name__ = 'somename', __loader__ = loader)
get_source = getattr(loader, 'get_source', None)
# loader must have a 'get_source' function that returns the source
if name and get_source:
try:
data = get_source(name)
except (ImportError, IOError):
pass
else:
if data is None:
# No luck, the PEP302 loader cannot find the source
# for this module.
return []
cache[filename] = (
len(data), None,
[line+'\n' for line in data.splitlines()], fullname
)
return cache[filename][2]
That means before you testrun just do:
class Loader:
def get_source(self):
return 'source of the module'
import linecache
linecache.updatecache(filename, dict(__name__ = 'modulename without <> around',
__loader__ = Loader()))
and 'source of the module' is the source of the module you test.
EDIT1:
My solution so far:
class MyExeption(Exception):
_traceback = None
#property
def __traceback__(self):
return self._traceback
#__traceback__.setter
def __traceback__(self, value):
self._traceback = value
def with_traceback(self, tb_or_none):
self.__traceback__ = tb_or_none
return self
Now you can set the custom tracebacks of the exception:
e = MyExeption().with_traceback(1)
What you usually do if you reraise an exception:
raise e.with_traceback(fake_tb)
All exception prints walk through this function:
import traceback
traceback.print_exception(_type, _error, _traceback)
Hope it helps somehow.
You should be able to simply raise whatever fake exception you want where you want it in your test runs. The python exception docs suggest you create a class and raise that as your exception. It's section 8.5 of the docs.
http://docs.python.org/2/tutorial/errors.html
Should be pretty straightforward once you've got the class created.

Categories