Skip to content
Open
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
83 changes: 83 additions & 0 deletions other/grocery_store_cart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
Console-free grocery cart logic.
"""


class GroceryStoreCart:
"""
Maintain cart item quantities and compute totals.

>>> cart = GroceryStoreCart({"apple": 1.5, "milk": 2.0})
>>> cart.add_item("apple", 2)
>>> cart.add_item("milk")
>>> round(cart.total_price(), 2)
5.0
>>> cart.remove_item("apple")
>>> round(cart.total_price(), 2)
3.5
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doctest only covers the happy path. Since __init__(), add_item(), and remove_item() introduce several error branches (empty catalog, unknown items, non-positive quantities), please add doctest cases that assert the expected exceptions/messages so these behaviors are exercised by CI.

Suggested change
3.5
3.5
>>> GroceryStoreCart({})
Traceback (most recent call last):
...
ValueError: price_catalog cannot be empty
>>> cart.add_item("bread")
Traceback (most recent call last):
...
KeyError: "'bread' is not in the catalog"
>>> cart.add_item("apple", 0)
Traceback (most recent call last):
...
ValueError: quantity must be positive
>>> cart.remove_item("milk", 0)
Traceback (most recent call last):
...
ValueError: quantity must be positive
>>> empty_cart = GroceryStoreCart({"apple": 1.5})
>>> empty_cart.remove_item("apple")
Traceback (most recent call last):
...
KeyError: "'apple' is not present in the cart"

Copilot uses AI. Check for mistakes.

>>> GroceryStoreCart({})
Traceback (most recent call last):
...
ValueError: price_catalog cannot be empty

>>> cart.add_item("bread")
Traceback (most recent call last):
...
KeyError: "'bread' is not in the catalog"

>>> cart.add_item("apple", 0)
Traceback (most recent call last):
...
ValueError: quantity must be positive

>>> cart.remove_item("milk", 0)
Traceback (most recent call last):
...
ValueError: quantity must be positive

>>> empty_cart = GroceryStoreCart({"apple": 1.5})
>>> empty_cart.remove_item("apple")
Traceback (most recent call last):
...
KeyError: "'apple' is not present in the cart"
"""

def __init__(self, price_catalog: dict[str, float]) -> None:
if not price_catalog:
raise ValueError("price_catalog cannot be empty")
self.price_catalog = dict(price_catalog)
self.quantities: dict[str, int] = {}

def add_item(self, item: str, quantity: int = 1) -> None:
if item not in self.price_catalog:
msg = f"{item!r} is not in the catalog"
raise KeyError(msg)
if quantity <= 0:
raise ValueError("quantity must be positive")
self.quantities[item] = self.quantities.get(item, 0) + quantity

def remove_item(self, item: str, quantity: int = 1) -> None:
if quantity <= 0:
raise ValueError("quantity must be positive")
current = self.quantities.get(item, 0)
if current == 0:
msg = f"{item!r} is not present in the cart"
raise KeyError(msg)
if quantity > current:
raise ValueError("quantity exceeds amount present in the cart")
if (remaining := current - quantity) > 0:
self.quantities[item] = remaining
else:
self.quantities.pop(item, None)
Comment on lines +69 to +72
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove_item() currently treats quantity values larger than the current quantity as “remove everything” (because remaining <= 0 pops the item). This makes it easy to silently remove more items than are present. Consider raising a ValueError when quantity > current (and only popping when quantity == current).

Suggested change
if (remaining := current - quantity) > 0:
self.quantities[item] = remaining
else:
self.quantities.pop(item, None)
if quantity > current:
raise ValueError("quantity exceeds amount present in the cart")
if quantity == current:
self.quantities.pop(item, None)
else:
self.quantities[item] = current - quantity

Copilot uses AI. Check for mistakes.

def total_price(self) -> float:
return sum(
self.price_catalog[item] * qty for item, qty in self.quantities.items()
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This total_price() return line is likely over the repo’s configured line-length limit (ruff/pycodestyle E501). Please wrap the sum(...) call across multiple lines to avoid lint failures.

Suggested change
self.price_catalog[item] * qty for item, qty in self.quantities.items()
self.price_catalog[item] * qty
for item, qty in self.quantities.items()

Copilot uses AI. Check for mistakes.
)


if __name__ == "__main__":
import doctest

doctest.testmod()
Loading