Beginning date is bigger than the end date of a calendaristic period - python

I am trying to write a function that validates a package that contains 2 dates (beginning and end), a destination and a price
Initially, I tried to write a function that "creates" dates and puts them in a list and then compares them to find out if the end date was lower than the beginning but I figured that was too complicated so I resorted to the datetime inbuilt module
However, when I try to run the test function, it fails and it outputs this error message
File "C:\Users\Anon\Desktop\Fac\FP\pythonProject\main.py", line 65, in valideaza_pachet
raise Exception(err)
Exception: wrong dates!
I assume that I must have messed up a condition in the valideaza_pachet() function, but I don't understand what I did wrong
The code:
import time
import calendar
from datetime import date, datetime
def creeaza_pachet(data_i, data_s, dest, pret):
# function that creates a tourism package, data_i = beginning date, data_s = end date, dest = destination and pret = price
return {
"data_i": data_i,
"data_s": data_s,
"dest": dest,
"pret": pret
}
def get_data_i(pachet):
# functie that returns the beginning date of the package
return pachet["data_i"]
def get_data_s(pachet):
# functie that returns the end date of the package
return pachet["data_s"]
def get_destinatie(pachet):
# functie that returns the destination of the package
return pachet["dest"]
def get_pret(pachet):
# functie that returns the price of the package
# input: pachet - un pachet
# output: pretul float > 0 al pachetului
return pachet["pret"]
def valideaza_pachet(pachet):
#functie that validates if a package was correctly introduced or not
#it raises an Exception as ex if any of the conditions aren't met
err = ""
if get_data_i(pachet) < get_data_s(pachet):
err += "wrong dates!" # if the end date is lower than the beginning date
if get_destinatie(pachet) == " ":
err += "wrong destination!"
if get_pret(pachet) <= 0:
err += "wrong price!"
if len(err) > 0:
raise Exception(err)
def test_valideaza_pachet():
pachet = creeaza_pachet(datetime.strptime('24/08/2021',"%d/%m/%Y"), datetime.strptime('26/08/2021',"%d/%m/%Y"), "Galati", 9000.1)
valideaza_pachet(pachet)
pachet_gresit = creeaza_pachet(datetime.strptime('24/08/2021',"%d/%m/%Y"), datetime.strptime('22/08/2021',"%d/%m/%Y"), "Galati", 9000.1)
try:
valideaza_pachet(pachet_gresit)
assert False
except Exception as ex:
assert (str(ex) == "wrong dates!\n")
alt_pachet = creeaza_pachet(datetime.strptime('24/08/2021',"%d/%m/%Y"), datetime.strptime('22/08/2021',"%d/%m/%Y"), " ", -904)
try:
valideaza_pachet(alt_pachet)
assert False
except Exception as ex:
assert(str(ex) == "wrong dates!\nwrong destination!\nwrong price!\n")
def test_creeaza_pachet():
data_i_string = '24/08/2021'
data_i = datetime.strptime(data_i_string, "%d/%m/%Y")
data_s_string = '26/08/2021'
data_s = datetime.strptime(data_s_string, "%d/%m/%Y")
dest = "Galati"
pret = 9000.1
pachet = creeaza_pachet(data_i,data_s,dest,pret)
assert (get_data_i(pachet) == data_i)
assert (get_data_s(pachet) == data_s)
assert (get_destinatie(pachet) == dest)
assert (abs(get_pret(pachet) - pret) < 0.0001)
def run_teste():
test_creeaza_pachet()
test_valideaza_pachet()
def run():
pass
def main():
run()
run_teste()
main()

This is more of a code review and kind of off-topic here but... First,
drop all assert False - these won't do anything useful
drop the getter functions, that just makes things convoluted (just use dict[key] in the code as long as you don't use custom class)
drop unnecessary imports like calendar
you might also want to drop the run and main functions (again convoluted) - just call the functions you need
Then you could change valideaza_pachet to raise specific value errors:
def valideaza_pachet(pachet):
if pachet["data_i"] >= pachet["data_s"]:
raise ValueError("start date must be < end date")
if pachet["dest"] == " ":
raise ValueError("destination must not be empty")
if pachet["pret"] <= 0:
raise ValueError("price must be >= 0")
# returns None if no exception was raised
Now to test a valid package, you would just do
valideaza_pachet(pachet) # no exception expected
Testing an invalid package is a bit more complicated without a class that can be unit-tested (see e.g. here) - but you can catch the exception and use the else clause of the try/except to raise an AssertionError that says you wanted an exception:
try:
valideaza_pachet(pachet_gresit)
except Exception as ex:
print(f"successfully received exception: {ex}")
else:
raise AssertionError("validation should have raised an Exception")
or even
assert str(ex) == "start date must be < end date", f"got '{ex}', expected 'start date must be < end date'"
in place of the print statement.

Related

Apply a function and check custom exceptions with decorators

I want to write a function that check a condition (with custom exceptions) and if no exceptions are raised it applies a function and return the result.
# Custom exceptions
class NegativeNumber(Exception):
pass
class BigNumber(Exception):
pass
def add_two(number):
return number + 2
def apply_function(number, f):
def check_condition(number, f):
try:
if number < 0:
raise NegativeNumber
if number > 10:
raise BigNumber
except NegativeNumber:
return ("Negative Number")
except BigNumber:
return ("Big Number")
return (f(number))
return check_condition(number, f)
apply_function(5, add_two)
Can be this code written better using decorators?
You've already done most of it. I modified your function name and the structure of your code a bit. This should be what you are looking for:
# Custom exceptions
class NegativeNumber(Exception):
pass
class BigNumber(Exception):
pass
def check_exceptions(f):
def wrapped(number):
try:
if number < 0:
raise NegativeNumber
if number > 10:
raise BigNumber
except NegativeNumber:
return "Negative Number"
except BigNumber:
return "Big Number"
return f(number)
return wrapped
#check_exceptions
def add_two(number):
return number + 2
for num in (-1, 5, 15):
print(add_two(num))
Output:
Negative Number
7
Big Number

W602 raise ValueError - How has the message to look like?

I am very new to python and my first task is to check older code (not mine!) to convert it according to pep8.
I have the following code block and I should change raise ValueError to raise ValueError("Message"). How has the syntax of the message to look like, something like 'could not find %c in %s' % (ch,str)?
def sort_key(self, string):
collation_elements = []
lookup_key = [ord(ch) for ch in string]
while lookup_key:
value, lookup_key = self.table.find_prefix(lookup_key)
if not value:
# ###
raise ValueError, map(hex, lookup_key)
collation_elements.extend(value)
sort_key = []
for level in range(4):
if level:
sort_key.append(0) # level separator
for element in collation_elements:
ce_l = int(element[1][level], 16)
if ce_l:
sort_key.append(ce_l)
return tuple(sort_key)

python udf error in pig

I am trying to run below python udf in Pig
#outputSchema("word:chararray")
def get(s):
out = s.lower()
return out;
I am getting below error :
File "/home/test.py", line 3, in get
out = s.lower()
AttributeError: 'NoneType' object has no attribute 'lower'
You should handle the case when s is none. In most of the examples such as:
from pig_util import outputSchema
#outputSchema('decade:chararray')
def decade(year):
"""
Get the decade, given a year.
e.g. for 1998 -> '1990s'
"""
try:
base_decade_year = int(year) - (int(year) % 10)
decade_str = '%ss' % base_decade_year
print 'input year: %s, decade: %s' % (year, decade_str)
return decade_str
except ValueError:
return None
You need to handle the case when the value is None. So, one possible fix would be to try:
#outputSchema("word:chararray")
def get(s):
if s is None:
return None
return str(s).lower()

Same Exceptions with different specific message

I tried to write a code that can distinguish the following four different errors.
TypeError: The first parameter is not an integer;
TypeError: The second parameter is not a string;
ValueError: The value of the first parameter is not in the range of 1 to 13; or
ValueError: The value of the second parameter is not one of the strings in the set {'s', 'h', 'c', 'd'}.
However, I only can get the first one to work but not the other three errors. I tried different ways to make it work, but still can't figure out what's wrong.
class Card: # One object of class Card represents a playing card
rank = ['','Ace','Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','Jack','Queen','King']
suit = {'d':'Diamonds', 'c':'Clubs', 'h':'Hearts', 's':'Spades'}
def __init__(self, rank=2, suit=0): # Card constructor, executed every time a new Card object is created
if type(rank) != int:
raise TypeError()
if type(suit) != str:
raise TypeError()
if rank != self.rank:
raise ValueError()
if suit != 'd' or 'c' or 'h' or 's':
raise ValueError()
self.rank = rank
self.suit = suit
def getRank(self): # Obtain the rank of the card
return self.rank
def getSuit(self): # Obtain the suit of the card
return Card.suit[self.suit]
def bjValue(self): # Obtain the Blackjack value of a card
return min(self.rank, 10)
def __str__(self): # Generate the name of a card in a string
return "%s of %s" % (Card.rank[int(self.rank)], Card.suit[self.suit])
if __name__ == "__main__": # Test the class Card above and will be skipped if it is imported into separate file to test
try:
c1 = Card(19,13)
except TypeError:
print ("The first parameter is not an integer")
except TypeError:
print ("The second parameter is not a string")
except ValueError:
print ("The value of first parameter is not in the range of 1 to 13")
except ValueError:
print ("The value of second parameter is not one of the strings in the set {'s','h','c','d'}")
print(c1)
I know maybe it is due to that I have same TypeError and ValueError. Therefore, Python can't distinguish the second TypeError which I hit c1 = Card(13,13) is different from the first TypeError. So, I only get the message that "The first parameter is not an integer" when I have c1 = Card(13,13).
You are trying to distinguish between the error sources in completely the wrong place. By the time the error gets out of Card.__init__, there is no way to tell why e.g. a TypeError was thrown. For each error class (TypeError, ValueError) only the first except will ever be triggered:
try:
...
except TypeError:
# all TypeErrors end up here
except TypeError:
# this is *never* reached
except ValueError:
# all ValueErrors end up here
except ValueError:
# this is *never* reached
Instead, you should provide the specific error messages inside Card.__init__, when you actually raise the error and already know what the reason is:
if not isinstance(rank, int): # better than comparing to type
raise TypeError("The first parameter is not an integer")
Then you can handle them much more simply:
try:
c1 = Card(19,13)
except (TypeError, ValueError) as err: # assign the error to the name 'err'
print(err) # whichever we catch, show the user the message
else:
print(c1) # only print the Card if there were no errors
If you have a particular need to distinguish between different errors of the same class, you can either explicitly check the message:
except TypeError as err:
if err.args[0] == "The first parameter is not an integer":
# do whatever you need to
or create your own, more specific Exception sub-classes, so you can have separate except blocks:
class FirstParamNotIntError(Exception):
pass
(this is just an example, they are too specific in your particular case).
It's probably worth having a read through the documentation on exceptions and the tutorial on using them.

Putting custom error messages in Python

I want to create custom error messages for a function.
def tr( launch_speed , launch_angle_deg , num_samples ):
#Error displays
try:
launch_speed>0
except:
raise Exception("Launch speed has to be positive!")
try:
0<launch_angle_deg<90
except:
raise Exception("Launch angle has to be 0 to 90 degrees!")
try:
um_samples = int(input())
except:
raise Exception("Integer amount of samples!")
try:
num_samples >=2
except:
raise Exception("At least 2 samples!")
Essentially, what I want is to get an error message every time a wrong value has been written in the function variables, and I've tried creating these messages based on what I've gathered on the Internet, but it doesn't seem to work.
You can't use try: except: for everything; for example, launch_speed>0 will not raise an error for negative values. Instead, I think you want e.g.
if launch_speed < 0: # note spacing, and if not try
raise ValueError("Launch speed must be positive.") # note specific error
You should also test for and raise more specific errors (see "the evils of except"), e.g.:
try:
num_samples = int(raw_input()) # don't use input in 2.x
except ValueError: # note specific error
raise TypeError("Integer amount of samples!")
You can see the list of built-in errors in the documentation.
Why not go one step further and build your own exception types? There's a quick tutorial in the docs which could be used something like:
class Error(Exception):
"""Base class for exceptions defined in this module"""
pass
class LaunchError(Error):
"""Errors related to the launch"""
pass
class LaunchSpeedError(LaunchError):
"""Launch speed is wrong"""
pass
class LaunchAngleError(LaunchError):
"""Launch angle is wrong"""
pass
class SamplesError(Error):
"""Error relating to samples"""
pass
In this case the default functionality of Exception is fine, but you may be able to get finer granularity in what you catch by defining extra exceptions.
if launch_speed < 0:
raise LaunchSpeedError("Launch speed must be positive")
if 0 <= launch_angle < 90:
raise LaunchAngleError("Launch angle must be between 0 and 90")
um_samples = input()
try:
um_samples = int(um_samples)
except ValueError:
raise SampleError("Samples must be an integer, not {}".format(um_samples))
if um_samples < 2:
raise SampleError("Must include more than one sample, not {}".format(str(um_samples)))

Categories