From 42b9a3bece7399ac717fb4c2acdd02052b1f9d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauro=20Fialho=20M=C3=BCller?= Date: Thu, 3 Jul 2025 12:27:24 +0200 Subject: [PATCH] chore: clean up code for start branch --- automated-testing/conftest.py | 14 +- automated-testing/test_assertions.py | 108 ----- automated-testing/test_fixtures.py | 92 ---- automated-testing/test_markers.py | 54 --- automated-testing/test_mocking_advanced.py | 100 ----- .../test_mocking_fundamentals.py | 69 --- automated-testing/test_parametrization.py | 113 ----- automated-testing/text_analysis.py | 29 +- error-handling/built-in-exceptions.ipynb | 363 +-------------- error-handling/context-managers.ipynb | 104 +---- error-handling/custom-context-managers.ipynb | 97 +--- error-handling/custom-exceptions.ipynb | 125 +----- error-handling/raising-exceptions.ipynb | 76 +--- .../filesystem-paths.ipynb | 136 +----- .../read-write-files.ipynb | 135 +----- .../regex-essentials.ipynb | 140 +----- .../regex-groups-back-references.ipynb | 146 +----- .../regex-search-split-substitute.ipynb | 120 +---- .../working-with-csv.ipynb | 120 +---- .../working-with-json.ipynb | 133 +----- .../working-with-yaml.ipynb | 105 +---- generators-decorators/decorators-intro.ipynb | 62 +-- .../decorators-with-arguments.ipynb | 59 +-- .../functions-first-class-citizens.ipynb | 126 +----- generators-decorators/functools-wraps.ipynb | 69 +-- generators-decorators/generators-intro.ipynb | 381 +--------------- .../handling-return-values.ipynb | 107 +---- generators-decorators/lazy-pipelines.ipynb | 112 +---- generators-decorators/return-vs-yield.ipynb | 141 +----- .../stacking-decorators.ipynb | 108 +---- http-requests/authentication.ipynb | 109 +---- .../handling-errors-status-codes.ipynb | 102 +---- http-requests/making-http-requests.ipynb | 307 +------------ http-requests/retries-timeouts.ipynb | 139 +----- .../environment-variables.ipynb | 177 +------- .../filesystem-operations.ipynb | 219 +-------- interacting-with-os/handling-errors.ipynb | 85 +--- interacting-with-os/subprocesses.ipynb | 97 +--- interacting-with-os/temporary-files.ipynb | 134 +----- logging/declarative-config.ini | 32 -- logging/declarative-config.json | 28 -- logging/declarative-config.py | 109 ----- logging/log-levels.py | 54 --- logging/logging-anatomy.ipynb | 122 +---- logging/logging-files.py | 78 ---- logging/structured-logging.py | 46 -- multi-file-projects/devops_utils/__init__.py | 5 - .../devops_utils/file_utils/__init__.py | 0 .../devops_utils/file_utils/file_ops.py | 34 -- .../devops_utils/network_utils/__init__.py | 0 .../devops_utils/network_utils/check_host.py | 15 - .../devops_utils/network_utils/network_ops.py | 43 -- multi-file-projects/main.py | 25 -- multi-file-projects/pyproject.toml | 6 - multi-file-projects/servers_config.yaml | 3 - multi-file-projects/tests/__init__.py | 0 multi-file-projects/tests/test_file_ops.py | 14 - python-fundamentals/args-kwargs.ipynb | 204 +-------- python-fundamentals/classes.ipynb | 147 +------ python-fundamentals/comments.ipynb | 14 +- python-fundamentals/conditionals.ipynb | 127 +----- python-fundamentals/dictionaries.ipynb | 187 +------- python-fundamentals/functions.ipynb | 276 ++---------- python-fundamentals/lambda-functions.ipynb | 92 +--- python-fundamentals/lazy-iteration.ipynb | 110 +---- python-fundamentals/list-comprehension.ipynb | 107 +---- python-fundamentals/lists.ipynb | 106 +---- python-fundamentals/loops.ipynb | 134 +----- python-fundamentals/numbers.ipynb | 59 +-- python-fundamentals/sets.ipynb | 138 +----- python-fundamentals/strings.ipynb | 415 ++++++------------ python-fundamentals/tuples.ipynb | 53 +-- python-fundamentals/variables.ipynb | 72 +-- typing/classes.py | 104 ----- typing/common_types.py | 76 ---- typing/generators_decorators.py | 101 +---- typing/generics.py | 104 ----- typing/typed_dicts.py | 18 - typing/typing_basics.py | 20 - 79 files changed, 637 insertions(+), 7554 deletions(-) delete mode 100644 multi-file-projects/devops_utils/__init__.py delete mode 100644 multi-file-projects/devops_utils/file_utils/__init__.py delete mode 100644 multi-file-projects/devops_utils/file_utils/file_ops.py delete mode 100644 multi-file-projects/devops_utils/network_utils/__init__.py delete mode 100644 multi-file-projects/devops_utils/network_utils/check_host.py delete mode 100644 multi-file-projects/devops_utils/network_utils/network_ops.py delete mode 100644 multi-file-projects/main.py delete mode 100644 multi-file-projects/pyproject.toml delete mode 100644 multi-file-projects/servers_config.yaml delete mode 100644 multi-file-projects/tests/__init__.py delete mode 100644 multi-file-projects/tests/test_file_ops.py diff --git a/automated-testing/conftest.py b/automated-testing/conftest.py index 7bdf927..1ad223e 100644 --- a/automated-testing/conftest.py +++ b/automated-testing/conftest.py @@ -1,13 +1 @@ -import pytest -from typing import Iterable - -ManagedResource = dict[str, str] - - -@pytest.fixture -def managed_resource() -> Iterable[ManagedResource]: - print(" [SHARED FIXTURE]: acquiring resource lock") - resource = {"status": "lock_acquired"} - yield resource - print(" [SHARED FIXTURE]: releasing resource lock") - resource["status"] = "lock_released" +# Section: Sharing Fixtures with conftest.py diff --git a/automated-testing/test_assertions.py b/automated-testing/test_assertions.py index 51ef42e..4b929c8 100644 --- a/automated-testing/test_assertions.py +++ b/automated-testing/test_assertions.py @@ -1,117 +1,9 @@ -from text_analysis import calculate_text_attributes -import pytest - # Section: The `assert` Statement -# Uncomment to play around with Python assertions - -# x: int = 5 - -# assert x == 5 # Nothing will happen, because this is True -# assert ( -# x == 10 -# ), "x should be 10, but it's not!" # Raise an AssertionError - # Section: Pytest and `assert` - -def test_string_equality() -> None: - expected_status = "SUCCESS" - actual_status = "success".upper() - - assert actual_status == expected_status - - -def test_word_count() -> None: - text = "Deploying microservice to Kubernetes cluster." - text_empty = "" - - assert (calculate_text_attributes(text)["word_count"]) == 5 - assert ( - calculate_text_attributes(text_empty)["word_count"] - ) == 0 - - -def test_unique_words() -> None: - text = "Deploying microservice to Kubernetes cluster." - text_with_duplicates = "Deploying deploying." - text_empty = "" - - text_results = calculate_text_attributes(text) - text_with_duplicates_result = calculate_text_attributes( - text_with_duplicates - ) - text_empty_results = calculate_text_attributes(text_empty) - - assert (len(text_results["unique_words"])) == 5 - assert ( - len(text_with_duplicates_result["unique_words"]) - ) == 1 - assert (len(text_empty_results["unique_words"])) == 0 - - -def test_average_word_length() -> None: - text = "Deploying microservice to Kubernetes cluster." # 40 / 5 = 8 - text_with_duplicates = "Deploying deploying." # 18 / 2 = 9 - text_empty = "" # 0 - - text_results = calculate_text_attributes(text) - text_with_duplicates_result = calculate_text_attributes( - text_with_duplicates - ) - text_empty_results = calculate_text_attributes(text_empty) - - assert (text_results["average_word_length"]) == 8.0 - assert ( - text_with_duplicates_result["average_word_length"] - ) == 9.0 - assert (text_empty_results["average_word_length"]) == 0.0 - - -def test_longest_word() -> None: - text = "Deploying microservice to Kubernetes cluster." # microservice - text_with_duplicates = "Deploying deploying." # deploying - text_empty = "" - - text_results = calculate_text_attributes(text) - text_with_duplicates_result = calculate_text_attributes( - text_with_duplicates - ) - text_empty_results = calculate_text_attributes(text_empty) - - assert ( - text_results["longest_word"].lower() - ) == "microservice" - assert ( - text_with_duplicates_result["longest_word"].lower() - ) == "deploying" - assert (text_empty_results["longest_word"]) == "" - - # Section: Pytest’s Rich Failure Output - -@pytest.mark.xfail # We're marking the test as an expected failure -def test_string_mismatch() -> None: - expected = "HEllo WOrlD" - actual = "hello world" - - assert expected == actual - - # Section: Asserting Floating-Point Numbers (`pytest.approx`) - -def test_float_with_approx() -> None: - calculated_val = 0.1 + 0.2 - expected_val = 0.3 - - assert calculated_val == pytest.approx(expected_val) # type: ignore - - # Section: Asserting Exceptions (`pytest.raises`) - - -def test_raises_exception() -> None: - with pytest.raises(ZeroDivisionError): - _division = 1 / 0 diff --git a/automated-testing/test_fixtures.py b/automated-testing/test_fixtures.py index e038805..cc51925 100644 --- a/automated-testing/test_fixtures.py +++ b/automated-testing/test_fixtures.py @@ -1,99 +1,7 @@ -import pytest -import tempfile -from pathlib import Path -from typing import Iterable -from conftest import ManagedResource - # Section: Defining a Simple Fixture with @pytest.fixture -ConfigDict = dict[str, int | str] - - -@pytest.fixture -def sample_config_dict() -> ConfigDict: - return { - "api_url": "https://test.api.example.com", - "timeout": 30, - "retries": 3, - } - - -def test_api_url_is_present(sample_config_dict: ConfigDict): - print(" [TEST]: test_api_url_is_present running...") - assert "api_url" in sample_config_dict - assert ( - sample_config_dict["api_url"] - == "https://test.api.example.com" - ) - - -@pytest.fixture -def temp_config_file() -> Iterable[Path]: - temp_file = tempfile.NamedTemporaryFile( - mode="w+t", - encoding="utf-8", - suffix=".yaml", - delete=False, - ) - temp_file_path = Path(temp_file.name) - print( - f"\n [FIXTURE SETUP]: temp_config_file created at {temp_file_path}" - ) - temp_file.write("setting1: value1\nsetting2: value2\n") - temp_file.close() - yield temp_file_path - print( - f"\n [FIXTURE TEARDOWN]: temp_config_file deleting {temp_file_path}" - ) - if temp_file_path.exists(): - temp_file_path.unlink() - - # Section: Using Fixtures in Test Functions - -def test_read_from_temp_file(temp_config_file: Path): - print(" [TEST] test_read_from_temp_file running...") - assert temp_config_file.exists() - content = temp_config_file.read_text(encoding="utf-8") - assert "setting1: value1" in content - - # Section: Fixture Scope and Lifecycle - -@pytest.fixture(scope="session") -def expensive_resource() -> Iterable[ConfigDict]: - print( - "\n [SESSION FIXTURE]: expensive_resource - creating..." - ) - yield {"id": "session_resource", "value": 123} - print( - "\n [SESSION FIXTURE]: expensive_resource - deleting..." - ) - - -def test_expensive_resource_id(expensive_resource: ConfigDict): - print(" [TEST] test_expensive_resource_id running...") - assert expensive_resource["id"] == "session_resource" - - -def test_expensive_resource_value( - expensive_resource: ConfigDict, -): - print(" [TEST] test_expensive_resource_value running...") - assert expensive_resource["value"] == 123 - - -@pytest.fixture(scope="session", autouse=True) -def global_setup() -> Iterable[None]: - print("\n [SESSION FIXTURE]: global_setup - creating...") - yield - print("\n [SESSION FIXTURE]: global_setup - deleting...") - - # Section: Sharing Fixtures with conftest.py -def test_managed_resource_status( - managed_resource: ManagedResource, -): - assert managed_resource["status"] == "lock_acquired" diff --git a/automated-testing/test_markers.py b/automated-testing/test_markers.py index 378716e..208b1db 100644 --- a/automated-testing/test_markers.py +++ b/automated-testing/test_markers.py @@ -1,63 +1,9 @@ -import pytest -import time - -try: - import some_optional_library # type: ignore -except ModuleNotFoundError: - some_optional_library = None # Section: Skipping Tests Unconditionally: @pytest.mark.skip - -@pytest.mark.skip( - reason="Skipping experimental feature until completion." -) -def test_new_experimental_feature() -> None: - assert False - - # Section: Skipping Tests Conditionally: @pytest.mark.skipif - -@pytest.mark.skipif( - some_optional_library is None, - reason="Requires 'some_optional_library' to be installed.", -) -def test_with_optional_dependency() -> None: - print( - f"Running tests that depends on an optional library..." - ) - assert some_optional_library - - # Section: Expected Failures: @pytest.mark.xfail - -@pytest.mark.xfail( - reason="Bug #123: Division by zero not handled properly." -) -def test_divide_by_zero() -> None: - _division = 1 / 0 - assert False - - -@pytest.mark.xfail # Add strict=True to make XPASS lead to a failure -def test_expected_to_fail() -> None: - assert True - - # Section: Custom Markers and Registration - -@pytest.mark.slow -def test_very_long_computations() -> None: - time.sleep(5) - assert True - - -@pytest.mark.api -@pytest.mark.smoke -def test_user_creation() -> None: - assert True - - # Section: Running Tests by Marker (m option) diff --git a/automated-testing/test_mocking_advanced.py b/automated-testing/test_mocking_advanced.py index 4ad4a02..1ad7be7 100644 --- a/automated-testing/test_mocking_advanced.py +++ b/automated-testing/test_mocking_advanced.py @@ -1,107 +1,7 @@ -import subprocess -import pytest -from unittest.mock import MagicMock -from pytest_mock import MockerFixture -from dummy_functions import ( - get_current_user, - check_file_exists, - fetch_both_endpoints, -) - # Section: Using side_effect - Exceptions - -def test_get_current_user_command_fails(mocker: MockerFixture): - mock_run = mocker.patch("dummy_functions.subprocess.run") - mock_run.side_effect = subprocess.CalledProcessError( - returncode=1, cmd=["whoami"] - ) - - result = get_current_user() - - assert result is None - - # Section: Using side_effect - List for Multiple Calls - -def test_check_file_exists_side_effect_list( - mocker: MockerFixture, -): - mock_exists = mocker.patch( - "dummy_functions.os.path.exists", - side_effect=[True, False], - ) - - assert check_file_exists("some/path/one") is True - assert check_file_exists("some/path/two") is False - - assert mock_exists.call_count == 2 - - assert [ - call.args for call in mock_exists.call_args_list - ] == [("some/path/one",), ("some/path/two",)] - - # Section: Using side_effect - Callable for Multiple Calls - -def test_fetch_both_endpoints_by_url(mocker: MockerFixture): - fake_responses: dict[str, MagicMock] = {} - - for url, data in [ - ("https://api.example.com/first", {"first": "data"}), - ("https://api.example.com/second", {"second": "data"}), - ]: - resp = mocker.MagicMock() - resp.status_code = 200 - resp.json.return_value = data - - fake_responses[url] = resp - - def _fake_get(url: str) -> MagicMock: - return fake_responses[url] - - mocker.patch( - "dummy_functions.requests.get", side_effect=_fake_get - ) - - result = fetch_both_endpoints() - - assert result == ({"first": "data"}, {"second": "data"}) - - # Section: Choosing between Mock and MagicMock - - -@pytest.mark.xfail( - reason="Context managers do not work with Mock", strict=True -) -def test_context_manager_with_mock(mocker: MockerFixture): - fake_cm = mocker.Mock() - fake_cm.__enter__.return_value = fake_cm - fake_cm.read.return_value = "file contents" - - mock_open = mocker.patch("builtins.open") - mock_open.return_value = fake_cm - - with open("somefile.txt") as f: - contents = f.read() - - mock_open.assert_called_once_with("somefile.txt") - assert contents == "file contents" - - -def test_context_manager_with_magicmock(mocker: MockerFixture): - fake_cm = mocker.MagicMock() - fake_cm.__enter__.return_value = fake_cm - fake_cm.read.return_value = "file contents" - - mock_open = mocker.patch("builtins.open") - mock_open.return_value = fake_cm - - with open("somefile.txt") as f: - contents = f.read() - - mock_open.assert_called_once_with("somefile.txt") - assert contents == "file contents" diff --git a/automated-testing/test_mocking_fundamentals.py b/automated-testing/test_mocking_fundamentals.py index b6efd65..349279a 100644 --- a/automated-testing/test_mocking_fundamentals.py +++ b/automated-testing/test_mocking_fundamentals.py @@ -1,72 +1,3 @@ -from unittest.mock import patch, Mock -from pytest_mock import MockerFixture -from dummy_functions import check_file_exists, get_user_data - - # Section: Using unittest.mock.patch -def test_check_file_exists_manual_patch() -> None: - filepath = "/path/to/some/file.txt" - - patcher = patch("dummy_functions.os.path.exists") - mock_exists = patcher.start() - - mock_exists.return_value = True - - try: - result = check_file_exists(filepath=filepath) - mock_exists.assert_called_once_with(filepath) - assert result is True - finally: - patcher.stop() - - -def test_check_file_exists_context_manager() -> None: - filepath = "/path/to/some/file.txt" - - with patch("dummy_functions.os.path.exists") as mock_exists: - mock_exists.return_value = True - - result = check_file_exists(filepath=filepath) - mock_exists.assert_called_once_with(filepath) - assert result is True - - -@patch("dummy_functions.os.path.exists") -def test_check_file_exists_decorator(mock_exists: Mock) -> None: - filepath = "/path/to/some/file.txt" - - mock_exists.return_value = True - - result = check_file_exists(filepath=filepath) - mock_exists.assert_called_once_with(filepath) - assert result is True - - -def test_check_file_pytest_mocker(mocker: MockerFixture) -> None: - filepath = "/path/to/some/file.txt" - - mock_exists = mocker.patch("dummy_functions.os.path.exists") - mock_exists.return_value = True - - result = check_file_exists(filepath=filepath) - mock_exists.assert_called_once_with(filepath) - assert result is True - # Section: MagicMock and Configuring Mock Objects -def test_get_user_data_success(mocker: MockerFixture) -> None: - mock_api_response: dict[str, str | int] = { - "id": 1, - "name": "test user", - } - - mock_get = mocker.patch("dummy_functions.requests.get") - mock_get.return_value.status_code = 200 - mock_get.return_value.json.return_value = mock_api_response - - data = get_user_data(user_id=1) - - mock_get.assert_called_once_with( - "https://api.example.com/users/1" - ) - assert data == mock_api_response diff --git a/automated-testing/test_parametrization.py b/automated-testing/test_parametrization.py index 24760c0..4bbdf7a 100644 --- a/automated-testing/test_parametrization.py +++ b/automated-testing/test_parametrization.py @@ -25,119 +25,6 @@ def check_url_status(url: str) -> tuple[int | str, str]: # Section: The Problem: Duplicated Test Logic -""" -a -> True -5 -> True -- -> True -A -> False -_ -> False -""" - - -def test_is_valid_lower_case_a(): - assert is_valid_hostname_char("a") is True - - -def test_is_valid_5(): - assert is_valid_hostname_char("5") is True - - -def test_is_valid_hyphen(): - assert is_valid_hostname_char("-") is True - - -def test_is_valid_upper_case_A(): - assert is_valid_hostname_char("A") is False - - -def test_is_valid_underscore(): - assert is_valid_hostname_char("_") is False - - # Section: Solution: @pytest.mark.parametrize - -@pytest.mark.parametrize( - "input_char, expected_result", - [ - ("a", True), - ("5", True), - ("-", True), - ("A", False), - ("_", False), - ("!", False), - ], -) -def test_is_valid_hostname_char( - input_char: str, expected_result: bool -): - assert is_valid_hostname_char(input_char) is expected_result - - # Section: Customizing Test IDs with pytest.param construct - - -@pytest.mark.parametrize( - "input_char, expected_result", - [ - pytest.param("a", True, id="lowercase_letter_a"), - pytest.param("z", True, id="lowercase_letter_z"), - pytest.param("0", True, id="digit_0"), - pytest.param("-", True, id="hyphen"), - pytest.param("A", False, id="uppercase_A_invalid"), - pytest.param("_", False, id="underscore_invalid"), - ], -) -def test_is_valid_hostname_custom_params( - input_char: str, expected_result: bool -): - assert is_valid_hostname_char(input_char) is expected_result - - -@pytest.mark.parametrize( - "url_to_check, expected_status_code, expected_status_text", - [ - ("https://google.com", 200, "OK"), - ( - "https://fakesite123.org/notfound", - 404, - "HTTP_ERROR (404)", - ), - ( - "http://httpbin.org/status/503", - 503, - "HTTP_ERROR (503)", - ), - ( - "http://localhost:1", - "CONNECTION_ERROR", - "CONNECTION_ERROR", - ), - pytest.param( - "https://pending.retries.tests", - 503, - "HTTP_ERROR (503)", - marks=( - pytest.mark.xfail( - reason="Retry logic for 503 is not yet implemented." - ), - pytest.mark.api, - ), - ), - ], - ids=[ - "google_ok", - "site_not_found", - "server_error_503", - "connection_error", - "xfail_retry_case", - ], -) -def test_various_url_statuses( - url_to_check: str, - expected_status_code: int, - expected_status_text: str, -): - status_code, status_text = check_url_status(url_to_check) - assert status_code == expected_status_code - assert status_text == expected_status_text diff --git a/automated-testing/text_analysis.py b/automated-testing/text_analysis.py index 19dab76..0a4ec8b 100644 --- a/automated-testing/text_analysis.py +++ b/automated-testing/text_analysis.py @@ -1,28 +1 @@ -from typing import TypedDict -import re - - -class TextAttributes(TypedDict): - word_count: int - unique_words: set[str] - average_word_length: float - longest_word: str - - -def calculate_text_attributes(input_text: str) -> TextAttributes: - split_text = re.findall(r"\w+", input_text) - word_length_sum = sum(len(word) for word in split_text) - avg_word_length = ( - word_length_sum / len(split_text) - if len(split_text) - else 0 - ) - - return { - "word_count": len(split_text), - "unique_words": set(text.lower() for text in split_text), - "average_word_length": avg_word_length, - "longest_word": ( - max(split_text, key=len) if split_text else "" - ), - } +# Section: Pytest and `assert` \ No newline at end of file diff --git a/error-handling/built-in-exceptions.ipynb b/error-handling/built-in-exceptions.ipynb index 9833e1f..6f04eac 100644 --- a/error-handling/built-in-exceptions.ipynb +++ b/error-handling/built-in-exceptions.ipynb @@ -14,177 +14,11 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "a58e40cd-c300-4120-a605-c38b470d744e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "- ArithmeticError\n", - "\t- FloatingPointError\n", - "\t- OverflowError\n", - "\t- ZeroDivisionError\n", - "- AssertionError\n", - "- AttributeError\n", - "- BufferError\n", - "- EOFError\n", - "- ImportError\n", - "\t- ModuleNotFoundError\n", - "- LookupError\n", - "\t- IndexError\n", - "\t- KeyError\n", - "- MemoryError\n", - "- NameError\n", - "\t- UnboundLocalError\n", - "- OSError\n", - "\t- BlockingIOError\n", - "\t- ChildProcessError\n", - "\t- ConnectionError\n", - "\t- FileExistsError\n", - "\t- FileNotFoundError\n", - "\t- InterruptedError\n", - "\t- IsADirectoryError\n", - "\t- NotADirectoryError\n", - "\t- PermissionError\n", - "\t- ProcessLookupError\n", - "\t- TimeoutError\n", - "\t- BrokenPipeError\n", - "\t- ConnectionAbortedError\n", - "\t- ConnectionRefusedError\n", - "\t- ConnectionResetError\n", - "- ReferenceError\n", - "- RuntimeError\n", - "\t- NotImplementedError\n", - "\t- PythonFinalizationError\n", - "\t- RecursionError\n", - "- StopAsyncIteration\n", - "- StopIteration\n", - "- SyntaxError\n", - "\t- IndentationError\n", - "\t- _IncompleteInputError\n", - "\t- TabError\n", - "- SystemError\n", - "- TypeError\n", - "- ValueError\n", - "\t- UnicodeError\n", - "\t- UnicodeDecodeError\n", - "\t- UnicodeEncodeError\n", - "\t- UnicodeTranslateError\n", - "- Warning\n", - "\t- BytesWarning\n", - "\t- DeprecationWarning\n", - "\t- EncodingWarning\n", - "\t- FutureWarning\n", - "\t- ImportWarning\n", - "\t- PendingDeprecationWarning\n", - "\t- ResourceWarning\n", - "\t- RuntimeWarning\n", - "\t- SyntaxWarning\n", - "\t- UnicodeWarning\n", - "\t- UserWarning\n", - "- FloatingPointError\n", - "- OverflowError\n", - "- ZeroDivisionError\n", - "- BytesWarning\n", - "- DeprecationWarning\n", - "- EncodingWarning\n", - "- FutureWarning\n", - "- ImportWarning\n", - "- PendingDeprecationWarning\n", - "- ResourceWarning\n", - "- RuntimeWarning\n", - "- SyntaxWarning\n", - "- UnicodeWarning\n", - "- UserWarning\n", - "- BlockingIOError\n", - "- ChildProcessError\n", - "- ConnectionError\n", - "\t- BrokenPipeError\n", - "\t- ConnectionAbortedError\n", - "\t- ConnectionRefusedError\n", - "\t- ConnectionResetError\n", - "- FileExistsError\n", - "- FileNotFoundError\n", - "- InterruptedError\n", - "- IsADirectoryError\n", - "- NotADirectoryError\n", - "- PermissionError\n", - "- ProcessLookupError\n", - "- TimeoutError\n", - "- IndentationError\n", - "\t- TabError\n", - "- _IncompleteInputError\n", - "- IndexError\n", - "- KeyError\n", - "- ModuleNotFoundError\n", - "- NotImplementedError\n", - "- PythonFinalizationError\n", - "- RecursionError\n", - "- UnboundLocalError\n", - "- UnicodeError\n", - "\t- UnicodeDecodeError\n", - "\t- UnicodeEncodeError\n", - "\t- UnicodeTranslateError\n", - "- BrokenPipeError\n", - "- ConnectionAbortedError\n", - "- ConnectionRefusedError\n", - "- ConnectionResetError\n", - "- TabError\n", - "- UnicodeDecodeError\n", - "- UnicodeEncodeError\n", - "- UnicodeTranslateError\n", - "- ExceptionGroup\n", - "- EnvironmentError\n", - "\t- BlockingIOError\n", - "\t- ChildProcessError\n", - "\t- ConnectionError\n", - "\t- FileExistsError\n", - "\t- FileNotFoundError\n", - "\t- InterruptedError\n", - "\t- IsADirectoryError\n", - "\t- NotADirectoryError\n", - "\t- PermissionError\n", - "\t- ProcessLookupError\n", - "\t- TimeoutError\n", - "\t- BrokenPipeError\n", - "\t- ConnectionAbortedError\n", - "\t- ConnectionRefusedError\n", - "\t- ConnectionResetError\n", - "- IOError\n", - "\t- BlockingIOError\n", - "\t- ChildProcessError\n", - "\t- ConnectionError\n", - "\t- FileExistsError\n", - "\t- FileNotFoundError\n", - "\t- InterruptedError\n", - "\t- IsADirectoryError\n", - "\t- NotADirectoryError\n", - "\t- PermissionError\n", - "\t- ProcessLookupError\n", - "\t- TimeoutError\n", - "\t- BrokenPipeError\n", - "\t- ConnectionAbortedError\n", - "\t- ConnectionRefusedError\n", - "\t- ConnectionResetError\n" - ] - } - ], - "source": [ - "import inspect, builtins\n", - "\n", - "def show_tree(base, level=0, max_depth=1):\n", - " if level > max_depth:\n", - " return\n", - "\n", - " for name, obj in vars(builtins).items():\n", - " if inspect.isclass(obj) and issubclass(obj, base) and obj is not base:\n", - " print(\"\\t\" * level + f\"- {name}\")\n", - " show_tree(obj, level + 1, max_depth)\n", - "\n", - "show_tree(Exception, max_depth=1)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -200,29 +34,11 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "74c88e06", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "File not found\n" - ] - } - ], - "source": [ - "try:\n", - " with open('nonexistent.txt', 'r') as file:\n", - " content = file.read()\n", - "except FileNotFoundError as e:\n", - " print(\"File not found\")\n", - "except PermissionError:\n", - " print(\"Permission denied when accessing resources.\")\n", - "except OSError as os_err:\n", - " print(f\"General OS error: {os_err}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -238,59 +54,11 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "id": "be6e58bf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "API Key: \n", - "No api-key available, not possible to call API.\n", - "Making API call to endpoint /users with key: 12345\n", - "No required key 'api-key' available, not possible to call API.\n", - "Making API call to endpoint /users with key: 12345\n" - ] - } - ], - "source": [ - "config = {\"host\": \"server1\", \"port\": 8080}\n", - "config2 = {\"host\": \"server1\", \"port\": 8080, \"api-key\": \"12345\"}\n", - "\n", - "api_key = config.get(\"api-key\", \"\")\n", - "print(f\"API Key: {api_key}\")\n", - "\n", - "def call_endpoint(config, endpoint):\n", - " \"\"\"Calls the specified endpoint of the configured host.\n", - "\n", - " Args:\n", - " config (dict(str)): Dict containing host, port, and api-key\n", - " endpoint (str): The endpoint to hit\n", - " \"\"\"\n", - " if \"api-key\" in config:\n", - " print(f\"Making API call to endpoint {endpoint} with key: {config[\"api-key\"]}\")\n", - " else:\n", - " print(\"No api-key available, not possible to call API.\")\n", - "\n", - "def call_endpoint_exception(config, endpoint):\n", - " \"\"\"Calls the specified endpoint of the configured host.\n", - "\n", - " Args:\n", - " config (dict(str)): Dict containing host, port, and api-key\n", - " endpoint (str): The endpoint to hit\n", - " \"\"\"\n", - " try:\n", - " print(f\"Making API call to endpoint {endpoint} with key: {config[\"api-key\"]}\")\n", - " except KeyError as missing_key:\n", - " print(f\"No required key {missing_key} available, not possible to call API.\")\n", - "\n", - "call_endpoint(config, \"/users\")\n", - "call_endpoint(config2, \"/users\")\n", - "\n", - "call_endpoint_exception(config, \"/users\")\n", - "call_endpoint_exception(config2, \"/users\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -306,31 +74,11 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": null, "id": "64c6dcd7", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Index error: list index out of range. List length is 2\n" - ] - } - ], - "source": [ - "servers = [\"web01\", \"web02\"]\n", - "\n", - "i = 2\n", - "\n", - "if i < len(servers):\n", - " print(servers[i])\n", - "\n", - "try:\n", - " print(servers[i])\n", - "except IndexError as e:\n", - " print(f\"Index error: {e}. List length is {len(servers)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -346,30 +94,11 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "id": "a5a0081b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Bad numeric string: invalid literal for int() with base 10: 'http'\n", - "Type mismatch: can only concatenate str (not \"int\") to str\n" - ] - } - ], - "source": [ - "try:\n", - " port = int(\"http\") # ValueError, since literal must be a valid base-10 value\n", - "except ValueError as e:\n", - " print(f\"Bad numeric string: {e}\")\n", - "\n", - "try:\n", - " total = \"Errors: \" + 5\n", - "except TypeError as e:\n", - " print(f\"Type mismatch: {e}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -385,42 +114,11 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": null, "id": "0aa95a17", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Object has no attribute 'subtract'\n", - "AttributeError caught: 'Calculator' object has no attribute 'subtract'.\n", - "AttributeError caught: 'Calculator' object has no attribute 'result'.\n" - ] - } - ], - "source": [ - "class Calculator:\n", - " def add(self, a, b):\n", - " return a + b\n", - "\n", - "calc = Calculator()\n", - "\n", - "if hasattr(calc, \"subtract\"):\n", - " print(calc.subtract(10, 5))\n", - "else:\n", - " print(\"Object has no attribute 'subtract'\")\n", - "\n", - "try:\n", - " print(calc.subtract(10, 5))\n", - "except AttributeError as e:\n", - " print(f\"AttributeError caught: {e}.\")\n", - "\n", - "try:\n", - " print(calc.result)\n", - "except AttributeError as e:\n", - " print(f\"AttributeError caught: {e}.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -434,31 +132,10 @@ "- Typical handling logs instructions and aborts early to avoid cascading failures. " ] }, - { - "cell_type": "code", - "execution_count": 62, - "id": "5018a9fc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Import failed: No module named 'non_existent_lib'. Is the library installed and the correct venv active?\n" - ] - } - ], - "source": [ - "try:\n", - " import non_existent_lib\n", - "except ModuleNotFoundError as e:\n", - " print(f\"Import failed: {e}. Is the library installed and the correct venv active?\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "2af3e6bf", + "id": "5018a9fc", "metadata": {}, "outputs": [], "source": [] @@ -480,7 +157,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/error-handling/context-managers.ipynb b/error-handling/context-managers.ipynb index 94a7f4e..bf16cb9 100644 --- a/error-handling/context-managers.ipynb +++ b/error-handling/context-managers.ipynb @@ -14,38 +14,11 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "9d3d30d2-7448-419f-bb64-0631c3240ae2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error has occurred.\n", - "Closing file\n", - "File closed: True\n" - ] - } - ], - "source": [ - "f = None\n", - "\n", - "try:\n", - " f = open(\"my_log.txt\", \"w\")\n", - " f.write(\"First line\\n\")\n", - " # Simulate an error\n", - " result = 1 / 0\n", - " f.write(\"Second line\\n\")\n", - "except:\n", - " print(\"Error has occurred.\")\n", - "finally:\n", - " if f:\n", - " print(\"Closing file.\")\n", - " f.close()\n", - "\n", - "print(f\"File closed: {f.closed}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -66,77 +39,16 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "580c450b-2061-4758-8aef-3caea2b4b0ac", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error has occurred.\n", - "File closed: True\n" - ] - } - ], - "source": [ - "f = None\n", - "\n", - "try:\n", - " with open(\"my_log.txt\", \"w\") as f:\n", - " f.write(\"First line\\n\")\n", - " # Simulate an error\n", - " result = 1 / 0\n", - " f.write(\"Second line\\n\")\n", - "except:\n", - " print(\"Error has occurred.\")\n", - "\n", - "print(f\"File closed: {f.closed}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "696ddcba-6e93-4436-9dbf-0dff25ab1e8f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created temp dir: /var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T/tmpah0oizsu\n", - "Files inside temp dir: ['test.txt']\n", - "Expected error accessing removed directory: [Errno 2] No such file or directory: '/var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T/tmpah0oizsu'\n" - ] - } - ], - "source": [ - "import tempfile, os\n", - "\n", - "dir_name = None\n", - "\n", - "with tempfile.TemporaryDirectory() as tempdir:\n", - " print(f\"Created temp dir: {tempdir}\")\n", - " \n", - " dir_name = tempdir\n", - " test_file = os.path.join(tempdir, \"test.txt\")\n", - "\n", - " with open(test_file, \"w\") as file:\n", - " file.write(\"Hello from temp directory.\")\n", - "\n", - " print(f\"Files inside temp dir: {os.listdir(tempdir)}\")\n", - "\n", - "try:\n", - " contents = os.listdir(dir_name)\n", - " print(f\"Contents of {dir_name}: {contents}\")\n", - "except FileNotFoundError as e:\n", - " print(f\"Expected error accessing removed directory: {e}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "9a91e2b0-17af-40ee-8e88-a79164af1bb1", + "id": "696ddcba-6e93-4436-9dbf-0dff25ab1e8f", "metadata": {}, "outputs": [], "source": [] @@ -158,7 +70,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/error-handling/custom-context-managers.ipynb b/error-handling/custom-context-managers.ipynb index b7d6884..6bf91a4 100644 --- a/error-handling/custom-context-managers.ipynb +++ b/error-handling/custom-context-managers.ipynb @@ -13,49 +13,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "7ade03fb-8d75-4295-ae76-df372248766a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Setup complete\n", - "a simple value\n", - "Inside the block\n", - "Teardown\n", - "\n", - "Simulated problem\n", - "\n" - ] - } - ], - "source": [ - "class MyContextManager:\n", - " def __init__(self, timeout):\n", - " self.timeout = timeout\n", - "\n", - " def __enter__(self):\n", - " print(\"Setup complete\")\n", - " return \"a simple value\"\n", - "\n", - " def __exit__(self, exception_type, exception_value, traceback):\n", - " print(f\"Teardown\")\n", - "\n", - " # Commenting out since we replaced *args for explicit\n", - " # exception_type, exception_value, traceback parameters\n", - " \n", - " # for arg in args:\n", - " # print(arg)\n", - "\n", - " return False\n", - "\n", - "with MyContextManager(timeout=30) as cm:\n", - " print(cm)\n", - " print(\"Inside the block\")\n", - " raise ValueError(\"Simulated problem\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -74,61 +36,10 @@ "- You can catch exceptions inside the generator if you want to suppress them." ] }, - { - "cell_type": "code", - "execution_count": 11, - "id": "34e23855-05da-4ddc-8e0f-c5244267011e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Start: /Users/lauromueller/Documents/courses/python-devops/code/error-handling\n", - "Changing into temp_dir\n", - "Inside: /Users/lauromueller/Documents/courses/python-devops/code/error-handling/temp_dir\n", - "Reverting to original dir: /Users/lauromueller/Documents/courses/python-devops/code/error-handling\n", - "End: /Users/lauromueller/Documents/courses/python-devops/code/error-handling\n" - ] - } - ], - "source": [ - "import os\n", - "from contextlib import contextmanager\n", - "\n", - "@contextmanager\n", - "def change_directory(destination):\n", - " \"\"\"\n", - " Temporarily switch into destination. If the directory does not exist,\n", - " it is created just before the switch.\n", - "\n", - " Args:\n", - " destination (str): Path to the directory that should become the working directory\n", - " \"\"\"\n", - "\n", - " origin_dir = os.getcwd()\n", - "\n", - " try:\n", - " print(f\"Changing into {destination}\")\n", - " os.makedirs(destination, exist_ok=True)\n", - " os.chdir(destination)\n", - " yield os.getcwd()\n", - " finally:\n", - " print(f\"Reverting to original dir: {origin_dir}\")\n", - " os.chdir(origin_dir)\n", - "\n", - "print(f\"Start: {os.getcwd()}\")\n", - "\n", - "with change_directory(\"temp_dir\") as new_dir:\n", - " print(f\"Inside: {new_dir}\")\n", - "\n", - "print(f\"End: {os.getcwd()}\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "916f4c9c-bf71-4eef-ba11-88aad7c3813f", + "id": "34e23855-05da-4ddc-8e0f-c5244267011e", "metadata": {}, "outputs": [], "source": [] diff --git a/error-handling/custom-exceptions.ipynb b/error-handling/custom-exceptions.ipynb index cbcd569..2f383b2 100644 --- a/error-handling/custom-exceptions.ipynb +++ b/error-handling/custom-exceptions.ipynb @@ -20,41 +20,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "50567d2e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "File error: Failed to process file at path: nonexistent.csv\n" - ] - } - ], - "source": [ - "class AutomationError(Exception):\n", - " \"\"\"Base for all automation script errors.\"\"\"\n", - " pass\n", - "\n", - "class FileProcessingError(AutomationError):\n", - " \"\"\"Error during file processing stage.\"\"\"\n", - " pass\n", - "\n", - "class APICallError(AutomationError):\n", - " \"\"\"Error during an external API call.\"\"\"\n", - " pass\n", - "\n", - "def process_file(filepath):\n", - " raise FileProcessingError(f\"Failed to process file at path: {filepath}\")\n", - "\n", - "try:\n", - " process_file(\"nonexistent.csv\")\n", - "except FileProcessingError as e:\n", - " print(f\"File error: {e}\")\n", - "except AutomationError:\n", - " print(\"Other automation error occurred.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -70,36 +40,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "69be0bbf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Timeout cannot be negative for key 'timeout': received '-5'\n", - " -> key: timeout\n", - " -> value: -5\n" - ] - } - ], - "source": [ - "class ConfigValueError(ValueError):\n", - " \"\"\"Raised when a config value is invalid.\"\"\"\n", - " def __init__(self, key_name, invalid_value, message=\"Invalid configuration value.\"):\n", - " self.key_name = key_name\n", - " self.invalid_value = invalid_value\n", - " full_message = f\"{message} for key '{key_name}': received '{invalid_value}'\"\n", - " super().__init__(full_message)\n", - "\n", - "try:\n", - " raise ConfigValueError(\"timeout\", -5, message=\"Timeout cannot be negative\")\n", - "except ConfigValueError as e:\n", - " print(f\"{e}\")\n", - " print(f\" -> key: {e.key_name}\")\n", - " print(f\" -> value: {e.invalid_value}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -112,64 +57,10 @@ "- Fallback `except BaseError:` catches any related subclass if no more specific handler exists. " ] }, - { - "cell_type": "code", - "execution_count": 20, - "id": "2d963c5e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Invalid environment 'dev'. Allowed values: ['staging', 'production']\n", - "Package 'critical-lib' is missing on host server-production.\n", - "Deployment to staging with package tool succeeded.\n" - ] - } - ], - "source": [ - "class DeploymentError(Exception):\n", - " \"\"\"Base class for deployment-related errors.\"\"\"\n", - " pass\n", - "\n", - "class InvalidEnvironmentError(DeploymentError):\n", - " \"\"\"Raised when environment is invalid.\"\"\"\n", - " def __init__(self, env_name, allowed_envs):\n", - " self.env_name = env_name\n", - " self.allowed_envs = allowed_envs\n", - " super().__init__(f\"Invalid environment '{env_name}'. Allowed values: {allowed_envs}\")\n", - "\n", - "class PackageMissingError(DeploymentError):\n", - " \"\"\"Raised when required packages are missing.\"\"\"\n", - " def __init__(self, package_name, host):\n", - " self.package_name = package_name\n", - " self.host = host\n", - " super().__init__(f\"Package '{package_name}' is missing on host {host}.\")\n", - "\n", - "def deploy_app(environment, package):\n", - " allowed_envs = [\"staging\", \"production\"]\n", - "\n", - " if environment not in allowed_envs:\n", - " raise InvalidEnvironmentError(environment, allowed_envs)\n", - "\n", - " if environment == \"production\" and package == \"critical-lib\":\n", - " raise PackageMissingError(package, f\"server-{environment}\")\n", - "\n", - " print(f\"Deployment to {environment} with package {package} succeeded.\")\n", - "\n", - "\n", - "for env, pkg in [(\"dev\", \"tool\"), (\"production\", \"critical-lib\"), (\"staging\", \"tool\")]:\n", - " try:\n", - " deploy_app(env, pkg)\n", - " except DeploymentError as e:\n", - " print(e)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "337a6dc1-c575-43a2-b61b-23df690eb70a", + "id": "2d963c5e", "metadata": {}, "outputs": [], "source": [] @@ -191,7 +82,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/error-handling/raising-exceptions.ipynb b/error-handling/raising-exceptions.ipynb index 0198939..32508eb 100644 --- a/error-handling/raising-exceptions.ipynb +++ b/error-handling/raising-exceptions.ipynb @@ -14,36 +14,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "ba4553af-86e3-46c0-8fab-1998a94d875c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are no servers to process. Exiting...\n", - "Processing 2 servers.\n" - ] - } - ], - "source": [ - "def process_servers(server_list):\n", - " if not isinstance(server_list, list):\n", - " # return None - BAD Practice, better to raise TypeError Exception\n", - " raise TypeError(\"Input 'server_list' must be of type list.\")\n", - "\n", - " # GOOD practice - Handle edge cases without raising exception\n", - " if len(server_list) == 0:\n", - " print(\"There are no servers to process. Exiting...\")\n", - " return\n", - " \n", - " print(f\"Processing {len(server_list)} servers.\")\n", - "\n", - "# process_servers(\"abc\") # Uncommenting will raise TypeError\n", - "process_servers([])\n", - "process_servers([\"web01\", \"web02\"])" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -57,51 +32,10 @@ "- Always include a clear, informative message describing the failure. " ] }, - { - "cell_type": "code", - "execution_count": 21, - "id": "ad00b0c2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Replicas set to 5\n", - "Caught error: Replica count must be between 0 and 100.\n", - "Caught error: Replica count must be int or convertible to int, got str.\n", - "Caught error: Replica count must be between 0 and 100.\n", - "Replicas set to 5\n", - "Replicas set to 5\n", - "Replicas set to 5\n", - "Replicas set to 6\n" - ] - } - ], - "source": [ - "def set_deployment_replicas(count):\n", - " \"\"\"Example: enforce input type and value boundaries with built-in Exceptions.\"\"\"\n", - " try:\n", - " parsed_count = int(count)\n", - " except (ValueError, TypeError):\n", - " raise TypeError(f\"Replica count must be int or convertible to int, got {type(count).__name__}\")\n", - "\n", - " if parsed_count < 0 or parsed_count > 100:\n", - " raise ValueError(f\"Replica count must be between 0 and 100\")\n", - "\n", - " print(f\"Replicas set to {parsed_count}\")\n", - "\n", - "for val in [5, -2, \"three\", 150, \"5\", 5.0]:\n", - " try:\n", - " set_deployment_replicas(val)\n", - " except (TypeError, ValueError) as e:\n", - " print(f\"Caught error: {e}.\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "e9881a7d-7d6f-4643-a51e-c8fab7329099", + "id": "ad00b0c2", "metadata": {}, "outputs": [], "source": [] @@ -123,7 +57,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/filesystem-paths.ipynb b/files-regex-data-formats/filesystem-paths.ipynb index 06d560b..e7473f8 100644 --- a/files-regex-data-formats/filesystem-paths.ipynb +++ b/files-regex-data-formats/filesystem-paths.ipynb @@ -23,30 +23,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - ". \n", - "/Users/lauromueller/Documents/courses/python-devops/code/files-regex-data-formats/settings.yaml\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "\n", - "config_dir = Path(\".\")\n", - "filename = \"settings.yaml\"\n", - "\n", - "print(config_dir, type(config_dir))\n", - "\n", - "config_path = config_dir / filename\n", - "print(config_path.resolve())" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -61,37 +42,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "aab0eccc-81ba-4087-af69-7da00f95519c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exists: False\n", - "Is file? False\n", - "Is directory? False\n", - "Parent: /var/log/app\n", - "Name: service.log\n", - "Stem: service\n", - "Suffix: .log\n", - "Resolved absolute path: /private/var/log/app/service.log\n" - ] - } - ], - "source": [ - "service_log = Path(\"/var/log/app/service.log\")\n", - "\n", - "print(f\"Exists: {service_log.exists()}\")\n", - "print(f\"Is file? {service_log.is_file()}\")\n", - "print(f\"Is directory? {service_log.is_dir()}\")\n", - "print(f\"Parent: {service_log.parent}\")\n", - "print(f\"Name: {service_log.name}\")\n", - "print(f\"Stem: {service_log.stem}\")\n", - "print(f\"Suffix: {service_log.suffix}\")\n", - "print(f\"Resolved absolute path: {service_log.resolve()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -106,50 +61,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "0980e1cd-fe00-4f92-8ec6-766835289a3a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Immediate children:\n", - " error-handling - True\n", - " requirements.txt - False\n", - " generators-decorators - True\n", - " python-fundamentals - True\n", - " .gitignore - False\n", - "Python files recursively:\n", - " ../error-handling/built-in-exceptions.ipynb\n", - " ../error-handling/custom-context-managers.ipynb\n", - " ../error-handling/custom-exceptions.ipynb\n", - " ../error-handling/raising-exceptions.ipynb\n", - " ../error-handling/context-managers.ipynb\n", - " ../generators-decorators/decorators-intro.ipynb\n", - " ../generators-decorators/lazy-pipelines.ipynb\n", - " ../generators-decorators/stacking-decorators.ipynb\n", - " ../generators-decorators/functions-first-class-citizens.ipynb\n", - " ../generators-decorators/generators-intro.ipynb\n", - " ../generators-decorators/functools-wraps.ipynb\n" - ] - } - ], - "source": [ - "course_parent = Path(\"..\")\n", - "\n", - "print(\"Immediate children:\")\n", - "\n", - "for i, child in enumerate(course_parent.iterdir()):\n", - " print(f\" {child.name} - {child.is_dir()}\")\n", - " if i >= 4: break\n", - "\n", - "print(\"Python files recursively:\")\n", - "\n", - "for i, child in enumerate(course_parent.glob(\"**/*.ipynb\")):\n", - " print(f\" {child}\")\n", - " if i >= 10: break" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -162,40 +78,10 @@ "- Path methods automatically manage file open/close. " ] }, - { - "cell_type": "code", - "execution_count": 16, - "id": "271f0aa0-26fe-48f6-94f8-aee210bec5bc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Read back: Hello, from pathlib!\n", - "Read back: Hello, from pathlib!\n", - "Appended line!\n" - ] - } - ], - "source": [ - "test_file = Path(\"demo.txt\")\n", - "\n", - "test_file.write_text(\"Hello, from pathlib!\", encoding=\"utf-8\")\n", - "print(f\"Read back: {test_file.read_text(encoding=\"utf-8\")}\")\n", - "\n", - "with test_file.open(mode=\"a\", encoding=\"utf-8\") as file:\n", - " file.write(\"\\nAppended line!\")\n", - "\n", - "print(f\"Read back: {test_file.read_text(encoding=\"utf-8\")}\")\n", - "\n", - "test_file.unlink()" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", + "id": "271f0aa0-26fe-48f6-94f8-aee210bec5bc", "metadata": {}, "outputs": [], "source": [] @@ -217,7 +103,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/read-write-files.ipynb b/files-regex-data-formats/read-write-files.ipynb index 1d159b0..fb5dea9 100644 --- a/files-regex-data-formats/read-write-files.ipynb +++ b/files-regex-data-formats/read-write-files.ipynb @@ -15,34 +15,11 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Contents of file:\n", - "Setting=Value\n", - "Other=Another\n", - "\n" - ] - } - ], - "source": [ - "try:\n", - " with open(\"config.txt\", \"w\", encoding=\"utf-8\") as file:\n", - " file.write(\"Setting=Value\\n\")\n", - " file.write(\"Other=Another\\n\")\n", - "\n", - " with open(\"config.txt\", \"r\", encoding=\"utf-8\") as file:\n", - " content = file.read()\n", - " print(f\"Contents of file:\")\n", - " print(content)\n", - "except OSError as e:\n", - " print(f\"File error: {e}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -71,27 +48,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, "outputs": [], - "source": [ - "from pathlib import Path\n", - "\n", - "path = Path(\"mode_demo.txt\")\n", - "\n", - "with path.open(mode=\"w\", encoding=\"utf-8\") as file:\n", - " file.write(\"Initial line\\n\")\n", - "\n", - "with path.open(mode=\"a\", encoding=\"utf-8\") as file:\n", - " file.write(\"Appended line\\n\")\n", - "\n", - "try:\n", - " with path.open(mode=\"x\", encoding=\"utf-8\") as file:\n", - " file.write(\"This will fail if file exists\\n\")\n", - "except FileExistsError as e:\n", - " print(e)" - ] + "source": [] }, { "cell_type": "markdown", @@ -117,54 +78,11 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Iteration for reading:\n", - " -> First\n", - " -> Second\n", - " -> Third\n", - "read() for reading:\n", - "First\n", - "Second\n", - "Third\n", - "\n", - "readline() for reading:\n", - "First\n", - "\n", - "readlines() for reading:\n", - "['First\\n', 'Second\\n', 'Third\\n']\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "\n", - "sample = Path(\"read_demo.txt\")\n", - "sample.write_text(\"First\\nSecond\\nThird\\n\", encoding=\"utf-8\")\n", - "\n", - "print(\"Iteration for reading:\")\n", - "with sample.open(mode=\"r\", encoding=\"utf-8\") as file:\n", - " for line in file:\n", - " print(f\" -> {line.strip()}\")\n", - "\n", - "print(\"read() for reading:\")\n", - "with sample.open(mode=\"r\", encoding=\"utf-8\") as file:\n", - " print(file.read())\n", - "\n", - "print(\"readline() for reading:\")\n", - "with sample.open(mode=\"r\", encoding=\"utf-8\") as file:\n", - " print(file.readline())\n", - "\n", - "print(\"readlines() for reading:\")\n", - "with sample.open(mode=\"r\", encoding=\"utf-8\") as file:\n", - " print(file.readlines())" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -181,43 +99,10 @@ " - **When to use:** When you need to write a **batch** of strings at once (for example, a list of CSV rows). It's more efficient than multiple calls to `.write()`, but you must include `\\n` at the end of each string if you want line breaks." ] }, - { - "cell_type": "code", - "execution_count": 37, - "id": "aab0eccc-81ba-4087-af69-7da00f95519c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "\n", - "write_demo = Path(\"write_demo.txt\")\n", - "\n", - "with write_demo.open(mode=\"w\", encoding=\"utf-8\") as file:\n", - " file.write(\"Line A\\n\")\n", - " file.write(\"Line B\\n\")\n", - "\n", - "lines_to_write = [\n", - " \"user,ip,role\",\n", - " \"alice,10.0.0.0,admin\",\n", - " \"bob,10.0.0.1,dev\",\n", - " \"charlie,10.0.02,audit\"\n", - "]\n", - "with write_demo.open(mode=\"w\", encoding=\"utf-8\") as file:\n", - " file.writelines(f\"{line}\\n\" for line in lines_to_write)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", + "id": "aab0eccc-81ba-4087-af69-7da00f95519c", "metadata": {}, "outputs": [], "source": [] @@ -239,7 +124,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/regex-essentials.ipynb b/files-regex-data-formats/regex-essentials.ipynb index 38701dd..d183577 100644 --- a/files-regex-data-formats/regex-essentials.ipynb +++ b/files-regex-data-formats/regex-essentials.ipynb @@ -20,28 +20,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "search 'WARN': True\n", - "match 'WARN': True\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "line = \"WARN: Disk usage at 91%\"\n", - "pattern = r\"WARN\"\n", - "\n", - "print(f\"search '{pattern}':\", bool(re.search(pattern, line)))\n", - "print(f\"match '{pattern}':\", bool(re.match(pattern, line)))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -58,33 +41,11 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dot matches any character: ['code', 'cxge']\n", - "Start anchor (finds): ['Error']\n", - "Start anchor (does not find): []\n", - "End anchor: ['cxge']\n", - "Character set: ['E', 'E1234']\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "test = \"Error code: E1234. cxge\"\n", - "\n", - "print(f\"Dot matches any character: {re.findall(r\"c..e\", test)}\")\n", - "print(f\"Start anchor (finds): {re.findall(r\"^Error\", test)}\")\n", - "print(f\"Start anchor (does not find): {re.findall(r\"^E1234\", test)}\")\n", - "print(f\"End anchor: {re.findall(r\"cxge$\", test)}\")\n", - "print(f\"Character set: {re.findall(r\"[E0-9]+\", test)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -100,31 +61,11 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Digits: ['1024']\n", - "Word characters: ['The', 'cat', 'scattered', '1024', 'catalogues']\n", - "Whitespace: [' ', ' ', ' ', ' ']\n", - "Word boundary: ['cat']\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "text = \"The cat scattered 1024 catalogues.\"\n", - "\n", - "print(f\"Digits: {re.findall(r\"\\d+\", text)}\")\n", - "print(f\"Word characters: {re.findall(r\"\\w+\", text)}\")\n", - "print(f\"Whitespace: {re.findall(r\"\\s+\", text)}\")\n", - "print(f\"Word boundary: {re.findall(r\"\\bcat\\b\", text)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -145,40 +86,11 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": null, "id": "e440f11c-bb93-485f-b05c-896dbc25fc85", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['a', 'a', 'a', 'a', '']\n", - "['aaaa', '']\n", - "['aaaa']\n", - "['aa', 'aa']\n", - "['aaa', 'a']\n", - "Non-greedy a*: ['', 'a', '', 'a', '', 'a', '', 'a', '']\n", - "Non-greedy a+: ['a', 'a', 'a', 'a']\n", - "Non-greedy a{1,3}?: ['a', 'a', 'a', 'a']\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "text = \"aaaa\"\n", - "\n", - "print(re.findall(r\"a?\", text))\n", - "print(re.findall(r\"a*\", text))\n", - "print(re.findall(r\"a+\", text))\n", - "print(re.findall(r\"a{2}\", text))\n", - "print(re.findall(r\"a{1,3}\", text))\n", - "\n", - "print(f\"Non-greedy a*: {re.findall(r\"a*?\", text)}\")\n", - "print(f\"Non-greedy a+: {re.findall(r\"a+?\", text)}\")\n", - "print(f\"Non-greedy a{{1,3}}?: {re.findall(r\"a{1,3}?\", text)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -191,34 +103,10 @@ "- Greedy quantifiers match the longest possible string that satisfies the pattern. Adding a `?` after them makes them non-greedy (or lazy), matching the shortest possible string." ] }, - { - "cell_type": "code", - "execution_count": 68, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Greedy: ['

One

Two

<>']\n", - "Non-greedy: ['

', '

', '

', '

', '<>', '']\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "html = \"

One

Two

<>\"\n", - "\n", - "print(f\"Greedy: {re.findall(r\"<.*>\", html)}\")\n", - "print(f\"Non-greedy: {re.findall(r\"<.*?>\", html)}\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "441b2068-a6c3-49c2-904f-3e7bfc982a0f", + "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", "metadata": {}, "outputs": [], "source": [] @@ -240,7 +128,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/regex-groups-back-references.ipynb b/files-regex-data-formats/regex-groups-back-references.ipynb index 35552d7..85b96c5 100644 --- a/files-regex-data-formats/regex-groups-back-references.ipynb +++ b/files-regex-data-formats/regex-groups-back-references.ipynb @@ -21,43 +21,11 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Full match: Level=ERROR User=admin Action=login_fail IP=10.0.0.5\n", - "Level: ERROR\n", - "User: admin\n", - "IP: 10.0.0.5\n", - "All groups: ('ERROR', 'admin', '10.0.0.5')\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "log_entry = \"Ts=2023-10-27T12:00:00Z Level=ERROR User=admin Action=login_fail IP=10.0.0.5\"\n", - "\n", - "# Our goal:\n", - "# 1. Group 1: The log level\n", - "# 2. Group 2: The user name\n", - "# 2. Group 3: The IP address\n", - "\n", - "pattern = r\"Level=(\\w+)\\s+User=(\\w+).*?\\s+IP=([\\d\\.]+)\"\n", - "\n", - "match = re.search(pattern, log_entry)\n", - "\n", - "if match:\n", - " print(f\"Full match: {match.group(0)}\")\n", - " print(f\"Level: {match.group(1)}\")\n", - " print(f\"User: {match.group(2)}\")\n", - " print(f\"IP: {match.group(3)}\")\n", - " print(f\"All groups: {match.groups()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -73,45 +41,11 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Full match: Level=ERROR User=admin Action=login_fail IP=10.0.0.5\n", - "Level: ERROR\n", - "User: admin\n", - "IP: 10.0.0.5\n", - "All groups: ('ERROR', 'admin', '10.0.0.5')\n", - "Group dictionary: {'level': 'ERROR', 'user': 'admin', 'ip': '10.0.0.5'}\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "log_entry = \"Ts=2023-10-27T12:00:00Z Level=ERROR User=admin Action=login_fail IP=10.0.0.5\"\n", - "\n", - "# Add labels to:\n", - "# 1. Group 1: The log level\n", - "# 2. Group 2: The user name\n", - "# 2. Group 3: The IP address\n", - "\n", - "pattern = r\"Level=(?P\\w+)\\s+User=(?P\\w+).*?\\s+IP=(?P[\\d\\.]+)\"\n", - "\n", - "match = re.search(pattern, log_entry)\n", - "\n", - "if match:\n", - " print(f\"Full match: {match.group(0)}\")\n", - " print(f\"Level: {match.group(\"level\")}\")\n", - " print(f\"User: {match.group(\"user\")}\")\n", - " print(f\"IP: {match.group(\"ip\")}\")\n", - " print(f\"All groups: {match.groups()}\")\n", - " print(f\"Group dictionary: {match.groupdict()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -126,37 +60,11 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "('report', 'OK')\n", - "('report', 'OK')\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "log_line1 = \"report.txt Status: OK\"\n", - "log_line2 = \"report Status: OK\"\n", - "\n", - "# Our goal:\n", - "# 1. Group 1: The stem of the filename, with .txt being an optional string\n", - "# 2. Group 2: The status code\n", - "\n", - "pattern = r\"^(.+?)(?:\\.txt)?\\s+Status:\\s+(.+)$\"\n", - "\n", - "match_line1 = re.search(pattern, log_line1)\n", - "match_line2 = re.search(pattern, log_line2)\n", - "\n", - "if match_line1: print(match_line1.groups())\n", - "if match_line2: print(match_line2.groups())" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -169,42 +77,10 @@ "- Can make patterns more complex but powerful for advanced text validation." ] }, - { - "cell_type": "code", - "execution_count": 61, - "id": "aab0eccc-81ba-4087-af69-7da00f95519c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Doubled words: ['This', 'test']\n", - "Doubled words: ['This', 'test']\n", - "Tags: [('p', 'Paragraph'), ('b', 'Bold')]\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "text = \"This this is a test test.\"\n", - "pattern_numbers = r\"(?i)\\b(\\w+)\\s+\\1\\b\"\n", - "pattern_labels = r\"(?i)\\b(?P\\w+)\\s+(?P=word)\\b\"\n", - "\n", - "print(f\"Doubled words: {re.findall(pattern_numbers, text)}\")\n", - "print(f\"Doubled words: {re.findall(pattern_labels, text)}\")\n", - "\n", - "html = \"

Paragraph

Bold\"\n", - "pattern_tags = r\"<(\\w+)>(.*?)\"\n", - "\n", - "print(f\"Tags: {re.findall(pattern_tags, html)}\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", + "id": "aab0eccc-81ba-4087-af69-7da00f95519c", "metadata": {}, "outputs": [], "source": [] @@ -226,7 +102,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/regex-search-split-substitute.ipynb b/files-regex-data-formats/regex-search-split-substitute.ipynb index f96628f..7a56c8e 100644 --- a/files-regex-data-formats/regex-search-split-substitute.ipynb +++ b/files-regex-data-formats/regex-search-split-substitute.ipynb @@ -19,38 +19,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Numbers found: ['404', '500', '403', '500', '123', '99']\n", - "Key-value pairs: [('timeout', '60'), ('retries', '3'), ('workers', '5')]\n", - "Whole match: timeout=60; key: timeout; value: 60 - at 0-10\n", - "Whole match: retries=3; key: retries; value: 3 - at 11-20\n", - "Whole match: workers=5; key: workers; value: 5 - at 21-30\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "text = \"Errors found: 404, 500, 403, 500. User IDs: user123, admin99.\"\n", - "config = \"timeout=60 retries=3 workers=5\"\n", - "\n", - "# Find all error codes:\n", - "print(f\"Numbers found: {re.findall(r\"\\d+\", text)}\")\n", - "\n", - "# findall with groups:\n", - "print(f\"Key-value pairs: {re.findall(r\"(\\w+)=(\\w+)\", config)}\")\n", - "\n", - "# finditer\n", - "for match in re.finditer(r\"(\\w+)=(\\w+)\", config):\n", - " print(f\"Whole match: {match.group(0)}; key: {match.group(1)}; value: {match.group(2)} - at {match.start()}-{match.end()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -70,42 +43,11 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Character class split: ['item1', 'item2', 'item3', 'item4', 'item5']\n", - "Capturing group split: ['item1', ',', 'item2', ';', 'item3', ',', 'item4', ';', 'item5']\n", - "HTML non-capturing split: ['\\n', 'First paragraph.', '\\n', 'Second paragraph.', '\\nEnd.\\n']\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "data = \"item1 , item2; item3 ,item4 ;item5\"\n", - "\n", - "# 1. Split on comma and semi-colon\n", - "pattern1 = r\"\\s*[,;]\\s*\"\n", - "print(f\"Character class split: {re.split(pattern1, data)}\")\n", - "\n", - "# 2. Capturing the delimiter\n", - "pattern2 = r\"\\s*([,;])\\s*\"\n", - "print(f\"Capturing group split: {re.split(pattern2, data)}\")\n", - "\n", - "html = \"\"\"\n", - "

First paragraph.

\n", - "Second paragraph.\n", - "End.\n", - "\"\"\"\n", - "\n", - "pattern3 = r\"<.*?class='(?:hello|world)'.*?>|\"\n", - "print(f\"HTML non-capturing split: {re.split(pattern3, html)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -118,56 +60,10 @@ "- Back-references (`\\1`, `\\g`) let you reorder or reuse captured text in the replacement. " ] }, - { - "cell_type": "code", - "execution_count": 92, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Result of redacting: User IDs: [REDACTED_USER], [REDACTED_USER], [REDACTED_USER]. Contact admin789 for help.\n", - "Result of redacting: User IDs: u[REDACTED_USER]23, u[REDACTED_USER]56, u[REDACTED_USER]89. Contact admin789 for help.\n", - "Result of redacting: User IDs: u[REDACTED_USER]23, u[REDACTED_USER]56, user123457689. Contact admin789 for help.\n", - "Result of date transformation: Start: 27/10/2023, End: 15/01/2024\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "text = \"User IDs: user123, user456, user123457689. Contact admin789 for help.\"\n", - "\n", - "# Basic substitution\n", - "redacted = re.sub(r\"user\\d+\", \"[REDACTED_USER]\", text)\n", - "print(f\"Result of redacting: {redacted}\")\n", - "\n", - "# Back-reference for reusing information\n", - "redacted_partially = re.sub(r\"(u)ser\\d+(\\d{2})\", r\"\\1[REDACTED_USER]\\2\", text)\n", - "print(f\"Result of redacting: {redacted_partially}\")\n", - "\n", - "# Limited count of substitutions\n", - "redacted_only_two = re.sub(r\"(u)ser\\d+(\\d{2})\", r\"\\1[REDACTED_USER]\\2\", text, count=2)\n", - "print(f\"Result of redacting: {redacted_only_two}\")\n", - "\n", - "# Named groups for substitution\n", - "date_text = \"Start: 2023-10-27, End: 2024-01-15\"\n", - "# Current format YYYY-MM-DD\n", - "# Target format DD/MM/YYYY\n", - "\n", - "date_pattern_named = r\"(?P\\d{4})-(?P\\d{2})-(?P\\d{2})\"\n", - "replacement_format_named = r\"\\g/\\g/\\g\"\n", - "reformatted_date = re.sub(date_pattern_named, replacement_format_named, date_text)\n", - "\n", - "print(f\"Result of date transformation: {reformatted_date}\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "7a83ede4-ab0e-4a6b-b287-f39da8684bbc", + "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", "metadata": {}, "outputs": [], "source": [] @@ -189,7 +85,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/working-with-csv.ipynb b/files-regex-data-formats/working-with-csv.ipynb index 6fc8c05..1a8870d 100644 --- a/files-regex-data-formats/working-with-csv.ipynb +++ b/files-regex-data-formats/working-with-csv.ipynb @@ -25,34 +25,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Header: ['hostname', 'ip_address', 'role', 'status', 'tags']\n", - "Row 1: ['web01', '10.0.1.5', 'webserver', 'running', 'frontend,prod']\n", - "Row 2: ['db01', '10.0.2.10', 'database', 'maintenance', 'backend,staging']\n" - ] - } - ], - "source": [ - "import csv\n", - "from pathlib import Path\n", - "\n", - "csv_path = Path(\"servers.csv\")\n", - "\n", - "with csv_path.open(\"r\", encoding=\"utf-8\", newline=\"\") as file:\n", - " reader = csv.reader(file)\n", - " header = next(reader)\n", - " print(f\"Header: {header}\")\n", - "\n", - " for idx, row in enumerate(reader, start=1):\n", - " print(f\"Row {idx}: {row}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -67,33 +44,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fieldnames: ['hostname', 'ip_address', 'role', 'status', 'tags']\n", - "Record 1: {'hostname': 'web01', 'ip_address': '10.0.1.5', 'role': 'webserver', 'status': 'running', 'tags': 'frontend,prod'}\n", - "Record 2: {'hostname': 'db01', 'ip_address': '10.0.2.10', 'role': 'database', 'status': 'maintenance', 'tags': 'backend,staging'}\n" - ] - } - ], - "source": [ - "import csv\n", - "from pathlib import Path\n", - "\n", - "csv_path = Path(\"servers.csv\")\n", - "\n", - "with csv_path.open(\"r\", encoding=\"utf-8\", newline=\"\") as file:\n", - " dict_reader = csv.DictReader(file)\n", - " print(f\"Fieldnames: {dict_reader.fieldnames}\")\n", - "\n", - " for idx, record in enumerate(dict_reader, start=1):\n", - " print(f\"Record {idx}: {record}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -108,26 +63,11 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, "outputs": [], - "source": [ - "import csv\n", - "from pathlib import Path\n", - "\n", - "data = [\n", - " [\"hostname\", \"ip_address\", \"role\"],\n", - " [\"web02\", \"10.0.1.6\", \"webserver\"],\n", - " [\"app01\", \"10.0.3.15\", \"application\"],\n", - "]\n", - "\n", - "out_path = Path(\"output_basic.csv\")\n", - "\n", - "with out_path.open(\"w\", encoding=\"utf-8\", newline=\"\") as file:\n", - " writer = csv.writer(file)\n", - " writer.writerows(data)" - ] + "source": [] }, { "cell_type": "markdown", @@ -139,50 +79,10 @@ "- Call `.writeheader()` before `.writerows()`. " ] }, - { - "cell_type": "code", - "execution_count": 27, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", - "metadata": {}, - "outputs": [], - "source": [ - "import csv\n", - "from pathlib import Path\n", - "\n", - "records = [\n", - " {\n", - " \"host\": \"web01\",\n", - " \"port\": \"80\",\n", - " \"status\": \"running\"\n", - " },\n", - " {\n", - " \"host\": \"db02\",\n", - " \"status\": \"maintenance\",\n", - " \"tags\": \"prod,finance\"\n", - " }\n", - "]\n", - "\n", - "out_dict_path = Path(\"output_dict.csv\")\n", - "fieldnames = set()\n", - "\n", - "for record in records:\n", - " fieldnames = fieldnames | record.keys()\n", - "\n", - "with out_dict_path.open(\"w\", encoding=\"utf-8\", newline=\"\") as file:\n", - " writer = csv.DictWriter(\n", - " file,\n", - " fieldnames=fieldnames,\n", - " restval=\"undefined\",\n", - " extrasaction=\"ignore\"\n", - " )\n", - " writer.writeheader()\n", - " writer.writerows(records)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "e81ef9e8", + "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", "metadata": {}, "outputs": [], "source": [] @@ -204,7 +104,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/working-with-json.ipynb b/files-regex-data-formats/working-with-json.ipynb index e820571..d2526ca 100644 --- a/files-regex-data-formats/working-with-json.ipynb +++ b/files-regex-data-formats/working-with-json.ipynb @@ -25,33 +25,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Parsed data type: \n", - "Instance ID: i-12345\n", - "Tags: ['web', 'prod']\n" - ] - } - ], - "source": [ - "import json\n", - "\n", - "api_response_str = '{\"status\": \"active\", \"instance_id\": \"i-12345\", \"cores\": 4, \"tags\": [\"web\", \"prod\"]}'\n", - "\n", - "try:\n", - " data = json.loads(api_response_str)\n", - " print(f\"Parsed data type: {type(data)}\")\n", - " print(f\"Instance ID: {data.get(\"instance_id\", None)}\")\n", - " print(f\"Tags: {data.get(\"tags\", None)}\")\n", - "except json.JSONDecodeError as e:\n", - " print(f\"Failed to parse JSON: {e}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -66,43 +44,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Service: database\n", - "Enabled: True\n", - "--------------------\n", - "Service: cache\n", - "Enabled: False\n", - "--------------------\n", - "Service: api\n", - "Enabled: True\n", - "--------------------\n" - ] - } - ], - "source": [ - "import json\n", - "from pathlib import Path\n", - "\n", - "config_path = Path(\"service_config.json\")\n", - "\n", - "with config_path.open(\"r\", encoding=\"utf-8\") as file:\n", - " config_data = json.load(file)\n", - "\n", - "for config in config_data:\n", - " service_name = config.get(\"service\", None)\n", - "\n", - " if service_name:\n", - " print(f\"Service: {service_name}\")\n", - " print(f\"Enabled: {config.get(\"enabled\", False)}\")\n", - " print('-' * 20)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -116,47 +62,11 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Simple JSON:\n", - "{\"deployment\": \"frontend-v2\", \"replicas\": 3, \"ports\": [80, 443], \"health_check\": true, \"logs_enabled\": null}\n", - "\n", - "\n", - "Pretty JSON:\n", - "{\n", - " \"deployment\": \"frontend-v2\",\n", - " \"health_check\": true,\n", - " \"logs_enabled\": null,\n", - " \"ports\": [\n", - " 80,\n", - " 443\n", - " ],\n", - " \"replicas\": 3\n", - "}\n" - ] - } - ], - "source": [ - "import json\n", - "\n", - "python_data = {\n", - " \"deployment\": \"frontend-v2\",\n", - " \"replicas\": 3,\n", - " \"ports\": [80, 443],\n", - " \"health_check\": True,\n", - " \"logs_enabled\": None\n", - "}\n", - "\n", - "print(f\"Simple JSON:\\n{json.dumps(python_data)}\")\n", - "print(\"\\n\")\n", - "print(f\"Pretty JSON:\\n{json.dumps(python_data, indent=2, sort_keys=True)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -168,31 +78,10 @@ "- Pass the file handle and optional `indent` for formatting." ] }, - { - "cell_type": "code", - "execution_count": 17, - "id": "aab0eccc-81ba-4087-af69-7da00f95519c", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "from pathlib import Path\n", - "\n", - "output = {\n", - " \"status\": \"complete\",\n", - " \"items_processed\": 1492,\n", - " \"errors\": []\n", - "}\n", - "output_path = Path(\"run_summary.json\")\n", - "\n", - "with output_path.open(\"w\", encoding=\"utf-8\") as file:\n", - " json.dump(output, file, indent=2)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", + "id": "aab0eccc-81ba-4087-af69-7da00f95519c", "metadata": {}, "outputs": [], "source": [] @@ -214,7 +103,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/files-regex-data-formats/working-with-yaml.ipynb b/files-regex-data-formats/working-with-yaml.ipynb index 78dafa0..761220b 100644 --- a/files-regex-data-formats/working-with-yaml.ipynb +++ b/files-regex-data-formats/working-with-yaml.ipynb @@ -23,26 +23,15 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 1, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'service': {'name': 'user-api', 'port': 8080, 'enabled': True, 'tags': ['api', 'user', 'internal']}, 'staging': {'name': 'user-api', 'port': 8080, 'enabled': True, 'tags': ['api', 'user', 'internal'], 'replicas': 2}, 'production': {'name': 'user-api', 'port': 8080, 'enabled': True, 'tags': ['api', 'user', 'internal'], 'replicas': 4}}\n", - "\n", - "\n", - "{'literal': 'line 1\\nline 2\\nline 3\\n', 'folded': 'This is a long string that could go out of screen, so we will break this up into multiple lines to improve readability.\\n'}\n" - ] - } - ], + "outputs": [], "source": [ "import yaml, json\n", "\n", "snippet = \"\"\"\n", - "service: &svc\n", + "staging:\n", " name: user-api\n", " port: 8080\n", " enabled: true\n", @@ -50,31 +39,17 @@ " - api\n", " - user\n", " - internal\n", - "staging:\n", - " <<: *svc\n", " replicas: 2\n", "production:\n", - " <<: *svc\n", + " name: user-api\n", + " port: 8080\n", + " enabled: true\n", + " tags:\n", + " - api\n", + " - user\n", + " - internal\n", " replicas: 4\n", - "\"\"\"\n", - "\n", - "parsed = yaml.safe_load(snippet)\n", - "print(parsed)\n", - "\n", - "multiline_demo = \"\"\"\n", - "literal: |\n", - " line 1\n", - " line 2\n", - " line 3\n", - "folded: >\n", - " This is a long string that\n", - " could go out of screen, so\n", - " we will break this up into\n", - " multiple lines to improve\n", - " readability.\n", - "\"\"\"\n", - "print(\"\\n\")\n", - "print(yaml.safe_load(multiline_demo))" + "\"\"\"" ] }, { @@ -90,37 +65,11 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Compose version: 3.8\n", - "Web image\t: myapp:latest\n", - "Redis image\t: redis:alpine\n" - ] - } - ], - "source": [ - "import yaml\n", - "from pathlib import Path\n", - "\n", - "compose = Path(\"compose.yaml\")\n", - "\n", - "try:\n", - " with compose.open(\"r\", encoding=\"utf-8\") as file:\n", - " config = yaml.safe_load(file)\n", - " print(f\"Compose version: {config[\"version\"]}\")\n", - " \n", - " for svc, options in config[\"services\"].items():\n", - " print(f\"{svc.capitalize()} image\\t: {options[\"image\"]}\")\n", - "except yaml.YAMLError as e:\n", - " print(\"YAML error:\")\n", - " print(e)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -132,32 +81,10 @@ "- Set `stream` to an open file handle to write directly; leave it `None` to return a string." ] }, - { - "cell_type": "code", - "execution_count": 36, - "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "from pathlib import Path\n", - "\n", - "python_cfg = {\n", - " \"service\": {\"name\": \"listener-service\", \"port\": 6789, \"workers\": 4, \"enabled\": False},\n", - " \"queues\": [\"high\", \"default\", \"low\"],\n", - " \"retry_policy\": None,\n", - "}\n", - "\n", - "output_path = Path(\"listener_config.yaml\")\n", - "\n", - "with output_path.open(\"w\", encoding=\"utf-8\") as file:\n", - " yaml.dump(python_cfg, file, sort_keys=False, default_flow_style=False)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", + "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, "outputs": [], "source": [] @@ -179,7 +106,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/decorators-intro.ipynb b/generators-decorators/decorators-intro.ipynb index d6d27c9..dfe6566 100644 --- a/generators-decorators/decorators-intro.ipynb +++ b/generators-decorators/decorators-intro.ipynb @@ -31,40 +31,11 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "6bdc65c1-1957-45d9-bd0e-f373b9e76611", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running a simple task...\n", - "simple_task took 0.305s\n" - ] - } - ], - "source": [ - "import time\n", - "\n", - "def simple_task(sleep_duration):\n", - " time.sleep(sleep_duration)\n", - " print(\"Running a simple task...\")\n", - "\n", - "def timing_decorator(original_function):\n", - " def wrapper(*args, **kwargs):\n", - " start = time.perf_counter()\n", - " result = original_function(*args, **kwargs)\n", - " duration = time.perf_counter() - start\n", - " print(f\"{original_function.__name__} took {duration:.3f}s\")\n", - "\n", - " return result\n", - "\n", - " return wrapper\n", - "\n", - "simple_task = timing_decorator(simple_task)\n", - "simple_task(0.3)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -78,33 +49,10 @@ "- This keeps the decoration visible and close to the function definition, improving readability. " ] }, - { - "cell_type": "code", - "execution_count": 8, - "id": "f5763005-acce-4f00-b741-c85a10b73ea5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running another task...\n", - "another_task took 0.000s\n" - ] - } - ], - "source": [ - "@timing_decorator\n", - "def another_task():\n", - " print(\"Running another task...\")\n", - "\n", - "another_task()" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "3f393e0c-46ec-44b6-88d5-e941c37e9e33", + "id": "f5763005-acce-4f00-b741-c85a10b73ea5", "metadata": {}, "outputs": [], "source": [] @@ -126,7 +74,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/decorators-with-arguments.ipynb b/generators-decorators/decorators-with-arguments.ipynb index abef27f..340ce7a 100644 --- a/generators-decorators/decorators-with-arguments.ipynb +++ b/generators-decorators/decorators-with-arguments.ipynb @@ -18,18 +18,7 @@ "id": "294245b3-c5d8-4d85-8b99-e3b5863e86f9", "metadata": {}, "outputs": [], - "source": [ - "def timing_decorator(original_function):\n", - " def wrapper(*args, **kwargs):\n", - " start = time.perf_counter()\n", - " result = original_function(*args, **kwargs)\n", - " duration = time.perf_counter() - start\n", - " print(f\"{original_function.__name__} took {duration:.3f}s\")\n", - "\n", - " return result\n", - "\n", - " return wrapper" - ] + "source": [] }, { "cell_type": "markdown", @@ -55,52 +44,10 @@ "- The factory takes `max_attempts`; the wrapper loops until success or until attempts are exhausted, re‑raising the last error. " ] }, - { - "cell_type": "code", - "execution_count": 19, - "id": "23014bc7-7dfe-45dd-a84d-c2bea1ae4944", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Attempt 1/4\n", - "Result: Success!\n" - ] - } - ], - "source": [ - "import random\n", - "\n", - "def retry(max_attempts=3):\n", - " def decorator(func):\n", - " def wrapper(*args, **kwargs):\n", - " for attempt in range(1, max_attempts + 1):\n", - " try:\n", - " print(f\"Attempt {attempt}/{max_attempts}\")\n", - " return func(*args, **kwargs)\n", - " except Exception as e:\n", - " print(f\" Error: {e}\")\n", - " if attempt == max_attempts:\n", - " raise\n", - "\n", - " return wrapper\n", - " return decorator\n", - "\n", - "@retry(4)\n", - "def sometimes_fails():\n", - " if random.random() < 0.7:\n", - " raise RuntimeError(\"Flaky failure\")\n", - " return \"Success!\"\n", - "\n", - "print(f\"Result: {sometimes_fails()}\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "20d39292-7ab1-46ec-96a7-e7e82ac284eb", + "id": "23014bc7-7dfe-45dd-a84d-c2bea1ae4944", "metadata": {}, "outputs": [], "source": [] @@ -122,7 +69,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/functions-first-class-citizens.ipynb b/generators-decorators/functions-first-class-citizens.ipynb index 4a69eff..b37f8bc 100644 --- a/generators-decorators/functions-first-class-citizens.ipynb +++ b/generators-decorators/functions-first-class-citizens.ipynb @@ -14,7 +14,7 @@ " - Return them from other functions\n", " - Stash them in data structures. \n", "- This flexibility is the foundation for patterns such as callbacks, plugin registries, and decorators.\n", - "- \n", + "\n", "## Assigning Functions to Variables\n", "\n", "- A variable can reference the function object itself, **not** its return value. \n", @@ -24,27 +24,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "d264d4b3-d203-43ab-a082-95c191c01a96", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n", - "Hello, Alice!\n" - ] - } - ], - "source": [ - "def greet(name):\n", - " print(f\"Hello, {name}!\")\n", - "\n", - "say_hello = greet\n", - "print(say_hello is greet)\n", - "say_hello(\"Alice\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -60,40 +44,11 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "6566b5fd-692b-45ff-9506-46a0d686658b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Applying add to (1, 2)\n", - "3\n", - "Applying mul to (1, 2, 3, 4)\n", - "24\n" - ] - } - ], - "source": [ - "def apply_operation(operation, *operands):\n", - " print(f\"Applying {operation.__name__} to {operands}\")\n", - " return operation(*operands)\n", - "\n", - "def add(*numbers):\n", - " return sum(numbers)\n", - "\n", - "def mul(*numbers):\n", - " result = 1\n", - "\n", - " for n in numbers:\n", - " result *= n\n", - "\n", - " return result\n", - "\n", - "print(apply_operation(add, 1, 2))\n", - "print(apply_operation(mul, 1, 2, 3, 4))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -109,32 +64,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "5c07388b-f91a-4036-8dbf-4d90af790af1", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hitting endpoint /users with method GET and auth token alice-token\n", - "Hitting endpoint /health with method GET and auth token bob-token\n" - ] - } - ], - "source": [ - "def create_api_client(auth_token):\n", - " def api_client(endpoint, method):\n", - " return f\"Hitting endpoint {endpoint} with method {method} and auth token {auth_token}\"\n", - "\n", - " return api_client\n", - "\n", - "alice_api_client = create_api_client(\"alice-token\")\n", - "bob_api_client = create_api_client(\"bob-token\")\n", - "\n", - "print(alice_api_client(\"/users\", \"GET\"))\n", - "print(bob_api_client(\"/health\", \"GET\"))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -149,43 +83,11 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "a4bf423f-8536-4034-9df6-599dd7062dbb", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running task B\n", - "Running task A\n", - "Running task C\n", - "Running task B\n" - ] - } - ], - "source": [ - "def task_A():\n", - " print(\"Running task A\")\n", - "\n", - "def task_B():\n", - " print(\"Running task B\")\n", - "\n", - "def task_C():\n", - " print(\"Running task C\")\n", - "\n", - "pipeline = [task_B, task_A, task_C]\n", - "\n", - "for task in pipeline:\n", - " task()\n", - "\n", - "command_registry = {\n", - " \"start\": task_A,\n", - " \"process\": task_B,\n", - " \"stop\": task_C\n", - "}\n", - "command_registry[\"process\"]()" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -224,7 +126,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/functools-wraps.ipynb b/generators-decorators/functools-wraps.ipynb index c3b368c..6d97eb3 100644 --- a/generators-decorators/functools-wraps.ipynb +++ b/generators-decorators/functools-wraps.ipynb @@ -16,75 +16,16 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "04cdb1cc-3997-4652-8dae-f30de40becdd", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Introspection without @wraps:\n", - " __name__: wrapper\n", - " __doc__: None\n" - ] - } - ], - "source": [ - "def broken_decorator(func):\n", - " def wrapper(*args, **kwargs):\n", - " return func(*args, **kwargs)\n", - " return wrapper\n", - "\n", - "@broken_decorator\n", - "def add(a, b):\n", - " \"\"\"Return the sum of two numbers.\"\"\"\n", - " return a + b\n", - "\n", - "print(\"Introspection without @wraps:\")\n", - "print(f\" __name__: {add.__name__}\")\n", - "print(f\" __doc__: {add.__doc__}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "40aa3e73-a983-488d-9ef2-7a4cf8437ead", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Introspection with @wraps:\n", - " __name__: multiply\n", - " __doc__: Return the product of two numbers.\n" - ] - } - ], - "source": [ - "from functools import wraps\n", - "\n", - "def correct_decorator(func):\n", - " @wraps(func) # Best practice: Always use it!\n", - " def wrapper(*args, **kwargs):\n", - " return func(*args, **kwargs)\n", - " return wrapper\n", - "\n", - "@correct_decorator\n", - "def multiply(a, b):\n", - " \"\"\"Return the product of two numbers.\"\"\"\n", - " return a * b\n", - "\n", - "print(\"Introspection with @wraps:\")\n", - "print(f\" __name__: {multiply.__name__}\")\n", - "print(f\" __doc__: {multiply.__doc__}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "fa5d81b4-c9ac-4385-9160-49199cb84905", + "id": "40aa3e73-a983-488d-9ef2-7a4cf8437ead", "metadata": {}, "outputs": [], "source": [] @@ -106,7 +47,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/generators-intro.ipynb b/generators-decorators/generators-intro.ipynb index ce538f5..d2a0a4d 100644 --- a/generators-decorators/generators-intro.ipynb +++ b/generators-decorators/generators-intro.ipynb @@ -14,61 +14,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "47e78505-6a4b-47c0-8a13-db36b5615ea3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Returned object: of type \n", - "First call to next outside of for loop.\n", - "Generator function started...\n", - "Yielding 1\n", - "Remaining output from for loop.\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - "2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - "3\n", - "Resumed after yielding 3.\n", - "Generator function finished.\n" - ] - } - ], - "source": [ - "def count_up_to(limit):\n", - " \"\"\"Generates numbers from 1 up to (and including) the limit.\n", - "\n", - " Args:\n", - " limit (int): The upper limit for counting.\n", - "\n", - " Returns:\n", - " generator(int): The generator to lazily count up to limit.\n", - " \"\"\"\n", - " print(\"Generator function started...\")\n", - " n = 1\n", - "\n", - " while n <= limit:\n", - " print(f\"Yielding {n}\")\n", - " yield n\n", - " print(f\"Resumed after yielding {n}.\")\n", - " n += 1\n", - "\n", - " print(\"Generator function finished.\")\n", - "\n", - "count_gen = count_up_to(3)\n", - "print(f\"Returned object: {count_gen} of type {type(count_gen)}\")\n", - "\n", - "print(\"First call to next outside of for loop.\")\n", - "next(count_gen)\n", - "\n", - "print(\"Remaining output from for loop.\")\n", - "for number in count_gen:\n", - " print(number)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -84,68 +34,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "a773d052-6124-4cf5-a44f-370a06797e19", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generator object created: \n", - "filter_evens: starting\n", - "filter_evens: yielding 0\n", - "Received even: 0\n", - "filter_evens: yielding 2\n", - "Received even: 2\n", - "filter_evens: yielding 4\n", - "Received even: 4\n", - "filter_evens: finished\n", - "Generator object created: \n", - "filter_evens: starting\n", - "filter_evens: yielding 0\n", - "Received even: 0\n", - "filter_evens: yielding 2\n", - "Received even: 2\n", - "filter_evens: yielding 4\n", - "Received even: 4\n", - "filter_evens: finished\n" - ] - } - ], - "source": [ - "def filter_evens(data):\n", - " \"\"\"Yield only the even items from the input sequence.\n", - "\n", - " Args:\n", - " data (iterable(int or float)): The data to iterate through and filter.\n", - "\n", - " Returns:\n", - " generator(int or float): A generator object that yields the even items.\n", - " \"\"\"\n", - " print(\"filter_evens: starting\")\n", - "\n", - " for item in data:\n", - " if item % 2 == 0:\n", - " print(f\"filter_evens: yielding {item}\")\n", - " yield item\n", - "\n", - " print(\"filter_evens: finished\")\n", - "\n", - "evens_from_range = filter_evens(range(6))\n", - "\n", - "print(f\"Generator object created: {evens_from_range}\")\n", - "\n", - "for num in evens_from_range:\n", - " print(f\"Received even: {num}\")\n", - "\n", - "evens_from_list = filter_evens([0, 1, 2, 3, 4, 5])\n", - "\n", - "print(f\"Generator object created: {evens_from_list}\")\n", - "\n", - "for num in evens_from_list:\n", - " print(f\"Received even: {num}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -161,41 +54,11 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "dca0853e-c7fc-49c0-b210-b3faa09b7c3b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generator started\n", - "1\n", - "Generator resumed after yielding 1.\n", - "2\n", - "Generator resumed after yielding 2.\n", - "3\n" - ] - } - ], - "source": [ - "def demo_three_yields():\n", - " \"\"\"Demonstrate how having multiple yield statements work.\"\"\"\n", - " print(\"Generator started\")\n", - " yield 1\n", - " print(\"Generator resumed after yielding 1.\")\n", - " yield 2\n", - " print(\"Generator resumed after yielding 2.\")\n", - " yield 3\n", - " print(\"Generator finished.\")\n", - "\n", - "demo_gen = demo_three_yields()\n", - "\n", - "print(next(demo_gen))\n", - "print(next(demo_gen))\n", - "print(next(demo_gen))\n", - "# print(next(demo_gen)) # Uncommenting will raise a StopIteration Exception because there are no more yields" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -210,191 +73,19 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "aab0eccc-81ba-4087-af69-7da00f95519c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "First call to next outside of for loop.\n", - "Generator function started...\n", - "Yielding 1\n", - "1\n", - "Second call to next outside of for loop - now the value yielded is 2.\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - "2\n", - "Remaining output from for loop - prints from 3 onwards.\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - "3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - "4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - "5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n" - ] - } - ], - "source": [ - "count_gen = count_up_to(5)\n", - "\n", - "print(\"First call to next outside of for loop.\")\n", - "print(next(count_gen))\n", - "\n", - "print(\"Second call to next outside of for loop - now the value yielded is 2.\")\n", - "print(next(count_gen))\n", - "\n", - "print(\"Remaining output from for loop - prints from 3 onwards.\")\n", - "for number in count_gen:\n", - " print(number)" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "99fae2be-c457-4d44-aa05-75dd5713e2c5", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generator function started...\n", - "Yielding 1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 1:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 1:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 1:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 1:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Generator function started...\n", - "Yielding 1\n", - "Generator function started...\n", - "Yielding 1\n", - " - 1:1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 1:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 1:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 1:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 1:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - "Generator function started...\n", - "Yielding 1\n", - " - 2:1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 2:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 2:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 2:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 2:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - "Generator function started...\n", - "Yielding 1\n", - " - 3:1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 3:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 3:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 3:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 3:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - "Generator function started...\n", - "Yielding 1\n", - " - 4:1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 4:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 4:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 4:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 4:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - "Generator function started...\n", - "Yielding 1\n", - " - 5:1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - " - 5:2\n", - "Resumed after yielding 2.\n", - "Yielding 3\n", - " - 5:3\n", - "Resumed after yielding 3.\n", - "Yielding 4\n", - " - 5:4\n", - "Resumed after yielding 4.\n", - "Yielding 5\n", - " - 5:5\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n", - "Resumed after yielding 5.\n", - "Generator function finished.\n" - ] - } - ], - "source": [ - "count_gen = count_up_to(5)\n", - "\n", - "# Since generators have state, using the same generator object in nested loops can lead to issues.\n", - "# The inner for loop will complete the iteration, and the outer for loop will have a sinle pass.\n", - "for num in count_gen:\n", - " for num2 in count_gen:\n", - " print(f\" - {num}:{num2}\")\n", - "\n", - "# The solution to this is to use distinct generator objects.\n", - "for num in count_up_to(5):\n", - " for num2 in count_up_to(5):\n", - " print(f\" - {num}:{num2}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -407,48 +98,10 @@ "- A `for` loop over an exhausted generator does nothing on subsequent passes—you must call the function again for a fresh iterator. " ] }, - { - "cell_type": "code", - "execution_count": 19, - "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generator function started...\n", - "Yielding 1\n", - "1\n", - "Resumed after yielding 1.\n", - "Yielding 2\n", - "2\n", - "Resumed after yielding 2.\n", - "Generator function finished.\n", - "Generator finished\n" - ] - } - ], - "source": [ - "count_gen = count_up_to(2)\n", - "\n", - "print(next(count_gen))\n", - "print(next(count_gen))\n", - "\n", - "try:\n", - " print(next(count_gen)) # Will raise StopIteration exception\n", - "except StopIteration:\n", - " print(\"Generator finished\")\n", - "\n", - "# Nothing will happen because the generator is already exhausted\n", - "for number in count_gen:\n", - " print(number)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "d62f0579-b6f9-44b2-80e3-783ff520b3a9", + "id": "12b59ac1-b1ef-4d0d-b47e-a2b94f38ecc5", "metadata": {}, "outputs": [], "source": [] @@ -470,7 +123,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/handling-return-values.ipynb b/generators-decorators/handling-return-values.ipynb index f1171a3..67ce4d6 100644 --- a/generators-decorators/handling-return-values.ipynb +++ b/generators-decorators/handling-return-values.ipynb @@ -14,34 +14,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "432f44f0-1a6a-4285-b666-169ab4453c25", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LOG: Calling add\n", - "LOG: Finished add\n", - "Result seen by caller: None\n" - ] - } - ], - "source": [ - "def log_calls_broken(func):\n", - " def wrapper(*args, **kwargs):\n", - " print(f\"LOG: Calling {func.__name__}\")\n", - " func(*args, **kwargs)\n", - " print(f\"LOG: Finished {func.__name__}\")\n", - " return wrapper\n", - "\n", - "@log_calls_broken\n", - "def add(x, y):\n", - " return x + y\n", - "\n", - "print(f\"Result seen by caller: {add(2, 3)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -65,35 +42,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "ac78a336-0eba-4d18-9bc4-25f205e59962", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LOG: Calling multiply\n", - "LOG: Finished multiply\n", - "Result seen by caller: 6\n" - ] - } - ], - "source": [ - "def log_calls(func):\n", - " def wrapper(*args, **kwargs):\n", - " print(f\"LOG: Calling {func.__name__}\")\n", - " value = func(*args, **kwargs)\n", - " print(f\"LOG: Finished {func.__name__}\")\n", - " return value\n", - " return wrapper\n", - "\n", - "@log_calls\n", - "def multiply(a, b):\n", - " return a * b\n", - "\n", - "print(f\"Result seen by caller: {multiply(2, 3)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -107,54 +60,10 @@ "- A decorator that swallows exceptions changes program semantics unless that is its explicit purpose (e.g. retry). " ] }, - { - "cell_type": "code", - "execution_count": 13, - "id": "6741543f-870a-4c2f-8049-5f71c2bcfde4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ERROR] fail raised ValueError\n" - ] - }, - { - "ename": "ValueError", - "evalue": "simulated problem", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 14\u001b[39m\n\u001b[32m 10\u001b[39m \u001b[38;5;129m@log_and_reraise\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfail\u001b[39m():\n\u001b[32m 12\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33msimulated problem\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m14\u001b[39m \u001b[43mfail\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 4\u001b[39m, in \u001b[36mlog_and_reraise..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mwrapper\u001b[39m(*args, **kwargs):\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 6\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33m[ERROR] \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc.\u001b[34m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m raised \u001b[39m\u001b[38;5;132;01m{\u001b[39;00merr.\u001b[34m__class__\u001b[39m.\u001b[34m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 12\u001b[39m, in \u001b[36mfail\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m 10\u001b[39m \u001b[38;5;129m@log_and_reraise\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfail\u001b[39m():\n\u001b[32m---> \u001b[39m\u001b[32m12\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33msimulated problem\u001b[39m\u001b[33m\"\u001b[39m)\n", - "\u001b[31mValueError\u001b[39m: simulated problem" - ] - } - ], - "source": [ - "def log_and_reraise(func):\n", - " def wrapper(*args, **kwargs):\n", - " try:\n", - " return func(*args, **kwargs)\n", - " except Exception as err:\n", - " print(f\"[ERROR] {func.__name__} raised {err.__class__.__name__}\")\n", - " raise\n", - " return wrapper\n", - "\n", - "@log_and_reraise\n", - "def fail():\n", - " raise ValueError(\"simulated problem\")\n", - "\n", - "fail()" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "7074f501-92f7-46cc-aec6-2ec92e6dbe4e", + "id": "6741543f-870a-4c2f-8049-5f71c2bcfde4", "metadata": {}, "outputs": [], "source": [] @@ -176,7 +85,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/lazy-pipelines.ipynb b/generators-decorators/lazy-pipelines.ipynb index 46a7290..a15a315 100644 --- a/generators-decorators/lazy-pipelines.ipynb +++ b/generators-decorators/lazy-pipelines.ipynb @@ -24,116 +24,6 @@ "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, "outputs": [], - "source": [ - "# 1. DONE Ingest the log lines\n", - "# 2. DONE Filter log lines based on either level or message substring\n", - "# 3. DONE Extract and return only the message attribute of the logs\n", - "\n", - "import sys\n", - "import json\n", - "\n", - "\n", - "def read_logs(filepath):\n", - " \"\"\"Reads the contents of a file line by line.\n", - "\n", - " Args:\n", - " filepath (str): The path where the file is located.\n", - "\n", - " Returns:\n", - " generator (dict(str)): The json dictionary for the log line.\n", - " \"\"\"\n", - " with open(filepath, 'r') as file:\n", - " for line in file:\n", - " line = line.strip()\n", - " if not line:\n", - " continue\n", - " yield json.loads(line)\n", - "\n", - "\n", - "def filter_logs(logs, level=None, message_substring=None):\n", - " \"\"\"Filters any iterable containing dictionaries by either level or message_substring (or both)\n", - "\n", - " Args:\n", - " logs (iterable(dict)): Iterable containing the logs to be filtered.\n", - " level (str): The log level to keep. Defaults to None.\n", - " message_substring (str): The pattern to look for in messages. Defaults to None.\n", - "\n", - " Returns:\n", - " generator (dict(str)): The json dictionary for the filtered log.\n", - " \"\"\"\n", - "\n", - " for log in logs:\n", - " if (\n", - " level is not None\n", - " and log.get(\"level\", \"\").lower() != level.lower()\n", - " ):\n", - " continue\n", - "\n", - " if (\n", - " message_substring is not None\n", - " and message_substring.lower() not in log.get(\"message\", \"\").lower()\n", - " ):\n", - " continue\n", - "\n", - " yield log\n", - "\n", - "\n", - "def extract_field(logs, field=\"message\"):\n", - " \"\"\"Extracts a specific field from any iterable containing dictionaries.\n", - "\n", - " Args:\n", - " logs (iterable(dict)): Iterable containing the logs to be evaluated.\n", - " field (str): The field to return. Defaults to 'message'.\n", - "\n", - " Returns:\n", - " generator (str): The value of the extracted field.\n", - " \"\"\"\n", - " for log in logs:\n", - " yield log.get(field, \"\").strip()\n", - "\n", - "\n", - "def get_first_n(logs, n=10):\n", - " \"\"\"Extracts the first n items from the provided iterable.\n", - "\n", - " Args:\n", - " logs (iterable(T)): Iterable from which items will be extracted.\n", - " n (int): The number of items to extract.\n", - "\n", - " Returns:\n", - " generator (T): The item from the iterable.\n", - " \"\"\"\n", - " count = 0\n", - "\n", - " for log in logs:\n", - " if count >= n:\n", - " break\n", - "\n", - " yield log\n", - " count += 1\n", - "\n", - "\n", - "logs_gen = read_logs(\"large_logs.txt\")\n", - "filter_gen = filter_logs(logs_gen, message_substring=\"user\")\n", - "extract_gen = extract_field(filter_gen, \"message\")\n", - "\n", - "\n", - "for log in get_first_n(extract_gen, 4):\n", - " print(log)\n", - "\n", - "\n", - "print(\"Generator object sizes (in bytes):\",\n", - " sys.getsizeof(logs_gen),\n", - " sys.getsizeof(filter_gen),\n", - " sys.getsizeof(extract_gen)\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3cf30897-f289-4b77-9a9e-42056d6110ec", - "metadata": {}, - "outputs": [], "source": [] } ], @@ -153,7 +43,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/return-vs-yield.ipynb b/generators-decorators/return-vs-yield.ipynb index 8f960fd..fbd69b2 100644 --- a/generators-decorators/return-vs-yield.ipynb +++ b/generators-decorators/return-vs-yield.ipynb @@ -20,40 +20,11 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "271f0aa0-26fe-48f6-94f8-aee210bec5bc", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Regular function started.\n", - "\tAdding server-0\n", - "\tAdding server-1\n", - "\tAdding server-2\n", - "Regular function finished.\n", - "Returned list: ['server-0', 'server-1', 'server-2']\n" - ] - } - ], - "source": [ - "def get_list_of_servers():\n", - " print(\"Regular function started.\")\n", - " servers = []\n", - "\n", - " for i in range(3):\n", - " server_name = f\"server-{i}\"\n", - " print(f\"\\tAdding {server_name}\")\n", - " servers.append(server_name)\n", - "\n", - " print(\"Regular function finished.\")\n", - "\n", - " return servers\n", - "\n", - "servers = get_list_of_servers()\n", - "print(f\"Returned list: {servers}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -69,41 +40,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "24fdd423-5b5a-4783-8a48-b2cb89f3d381", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generator function started.\n", - "\tYielding server-0\n", - "Server received: server-0\n", - "\tYielding server-1\n", - "Server received: server-1\n", - "\tYielding server-2\n", - "Server received: server-2\n", - "Generator function finished.\n" - ] - } - ], - "source": [ - "def yield_servers(count):\n", - " print(\"Generator function started.\")\n", - "\n", - " for i in range(count):\n", - " server_name = f\"server-{i}\"\n", - " print(f\"\\tYielding {server_name}\")\n", - " yield server_name\n", - "\n", - " print(\"Generator function finished.\")\n", - "\n", - "servers_gen = yield_servers(3)\n", - "\n", - "for server in servers_gen:\n", - " print(f\"Server received: {server}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -122,73 +63,11 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "62f26849-af68-4920-b135-7cf8bd340093", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "REGULAR : Returned ['Data item 0.', 'Data item 1.', 'Data item 2.', 'Data item 3.', 'Data item 4.'] in 0.52s\n", - "Received data Data item 0. in 0.10s\n", - "Received data Data item 1. in 0.10s\n", - "Received data Data item 2. in 0.10s\n", - "Received data Data item 3. in 0.10s\n", - "Received data Data item 4. in 0.10s\n" - ] - } - ], - "source": [ - "import time\n", - "\n", - "def load_data_eagerly(count, delay=0.1):\n", - " \"\"\"Simulates loading `count` items eagerly with a delay between each item.\n", - "\n", - " Args:\n", - " count (int): The number of items to load\n", - " delay (float, defaults to 0.1): The delay between each item loaded, in seconds.\n", - "\n", - " Returns:\n", - " list(str): The loaded items\n", - " \"\"\"\n", - " result = []\n", - " for i in range(count):\n", - " time.sleep(delay)\n", - " result.append(f\"Data item {i}.\")\n", - "\n", - " return result\n", - "\n", - "t0 = time.time()\n", - "data = load_data_eagerly(5)\n", - "t1 = time.time()\n", - "print(f\"REGULAR : Returned {data} in {t1-t0:.2f}s\")\n", - "\n", - "def load_data_lazily(count, delay=0.1):\n", - " \"\"\"Simulates loading `count` items lazily with a delay between each item.\n", - "\n", - " Args:\n", - " count (int): The number of items to load\n", - " delay (float, defaults to 0.1): The delay between each item loaded, in seconds.\n", - "\n", - " Returns:\n", - " generator(str): A generator that yields each data item.\n", - " \"\"\"\n", - " for i in range(count):\n", - " time.sleep(delay)\n", - " yield f\"Data item {i}.\"\n", - "\n", - "data_gen = load_data_lazily(5)\n", - "\n", - "while True:\n", - " t0 = time.time()\n", - " try:\n", - " item = next(data_gen)\n", - " except StopIteration:\n", - " break\n", - " t1 = time.time()\n", - " print(f\"Received data {item} in {t1-t0:.2f}s\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -215,7 +94,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/generators-decorators/stacking-decorators.ipynb b/generators-decorators/stacking-decorators.ipynb index c7e716d..f837915 100644 --- a/generators-decorators/stacking-decorators.ipynb +++ b/generators-decorators/stacking-decorators.ipynb @@ -25,114 +25,8 @@ { "cell_type": "code", "execution_count": null, - "id": "c4e8bb8b-1719-4158-b465-af7ff8806754", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6719ee97-9eb4-486b-b738-a867698fe893", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- Call foo() (A outside, B inside) ---\n", - "A before\n", - "B before\n", - " >>> inside foo\n", - "B after\n", - "A after\n", - "\n", - "--- Call bar() (B outside, A inside) ---\n", - "B before\n", - "A before\n", - " >>> inside bar\n", - "A after\n", - "B after\n" - ] - } - ], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a323899-7968-4735-85f9-a5dfa09ac27d", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 9, "id": "0e3d2682-85e2-4b63-ab1a-fced5066aee0", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A before\n", - "B before\n", - " >>> inside function foo\n", - "B after\n", - "A after\n", - "----\n", - "B before\n", - "A before\n", - " >>> inside function bar\n", - "A after\n", - "B after\n" - ] - } - ], - "source": [ - "from functools import wraps\n", - "\n", - "def decorator_A(func):\n", - " @wraps(func)\n", - " def wrapper(*args, **kwargs):\n", - " print(\"A before\")\n", - " result = func(*args, **kwargs)\n", - " print(\"A after\")\n", - " return result\n", - " return wrapper\n", - "\n", - "def decorator_B(func):\n", - " @wraps(func)\n", - " def wrapper(*args, **kwargs):\n", - " print(\"B before\")\n", - " result = func(*args, **kwargs)\n", - " print(\"B after\")\n", - " return result\n", - " return wrapper\n", - "\n", - "@decorator_A\n", - "@decorator_B\n", - "def foo():\n", - " print(\" >>> inside function foo\")\n", - "\n", - "@decorator_B\n", - "@decorator_A\n", - "def bar():\n", - " print(\" >>> inside function bar\")\n", - "\n", - "foo()\n", - "\n", - "print(\"----\")\n", - "\n", - "bar()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "596a4824-f7c1-416d-8a02-4653027cd0d3", - "metadata": {}, "outputs": [], "source": [] } @@ -153,7 +47,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/http-requests/authentication.ipynb b/http-requests/authentication.ipynb index e3612b3..59f3185 100644 --- a/http-requests/authentication.ipynb +++ b/http-requests/authentication.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "27a10c2f-f21d-48af-af9f-367a43544acf", "metadata": {}, "outputs": [], @@ -31,34 +31,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "public_endpoint (https://api.github.com/zen) : 200\n", - "Design for failure.\n", - "protected_endpoint (https://api.github.com/user) : 401\n", - "{\"message\":\"Requires authentication\",\"documentation_url\":\"https://docs.github.com/rest/users/users#get-the-authenticated-user\",\"status\":\"401\"}\n" - ] - } - ], - "source": [ - "import requests\n", - "\n", - "urls = {\n", - " \"public_endpoint\": f\"{GITHUB_ENDPOINT}/zen\",\n", - " \"protected_endpoint\": f\"{GITHUB_ENDPOINT}/user\",\n", - "}\n", - "\n", - "for description, url in urls.items():\n", - " res = requests.get(url, timeout=5)\n", - " print(f\"{description} ({url}) : {res.status_code}\")\n", - " print(res.text[:200])" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -74,33 +51,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "401 Client Error: UNAUTHORIZED for url: https://httpbin.org/basic-auth/myuser/myotherpwd\n" - ] - } - ], - "source": [ - "import requests\n", - "import json\n", - "\n", - "url = f\"{HTTPBIN_ENDPOINT}/basic-auth/myuser/myotherpwd\"\n", - "\n", - "try:\n", - " res = requests.get(url, auth=(\"myuser\", \"mypasswd\"), timeout=10)\n", - " res.raise_for_status()\n", - " print(f\"Status code: {res.status_code}\")\n", - " print(\"Response JSON:\")\n", - " print(json.dumps(res.json()))\n", - "except requests.exceptions.HTTPError as err:\n", - " print(err)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -116,53 +71,11 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "5cfddd0c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Token: github_pat_11AH\n", - "Status code: 200\n", - "Invalid JSON in response body. Defaulting to text:\n", - "Mind your words, they are important.\n", - "Status code: 200\n", - "Authenticated user: lauromueller\n" - ] - } - ], - "source": [ - "import requests\n", - "import os\n", - "from dotenv import load_dotenv\n", - "\n", - "load_dotenv(override=True)\n", - "\n", - "token = os.getenv(\"GH_PAT\", \"\")\n", - "print(f\"Token: {token[:15]}\")\n", - "\n", - "urls = {\n", - " \"public_endpoint\": f\"{GITHUB_ENDPOINT}/zen\",\n", - " \"protected_endpoint\": f\"{GITHUB_ENDPOINT}/user\",\n", - "}\n", - "\n", - "for description, url in urls.items():\n", - " try:\n", - " headers = {\n", - " \"Authorization\": f\"Bearer {token}\"\n", - " }\n", - " res = requests.get(url, headers=headers, timeout=10)\n", - " res.raise_for_status()\n", - " print(f\"Status code: {res.status_code}\")\n", - " print(f\"Authenticated user: {res.json().get(\"login\")}\")\n", - " except requests.exceptions.JSONDecodeError as err:\n", - " print(f\"Invalid JSON in response body. Defaulting to text:\")\n", - " print(res.text[:200])\n", - " except requests.exceptions.HTTPError as err:\n", - " print(err)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -200,7 +113,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/http-requests/handling-errors-status-codes.ipynb b/http-requests/handling-errors-status-codes.ipynb index 6400e0a..cd2b515 100644 --- a/http-requests/handling-errors-status-codes.ipynb +++ b/http-requests/handling-errors-status-codes.ipynb @@ -26,7 +26,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "720c3103-3517-4dff-9f84-83522b14c4f8", "metadata": {}, "outputs": [], @@ -37,31 +37,11 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ok: status 200\n", - "not_found: status 404\n" - ] - } - ], - "source": [ - "import requests\n", - "\n", - "urls = {\n", - " \"ok\": f\"{GITHUB_ENDPOINT}/zen\",\n", - " \"not_found\": f\"{GITHUB_ENDPOINT}/nonexistentendpoint\"\n", - "}\n", - "\n", - "for description, url in urls.items():\n", - " response = requests.get(url, timeout=5)\n", - " print(f\"{description}: status {response.status_code}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -77,31 +57,11 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ok: ok? Yes\n", - "not_found: ok? No. Failed with status 404\n" - ] - } - ], - "source": [ - "import requests\n", - "\n", - "urls = {\n", - " \"ok\": f\"{GITHUB_ENDPOINT}/zen\",\n", - " \"not_found\": f\"{GITHUB_ENDPOINT}/nonexistentendpoint\"\n", - "}\n", - "\n", - "for description, url in urls.items():\n", - " response = requests.get(url, timeout=5)\n", - " print(f\"{description}: ok? {\"Yes\" if response.ok else f\"No. Failed with status {response.status_code}\"}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -117,51 +77,11 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requesting: https://api.github.com/zen\n", - " Success!\n", - "Requesting: https://api.github.com/nonexistentendpoint\n", - " HTTPError: 404 Client Error: Not Found for url: https://api.github.com/nonexistentendpoint (status 404)\n", - " Error details:\n", - "{\n", - " \"message\": \"Not Found\",\n", - " \"documentation_url\": \"https://docs.github.com/rest\",\n", - " \"status\": \"404\"\n", - "}\n" - ] - } - ], - "source": [ - "import requests\n", - "import json\n", - "\n", - "urls = {\n", - " \"ok\": f\"{GITHUB_ENDPOINT}/zen\",\n", - " \"not_found\": f\"{GITHUB_ENDPOINT}/nonexistentendpoint\"\n", - "}\n", - "\n", - "for url in urls.values():\n", - " print(f\"Requesting: {url}\")\n", - " try:\n", - " res = requests.get(url, timeout=5)\n", - " res.raise_for_status()\n", - " print(\" Success!\")\n", - " except requests.exceptions.HTTPError as err:\n", - " print(f\" HTTPError: {err} (status {err.response.status_code})\")\n", - " try:\n", - " details = err.response.json()\n", - " print(\" Error details:\")\n", - " print(json.dumps(details, indent=2))\n", - " except ValueError:\n", - " print(f\" Non-JSON response body: {err.response.text[:100]}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -198,7 +118,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/http-requests/making-http-requests.ipynb b/http-requests/making-http-requests.ipynb index 45d65e7..6f0454d 100644 --- a/http-requests/making-http-requests.ipynb +++ b/http-requests/making-http-requests.ipynb @@ -16,12 +16,13 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 1, "id": "10c80e62-7082-430a-a2d3-cedea4dcfad6", "metadata": {}, "outputs": [], "source": [ - "GITHUB_ENDPOINT = \"https://api.github.com\"" + "GITHUB_ENDPOINT = \"https://api.github.com\"\n", + "HTTPBIN_ENDPOINT = \"https://httpbin.org\"" ] }, { @@ -38,81 +39,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Status code: 200\n", - "Content-Type: application/json; charset=utf-8\n", - "Available endpoints:\n", - "{\n", - " \"current_user_url\": \"https://api.github.com/user\",\n", - " \"current_user_authorizations_html_url\": \"https://github.com/settings/connections/applications{/client_id}\",\n", - " \"authorizations_url\": \"https://api.github.com/authorizations\",\n", - " \"code_search_url\": \"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}\",\n", - " \"commit_search_url\": \"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}\",\n", - " \"emails_url\": \"https://api.github.com/user/emails\",\n", - " \"emojis_url\": \"https://api.github.com/emojis\",\n", - " \"events_url\": \"https://api.github.com/events\",\n", - " \"feeds_url\": \"https://api.github.com/feeds\",\n", - " \"followers_url\": \"https://api.github.com/user/followers\",\n", - " \"following_url\": \"https://api.github.com/user/following{/target}\",\n", - " \"gists_url\": \"https://api.github.com/gists{/gist_id}\",\n", - " \"hub_url\": \"https://api.github.com/hub\",\n", - " \"issue_search_url\": \"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}\",\n", - " \"issues_url\": \"https://api.github.com/issues\",\n", - " \"keys_url\": \"https://api.github.com/user/keys\",\n", - " \"label_search_url\": \"https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}\",\n", - " \"notifications_url\": \"https://api.github.com/notifications\",\n", - " \"organization_url\": \"https://api.github.com/orgs/{org}\",\n", - " \"organization_repositories_url\": \"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}\",\n", - " \"organization_teams_url\": \"https://api.github.com/orgs/{org}/teams\",\n", - " \"public_gists_url\": \"https://api.github.com/gists/public\",\n", - " \"rate_limit_url\": \"https://api.github.com/rate_limit\",\n", - " \"repository_url\": \"https://api.github.com/repos/{owner}/{repo}\",\n", - " \"repository_search_url\": \"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}\",\n", - " \"current_user_repositories_url\": \"https://api.github.com/user/repos{?type,page,per_page,sort}\",\n", - " \"starred_url\": \"https://api.github.com/user/starred{/owner}{/repo}\",\n", - " \"starred_gists_url\": \"https://api.github.com/gists/starred\",\n", - " \"topic_search_url\": \"https://api.github.com/search/topics?q={query}{&page,per_page}\",\n", - " \"user_url\": \"https://api.github.com/users/{user}\",\n", - " \"user_organizations_url\": \"https://api.github.com/user/orgs\",\n", - " \"user_repositories_url\": \"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}\",\n", - " \"user_search_url\": \"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}\"\n", - "}\n" - ] - } - ], - "source": [ - "import requests\n", - "import json\n", - "\n", - "response = requests.get(GITHUB_ENDPOINT, timeout=10)\n", - "\n", - "print(f\"Status code: {response.status_code}\")\n", - "print(f\"Content-Type: {response.headers.get(\"Content-Type\")}\")\n", - "\n", - "\"\"\"\n", - "# Commenting out for brevity, but leaving for documentation\n", - "\n", - "print(\".text attribute:\")\n", - "print(response.text)\n", - "print(\"\\n\")\n", - "print(\".content attribute:\")\n", - "print(response.content)\n", - "print(\"\\n\")\n", - "print(\".json() method:\")\n", - "print(response.json())\n", - "\"\"\"\n", - "\n", - "data = response.json()\n", - "print(\"Available endpoints:\")\n", - "print(json.dumps(data, indent=2))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -128,176 +59,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requested URL: https://api.github.com/search/repositories?q=python+devops&sort=stars&order=desc&per_page=5\n", - "Found 6203 repositories. Top 5:\n", - "- devops-exercises (Stars: 76048)\n", - "- sentry (Stars: 40891)\n", - "- cli (Stars: 35645)\n", - "- jumpserver (Stars: 27716)\n", - "- DevOps-Roadmap (Stars: 15895)\n", - "{\n", - " \"id\": 212639071,\n", - " \"node_id\": \"MDEwOlJlcG9zaXRvcnkyMTI2MzkwNzE=\",\n", - " \"name\": \"devops-exercises\",\n", - " \"full_name\": \"bregman-arie/devops-exercises\",\n", - " \"private\": false,\n", - " \"owner\": {\n", - " \"login\": \"bregman-arie\",\n", - " \"id\": 10349437,\n", - " \"node_id\": \"MDQ6VXNlcjEwMzQ5NDM3\",\n", - " \"avatar_url\": \"https://avatars.githubusercontent.com/u/10349437?v=4\",\n", - " \"gravatar_id\": \"\",\n", - " \"url\": \"https://api.github.com/users/bregman-arie\",\n", - " \"html_url\": \"https://github.com/bregman-arie\",\n", - " \"followers_url\": \"https://api.github.com/users/bregman-arie/followers\",\n", - " \"following_url\": \"https://api.github.com/users/bregman-arie/following{/other_user}\",\n", - " \"gists_url\": \"https://api.github.com/users/bregman-arie/gists{/gist_id}\",\n", - " \"starred_url\": \"https://api.github.com/users/bregman-arie/starred{/owner}{/repo}\",\n", - " \"subscriptions_url\": \"https://api.github.com/users/bregman-arie/subscriptions\",\n", - " \"organizations_url\": \"https://api.github.com/users/bregman-arie/orgs\",\n", - " \"repos_url\": \"https://api.github.com/users/bregman-arie/repos\",\n", - " \"events_url\": \"https://api.github.com/users/bregman-arie/events{/privacy}\",\n", - " \"received_events_url\": \"https://api.github.com/users/bregman-arie/received_events\",\n", - " \"type\": \"User\",\n", - " \"user_view_type\": \"public\",\n", - " \"site_admin\": false\n", - " },\n", - " \"html_url\": \"https://github.com/bregman-arie/devops-exercises\",\n", - " \"description\": \"Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions\",\n", - " \"fork\": false,\n", - " \"url\": \"https://api.github.com/repos/bregman-arie/devops-exercises\",\n", - " \"forks_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/forks\",\n", - " \"keys_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/keys{/key_id}\",\n", - " \"collaborators_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/collaborators{/collaborator}\",\n", - " \"teams_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/teams\",\n", - " \"hooks_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/hooks\",\n", - " \"issue_events_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/issues/events{/number}\",\n", - " \"events_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/events\",\n", - " \"assignees_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/assignees{/user}\",\n", - " \"branches_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/branches{/branch}\",\n", - " \"tags_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/tags\",\n", - " \"blobs_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/git/blobs{/sha}\",\n", - " \"git_tags_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/git/tags{/sha}\",\n", - " \"git_refs_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/git/refs{/sha}\",\n", - " \"trees_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/git/trees{/sha}\",\n", - " \"statuses_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/statuses/{sha}\",\n", - " \"languages_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/languages\",\n", - " \"stargazers_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/stargazers\",\n", - " \"contributors_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/contributors\",\n", - " \"subscribers_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/subscribers\",\n", - " \"subscription_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/subscription\",\n", - " \"commits_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/commits{/sha}\",\n", - " \"git_commits_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/git/commits{/sha}\",\n", - " \"comments_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/comments{/number}\",\n", - " \"issue_comment_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/issues/comments{/number}\",\n", - " \"contents_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/contents/{+path}\",\n", - " \"compare_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/compare/{base}...{head}\",\n", - " \"merges_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/merges\",\n", - " \"archive_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/{archive_format}{/ref}\",\n", - " \"downloads_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/downloads\",\n", - " \"issues_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/issues{/number}\",\n", - " \"pulls_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/pulls{/number}\",\n", - " \"milestones_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/milestones{/number}\",\n", - " \"notifications_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/notifications{?since,all,participating}\",\n", - " \"labels_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/labels{/name}\",\n", - " \"releases_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/releases{/id}\",\n", - " \"deployments_url\": \"https://api.github.com/repos/bregman-arie/devops-exercises/deployments\",\n", - " \"created_at\": \"2019-10-03T17:31:21Z\",\n", - " \"updated_at\": \"2025-05-21T08:59:25Z\",\n", - " \"pushed_at\": \"2025-04-24T19:36:05Z\",\n", - " \"git_url\": \"git://github.com/bregman-arie/devops-exercises.git\",\n", - " \"ssh_url\": \"git@github.com:bregman-arie/devops-exercises.git\",\n", - " \"clone_url\": \"https://github.com/bregman-arie/devops-exercises.git\",\n", - " \"svn_url\": \"https://github.com/bregman-arie/devops-exercises\",\n", - " \"homepage\": \"\",\n", - " \"size\": 7771,\n", - " \"stargazers_count\": 76048,\n", - " \"watchers_count\": 76048,\n", - " \"language\": \"Python\",\n", - " \"has_issues\": true,\n", - " \"has_projects\": true,\n", - " \"has_downloads\": true,\n", - " \"has_wiki\": true,\n", - " \"has_pages\": true,\n", - " \"has_discussions\": true,\n", - " \"forks_count\": 16975,\n", - " \"mirror_url\": null,\n", - " \"archived\": false,\n", - " \"disabled\": false,\n", - " \"open_issues_count\": 41,\n", - " \"license\": {\n", - " \"key\": \"other\",\n", - " \"name\": \"Other\",\n", - " \"spdx_id\": \"NOASSERTION\",\n", - " \"url\": null,\n", - " \"node_id\": \"MDc6TGljZW5zZTA=\"\n", - " },\n", - " \"allow_forking\": true,\n", - " \"is_template\": false,\n", - " \"web_commit_signoff_required\": false,\n", - " \"topics\": [\n", - " \"ansible\",\n", - " \"aws\",\n", - " \"azure\",\n", - " \"coding\",\n", - " \"containers\",\n", - " \"devops\",\n", - " \"docker\",\n", - " \"git\",\n", - " \"interview\",\n", - " \"interview-questions\",\n", - " \"kubernetes\",\n", - " \"linux\",\n", - " \"openstack\",\n", - " \"production-engineer\",\n", - " \"prometheus\",\n", - " \"python\",\n", - " \"sql\",\n", - " \"sre\",\n", - " \"terraform\"\n", - " ],\n", - " \"visibility\": \"public\",\n", - " \"forks\": 16975,\n", - " \"open_issues\": 41,\n", - " \"watchers\": 76048,\n", - " \"default_branch\": \"master\",\n", - " \"score\": 1.0\n", - "}\n" - ] - } - ], - "source": [ - "import requests\n", - "import json\n", - "\n", - "search_url = f\"{GITHUB_ENDPOINT}/search/repositories\"\n", - "query_params = {\n", - " \"q\": \"python devops\",\n", - " \"sort\": \"stars\",\n", - " \"order\": \"desc\",\n", - " \"per_page\": 5\n", - "}\n", - "\n", - "response = requests.get(search_url, params=query_params, timeout=10)\n", - "response.raise_for_status()\n", - "\n", - "print(f\"Requested URL: {response.url}\")\n", - "results = response.json()\n", - "\n", - "print(f\"Found {results.get(\"total_count\")} repositories. Top 5:\")\n", - "for repo in results.get(\"items\", []):\n", - " print(f\"- {repo[\"name\"]} (Stars: {repo[\"stargazers_count\"]})\")\n", - "\n", - "print(json.dumps(results.get(\"items\", [])[0], indent=2))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -313,58 +79,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "5cfddd0c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"args\": {},\n", - " \"data\": \"{\\\"script_name\\\": \\\"devops_automation\\\", \\\"action\\\": \\\"trigger_deployment\\\", \\\"environment\\\": \\\"staging\\\", \\\"version\\\": \\\"v1.5.0\\\"}\",\n", - " \"files\": {},\n", - " \"form\": {},\n", - " \"headers\": {\n", - " \"Accept\": \"*/*\",\n", - " \"Accept-Encoding\": \"gzip, deflate\",\n", - " \"Content-Length\": \"115\",\n", - " \"Content-Type\": \"application/json\",\n", - " \"Host\": \"httpbin.org\",\n", - " \"User-Agent\": \"python-requests/2.32.3\",\n", - " \"X-Amzn-Trace-Id\": \"Root=1-682d9d6f-3322ea7b73fe90fe183848bb\"\n", - " },\n", - " \"json\": {\n", - " \"action\": \"trigger_deployment\",\n", - " \"environment\": \"staging\",\n", - " \"script_name\": \"devops_automation\",\n", - " \"version\": \"v1.5.0\"\n", - " },\n", - " \"origin\": \"45.10.155.234\",\n", - " \"url\": \"https://httpbin.org/post\"\n", - "}\n" - ] - } - ], - "source": [ - "import requests\n", - "import json\n", - "\n", - "post_echo_url = \"https://httpbin.org/post\"\n", - "\n", - "payload = {\n", - " \"script_name\": \"devops_automation\",\n", - " \"action\": \"trigger_deployment\",\n", - " \"environment\": \"staging\",\n", - " \"version\": \"v1.5.0\"\n", - "}\n", - "\n", - "response = requests.post(post_echo_url, json=payload, timeout=10)\n", - "response.raise_for_status()\n", - "\n", - "print(json.dumps(response.json(), indent=2))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -402,7 +121,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/http-requests/retries-timeouts.ipynb b/http-requests/retries-timeouts.ipynb index 0bbc0b0..b9b6abb 100644 --- a/http-requests/retries-timeouts.ipynb +++ b/http-requests/retries-timeouts.ipynb @@ -24,40 +24,17 @@ "metadata": {}, "outputs": [], "source": [ + "GITHUB_ENDPOINT = \"https://api.github.com\"\n", "HTTPBIN_ENDPOINT = \"https://httpbin.org\"" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Timeout after 2.37s: HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=2)\n" - ] - } - ], - "source": [ - "import requests\n", - "import time\n", - "\n", - "delay_url = f\"{HTTPBIN_ENDPOINT}/delay/5\" # Simulate a 5-second delay\n", - "\n", - "start = time.perf_counter()\n", - "\n", - "try:\n", - " res = requests.get(delay_url, timeout=2)\n", - " print(f\"Completed in {time.perf_counter() - start:.2f}s, status {response.status_code}\")\n", - "except (\n", - " requests.exceptions.ConnectTimeout,\n", - " requests.exceptions.ReadTimeout\n", - ") as timeout_err:\n", - " print(f\"Timeout after {time.perf_counter() - start:.2f}s: {timeout_err}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -74,51 +51,11 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Attempt 1/3...\n", - "Failed with server error code 503.\n", - "Waiting 2s before retry...\n", - "Attempt 2/3...\n", - "Succeeded with status 200\n" - ] - } - ], - "source": [ - "import requests\n", - "import time\n", - "\n", - "flaky_url = f\"{HTTPBIN_ENDPOINT}/status/200,500,503\"\n", - "\n", - "max_retries = 3\n", - "delay = 2\n", - "\n", - "for attempt in range(1, max_retries + 1):\n", - " print(f\"Attempt {attempt}/{max_retries}...\")\n", - "\n", - " try:\n", - " res = requests.get(flaky_url, timeout=10)\n", - " res.raise_for_status()\n", - " print(f\"Succeeded with status {res.status_code}\")\n", - " break\n", - " except requests.exceptions.HTTPError as err:\n", - " if err.response.status_code < 500:\n", - " print(f\"Failed with client error code {err.response.status_code}. Skipping retry.\")\n", - " break\n", - " else:\n", - " print(f\"Failed with server error code {err.response.status_code}.\")\n", - " if attempt < max_retries:\n", - " print(f\"Waiting {delay}s before retry...\")\n", - " time.sleep(delay)\n", - "else:\n", - " print(f\"All {max_retries} attempts failed!\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -134,65 +71,11 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "5cfddd0c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Attempt 1/4...\n", - " Failed with server error code 503. Retrying in 1.99s\n", - "Attempt 2/4...\n", - " Failed with server error code 503. Retrying in 4.14s\n", - "Attempt 3/4...\n", - " Failed with server error code 503. Retrying in 7.81s\n", - "Attempt 4/4...\n", - " Failed with server error code 503. Retrying in 16.78s\n", - "All retries to query https://httpbin.org/status/503 failed!\n" - ] - } - ], - "source": [ - "import requests\n", - "import time\n", - "import random\n", - "\n", - "def get_with_backoff(url, max_retries=3):\n", - " delay=1\n", - "\n", - " for attempt in range(1, max_retries + 1):\n", - " print(f\"Attempt {attempt}/{max_retries}...\")\n", - "\n", - " try:\n", - " res = requests.get(url, timeout=10)\n", - " res.raise_for_status()\n", - " print(f\"Succeeded with status {res.status_code}\")\n", - " return res\n", - " except requests.exceptions.HTTPError as err:\n", - " if err.response.status_code < 500:\n", - " print(f\"Failed with client error code {err.response.status_code}. Skipping retry.\")\n", - " raise RuntimeError(f\"Client error! Please review request.\")\n", - " else:\n", - " jitter = random.uniform(-0.1 * delay, 0.1 * delay)\n", - " # delay = 1 -> jitter [-0.1, 0.1] -> 0.9 and 1.1s\n", - " # delay = 2 -> jitter [-0.2, 0.2] -> 1.8 and 2.2s\n", - " # delay = 4 -> jitter [-0.4, 0.4] -> 3.6 and 4.4s\n", - " wait = min(delay * 2, 30) + jitter\n", - " print(f\" Failed with server error code {err.response.status_code}. Retrying in {wait:.2f}s\")\n", - " time.sleep(wait)\n", - " delay = min(delay * 2, 30)\n", - " raise RuntimeError(f\"All retries to query {url} failed!\")\n", - "\n", - "try:\n", - " res = get_with_backoff(\n", - " f\"{HTTPBIN_ENDPOINT}/status/503\",\n", - " max_retries=4\n", - " )\n", - "except RuntimeError as e:\n", - " print(e)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -232,7 +115,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/interacting-with-os/environment-variables.ipynb b/interacting-with-os/environment-variables.ipynb index 7c1c430..a8f1393 100644 --- a/interacting-with-os/environment-variables.ipynb +++ b/interacting-with-os/environment-variables.ipynb @@ -14,38 +14,11 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "HOME = /Users/lauromueller\n", - "SHELL = /bin/zsh\n", - "We have 60 environment variables available!\n", - "MallocNanoZone\n", - "USER\n", - "SECURITYSESSIONID\n", - "COMMAND_MODE\n", - "__CFBundleIdentifier\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "for key in [\"HOME\", \"SHELL\"]:\n", - " value = os.getenv(key)\n", - " print(f\"{key} = {value if value else \"Not set\"}\")\n", - "\n", - "env_keys = list(os.environ.keys())\n", - "print(f\"We have {len(env_keys)} environment variables available!\")\n", - "\n", - "for key in env_keys[:5]:\n", - " print(key)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -61,34 +34,11 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "API key found: ab12... (masked)\n", - "Debug mode: False\n" - ] - } - ], - "source": [ - "import os \n", - "\n", - "os.environ[\"APP_API_KEY\"] = \"ab12cd34\"\n", - "\n", - "api_key = os.getenv(\"APP_API_KEY\")\n", - "debug_mode = os.getenv(\"DEBUG_MODE\", False)\n", - "\n", - "if api_key:\n", - " print(f\"API key found: {api_key[:4]}... (masked)\")\n", - "else:\n", - " print(\"APP_API_KEY not set.\")\n", - "\n", - "print(f\"Debug mode: {debug_mode}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -104,27 +54,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "JAVA_HOME environment variable not set.\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "try:\n", - " java_home = os.environ[\"JAVA_HOME\"]\n", - " print(java_home)\n", - "except KeyError:\n", - " print(\"JAVA_HOME environment variable not set.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -140,43 +74,11 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "f27457c5-12cd-48bf-a208-f28f1e156655", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initial MY_CUSTOM_VAR: None\n", - "Updated MY_CUSTOM_VAR: SetByOurScript\n", - "Child sees MY_CUSTOM_VAR: SetByOurScript\n", - "After deletion, MY_CUSTOM_VAR: None\n" - ] - } - ], - "source": [ - "import os\n", - "import sys\n", - "import subprocess\n", - "\n", - "print(f\"Initial MY_CUSTOM_VAR: {os.getenv(\"MY_CUSTOM_VAR\")}\")\n", - "\n", - "os.environ[\"MY_CUSTOM_VAR\"] = \"SetByOurScript\"\n", - "print(f\"Updated MY_CUSTOM_VAR: {os.getenv(\"MY_CUSTOM_VAR\")}\")\n", - "\n", - "result = subprocess.run([\n", - " sys.executable,\n", - " \"-c\",\n", - " \"\"\"import os\n", - "print(f\"Child sees MY_CUSTOM_VAR: {os.getenv(\"MY_CUSTOM_VAR\")}\")\"\"\" \n", - "])\n", - "\n", - "result.stdout\n", - "\n", - "del os.environ[\"MY_CUSTOM_VAR\"]\n", - "print(f\"After deletion, MY_CUSTOM_VAR: {os.getenv(\"MY_CUSTOM_VAR\")}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -193,30 +95,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "94061dc2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved MY_DOTENV_VAR with value None\n" - ] - } - ], - "source": [ - "import os\n", - "from dotenv import load_dotenv\n", - "\n", - "os.environ[\"MY_DOTENV_VAR\"] = \"setFromJupyter\"\n", - "\n", - "load_dotenv(override=True)\n", - "\n", - "secret_dotenv_value = os.getenv(\"MY_DOTENV_VAR\")\n", - "\n", - "print(f\"Retrieved MY_DOTENV_VAR with value {secret_dotenv_value}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -231,36 +114,10 @@ "- Storing highly sensitive secrets in plain environment variables carries security risks; for production use, consider managed secrets solutions like Vault or AWS Secrets Manager." ] }, - { - "cell_type": "code", - "execution_count": 16, - "id": "76f37418-d0ac-4739-86d1-03e7a912abaf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "import os\n", - "from dotenv import load_dotenv\n", - "\n", - "load_dotenv(override=True)\n", - "\n", - "number_dotenv_value = os.getenv(\"MY_NUMBER_VAR\")\n", - "\n", - "print(type(number_dotenv_value))\n", - "# print(number_dotenv_value + 45) # Uncommenting will raise TypeError because number_dotenv_value is a string!" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "2d8e93b5-644e-45c8-b652-9f18cc84a7b6", + "id": "76f37418-d0ac-4739-86d1-03e7a912abaf", "metadata": {}, "outputs": [], "source": [] @@ -282,7 +139,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/interacting-with-os/filesystem-operations.ipynb b/interacting-with-os/filesystem-operations.ipynb index cafc338..df75c95 100644 --- a/interacting-with-os/filesystem-operations.ipynb +++ b/interacting-with-os/filesystem-operations.ipynb @@ -20,57 +20,11 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--- os.listdir(\"temp_listing_dir\") ---\n", - "file1.txt\n", - "subdir\n", - "file2.log\n", - "--- Path(\"temp_listing_dir\").iterdir() ---\n", - "temp_listing_dir/file1.txt\n", - "temp_listing_dir/subdir\n", - "temp_listing_dir/file2.log\n" - ] - } - ], - "source": [ - "import os\n", - "from pathlib import Path\n", - "import shutil\n", - "\n", - "\"\"\"\n", - "Directory structure:\n", - "\n", - "temp_listing_dir/\n", - "├── file1.txt\n", - "├── file2.log\n", - "└── subdir/\n", - " └── subfile.py\n", - "\"\"\"\n", - "\n", - "tmp_path = Path(\"temp_listing_dir\")\n", - "tmp_path.mkdir(exist_ok=True)\n", - "(tmp_path / \"file1.txt\").touch()\n", - "(tmp_path / \"file2.log\").touch()\n", - "(tmp_path / \"subdir\").mkdir(exist_ok=True)\n", - "(tmp_path / \"subdir\" / \"subfile.py\").touch()\n", - "\n", - "print(f\"--- os.listdir(\\\"{tmp_path}\\\") ---\")\n", - "for name in os.listdir(tmp_path):\n", - " print(name)\n", - "\n", - "print(f\"--- Path(\\\"{tmp_path}\\\").iterdir() ---\")\n", - "for entry in tmp_path.iterdir():\n", - " print(entry)\n", - "\n", - "shutil.rmtree(tmp_path)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -86,38 +40,11 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created my_single_dir: True\n", - "Created nested path parent/child/grandchild: True\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import shutil\n", - "\n", - "single = Path(\"my_single_dir\")\n", - "\n", - "try:\n", - " single.mkdir(exist_ok=True)\n", - " print(f\"Created {single}: {single.exists()}\")\n", - "finally:\n", - " if single.exists():\n", - " single.rmdir()\n", - "\n", - "nested = Path(\"parent/child/grandchild\")\n", - "nested.mkdir(parents=True, exist_ok=True)\n", - "print(f\"Created nested path {nested}: {nested.exists()}\")\n", - "\n", - "shutil.rmtree(\"parent\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -133,53 +60,11 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Removed file temp_file.txt. Exists? False\n", - "Removed dir empty_dir. Exists? False\n", - "Removed \"tree_root\" recursively. Exists? False\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import shutil\n", - "\n", - "\"\"\"\n", - "Directory structure:\n", - "\n", - ".\n", - "├── temp_file.txt\n", - "├── empty_dir/\n", - "└── tree_root/\n", - " └── child/\n", - " └── inner.txt\n", - "\"\"\"\n", - "\n", - "temp_file = Path(\"temp_file.txt\")\n", - "temp_file.touch()\n", - "\n", - "empty_dir = Path(\"empty_dir\")\n", - "empty_dir.mkdir(exist_ok=True)\n", - "\n", - "tree = Path(\"tree_root/child\")\n", - "tree.mkdir(parents=True, exist_ok=True)\n", - "(tree / \"inner.txt\").touch()\n", - "\n", - "temp_file.unlink()\n", - "print(f\"Removed file {temp_file}. Exists? {temp_file.exists()}\")\n", - "empty_dir.rmdir()\n", - "print(f\"Removed dir {empty_dir}. Exists? {empty_dir.exists()}\")\n", - "\n", - "shutil.rmtree(\"tree_root\")\n", - "print(f\"Removed \\\"tree_root\\\" recursively. Exists? {tree.exists()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -195,46 +80,11 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": null, "id": "94061dc2", "metadata": {}, "outputs": [], - "source": [ - "import shutil\n", - "from pathlib import Path\n", - "\n", - "\"\"\"\n", - "Directory structure:\n", - "\n", - "src_copy/\n", - "├── a.txt\n", - "└── sub/\n", - " └── b.txt\n", - "\"\"\"\n", - "\n", - "src = Path(\"src_copy\")\n", - "src.mkdir(exist_ok=True)\n", - "(src / \"a.txt\").write_text(\"A\")\n", - "(src / \"sub\").mkdir(exist_ok=True)\n", - "(src / \"sub\" / \"b.txt\").write_text(\"B\")\n", - "\n", - "dest_file = Path(\"copied_a.txt\")\n", - "dest_file_metadata = Path(\"copied_a_metadata.txt\")\n", - "shutil.copy(src / \"a.txt\", dest_file)\n", - "shutil.copy2(src / \"a.txt\", dest_file_metadata)\n", - "\n", - "dest_dir = Path(\"copied_src\")\n", - "\n", - "if dest_dir.exists():\n", - " shutil.rmtree(dest_dir)\n", - "\n", - "shutil.copytree(src, dest_dir)\n", - "\n", - "shutil.rmtree(\"src_copy\")\n", - "shutil.rmtree(dest_dir)\n", - "dest_file.unlink()\n", - "dest_file_metadata.unlink()" - ] + "source": [] }, { "cell_type": "markdown", @@ -250,52 +100,11 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": null, "id": "5704b344", "metadata": {}, "outputs": [], - "source": [ - "import shutil\n", - "from pathlib import Path\n", - "\n", - "\"\"\"\n", - "Directory structure:\n", - "\n", - ".\n", - "├── move_me.txt\n", - "├── move_dir/\n", - "│ └── inside.txt\n", - "└── dest_folder/\n", - "\"\"\"\n", - "\n", - "file_src = Path(\"move_me.txt\")\n", - "file_src.write_text(\"Moving file.\")\n", - "\n", - "dir_src = Path(\"move_dir\")\n", - "dir_src.mkdir(exist_ok=True)\n", - "(dir_src / \"inside.txt\").write_text(\"Inside source dir.\")\n", - "\n", - "dest_dir = Path(\"dest_folder\")\n", - "dest_dir.mkdir(exist_ok=True)\n", - "\n", - "try:\n", - " shutil.move(file_src, dest_dir)\n", - "except Exception as e:\n", - " print(f\"Error occurred: {e}\")\n", - "\n", - "file_src2 = dest_dir / file_src.name\n", - "new_name = Path(\"renamed.txt\")\n", - "shutil.move(file_src2, new_name)\n", - "\n", - "try:\n", - " shutil.move(dir_src, dest_dir)\n", - "except Exception as e:\n", - " print(f\"Error occurred: {e}\")\n", - "\n", - "shutil.rmtree(dest_dir)\n", - "if new_name.exists():\n", - " new_name.unlink()" - ] + "source": [] }, { "cell_type": "markdown", @@ -333,7 +142,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/interacting-with-os/handling-errors.ipynb b/interacting-with-os/handling-errors.ipynb index 3db4367..680c1ca 100644 --- a/interacting-with-os/handling-errors.ipynb +++ b/interacting-with-os/handling-errors.ipynb @@ -22,35 +22,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Command executed: ['ls', 'missing_dir']\n", - "Return code 1\n", - "STDOUT capture: \n", - "STDERR capture: ls: missing_dir: No such file or directory\n", - "\n" - ] - } - ], - "source": [ - "import subprocess\n", - "\n", - "cmd = [\"ls\", \"missing_dir\"]\n", - "\n", - "try:\n", - " subprocess.run(cmd, check=True, capture_output=True, text=True)\n", - "except subprocess.CalledProcessError as err:\n", - " print(f\"Command executed: {err.cmd}\")\n", - " print(f\"Return code {err.returncode}\")\n", - " print(f\"STDOUT capture: {err.stdout}\")\n", - " print(f\"STDERR capture: {err.stderr}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -65,30 +41,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FileNotFoundError caught!\n", - " The command 'fakecmd' was not found on this system.\n" - ] - } - ], - "source": [ - "import subprocess\n", - "\n", - "cmd = [\"fakecmd\", \"--version\"]\n", - "\n", - "try:\n", - " subprocess.run(cmd, check=True, capture_output=True, text=True)\n", - "except FileNotFoundError as err:\n", - " print(\"FileNotFoundError caught!\")\n", - " print(f\" The command '{cmd[0]}' was not found on this system.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -104,33 +61,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "TimeoutExpired caught!\n", - " Command: ['sleep', '5']\n", - " Timeout after 2 seconds\n" - ] - } - ], - "source": [ - "import subprocess\n", - "\n", - "cmd = [\"sleep\", \"5\"]\n", - "\n", - "try:\n", - " subprocess.run(cmd, timeout=2, capture_output=True, text=True)\n", - " print(\"Command completed within timeout.\")\n", - "except subprocess.TimeoutExpired as err:\n", - " print(\"TimeoutExpired caught!\")\n", - " print(f\" Command: {err.cmd}\")\n", - " print(f\" Timeout after {err.timeout} seconds\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -170,7 +105,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/interacting-with-os/subprocesses.ipynb b/interacting-with-os/subprocesses.ipynb index ac034e4..98669ff 100644 --- a/interacting-with-os/subprocesses.ipynb +++ b/interacting-with-os/subprocesses.ipynb @@ -14,32 +14,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Return code: 0\n", - "Stdout: Hello from subprocess.\n" - ] - } - ], - "source": [ - "import subprocess\n", - "import sys\n", - "\n", - "result = subprocess.run(\n", - " [sys.executable, \"-c\", \"print('Hello from subprocess.')\"],\n", - " capture_output=True,\n", - " text=True\n", - ")\n", - "\n", - "print(f\"Return code: {result.returncode}\")\n", - "print(f\"Stdout: {result.stdout.strip()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -70,40 +49,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Args: ['/Users/lauromueller/Documents/courses/python-devops/code/.venv/bin/python', '-c', 'print(\"Hello from subprocess\")\\ninvalid_function()']\n", - "Stdout: Hello from subprocess\n", - "Stderr: Traceback (most recent call last):\n", - " File \"\", line 2, in \n", - "NameError: name 'invalid_function' is not defined\n", - "Return code: 1\n" - ] - } - ], - "source": [ - "import subprocess\n", - "import sys\n", - "\n", - "cmd = [\n", - " sys.executable,\n", - " \"-c\",\n", - " \"\"\"print(\"Hello from subprocess\")\n", - "invalid_function()\"\"\"\n", - "]\n", - "\n", - "result = subprocess.run(cmd, capture_output=True, text=True)\n", - "print(f\"Args: {result.args}\")\n", - "print(f\"Stdout: {result.stdout.strip()}\")\n", - "print(f\"Stderr: {result.stderr.strip()}\")\n", - "print(f\"Return code: {result.returncode}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -119,30 +69,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "ce894926-dc04-4aae-80fa-67990cd459b6", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Darwin Mac.fritz.box 24.4.0 Darwin Kernel Version 24.4.0: Fri Apr 11 18:33:40 PDT 2025; root:xnu-11417.101.15~117/RELEASE_ARM64_T6031 arm64\n" - ] - } - ], - "source": [ - "import subprocess\n", - "import platform\n", - "\n", - "if platform.system() == \"Windows\":\n", - " cmd = [\"ver\"]\n", - "else:\n", - " cmd = [\"uname\", \"-a\"]\n", - "\n", - "result = subprocess.run(cmd, capture_output=True, text=True)\n", - "print(result.stdout.strip())" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -164,14 +95,6 @@ "metadata": {}, "outputs": [], "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8afef57", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -190,7 +113,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/interacting-with-os/temporary-files.ipynb b/interacting-with-os/temporary-files.ipynb index e2026fe..6c2e8af 100644 --- a/interacting-with-os/temporary-files.ipynb +++ b/interacting-with-os/temporary-files.ipynb @@ -21,27 +21,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "6da1ba08-a588-4f4b-9c07-e444466f810d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Default temporary directory: /var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T\n", - "Sample contents: ['TelemetryUploadFilecom.microsoft.autoupdate.fba.txt', 'vscode-git-f400a76488.sock', 'com.apple.ThreadCommissionerService', 'com.apple.avconferenced', 'python-languageserver-cancellation']\n" - ] - } - ], - "source": [ - "import tempfile\n", - "import os\n", - "\n", - "temp_dir = tempfile.gettempdir()\n", - "print(f\"Default temporary directory: {temp_dir}\")\n", - "print(f\"Sample contents: {os.listdir(temp_dir)[:5]}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -58,28 +42,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "b50229d7-ce3f-4667-81b7-25ac8aefd080", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Content from TemporaryFile:\n", - "This is some temporary data.\n" - ] - } - ], - "source": [ - "import tempfile\n", - "\n", - "with tempfile.TemporaryFile(mode=\"w+t\", encoding=\"utf-8\") as temp_file:\n", - " temp_file.write(\"This is some temporary data.\")\n", - " temp_file.seek(0)\n", - " print(\"Content from TemporaryFile:\")\n", - " print(temp_file.read())" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -96,54 +63,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "16e439e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created temp file at /var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T/tmpl3sszx7y.log. Exists: True\n", - "After close. Exists? False\n", - "Created temp file at /var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T/tmpl3sszx7y.log. Exists: False\n", - "After close. Exists? True\n", - "After unlink. Exists? False\n" - ] - } - ], - "source": [ - "import tempfile\n", - "from pathlib import Path\n", - "\n", - "# Auto-delete on with exit\n", - "path = None\n", - "\n", - "with tempfile.NamedTemporaryFile(mode=\"w+t\", encoding=\"utf-8\", suffix=\".log\") as temp_file:\n", - " path = Path(temp_file.name)\n", - " print(f\"Created temp file at {path}. Exists: {path.exists()}\")\n", - "\n", - "print(f\"After close. Exists? {path.exists()}\")\n", - "\n", - "# Persist after with exit\n", - "path_persistent = None\n", - "\n", - "with tempfile.NamedTemporaryFile(\n", - " mode=\"w+t\",\n", - " encoding=\"utf-8\",\n", - " suffix=\".log\",\n", - " delete=False\n", - ") as temp_file:\n", - " path_persistent = Path(temp_file.name)\n", - " print(f\"Created temp file at {path}. Exists: {path.exists()}\")\n", - "\n", - "print(f\"After close. Exists? {path_persistent.exists()}\")\n", - "\n", - "if path_persistent.exists():\n", - " path_persistent.unlink()\n", - "\n", - "print(f\"After unlink. Exists? {path_persistent.exists()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -159,37 +83,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "94061dc2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/var/folders/mj/cbl0hyj553x1hg303r5syl5r0000gn/T/batch_job_ety690wr - type: \n", - "Contents: ['file1.txt', 'subdir']\n", - "After close. Exists? False\n" - ] - } - ], - "source": [ - "import tempfile\n", - "from pathlib import Path\n", - "\n", - "temp_path = None\n", - "\n", - "with tempfile.TemporaryDirectory(prefix=\"batch_job_\") as temp_dir:\n", - " print(f\"{temp_dir} - type: {type(temp_dir)}\")\n", - " temp_path = Path(temp_dir)\n", - " (temp_path / \"file1.txt\").write_text(\"data\")\n", - " subdir = temp_path / \"subdir\"\n", - " subdir.mkdir(exist_ok=True)\n", - " (subdir / \"file2.txt\").write_text(\"data2\")\n", - " print(f\"Contents: {[p.name for p in temp_path.iterdir()]}\")\n", - "\n", - "print(f\"After close. Exists? {temp_path.exists()}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -211,12 +109,6 @@ "metadata": {}, "outputs": [], "source": [] - }, - { - "cell_type": "markdown", - "id": "4ec3255f", - "metadata": {}, - "source": [] } ], "metadata": { @@ -235,7 +127,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/logging/declarative-config.ini b/logging/declarative-config.ini index 5530a1b..e69de29 100644 --- a/logging/declarative-config.ini +++ b/logging/declarative-config.ini @@ -1,32 +0,0 @@ -[loggers] -keys=root,app - -[handlers] -keys=consoleHandler,nullHandler - -[formatters] -keys=simpleFormatter - -[logger_root] -level=INFO -handlers=consoleHandler - -[logger_app] -level=DEBUG -handlers=nullHandler -qualname=app - -[handler_consoleHandler] -class=StreamHandler -level=DEBUG -formatter=simpleFormatter -args=(sys.stdout,) - -[handler_nullHandler] -class=NullHandler -level=NOTSET -formatter= -args=() - -[formatter_simpleFormatter] -format=%(asctime)s - %(name)s - %(levelname)-8s - %(message)s \ No newline at end of file diff --git a/logging/declarative-config.json b/logging/declarative-config.json index a82278d..e69de29 100644 --- a/logging/declarative-config.json +++ b/logging/declarative-config.json @@ -1,28 +0,0 @@ -{ - "version": 1, - "disable_existing_loggers": false, - "formatters": { - "simple": { "format": "%(levelname)-8s - %(message)s" }, - "detailed": { - "format": "%(asctime)s %(name)s [%(levelname)s]: %(message)s", - "datefmt": "%Y-%m-%d %H:%M:%S" - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "INFO", - "formatter": "detailed", - "stream": "ext://sys.stdout" - } - }, - "loggers": { - "config.json": { - "level": "DEBUG" - } - }, - "root": { - "level": "DEBUG", - "handlers": ["console"] - } -} diff --git a/logging/declarative-config.py b/logging/declarative-config.py index b933659..9fd490a 100644 --- a/logging/declarative-config.py +++ b/logging/declarative-config.py @@ -1,117 +1,8 @@ -import logging -import logging.config -import json -from typing import Any, Dict - -""" -# Uncomment to test INI configuration - # Declarative logging configuration - INI-file -print("Declarative configuration using INI files") -print("---------\n") - -config_path = "declarative-config.ini" - -logging.config.fileConfig( - fname=config_path, -) - -app_logger = logging.getLogger("app") -app_logger.debug("INI-style fileConfig is working!") -""" - -""" -# Uncomment to test Dictionary configuration # Declarative logging configuration - Dictionary config -print("Declarative configuration using dictionary config") -print("---------\n") - -dict_config: Dict[str, Any] = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "simple": {"format": "%(levelname)-8s - %(message)s"} - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "INFO", - "formatter": "simple", - "stream": "ext://sys.stdout", - } - }, - "loggers": { - "config.dict": { - "level": "DEBUG", - "handlers": ["console"], - } - }, -} - -logging.config.dictConfig(dict_config) -config_logger = logging.getLogger("config.dict") -config_logger.debug("dictConfig setup successfully") -config_logger.info("Info goes to console") -""" - -""" -# Uncomment to test JSON configuration # Declarative logging configuration - JSON config -print("Declarative configuration using JSON config") -print("---------\n") - -config_path = "declarative-config.json" - -with open(config_path, "r") as config_file: - json_config = json.load(config_file) - -logging.config.dictConfig(json_config) -config_logger = logging.getLogger("config.json") -config_logger.debug("JSON config setup successfully") -config_logger.info("Info goes to console") -""" # Dynamically building config -print("Dynamically building config") -print("---------\n") - -base_config: Dict[str, Any] = { - "version": 1, - "disable_existing_loggers": True, - "handlers": {}, - "formatters": {}, - "loggers": {}, -} - -base_config["formatters"]["simple"] = { - "format": "%(levelname)-8s - %(message)s" -} - -base_config["handlers"]["console"] = { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout", -} - -base_config["loggers"]["config.dynamic"] = { - "level": "WARNING", - "handlers": ["console"], -} - - -def is_debug(): - return True - - -if is_debug(): - for logger, _config in base_config["loggers"].items(): - base_config["loggers"][logger]["level"] = "DEBUG" - -logging.config.dictConfig(base_config) -config_logger = logging.getLogger("config.dynamic") -config_logger.debug("Dynamic config setup successfully") -config_logger.info("Info goes to console") diff --git a/logging/log-levels.py b/logging/log-levels.py index 8a2196f..a7ec939 100644 --- a/logging/log-levels.py +++ b/logging/log-levels.py @@ -1,59 +1,5 @@ # Log levels in practice -import logging -import sys - -print("Log levels in practice") -print("------\n") - -for lvl in ( - logging.DEBUG, - logging.INFO, - logging.WARNING, - logging.ERROR, - logging.CRITICAL, -): - print( - f"{logging.getLevelName(lvl):8} = {lvl}" - ) - # Two-stage filtering -print("\n") -print("Two stage filtering") -print("------\n") - -filter_logger = logging.getLogger("demo.filter") -filter_logger.setLevel(logging.INFO) - -stream_handler = logging.StreamHandler(sys.stdout) -stream_handler.setLevel(logging.ERROR) - -filter_logger.addHandler(stream_handler) - -filter_logger.info("INFO: will not be shown") -filter_logger.error("ERROR: will be shown") - # Configuring logs and handlers - -print("\n") -print("Configuring logs and handlers") -print("------\n") - -data_logger = logging.getLogger("demo.data") -data_logger.setLevel(logging.DEBUG) - -data_sh = logging.StreamHandler(sys.stdout) -data_sh.setLevel(logging.ERROR) - -data_fh = logging.FileHandler("process.log", "w") -data_fh.setLevel(logging.INFO) - -data_logger.addHandler(data_sh) -data_logger.addHandler(data_fh) - -data_logger.debug("DEBUG: will be dropped") -data_logger.info("INFO: file only") -data_logger.warning("WARNING: file only") -data_logger.error("ERROR: file and console") -data_logger.critical("CRITICAL: file and console") diff --git a/logging/logging-anatomy.ipynb b/logging/logging-anatomy.ipynb index e154e9c..db92d1a 100644 --- a/logging/logging-anatomy.ipynb +++ b/logging/logging-anatomy.ipynb @@ -15,32 +15,11 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "ff37d05e-1d78-4800-bbbb-0b2a6f338997", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Root logger: name=root, level=WARNING\n", - "App logger: name=app, level=NOTSET, parent=root\n", - "Network logger: name=app.network, level=NOTSET, parent=app\n" - ] - } - ], - "source": [ - "import logging\n", - "\n", - "root_logger = logging.getLogger()\n", - "print(f\"Root logger: name={root_logger.name}, level={logging.getLevelName(root_logger.level)}\")\n", - "\n", - "app_logger = logging.getLogger(\"app\")\n", - "print(f\"App logger: name={app_logger.name}, level={logging.getLevelName(app_logger.level)}, parent={app_logger.parent.name}\")\n", - "\n", - "network_logger = logging.getLogger(\"app.network\")\n", - "print(f\"Network logger: name={network_logger.name}, level={logging.getLevelName(network_logger.level)}, parent={network_logger.parent.name}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -55,40 +34,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "843fee5b-ed1c-4ac3-b755-089bf19cf6aa", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogRecord contents:\n", - " name => app.network\n", - " levelname => ERROR\n", - " pathname => /path/to/file.py\n", - " msg => My log message\n" - ] - } - ], - "source": [ - "from logging import LogRecord\n", - "\n", - "record = LogRecord(\n", - " name=\"app.network\",\n", - " level=logging.ERROR,\n", - " pathname=\"/path/to/file.py\",\n", - " lineno=43,\n", - " msg=\"My log message\",\n", - " args=(),\n", - " exc_info=None\n", - ")\n", - "\n", - "print(\"LogRecord contents:\")\n", - "\n", - "for attr in (\"name\", \"levelname\", \"pathname\", \"msg\"):\n", - " print(f\" {attr} => {getattr(record, attr)}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -110,36 +60,11 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": null, "id": "cefda16a-c802-4bfc-adaa-733bdd3792a0", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Info message: will show\n", - "Warning message: will show\n", - "Error message: will show\n" - ] - } - ], - "source": [ - "import sys\n", - "\n", - "demo_logger = logging.getLogger(\"handler_demo\")\n", - "demo_logger.setLevel(logging.INFO)\n", - "demo_logger.handlers.clear()\n", - "\n", - "stream_handler = logging.StreamHandler(sys.stdout)\n", - "stream_handler.setLevel(logging.DEBUG)\n", - "demo_logger.addHandler(stream_handler)\n", - "\n", - "demo_logger.debug(\"Debug message: will not show\")\n", - "demo_logger.info(\"Info message: will show\")\n", - "demo_logger.warning(\"Warning message: will show\")\n", - "demo_logger.error(\"Error message: will show\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -152,35 +77,10 @@ "- Common attributes: `asctime`, `levelname`, `name`, `message`, `filename`, `lineno`, `funcName`, `process`, `thread`. " ] }, - { - "cell_type": "code", - "execution_count": 54, - "id": "d38b58c4-1816-4e13-9e35-b600adde4a44", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2025-05-15 11:30:38 - handler_demo - WARNING - Formatted warning\n" - ] - } - ], - "source": [ - "formatter = logging.Formatter(\n", - " \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\",\n", - " datefmt=\"%Y-%m-%d %H:%M:%S\"\n", - ")\n", - "\n", - "stream_handler.setFormatter(formatter)\n", - "\n", - "demo_logger.warning(\"Formatted warning\")" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "056021be-55f9-4941-a86a-f10f41fbb6c8", + "id": "d38b58c4-1816-4e13-9e35-b600adde4a44", "metadata": {}, "outputs": [], "source": [] @@ -202,7 +102,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/logging/logging-files.py b/logging/logging-files.py index fe6119b..8fb5d65 100644 --- a/logging/logging-files.py +++ b/logging/logging-files.py @@ -1,83 +1,5 @@ -import logging -import logging.handlers -import os -import time - - -def cleanup_log_files(base_name: str): - for file_name in os.listdir("."): - if file_name.startswith(base_name): - os.remove(file_name) - - # Basic logging with FileHandler -print("Basic logging with FileHandler") -print("-------\n") - -basic_logger = logging.getLogger("file.basic") -basic_logger.setLevel(logging.DEBUG) - -basic_fh = logging.FileHandler( - "basicfile.log", delay=True, encoding="utf-8" -) -basic_fh.setLevel(logging.INFO) - -basic_logger.addHandler(basic_fh) - -basic_logger.info("INFO: will be written to file") # Size-based log rotation with RotatingFileHandler -print("Size-based log rotation with RotatingFileHandler") -print("-------\n") - -rotating_logs_filename = "rotatingfile.log" - -cleanup_log_files(rotating_logs_filename) - -rotating_logger = logging.getLogger("file.rotating") -rotating_logger.setLevel(logging.DEBUG) - -rotating_fh = logging.handlers.RotatingFileHandler( - rotating_logs_filename, - maxBytes=500, - backupCount=2, - encoding="utf-8", -) -rotating_fh.setFormatter( - logging.Formatter("%(levelname)-8s %(message)s") -) - -rotating_logger.addHandler(rotating_fh) - -for i in range(30): - rotating_logger.info(f"Entry {i}: {'Z' * 50}") - time.sleep(0.05) - # Time-based log rotation with TimedRotatingFileHandler -print("Time-based log rotation with TimedRotatingFileHandler") -print("-------\n") - -timed_rotating_logs_filename = "timedrotatingfile.log" - -cleanup_log_files(timed_rotating_logs_filename) - -timed_rotating_logger = logging.getLogger("file.timed") -timed_rotating_logger.setLevel(logging.DEBUG) - -timed_rotating_fh = logging.handlers.TimedRotatingFileHandler( - timed_rotating_logs_filename, - when="s", - interval=3, - backupCount=2, - encoding="utf-8", -) -timed_rotating_fh.setFormatter( - logging.Formatter("%(levelname)-8s %(message)s") -) - -timed_rotating_logger.addHandler(timed_rotating_fh) - -for i in range(30): - timed_rotating_logger.info(f"Entry {i}: {'Z' * 50}") - time.sleep(0.5) diff --git a/logging/structured-logging.py b/logging/structured-logging.py index dee3c3a..da3eb4d 100644 --- a/logging/structured-logging.py +++ b/logging/structured-logging.py @@ -1,51 +1,5 @@ # Configuring python-json-logger -print("Configuring python-json-logger") -print("---------\n") - -import logging -import sys -from pythonjsonlogger.json import JsonFormatter - - -json_logger = logging.getLogger("demo.json") -json_logger.setLevel(logging.INFO) - -handler = logging.StreamHandler(sys.stdout) -json_formatter = JsonFormatter( - "{asctime}{levelname}{message}", - style="{", - json_indent=4, - rename_fields={"asctime": "timestamp", "levelname": "level"}, -) -handler.setFormatter(json_formatter) - -json_logger.addHandler(handler) - -json_logger.info("Structured logging initialized") # Logging with extra context -print("Logging with extra context") -print("---------\n") - -extra_context = { - "user_id": "devops1", - "request_id": "request-12345abc", - "source_ip": "10.0.0.5", -} - -json_logger.warning( - "Request took longer than 5s to complete", - extra=extra_context, -) # Logging exceptions as JSON -print("Logging exceptions as JSON") -print("---------\n") - -try: - result = 1 / 0 -except ZeroDivisionError: - json_logger.exception( - "Unexpected calculation error", - extra={"operation": "division"}, - ) diff --git a/multi-file-projects/devops_utils/__init__.py b/multi-file-projects/devops_utils/__init__.py deleted file mode 100644 index 9170fdc..0000000 --- a/multi-file-projects/devops_utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .file_utils.file_ops import check_file_extension -from .network_utils.network_ops import ( - is_host_up, - check_hosts_from_config, -) diff --git a/multi-file-projects/devops_utils/file_utils/__init__.py b/multi-file-projects/devops_utils/file_utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/multi-file-projects/devops_utils/file_utils/file_ops.py b/multi-file-projects/devops_utils/file_utils/file_ops.py deleted file mode 100644 index 9608533..0000000 --- a/multi-file-projects/devops_utils/file_utils/file_ops.py +++ /dev/null @@ -1,34 +0,0 @@ -print("Module file_ops is being imported") - -from typing import Any - -try: - import yaml -except (ModuleNotFoundError, ImportError): - print( - "Warning: PyYAML not found, parse_yaml_file will not work." - ) - yaml = None - - -SUPPORTED_EXTENSIONS: list[str] = [".json", ".yaml", ".txt"] - - -def check_file_extension(filename: str) -> bool: - """Checks if a file has a supported extension""" - print( - f" - file_ops.check_file_extension called for {filename}" - ) - return any( - filename.endswith(ext) for ext in SUPPORTED_EXTENSIONS - ) - - -def parse_yaml_file(path_str: str) -> dict[str, Any]: - """Parses a YAML file and returns its contents.""" - print(f" - file_ops.parse_yaml_file called for {path_str}") - if yaml: - with open(path_str, "r") as file: - return yaml.safe_load(file) - else: - return {} diff --git a/multi-file-projects/devops_utils/network_utils/__init__.py b/multi-file-projects/devops_utils/network_utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/multi-file-projects/devops_utils/network_utils/check_host.py b/multi-file-projects/devops_utils/network_utils/check_host.py deleted file mode 100644 index 4ef7f9a..0000000 --- a/multi-file-projects/devops_utils/network_utils/check_host.py +++ /dev/null @@ -1,15 +0,0 @@ -import sys - -# Both relative and absolute import work -# from .network_ops import is_host_up -from devops_utils.network_utils.network_ops import is_host_up - - -def main(): - print("UPDATED Executing check_host") - print(sys.path) - print(is_host_up("localhost")) - - -if __name__ == "__main__": - main() diff --git a/multi-file-projects/devops_utils/network_utils/network_ops.py b/multi-file-projects/devops_utils/network_utils/network_ops.py deleted file mode 100644 index d33cde5..0000000 --- a/multi-file-projects/devops_utils/network_utils/network_ops.py +++ /dev/null @@ -1,43 +0,0 @@ -import subprocess -from ..file_utils.file_ops import parse_yaml_file - - -def is_host_up(hostname: str) -> bool: - """Pings a host to check if it's reachable.""" - print(f" - network_ops.is_host_up called for {hostname}") - try: - result = subprocess.run( - [ - "ping", - "-c", - "1", - hostname, - ], # on Windows, -n instead of -c - check=True, - capture_output=True, - text=True, - timeout=3, - ) - - return result.returncode == 0 - except ( - subprocess.CalledProcessError, - subprocess.TimeoutExpired, - ): - return False - - -def check_hosts_from_config(config_path: str) -> bool: - """Checks if all hosts in config file are reachable.""" - print( - f" - network_ops.check_hosts_from_config called for {config_path}" - ) - - config_data = parse_yaml_file(config_path) - hosts = config_data.get("hosts", []) - - if not hosts: - print("No hosts available") - return False - - return all(is_host_up(hostname) for hostname in hosts) diff --git a/multi-file-projects/main.py b/multi-file-projects/main.py deleted file mode 100644 index f42ba3c..0000000 --- a/multi-file-projects/main.py +++ /dev/null @@ -1,25 +0,0 @@ -print("Main script starting...") - -from devops_utils import ( - check_file_extension, - is_host_up, - check_hosts_from_config, -) -import sys - -print(sys.path) - -filenames = ["config.yaml", "script.sh"] - -for filename in filenames: - print(f"Checking {filename}") - print(f"Result: {check_file_extension(filename)}") - -print(f"\nIs localhost up? {is_host_up("localhost")}") -print( - f"Is nonexistenthost12345 up? {is_host_up("nonexistenthost12345")}" -) - -print( - f"\nAre all hosts from servers_config.yaml up? {check_hosts_from_config("servers_config.yaml")}" -) diff --git a/multi-file-projects/pyproject.toml b/multi-file-projects/pyproject.toml deleted file mode 100644 index 670095d..0000000 --- a/multi-file-projects/pyproject.toml +++ /dev/null @@ -1,6 +0,0 @@ -[project] -name = "devops_utils" -version = "0.1.0" - -[project.scripts] -check_host = "devops_utils.network_utils.check_host:main" \ No newline at end of file diff --git a/multi-file-projects/servers_config.yaml b/multi-file-projects/servers_config.yaml deleted file mode 100644 index e4fb5aa..0000000 --- a/multi-file-projects/servers_config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -hosts: -- localhost -# - nonexistenthost12345 \ No newline at end of file diff --git a/multi-file-projects/tests/__init__.py b/multi-file-projects/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/multi-file-projects/tests/test_file_ops.py b/multi-file-projects/tests/test_file_ops.py deleted file mode 100644 index 6e9c291..0000000 --- a/multi-file-projects/tests/test_file_ops.py +++ /dev/null @@ -1,14 +0,0 @@ -from pathlib import Path -from devops_utils.file_utils.file_ops import parse_yaml_file - - -def test_parse_valid_yaml(tmp_path: Path) -> None: - config_content = "service: my_app\nport: 8080" - yaml_file: Path = tmp_path / "config.yaml" - yaml_file.write_text(config_content) - - parsed_data = parse_yaml_file(str(yaml_file)) - - assert isinstance(parsed_data, dict) - assert parsed_data["service"] == "my_app" - assert parsed_data["port"] == 8080 diff --git a/python-fundamentals/args-kwargs.ipynb b/python-fundamentals/args-kwargs.ipynb index 97d0ed9..d465528 100644 --- a/python-fundamentals/args-kwargs.ipynb +++ b/python-fundamentals/args-kwargs.ipynb @@ -12,26 +12,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "f8acdefe", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Positional args: (1, 2, 3)\n", - "Keyword args: {'a': 'Value', 'b': True}\n" - ] - } - ], - "source": [ - "def example_function(*args, **kwargs):\n", - " print(f\"Positional args: {args}\")\n", - " print(f\"Keyword args: {kwargs}\")\n", - "\n", - "example_function(1, 2, 3, a=\"Value\", b=True)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -46,59 +31,11 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "id": "5b4f2469", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "10\n", - "28\n", - "3\n", - "24\n", - "5040\n", - "2\n" - ] - } - ], - "source": [ - "def apply_operator(operator, *operands):\n", - " \"\"\"Applies operator to a variable number of operands. Supports 'add' and 'mul'.\n", - "\n", - " Args:\n", - " operator (str): The operator to apply. Must be either 'add' or 'mul'.\n", - " *operands (int or float): Zero or more numbers to be combined.\n", - "\n", - " Returns:\n", - " int or float: The result of applying the operator on the operands.\n", - "\n", - " Raises:\n", - " ValueError: Raised when operator is not 'add' nor 'mul'.\n", - " \"\"\"\n", - "\n", - " if operator == 'add':\n", - " result = sum(operands)\n", - " elif operator == 'mul':\n", - " result = 1\n", - " for n in operands:\n", - " result *= n\n", - " else:\n", - " raise ValueError(f\"Unknown operator {operator}. Supported values are 'add' and 'mul'\")\n", - "\n", - " return result\n", - "\n", - "print(apply_operator('add', 1, 2, 3, 4))\n", - "print(apply_operator('add', 1, 2, 3, 4, 5, 6, 7))\n", - "print(apply_operator('add', 1, 2))\n", - "\n", - "print(apply_operator('mul', 1, 2, 3, 4))\n", - "print(apply_operator('mul', 1, 2, 3, 4, 5, 6, 7))\n", - "print(apply_operator('mul', 1, 2))\n", - "\n", - "# print(apply_operator('div', 1, 2)) # Uncommenting raises ValueError since div is not supported" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -113,29 +50,11 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "d98ff665", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Received dictionary: {'timeout': 30, 'user': 'admin', 'retries': 5}\n", - "\ttimeout = 30\n", - "\tuser = admin\n", - "\tretries = 5\n" - ] - } - ], - "source": [ - "def set_options(**settings):\n", - " print(f\"Received dictionary: {settings}\")\n", - " for key, value in settings.items():\n", - " print(f\"\\t{key} = {value}\")\n", - "\n", - "set_options(timeout=30, user=\"admin\", retries=5)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -151,61 +70,11 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": null, "id": "55c1694f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "url=https://www.example.com, method=GET, timeout=30\n", - "headers=()\n", - "params={}\n", - "url=https://www.example.com, method=PUT, timeout=30\n", - "headers=()\n", - "params={}\n", - "url=https://www.example.com, method=PUT, timeout=30\n", - "headers=()\n", - "params={}\n", - "url=https://www.example.com, method=PUT, timeout=30\n", - "headers=('Auth: xyz', 'Content-Type: application/json')\n", - "params={}\n", - "url=https://www.example.com, method=PUT, timeout=30\n", - "headers=('Auth: xyz', 'Content-Type: application/json')\n", - "params={'retries': 5, 'log_level': 'DEBUG'}\n" - ] - } - ], - "source": [ - "def process_request(url, method=\"GET\", *headers, timeout, **params):\n", - " print(f\"url={url}, method={method}, timeout={timeout}\")\n", - " print(f\"headers={headers}\")\n", - " print(f\"params={params}\")\n", - "\n", - "process_request(\"https://www.example.com\", timeout=30)\n", - "process_request(\"https://www.example.com\", \"PUT\", timeout=30)\n", - "# Equivalent to call above\n", - "process_request(\"https://www.example.com\", timeout=30, method=\"PUT\")\n", - "\n", - "process_request(\n", - " \"https://www.example.com\",\n", - " \"PUT\",\n", - " \"Auth: xyz\",\n", - " \"Content-Type: application/json\",\n", - " timeout=30\n", - ")\n", - "\n", - "process_request(\n", - " \"https://www.example.com\",\n", - " \"PUT\",\n", - " \"Auth: xyz\",\n", - " \"Content-Type: application/json\",\n", - " timeout=30,\n", - " retries=5,\n", - " log_level=\"DEBUG\"\n", - ")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -220,28 +89,11 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": null, "id": "b36bc314", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connecting to db.internal:5432 with timeout 10s.\n", - "Connecting to db.internal:5432 with timeout 10s.\n" - ] - } - ], - "source": [ - "def connect(host, port, timeout):\n", - " print(f\"Connecting to {host}:{port} with timeout {timeout}s.\")\n", - "\n", - "params = [\"db.internal\", 5432, 10]\n", - "params_with_extra_values = [\"db.internal\", 5432, 10, \"a\", True]\n", - "connect(*params)\n", - "connect(*params_with_extra_values[:3])" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -254,32 +106,10 @@ "- Common in configuration-driven function calls" ] }, - { - "cell_type": "code", - "execution_count": 57, - "id": "6bc1c7f2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Setting up auth-service v2.1.0 with 3 replicas...\n" - ] - } - ], - "source": [ - "def configure_service(name, version, replicas=1):\n", - " print(f\"Setting up {name} v{version} with {replicas} replicas...\")\n", - "\n", - "config = {\"name\": \"auth-service\", \"version\": \"2.1.0\", \"replicas\": 3}\n", - "configure_service(**config)" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "3a058c28-23cd-405b-aa7a-6abdbb30ebba", + "id": "6bc1c7f2", "metadata": {}, "outputs": [], "source": [] @@ -301,7 +131,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/classes.ipynb b/python-fundamentals/classes.ipynb index 5822ce1..d635ad8 100644 --- a/python-fundamentals/classes.ipynb +++ b/python-fundamentals/classes.ipynb @@ -23,25 +23,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "9f9dd0df-f550-4e62-a2b1-8120515ba266", "metadata": {}, "outputs": [], - "source": [ - "class ServiceMonitor:\n", - " \"\"\"Provides service checks for a single service\"\"\"\n", - " def __init__(self, service_name, port):\n", - " \"\"\"Initializes the monitor for a specific service.\n", - "\n", - " Args:\n", - " service_name (str): the name of the service.\n", - " port (int): the port to use for checks.\n", - " \"\"\"\n", - " print(f\"Initializing monitor for service {service_name} on port {port}.\")\n", - " self.service = service_name\n", - " self.port = port\n", - " self.is_alive = False" - ] + "source": [] }, { "cell_type": "markdown", @@ -55,33 +41,11 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "e340ef79-e5b8-403b-a176-19f0b7781a31", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing monitor for service nginx on port 80.\n", - "True\n", - "Initializing monitor for service redis on port 6379.\n", - "True\n", - "nginx\n", - "redis\n" - ] - } - ], - "source": [ - "nginx_monitor = ServiceMonitor(\"nginx\", 80)\n", - "print(isinstance(nginx_monitor, ServiceMonitor))\n", - "\n", - "redis_monitor = ServiceMonitor(service_name=\"redis\", port=6379)\n", - "print(isinstance(redis_monitor, ServiceMonitor))\n", - "\n", - "print(nginx_monitor.service)\n", - "print(redis_monitor.service)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -96,47 +60,11 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "8e6fae9d-5b6d-422a-903d-90526a32c5db", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing monitor for service nginx on port 80.\n", - "METHOD: Checking nginx on port 80...\n", - "METHOD: Status for service nginx: Alive\n", - "Received status: True\n" - ] - } - ], - "source": [ - "class ServiceMonitor:\n", - " \"\"\"Provides service checks for a single service\"\"\"\n", - " def __init__(self, service_name, port):\n", - " \"\"\"Initializes the monitor for a specific service.\n", - "\n", - " Args:\n", - " service_name (str): the name of the service.\n", - " port (int): the port to use for checks.\n", - " \"\"\"\n", - " print(f\"Initializing monitor for service {service_name} on port {port}.\")\n", - " self.service = service_name\n", - " self.port = port\n", - " self.is_alive = False\n", - "\n", - " def check(self):\n", - " \"\"\"Simulates checking the service status\"\"\"\n", - " print(f\"METHOD: Checking {self.service} on port {self.port}...\")\n", - " self.is_alive = True\n", - " print(f\"METHOD: Status for service {self.service}: {\"Alive\" if self.is_alive else \"Down\"}\")\n", - " return self.is_alive\n", - "\n", - "nginx_monitor = ServiceMonitor(\"nginx\", 80)\n", - "status = nginx_monitor.check()\n", - "print(f\"Received status: {status}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -153,65 +81,10 @@ "- **`super()`:** Inside the Child's methods, use `super().method_name(...)` to explicitly call the Parent's version of a method (very common in `__init__`)." ] }, - { - "cell_type": "code", - "execution_count": 34, - "id": "8949c112", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing monitor for service web on port 8080.\n", - "Initializing monitor for service nginx on port 80.\n", - "METHOD: Pinging url http://localhost\n", - "METHOD: Checking web on port 8080...\n", - "METHOD: Status for service web: Alive\n", - "METHOD: Performing HTTP check on http://localhost\n", - "METHOD: Checking nginx on port 80...\n", - "METHOD: Status for service nginx: Alive\n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "class HttpServiceMonitor(ServiceMonitor):\n", - " \"\"\"Extends ServiceMonitor to add an HTTP endpoint check.\"\"\"\n", - " def __init__(self, service_name, port, url):\n", - " super().__init__(service_name, port)\n", - " self.url = url\n", - "\n", - " def ping(self):\n", - " \"\"\"Ping url provided when creating instance.\"\"\"\n", - " print(f\"METHOD: Pinging url {self.url}\")\n", - "\n", - " def check(self):\n", - " alive = super().check()\n", - " print(f\"METHOD: Performing HTTP check on {self.url}\")\n", - "\n", - "http_monitor = HttpServiceMonitor(\"web\", 8080, \"http://localhost\")\n", - "nginx_monitor = ServiceMonitor(\"nginx\", 80)\n", - "\n", - "http_monitor.ping()\n", - "http_monitor.check()\n", - "# nginx_monitor.ping() # Uncommenting will raise AttributeError since ping() is a method only of the subclass\n", - "nginx_monitor.check()" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "437475bc-f9c6-43a4-9c62-b418f894296d", + "id": "8949c112", "metadata": {}, "outputs": [], "source": [] @@ -233,7 +106,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/comments.ipynb b/python-fundamentals/comments.ipynb index b02952a..7ae42d3 100644 --- a/python-fundamentals/comments.ipynb +++ b/python-fundamentals/comments.ipynb @@ -24,12 +24,7 @@ "id": "daa90315", "metadata": {}, "outputs": [], - "source": [ - "# Example of a single-line comment\n", - "error_code = 0\n", - "\n", - "# TODO: handle case when argument is None " - ] + "source": [] }, { "cell_type": "markdown", @@ -46,10 +41,7 @@ "id": "9b06cd4c", "metadata": {}, "outputs": [], - "source": [ - "# if True:\n", - "# print(\"I will execute\")" - ] + "source": [] } ], "metadata": { @@ -68,7 +60,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/conditionals.ipynb b/python-fundamentals/conditionals.ipynb index fc6de31..ec6f0bf 100644 --- a/python-fundamentals/conditionals.ipynb +++ b/python-fundamentals/conditionals.ipynb @@ -30,26 +30,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "49aba219", "metadata": { "language": "python" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Service is active.\n" - ] - } - ], - "source": [ - "server_status = \"running\"\n", - "\n", - "if server_status == \"running\":\n", - " print(\"Service is active.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -68,33 +55,13 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "daf3fbf7", "metadata": { "language": "python" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Processing 2 servers.\n", - "Default config not available, please provide the configuration values.\n" - ] - } - ], - "source": [ - "servers = [\"web01\", \"web02\"]\n", - "error_message = \"\"\n", - "default_config = {}\n", - "\n", - "if servers:\n", - " print(f\"Processing {len(servers)} servers.\")\n", - "if error_message:\n", - " print(\"Something went wrong:\", error_message)\n", - "if not default_config:\n", - " print(\"Default config not available, please provide the configuration values.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -110,28 +77,13 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "b13de24d", "metadata": { "language": "python" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU Usage is normal.\n" - ] - } - ], - "source": [ - "cpu_usage = 85.0\n", - "\n", - "if cpu_usage > 90.0:\n", - " print(\"ALERT: High CPU Usage\")\n", - "else:\n", - " print(\"CPU Usage is normal.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -147,32 +99,13 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "666d75a2", "metadata": { "language": "python" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Server error (5xx)\n" - ] - } - ], - "source": [ - "http_status = 503\n", - "\n", - "if http_status == 200:\n", - " print(\"Status OK\")\n", - "elif http_status == 404:\n", - " print(\"Resource not found\")\n", - "elif http_status >= 500:\n", - " print(\"Server error (5xx)\")\n", - "else:\n", - " print(\"Another status:\", http_status)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -188,39 +121,11 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "1decd6fe-6463-4a5f-b356-a80e2c1e43e1", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "No data provided\n", - "No data provided\n", - "Invalid value type for 'data'. Provided ; Required: list\n", - "Invalid value type for 'data'. Provided ; Required: list\n", - "Processing 3 items...\n", - "Processed\n" - ] - } - ], - "source": [ - "def process_data_guarded(data):\n", - " if not data:\n", - " print(\"No data provided\")\n", - " elif not isinstance(data, list):\n", - " print(f\"Invalid value type for 'data'. Provided {type(data)}; Required: list\")\n", - " else:\n", - " print(f\"Processing {len(data)} items...\")\n", - " print(\"Processed\")\n", - "\n", - "process_data_guarded(None)\n", - "process_data_guarded([])\n", - "process_data_guarded(\"abc\")\n", - "process_data_guarded(10)\n", - "process_data_guarded([1, 2, 3])" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -247,7 +152,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/dictionaries.ipynb b/python-fundamentals/dictionaries.ipynb index 096bab1..b7286fa 100644 --- a/python-fundamentals/dictionaries.ipynb +++ b/python-fundamentals/dictionaries.ipynb @@ -44,122 +44,19 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "id": "773e70a5", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'a': 1, 'b': 2, 'c': 3}\n", - "Length: 3\n", - "Keys: dict_keys(['a', 'b', 'c'])\n", - "Values: dict_values([1, 2, 3])\n", - "Items: dict_items([('a', 1), ('b', 2), ('c', 3)])\n", - "\n", - "\n", - "\n", - "- a: 1\n", - "- b: 2\n", - "- c: 3\n", - "'b' is in my_dictionary? True\n", - "'d' is in my_dictionary? False\n", - "1 is in my_dictionary? False\n", - "1 is in values of my_dictionary? True\n", - "'b': 2\n", - "'b': 2\n", - "'e' without default: None\n", - "'e' with default: -1\n", - "{'a': 1, 'b': 2, 'c': 3, 'd': 4}\n", - "Removed value: 1\n", - "Removed value: ('d', 4)\n", - "Removed value: ('c', 3)\n" - ] - } - ], - "source": [ - "my_dictionary = {'a': 1, 'b': 2, 'c': 3}\n", - "print(my_dictionary)\n", - "\n", - "print(f\"Length: {len(my_dictionary)}\")\n", - "\n", - "# Keys, Values, and Items\n", - "print(f\"Keys: {my_dictionary.keys()}\")\n", - "print(f\"Values: {my_dictionary.values()}\")\n", - "print(f\"Items: {my_dictionary.items()}\")\n", - "\n", - "for item in my_dictionary.items():\n", - " print(type(item))\n", - "\n", - "for key, value in my_dictionary.items():\n", - " print(f\"- {key}: {value}\")\n", - "\n", - "# Membership test\n", - "print(f\"'b' is in my_dictionary? {\"b\" in my_dictionary}\")\n", - "print(f\"'d' is in my_dictionary? {\"d\" in my_dictionary}\")\n", - "print(f\"1 is in my_dictionary? {1 in my_dictionary}\")\n", - "print(f\"1 is in values of my_dictionary? {1 in set(my_dictionary.values())}\")\n", - "\n", - "# Accessing elements\n", - "print(\"'b':\", my_dictionary[\"b\"]) # Will raise KeyError if key is not present in the dictionary\n", - "print(\"'b':\", my_dictionary.get(\"b\")) # Will not raise KeyError\n", - "print(\"'e' without default:\", my_dictionary.get(\"e\"))\n", - "print(\"'e' with default:\", my_dictionary.get(\"e\", -1))\n", - "\n", - "my_dictionary.setdefault(\"d\", 4)\n", - "print(my_dictionary)\n", - "\n", - "# Removing elements\n", - "removed = my_dictionary.pop(\"a\")\n", - "print(f\"Removed value: {removed}\")\n", - "removed = my_dictionary.popitem()\n", - "print(f\"Removed value: {removed}\")\n", - "removed = my_dictionary.popitem()\n", - "print(f\"Removed value: {removed}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": null, "id": "7b169d2c-9993-49a4-854c-50cda3b0b955", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'Environment': 'Production', 'Owner': 'Finance', 'CostCenter': '12345'}\n", - "{'Environment': 'Production', 'Owner': 'Finance', 'CostCenter': '12345'}\n", - "{'one': 0, 'two': 0}\n", - "{}\n" - ] - } - ], - "source": [ - "# Merging of dictionaries\n", - "default_tags = {\n", - " \"Environment\": \"Production\",\n", - " \"Owner\": \"Finance\",\n", - " \"CostCenter\": \"10000\"\n", - "}\n", - "\n", - "custom_tags = {\n", - " \"CostCenter\": \"12345\"\n", - "}\n", - "\n", - "merged_tags = default_tags | custom_tags\n", - "print(merged_tags)\n", - "default_tags.update(custom_tags)\n", - "print(default_tags)\n", - "\n", - "# Creating new dictionary based on a set of keys\n", - "new_dict = dict.fromkeys(['one', 'two', 'one'], 0)\n", - "print(new_dict)\n", - "\n", - "new_dict.clear()\n", - "print(new_dict)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -173,30 +70,11 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": null, "id": "5673b794", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'Environment': 'Production', 'Owner': 'Finance', 'CostCenter': '12345', 'Project': 'Python for DevOps'}\n" - ] - } - ], - "source": [ - "tags = {\n", - " \"Environment\": \"Production\",\n", - " \"Owner\": \"Finance\",\n", - " \"CostCenter\": \"10000\"\n", - "}\n", - "\n", - "tags[\"CostCenter\"] = \"12345\"\n", - "tags[\"Project\"] = \"Python for DevOps\"\n", - "\n", - "print(tags)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -215,50 +93,11 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": null, "id": "64295288-0e18-41fa-b7f4-0e893ac36b28", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Server state: running\n", - "Instance type: t2.micro\n", - "{'id': 'web01', 'ip_address': '192.168.1.1', 'state': 'stopped', 'tags': {'environment': 'production', 'owner': 'engineering'}}\n", - "{'id': 'web01', 'ip_address': '192.168.1.1', 'state': 'stopped', 'tags': {'environment': 'production', 'owner': 'engineering', 'region': 'eu-central-1'}}\n", - "- id: web01\n", - "- ip_address: 192.168.1.1\n", - "- state: stopped\n", - "- tags: {'environment': 'production', 'owner': 'engineering', 'region': 'eu-central-1'}\n" - ] - } - ], - "source": [ - "server_info = {\n", - " \"id\": \"web01\",\n", - " \"ip_address\": \"192.168.1.1\",\n", - " \"state\": \"running\",\n", - " \"tags\": {\n", - " \"environment\": \"production\",\n", - " \"owner\": \"engineering\"\n", - " }\n", - "}\n", - "\n", - "print(\"Server state:\", server_info.get(\"state\"))\n", - "\n", - "instance_type = server_info.get(\"instance_type\", \"t2.micro\")\n", - "print(\"Instance type:\", instance_type)\n", - "\n", - "server_info[\"state\"] = \"stopped\"\n", - "print(server_info)\n", - "\n", - "server_info[\"tags\"][\"region\"] = \"eu-central-1\"\n", - "print(server_info)\n", - "\n", - "for key, value in server_info.items():\n", - " print(f\"- {key}: {value}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -285,7 +124,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/functions.ipynb b/python-fundamentals/functions.ipynb index f716856..9b2644e 100644 --- a/python-fundamentals/functions.ipynb +++ b/python-fundamentals/functions.ipynb @@ -22,38 +22,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "48eb5604", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, Alice!\n", - "Help on function greet_user in module __main__:\n", - "\n", - "greet_user(name)\n", - " Greets the user by name.\n", - "\n", - " Args:\n", - " name (str): The user to greet\n", - "\n" - ] - } - ], - "source": [ - "def greet_user(name):\n", - " \"\"\"Greets the user by name.\n", - "\n", - " Args:\n", - " name (str): The user to greet\n", - " \"\"\"\n", - "\n", - " print(f\"Hello, {name}!\")\n", - "\n", - "greet_user(\"Alice\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -67,36 +40,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "5af445cb", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generated number: 2\n" - ] - } - ], - "source": [ - "import random\n", - "\n", - "def random_number(min_val, max_val):\n", - " \"\"\"Generates an integer between min_val and max_val\n", - "\n", - " Args:\n", - " min_val (int): The lower boundary of the interval\n", - " max_val (int): The upper boundary of the interval\n", - "\n", - " Returns:\n", - " int: The generated random number\n", - " \"\"\"\n", - " return random.randint(min_val, max_val)\n", - "\n", - "generated_number = random_number(0, 10)\n", - "print(f\"Generated number: {generated_number}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -114,9 +62,7 @@ "id": "66a62166", "metadata": {}, "outputs": [], - "source": [ - "generated_number = random_number(-1, 100)" - ] + "source": [] }, { "cell_type": "markdown", @@ -130,45 +76,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "ebcf2032", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Checking nginx for running...\n", - "Checking running for nginx...\n", - "Checking nginx for running...\n", - "Checking nginx for running...\n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def check_service_status(service_name, expected_status):\n", - " print(f\"Checking {service_name} for {expected_status}...\")\n", - " return True\n", - "\n", - "check_service_status(\"nginx\", \"running\")\n", - "check_service_status(\"running\", \"nginx\")\n", - "\n", - "check_service_status(service_name=\"nginx\", expected_status=\"running\")\n", - "check_service_status(expected_status=\"running\", service_name=\"nginx\")\n", - "\n", - "# Positional arguments must come before keyword arguments\n", - "# check_service_status(service_name=\"nginx\", \"running\") # Uncommenting raises a SyntaxError" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -182,38 +94,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "6adc06d4", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connect to host web01 on port 22 (timeout 30)\n", - "Connect to host web02 on port 443 (timeout 60)\n", - "Connect to host web03 on port 60 (timeout 30)\n", - "Connect to host web03 on port 22 (timeout 60)\n" - ] - } - ], - "source": [ - "def connect(host, port=22, timeout=30):\n", - " print(f\"Connect to host {host} on port {port} (timeout {timeout})\")\n", - "\n", - "connect(\"web01\")\n", - "connect(\"web02\", 443, 60)\n", - "\n", - "# When wanting to set the value of timeout but use the default value of port\n", - "# We need to use keyword arguments, since positional arguments would be\n", - "# incorrectly mapped\n", - "\n", - "# Bad exaple - see how port is set to 60 and timeout remains 30\n", - "connect(\"web03\", 60)\n", - "\n", - "# Good example - both values are set as we expect\n", - "connect(\"web03\", timeout=60)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -233,49 +118,11 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "840e3b40", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n", - "False\n", - "False\n" - ] - } - ], - "source": [ - "import socket\n", - "\n", - "def check_port(host, port, timeout=5):\n", - " \"\"\"Checks if a TCP port is open on a given host.\n", - " \n", - " Args:\n", - " host (str): Hostname or IP address.\n", - " port (int): TCP port number.\n", - " timeout (int, option): Connection timeout in seconds. Defaults to 5.\n", - "\n", - " Returns:\n", - " bool: True if the port is open, False otherwise.\n", - " \"\"\"\n", - "\n", - " try:\n", - " with socket.create_connection((host, port), timeout):\n", - " return True\n", - " except Exception:\n", - " return False\n", - "\n", - "print(check_port(\"www.google.com\", 443))\n", - "\n", - "# Port 22 is not open, should return False\n", - "print(check_port(\"www.google.com\", 22))\n", - "\n", - "# Host does not exist, should return False\n", - "print(check_port(\"www.afbdoaubfdoabdfoubaf.com\", 22))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -308,33 +155,11 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "3aa3d109", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, Alice!\n", - "Hello, Bob!\n", - "Hello, Charlie!\n" - ] - } - ], - "source": [ - "def greet_users(names):\n", - " \"\"\"Greets a list of users by name.\n", - "\n", - " Args:\n", - " names (list(str)): The list of users to greet.\n", - " \"\"\"\n", - "\n", - " for name in names:\n", - " print(f\"Hello, {name}!\")\n", - "\n", - "greet_users([\"Alice\", \"Bob\", \"Charlie\"])" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -350,32 +175,11 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "id": "7e90edc0", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "12\n" - ] - } - ], - "source": [ - "def sum_even(numbers):\n", - " \"\"\"Calculates the sum of even numbers from a list.\n", - "\n", - " Args:\n", - " numbers (list(int)): The list of numbers to evaluate.\n", - "\n", - " Returns:\n", - " int: The sum of the even numbers.\n", - " \"\"\"\n", - " return sum(x for x in numbers if x % 2 == 0)\n", - "\n", - "print(sum_even([1, 2, 3, 4, 5, 6]))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -391,39 +195,11 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "id": "1b9035f3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]\n", - "[0]\n" - ] - } - ], - "source": [ - "def fibonacci(n):\n", - " \"\"\"Calculates the first n Fibonacci numbers.\n", - "\n", - " Args:\n", - " n (int): The amount of numbers to calculate.\n", - "\n", - " Returns:\n", - " list(int): The first n Fibonacci numbers.\n", - " \"\"\"\n", - " seq = [0, 1]\n", - "\n", - " for _ in range(2, n):\n", - " seq.append(seq[-1] + seq[-2])\n", - "\n", - " return seq[:n]\n", - "\n", - "print(fibonacci(10))\n", - "print(fibonacci(1))" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -450,7 +226,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/lambda-functions.ipynb b/python-fundamentals/lambda-functions.ipynb index dd32fd7..630cc01 100644 --- a/python-fundamentals/lambda-functions.ipynb +++ b/python-fundamentals/lambda-functions.ipynb @@ -26,25 +26,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "3a0057ff-0504-4123-8c94-1210774e4177", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "25\n", - "7\n" - ] - } - ], - "source": [ - "square = lambda x: x * x\n", - "print(square(5))\n", - "\n", - "print((lambda a, b: a + b)(3, 4))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -60,31 +46,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "2be63c6e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Default sort: [('api-gateway', 2), ('cache', 5), ('database', 1), ('web-app', 3)]\n", - "Sorting by replica count - standard function: [('database', 1), ('api-gateway', 2), ('web-app', 3), ('cache', 5)]\n", - "Sorting by replica count - lambda function: [('database', 1), ('api-gateway', 2), ('web-app', 3), ('cache', 5)]\n" - ] - } - ], - "source": [ - "services = [(\"web-app\", 3), (\"database\", 1), (\"cache\", 5), (\"api-gateway\", 2)]\n", - "\n", - "print(f\"Default sort: {sorted(services)}\")\n", - "\n", - "def get_replica_count(svc_tuple):\n", - " return svc_tuple[1]\n", - "\n", - "print(f\"Sorting by replica count - standard function: {sorted(services, key=get_replica_count)}\")\n", - "print(f\"Sorting by replica count - lambda function: {sorted(services, key=lambda svc: svc[1])}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -100,28 +66,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "d197dc77-f1a8-4171-ab38-93f2e5ea1009", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2, 4, 6, 8]\n", - "['Port 80 is open', 'Port 443 is open', 'Port 8080 is open', 'Port 22 is open']\n" - ] - } - ], - "source": [ - "my_numbers = [1, 2, 3, 4]\n", - "print(list(map(lambda num: num * 2, my_numbers)))\n", - "\n", - "ports = [80, 443, 8080, 22]\n", - "port_descriptions = list(map(lambda port: f\"Port {port} is open\", ports))\n", - "\n", - "print(port_descriptions)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -137,28 +86,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "4e2102bf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[80, 443, 22]\n", - "[80, 443, 22]\n" - ] - } - ], - "source": [ - "ports = [80, 443, 8080, 22, 5432]\n", - "\n", - "privileged_ports = list(filter(lambda port: port < 1024, ports))\n", - "print(privileged_ports)\n", - "\n", - "privileged_comprehension = [port for port in ports if port < 1024]\n", - "print(privileged_comprehension)" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", diff --git a/python-fundamentals/lazy-iteration.ipynb b/python-fundamentals/lazy-iteration.ipynb index dc9e64b..c30b3d2 100644 --- a/python-fundamentals/lazy-iteration.ipynb +++ b/python-fundamentals/lazy-iteration.ipynb @@ -15,35 +15,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "6e241cb0", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "List size: 76.29\n", - "Range size: 0.000046\n", - "List uses 1666667.83 more memory!\n" - ] - } - ], - "source": [ - "import sys\n", - "\n", - "number_count = 10_000_000\n", - "\n", - "numbers_list = list(range(number_count))\n", - "numbers_range = range(number_count)\n", - "\n", - "list_mb = sys.getsizeof(numbers_list) / (1024**2)\n", - "range_mb = sys.getsizeof(numbers_range) / (1024**2)\n", - "\n", - "print(f\"List size: {list_mb:.2f}\")\n", - "print(f\"Range size: {range_mb:.6f}\")\n", - "print(f\"List uses {(list_mb / range_mb):.2f} more memory!\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -59,40 +35,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "9033a333", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retry #0\n", - "Retry #1\n", - "Retry #2\n", - "Retry #3\n", - "Retry #4\n", - "Processing logs for 2020\n", - "Processing logs for 2021\n", - "Processing logs for 2022\n", - "Processing logs for 2023\n", - "Checking server 10\n", - "Checking server 15\n", - "Checking server 20\n", - "Checking server 25\n" - ] - } - ], - "source": [ - "for i in range(5):\n", - " print(f\"Retry #{i}\")\n", - "\n", - "for year in range(2020, 2024):\n", - " print(f\"Processing logs for {year}\")\n", - "\n", - "for server_id in range(10, 30, 5):\n", - " print(f\"Checking server {server_id}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -107,26 +54,11 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "b889234c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "#1: Processing server web01\n", - "#2: Processing server web02\n", - "#3: Processing server web03\n" - ] - } - ], - "source": [ - "servers = [\"web01\", \"web02\", \"web03\"]\n", - "\n", - "for idx, server in enumerate(servers, 1):\n", - " print(f\"#{idx}: Processing server {server}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -141,27 +73,11 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "b6baf3d4-236b-414c-8d50-8753b8583ca7", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Host: hostA, IP: 10.0.0.1, AZ: us-east-1a\n", - "Host: hostB, IP: 10.0.0.2, AZ: us-east-1b\n" - ] - } - ], - "source": [ - "hosts = [\"hostA\", \"hostB\", \"hostC\"]\n", - "ips = [\"10.0.0.1\", \"10.0.0.2\"]\n", - "azs = [\"us-east-1a\", \"us-east-1b\"]\n", - "\n", - "for host, ip, az in zip(hosts, ips, azs):\n", - " print(f\"Host: {host}, IP: {ip}, AZ: {az}\")" - ] + "outputs": [], + "source": [] } ], "metadata": { @@ -180,7 +96,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/list-comprehension.ipynb b/python-fundamentals/list-comprehension.ipynb index d655a34..3a9bdef 100644 --- a/python-fundamentals/list-comprehension.ipynb +++ b/python-fundamentals/list-comprehension.ipynb @@ -12,33 +12,11 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "0bc29d4d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2, 4, 6, 8]\n", - "[2, 4, 6, 8]\n" - ] - } - ], - "source": [ - "# Example: Double items using a for loop\n", - "old_items = [1, 2, 3, 4]\n", - "doubled_items = []\n", - "\n", - "for item in old_items:\n", - " doubled_items.append(item * 2)\n", - "\n", - "print(doubled_items)\n", - "\n", - "# Example: Double items using list comprehension\n", - "doubled_items_with_comprehension = [item * 2 for item in old_items]\n", - "print(doubled_items_with_comprehension)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -55,23 +33,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "716cb804", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['WEB', 'DB', 'BACKEND']\n" - ] - } - ], - "source": [ - "servers = [\"web\", \"db\", \"backend\"]\n", - "uppercase_servers = [server.upper() for server in servers]\n", - "print(uppercase_servers)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -87,23 +53,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "d82281a3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[11, 9, 3]\n" - ] - } - ], - "source": [ - "numbers = [1, 5, 10, 8, 2, 15]\n", - "even_numbers = [num + 1 for num in numbers if num % 2 == 0]\n", - "print(even_numbers)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -119,28 +73,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "51c28a23", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{16, 1, 4, 9}\n", - "{'web': '192.168.1.0', 'backend': '192.168.1.1'}\n" - ] - } - ], - "source": [ - "numbers = [1, 2, 3, 2, 4, 1, 3]\n", - "unique_squares = {x * x for x in numbers}\n", - "print(unique_squares)\n", - "\n", - "servers = [\"web\", \"backend\"]\n", - "server_ips = {server: f\"192.168.1.{i}\" for i, server in enumerate(servers)}\n", - "print(server_ips)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -156,23 +93,11 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "6e7bdc52", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['FAIL', 'FAIL', 'PASS', 'PASS', 'FAIL', 'PASS']\n" - ] - } - ], - "source": [ - "numbers = [1, 5, 10, 8, 2, 15]\n", - "categories = [\"PASS\" if num >= 8 else \"FAIL\" for num in numbers]\n", - "print(categories)" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -199,7 +124,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/lists.ipynb b/python-fundamentals/lists.ipynb index 922b5cf..57d9ddb 100644 --- a/python-fundamentals/lists.ipynb +++ b/python-fundamentals/lists.ipynb @@ -29,88 +29,19 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "id": "a926ed1b-4d48-46e5-93e6-706e763878c9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\n", - "web01\n", - "web03\n", - "web02\n", - "['web01', 'web02']\n", - "['web02', 'web03']\n", - "['web02', 'web03']\n", - "['web01', 'web02', 'web03']\n" - ] - } - ], - "source": [ - "servers = [\"web01\", \"web02\", \"web03\"]\n", - "mixed_list = [\"config.yaml\", 8080, True]\n", - "\n", - "for item in mixed_list:\n", - " print(type(item))\n", - "\n", - "print(servers[0])\n", - "# print(servers[3]) # Commenting this out will raise an IndexError Exception\n", - "print(servers[-1])\n", - "print(servers[-2])\n", - "\n", - "# Slicing\n", - "print(servers[:2]) # Will print only elements at indexes 0 and 1\n", - "print(servers[1:]) # Will print only elements at indexes 1 and 2\n", - "print(servers[-2:]) # Will print only the second to last and last elements\n", - "# Slicing does not alter the original list\n", - "print(servers)" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "id": "c3cb2082-2372-4e77-996e-a44fa192ec0b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[80, 443, 8080, 5000]\n", - "[80, 3000, 443, 8080, 5000]\n", - "[3000, 443, 8080, 5000]\n", - "[3000, 443, 5000]\n", - "8080\n", - "['a', 'b']\n" - ] - } - ], - "source": [ - "# Mutating lists\n", - "ports = [80, 443, 8080]\n", - "ports.append(5000)\n", - "print(ports)\n", - "ports.insert(1, 3000)\n", - "print(ports)\n", - "ports.remove(80)\n", - "print(ports)\n", - "removed_value = ports.pop(2)\n", - "print(ports)\n", - "print(removed_value)\n", - "\n", - "# Example to show how mutating lists can lead to side-effects outside of\n", - "# the scope of the code that modifies the list.\n", - "def mutate_list(l):\n", - " l.pop()\n", - "\n", - "new_list = [\"a\", \"b\", \"c\"]\n", - "mutate_list(new_list)\n", - "print(new_list)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -127,28 +58,11 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": null, "id": "01c35747-c9fd-45b5-97bb-193beb4f958f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "us-east-1\n", - "['us-east-1', 'eu-west-1', 'ap-southeast-2', 'us-west-2']\n", - "['us-east-1', 'eu-central-1', 'ap-southeast-2', 'us-west-2']\n" - ] - } - ], - "source": [ - "deployment_targets = [\"us-east-1\", \"eu-west-1\", \"ap-southeast-2\"]\n", - "print(deployment_targets[0])\n", - "deployment_targets.append(\"us-west-2\")\n", - "print(deployment_targets)\n", - "deployment_targets[1] = \"eu-central-1\"\n", - "print(deployment_targets)" - ] + "outputs": [], + "source": [] } ], "metadata": { @@ -167,7 +81,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/loops.ipynb b/python-fundamentals/loops.ipynb index d8e1c2c..3badd90 100644 --- a/python-fundamentals/loops.ipynb +++ b/python-fundamentals/loops.ipynb @@ -33,49 +33,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "fe2defb2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Pinging server: web01\n", - "Pinging server: web02\n", - "Pinging server: web03\n", - "S\n", - "U\n", - "C\n", - "C\n", - "E\n", - "S\n", - "S\n", - "Pinging server: 0\n", - "Pinging server: 1\n", - "Pinging server: 2\n", - "Pinging server: 3\n", - "Pinging server: 4\n", - "Pinging server: 5\n", - "Pinging server: 6\n", - "Pinging server: 7\n", - "Pinging server: 8\n", - "Pinging server: 9\n" - ] - } - ], - "source": [ - "servers = [\"web01\", \"web02\", \"web03\"]\n", - "\n", - "for server in servers:\n", - " print(\"Pinging server:\", server)\n", - "\n", - "for char in \"SUCCESS\":\n", - " print(char)\n", - "\n", - "for idx in range(10):\n", - " print(\"Pinging server:\", idx)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -89,37 +51,11 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "4e2102bf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Attempting to reach server: 1\n", - "Attempting to reach server: 2\n", - "Attempting to reach server: 3\n", - "Attempting to reach server: 4\n" - ] - } - ], - "source": [ - "connection_attempts = 0\n", - "max_attempts = 5\n", - "connected = False\n", - "\n", - "while not connected and connection_attempts < max_attempts:\n", - " print(f\"Attempting to reach server: {connection_attempts + 1}\")\n", - " # Simulating for the purposes of demonstration - Succeeds on 4th attempt\n", - " if connection_attempts == 3:\n", - " connected = True\n", - "\n", - " connection_attempts += 1\n", - "\n", - "if not connected:\n", - " print(\"Failed to connect after maximum attempts.\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -142,64 +78,16 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "73b9db6f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Checking user: guest\n", - "Checking user: tester\n", - "Checking user: admin01\n", - "Admin user found: admin01. Stopping search.\n" - ] - } - ], - "source": [ - "users = [\"guest\", \"tester\", \"admin01\", \"admin02\", \"dev01\"]\n", - "found_admin = None\n", - "\n", - "for user in users:\n", - " print(f\"Checking user: {user}\")\n", - " if user.startswith(\"admin\"):\n", - " found_admin = user\n", - " print(f\"Admin user found: {found_admin}. Stopping search.\")\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "34a0c102-0fd5-4b04-bbe0-5825cf2831a5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Skipping non-yaml file: nginx.conf\n", - "Processing YAML config: app.yaml\n", - "Processing YAML config: db.yaml\n", - "Skipping non-yaml file: notes.txt\n" - ] - } - ], - "source": [ - "filenames = [\"nginx.conf\", \"app.yaml\", \"db.yaml\", \"notes.txt\"]\n", - "\n", - "for file in filenames:\n", - " if not file.endswith(\".yaml\"):\n", - " print(f\"Skipping non-yaml file: {file}\") \n", - " continue\n", - " print(f\"Processing YAML config: {file}\")" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "34117803-d294-43a5-a4bd-60a19bb3c69d", + "id": "34a0c102-0fd5-4b04-bbe0-5825cf2831a5", "metadata": {}, "outputs": [], "source": [] @@ -221,7 +109,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/numbers.ipynb b/python-fundamentals/numbers.ipynb index 4ffbe7c..2bba1a9 100644 --- a/python-fundamentals/numbers.ipynb +++ b/python-fundamentals/numbers.ipynb @@ -13,32 +13,11 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "44afd4c3-cf45-4a88-b1fd-e3b3216ee622", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "When comparing floats directly, we may run into precision issues:\n", - "0.1 * 3 == 0.3: False\n", - "To tackle this, we can use the math.isclose() function:\n", - "math.isclose(0.1 * 3, 0.3): True\n" - ] - } - ], - "source": [ - "import math\n", - "\n", - "print(type(1.0))\n", - "\n", - "print(\"When comparing floats directly, we may run into precision issues:\")\n", - "print(\"0.1 * 3 == 0.3: \", 0.1 * 3 == 0.3)\n", - "print(\"To tackle this, we can use the math.isclose() function:\")\n", - "print(\"math.isclose(0.1 * 3, 0.3): \", math.isclose(0.1 * 3, 0.3))" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -56,35 +35,11 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "id": "9672fcdd-f9e6-41bb-85e1-07b7ba40b810", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "4.0\n", - "\n", - "1.6666666666666667\n", - "4\n", - "\n", - "1\n", - "1.0\n", - "2\n" - ] - } - ], - "source": [ - "print(8/2)\n", - "print(type(8/2))\n", - "print(5/3)\n", - "print(8//2)\n", - "print(type(8//2))\n", - "print(5//3)\n", - "print(5//3.0)\n", - "print(5%3) # 1 as the result, and 2 remaining" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -111,7 +66,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/sets.ipynb b/python-fundamentals/sets.ipynb index c12af3f..76d967d 100644 --- a/python-fundamentals/sets.ipynb +++ b/python-fundamentals/sets.ipynb @@ -30,100 +30,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "d322affb-a9b4-4e7f-b562-88c70e31bfa8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{80, 443, 8080, 22}\n", - "True\n", - "False\n", - "{80, 8080, 22, 3000, 443}\n", - "{80, 8080, 3000, 443}\n", - "{80, 8080, 3000, 443}\n" - ] - } - ], - "source": [ - "unique_ports = set([80, 443, 22, 80, 8080, 443])\n", - "server_names = {\"web01\", \"web02\"}\n", - "\n", - "print(unique_ports)\n", - "print(22 in unique_ports)\n", - "print(22 in server_names)\n", - "\n", - "unique_ports.add(3000)\n", - "print(unique_ports)\n", - "unique_ports.remove(22)\n", - "print(unique_ports)\n", - "# unique_ports.remove(22) # Will raise KeyError because item 22 is not in the set anymore\n", - "unique_ports.discard(22)\n", - "print(unique_ports)" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "302359f2-2892-4476-b267-fbb3efd33abb", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{(1, 2), (3, 4)}\n", - "False\n" - ] - } - ], - "source": [ - "# set_of_lists = set([[1, 2], [3, 4]]) # Will throw a TypeError, since lists are mutable\n", - "# set_of_sets = {{1, 2}, {3, 4}} # Will throw a TypeError, since sets are mutable\n", - "set_of_tuples = {(1, 2), (3, 4)}\n", - "print(set_of_tuples)\n", - "print((1, 2) in set_of_tuples)\n", - "print((1, 3) in set_of_tuples)" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "a03d3012-c5f0-4a78-a5ab-61d965aef712", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n", - "True\n", - "Union: {'david', 'charlie', 'alice', 'bob'}\n", - "Intersection: {'alice'}\n", - "Difference: {'bob', 'charlie'}\n", - "Union: {'david', 'charlie', 'alice', 'bob'}\n", - "Intersection: {'alice'}\n", - "Difference: {'bob', 'charlie'}\n" - ] - } - ], - "source": [ - "# Set operations\n", - "\n", - "developers = set([\"alice\", \"bob\", \"charlie\"])\n", - "admins = set([\"alice\", \"david\"])\n", - "\n", - "print(\"alice\" in developers)\n", - "print(\"alice\" in admins)\n", - "print(\"Union:\", developers.union(admins))\n", - "print(\"Intersection:\", developers.intersection(admins))\n", - "print(\"Difference:\", developers.difference(admins))\n", - "print(\"Union:\", developers | admins)\n", - "print(\"Intersection:\", developers & admins)\n", - "print(\"Difference:\", developers - admins)" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -143,54 +62,13 @@ "6. Given these two sets, compute missing, extra, and common packages." ] }, - { - "cell_type": "code", - "execution_count": 40, - "id": "7ca1ac04", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'pip', 'boto3', 'requests', 'python3'}\n", - "Is 'requests' required? True\n", - "Is 'ansible' required? False\n", - "{'boto3', 'paramiko', 'python3', 'requests'}\n", - "Missing packages: {'boto3', 'requests', 'paramiko'}\n", - "Extra packages: {'pip', 'docker'}\n", - "Common packages: {'python3'}\n" - ] - } - ], - "source": [] - }, { "cell_type": "code", "execution_count": null, "id": "61d91d6d-2ef4-4063-9384-5b7f5b7f7362", "metadata": {}, "outputs": [], - "source": [ - "required_packages = set([\"python3\", \"pip\", \"requests\", \"boto3\", \"pip\"])\n", - "print(required_packages)\n", - "\n", - "print(f\"Is 'requests' required? {\"requests\" in required_packages}\")\n", - "print(f\"Is 'ansible' required? {\"ansible\" in required_packages}\")\n", - "\n", - "required_packages.add(\"paramiko\")\n", - "required_packages.discard(\"pip\")\n", - "print(required_packages)\n", - "\n", - "installed_packages = {\"docker\", \"python3\", \"pip\"}\n", - "missing_packages = required_packages - installed_packages\n", - "extra_packages = installed_packages - required_packages\n", - "common_packages = required_packages & installed_packages\n", - "\n", - "print(f\"Missing packages: {missing_packages}\")\n", - "print(f\"Extra packages: {extra_packages}\")\n", - "print(f\"Common packages: {common_packages}\")" - ] + "source": [] } ], "metadata": { @@ -209,7 +87,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/strings.ipynb b/python-fundamentals/strings.ipynb index ab4c204..ea49f65 100644 --- a/python-fundamentals/strings.ipynb +++ b/python-fundamentals/strings.ipynb @@ -1,281 +1,152 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "369af8d8-d881-4b2a-8ab8-7d0256d7d358", - "metadata": {}, - "source": [ - "# Strings" - ] - }, - { - "cell_type": "markdown", - "id": "7845c5c4", - "metadata": {}, - "source": [ - "## String Manipulation\n", - "\n", - "- Strings are ordered, immutable sequences of characters.\n", - "- Use single or double quotes consistently; triple quotes for multi-line strings or docstrings." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "5071b74a", - "metadata": {}, - "outputs": [ + "cells": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "I will not be indented\n", - " I will be indented\n", - "\n" - ] - } - ], - "source": [ - "single_line_str = \"Double quoted\"\n", - "single_line_str2 = 'Single quoted'\n", - "\n", - "command_template = \"\"\"\n", - "I will not be indented\n", - " I will be indented\n", - "\"\"\"\n", - "\n", - "print(command_template)" - ] - }, - { - "cell_type": "markdown", - "id": "34371c3e", - "metadata": {}, - "source": [ - "## Format Output using f-string\n", - "\n", - "**Tip:** f-strings allow inline expression evaluation and formatting, making string construction concise and readable." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "b1b1007b", - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "id": "369af8d8-d881-4b2a-8ab8-7d0256d7d358", + "metadata": {}, + "source": [ + "# Strings" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Result: 3.5\n", - "Result: 3.5\n" - ] - } - ], - "source": [ - "math_division = 7/2\n", - "print(f\"Result: {math_division}\")\n", - "print(f\"Result: {7/2}\")" - ] - }, - { - "cell_type": "markdown", - "id": "53d76649", - "metadata": {}, - "source": [ - "### Common Operations and Essential String Methods\n", - "\n", - "- Concatenation (`+`): Joins strings.\n", - "- Length (`len()`): Gets the number of characters.\n", - "- Indexing (`[]`): Access a character by position (0-based).\n", - "- Slicing (`[:]`): Extract substrings.\n", - "- `.lower() / .upper()`\n", - "- `.strip() / .lstrip() / .rstrip()`\n", - "- `.startswith() / .endswith()`\n", - "- `.split() / .join()`\n", - "- `.replace()`" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "b30131e9", - "metadata": { - "scrolled": true - }, - "outputs": [ + "cell_type": "markdown", + "id": "7845c5c4", + "metadata": {}, + "source": [ + "## String Manipulation\n", + "\n", + "- Strings are ordered, immutable sequences of characters.\n", + "- Use single or double quotes consistently; triple quotes for multi-line strings or docstrings." + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - " Python for DevOps \n", - "Result of .strip(): Python for DevOps\n", - "Result of .lstrip(): Python for DevOps \n", - "Result of .rstrip(): Python for DevOps\n", - "Result of .upper(): PYTHON FOR DEVOPS \n", - "Result of .lower(): python for devops \n", - "True\n", - "True\n", - "path parts: ['', 'usr', 'local', 'bin']\n", - "joined path paths: \\usr\\local\\bin\n", - "/usr/local/bin/python\n", - "14\n", - "r\n", - "r/local\n", - "r/local/bin\n", - "/usr/local\n" - ] - } - ], - "source": [ - "course_title = \" Python for DevOps \"\n", - "print(course_title)\n", - "print(f\"Result of .strip(): {course_title.strip()}\")\n", - "print(f\"Result of .lstrip(): {course_title.lstrip()}\")\n", - "print(f\"Result of .rstrip(): {course_title.rstrip()}\")\n", - "print(f\"Result of .upper(): {course_title.upper()}\")\n", - "print(f\"Result of .lower(): {course_title.lower()}\")\n", - "\n", - "filename = \"file.yaml\"\n", - "print(filename.startswith(\"file\"))\n", - "print(filename.endswith(\"yaml\"))\n", - "\n", - "path = \"/usr/local/bin\"\n", - "path_parts = path.split(\"/\")\n", - "print(f\"path parts: {path_parts}\")\n", - "print(f\"joined path paths: {\"\\\\\".join(path_parts)}\")\n", - "\n", - "print(path + \"/python\")\n", - "print(len(path))\n", - "print(path[3])\n", - "print(path[3:10])\n", - "print(path[3:])\n", - "print(path[:10])" - ] - }, - { - "cell_type": "markdown", - "id": "bd325a2b-71d3-4434-96c1-4d5c2a426877", - "metadata": {}, - "source": [ - "### String Immutability\n", - "\n", - "Strings are immutable, meaning you cannot change a string in place; operations that seem to modify a string actually create and return a new string object." - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "23ea841e", - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": null, + "id": "5071b74a", + "metadata": {}, + "outputs": [], + "source": [] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - " Python for DevOps \n", - "Result of .strip(): Python for DevOps\n", - "Result of .lstrip(): Python for DevOps \n", - "Result of .rstrip(): Python for DevOps\n", - "Result of .upper(): PYTHON FOR DEVOPS \n", - "Result of .lower(): python for devops \n", - " Python for DevOps \n" - ] - } - ], - "source": [ - "course_title = \" Python for DevOps \"\n", - "print(course_title)\n", - "print(f\"Result of .strip(): {course_title.strip()}\")\n", - "print(f\"Result of .lstrip(): {course_title.lstrip()}\")\n", - "print(f\"Result of .rstrip(): {course_title.rstrip()}\")\n", - "print(f\"Result of .upper(): {course_title.upper()}\")\n", - "print(f\"Result of .lower(): {course_title.lower()}\")\n", - "print(course_title)" - ] - }, - { - "cell_type": "markdown", - "id": "c113ccfa-1e07-447e-9312-dda2678bd984", - "metadata": {}, - "source": [ - "## Exercise: Calculate Disk Usage Percentage\n", - "\n", - "In this exercise you’ll combine basic arithmetic with f‑string formatting to report disk usage for a server.\n", - "\n", - "Objectives:\n", - "- Given the variables below, compute the disk usage percentage.\n", - "- Print the raw percentage value.\n", - "- Build a human‑readable summary string:\n", - " - Convert the server name to uppercase.\n", - " - Include the number of CPU cores and amount of RAM.\n", - " - Show the disk usage percentage rounded to one decimal place.\n", - "- Print a summary containing the server name in uppercase, the number of CPU cores, the memory, and the disk usage).\n", - "- Finally, use the `.2%` format specifier in an f‑string to display the usage with two decimal places and a percent sign.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "id": "146c476b", - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "id": "34371c3e", + "metadata": {}, + "source": [ + "## Format Output using f-string\n", + "\n", + "**Tip:** f-strings allow inline expression evaluation and formatting, making string construction concise and readable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1b1007b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "53d76649", + "metadata": {}, + "source": [ + "### Common Operations and Essential String Methods\n", + "\n", + "- Concatenation (`+`): Joins strings.\n", + "- Length (`len()`): Gets the number of characters.\n", + "- Indexing (`[]`): Access a character by position (0-based).\n", + "- Slicing (`[:]`): Extract substrings.\n", + "- `.lower() / .upper()`\n", + "- `.strip() / .lstrip() / .rstrip()`\n", + "- `.startswith() / .endswith()`\n", + "- `.split() / .join()`\n", + "- `.replace()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b30131e9", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "bd325a2b-71d3-4434-96c1-4d5c2a426877", + "metadata": {}, + "source": [ + "### String Immutability\n", + "\n", + "Strings are immutable, meaning you cannot change a string in place; operations that seem to modify a string actually create and return a new string object." + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "70.0\n", - "Server 'WEBSERVER-03' (4 cores, 8.0GB RAM) Disk usage: 0.7\n", - "Server 'WEBSERVER-03' (4 cores, 8.0GB RAM) Disk usage: 70.00%\n" - ] + "cell_type": "code", + "execution_count": null, + "id": "23ea841e", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c113ccfa-1e07-447e-9312-dda2678bd984", + "metadata": {}, + "source": [ + "## Exercise: Calculate Disk Usage Percentage\n", + "\n", + "In this exercise you’ll combine basic arithmetic with f‑string formatting to report disk usage for a server.\n", + "\n", + "Objectives:\n", + "- Given the variables below, compute the disk usage percentage.\n", + "- Print the raw percentage value.\n", + "- Build a human‑readable summary string:\n", + " - Convert the server name to uppercase.\n", + " - Include the number of CPU cores and amount of RAM.\n", + " - Show the disk usage percentage rounded to one decimal place.\n", + "- Print a summary containing the server name in uppercase, the number of CPU cores, the memory, and the disk usage).\n", + "- Finally, use the `.2%` format specifier in an f‑string to display the usage with two decimal places and a percent sign.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "146c476b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a1be08a-a36e-46ef-8398-291103e90086", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.9" } - ], - "source": [ - "server_name = \"webserver-03\"\n", - "cpu_cores = 4\n", - "memory_gb = 8.0\n", - "disk_total_gb = 500\n", - "disk_used_gb = 350\n", - "\n", - "disk_usage_percentage = disk_used_gb / disk_total_gb\n", - "print(disk_usage_percentage)\n", - "\n", - "summary = f\"Server '{server_name.upper()}' ({cpu_cores} cores, {memory_gb}GB RAM) Disk usage: {disk_usage_percentage}\"\n", - "print(summary)\n", - "\n", - "summary_formatted = f\"Server '{server_name.upper()}' ({cpu_cores} cores, {memory_gb}GB RAM) Disk usage: {disk_usage_percentage:.2%}\"\n", - "print(summary_formatted)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8a1be08a-a36e-46ef-8398-291103e90086", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/python-fundamentals/tuples.ipynb b/python-fundamentals/tuples.ipynb index 4b2881d..8c37499 100644 --- a/python-fundamentals/tuples.ipynb +++ b/python-fundamentals/tuples.ipynb @@ -23,35 +23,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "5ad35df9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "Host: 127.0.0.1\n", - "(0, 0)\n", - "\n" - ] - } - ], - "source": [ - "host_port = (\"127.0.0.1\", 3000)\n", - "red_rgb = (255, 0, 0)\n", - "tuple_single_value = (\"only-value\",) # To create a single-item tuple, add a trailing comma\n", - "print(type(host_port))\n", - "print(type(tuple_single_value))\n", - "\n", - "print(f\"Host: {host_port[0]}\")\n", - "print(red_rgb[-2:])\n", - "print(type(red_rgb[-2:]))\n", - "\n", - "# host_port[0] = \"192.168.1.1\" # Uncommenting will raise a TypeError because tuples are immutable" - ] + "outputs": [], + "source": [] }, { "cell_type": "markdown", @@ -66,26 +42,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "d48b5545-7c66-4d1e-b3f3-bca5ccb5211e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hostname: auth-server.dev.local\n", - "Port: 80\n" - ] - } - ], - "source": [ - "service_endpoint = (\"auth-server.dev.local\", 80)\n", - "print(f\"Hostname: {service_endpoint[0]}\")\n", - "print(f\"Port: {service_endpoint[1]}\")\n", - "\n", - "# service_endpoint[1] = 443 # Uncommenting will raise a TypeError because tuples are immutable" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -112,7 +73,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/python-fundamentals/variables.ipynb b/python-fundamentals/variables.ipynb index a1097bd..0b2879c 100644 --- a/python-fundamentals/variables.ipynb +++ b/python-fundamentals/variables.ipynb @@ -15,76 +15,6 @@ "- **Typing:** Python uses dynamic typing, which means we don't need to explicitly declare the variable type, and we can assign values with different types to the same variable (not recommended!)" ] }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f8866add-e45a-4386-8f78-950ad893e7c5", - "metadata": {}, - "outputs": [], - "source": [ - "var1 = \"hello\"" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "f8014d7c-3a47-451d-809a-cf29ca94af75", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'hello'" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "var1" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "3a98bc67-d26f-4bd8-bd74-83b339610c27", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "item = 101\n", - "print(type(item))" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "ed2294c5-7078-4c52-88b7-290fe1995a30", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "# Never do that! Don't assign a value of a different type to the same variable!\n", - "item = \"Code 101\"\n", - "print(type(item))" - ] - }, { "cell_type": "code", "execution_count": null, @@ -110,7 +40,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.9" } }, "nbformat": 4, diff --git a/typing/classes.py b/typing/classes.py index f4f8cc7..3019b39 100644 --- a/typing/classes.py +++ b/typing/classes.py @@ -1,109 +1,5 @@ -from __future__ import annotations -from typing import Self, Optional - # Section: Classes as Type Hints - -class Server: - def __init__( - self, - hostname: str, - ip_address: str, - os_type: str = "Linux", - ): - self.hostname: str = hostname - self.ip_address: str = ip_address - self.os_type: str = os_type - self.is_online: bool = False - - def connect(self) -> None: - print( - f"Connecting to {self.hostname} (IP address: {self.ip_address})" - ) - self.is_online = True - print(f"{self.hostname} is online.") - - def get_status(self) -> str: - return "online" if self.is_online else "offline" - - -def deploy_app_to_server( - target_server: Server, app_name: str -) -> bool: - print( - f"Deploying {app_name} to server: {target_server.hostname}" - ) - - if not target_server.is_online: - target_server.connect() - - print( - f"Deployment of {app_name} to {target_server.hostname} successful." - ) - return True - - -web_server = Server( - hostname="web01.dev.local", ip_address="10.0.1.10" -) -db_server = Server( - hostname="db01.dev.local", ip_address="10.0.2.20" -) - -deploy_app_to_server(web_server, "FrontendApp") -deploy_app_to_server(db_server, "UserDBApi") - - # Section: Hinting Methods Within a Class - -class Calculator: - def __init__(self, initial_value: int | float = 0): - self.total: int | float = initial_value - - def add(self, value: int | float) -> Self: - self.total += value - - return self - - def subtract(self, value: int | float) -> Self: - self.total -= value - - return self - - def multiply_by(self, value: int | float) -> Self: - self.total *= value - - return self - - def divide_by(self, value: int | float) -> Self: - self.total /= value - - return self - - def get_total(self) -> int | float: - return self.total - - -my_calc = Calculator(1) - -print(my_calc.add(2).subtract(1).multiply_by(10).get_total()) - # Section: Forward References (Strings) - - -class Employee: - def __init__( - self, name: str, manager: Optional[Employee] = None - ) -> None: - self.name: str = name - self.manager: Optional[Employee] = manager - self.reports: list[Employee] = [] - - def add_report(self, report: Employee) -> None: - self.reports.append(report) - - -ceo = Employee("ceo") -manager1 = Employee("Alice", ceo) -ceo.add_report(manager1) diff --git a/typing/common_types.py b/typing/common_types.py index c8929e5..e3fdc84 100644 --- a/typing/common_types.py +++ b/typing/common_types.py @@ -1,89 +1,13 @@ -from typing import Optional, Any - # Section: Typing Lists -hostnames: list[str] = ["web01.example.com", "db01.example.com"] -open_ports: list[int] = [80, 443, 22] - - -def process_hostnames(hosts: list[str]) -> None: - for host in hosts: - print(f"Processing host: {host.upper()}") - - -process_hostnames(hostnames) -# process_hostnames(open_ports) # Uncommenting will lead to type error - # Section: Typing Dictionaries -server_config: dict[str, str] = { - "hostname": "app01.prod", - "ip_address": "10.0.5.20", - "os_type": "Linux", -} - -user_roles: dict[str, list[str]] = { - "user-123": ["admin", "editor"], - "user-456": ["dev", "viewer"], -} - # Section: Typing Tuples -server_status: tuple[str, int, bool] = ( - "api.example.com", - 443, - True, -) - -ip_parts: tuple[int, ...] = (192, 168, 1, 100) - # Section: Typing Sets -admin_users: set[str] = {"alice", "bob", "charlie"} - - -def is_admin(username: str, admins: set[str]) -> bool: - return username in admins - - # Section: Union[X, Y, ...] for Multiple Possible Types -identifier: str | int = "abcde-1234" -identifier = 1234 - - -def process_mixed_data(data: list[int | str]) -> None: - for item in data: - if isinstance(item, str): - print(f"Processing string: {item.upper()}") - else: - print(f"Processing int: {item * 2}") - - # Section: Optional[X] for Values That Can Be None - -def find_user(user_id: str) -> Optional[dict[str, str]]: - if user_id == "123": - return { - "id": "123", - "name": "Admin user", - "email": "admin@example.com", - } - - return None - - -found_user = find_user("123") - -if found_user: - print(f"Found user: {found_user["name"]}") - - # Section: Any for Unrestricted Types -def print_anything(item: Any) -> None: - print(f"Item: {item}, type: {type(item)}") - - -print_anything(1) -print_anything("hello") diff --git a/typing/generators_decorators.py b/typing/generators_decorators.py index 9ec0a3d..3a67485 100644 --- a/typing/generators_decorators.py +++ b/typing/generators_decorators.py @@ -1,106 +1,7 @@ -from typing import ( - Callable, - Any, - TypeVar, - ParamSpec, - Generator, - Iterable, -) -import functools - # Section: Typing Decorators (simple_logging_decorator) - -def simple_logging_decorator( - func: Callable[..., Any], -) -> Callable[..., Any]: - @functools.wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> Any: - print(f"LOG: Calling {func.__name__}") - result = func(*args, **kwargs) - print(f"LOG: {func.__name__} returned {result}") - - return result - - return wrapper - - -@simple_logging_decorator -def add(x: int, y: int) -> int: - return x + y - - -result_add = add(3, 5) - # Section: Typing Decorators (better_logging_decorator with TypeVar) -P = ParamSpec("P") -R = TypeVar("R") - - -def better_logging_decorator( - func: Callable[P, R], -) -> Callable[P, R]: - @functools.wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any: - print(f"LOG: Calling {func.__name__}") - result = func(*args, **kwargs) - print(f"LOG: {func.__name__} returned {result}") - - return result - - return wrapper - - -@better_logging_decorator -def subtract(x: int, y: int) -> int: - return x - y - - -result_subtract = subtract(3, 5) - - # Section: Typing Generators - -def count_up_to(limit: int) -> Generator[int, None, str]: - for i in range(limit): - yield i - - return "Counting complete!" - - -def accumulate_and_send() -> ( - Generator[float, float | None, None] -): - total = 0.0 - - try: - while True: - sent = yield total - - if sent: - total += sent - except GeneratorExit: - pass - - -test_accumulate = accumulate_and_send() -next(test_accumulate) -print(test_accumulate.send(1.0)) -print(next(test_accumulate)) -print(test_accumulate.send(2.0)) -print(test_accumulate.send(3.0)) -print(next(test_accumulate)) - -# Section: Iterable & Iterator - - -def process_items(items: Iterable[str]) -> list[str]: - return [item.upper() for item in items] - - -print(process_items(["a", "b"])) -print(process_items(("a", "b"))) -print(process_items({"a", "b"})) -print(process_items({"a": "b", "hello": "world"})) +# Section: Iterable & Iterator \ No newline at end of file diff --git a/typing/generics.py b/typing/generics.py index b6b1918..2f60e13 100644 --- a/typing/generics.py +++ b/typing/generics.py @@ -1,111 +1,7 @@ -from typing import Optional, TypeVar, Generic - # Section: Defining a generic function to get the first item of a list -T = TypeVar("T") - - -def get_first_item( - input_list: list[T], -) -> Optional[T]: - if input_list: - return input_list[0] - - return None - - -first_number = get_first_item([1, 2, 3]) -first_str = get_first_item(["abc", "def"]) -first_mixed_list = get_first_item(["abc", "def", 1, 2, 3]) - # Section: Constrained TypeVar for numeric addition -NumberType = TypeVar("NumberType", int, float) - - -def add_generic_numbers( - x: NumberType, y: NumberType -) -> NumberType: - return x + y - - -sum_int = add_generic_numbers(3, 5.0) - # Section: Bounded TypeVar with deployed filter for DevOps resources - -class CloudResource: - def __init__(self, name: str, cpu_usage: float) -> None: - self.name = name - self.cpu_usage = cpu_usage - self.deployed: bool = False - - def deploy(self) -> None: - print(f"Deploying {self.name}") - self.deployed = True - - -class VirtualMachine(CloudResource): - def reboot(self) -> None: - print(f"Rebooting VM {self.name}") - - -class DockerContainer(CloudResource): - def restart(self) -> None: - print(f"Restarting container {self.name}") - - -ResourceType = TypeVar("ResourceType", bound=CloudResource) - - -def filter_deployed( - resources: list[ResourceType], -) -> list[ResourceType]: - return [ - resource for resource in resources if resource.deployed - ] - - -vm1 = VirtualMachine("vm-01", cpu_usage=65.0) -vm2 = VirtualMachine("vm-02", cpu_usage=45.0) -container1 = DockerContainer("api-service", cpu_usage=85.0) -container2 = DockerContainer("worker", cpu_usage=55.0) - -vm1.deploy() -container1.deploy() - -all_resources = [vm1, vm2, container1, container2] -deployed_resources = filter_deployed(all_resources) - # Section: Generic class SimpleStack - -G = TypeVar("G") - - -class SimpleStack(Generic[G]): - def __init__(self) -> None: - self._items: list[G] = [] - - def push(self, item: G) -> None: - self._items.append(item) - - def pop(self) -> G: - if self.is_empty(): - raise IndexError("Stack is empty!") - return self._items.pop() - - def peek(self) -> Optional[G]: - if self.is_empty(): - return None - - return self._items[-1] - - def is_empty(self) -> bool: - return not self._items - - -str_stack = SimpleStack[str]() -str_stack.push("str") - -int_stack = SimpleStack[int]() -int_stack.push(12) diff --git a/typing/typed_dicts.py b/typing/typed_dicts.py index 4a30af4..e69de29 100644 --- a/typing/typed_dicts.py +++ b/typing/typed_dicts.py @@ -1,18 +0,0 @@ -from typing import TypedDict, NotRequired - - -class User(TypedDict): - id: int - name: str - email: str - phone: NotRequired[str] - - -user: User = { - "id": 123, - "name": "Alice", - "email": "alice@example.com", - "phone": "+123456789", -} - -print(f"User data: {user.get("email")}") diff --git a/typing/typing_basics.py b/typing/typing_basics.py index a2a8f59..4b1e142 100644 --- a/typing/typing_basics.py +++ b/typing/typing_basics.py @@ -1,27 +1,7 @@ # Section: Basic Type Hint Syntax - Variable Annotations -config_path: str = "/etc/app.conf" -retry_count: int = 3 -is_enabled: bool = bool(1) -servers: list[str] = ["web01", "web02"] -settings: dict[str, int | str] = {"port": 8080, "user": "admin"} - # Section: Basic Type Hint Syntax - Function Argument and Return Type Annotations -def get_server_status(hostname: str, port: int) -> str: - print(f"Checking {hostname}:{port}") - if port == 80: - return "Online" - else: - return "Unknown" - # Section: Python Remains Dynamically Typed -def process_id(user_id: int) -> None: - print( - f"Processing user ID: {user_id} (type: {type(user_id)})" - ) - # Demonstration of dynamic typing -process_id(1234) -# process_id("user-1234") # Uncommenting will lead to a static type checking error.