Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions exercises/practice/bowling/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Instructions append

## Exception messages

Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.

This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" an error when the scoring or playing rules are not followed. The tests will only pass if you both `raise` the `exception` and include a message with it.

To raise a `ValueError` with a message, write the message as an argument to the `exception` type:

```python
# example when a bonus is attempted with an open frame
raise IndexError("cannot throw bonus with an open tenth frame")

# example when fill balls are invalid
raise ValueError("invalid fill balls")
```
14 changes: 14 additions & 0 deletions exercises/practice/change/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Instructions append

## Exception messages

Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.

This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when change cannot be made with the coins given. The tests will only pass if you both `raise` the `exception` and include a message with it.

To raise a `ValueError` with a message, write the message as an argument to the `exception` type:

```python
# example when change cannot be made with the coins passed in
raise ValueError("can't make target with given coins")
```
7 changes: 4 additions & 3 deletions exercises/practice/change/.meta/template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{%- set coins = case["input"]["coins"] %}
{%- set target = case["input"]["target"] %}
{%- set expected = case["expected"] %}
{%- set exp_error = expected["error"] %}
{%- if case is error_case %}
with self.assertRaisesWithMessage(ValueError):
with self.assertRaises(ValueError) as err:
{{case["property"] |to_snake }}({{coins}}, {{target}})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ exp_error }}")
{% else %}
self.assertEqual({{ case["property"] |to_snake }}({{coins}}, {{target}}), {{expected}})
{% endif %}
{% endfor %}

{{ macros.footer() }}
24 changes: 13 additions & 11 deletions exercises/practice/change/change_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,23 @@ def test_no_coins_make_0_change(self):
self.assertEqual(find_fewest_coins([1, 5, 10, 21, 25], 0), [])

def test_error_testing_for_change_smaller_than_the_smallest_of_coins(self):
with self.assertRaisesWithMessage(ValueError):
with self.assertRaises(ValueError) as err:
find_fewest_coins([5, 10], 3)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(
err.exception.args[0], "can't make target with given coins"
)

def test_error_if_no_combination_can_add_up_to_target(self):
with self.assertRaisesWithMessage(ValueError):
with self.assertRaises(ValueError) as err:
find_fewest_coins([5, 10], 94)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(
err.exception.args[0], "can't make target with given coins"
)

def test_cannot_find_negative_change_values(self):
with self.assertRaisesWithMessage(ValueError):
with self.assertRaises(ValueError) as err:
find_fewest_coins([1, 2, 5], -5)

# Utility functions
def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == "__main__":
unittest.main()
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "target can't be negative")
29 changes: 29 additions & 0 deletions exercises/practice/circular-buffer/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Instructions append

## Customizing and Raising Exceptions

Sometimes it is necessary to both [customize](https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions) and [`raise`](https://docs.python.org/3/tutorial/errors.html#raising-exceptions) exceptions in your code. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging.

Custom exceptions can be created through new exception classes (see [`classes`](https://docs.python.org/3/tutorial/classes.html#tut-classes) for more detail.) that are typically subclasses of [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception).

For situations where you know the error source will be a derivative of a certain exception type, you can choose to inherit from one of the [`built in error types`](https://docs.python.org/3/library/exceptions.html#base-classes) under the _Exception_ class. When raising the error, you should still include a meaningful message.

This particular exercise requires that you create two _custom exceptions_. One exception to be [raised](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement)/"thrown" when your circular buffer is **full**, and one for when it is **empty**. The tests will only pass if you customize appropriate exceptions, `raise` those exceptions, and include appropriate error messages.

To customize a `built-in exception`, create a `class` that inherits from that exception. When raising the custom exception with a message, write the message as an argument to the `exception` type:

```python
# subclassing the built-in BufferError to create BufferFullException
class BufferFullException(BufferError):
"""Exception raised when CircularBuffer is full.

message: explanation of the error.

"""
def __init__(self, message):
self.message = message


# raising a BufferFullException
raise BufferFullException("Circular buffer is full")
```
20 changes: 16 additions & 4 deletions exercises/practice/circular-buffer/.meta/example.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
class BufferFullException(Exception):
pass
class BufferFullException(BufferError):
"""Exception raised when CircularBuffer is full.

message: explanation of the error.

class BufferEmptyException(Exception):
pass
"""
def __init__(self, message):
self.message = message


class BufferEmptyException(BufferError):
"""Exception raised when CircularBuffer is empty.

message: explanation of the error.

"""
def __init__(self, message):
self.message = message


class CircularBuffer:
Expand Down
11 changes: 6 additions & 5 deletions exercises/practice/circular-buffer/.meta/template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ buf.{{ op["operation"] }}(
{%- endif -%}
)
{%- endmacro -%}
{{ macros.header(["CircularBuffer"]) }}
{{ macros.header(["CircularBuffer","BufferEmptyException", "BufferFullException"]) }}

class {{ exercise | camel_case }}Test(unittest.TestCase):
{% for case in cases %}
{%- set input = case["input"] -%}
def test_{{ case["description"] | to_snake }}(self):
{%- set case_name = case["description"] | to_snake %}
def test_{{ case_name }}(self):
buf = CircularBuffer({{ input["capacity"] }})
{% for op in input["operations"] -%}
{% if op.get("should_succeed", True) -%}
Expand All @@ -21,10 +22,10 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{{ call_op(op) }}
{% endif -%}
{% else -%}
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
{{ call_op(op) }}
self.assertEqual(type(err.exception), (BufferEmptyException, BufferFullException))
self.assertEqual(err.exception.args[0], ("Circular buffer is empty", "Circular buffer is full"))
{% endif -%}
{% endfor %}
{% endfor %}

{{ macros.footer(True) }}
20 changes: 16 additions & 4 deletions exercises/practice/circular-buffer/circular_buffer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
class BufferFullException(Exception):
pass
class BufferFullException(BufferError):
"""Exception raised when CircularBuffer is full.

message: explanation of the error.

class BufferEmptyException(Exception):
pass
"""
def __init__(self, message):
pass


class BufferEmptyException(BufferError):
"""Exception raised when CircularBuffer is empty.

message: explanation of the error.

"""
def __init__(self, message):
pass


class CircularBuffer:
Expand Down
55 changes: 42 additions & 13 deletions exercises/practice/circular-buffer/circular_buffer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from circular_buffer import (
CircularBuffer,
BufferEmptyException,
BufferFullException,
)

# Tests adapted from `problem-specifications//canonical-data.json`
Expand All @@ -10,8 +12,15 @@
class CircularBufferTest(unittest.TestCase):
def test_reading_empty_buffer_should_fail(self):
buf = CircularBuffer(1)
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
buf.read()
self.assertEqual(
type(err.exception), (BufferEmptyException, BufferFullException)
)
self.assertEqual(
err.exception.args[0],
("Circular buffer is empty", "Circular buffer is full"),
)

def test_can_read_an_item_just_written(self):
buf = CircularBuffer(1)
Expand All @@ -22,8 +31,15 @@ def test_each_item_may_only_be_read_once(self):
buf = CircularBuffer(1)
buf.write("1")
self.assertEqual(buf.read(), "1")
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
buf.read()
self.assertEqual(
type(err.exception), (BufferEmptyException, BufferFullException)
)
self.assertEqual(
err.exception.args[0],
("Circular buffer is empty", "Circular buffer is full"),
)

def test_items_are_read_in_the_order_they_are_written(self):
buf = CircularBuffer(2)
Expand All @@ -35,8 +51,15 @@ def test_items_are_read_in_the_order_they_are_written(self):
def test_full_buffer_can_t_be_written_to(self):
buf = CircularBuffer(1)
buf.write("1")
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
buf.write("2")
self.assertEqual(
type(err.exception), (BufferEmptyException, BufferFullException)
)
self.assertEqual(
err.exception.args[0],
("Circular buffer is empty", "Circular buffer is full"),
)

def test_a_read_frees_up_capacity_for_another_write(self):
buf = CircularBuffer(1)
Expand All @@ -58,8 +81,15 @@ def test_items_cleared_out_of_buffer_can_t_be_read(self):
buf = CircularBuffer(1)
buf.write("1")
buf.clear()
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
buf.read()
self.assertEqual(
type(err.exception), (BufferEmptyException, BufferFullException)
)
self.assertEqual(
err.exception.args[0],
("Circular buffer is empty", "Circular buffer is full"),
)

def test_clear_frees_up_capacity_for_another_write(self):
buf = CircularBuffer(1)
Expand Down Expand Up @@ -112,13 +142,12 @@ def test_initial_clear_does_not_affect_wrapping_around(self):
buf.overwrite("4")
self.assertEqual(buf.read(), "3")
self.assertEqual(buf.read(), "4")
with self.assertRaisesWithMessage(BaseException):
with self.assertRaises(BufferError) as err:
buf.read()

# Utility functions
def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == "__main__":
unittest.main()
self.assertEqual(
type(err.exception), (BufferEmptyException, BufferFullException)
)
self.assertEqual(
err.exception.args[0],
("Circular buffer is empty", "Circular buffer is full"),
)
15 changes: 13 additions & 2 deletions exercises/practice/collatz-conjecture/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# Notes
# Instructions append

The Collatz Conjecture is only concerned with strictly positive integers, so your solution should raise a `ValueError` with a meaningful message if given 0 or a negative integer.
## Exception messages

Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.

The Collatz Conjecture is only concerned with **strictly positive integers**, so this exercise expects you to use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) and "throw" a `ValueError` in your solution if the given value is zero or a negative integer. The tests will only pass if you both `raise` the `exception` and include a message with it.

To raise a `ValueError` with a message, write the message as an argument to the `exception` type:

```python
# example when argument is zero or a negative integer
raise ValueError("Only positive numbers are allowed")
```
2 changes: 1 addition & 1 deletion exercises/practice/collatz-conjecture/.meta/example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def steps(n):
if n <= 0:
raise ValueError("input should be positive")
raise ValueError("Only positive numbers are allowed")

step_count = 0
while n > 1:
Expand Down
6 changes: 5 additions & 1 deletion exercises/practice/collatz-conjecture/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
def test_{{ case["description"] | to_snake }}(self):
{% set expected = case["expected"] -%}
{% set exp_error = expected["error"] -%}
{%- if case is error_case %}
with self.assertRaisesWithMessage(ValueError):
with self.assertRaises(ValueError) as err:
{{ case["property"] }}({{ case["input"]["number"] }})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ exp_error }}")
{% else %}
self.assertEqual(
{{ case["property"] }}({{ case["input"]["number"] }}),
Expand Down
14 changes: 12 additions & 2 deletions exercises/practice/collatz-conjecture/collatz_conjecture_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,34 @@

class CollatzConjectureTest(unittest.TestCase):
def test_zero_steps_for_one(self):

self.assertEqual(steps(1), 0)

def test_divide_if_even(self):

self.assertEqual(steps(16), 4)

def test_even_and_odd_steps(self):

self.assertEqual(steps(12), 9)

def test_large_number_of_even_and_odd_steps(self):

self.assertEqual(steps(1000000), 152)

def test_zero_is_an_error(self):
with self.assertRaisesWithMessage(ValueError):

with self.assertRaises(ValueError) as err:
steps(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Only positive numbers are allowed")

def test_negative_value_is_an_error(self):
with self.assertRaisesWithMessage(ValueError):

with self.assertRaises(ValueError) as err:
steps(-15)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Only positive numbers are allowed")

# Utility functions
def assertRaisesWithMessage(self, exception):
Expand Down
Loading