diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7f0e104 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,38 @@ +# Contributor Code of Conduct + +## Our Pledge + +We are committed to providing a friendly, safe, and welcoming environment for all contributors and learners, regardless of background, experience level, identity, or perspective. + +--- + +## Standards + +Contributors are expected to uphold the following standards: + +- Demonstrate empathy and kindness toward other members of the community. +- Respect differing viewpoints, approaches, and constructive feedback. +- Focus on what is best for the overall learning community. +- Gracefully accept constructive criticism and suggestions. +- Write clean, well-documented code that benefits others. + +--- + +## Unacceptable Behavior + +The following behaviors are considered unacceptable: + +- Harassment, trolling, derogatory comments, or personal attacks. +- Disrespectful or exclusionary conduct in issues, pull requests, or discussions. +- Publishing private information of others without explicit permission. +- Any conduct that could reasonably be considered inappropriate in a professional or educational setting. + +--- + +## Enforcement and Reporting + +Project maintainers are responsible for clarifying and enforcing standards of acceptable behavior. Instances of abusive, harassing, or otherwise unacceptable behavior can be reported to the repository maintainer at: + +`chandio.saqib.m@gmail.com` + +All complaints will be reviewed and investigated promptly and fairly. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7b3d459 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,84 @@ +# Contributing Guidelines + +Thank you for considering contributing to Fluent Python. Contributions help keep this repository a valuable learning resource for everyone. + +Direct pushes to the `main` branch are blocked. All contributions must go through a fork, branch, and Pull Request workflow. + +--- + +## Contribution Workflow + +Follow these steps to contribute code, notebooks, or documentation: + +### 1. Fork and Clone + +Fork the repository to your own GitHub account, then clone it locally: + +```bash +git clone https://github.com//fluent-python.git +cd fluent-python +``` + +Add the upstream remote to stay in sync with changes: + +```bash +git remote add upstream https://github.com/saqibbedar/fluent-python.git +``` + +### 2. Create a Topic Branch + +Always create a dedicated branch off `main` for your work. Never work directly on `main`: + +```bash +git checkout -b feature/topic-name +``` + +Use descriptive branch names, such as `feature/generator-expressions` or `fix/pathlib-typo`. + +### 3. Guidelines on File Types and Naming + +- **Jupyter Notebooks (`.ipynb`)**: Strongly recommended for thorough, step-by-step explanations, conceptual walkthroughs, and learning modules. Keep them organized and clear. +- **Python Scripts (`.py`)**: Recommended for standalone scripts, modules, or larger application setups (e.g. `fastapi-projects/`, custom utilities). +- **Markdown Files (`.md`)**: Accepted for documentation, theoretical summaries, and reference material. +- **File Naming**: Names must clearly communicate contents (e.g., `list_comprehensions.ipynb` instead of `temp.ipynb` or `test.py`). + +> **Note on Structure Reorganization:** This repository is actively expanding and undergoing continuous normalization. The maintainers reserve the right to shuffle, relocate, or refactor contributed files and directories as the repository grows. + +### 4. Run Code Quality Checks Locally + +Before committing or pushing, verify that your code conforms to the project's formatting and linting standards: + +```bash +just code_quality +``` + +This runs Ruff checks and formatting on all modified and untracked files, writing results to `.logs/ruff.txt`. + +- Inspect `.logs/ruff.txt` to confirm that all checks pass. +- Fix any syntax or lint errors before proceeding. + +### 5. Continuous Integration (GitHub Actions) + +An automated GitHub Actions workflow runs the same Ruff lint and format verification on every Pull Request. If local checks are skipped and the CI workflow fails, your PR cannot be merged until all issues are resolved. + +### 6. Commit and Push + +Stage and commit your changes: + +```bash +git add . +git commit -m "feat(comprehensions): add set comprehension examples" +``` + +Push the branch to your fork: + +```bash +git push origin feature/topic-name +``` + +### 7. Open a Pull Request + +1. Navigate to the original repository: `https://github.com/saqibbedar/fluent-python`. +2. Click **New Pull Request** and select your branch. +3. Fill out the provided Pull Request template completely. +4. Submit the PR for review and address any reviewer comments or suggestions. diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..2e612f8 --- /dev/null +++ b/Justfile @@ -0,0 +1,4 @@ +# Justfile for automatic scripts + +code_quality: + uv run --project kernel python scripts/code_quality.py \ No newline at end of file diff --git a/README.md b/README.md index f737f76..6d263ae 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,71 @@ # Fluent Python -This repository covers massive Python topics, organized in proper folder structure. Anyone interested in learning Python can find this helpful. Constantly, it is being updated with highly documented notebooks that makes it so easy for you to learn Python's any topic. +A comprehensive, hands-on repository dedicated to learning and mastering modern Python. This repository provides structured code examples, deep dives, and documented Jupyter notebooks across core language constructs, design patterns, standard library modules, and practical applications. -> ⚠️ Important Note: As repository is continuously updating, so contents will be restructured on fly. Any discrepancy will subject to removal. Current folder structure is not fixed—with daily changes the things can go here and there for absolute optimized repository for learning Python. +> **Work in Progress (WIP):** This repository is under active development and continuously expanding. File paths, folder structure, and topics may be shuffled or refactored as the repository is normalized. -# Ultimate Goal +--- -After the work is finished, this repository will serve a quick learning hub for any body who wants to master the Python, its design patterns through properly documented notebooks. And, final conversion will be a website that will make it even more easy for you to learn anything about Python systematically and steptistically. +## Scope and Covered Topics -# Folder structure +The repository is organized modularly by concept and domain: -Currently, folder structure is a bit weird as I have `0x_` prefix which will be refactored once all major topics are covered and and organized orderly. +- **Language Fundamentals**: Basics, functions, exceptions, and file handling. +- **Data Structures**: Detailed coverage of built-in types (strings, bytes, bytearrays, lists, dicts, sets, tuples) and advanced collections. +- **Pythonic Patterns**: Comprehensions, iterators, generators, and closures. +- **Object-Oriented Programming (OOP)**: Classes, inheritance, polymorphism, and special dunder methods. +- **Standard Library & Core Modules**: Modules such as `pathlib`, `datetime`, `subprocess`, and locale management. +- **Projects & Frameworks**: Practical applications including FastAPI projects. +- **Tooling & Automation**: Maintenance scripts, local quality runners, and CI checks. -# Getting Started +--- -You can start exploring the repository locally, or you can add your programs, too, in `/contributors` directory. If not exist then do create it and push your code inside it. This will help us to maintain the main code and contributors code easily and inject any topic to any directory on purpose. +## Getting Started -Here is how to clone and get started fully on your local machine. +### Prerequisites + +- Python 3.12+ (Python 3.14 used in kernel environment) +- [`uv`](https://docs.astral.sh/uv/) for package and tool management +- [`just`](https://github.com/casey/just) command runner + +### Setup + +1. Clone the repository: + ```bash + git clone https://github.com/saqibbedar/fluent-python.git + cd fluent-python + ``` + +2. Open the project in your editor of choice (e.g., VS Code, PyCharm, or JupyterLab). + +--- + +## Code Quality + +All Python scripts and notebooks are checked and formatted using [Ruff](https://docs.astral.sh/ruff/). + +To run linting, formatting, and auto-fixes across all modified and untracked files: ```bash -git clone https://github.com/saqibbedar/fluent-python.git -cd fluent-python && code . # && code . is vs code command, if you don't use it to then skip it and open folder manually or use IDE of your choice, i.e., PyCharm etc. +just code_quality ``` -# Contributions +Logs are generated in `.logs/ruff.txt`. Code quality checks run automatically on every pull request via GitHub Actions. + +--- + +## Contributing + +Contributions are welcome. Before submitting any changes, please review: + +- [CONTRIBUTING.md](./CONTRIBUTING.md) for branch workflow, file naming conventions, and PR requirements. +- [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) for community standards. + +Direct pushes to `main` are restricted; all contributions must be submitted through pull requests. -As like always, contributions are heart of any repository. So, you are invited and welcomed, even help in documentation is a big contribution and any flaw you find in existing code or anything you have to share fell free to add on. You are superbly welcomed. +--- -# LICENSE +## License +This project is licensed under the [MIT License](./LICENSE). See [LICENSE](./LICENSE). \ No newline at end of file diff --git a/kernel/pyproject.toml b/kernel/pyproject.toml index 86a9b9c..07aec50 100644 --- a/kernel/pyproject.toml +++ b/kernel/pyproject.toml @@ -8,8 +8,10 @@ authors = [ ] requires-python = ">=3.14" dependencies = [ + "gitpython>=3.2.0", "numpy>=2.5.3", "pandas>=3.0.6", + "yachalk>=0.1.8", ] [dependency-groups] diff --git a/kernel/uv.lock b/kernel/uv.lock index 5dfd3ac..5d237e3 100644 --- a/kernel/uv.lock +++ b/kernel/uv.lock @@ -7,13 +7,48 @@ resolution-markers = [ "sys_platform != 'emscripten' and sys_platform != 'win32'", ] +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/2d/6f6e649818da44d4499604802c89329b8d9799687a124e3a5e467a643336/gitpython-3.2.0.tar.gz", hash = "sha256:fb92310af6844d96adc95ca066ed2e617c00e1dbd146a326626c81e72e18cc2e", size = 238091, upload-time = "2026-09-30T09:10:32.65Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/48/f36d233ce749d1bb880cb5e6f8bb171dc2effabc7435c31da604d831b3ec/gitpython-3.2.0-py3-none-any.whl", hash = "sha256:bd70c5ec05cd2b797423e7eb312147d2458d3cca92085888fba2213f85905537", size = 228537, upload-time = "2026-09-30T09:10:31.159Z" }, +] + +[[package]] +name = "importlib-resources" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" }, +] + [[package]] name = "kernel" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "gitpython" }, { name = "numpy" }, { name = "pandas" }, + { name = "yachalk" }, ] [package.dev-dependencies] @@ -23,8 +58,10 @@ dev = [ [package.metadata] requires-dist = [ + { name = "gitpython", specifier = ">=3.2.0" }, { name = "numpy", specifier = ">=2.5.3" }, { name = "pandas", specifier = ">=3.0.6" }, + { name = "yachalk", specifier = ">=0.1.8" }, ] [package.metadata.requires-dev] @@ -186,6 +223,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "setuptools" +version = "84.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -195,6 +241,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + [[package]] name = "tzdata" version = "2026.4" @@ -203,3 +258,16 @@ sdist = { url = "https://files.pythonhosted.org/packages/e4/31/3d74fa778a63b98b7 wheels = [ { url = "https://files.pythonhosted.org/packages/f9/bc/8737e8d54cf51106118039b83f485a4783112fab49ea9d044b234978a46e/tzdata-2026.4-py2.py3-none-any.whl", hash = "sha256:c2169a8b0a7a5e9674da5a135ccdfb2b3e671b333ed9fed17b41f73c34476e81", size = 347494, upload-time = "2026-09-12T12:56:01.67Z" }, ] + +[[package]] +name = "yachalk" +version = "0.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-resources" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/e6/17b8642bc554083ca149f69c6b61a801e81afcbd60bb4aa22783d54322ed/yachalk-0.1.8.tar.gz", hash = "sha256:bb9b52fda52f34679c9b13d4284a8603eacb9da9d6365b870e0a0c8c41ea9382", size = 17079, upload-time = "2025-10-10T15:50:05.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/3f/233733fddeaf60920d0e08f94e862b5b178c07740342623d00d02211c16f/yachalk-0.1.8-py3-none-any.whl", hash = "sha256:9667709f53a6121b2b272f1e31a77259b98308b267ce8a8591e26e76a9517fb3", size = 13928, upload-time = "2025-10-10T15:50:05.04Z" }, +] diff --git a/scripts/code_quality.py b/scripts/code_quality.py new file mode 100644 index 0000000..12e1eb3 --- /dev/null +++ b/scripts/code_quality.py @@ -0,0 +1,130 @@ +# code_quality: an automatic script to run ruff tests +# run file: +# - using just: just code_quality + +""" +These are the cmds will be executed directly on all modified or untracked files automatically. + +uvx ruff check . +uvx ruff format . +uvx ruff check --fix . + +and logs will be saved in .logs dir (if not already existing, a .logs will be created automatically) +""" + +import locale +import subprocess # for cmds to run in terminal +from datetime import datetime # for logs time +from pathlib import Path # for paths resolution + +# importing GitPython from global dependency env "kernel" directory +# GitPython is installed at kernel dir and current selected env is kernel/.venv +# if you are using vscode then from root(*this repo) cd to kernel directory and run uv sync to install required dependencies, and then select the evn for python, in vscode you can do that via a bottom-bar list (look for Python X.X.X version) click on it and change your env with kernel/.venv/bin/python +import git # for untracked and modified (unstaged) files +from yachalk import chalk # improve terminal output + + +# create logs directory +def make_logs_dir(root_dir: Path | None = None): + + print(f"\n{chalk.bold.cyan('--- Attempting .logs directory creation ---')}\n") + + if root_dir is None: + print(chalk.bold.red(f"- Invalid Path {root_dir}\n")) + + else: + logs_dir: Path = (root_dir / ".logs").resolve() + + if logs_dir.exists(): + print(f"- .logs directory already exist at {chalk.yellow(str(logs_dir))}\n") + + else: + # exist_ok: prevents errors if they already exist + logs_dir.mkdir(exist_ok=True) + print(chalk.green(f".logs directory was successfully created at {logs_dir}\n")) + + +# get the all untracked and modified (unstaged) files +def get_git_status(repo_path: Path = Path(".")) -> list[str | None] | None: + try: + # git repository object + repo = git.Repo(repo_path) + + # 1. get untracked files + untracked_files = repo.untracked_files + + # 2. get modified files (unstaged) + modified_files = [item.a_path for item in repo.index.diff(None) if item.change_type == "M"] + + return [*untracked_files, *modified_files] + + except git.InvalidGitRepositoryError: + print(chalk.bold.red(f"Error: {repo_path} is not a valid Git repository")) + + except git.NoSuchPathError: + print(chalk.bold.red(f"Error: The path {repo_path} does not exist")) + + +# prepare the ROOT (path) +ROOT: Path = Path(__file__).parent.parent + +# create .logs dir +make_logs_dir(ROOT) + +# untracked and unstaged (modified) files +files: list[str | None] | None = get_git_status(ROOT) + +# display files +print(f"{chalk.gray('-' * 50)}\n") +print(f"{chalk.bold.blue('--- Untracked And Modified (unstaged) file list ---')}\n") +print(chalk.yellow(str(files)), "\n") + + +# Ruff automatic tests +print(f"{chalk.gray('-' * 50)}\n") +print(chalk.bold.magenta("--- Running Ruff Tests ---")) + +# shell=True: perform cmd exactly as we type in terminal +# text=True returns raw python string +# capture_output: capture output in variable + +if files is not None: + # filter only python files + python_files: list[str] = [f for f in (files or []) if f and (f.endswith(".py") or f.endswith(".ipynb"))] + + if not python_files: + print(chalk.yellow("No modified or untracked Python files found. Skipping Ruff.")) + exit(0) + +# run ruff commands +ruff_check = subprocess.run(["uvx", "ruff", "check", *python_files], text=True, capture_output=True) +ruff_format = subprocess.run(["uvx", "ruff", "format", *python_files], text=True, capture_output=True) +ruff_check_fix = subprocess.run(["uvx", "ruff", "check", "--fix", *python_files], text=True, capture_output=True) + + +# prepare pretty output +def pretty_log_output() -> str: + # Set to user's default system locale + locale.setlocale(locale.LC_ALL, "") + + # prepare date & time + now = datetime.now() + + time = now.strftime("%H:%M:%S") + date = now.strftime("%Y-%m-%d") + + output = f"---------- Ruff Test Logs ----------\n\n- time: {time}\n- date: {date}\n\n" + output += f"--- cmd: uvx ruff check ---\n[\n {ruff_check.stdout + ruff_check.stderr}]\n\n" + output += f"--- cmd: uvx ruff format ---\n[\n {ruff_format.stdout + ruff_format.stderr}]\n\n" + output += f"--- cmd: uvx ruff check --fix ---\n[\n {ruff_check_fix.stdout + ruff_check_fix.stderr}]\n\n" + + return output + + +# save logs +with open(f"{(ROOT / '.logs' / 'ruff.txt')}", mode="w") as file: + output: str = pretty_log_output() + file.write(output) + +print(f"{chalk.bold.green('--- All tests are done ----')}\n") +print(f"- View generated logs at {chalk.bold.cyan(str((ROOT / '.logs' / 'ruff.txt').resolve()))}")