From 0fb671ddeff1db44f6d76811c4d853e7ec4401d4 Mon Sep 17 00:00:00 2001 From: martin-martin Date: Fri, 6 Sep 2024 17:56:27 +0200 Subject: [PATCH 1/5] Add code files from tutorial --- thread-safety-locks/README.md | 11 ++++ thread-safety-locks/bank_barrier.py | 27 ++++++++++ thread-safety-locks/bank_condition.py | 53 +++++++++++++++++++ thread-safety-locks/bank_deadlock.py | 51 ++++++++++++++++++ thread-safety-locks/bank_event.py | 50 +++++++++++++++++ .../bank_multithreaded_withdrawal.py | 24 +++++++++ thread-safety-locks/bank_rlock.py | 51 ++++++++++++++++++ thread-safety-locks/bank_semaphore.py | 32 +++++++++++ thread-safety-locks/bank_thread_safe.py | 38 +++++++++++++ thread-safety-locks/threading_example.py | 16 ++++++ 10 files changed, 353 insertions(+) create mode 100644 thread-safety-locks/README.md create mode 100644 thread-safety-locks/bank_barrier.py create mode 100644 thread-safety-locks/bank_condition.py create mode 100644 thread-safety-locks/bank_deadlock.py create mode 100644 thread-safety-locks/bank_event.py create mode 100644 thread-safety-locks/bank_multithreaded_withdrawal.py create mode 100644 thread-safety-locks/bank_rlock.py create mode 100644 thread-safety-locks/bank_semaphore.py create mode 100644 thread-safety-locks/bank_thread_safe.py create mode 100644 thread-safety-locks/threading_example.py diff --git a/thread-safety-locks/README.md b/thread-safety-locks/README.md new file mode 100644 index 0000000000..44107cec62 --- /dev/null +++ b/thread-safety-locks/README.md @@ -0,0 +1,11 @@ +# Python Thread Safety: Using a Lock and Other Techniques + +This folder contains code examples from the Real Python tutorial [Python Thread Safety: Using a Lock and Other Techniques](https://realpython.com/python-thread-lock/). + +## About the Author + +Adarsh Divakaran - Website: https://adarshd.dev/ + +## License + +Distributed under the MIT license. See ``LICENSE`` for more information. \ No newline at end of file diff --git a/thread-safety-locks/bank_barrier.py b/thread-safety-locks/bank_barrier.py new file mode 100644 index 0000000000..6fe6dcc52d --- /dev/null +++ b/thread-safety-locks/bank_barrier.py @@ -0,0 +1,27 @@ +import random +import threading +import time +from concurrent.futures import ThreadPoolExecutor + +teller_barrier = threading.Barrier(3) + + +def prepare_for_work(name): + print(f"{int(time.time())}: {name} is preparing their counter.") + + # Simulate the delay to prepare the counter + time.sleep(random.randint(1, 3)) + print(f"{int(time.time())}: {name} has finished preparing.") + + # Wait for all tellers to finish preparing + teller_barrier.wait() + print(f"{int(time.time())}: {name} is now ready to serve customers.") + + +tellers = ["Teller 1", "Teller 2", "Teller 3"] + +with ThreadPoolExecutor(max_workers=3) as executor: + for teller_name in tellers: + executor.submit(prepare_for_work, teller_name) + +print(f"{int(time.time())}: All tellers are ready to serve customers.") diff --git a/thread-safety-locks/bank_condition.py b/thread-safety-locks/bank_condition.py new file mode 100644 index 0000000000..0b64fa872e --- /dev/null +++ b/thread-safety-locks/bank_condition.py @@ -0,0 +1,53 @@ +import random +import threading +import time +from concurrent.futures import ThreadPoolExecutor + +customer_available_condition = threading.Condition() + +# Customers waiting to be served by the Teller +customer_queue = [] + + +def serve_customers(): + while True: + with customer_available_condition: + # Wait for a customer to arrive + while not customer_queue: + print(f"{int(time.time())}: Teller is waiting for a customer.") + customer_available_condition.wait() + + # Serve the customer + customer = customer_queue.pop(0) + print(f"{int(time.time())}: Teller is serving {customer}.") + + # Simulate the time taken to serve the customer + time.sleep(random.randint(1, 3)) + print(f"{int(time.time())}: Teller has finished serving {customer}.") + + +def add_customer_to_queue(name): + with customer_available_condition: + print(f"{int(time.time())}: {name} has arrived at the bank.") + customer_queue.append(name) + + customer_available_condition.notify() + + +customer_names = [ + "Customer 1", + "Customer 2", + "Customer 3", + "Customer 4", + "Customer 5", +] + +with ThreadPoolExecutor(max_workers=6) as executor: + + teller_thread = executor.submit(serve_customers) + + for name in customer_names: + # Simulate customers arriving at random intervals + time.sleep(random.randint(2, 5)) + + executor.submit(add_customer_to_queue, name) diff --git a/thread-safety-locks/bank_deadlock.py b/thread-safety-locks/bank_deadlock.py new file mode 100644 index 0000000000..b307c7702a --- /dev/null +++ b/thread-safety-locks/bank_deadlock.py @@ -0,0 +1,51 @@ +import threading +import time +from concurrent.futures import ThreadPoolExecutor + + +class BankAccount: + def __init__(self): + self.balance = 0 + self.lock = threading.Lock() + + def deposit(self, amount): + print( + f"Thread {threading.current_thread().name} waiting " + f"to acquire lock for deposit()" + ) + with self.lock: + print( + f"Thread {threading.current_thread().name} " + "acquired lock for deposit()" + ) + time.sleep(0.1) + self._update_balance(amount) + + def _update_balance(self, amount): + print( + f"Thread {threading.current_thread().name} waiting to acquire " + f"lock for _update_balance()" + ) + with self.lock: # This will cause a deadlock + print( + f"Thread {threading.current_thread().name} " + "acquired lock for _update_balance()" + ) + self.balance += amount + + +account = BankAccount() + + +def make_deposit(): + account.deposit(100) + + +with ThreadPoolExecutor( + max_workers=3, thread_name_prefix="Worker" +) as executor: + for _ in range(3): + executor.submit(make_deposit) + + +print(f"Final balance: {account.balance}") diff --git a/thread-safety-locks/bank_event.py b/thread-safety-locks/bank_event.py new file mode 100644 index 0000000000..973b8344fa --- /dev/null +++ b/thread-safety-locks/bank_event.py @@ -0,0 +1,50 @@ +import threading +import time +from concurrent.futures import ThreadPoolExecutor + +bank_open = threading.Event() +transactions_open = threading.Event() + + +def serve_customer(customer_data): + print(f"{customer_data['name']} is waiting for the bank to open.") + + bank_open.wait() + print(f"{customer_data['name']} entered the bank") + if customer_data["type"] == "WITHDRAW_MONEY": + print(f"{customer_data['name']} is waiting for transactions to open.") + transactions_open.wait() + print(f"{customer_data['name']} is starting their transaction.") + + # Simulate the time taken for performing the transaction + time.sleep(2) + + print(f"{customer_data['name']} completed transaction and exited bank") + else: + # Simulate the time taken for banking + time.sleep(2) + print(f"{customer_data['name']} has exited bank") + + +customers = [ + {"name": "Customer 1", "type": "WITHDRAW_MONEY"}, + {"name": "Customer 2", "type": "CHECK_BALANCE"}, + {"name": "Customer 3", "type": "WITHDRAW_MONEY"}, + {"name": "Customer 4", "type": "WITHDRAW_MONEY"}, +] + +with ThreadPoolExecutor(max_workers=4) as executor: + for customer_data in customers: + executor.submit(serve_customer, customer_data) + + print("Bank manager is preparing to open the bank.") + time.sleep(2) + print("Bank is now open!") + bank_open.set() # Signal that the bank is open + + time.sleep(3) + print("Transactions are now open!") + transactions_open.set() + + +print("All customers have completed their transactions.") diff --git a/thread-safety-locks/bank_multithreaded_withdrawal.py b/thread-safety-locks/bank_multithreaded_withdrawal.py new file mode 100644 index 0000000000..a3fe1d5721 --- /dev/null +++ b/thread-safety-locks/bank_multithreaded_withdrawal.py @@ -0,0 +1,24 @@ +import time +from concurrent.futures import ThreadPoolExecutor + + +class BankAccount: + def __init__(self): + self.balance = 1000 + + def withdraw(self, amount): + if self.balance >= amount: + new_balance = self.balance - amount + time.sleep(0.1) # Simulate a delay + self.balance = new_balance + else: + raise Exception("Insufficient balance") + + +account = BankAccount() + +with ThreadPoolExecutor(max_workers=2) as executor: + executor.submit(account.withdraw, 500) + executor.submit(account.withdraw, 700) + +print(f"Final account balance: {account.balance}") diff --git a/thread-safety-locks/bank_rlock.py b/thread-safety-locks/bank_rlock.py new file mode 100644 index 0000000000..47d3939446 --- /dev/null +++ b/thread-safety-locks/bank_rlock.py @@ -0,0 +1,51 @@ +import threading +import time +from concurrent.futures import ThreadPoolExecutor + + +class BankAccount: + def __init__(self): + self.balance = 0 + self.lock = threading.RLock() + + def deposit(self, amount): + print( + f"Thread {threading.current_thread().name} " + "waiting to acquire lock for .deposit()" + ) + with self.lock: + print( + f"Thread {threading.current_thread().name} " + "acquired lock for .deposit()" + ) + time.sleep(0.1) + self._update_balance(amount) + + def _update_balance(self, amount): + print( + f"Thread {threading.current_thread().name} " + "waiting to acquire lock for ._update_balance()" + ) + with self.lock: + print( + f"Thread {threading.current_thread().name} " + "acquired lock for ._update_balance()" + ) + self.balance += amount + + +account = BankAccount() + + +def make_deposit(): + account.deposit(100) + + +with ThreadPoolExecutor( + max_workers=3, thread_name_prefix="Worker" +) as executor: + for _ in range(3): + executor.submit(make_deposit) + + +print(f"Final balance: {account.balance}") diff --git a/thread-safety-locks/bank_semaphore.py b/thread-safety-locks/bank_semaphore.py new file mode 100644 index 0000000000..91f97eaf78 --- /dev/null +++ b/thread-safety-locks/bank_semaphore.py @@ -0,0 +1,32 @@ +import random +import threading +import time +from concurrent.futures import ThreadPoolExecutor + +# Semaphore with a maximum of 2 resources (tellers) +teller_semaphore = threading.Semaphore(2) + + +def serve_customer(name): + print(f"{int(time.time())}: {name} is waiting for a teller.") + with teller_semaphore: + print(f"{int(time.time())}: {name} is being served by a teller.") + # Simulate the time taken for the teller to serve the customer + time.sleep(random.randint(1, 3)) + print(f"{int(time.time())}: {name} is done being served.") + + +customers = [ + "Customer 1", + "Customer 2", + "Customer 3", + "Customer 4", + "Customer 5", +] + +with ThreadPoolExecutor(max_workers=5) as executor: + for customer_name in customers: + thread = executor.submit(serve_customer, customer_name) + + +print("All customers have been served.") diff --git a/thread-safety-locks/bank_thread_safe.py b/thread-safety-locks/bank_thread_safe.py new file mode 100644 index 0000000000..3d1db1a16f --- /dev/null +++ b/thread-safety-locks/bank_thread_safe.py @@ -0,0 +1,38 @@ +import threading +import time +from concurrent.futures import ThreadPoolExecutor + + +class BankAccount: + def __init__(self, balance=0): + self.balance = balance + self.account_lock = threading.Lock() + + def withdraw(self, amount): + with self.account_lock: + if self.balance >= amount: + new_balance = self.balance - amount + print(f"Withdrawing {amount}...") + time.sleep(0.1) # Simulate a delay + self.balance = new_balance + else: + raise Exception("Insufficient balance") + + def deposit(self, amount): + with self.account_lock: + new_balance = self.balance + amount + print(f"Depositing {amount}...") + time.sleep(0.1) # Simulate a delay + self.balance = new_balance + + +account = BankAccount(1000) + +with ThreadPoolExecutor(max_workers=3) as executor: + + executor.submit(account.withdraw, 700) + executor.submit(account.deposit, 1000) + executor.submit(account.withdraw, 300) + + +print(f"Final account balance: {account.balance}") diff --git a/thread-safety-locks/threading_example.py b/thread-safety-locks/threading_example.py new file mode 100644 index 0000000000..f4edf393c6 --- /dev/null +++ b/thread-safety-locks/threading_example.py @@ -0,0 +1,16 @@ +import threading +import time +from concurrent.futures import ThreadPoolExecutor + + +def threaded_function(): + for number in range(3): + print(f"Printing from {threading.current_thread().name}. {number=}") + time.sleep(0.1) + + +with ThreadPoolExecutor( + max_workers=4, thread_name_prefix="Worker" +) as executor: + for _ in range(4): + executor.submit(threaded_function) From 4da829531da908589236862166dddbc1348ed3db Mon Sep 17 00:00:00 2001 From: martin-martin Date: Tue, 1 Oct 2024 16:52:14 -0400 Subject: [PATCH 2/5] Add updated resources --- web-scraping-bs4/README.md | 36 ++++++++++++++++++- web-scraping-bs4/requirements.txt | 14 ++++---- .../{scrape_jobs.py => scraper.py} | 16 ++++----- 3 files changed, 49 insertions(+), 17 deletions(-) rename web-scraping-bs4/{scrape_jobs.py => scraper.py} (57%) diff --git a/web-scraping-bs4/README.md b/web-scraping-bs4/README.md index f89a71e00e..29aea61735 100644 --- a/web-scraping-bs4/README.md +++ b/web-scraping-bs4/README.md @@ -1,3 +1,37 @@ # Build a Web Scraper With Requests and Beautiful Soup -This repository contains [`scrape_jobs.py`](https://github.com/realpython/materials/blob/master/web-scraping-bs4/scrape_jobs.py), which is the sample script built in the Real Python tutorial on how to [Build a Web Scraper With Requests and Beautiful Soup](https://realpython.com/beautiful-soup-web-scraper-python/). +This repository contains `scraper.py`, which is the sample script built in the Real Python tutorial on how to [Build a Web Scraper With Requests and Beautiful Soup](https://realpython.com/beautiful-soup-web-scraper-python/). + +## Installation and Setup + +1. Create a Python virtual environment + +```sh +$ python -m venv venv/ +$ source venv/bin/activate +(venv) $ +``` + +2. Install the requirements + +```sh +(venv) $ pip install -r requirements.txt +``` + +## Run the Scraper + +Run the scraper script: + +```sh +(venv) $ python scraper.py +``` + +You'll see the filtered and formatted Python job listings from the Fake Python job board printed to your console. + +## About the Author + +Martin Breuss - Email: martin@realpython.com + +## License + +Distributed under the MIT license. See ``LICENSE`` for more information. diff --git a/web-scraping-bs4/requirements.txt b/web-scraping-bs4/requirements.txt index b7a5bb209c..c26a496adf 100644 --- a/web-scraping-bs4/requirements.txt +++ b/web-scraping-bs4/requirements.txt @@ -1,7 +1,7 @@ -beautifulsoup4==4.9.3 -certifi==2020.12.5 -chardet==4.0.0 -idna==2.10 -requests==2.25.1 -soupsieve==2.2.1 -urllib3==1.26.4 +beautifulsoup4==4.12.3 +certifi==2024.8.30 +charset-normalizer==3.3.2 +idna==3.10 +requests==2.32.3 +soupsieve==2.6 +urllib3==2.2.3 diff --git a/web-scraping-bs4/scrape_jobs.py b/web-scraping-bs4/scraper.py similarity index 57% rename from web-scraping-bs4/scrape_jobs.py rename to web-scraping-bs4/scraper.py index b50dd04e56..8f7b0d5159 100644 --- a/web-scraping-bs4/scrape_jobs.py +++ b/web-scraping-bs4/scraper.py @@ -7,22 +7,20 @@ soup = BeautifulSoup(page.content, "html.parser") results = soup.find(id="ResultsContainer") -# Look for Python jobs -print("PYTHON JOBS\n==============================\n") python_jobs = results.find_all( "h2", string=lambda text: "python" in text.lower() ) -python_job_elements = [ + +python_job_cards = [ h2_element.parent.parent.parent for h2_element in python_jobs ] -for job_element in python_job_elements: - title_element = job_element.find("h2", class_="title") - company_element = job_element.find("h3", class_="company") - location_element = job_element.find("p", class_="location") +for job_card in python_job_cards: + title_element = job_card.find("h2", class_="title") + company_element = job_card.find("h3", class_="company") + location_element = job_card.find("p", class_="location") print(title_element.text.strip()) print(company_element.text.strip()) print(location_element.text.strip()) - link_url = job_element.find_all("a")[1]["href"] + link_url = job_card.find_all("a")[1]["href"] print(f"Apply here: {link_url}\n") - print() From d5abe4047a1a647baf048ed64248105b11867e65 Mon Sep 17 00:00:00 2001 From: Geir Arne Hjelle Date: Tue, 8 Oct 2024 22:34:24 +0200 Subject: [PATCH 3/5] Final QA (#594) --- python-closure/roots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-closure/roots.py b/python-closure/roots.py index 57275bc3da..970664f29e 100644 --- a/python-closure/roots.py +++ b/python-closure/roots.py @@ -8,7 +8,7 @@ def root_calculator(number): square_root = make_root_calculator(2, 4) print(square_root(42)) -cubic_root = make_root_calculator(3, 2) +cubic_root = make_root_calculator(3) print(cubic_root(42)) @@ -24,5 +24,5 @@ def __call__(self, number): square_root = RootCalculator(2, 4) print(square_root(42)) -cubic_root = RootCalculator(3, 2) +cubic_root = RootCalculator(3) print(cubic_root(42)) From 98d865bccc770f052342b81764e276ae3d350465 Mon Sep 17 00:00:00 2001 From: Geir Arne Hjelle Date: Fri, 11 Oct 2024 11:00:42 +0200 Subject: [PATCH 4/5] Final QA (#595) --- thread-safety-locks/bank_barrier.py | 12 ++++++++---- thread-safety-locks/bank_condition.py | 19 ++++++++++--------- thread-safety-locks/bank_deadlock.py | 11 +++-------- .../bank_multithreaded_withdrawal.py | 2 +- thread-safety-locks/bank_rlock.py | 7 +------ thread-safety-locks/bank_semaphore.py | 12 ++++++++---- thread-safety-locks/bank_thread_safe.py | 3 +-- 7 files changed, 32 insertions(+), 34 deletions(-) diff --git a/thread-safety-locks/bank_barrier.py b/thread-safety-locks/bank_barrier.py index 6fe6dcc52d..eb8a22771a 100644 --- a/thread-safety-locks/bank_barrier.py +++ b/thread-safety-locks/bank_barrier.py @@ -6,16 +6,20 @@ teller_barrier = threading.Barrier(3) +def now(): + return time.strftime("%H:%M:%S") + + def prepare_for_work(name): - print(f"{int(time.time())}: {name} is preparing their counter.") + print(f"{now()}: {name} is preparing their counter.") # Simulate the delay to prepare the counter time.sleep(random.randint(1, 3)) - print(f"{int(time.time())}: {name} has finished preparing.") + print(f"{now()}: {name} has finished preparing.") # Wait for all tellers to finish preparing teller_barrier.wait() - print(f"{int(time.time())}: {name} is now ready to serve customers.") + print(f"{now()}: {name} is now ready to serve customers.") tellers = ["Teller 1", "Teller 2", "Teller 3"] @@ -24,4 +28,4 @@ def prepare_for_work(name): for teller_name in tellers: executor.submit(prepare_for_work, teller_name) -print(f"{int(time.time())}: All tellers are ready to serve customers.") +print(f"{now()}: All tellers are ready to serve customers.") diff --git a/thread-safety-locks/bank_condition.py b/thread-safety-locks/bank_condition.py index 0b64fa872e..26fe2fb3b2 100644 --- a/thread-safety-locks/bank_condition.py +++ b/thread-safety-locks/bank_condition.py @@ -9,26 +9,30 @@ customer_queue = [] +def now(): + return time.strftime("%H:%M:%S") + + def serve_customers(): while True: with customer_available_condition: # Wait for a customer to arrive while not customer_queue: - print(f"{int(time.time())}: Teller is waiting for a customer.") + print(f"{now()}: Teller is waiting for a customer.") customer_available_condition.wait() # Serve the customer customer = customer_queue.pop(0) - print(f"{int(time.time())}: Teller is serving {customer}.") + print(f"{now()}: Teller is serving {customer}.") # Simulate the time taken to serve the customer - time.sleep(random.randint(1, 3)) - print(f"{int(time.time())}: Teller has finished serving {customer}.") + time.sleep(random.randint(1, 5)) + print(f"{now()}: Teller has finished serving {customer}.") def add_customer_to_queue(name): with customer_available_condition: - print(f"{int(time.time())}: {name} has arrived at the bank.") + print(f"{now()}: {name} has arrived at the bank.") customer_queue.append(name) customer_available_condition.notify() @@ -43,11 +47,8 @@ def add_customer_to_queue(name): ] with ThreadPoolExecutor(max_workers=6) as executor: - teller_thread = executor.submit(serve_customers) - for name in customer_names: # Simulate customers arriving at random intervals - time.sleep(random.randint(2, 5)) - + time.sleep(random.randint(1, 3)) executor.submit(add_customer_to_queue, name) diff --git a/thread-safety-locks/bank_deadlock.py b/thread-safety-locks/bank_deadlock.py index b307c7702a..d079dab54f 100644 --- a/thread-safety-locks/bank_deadlock.py +++ b/thread-safety-locks/bank_deadlock.py @@ -11,7 +11,7 @@ def __init__(self): def deposit(self, amount): print( f"Thread {threading.current_thread().name} waiting " - f"to acquire lock for deposit()" + "to acquire lock for deposit()" ) with self.lock: print( @@ -24,7 +24,7 @@ def deposit(self, amount): def _update_balance(self, amount): print( f"Thread {threading.current_thread().name} waiting to acquire " - f"lock for _update_balance()" + "lock for _update_balance()" ) with self.lock: # This will cause a deadlock print( @@ -36,16 +36,11 @@ def _update_balance(self, amount): account = BankAccount() - -def make_deposit(): - account.deposit(100) - - with ThreadPoolExecutor( max_workers=3, thread_name_prefix="Worker" ) as executor: for _ in range(3): - executor.submit(make_deposit) + executor.submit(account.deposit, 100) print(f"Final balance: {account.balance}") diff --git a/thread-safety-locks/bank_multithreaded_withdrawal.py b/thread-safety-locks/bank_multithreaded_withdrawal.py index a3fe1d5721..9ab992ce57 100644 --- a/thread-safety-locks/bank_multithreaded_withdrawal.py +++ b/thread-safety-locks/bank_multithreaded_withdrawal.py @@ -12,7 +12,7 @@ def withdraw(self, amount): time.sleep(0.1) # Simulate a delay self.balance = new_balance else: - raise Exception("Insufficient balance") + raise ValueError("Insufficient balance") account = BankAccount() diff --git a/thread-safety-locks/bank_rlock.py b/thread-safety-locks/bank_rlock.py index 47d3939446..280c71e849 100644 --- a/thread-safety-locks/bank_rlock.py +++ b/thread-safety-locks/bank_rlock.py @@ -36,16 +36,11 @@ def _update_balance(self, amount): account = BankAccount() - -def make_deposit(): - account.deposit(100) - - with ThreadPoolExecutor( max_workers=3, thread_name_prefix="Worker" ) as executor: for _ in range(3): - executor.submit(make_deposit) + executor.submit(account.deposit, 100) print(f"Final balance: {account.balance}") diff --git a/thread-safety-locks/bank_semaphore.py b/thread-safety-locks/bank_semaphore.py index 91f97eaf78..fcc1f21e4c 100644 --- a/thread-safety-locks/bank_semaphore.py +++ b/thread-safety-locks/bank_semaphore.py @@ -7,13 +7,17 @@ teller_semaphore = threading.Semaphore(2) +def now(): + return time.strftime("%H:%M:%S") + + def serve_customer(name): - print(f"{int(time.time())}: {name} is waiting for a teller.") + print(f"{now()}: {name} is waiting for a teller.") with teller_semaphore: - print(f"{int(time.time())}: {name} is being served by a teller.") + print(f"{now()}: {name} is being served by a teller.") # Simulate the time taken for the teller to serve the customer time.sleep(random.randint(1, 3)) - print(f"{int(time.time())}: {name} is done being served.") + print(f"{now()}: {name} is done being served.") customers = [ @@ -29,4 +33,4 @@ def serve_customer(name): thread = executor.submit(serve_customer, customer_name) -print("All customers have been served.") +print(f"{now()}: All customers have been served.") diff --git a/thread-safety-locks/bank_thread_safe.py b/thread-safety-locks/bank_thread_safe.py index 3d1db1a16f..69f38186a0 100644 --- a/thread-safety-locks/bank_thread_safe.py +++ b/thread-safety-locks/bank_thread_safe.py @@ -16,7 +16,7 @@ def withdraw(self, amount): time.sleep(0.1) # Simulate a delay self.balance = new_balance else: - raise Exception("Insufficient balance") + raise ValueError("Insufficient balance") def deposit(self, amount): with self.account_lock: @@ -29,7 +29,6 @@ def deposit(self, amount): account = BankAccount(1000) with ThreadPoolExecutor(max_workers=3) as executor: - executor.submit(account.withdraw, 700) executor.submit(account.deposit, 1000) executor.submit(account.withdraw, 300) From 1b1311ded0a3af742712b1fc223ea8a84aac86c9 Mon Sep 17 00:00:00 2001 From: brendaweles <160772586+brendaweles@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:11:19 -0600 Subject: [PATCH 5/5] Language Edit --- web-scraping-bs4/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web-scraping-bs4/README.md b/web-scraping-bs4/README.md index 29aea61735..f64a2ce475 100644 --- a/web-scraping-bs4/README.md +++ b/web-scraping-bs4/README.md @@ -1,6 +1,6 @@ -# Build a Web Scraper With Requests and Beautiful Soup +# Beautiful Soup: Build a Web Scraper With Python -This repository contains `scraper.py`, which is the sample script built in the Real Python tutorial on how to [Build a Web Scraper With Requests and Beautiful Soup](https://realpython.com/beautiful-soup-web-scraper-python/). +This repository contains `scraper.py`, which is the sample script built in the Real Python tutorial [Beautiful Soup: Build a Web Scraper With Python](https://realpython.com/beautiful-soup-web-scraper-python/). ## Installation and Setup