From 74762732e59a1d4e06ab73d8630c94ec0e69356d Mon Sep 17 00:00:00 2001 From: Tejas Dixit Date: Wed, 29 Jul 2026 02:44:38 +0530 Subject: [PATCH] refactor(repo): reorganize learning modules --- .pre-commit-config.yaml | 5 - .../01-decorators}/decorator.py | 0 .../02-context-managers}/context_manager.py | 0 .../contextlib_example.py | 0 .../03-dataclasses}/dataclass_basics.py | 0 .../04-timer-decorator}/timer.py | 0 .../01-inventory}/inventory.py | 0 .../02-pricing}/pricing.py | 0 .../03-employee-salary}/assignment.md | 0 .../03-employee-salary/employee_salary.py | 0 .../04-pydantic}/day11_product_manager.py | 0 .../04-pydantic}/day12_product_validation.py | 0 .../04-pydantic}/day12_pydantic_basics.py | 0 .../day12_pydantic_user_model_exercise.py | 0 .../04-pydantic}/day13_email_validation.py | 0 .../04-pydantic}/day14_computed_field.py | 0 .../04-pydantic}/day18-field-alias.py | 0 .../04-pydantic/day_23_str_enum}/__init__.py | 0 .../day_23_str_enum/str_enum_employee.py | 0 .../day_24_decimal/product_invoice.py | 0 .../day_25_date_datetime/README.md | 0 .../day_25_date_datetime/employee.py | 6 +- .../day_26_model_dump_serialization/README.md | 0 .../employee.py | 6 +- .../04-pydantic}/employee_field_serializer.py | 0 .../04-pydantic}/nested_models.py | 0 .../04-pydantic}/pydantic-extra.py | 6 +- .../01-employee-management-system}/company.py | 0 pyproject.toml | 15 +- python_fundamentals/exercises/__init__.py | 0 .../employee_salary_module/__init__.py | 0 .../employee_salary_module/test_employee.py | 137 ----- .../notes/day01-functions-and-validation.md | 52 -- .../notes/day02-args-and-kwargs.md | 61 --- python_fundamentals/notes/day03-decorators.md | 66 --- .../notes/day04-context-managers.md | 24 - python_fundamentals/notes/day05-contextlib.md | 52 -- python_fundamentals/notes/day06-type-hints.md | 157 ------ .../notes/day07-dataclasses.md | 74 --- .../notes/day08-inventory-manager.md | 61 --- python_fundamentals/notes/day09-pytest.md | 212 -------- python_fundamentals/notes/day10-typeddict.md | 324 ------------ .../notes/day11-oop-fundamentals.md | 335 ------------ .../notes/day12-pydantic-basics.md | 476 ------------------ .../day13-email-str-and-field-validator.md | 291 ----------- python_fundamentals/notes/day23-str-enum.md | 239 --------- .../notes/day25-date-datetime.md | 220 -------- python_fundamentals/notes/day26-model-dump.md | 183 ------- .../__init__.py | 0 .../tests/test_day13_email_validation.py | 13 - python_fundamentals/tests/test_inventory.py | 14 - python_fundamentals/tests/test_pricing.py | 11 - 52 files changed, 22 insertions(+), 3018 deletions(-) rename {python_fundamentals/exercises => modules/01-python-fundamentals/01-decorators}/decorator.py (100%) rename {python_fundamentals/exercises => modules/01-python-fundamentals/02-context-managers}/context_manager.py (100%) rename {python_fundamentals/exercises => modules/01-python-fundamentals/02-context-managers}/contextlib_example.py (100%) rename {python_fundamentals/exercises => modules/01-python-fundamentals/03-dataclasses}/dataclass_basics.py (100%) rename {python_fundamentals/exercises => modules/01-python-fundamentals/04-timer-decorator}/timer.py (100%) rename {python_fundamentals/exercises => modules/02-object-oriented-programming/01-inventory}/inventory.py (100%) rename {python_fundamentals/exercises => modules/02-object-oriented-programming/02-pricing}/pricing.py (100%) rename {python_fundamentals/exercises/employee_salary_module => modules/02-object-oriented-programming/03-employee-salary}/assignment.md (100%) rename python_fundamentals/exercises/employee_salary_module/employee.py => modules/02-object-oriented-programming/03-employee-salary/employee_salary.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day11_product_manager.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day12_product_validation.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day12_pydantic_basics.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day12_pydantic_user_model_exercise.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day13_email_validation.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day14_computed_field.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day18-field-alias.py (100%) rename {python_fundamentals => modules/04-pydantic/day_23_str_enum}/__init__.py (100%) rename python_fundamentals/exercises/day_23_str_enum/employee.py => modules/04-pydantic/day_23_str_enum/str_enum_employee.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day_24_decimal/product_invoice.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day_25_date_datetime/README.md (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day_25_date_datetime/employee.py (87%) rename {python_fundamentals/exercises => modules/04-pydantic}/day_26_model_dump_serialization/README.md (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/day_26_model_dump_serialization/employee.py (91%) rename {python_fundamentals/exercises => modules/04-pydantic}/employee_field_serializer.py (100%) rename {python_fundamentals/exercises/nested_models => modules/04-pydantic}/nested_models.py (100%) rename {python_fundamentals/exercises => modules/04-pydantic}/pydantic-extra.py (92%) rename {python_fundamentals/projects/capstone_01_employee_management_system => projects/01-employee-management-system}/company.py (100%) delete mode 100644 python_fundamentals/exercises/__init__.py delete mode 100644 python_fundamentals/exercises/employee_salary_module/__init__.py delete mode 100644 python_fundamentals/exercises/employee_salary_module/test_employee.py delete mode 100644 python_fundamentals/notes/day01-functions-and-validation.md delete mode 100644 python_fundamentals/notes/day02-args-and-kwargs.md delete mode 100644 python_fundamentals/notes/day03-decorators.md delete mode 100644 python_fundamentals/notes/day04-context-managers.md delete mode 100644 python_fundamentals/notes/day05-contextlib.md delete mode 100644 python_fundamentals/notes/day06-type-hints.md delete mode 100644 python_fundamentals/notes/day07-dataclasses.md delete mode 100644 python_fundamentals/notes/day08-inventory-manager.md delete mode 100644 python_fundamentals/notes/day09-pytest.md delete mode 100644 python_fundamentals/notes/day10-typeddict.md delete mode 100644 python_fundamentals/notes/day11-oop-fundamentals.md delete mode 100644 python_fundamentals/notes/day12-pydantic-basics.md delete mode 100644 python_fundamentals/notes/day13-email-str-and-field-validator.md delete mode 100644 python_fundamentals/notes/day23-str-enum.md delete mode 100644 python_fundamentals/notes/day25-date-datetime.md delete mode 100644 python_fundamentals/notes/day26-model-dump.md delete mode 100644 python_fundamentals/projects/capstone_01_employee_management_system/__init__.py delete mode 100644 python_fundamentals/tests/test_day13_email_validation.py delete mode 100644 python_fundamentals/tests/test_inventory.py delete mode 100644 python_fundamentals/tests/test_pricing.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ae38100..cabf1ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,8 +6,3 @@ repos: args: [--fix] - id: ruff-format - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.2 - hooks: - - id: mypy diff --git a/python_fundamentals/exercises/decorator.py b/modules/01-python-fundamentals/01-decorators/decorator.py similarity index 100% rename from python_fundamentals/exercises/decorator.py rename to modules/01-python-fundamentals/01-decorators/decorator.py diff --git a/python_fundamentals/exercises/context_manager.py b/modules/01-python-fundamentals/02-context-managers/context_manager.py similarity index 100% rename from python_fundamentals/exercises/context_manager.py rename to modules/01-python-fundamentals/02-context-managers/context_manager.py diff --git a/python_fundamentals/exercises/contextlib_example.py b/modules/01-python-fundamentals/02-context-managers/contextlib_example.py similarity index 100% rename from python_fundamentals/exercises/contextlib_example.py rename to modules/01-python-fundamentals/02-context-managers/contextlib_example.py diff --git a/python_fundamentals/exercises/dataclass_basics.py b/modules/01-python-fundamentals/03-dataclasses/dataclass_basics.py similarity index 100% rename from python_fundamentals/exercises/dataclass_basics.py rename to modules/01-python-fundamentals/03-dataclasses/dataclass_basics.py diff --git a/python_fundamentals/exercises/timer.py b/modules/01-python-fundamentals/04-timer-decorator/timer.py similarity index 100% rename from python_fundamentals/exercises/timer.py rename to modules/01-python-fundamentals/04-timer-decorator/timer.py diff --git a/python_fundamentals/exercises/inventory.py b/modules/02-object-oriented-programming/01-inventory/inventory.py similarity index 100% rename from python_fundamentals/exercises/inventory.py rename to modules/02-object-oriented-programming/01-inventory/inventory.py diff --git a/python_fundamentals/exercises/pricing.py b/modules/02-object-oriented-programming/02-pricing/pricing.py similarity index 100% rename from python_fundamentals/exercises/pricing.py rename to modules/02-object-oriented-programming/02-pricing/pricing.py diff --git a/python_fundamentals/exercises/employee_salary_module/assignment.md b/modules/02-object-oriented-programming/03-employee-salary/assignment.md similarity index 100% rename from python_fundamentals/exercises/employee_salary_module/assignment.md rename to modules/02-object-oriented-programming/03-employee-salary/assignment.md diff --git a/python_fundamentals/exercises/employee_salary_module/employee.py b/modules/02-object-oriented-programming/03-employee-salary/employee_salary.py similarity index 100% rename from python_fundamentals/exercises/employee_salary_module/employee.py rename to modules/02-object-oriented-programming/03-employee-salary/employee_salary.py diff --git a/python_fundamentals/exercises/day11_product_manager.py b/modules/04-pydantic/day11_product_manager.py similarity index 100% rename from python_fundamentals/exercises/day11_product_manager.py rename to modules/04-pydantic/day11_product_manager.py diff --git a/python_fundamentals/exercises/day12_product_validation.py b/modules/04-pydantic/day12_product_validation.py similarity index 100% rename from python_fundamentals/exercises/day12_product_validation.py rename to modules/04-pydantic/day12_product_validation.py diff --git a/python_fundamentals/exercises/day12_pydantic_basics.py b/modules/04-pydantic/day12_pydantic_basics.py similarity index 100% rename from python_fundamentals/exercises/day12_pydantic_basics.py rename to modules/04-pydantic/day12_pydantic_basics.py diff --git a/python_fundamentals/exercises/day12_pydantic_user_model_exercise.py b/modules/04-pydantic/day12_pydantic_user_model_exercise.py similarity index 100% rename from python_fundamentals/exercises/day12_pydantic_user_model_exercise.py rename to modules/04-pydantic/day12_pydantic_user_model_exercise.py diff --git a/python_fundamentals/exercises/day13_email_validation.py b/modules/04-pydantic/day13_email_validation.py similarity index 100% rename from python_fundamentals/exercises/day13_email_validation.py rename to modules/04-pydantic/day13_email_validation.py diff --git a/python_fundamentals/exercises/day14_computed_field.py b/modules/04-pydantic/day14_computed_field.py similarity index 100% rename from python_fundamentals/exercises/day14_computed_field.py rename to modules/04-pydantic/day14_computed_field.py diff --git a/python_fundamentals/exercises/day18-field-alias.py b/modules/04-pydantic/day18-field-alias.py similarity index 100% rename from python_fundamentals/exercises/day18-field-alias.py rename to modules/04-pydantic/day18-field-alias.py diff --git a/python_fundamentals/__init__.py b/modules/04-pydantic/day_23_str_enum/__init__.py similarity index 100% rename from python_fundamentals/__init__.py rename to modules/04-pydantic/day_23_str_enum/__init__.py diff --git a/python_fundamentals/exercises/day_23_str_enum/employee.py b/modules/04-pydantic/day_23_str_enum/str_enum_employee.py similarity index 100% rename from python_fundamentals/exercises/day_23_str_enum/employee.py rename to modules/04-pydantic/day_23_str_enum/str_enum_employee.py diff --git a/python_fundamentals/exercises/day_24_decimal/product_invoice.py b/modules/04-pydantic/day_24_decimal/product_invoice.py similarity index 100% rename from python_fundamentals/exercises/day_24_decimal/product_invoice.py rename to modules/04-pydantic/day_24_decimal/product_invoice.py diff --git a/python_fundamentals/exercises/day_25_date_datetime/README.md b/modules/04-pydantic/day_25_date_datetime/README.md similarity index 100% rename from python_fundamentals/exercises/day_25_date_datetime/README.md rename to modules/04-pydantic/day_25_date_datetime/README.md diff --git a/python_fundamentals/exercises/day_25_date_datetime/employee.py b/modules/04-pydantic/day_25_date_datetime/employee.py similarity index 87% rename from python_fundamentals/exercises/day_25_date_datetime/employee.py rename to modules/04-pydantic/day_25_date_datetime/employee.py index 7b09b53..d9f2105 100644 --- a/python_fundamentals/exercises/day_25_date_datetime/employee.py +++ b/modules/04-pydantic/day_25_date_datetime/employee.py @@ -23,7 +23,11 @@ class Employee(BaseModel): # Use model_validate for string inputs to avoid static type errors and to # keep intentional parsing tests. vignesh_gawali = Employee.model_validate( - {"emp_name": "Vignesh Gawali", "joining_date": "2024-12-12", "created_at": "2024-12-12 12:12"} + { + "emp_name": "Vignesh Gawali", + "joining_date": "2024-12-12", + "created_at": "2024-12-12 12:12", + } ) print(f"Python Representaion of Employee Object: {vignesh_gawali.model_dump()}") diff --git a/python_fundamentals/exercises/day_26_model_dump_serialization/README.md b/modules/04-pydantic/day_26_model_dump_serialization/README.md similarity index 100% rename from python_fundamentals/exercises/day_26_model_dump_serialization/README.md rename to modules/04-pydantic/day_26_model_dump_serialization/README.md diff --git a/python_fundamentals/exercises/day_26_model_dump_serialization/employee.py b/modules/04-pydantic/day_26_model_dump_serialization/employee.py similarity index 91% rename from python_fundamentals/exercises/day_26_model_dump_serialization/employee.py rename to modules/04-pydantic/day_26_model_dump_serialization/employee.py index 2c25a51..362d762 100644 --- a/python_fundamentals/exercises/day_26_model_dump_serialization/employee.py +++ b/modules/04-pydantic/day_26_model_dump_serialization/employee.py @@ -44,7 +44,11 @@ def format_emp() -> None: # Use model_validate to pass string inputs (so static checkers don't flag arg-type). another_emp = Employee.model_validate( - {"emp_name": "Tejas Dixit", "joining_date": "2024-12-29", "created_at": "2024-12-28 12:12"} + { + "emp_name": "Tejas Dixit", + "joining_date": "2024-12-29", + "created_at": "2024-12-28 12:12", + } ) print( diff --git a/python_fundamentals/exercises/employee_field_serializer.py b/modules/04-pydantic/employee_field_serializer.py similarity index 100% rename from python_fundamentals/exercises/employee_field_serializer.py rename to modules/04-pydantic/employee_field_serializer.py diff --git a/python_fundamentals/exercises/nested_models/nested_models.py b/modules/04-pydantic/nested_models.py similarity index 100% rename from python_fundamentals/exercises/nested_models/nested_models.py rename to modules/04-pydantic/nested_models.py diff --git a/python_fundamentals/exercises/pydantic-extra.py b/modules/04-pydantic/pydantic-extra.py similarity index 92% rename from python_fundamentals/exercises/pydantic-extra.py rename to modules/04-pydantic/pydantic-extra.py index a7c5551..d52f4dd 100644 --- a/python_fundamentals/exercises/pydantic-extra.py +++ b/modules/04-pydantic/pydantic-extra.py @@ -42,7 +42,11 @@ class EventLog(BaseModel): # Use model_validate when passing extra fields via a mapping so static type # checkers don't complain about unexpected keyword arguments. event = EventLog.model_validate( - {"event_id": 0, "event_type": "Error", "event_description": "ValidationError"} + { + "event_id": 0, + "event_type": "Error", + "event_description": "ValidationError", + } ) print(event.model_dump()) diff --git a/python_fundamentals/projects/capstone_01_employee_management_system/company.py b/projects/01-employee-management-system/company.py similarity index 100% rename from python_fundamentals/projects/capstone_01_employee_management_system/company.py rename to projects/01-employee-management-system/company.py diff --git a/pyproject.toml b/pyproject.toml index b7d9ae2..d20b664 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,12 +25,8 @@ dev = [ [tool.pytest.ini_options] addopts = "--import-mode=importlib -v" -testpaths = [ - "python_fundamentals/tests", -] -python_files = [ - "test_*.py", -] +testpaths = ["tests"] +python_files = ["test_*.py"] # ----------------------------------------------------------------------------- # Ruff @@ -63,6 +59,9 @@ target-version = ["py312"] [tool.mypy] python_version = "3.12" +pretty = true +show_error_codes = true + warn_return_any = true warn_unused_configs = true warn_redundant_casts = true @@ -73,5 +72,5 @@ disallow_incomplete_defs = true check_untyped_defs = true no_implicit_optional = true -pretty = true -show_error_codes = true + +explicit_package_bases = true diff --git a/python_fundamentals/exercises/__init__.py b/python_fundamentals/exercises/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python_fundamentals/exercises/employee_salary_module/__init__.py b/python_fundamentals/exercises/employee_salary_module/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python_fundamentals/exercises/employee_salary_module/test_employee.py b/python_fundamentals/exercises/employee_salary_module/test_employee.py deleted file mode 100644 index 4904d6b..0000000 --- a/python_fundamentals/exercises/employee_salary_module/test_employee.py +++ /dev/null @@ -1,137 +0,0 @@ -import pytest -from pydantic import ValidationError - -from .employee import Employee - - -def test_create_valid_employee() -> None: - employee = Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - is_active=True, - ) - - assert employee.employee_id == "EMP-1001" - assert employee.name == "Tejas Dixit" - assert employee.department == "HR" - assert employee.salary == 25000 - assert employee.age == 25 - assert employee.is_active is True - - -def test_invalid_employee_id() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - ) - - -def test_invalid_name() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="EMP-1001", - name="Tejas123", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - ) - - -def test_invalid_email() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="invalid-email", - department="HR", - salary=25000, - age=25, - ) - - -def test_invalid_department() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="SALES", - salary=25000, - age=25, - ) - - -def test_invalid_salary() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=-100, - age=25, - ) - - -def test_invalid_age() -> None: - with pytest.raises(ValidationError): - Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=15, - ) - - -def test_annual_salary() -> None: - employee = Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - ) - - assert employee.annual_salary() == 300000 - - -def test_apply_raise() -> None: - employee = Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - ) - - employee.apply_raise(10) - - assert employee.salary == 27500 - - -def test_apply_raise_invalid_percentage() -> None: - employee = Employee( - employee_id="EMP-1001", - name="Tejas Dixit", - email="tejasdixit17@zohomail.in", - department="HR", - salary=25000, - age=25, - ) - - with pytest.raises(ValueError): - employee.apply_raise(0) diff --git a/python_fundamentals/notes/day01-functions-and-validation.md b/python_fundamentals/notes/day01-functions-and-validation.md deleted file mode 100644 index 81820a0..0000000 --- a/python_fundamentals/notes/day01-functions-and-validation.md +++ /dev/null @@ -1,52 +0,0 @@ - -# Day 01 - Functions and Validation - -## Problem - -Functions help organize reusable business logic. - -## Key Concepts - -### Function - -```python -def greet(name: str) -> str: - return f"Hello {name}" -``` - -### Return vs Print - -```python -return value -``` - -is preferred over: - -```python -print(value) -``` - -because returned values can be reused. - -### Validation - -Validate inputs before processing. - -```python -if price <= 0: - raise ValueError("Price must be greater than 0") -``` - -### Exceptions - -```python -TypeError -ValueError -``` - -## Key Learning - -* Functions should do one thing. -* Return values instead of printing. -* Validate inputs early. -* Raise meaningful exceptions. diff --git a/python_fundamentals/notes/day02-args-and-kwargs.md b/python_fundamentals/notes/day02-args-and-kwargs.md deleted file mode 100644 index ed9c50f..0000000 --- a/python_fundamentals/notes/day02-args-and-kwargs.md +++ /dev/null @@ -1,61 +0,0 @@ - -# Day 02 - *args and **kwargs - -## Problem - -Sometimes the number of arguments is unknown. - -## *args - -```python -def add(*args): - print(args) -``` - -Type: - -```python -tuple -``` - -Example: - -```python -add(1, 2, 3) -``` - -Output: - -```python -(1, 2, 3) -``` - -## **kwargs - -```python -def display(**kwargs): - print(kwargs) -``` - -Type: - -```python -dict -``` - -Example: - -```python -display(name="Madhav") -``` - -Output: - -```python -{"name": "Madhav"} -``` - -## Key Learning - -* `*args` collects positional arguments into a tuple. -* `**kwargs` collects keyword arguments into a dictionary. diff --git a/python_fundamentals/notes/day03-decorators.md b/python_fundamentals/notes/day03-decorators.md deleted file mode 100644 index 5bb9440..0000000 --- a/python_fundamentals/notes/day03-decorators.md +++ /dev/null @@ -1,66 +0,0 @@ -# Day 03 - Decorators - -## What is a decorator? - -A decorator modifies or extends the behavior of a function without changing the original function code. - -## Key Concepts - -### Decorator Syntax - -```python -@timer -def calculate_sum(): - ... -``` - -Equivalent to: - -```python -calculate_sum = timer(calculate_sum) -``` - -### Why use *args and **kwargs? - -The decorator does not know the signature of the function it decorates. - -```python -def wrapper(*args, **kwargs): -``` - -allows forwarding all positional and keyword arguments. - -### Why return result? - -```python -result = func(*args, **kwargs) -return result -``` - -This preserves the original function's return value. - -### functools.wraps - -Without wraps: - -```python -greet.__name__ -# wrapper -``` - -With wraps: - -```python -greet.__name__ -# greet -``` - -### Timer Decorator Example - -Measures execution time using: - -```python -time.perf_counter() -``` - -Useful for profiling slow code. diff --git a/python_fundamentals/notes/day04-context-managers.md b/python_fundamentals/notes/day04-context-managers.md deleted file mode 100644 index 737d1e5..0000000 --- a/python_fundamentals/notes/day04-context-managers.md +++ /dev/null @@ -1,24 +0,0 @@ -# context managers - -Context Manager are used to -cleanup the resource - -``` - -Acquire Resource -↓ -Use Resource -↓ -Release Resource - -``` - - -### Context Manager Summary - -*A context manager can:* - -1. Setup resources `(__enter__)` -2. Cleanup resources `(__exit__)` -3. Inspect exceptions `(exc_type, exc_value)` -4. Optionally suppress exceptions `(return True)` diff --git a/python_fundamentals/notes/day05-contextlib.md b/python_fundamentals/notes/day05-contextlib.md deleted file mode 100644 index 9a6e484..0000000 --- a/python_fundamentals/notes/day05-contextlib.md +++ /dev/null @@ -1,52 +0,0 @@ - -# Day 05 - contextlib.contextmanager - -## Problem - -Writing custom classes for every context manager can be verbose. - -## Solution - -```python -from contextlib import contextmanager -``` - -## Example - -```python -@contextmanager -def get_session(): - print("Opening") - - try: - yield "session" - finally: - print("Closing") -``` - -Usage: - -```python -with get_session() as session: - print(session) -``` - -## Yield Behavior - -Before `yield`: - -```text -__enter__() -``` - -After `yield`: - -```text -__exit__() -``` - -## Key Learning - -* `yield` splits enter and exit behavior. -* `finally` guarantees cleanup. -* Useful for database sessions and file handling. diff --git a/python_fundamentals/notes/day06-type-hints.md b/python_fundamentals/notes/day06-type-hints.md deleted file mode 100644 index 5f2b3f1..0000000 --- a/python_fundamentals/notes/day06-type-hints.md +++ /dev/null @@ -1,157 +0,0 @@ - -# Day 06 - Type Hints and Generics - -## Problem Statement - -As Python applications grow, it becomes difficult to understand what types of data functions expect and return. Type hints improve readability, tooling support, and early bug detection without changing runtime behavior. - -## Key Learnings - -### Basic Type Hints - -```python -def greet(name: str) -> str: - return f"Hello {name}" -``` - -* `name: str` indicates the parameter should be a string. -* `-> str` indicates the function returns a string. - -### Type Hints Are Not Runtime Validation - -```python -def add(a: int, b: int) -> int: - return a + b - -add("10", "20") -``` - -Python executes this code and returns: - -```python -"1020" -``` - -Type hints are mainly used by: - -* MyPy -* BasedPyright -* IDEs -* Linters - -### Generic Types - -#### List of Strings - -```python -list[str] -``` - -Example: - -```python -["Madhav", "Tejas", "Vignesh"] -``` - -#### Dictionary - -```python -dict[str, int] -``` - -Example: - -```python -{ - "Maths": 100, - "Python": 99, -} -``` - -### Nested Types - -#### List of Dictionaries - -```python -list[dict[str, str]] -``` - -Example: - -```python -[ - {"name": "Madhav", "city": "Pune"}, - {"name": "Tejas", "city": "Nashik"}, -] -``` - -#### Dictionary of Lists - -```python -dict[str, list[int]] -``` - -Example: - -```python -{ - "test_series": [89, 99, 100, 99, 0, 60] -} -``` - -#### Complex Nested Type - -```python -list[dict[str, list[int]]] -``` - -Example: - -```python -[ - {"Programming": [100, 99, 100, 78]}, - {"Database": [100, 88, 99, 100]}, -] -``` - -### Return Type None - -```python -def process_names(names: list[str]) -> None: - print(names) -``` - -`-> None` means the function does not return a meaningful value. - -Example: - -```python -result = process_names(["Madhav"]) -print(result) -``` - -Output: - -```python -None -``` - -## Why Type Hints Matter - -* Improve code readability -* Better IDE autocomplete -* Static analysis with MyPy -* Earlier bug detection -* Self-documenting code -* Commonly used in FastAPI, Pydantic, and SQLAlchemy - -## Summary - -Today I learned: - -* Basic type hints -* Return types -* Generic types -* Nested generic types -* Difference between type hints and runtime validation -* How to read complex type annotations diff --git a/python_fundamentals/notes/day07-dataclasses.md b/python_fundamentals/notes/day07-dataclasses.md deleted file mode 100644 index 62874e5..0000000 --- a/python_fundamentals/notes/day07-dataclasses.md +++ /dev/null @@ -1,74 +0,0 @@ - -# Day 07 - Dataclasses - -## Problem - -Dictionaries are flexible but prone to typo-related bugs. - -## Example - -```python -user = { - "name": "Madhav", - "city": "Nagpur", -} -``` - -## Dataclass - -```python -from dataclasses import dataclass - -@dataclass -class User: - name: str - city: str -``` - -## Benefits - -Automatically generates: - -* `__init__` -* `__repr__` -* `__eq__` - -## Mutable Objects - -```python -user.city = "Pune" -``` - -Allowed by default. - -## Frozen Dataclass - -```python -@dataclass(frozen=True) -class User: - ... -``` - -Creates immutable objects. - -## Convert To Dictionary - -```python -from dataclasses import asdict - -data = asdict(user) -``` - -## Key Learning - -Objects use: - -```python -user.name -``` - -Dictionaries use: - -```python -data["name"] -``` diff --git a/python_fundamentals/notes/day08-inventory-manager.md b/python_fundamentals/notes/day08-inventory-manager.md deleted file mode 100644 index e018222..0000000 --- a/python_fundamentals/notes/day08-inventory-manager.md +++ /dev/null @@ -1,61 +0,0 @@ - -# Day 08 - Inventory Manager Mini Project - -## Objective - -Combine previously learned Python concepts into a small backend-style application. - -## Concepts Used - -* Dataclasses -* Type Hints -* Validation -* Context Managers -* Exception Handling -* asdict() - -## Features - -### Product Model - -```python -@dataclass -class Product: - name: str - price: float - quantity: int -``` - -### Product Validation - -```python -validate_product(product) -``` - -### Inventory Calculation - -```python -calculate_inventory_value(products) -``` - -### Context Manager - -```python -with inventory_session(): -``` - -### Export Products - -```python -export_products(products) -``` - -Uses: - -```python -asdict() -``` - -## Key Learning - -This project demonstrated how multiple Python concepts work together in a real application rather than isolated examples. diff --git a/python_fundamentals/notes/day09-pytest.md b/python_fundamentals/notes/day09-pytest.md deleted file mode 100644 index 55bb456..0000000 --- a/python_fundamentals/notes/day09-pytest.md +++ /dev/null @@ -1,212 +0,0 @@ - -# Day 09 - Pytest Basics - -## Problem - -Manually running code and checking output is slow and error-prone. - -Example: - -```python -result = calculate_discounted_price( - 100, - 2, - 10, -) - -print(result) -``` - -As projects grow, manually verifying behavior becomes difficult. - -## Solution - -Use pytest to automate verification of code behavior. - ---- - -## First Test - -Function: - -```python -def calculate_discounted_price( - price: float, - quantity: int, - discount_percentage: float, -) -> float: - ... -``` - -Test: - -```python -def test_discounted_price(): - result = calculate_discounted_price( - 100, - 2, - 10, - ) - - assert result == 180.0 -``` - ---- - -## assert - -`assert` verifies expectations. - -Example: - -```python -assert 10 == 10 -``` - -Passes. - -Example: - -```python -assert 10 == 20 -``` - -Fails. - -Mental model: - -```text -I expect this expression to be True. -``` - ---- - -## Test Discovery - -Pytest automatically discovers: - -```python -def test_something(): - ... -``` - -Functions must start with: - -```text -test_ -``` - -Example: - -```python -test_discounted_price -test_invalid_price -test_inventory_value -``` - ---- - -## Test Structure - -Repository: - -```text -python_fundamentals/ -├── exercises/ -│ └── pricing.py -│ -└── tests/ - └── test_pricing.py -``` - ---- - -## Running Tests - -Run all tests: - -```bash -uv run pytest -v -``` - -Verbose mode: - -```bash -uv run pytest -v -``` - ---- - -## Real-World Debugging Lesson - -Initial error: - -```text -ModuleNotFoundError: -No module named 'python_fundamentals' -``` - -However: - -```python -import python_fundamentals -``` - -worked inside Python. - -### Cause - -Pytest and Python can import modules differently. - -Python REPL automatically included the current working directory. - -Pytest was using a different import mechanism during test collection. - -### Fix - -Run pytest using: - -```bash -uv run pytest --import-mode=importlib -v -``` - -Permanent configuration: - -```toml -[tool.pytest.ini_options] -addopts = "--import-mode=importlib" -``` - -inside: - -```text -pyproject.toml -``` - ---- - -## Key Learning - -* pytest automates verification of code. -* `assert` is used to validate expectations. -* Test functions should start with `test_`. -* Pytest discovers tests automatically. -* Python imports and pytest imports can behave differently. -* Understanding packages and import paths is important for backend projects. - -## Deliverables - -Completed: - -* First pytest test -* Test discovery -* assert statements -* Package import debugging - -Next: - -* pytest.raises() -* Testing exceptions -* Inventory Manager tests -* Fixtures - """ diff --git a/python_fundamentals/notes/day10-typeddict.md b/python_fundamentals/notes/day10-typeddict.md deleted file mode 100644 index 4a86192..0000000 --- a/python_fundamentals/notes/day10-typeddict.md +++ /dev/null @@ -1,324 +0,0 @@ - -# Day 10 - TypedDict - -# Problem - -Using: - -```python -data: dict[str, object] -``` - -is weak because it does not describe the structure of the dictionary. - -Example: - -```python -user: dict[str, object] = { - "name": 123, - "city": [], -} -``` - -The type checker knows: - -* Keys are strings -* Values are objects - -But it does not know: - -* What keys are required -* What each value type should be - ---- - -# Concept - -`TypedDict` allows us to define a dictionary with a known structure. - -Example: - -```python -from typing import TypedDict - - -class User(TypedDict): - name: str - email: str -``` - -Now the type checker understands: - -```python -{ - "name": str, - "email": str, -} -``` - ---- - -# Example - -```python -from typing import NotRequired, TypedDict - - -class User(TypedDict): - name: str - email: str - city: NotRequired[str] - - -user: User = { - "name": "Tejas Dixit", - "email": "tejasdixit17@gmail.com", - "city": "Pune", -} -``` - -Access values like a normal dictionary: - -```python -user["name"] -user["email"] -user["city"] -``` - ---- - -# Required and Optional Keys - -```python -from typing import NotRequired, TypedDict - - -class User(TypedDict): - name: str - email: str - city: NotRequired[str] -``` - -Required keys: - -```text -name -email -``` - -Optional keys: - -```text -city -``` - -Valid: - -```python -user: User = { - "name": "Tejas Dixit", - "email": "tejasdixit17@gmail.com", -} -``` - -Also valid: - -```python -user: User = { - "name": "Tejas Dixit", - "email": "tejasdixit17@gmail.com", - "city": "Pune", -} -``` - ---- - -# Important Notes - -TypedDict does NOT perform runtime validation. - -Example: - -```python -from typing import TypedDict - - -class User(TypedDict): - name: str - email: str - - -user: User = { - "name": 123, - "email": [], -} - -print(user) -``` - -Python executes this successfully. - -Type checkers such as: - -* mypy -* pyright -* basedpyright - -will report errors. - -TypedDict is primarily for static type checking. - ---- - -# TypedDict vs Dataclass - -## TypedDict - -```python -from typing import TypedDict - - -class User(TypedDict): - name: str - email: str -``` - -Access: - -```python -user["name"] -``` - -Characteristics: - -* Dictionary -* JSON-friendly -* API payloads -* Static type checking - ---- - -## Dataclass - -```python -from dataclasses import dataclass - - -@dataclass -class User: - name: str - email: str -``` - -Access: - -```python -user.name -``` - -Characteristics: - -* Object -* Business logic -* Domain models -* Application state - ---- - -# Comparison - -```text -dict -> Generic dictionary -TypedDict -> Typed dictionary -dataclass -> Application object -Pydantic -> Runtime validation -``` - ---- - -# When To Use - -## Use dict - -When structure is unknown or highly dynamic. - -Example: - -```python -headers: dict[str, str] -``` - ---- - -## Use TypedDict - -When working with: - -* JSON payloads -* API responses -* Request/response structures -* Dictionary-based data - -Example: - -```python -class UserResponse(TypedDict): - id: int - name: str - email: str -``` - ---- - -## Use Dataclass - -When working with: - -* Business logic -* Domain entities -* Application objects - -Example: - -```python -@dataclass -class Product: - name: str - price: float -``` - ---- - -## Use Pydantic - -When runtime validation is required. - -Example: - -```python -class Product(BaseModel): - name: str - price: float -``` - ---- - -# Key Learning - -* TypedDict is a typed dictionary. -* TypedDict remains a normal Python dictionary at runtime. -* Access values using dictionary keys. -* TypedDict improves static type checking. -* Dataclasses are better for application objects and business logic. -* Pydantic adds runtime validation. -* A useful progression in backend development is: - -```text -dict -↓ -TypedDict -↓ -dataclass -↓ -Pydantic -``` diff --git a/python_fundamentals/notes/day11-oop-fundamentals.md b/python_fundamentals/notes/day11-oop-fundamentals.md deleted file mode 100644 index 7424a91..0000000 --- a/python_fundamentals/notes/day11-oop-fundamentals.md +++ /dev/null @@ -1,335 +0,0 @@ - -# Day 11 - OOP Fundamentals - -# Problem - -As applications grow, related data and behavior should stay together. - -Instead of: - -```python -calculate_total_price(price, quantity) -``` - -we can group data and behavior into an object. - -This is one of the primary goals of Object-Oriented Programming (OOP). - ---- - -# Core Concepts - -## Class - -A class is a blueprint used to create objects. - -Example: - -```python -class Product: - pass -``` - -`Product` is a class. - ---- - -## Object / Instance - -An object is created from a class. - -Example: - -```python -mobile = Product() -``` - -`mobile` is an object (instance) of the `Product` class. - ---- - -# Attributes - -Attributes store data inside an object. - -Example: - -```python -class Product: - - def __init__( - self, - name: str, - price: float, - ): - self.name = name - self.price = price -``` - -Attributes: - -* name -* price - -Access: - -```python -mobile.name -mobile.price -``` - ---- - -# self - -`self` represents the current object. - -Example: - -```python -self.name = name -``` - -The value passed to the constructor is stored inside the object. - ---- - -# Methods - -Methods define behavior for an object. - -Example: - -```python -class Product: - - def display(self): - print( - self.name, - self.price, - ) -``` - -Usage: - -```python -mobile.display() -``` - -Methods operate on object data. - ---- - -# Data + Behavior - -A core OOP idea: - -```text -Object -= -Data -+ -Behavior -``` - -Example: - -Data: - -```python -name -price -quantity -``` - -Behavior: - -```python -display() -total_price() -apply_discount() -``` - ---- - -# Read-Only Methods - -A read-only method calculates and returns a value without modifying the object. - -Example: - -```python -def discounted_price( - self, - percentage: float, -): - return self.price - ( - self.price * percentage / 100 - ) -``` - -Usage: - -```python -new_price = mobile.discounted_price(10) -``` - -The object's state remains unchanged. - ---- - -# State-Changing Methods - -A state-changing method modifies object attributes. - -Example: - -```python -def apply_discount( - self, - percentage: float, -): - self.price = self.price - ( - self.price * percentage / 100 - ) -``` - -Usage: - -```python -mobile.apply_discount(10) -``` - -The object's state changes. - ---- - -# Object State - -State is the current value of an object's attributes. - -Before: - -```python -{ - "name": "Moto G35", - "price": 12000, -} -``` - -After: - -```python -mobile.apply_discount(10) -``` - -State becomes: - -```python -{ - "name": "Moto G35", - "price": 10800, -} -``` - ---- - -# Class Attributes - -Class attributes belong to the class itself and are shared by all objects. - -Example: - -```python -class Product: - tax_rate = 0.18 -``` - -Usage: - -```python -mobile.tax_rate -laptop.tax_rate -``` - -Both objects access the same value. - ---- - -# Instance Attributes vs Class Attributes - -Instance Attributes: - -```python -self.name -self.price -``` - -Stored separately for each object. - -Example: - -```text -mobile.price = 12000 -laptop.price = 50000 -``` - ---- - -Class Attributes: - -```python -tax_rate = 0.18 -``` - -Shared by all objects. - -Example: - -```text -mobile.tax_rate = 0.18 -laptop.tax_rate = 0.18 -``` - ---- - -# Why It Matters - -Backend applications are full of objects: - -```python -User -Product -Order -Invoice -DatabaseSession -``` - -These objects contain: - -* Data -* Business Rules -* State Changes - -Examples: - -```python -order.mark_paid() -user.activate() -product.apply_discount() -``` - ---- - -# Key Learning - -* Class = Blueprint -* Object = Instance of a class -* Attributes store data -* Methods define behavior -* self refers to the current object -* Objects combine data and behavior -* Read-only methods calculate values -* State-changing methods modify objects -* Class attributes are shared -* Instance attributes belong to each object diff --git a/python_fundamentals/notes/day12-pydantic-basics.md b/python_fundamentals/notes/day12-pydantic-basics.md deleted file mode 100644 index 49b1c3d..0000000 --- a/python_fundamentals/notes/day12-pydantic-basics.md +++ /dev/null @@ -1,476 +0,0 @@ - -# Day 12 - Pydantic Basics - -# Lesson Goal - -Learn how Pydantic performs **runtime validation**, automatic **type conversion**, and how to define validation rules using `Field()`. - ---- - -# Problem - -Type hints and dataclasses describe the expected types, but they do **not** validate data at runtime. - -Example: - -```python -from dataclasses import dataclass - -@dataclass -class User: - name: str - age: int - - -user = User( - name=123, - age=[] -) -``` - -Python creates the object successfully even though the data is incorrect. - -Pydantic solves this problem by validating data while creating the object. - ---- - -# BaseModel - -Every Pydantic model inherits from `BaseModel`. - -```python -from pydantic import BaseModel - - -class User(BaseModel): - name: str - age: int -``` - -Creating an object: - -```python -user = User( - name="Tejas", - age=25, -) -``` - -Access fields like a normal object: - -```python -print(user.name) -print(user.age) -``` - ---- - -# Runtime Validation - -Pydantic validates data when an object is created. - -Valid: - -```python -User( - name="Tejas", - age=25, -) -``` - -Invalid: - -```python -User( - name="Tejas", - age=[], -) -``` - -Raises: - -```text -ValidationError -``` - ---- - -# Type Coercion - -Pydantic automatically converts compatible values. - -Example: - -```python -from pydantic import BaseModel - - -class Product(BaseModel): - name: str - price: float - quantity: int - - -product = Product( - name="Motorola G35", - price="50000", - quantity="2", -) -``` - -Stored values become: - -```python -price -> 50000.0 -quantity -> 2 -``` - -Types: - -```python -type(product.price) # float -type(product.quantity) # int -``` - ---- - -# ValidationError - -If a value cannot be converted into the expected type, Pydantic raises a `ValidationError`. - -Example: - -```python -Product( - name="Laptop", - price="abc", - quantity=2, -) -``` - -Result: - -```text -ValidationError -``` - -Reason: - -```text -"abc" cannot be converted into float. -``` - ---- - -# model_dump() - -Convert a Pydantic model into a Python dictionary. - -Example: - -```python -product.model_dump() -``` - -Returns: - -```python -{ - "name": "Motorola G35", - "price": 50000.0, - "quantity": 2, -} -``` - -Equivalent to: - -```python -asdict(dataclass_object) -``` - -for dataclasses. - ---- - -# model_dump_json() - -Convert the model into a JSON string. - -Example: - -```python -print(product.model_dump_json()) -``` - -Output: - -```json -{"name":"Motorola G35","price":50000.0,"quantity":2} -``` - -Useful when returning API responses. - ---- - -# Field() - -`Field()` allows us to define validation rules and metadata. - -Think of it as: - -```text -Field -= -Type Hint -+ -Validation Rules -+ -Metadata -``` - -Example: - -```python -from pydantic import BaseModel, Field - - -class Product(BaseModel): - name: str = Field(min_length=3) - price: float = Field(gt=0) - quantity: int = Field(gt=0) -``` - ---- - -# Numeric Constraints - -Greater Than - -```python -price: float = Field(gt=0) -``` - -Accepts: - -```text -1 -10 -100 -``` - -Rejects: - -```text -0 --1 -``` - ---- - -Greater Than or Equal - -```python -price: float = Field(ge=0) -``` - -Accepts: - -```text -0 -1 -100 -``` - ---- - -Less Than - -```python -discount: int = Field(lt=100) -``` - -Accepts: - -```text -99 -``` - -Rejects: - -```text -100 -101 -``` - ---- - -Less Than or Equal - -```python -discount: int = Field(le=100) -``` - -Accepts: - -```text -100 -``` - ---- - -# String Constraints - -Minimum Length - -```python -username: str = Field(min_length=4) -``` - ---- - -Maximum Length - -```python -username: str = Field(max_length=20) -``` - ---- - -Both Together - -```python -username: str = Field( - min_length=4, - max_length=20, -) -``` - ---- - -# Example - -```python -from pydantic import BaseModel, Field - - -class User(BaseModel): - username: str = Field( - min_length=4, - max_length=20, - ) - email: str - age: int = Field(ge=18) - salary: int = Field(gt=0) -``` - ---- - -# Validation Flow - -```text -Incoming Data - │ - ▼ -Pydantic Validation - │ - ▼ -Type Conversion - │ - ▼ -Valid Model -``` - -If validation fails: - -```text -Incoming Data - │ - ▼ -ValidationError -``` - ---- - -# Comparison - -## Dataclass - -```python -@dataclass -class User: - ... -``` - -* Creates objects -* No runtime validation - ---- - -## TypedDict - -```python -class User(TypedDict): - ... -``` - -* Static type checking -* No runtime validation - ---- - -## Pydantic - -```python -class User(BaseModel): - ... -``` - -* Runtime validation -* Automatic type conversion -* Serialization support -* Ideal for APIs - ---- - -# When To Use - -Use **Pydantic** when: - -* Building FastAPI applications -* Validating API request data -* Validating JSON payloads -* Parsing configuration -* Ensuring runtime type safety - ---- - -# Key Learning - -* `BaseModel` is the foundation of every Pydantic model. -* Pydantic validates data during object creation. -* Compatible values are automatically converted. -* Invalid values raise `ValidationError`. -* `Field()` defines validation rules. -* `model_dump()` converts a model into a Python dictionary. -* `model_dump_json()` converts a model into a JSON string. -* Pydantic replaces much of the manual validation code written using `if` statements. - ---- - -# Recap - -```text -dict - ↓ -TypedDict - ↓ -dataclass - ↓ -Pydantic BaseModel - ↓ -Field() - ↓ -Runtime Validation - ↓ -FastAPI Request Models -``` diff --git a/python_fundamentals/notes/day13-email-str-and-field-validator.md b/python_fundamentals/notes/day13-email-str-and-field-validator.md deleted file mode 100644 index 2cdbd14..0000000 --- a/python_fundamentals/notes/day13-email-str-and-field-validator.md +++ /dev/null @@ -1,291 +0,0 @@ - -# Day 13 — Advanced Pydantic: EmailStr & `field_validator` - -## Learning Objectives - -By the end of this lesson I learned: - -* How to use specialized Pydantic types. -* Why `EmailStr` is better than `str` for email fields. -* How to write custom validation using `@field_validator`. -* How to normalize input before validation. -* How to keep business validation inside the model. -* How to test Pydantic validation using `pytest`. - ---- - -# Why Pydantic? - -Python type hints provide static information: - -```python -email: str -``` - -This only tells Python that the field is a string. - -Pydantic adds **runtime validation**. - -```python -email: EmailStr -``` - -Now the field must contain a valid email address. - ---- - -# Specialized Types - -Instead of using primitive types everywhere, Pydantic provides semantic types. - -Example: - -```python -from pydantic import EmailStr - -email: EmailStr -``` - -Benefits: - -* Validates email addresses. -* Produces meaningful validation errors. -* Makes the model self-documenting. - ---- - -# `Field()` - -`Field()` is used to declare validation rules. - -Example: - -```python -name: str = Field( - min_length=3, - max_length=20, -) - -salary: float = Field(gt=0) -``` - -Supported validations include: - -* `min_length` -* `max_length` -* `gt` -* `ge` -* `lt` -* `le` -* `default` -* `description` - ---- - -# `field_validator` - -`field_validator` allows custom validation that cannot be expressed using `Field()`. - -Example: - -```python -@field_validator("name") -@classmethod -def normalize_and_validate_name( - cls, - value: str, -) -> str: - ... -``` - -A validator can: - -* Validate values. -* Normalize values. -* Transform values. - ---- - -# Validation Pipeline - -When creating a model, validation occurs in this order: - -```text -Input - │ - ▼ -Type Conversion - │ - ▼ -Field() - │ - ▼ -field_validator() - │ - ▼ -Model Created -``` - -Understanding this order helps explain why validators receive values that have already been converted to the declared type. - ---- - -# Normalization Before Validation - -A common pattern is: - -```python -value = value.strip() -value = value.title() -``` - -Then validate: - -```python -if len(value) < 3: - raise ValueError(...) -``` - -Finally: - -```python -return value -``` - -Recommended order: - -```text -Normalize - ↓ -Validate - ↓ -Return -``` - ---- - -# Returning the Value - -Every validator **must** return the final value. - -Example: - -```python -return value -``` - -If the value is not returned, the model has nothing to store. - ---- - -# Raising Errors - -Inside a validator: - -```python -raise ValueError(...) -``` - -Outside the model: - -```python -ValidationError -``` - -Pydantic automatically converts `ValueError` into `ValidationError`. - ---- - -# Keeping Validation Inside the Model - -Instead of: - -```python -employee = Employee(...) - -if employee.salary < 0: - ... -``` - -Prefer: - -```python -class Employee(BaseModel): - ... -``` - -The model should enforce its own business rules. - ---- - -# Testing Validation - -Validation should be tested using `pytest`. - -Example: - -```python -def test_invalid_email() -> None: - with pytest.raises(ValidationError): - Employee( - name="Tejas Dixit", - email="invalid-email", - salary=25_000, - ) -``` - -Testing validation rules is more reliable than checking printed output. - ---- - -# Best Practices - -✔ Use semantic types (`EmailStr`) instead of plain strings. - -✔ Keep validation close to the data. - -✔ Normalize input before validating. - -✔ Return the validated value from every validator. - -✔ Raise `ValueError` inside validators. - -✔ Test validation using `pytest`. - ---- - -# Concepts Learned - -* `BaseModel` -* `EmailStr` -* `Field` -* `field_validator` -* `ValidationError` -* `model_dump()` - ---- - -# Key Takeaways - -* Type hints describe the expected type. -* Pydantic validates data at runtime. -* `Field()` handles common validation rules. -* `field_validator()` handles custom business rules. -* Models should protect their own invariants. -* Validation belongs inside the model, not throughout the application. - ---- - -# Summary - -Today I learned how to build self-validating models using Pydantic. - -I can now: - -* Create runtime-validated models. -* Use semantic types like `EmailStr`. -* Normalize incoming data. -* Implement custom validation with `field_validator`. -* Test validation logic using `pytest`. - -This completes the foundation required before learning **`model_validator`**, where validation involves relationships between multiple fields. diff --git a/python_fundamentals/notes/day23-str-enum.md b/python_fundamentals/notes/day23-str-enum.md deleted file mode 100644 index 91b4d4d..0000000 --- a/python_fundamentals/notes/day23-str-enum.md +++ /dev/null @@ -1,239 +0,0 @@ -# Day 23 — StrEnum - -## Goal - -Understand why `StrEnum` is used for business fields with a fixed set of values and why it is preferred for JSON APIs. - ---- - -## Business Problem - -Using `str` allows inconsistent values. - -```python -department = "HR" -department = "hr" -department = "Human Resource" -department = "Human Resources" -``` - -Although they represent the same department, they produce inconsistent data. - ---- - -## Solution - -Use `StrEnum` when a business field has a predefined set of valid values. - -```python -from enum import StrEnum - -class Department(StrEnum): - ADMIN = "ADMIN" - TECH = "TECH" - DATA = "DATA" - CONTENT = "CONTENT" -``` - -Now only the declared values are accepted. - ---- - -## Syntax - -```python -from enum import StrEnum - -class Department(StrEnum): - ADMIN = "ADMIN" - TECH = "TECH" -``` - -Use inside a model: - -```python -department: Department -``` - ---- - -## Business Use Cases - -- Department -- User Role -- Order Status -- Payment Status -- Task Status -- Account Type - ---- - -## Rules - -- Use `StrEnum` only for fixed business values. -- Do not use `str` when only predefined values are allowed. -- Prefer `StrEnum` over `Enum` for API models. -- `StrEnum` improves consistency and prevents invalid values. - ---- - -## Validation - -Valid: - -```python -department = Department.TECH -``` - -or - -```python -department = "TECH" -``` - -Invalid: - -```python -department = "SALES" -``` - -Result: - -``` -ValidationError -``` - ---- - -## Output - -Python representation: - -```python -employee.model_dump() -``` - -Output: - -```python -{ - "department": -} -``` - -JSON representation: - -```python -employee.model_dump_json() -``` - -Output: - -```json -{ - "department": "TECH" -} -``` - ---- - -## Common Mistakes - -### Wrong - -```python -department: str -``` - -### Correct - -```python -department: Department -``` - ---- - -### Wrong - -```python -Department.SALES -``` - -Raises: - -``` -AttributeError -``` - -Reason: - -`SALES` is not a member of the enum. - ---- - -### Wrong - -```python -department="SALES" -``` - -Raises: - -``` -ValidationError -``` - -Reason: - -Pydantic validates the input against the allowed enum values. - ---- - -## Key Takeaways - -- `StrEnum` represents a fixed set of business values. -- It prevents inconsistent string values. -- It serializes cleanly to JSON. -- It is preferred for FastAPI request and response models. - ---- - -## Hansei - -### Today I Learned - -- Why business-controlled fields should use `StrEnum`. -- Difference between `Enum` and `StrEnum`. -- Difference between `AttributeError` and `ValidationError`. - -### Mistakes I Made - -- Expected `Department.SALES` to raise a `ValidationError`. - -### Why It Happened - -- Python evaluates enum members before Pydantic validation. - -### Improvement - -- Distinguish between Python runtime errors and Pydantic validation errors. - ---- - -## Interview Questions - -1. Why should `Department` be modeled as a `StrEnum` instead of a `str`? -2. What is the difference between `Enum` and `StrEnum`? -3. Why does `Department.SALES` raise an `AttributeError`? -4. When does Pydantic raise a `ValidationError` for an enum? -5. Why is `StrEnum` preferred in FastAPI applications? - ---- - -## Summary - -- Fixed business values → `StrEnum` -- Free text → `str` -- Python validates enum members. -- Pydantic validates input values. -- `model_dump_json()` produces API-friendly JSON. diff --git a/python_fundamentals/notes/day25-date-datetime.md b/python_fundamentals/notes/day25-date-datetime.md deleted file mode 100644 index a6a3727..0000000 --- a/python_fundamentals/notes/day25-date-datetime.md +++ /dev/null @@ -1,220 +0,0 @@ -# Day 25 — Date & DateTime - -## Goal - -Understand when to use `date` and `datetime` in Pydantic models and how Pydantic validates and parses them. - ---- - -## Business Problem - -Using `str` for dates allows inconsistent formats. - -```python -"21/07/2026" -"21-07-2026" -"July 21, 2026" -"2026-07-21" -``` - -Business applications require a consistent and validated date format. - ---- - -## Solution - -Use Python's `date` and `datetime` types. - -```python -from datetime import date, datetime - -joining_date: date -created_at: datetime -``` - -Pydantic automatically parses valid ISO formatted strings into Python objects. - ---- - -## Syntax - -```python -from datetime import date, datetime - -class Employee(BaseModel): - joining_date: date - created_at: datetime -``` - ---- - -## Business Use Cases - -### Use `date` - -- Employee joining date -- Date of birth -- Manufacturing date -- Invoice due date - -### Use `datetime` - -- User login -- Payment timestamp -- Order creation -- Audit logs -- Record creation time - ---- - -## Validation - -### Python Validation - -```python -date(year=2024, month=13, day=1) -``` - -Result: - -``` -ValueError -``` - -Reason: - -Python validates the `date()` constructor before Pydantic receives the value. - ---- - -### Pydantic Validation - -```python -Employee( - joining_date="2024-13-01", -) -``` - -Result: - -``` -ValidationError -``` - -Reason: - -Pydantic parses the string into a `date`. Invalid values raise a `ValidationError`. - ---- - -## Validation Ownership - -| Layer | Responsibility | -|--------|----------------| -| Python | Validates `date()` and `datetime()` constructors | -| Pydantic | Parses raw input into Python `date` and `datetime` objects | -| Business Validators | Business-specific rules (future lessons) | - ---- - -## Common Mistakes - -### Wrong - -```python -joining_date: str -``` - -### Correct - -```python -joining_date: date -``` - ---- - -### Wrong - -```python -created_at: str -``` - -### Correct - -```python -created_at: datetime -``` - ---- - -### Wrong - -Expecting: - -```python -date(year=2024, month=13, day=1) -``` - -to raise: - -``` -ValidationError -``` - -Actual: - -``` -ValueError -``` - ---- - -## Key Takeaways - -- `date` stores only the calendar date. -- `datetime` stores both date and time. -- Business requirements determine whether to use `date` or `datetime`. -- Python validates constructed `date` and `datetime` objects. -- Pydantic parses raw input and raises `ValidationError` when parsing fails. - ---- - -## Hansei - -### Today I Learned - -- Difference between `date` and `datetime`. -- Difference between Python validation and Pydantic validation. -- Pydantic parses ISO formatted strings automatically. - -### Mistakes I Made - -Initially expected invalid `date()` construction to raise a `ValidationError`. - -### Why It Happened - -I did not distinguish between Python runtime validation and Pydantic model validation. - -### Improvement - -Before debugging, identify which layer owns the validation. - ---- - -## Interview Questions - -1. When should you use `date` instead of `datetime`? -2. Why should dates not be stored as `str`? -3. What is the difference between `ValueError` and `ValidationError`? -4. How does Pydantic handle ISO formatted date strings? -5. Which layer owns the validation of `date()`? - ---- - -## Summary - -- Business requirement decides `date` vs `datetime`. -- Python validates constructors. -- Pydantic parses and validates input. -- Use `date` for calendar dates. -- Use `datetime` when time is required. diff --git a/python_fundamentals/notes/day26-model-dump.md b/python_fundamentals/notes/day26-model-dump.md deleted file mode 100644 index 0f025cf..0000000 --- a/python_fundamentals/notes/day26-model-dump.md +++ /dev/null @@ -1,183 +0,0 @@ -# Day 26 — model_dump() - -## Goal - -Understand how `model_dump()` serializes a validated Pydantic model and why serialization is important for different business views. - ---- - -## Business Problem - -Different consumers require different representations of the same object. - -Example: - -### HR - -- Employee Name -- Joining Date - -### Admin - -- Employee Name -- Created At - -### Public Profile - -- Employee Name - -The business object remains the same, but the serialized output changes based on business requirements. - ---- - -## Solution - -Use `model_dump()` to serialize a validated Pydantic model into a Python dictionary. - -```python -employee.model_dump() -``` - ---- - -## Syntax - -### Serialize Entire Model - -```python -employee.model_dump() -``` - -### Include Fields - -```python -employee.model_dump( - include={"emp_name", "joining_date"} -) -``` - -### Exclude Fields - -```python -employee.model_dump( - exclude={"created_at"} -) -``` - ---- - -## Business Use Cases - -- API responses -- HR dashboard -- Admin dashboard -- Public profile -- Logging -- Exporting reports - ---- - -## Serialization Flow - -```text -Raw Input - ↓ -Pydantic Validation - ↓ -Employee Model - ↓ -model_dump() - ↓ -Python Dictionary -``` - ---- - -## Rules - -- `model_dump()` serializes a validated model. -- Validation happens during model creation, not during serialization. -- One model can have multiple serialized representations. -- Prefer `model_dump()` over `__dict__` for Pydantic models. - ---- - -## Common Mistakes - -### Wrong - -Thinking `model_dump()` validates the model. - -### Correct - -Validation occurs during model creation. - -`model_dump()` only serializes the existing model. - ---- - -### Wrong - -```python -employee.__dict__ -``` - -for application serialization. - -### Correct - -```python -employee.model_dump() -``` - ---- - -## Key Takeaways - -- Serialization converts a model into a dictionary. -- Business requirements determine which fields are serialized. -- `include` selects specific fields. -- `exclude` hides specific fields. -- Serialization and validation are different stages. - ---- - -## Hansei - -### Today I Learned - -- Difference between validation and serialization. -- One model can produce multiple business views. -- `model_dump()` returns a Python dictionary. - -### Mistakes I Made - -Initially thought `model_dump()` performed validation. - -### Why It Happened - -I associated every Pydantic method with validation. - -### Improvement - -Separate model creation from model representation. - ---- - -## Interview Questions - -1. What is serialization? -2. What does `model_dump()` return? -3. What is the difference between validation and serialization? -4. Why is `model_dump()` preferred over `__dict__`? -5. When should `include` and `exclude` be used? - ---- - -## Summary - -- `model_dump()` serializes a validated Pydantic model. -- It returns a standard Python dictionary. -- Use `include` for selected fields. -- Use `exclude` to hide fields. -- The same model can produce multiple business-specific views. diff --git a/python_fundamentals/projects/capstone_01_employee_management_system/__init__.py b/python_fundamentals/projects/capstone_01_employee_management_system/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python_fundamentals/tests/test_day13_email_validation.py b/python_fundamentals/tests/test_day13_email_validation.py deleted file mode 100644 index 502db54..0000000 --- a/python_fundamentals/tests/test_day13_email_validation.py +++ /dev/null @@ -1,13 +0,0 @@ -import pytest -from pydantic import ValidationError - -from python_fundamentals.exercises.day13_email_validation import Employee - - -def test_invalid_email() -> None: - with pytest.raises(ValidationError): - _: Employee = Employee( - name="tejas Dixit", - email="invalid-email", - salary=25_000, - ) diff --git a/python_fundamentals/tests/test_inventory.py b/python_fundamentals/tests/test_inventory.py deleted file mode 100644 index 1d869f4..0000000 --- a/python_fundamentals/tests/test_inventory.py +++ /dev/null @@ -1,14 +0,0 @@ -import pytest - -from python_fundamentals.exercises.inventory import Product, validate_product - - -def test_invalid_product() -> None: - with pytest.raises(TypeError): - validate_product( - Product( - name="laptop", - price=-100, - quantity=1, - ) - ) diff --git a/python_fundamentals/tests/test_pricing.py b/python_fundamentals/tests/test_pricing.py deleted file mode 100644 index aae4157..0000000 --- a/python_fundamentals/tests/test_pricing.py +++ /dev/null @@ -1,11 +0,0 @@ -from python_fundamentals.exercises.pricing import ( - calculate_discounted_price, -) - - -def test_discounted_price() -> None: - result: float = calculate_discounted_price( - price=100, quantity=2, discount_percentage=10 - ) - - assert result == 180.0