diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8e0e1c8..c75763e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,44 +11,52 @@ "ms-python.python", "ms-python.vscode-pylance", "ms-python.black-formatter", - "oderwat.indent-rainbow", "ms-python.isort", "ms-python.debugpy", + "ms-python.flake8", "ms-toolsai.jupyter", "DavidAnson.vscode-markdownlint", - "GitHub.copilot", - "GitHub.copilot-chat", + "github.copilot", + "github.copilot-chat", + "github.vscode-github-actions", "mikestead.dotenv", - "ms-python.flake8", - "tamasfe.even-better-toml", - "github.vscode-github-actions" + "tamasfe.even-better-toml" ], "settings": { - "terminal.integrated.shell.linux": "/bin/bash", - "editor.defaultFormatter": "ms-python.black-formatter", - "indentRainbow.indicatorStyle": "light", + "editor.linkedEditing": true, "editor.formatOnSave": true, + "editor.rulers": [ + 80, + 100 + ], + "editor.guides.bracketPairs": true, + "editor.guides.highlightActiveIndentation": true, + "explorer.sortOrderLexicographicOptions": "upper", "python.condaPath": "/opt/conda/bin/conda", "python.defaultInterpreterPath": "/opt/conda/envs/venv/bin/python", "python.analysis.autoImportCompletions": true, "python.analysis.typeCheckingMode": "strict", "python.analysis.diagnosticMode": "workspace", - "explorer.sortOrderLexicographicOptions": "upper", "python.testing.pytestArgs": [ "tests" ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "editor.rulers": [ - 80, - 100 - ], + "terminal.integrated.shell.linux": "/bin/bash", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, "[toml]": { "editor.defaultFormatter": "tamasfe.even-better-toml", "editor.formatOnSave": true }, "[markdown]": { - "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", + "editor.wordWrap": "on" } } } diff --git a/README.md b/README.md index 7fbd5cb..f99323e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TheCodeCrate's Middlewareable -This package provides a middleware pattern implementation built on top of the [`thecodecrate-pipeline`](https://pypi.org/project/thecodecrate-pipeline/) library. +This package provides a middleware pattern implementation built on top of the [`thecodecrate-pipeline`](https://github.com/thecodecrate/python-pipeline) library. ## Installation @@ -21,7 +21,7 @@ A pipeline consists of zero, one, or multiple stages. A pipeline can process a p This particular implementation is build on top of the `thecodecrate-pipeline` library, which provides a generic pipeline pattern. -The standard processor in _TheCodeCrate's Pipeline_ executes stages in a for-loop manner: +The standard processor in Pipeline executes stages in a for-loop manner: ```python result = payload @@ -32,7 +32,7 @@ for stage in stages: return result ``` -However, this library provides a middleware processor for executing stages in a nested manner: +This library provides a middleware processor for executing stages in a nested manner: ```python stage3 = lambda payload: payload + 3 @@ -46,7 +46,35 @@ This is useful for implementing a chain of responsibility, where each stage can ## Usage -You define middleware as callables that accept a `payload` and a `next_call` function: +You define middleware as callables that accept a payload and a `next_call` function: + +```python +# Create a middleware pipeline using lambda functions +pipeline = ( + MiddlewarePipeline[int]() + .pipe(lambda x, next_call: next_call(x + 1)) + .pipe(lambda x, next_call: next_call(x * 2)) + .pipe(lambda x, next_call: next_call(x + 3)) +) + +# Process a payload +result = await pipeline.process(5) +print(f"Result: {result}") +``` + +This pipeline processes the payload `5` through the following stages: + +1. Add 1: `5 + 1 = 6` +2. Multiply by 2: `6 * 2 = 12` +3. Add 3: `12 + 3 = 15` + +**Output:** + +```plaintext +Result: 15 +``` + +With middlewares, you can include pre-processing and post-processing logic: ```python # Define middleware functions @@ -95,7 +123,7 @@ You can also define middleware as classes by implementing the `MiddlewareStage` ```python class AddMiddleware(MiddlewareStage[int, int]): - async def __call__(self, payload: int, next_call: MiddlewareNextCall[int, int]) -> int: + async def __call__(self, payload: int, next_call: MiddlewareNextCall) -> int: print("AddMiddleware - Before") payload += 3 result = await next_call(payload) diff --git a/docker/local/etc/bash_aliases b/docker/local/etc/bash_aliases index 2d0a40a..f88af1e 100644 --- a/docker/local/etc/bash_aliases +++ b/docker/local/etc/bash_aliases @@ -1,3 +1,9 @@ # Aliases for GIT shortcuts (https://laracasts.com/series/how-to-contribute-to-open-source/episodes/5) alias wip='git add . && git commit -m '\''Work in progress'\' alias nope='git reset --hard && git clean -f -d && git checkout HEAD' + +# Alias for publishing the package to pypi +alias clean_dist='rm -rf dist/*' +alias bump='bumpver update --minor' +alias build='clean_dist && python -m build' +alias deploy='twine upload dist/*' \ No newline at end of file diff --git a/jupyter/01-instructions.ipynb b/jupyter/01-instructions.ipynb index 17c349c..0a28278 100644 --- a/jupyter/01-instructions.ipynb +++ b/jupyter/01-instructions.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -25,7 +25,7 @@ "from python_middlewareable import MiddlewarePipeline\n", "\n", "pipeline = (\n", - " (MiddlewarePipeline[int]())\n", + " MiddlewarePipeline[int]()\n", " .pipe(lambda x, next_call: next_call(x + 1))\n", " .pipe(lambda x, next_call: next_call(x * 2))\n", " .pipe(lambda x, next_call: next_call(x + 3))\n",