diff --git a/.claude/commands/new-pattern.md b/.claude/commands/new-pattern.md index 2edae05..844f74b 100644 --- a/.claude/commands/new-pattern.md +++ b/.claude/commands/new-pattern.md @@ -17,7 +17,7 @@ Scaffold a new pattern module for $ARGUMENTS. - `docs/fundamentals.md`, `docs/implementation.md`, `docs/examples.md` — each a heading plus a `TODO` line naming what belongs there (classic-form contrast in fundamentals; never use the word "naive"). - - `examples/demo/__main__.py` with a typed `main() -> None` + script guard + - `examples/demo/main.py` with a typed `main() -> None` + script guard that imports from `...pattern`. Create NO other `__init__.py` — empty ones are banned (PEP 420 namespace packages); the loader rejects them. - `tests/test_.py` with one failing `test_todo` marked diff --git a/AGENTS.md b/AGENTS.md index 45d9d38..8e4e0d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,7 @@ patterns/// │ ├── implementation.md# introducing it into a real system: smell, steps, idioms, pitfalls │ └── examples.md # cited EXTERNAL usages: stdlib, OSS, articles ├── examples/ # runnable mini-projects that import pattern/ -│ └── / # realistic domain, no Foo/Bar; __main__.py + modules +│ └── / # realistic domain, no Foo/Bar; main.py + modules └── tests/ # isolated: test_.py + test_.py ``` @@ -38,7 +38,7 @@ No other `__init__.py` exist — namespace packages (PEP 420) carry the rest; the loader rejects empty ones. - Everything import-safe (no side effects at import); mini-projects run via - `uv run python -m patterns...examples.`. + `uv run python -m patterns...examples..main`. - Tests assert behavior, never just "it runs"; load-bearing claims get the mutation treatment (mutate the code, prove the suite fails, revert). - Examples must genuinely build on `pattern/` — the loader and a catalog test diff --git a/CLAUDE.md b/CLAUDE.md index 42097cd..01ae7e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,7 +30,7 @@ patterns/// │ ├── implementation.md# introducing it into a real system: smell, steps, idioms, pitfalls │ └── examples.md # cited EXTERNAL usages: stdlib, OSS, articles ├── examples/ # runnable mini-projects that import pattern/ -│ └── / # realistic domain, no Foo/Bar; __main__.py + modules +│ └── / # realistic domain, no Foo/Bar; main.py + modules └── tests/ # isolated: test_.py + test_.py ``` @@ -38,7 +38,7 @@ No other `__init__.py` exist — namespace packages (PEP 420) carry the rest; the loader rejects empty ones. - Everything import-safe (no side effects at import); mini-projects run via - `uv run python -m patterns...examples.`. + `uv run python -m patterns...examples..main`. - Tests assert behavior, never just "it runs"; load-bearing claims get the mutation treatment (mutate the code, prove the suite fails, revert). - Examples must genuinely build on `pattern/` — the loader and a catalog test diff --git a/README.md b/README.md index c847d70..e750d34 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ patterns/structural/decorator/ from patterns.structural.decorator import retry, logged ``` -Run any mini-project: `uv run python -m patterns.structural.decorator.examples.resilient_client` +Run any mini-project: `uv run python -m patterns.structural.decorator.examples.resilient_client.main` Give it to your agents (MCP server with search, runnable examples, and pattern recommendations): diff --git a/docs/how-to-read-this-repo.md b/docs/how-to-read-this-repo.md index 2a865ce..59208dc 100644 --- a/docs/how-to-read-this-repo.md +++ b/docs/how-to-read-this-repo.md @@ -19,7 +19,7 @@ from patterns.behavioral.chain_of_responsibility import Chain ``` ```bash -uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation +uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.main ``` ## Where to start diff --git a/patterns/behavioral/chain_of_responsibility/README.md b/patterns/behavioral/chain_of_responsibility/README.md index 20b9cfc..60ebd00 100644 --- a/patterns/behavioral/chain_of_responsibility/README.md +++ b/patterns/behavioral/chain_of_responsibility/README.md @@ -26,5 +26,5 @@ chain is callables in a list, not objects with successor pointers. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation +uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.main ``` diff --git a/patterns/behavioral/chain_of_responsibility/docs/implementation.md b/patterns/behavioral/chain_of_responsibility/docs/implementation.md index 2124c86..33ea243 100644 --- a/patterns/behavioral/chain_of_responsibility/docs/implementation.md +++ b/patterns/behavioral/chain_of_responsibility/docs/implementation.md @@ -75,5 +75,5 @@ resolution = chain.handle_or(ticket, triage(ticket)) step above to support-ticket routing — run it with: ```bash -uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation +uv run python -m patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.main ``` diff --git a/patterns/behavioral/chain_of_responsibility/examples/ticket_escalation/__main__.py b/patterns/behavioral/chain_of_responsibility/examples/ticket_escalation/main.py similarity index 100% rename from patterns/behavioral/chain_of_responsibility/examples/ticket_escalation/__main__.py rename to patterns/behavioral/chain_of_responsibility/examples/ticket_escalation/main.py diff --git a/patterns/behavioral/chain_of_responsibility/tests/test_ticket_escalation.py b/patterns/behavioral/chain_of_responsibility/tests/test_ticket_escalation.py index e83e4da..9e77d14 100644 --- a/patterns/behavioral/chain_of_responsibility/tests/test_ticket_escalation.py +++ b/patterns/behavioral/chain_of_responsibility/tests/test_ticket_escalation.py @@ -4,11 +4,11 @@ import pytest -from patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.__main__ import main from patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.handlers import ( build_escalation_chain, route, ) +from patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.main import main from patterns.behavioral.chain_of_responsibility.examples.ticket_escalation.models import Ticket diff --git a/patterns/behavioral/command/README.md b/patterns/behavioral/command/README.md index c881020..64278b3 100644 --- a/patterns/behavioral/command/README.md +++ b/patterns/behavioral/command/README.md @@ -26,5 +26,5 @@ the whole pattern until commands need undo, logs, or metadata. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.command.examples.editor_undo +uv run python -m patterns.behavioral.command.examples.editor_undo.main ``` diff --git a/patterns/behavioral/command/docs/implementation.md b/patterns/behavioral/command/docs/implementation.md index c6ebf41..da340fb 100644 --- a/patterns/behavioral/command/docs/implementation.md +++ b/patterns/behavioral/command/docs/implementation.md @@ -62,5 +62,5 @@ Both grow unbounded and neither can answer "what exactly did the user do?". text editor — insert/delete/replace with undo, redo, and a session log: ```bash -uv run python -m patterns.behavioral.command.examples.editor_undo +uv run python -m patterns.behavioral.command.examples.editor_undo.main ``` diff --git a/patterns/behavioral/command/examples/editor_undo/__main__.py b/patterns/behavioral/command/examples/editor_undo/main.py similarity index 100% rename from patterns/behavioral/command/examples/editor_undo/__main__.py rename to patterns/behavioral/command/examples/editor_undo/main.py diff --git a/patterns/behavioral/interpreter/README.md b/patterns/behavioral/interpreter/README.md index ef4d630..3cee412 100644 --- a/patterns/behavioral/interpreter/README.md +++ b/patterns/behavioral/interpreter/README.md @@ -26,5 +26,5 @@ most little-language needs; grammar-as-data covers the rest. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.interpreter.examples.flag_rules +uv run python -m patterns.behavioral.interpreter.examples.flag_rules.main ``` diff --git a/patterns/behavioral/interpreter/docs/implementation.md b/patterns/behavioral/interpreter/docs/implementation.md index 8f383ee..3c9ac1d 100644 --- a/patterns/behavioral/interpreter/docs/implementation.md +++ b/patterns/behavioral/interpreter/docs/implementation.md @@ -65,5 +65,5 @@ feature-flag engine — rules as data, per-user evaluation, hostile input rejected: ```bash -uv run python -m patterns.behavioral.interpreter.examples.flag_rules +uv run python -m patterns.behavioral.interpreter.examples.flag_rules.main ``` diff --git a/patterns/behavioral/interpreter/examples/flag_rules/__main__.py b/patterns/behavioral/interpreter/examples/flag_rules/main.py similarity index 100% rename from patterns/behavioral/interpreter/examples/flag_rules/__main__.py rename to patterns/behavioral/interpreter/examples/flag_rules/main.py diff --git a/patterns/behavioral/iterator/README.md b/patterns/behavioral/iterator/README.md index 98e5917..fb6667b 100644 --- a/patterns/behavioral/iterator/README.md +++ b/patterns/behavioral/iterator/README.md @@ -25,5 +25,5 @@ Traverse elements without exposing storage — lazily when it matters. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.iterator.examples.paginated_client +uv run python -m patterns.behavioral.iterator.examples.paginated_client.main ``` diff --git a/patterns/behavioral/iterator/docs/implementation.md b/patterns/behavioral/iterator/docs/implementation.md index 3e11ca1..74c7a4a 100644 --- a/patterns/behavioral/iterator/docs/implementation.md +++ b/patterns/behavioral/iterator/docs/implementation.md @@ -61,5 +61,5 @@ million records. step to an article API client with an observably lazy fetch log: ```bash -uv run python -m patterns.behavioral.iterator.examples.paginated_client +uv run python -m patterns.behavioral.iterator.examples.paginated_client.main ``` diff --git a/patterns/behavioral/iterator/examples/paginated_client/__main__.py b/patterns/behavioral/iterator/examples/paginated_client/main.py similarity index 100% rename from patterns/behavioral/iterator/examples/paginated_client/__main__.py rename to patterns/behavioral/iterator/examples/paginated_client/main.py diff --git a/patterns/behavioral/mediator/README.md b/patterns/behavioral/mediator/README.md index 003100c..17b496b 100644 --- a/patterns/behavioral/mediator/README.md +++ b/patterns/behavioral/mediator/README.md @@ -26,5 +26,5 @@ for god-object drift. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.mediator.examples.checkout_form +uv run python -m patterns.behavioral.mediator.examples.checkout_form.main ``` diff --git a/patterns/behavioral/mediator/docs/implementation.md b/patterns/behavioral/mediator/docs/implementation.md index 1335a74..1caf90f 100644 --- a/patterns/behavioral/mediator/docs/implementation.md +++ b/patterns/behavioral/mediator/docs/implementation.md @@ -62,5 +62,5 @@ assert form.payment_options == ("card", "cod") country/shipping/payment with cascading resets and submit gating: ```bash -uv run python -m patterns.behavioral.mediator.examples.checkout_form +uv run python -m patterns.behavioral.mediator.examples.checkout_form.main ``` diff --git a/patterns/behavioral/mediator/examples/checkout_form/__main__.py b/patterns/behavioral/mediator/examples/checkout_form/main.py similarity index 100% rename from patterns/behavioral/mediator/examples/checkout_form/__main__.py rename to patterns/behavioral/mediator/examples/checkout_form/main.py diff --git a/patterns/behavioral/memento/README.md b/patterns/behavioral/memento/README.md index 92d79f9..253f2c2 100644 --- a/patterns/behavioral/memento/README.md +++ b/patterns/behavioral/memento/README.md @@ -27,5 +27,5 @@ state and the pattern is nearly free. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.memento.examples.config_checkpoints +uv run python -m patterns.behavioral.memento.examples.config_checkpoints.main ``` diff --git a/patterns/behavioral/memento/docs/implementation.md b/patterns/behavioral/memento/docs/implementation.md index a44fa2b..d6938a9 100644 --- a/patterns/behavioral/memento/docs/implementation.md +++ b/patterns/behavioral/memento/docs/implementation.md @@ -82,5 +82,5 @@ every step: atomic validate-or-reject batches, LIFO undo, and a named "before-upgrade" checkpoint. Run it with: ```bash -uv run python -m patterns.behavioral.memento.examples.config_checkpoints +uv run python -m patterns.behavioral.memento.examples.config_checkpoints.main ``` diff --git a/patterns/behavioral/memento/examples/config_checkpoints/__main__.py b/patterns/behavioral/memento/examples/config_checkpoints/main.py similarity index 100% rename from patterns/behavioral/memento/examples/config_checkpoints/__main__.py rename to patterns/behavioral/memento/examples/config_checkpoints/main.py diff --git a/patterns/behavioral/memento/tests/test_config_checkpoints.py b/patterns/behavioral/memento/tests/test_config_checkpoints.py index 2d89135..7ac0032 100644 --- a/patterns/behavioral/memento/tests/test_config_checkpoints.py +++ b/patterns/behavioral/memento/tests/test_config_checkpoints.py @@ -4,8 +4,8 @@ import pytest -from patterns.behavioral.memento.examples.config_checkpoints.__main__ import main from patterns.behavioral.memento.examples.config_checkpoints.editor import ConfigEditor +from patterns.behavioral.memento.examples.config_checkpoints.main import main from patterns.behavioral.memento.examples.config_checkpoints.models import ( InvalidConfigError, ServiceConfig, diff --git a/patterns/behavioral/observer/README.md b/patterns/behavioral/observer/README.md index f21d718..85e55fc 100644 --- a/patterns/behavioral/observer/README.md +++ b/patterns/behavioral/observer/README.md @@ -26,5 +26,5 @@ list; the only real design decisions are order and failure policy. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.observer.examples.order_events +uv run python -m patterns.behavioral.observer.examples.order_events.main ``` diff --git a/patterns/behavioral/observer/docs/implementation.md b/patterns/behavioral/observer/docs/implementation.md index 61a14bf..dd377dc 100644 --- a/patterns/behavioral/observer/docs/implementation.md +++ b/patterns/behavioral/observer/docs/implementation.md @@ -80,5 +80,5 @@ one pipeline, four independent subscribers, a down webhook quarantined to a dead-letter list while the rest keep working. Run it with: ```bash -uv run python -m patterns.behavioral.observer.examples.order_events +uv run python -m patterns.behavioral.observer.examples.order_events.main ``` diff --git a/patterns/behavioral/observer/examples/order_events/__main__.py b/patterns/behavioral/observer/examples/order_events/main.py similarity index 100% rename from patterns/behavioral/observer/examples/order_events/__main__.py rename to patterns/behavioral/observer/examples/order_events/main.py diff --git a/patterns/behavioral/observer/tests/test_order_events.py b/patterns/behavioral/observer/tests/test_order_events.py index a577d3c..c3370eb 100644 --- a/patterns/behavioral/observer/tests/test_order_events.py +++ b/patterns/behavioral/observer/tests/test_order_events.py @@ -4,7 +4,7 @@ import pytest -from patterns.behavioral.observer.examples.order_events.__main__ import main +from patterns.behavioral.observer.examples.order_events.main import main from patterns.behavioral.observer.examples.order_events.subscribers import ( AuditLog, EmailNotifier, diff --git a/patterns/behavioral/state/README.md b/patterns/behavioral/state/README.md index 09a45b2..65a460a 100644 --- a/patterns/behavioral/state/README.md +++ b/patterns/behavioral/state/README.md @@ -27,5 +27,5 @@ class-per-state only pays at real size. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.state.examples.order_lifecycle +uv run python -m patterns.behavioral.state.examples.order_lifecycle.main ``` diff --git a/patterns/behavioral/state/docs/implementation.md b/patterns/behavioral/state/docs/implementation.md index 2315f98..961600e 100644 --- a/patterns/behavioral/state/docs/implementation.md +++ b/patterns/behavioral/state/docs/implementation.md @@ -81,5 +81,5 @@ step: an eight-row table, three data guards, refusal of a too-late cancel, and the audit log printed at the end. Run it with: ```bash -uv run python -m patterns.behavioral.state.examples.order_lifecycle +uv run python -m patterns.behavioral.state.examples.order_lifecycle.main ``` diff --git a/patterns/behavioral/state/examples/order_lifecycle/__main__.py b/patterns/behavioral/state/examples/order_lifecycle/main.py similarity index 100% rename from patterns/behavioral/state/examples/order_lifecycle/__main__.py rename to patterns/behavioral/state/examples/order_lifecycle/main.py diff --git a/patterns/behavioral/state/tests/test_order_lifecycle.py b/patterns/behavioral/state/tests/test_order_lifecycle.py index fecfab3..147a1c3 100644 --- a/patterns/behavioral/state/tests/test_order_lifecycle.py +++ b/patterns/behavioral/state/tests/test_order_lifecycle.py @@ -5,8 +5,8 @@ import pytest from patterns.behavioral.state import IllegalTransitionError -from patterns.behavioral.state.examples.order_lifecycle.__main__ import main from patterns.behavioral.state.examples.order_lifecycle.lifecycle import build_lifecycle +from patterns.behavioral.state.examples.order_lifecycle.main import main from patterns.behavioral.state.examples.order_lifecycle.models import ( Order, OrderAction, diff --git a/patterns/behavioral/strategy/README.md b/patterns/behavioral/strategy/README.md index 742fa3e..c0d54d4 100644 --- a/patterns/behavioral/strategy/README.md +++ b/patterns/behavioral/strategy/README.md @@ -26,5 +26,5 @@ the registry below is for families that grow. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.strategy.examples.promotions +uv run python -m patterns.behavioral.strategy.examples.promotions.main ``` diff --git a/patterns/behavioral/strategy/docs/implementation.md b/patterns/behavioral/strategy/docs/implementation.md index 2fc22ea..2cbda4d 100644 --- a/patterns/behavioral/strategy/docs/implementation.md +++ b/patterns/behavioral/strategy/docs/implementation.md @@ -77,5 +77,5 @@ checkout pricing — three registered rules, a best-rule engine, and a comparison report: ```bash -uv run python -m patterns.behavioral.strategy.examples.promotions +uv run python -m patterns.behavioral.strategy.examples.promotions.main ``` diff --git a/patterns/behavioral/strategy/examples/promotions/__main__.py b/patterns/behavioral/strategy/examples/promotions/main.py similarity index 100% rename from patterns/behavioral/strategy/examples/promotions/__main__.py rename to patterns/behavioral/strategy/examples/promotions/main.py diff --git a/patterns/behavioral/strategy/tests/test_promotions.py b/patterns/behavioral/strategy/tests/test_promotions.py index 596255c..2d74db4 100644 --- a/patterns/behavioral/strategy/tests/test_promotions.py +++ b/patterns/behavioral/strategy/tests/test_promotions.py @@ -4,7 +4,7 @@ import pytest -from patterns.behavioral.strategy.examples.promotions.__main__ import main +from patterns.behavioral.strategy.examples.promotions.main import main from patterns.behavioral.strategy.examples.promotions.models import LineItem, Order from patterns.behavioral.strategy.examples.promotions.rules import best_promo, due, promotion from patterns.behavioral.strategy.pattern import StrategyRegistry diff --git a/patterns/behavioral/template_method/README.md b/patterns/behavioral/template_method/README.md index d623d8d..3187a47 100644 --- a/patterns/behavioral/template_method/README.md +++ b/patterns/behavioral/template_method/README.md @@ -26,5 +26,5 @@ that hand them to you. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.template_method.examples.report_pipeline +uv run python -m patterns.behavioral.template_method.examples.report_pipeline.main ``` diff --git a/patterns/behavioral/template_method/docs/implementation.md b/patterns/behavioral/template_method/docs/implementation.md index f8f3943..af2b87c 100644 --- a/patterns/behavioral/template_method/docs/implementation.md +++ b/patterns/behavioral/template_method/docs/implementation.md @@ -69,5 +69,5 @@ step above to sales reporting — one spine, CSV and Markdown variants derived from a baseline: ```bash -uv run python -m patterns.behavioral.template_method.examples.report_pipeline +uv run python -m patterns.behavioral.template_method.examples.report_pipeline.main ``` diff --git a/patterns/behavioral/template_method/examples/report_pipeline/__main__.py b/patterns/behavioral/template_method/examples/report_pipeline/main.py similarity index 100% rename from patterns/behavioral/template_method/examples/report_pipeline/__main__.py rename to patterns/behavioral/template_method/examples/report_pipeline/main.py diff --git a/patterns/behavioral/template_method/tests/test_report_pipeline.py b/patterns/behavioral/template_method/tests/test_report_pipeline.py index e8ca8d1..efb7b4c 100644 --- a/patterns/behavioral/template_method/tests/test_report_pipeline.py +++ b/patterns/behavioral/template_method/tests/test_report_pipeline.py @@ -4,7 +4,7 @@ import pytest -from patterns.behavioral.template_method.examples.report_pipeline.__main__ import main +from patterns.behavioral.template_method.examples.report_pipeline.main import main from patterns.behavioral.template_method.examples.report_pipeline.models import Sale from patterns.behavioral.template_method.examples.report_pipeline.pipeline import ( build_csv_report, diff --git a/patterns/behavioral/visitor/README.md b/patterns/behavioral/visitor/README.md index 0857e89..dbef21d 100644 --- a/patterns/behavioral/visitor/README.md +++ b/patterns/behavioral/visitor/README.md @@ -26,5 +26,5 @@ the subclass form survives at stdlib boundaries (`ast.NodeVisitor`). | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.behavioral.visitor.examples.doc_exporters +uv run python -m patterns.behavioral.visitor.examples.doc_exporters.main ``` diff --git a/patterns/behavioral/visitor/docs/implementation.md b/patterns/behavioral/visitor/docs/implementation.md index e3446de..cec5345 100644 --- a/patterns/behavioral/visitor/docs/implementation.md +++ b/patterns/behavioral/visitor/docs/implementation.md @@ -79,5 +79,5 @@ above to a document tree — Markdown, plain-text, and word-count operations over five node types the operations never edit: ```bash -uv run python -m patterns.behavioral.visitor.examples.doc_exporters +uv run python -m patterns.behavioral.visitor.examples.doc_exporters.main ``` diff --git a/patterns/behavioral/visitor/examples/doc_exporters/__main__.py b/patterns/behavioral/visitor/examples/doc_exporters/main.py similarity index 100% rename from patterns/behavioral/visitor/examples/doc_exporters/__main__.py rename to patterns/behavioral/visitor/examples/doc_exporters/main.py diff --git a/patterns/behavioral/visitor/tests/test_doc_exporters.py b/patterns/behavioral/visitor/tests/test_doc_exporters.py index d1504bb..d4eb7df 100644 --- a/patterns/behavioral/visitor/tests/test_doc_exporters.py +++ b/patterns/behavioral/visitor/tests/test_doc_exporters.py @@ -5,12 +5,12 @@ import pytest from patterns.behavioral.visitor import Operation, UnhandledNodeError -from patterns.behavioral.visitor.examples.doc_exporters.__main__ import main, sample_document from patterns.behavioral.visitor.examples.doc_exporters.exporters import ( markdown, plain_text, word_count, ) +from patterns.behavioral.visitor.examples.doc_exporters.main import main, sample_document from patterns.behavioral.visitor.examples.doc_exporters.nodes import ( BulletList, CodeBlock, diff --git a/patterns/creational/abstract_factory/README.md b/patterns/creational/abstract_factory/README.md index 8ef89a2..c7a8b2e 100644 --- a/patterns/creational/abstract_factory/README.md +++ b/patterns/creational/abstract_factory/README.md @@ -28,5 +28,5 @@ consistent with each other. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.creational.abstract_factory.examples.report_renderer +uv run python -m patterns.creational.abstract_factory.examples.report_renderer.main ``` diff --git a/patterns/creational/abstract_factory/docs/implementation.md b/patterns/creational/abstract_factory/docs/implementation.md index 2f98513..6b6b189 100644 --- a/patterns/creational/abstract_factory/docs/implementation.md +++ b/patterns/creational/abstract_factory/docs/implementation.md @@ -75,5 +75,5 @@ quarterly report through the `MARKDOWN` and `HTML` families — same client code, both outputs: ```bash -uv run python -m patterns.creational.abstract_factory.examples.report_renderer +uv run python -m patterns.creational.abstract_factory.examples.report_renderer.main ``` diff --git a/patterns/creational/abstract_factory/examples/report_renderer/__main__.py b/patterns/creational/abstract_factory/examples/report_renderer/main.py similarity index 100% rename from patterns/creational/abstract_factory/examples/report_renderer/__main__.py rename to patterns/creational/abstract_factory/examples/report_renderer/main.py diff --git a/patterns/creational/abstract_factory/tests/test_report_renderer.py b/patterns/creational/abstract_factory/tests/test_report_renderer.py index beee41f..29c6994 100644 --- a/patterns/creational/abstract_factory/tests/test_report_renderer.py +++ b/patterns/creational/abstract_factory/tests/test_report_renderer.py @@ -2,7 +2,7 @@ from __future__ import annotations -from patterns.creational.abstract_factory.examples.report_renderer.__main__ import Q3 +from patterns.creational.abstract_factory.examples.report_renderer.main import Q3 from patterns.creational.abstract_factory.examples.report_renderer.renderer import render from patterns.creational.abstract_factory.examples.report_renderer.report import ( Report, diff --git a/patterns/creational/builder/README.md b/patterns/creational/builder/README.md index 04e60bb..7af6b72 100644 --- a/patterns/creational/builder/README.md +++ b/patterns/creational/builder/README.md @@ -27,5 +27,5 @@ genuinely staged. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.creational.builder.examples.sql_select_builder +uv run python -m patterns.creational.builder.examples.sql_select_builder.main ``` diff --git a/patterns/creational/builder/docs/implementation.md b/patterns/creational/builder/docs/implementation.md index 46c132d..1ab1d54 100644 --- a/patterns/creational/builder/docs/implementation.md +++ b/patterns/creational/builder/docs/implementation.md @@ -74,5 +74,5 @@ three analytics queries — including a conditionally-narrowed one — and runs them against a real in-memory sqlite database: ```bash -uv run python -m patterns.creational.builder.examples.sql_select_builder +uv run python -m patterns.creational.builder.examples.sql_select_builder.main ``` diff --git a/patterns/creational/builder/examples/sql_select_builder/__main__.py b/patterns/creational/builder/examples/sql_select_builder/main.py similarity index 100% rename from patterns/creational/builder/examples/sql_select_builder/__main__.py rename to patterns/creational/builder/examples/sql_select_builder/main.py diff --git a/patterns/creational/factory_method/README.md b/patterns/creational/factory_method/README.md index 3946541..c7881ae 100644 --- a/patterns/creational/factory_method/README.md +++ b/patterns/creational/factory_method/README.md @@ -27,5 +27,5 @@ Java with the serial numbers filed off. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.creational.factory_method.examples.feed_client +uv run python -m patterns.creational.factory_method.examples.feed_client.main ``` diff --git a/patterns/creational/factory_method/docs/implementation.md b/patterns/creational/factory_method/docs/implementation.md index b3f3877..9889677 100644 --- a/patterns/creational/factory_method/docs/implementation.md +++ b/patterns/creational/factory_method/docs/implementation.md @@ -75,5 +75,5 @@ class StrictClient(FeedClient): # or per-subclass; factory_slot because subclass, by instance, and by a test double — run it with: ```bash -uv run python -m patterns.creational.factory_method.examples.feed_client +uv run python -m patterns.creational.factory_method.examples.feed_client.main ``` diff --git a/patterns/creational/factory_method/examples/feed_client/__main__.py b/patterns/creational/factory_method/examples/feed_client/main.py similarity index 100% rename from patterns/creational/factory_method/examples/feed_client/__main__.py rename to patterns/creational/factory_method/examples/feed_client/main.py diff --git a/patterns/creational/prototype/README.md b/patterns/creational/prototype/README.md index 2024366..9f5bb82 100644 --- a/patterns/creational/prototype/README.md +++ b/patterns/creational/prototype/README.md @@ -26,5 +26,5 @@ with a `clone()` protocol; tweak frozen products with `dataclasses.replace`. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.creational.prototype.examples.report_job_templates +uv run python -m patterns.creational.prototype.examples.report_job_templates.main ``` diff --git a/patterns/creational/prototype/docs/implementation.md b/patterns/creational/prototype/docs/implementation.md index b97bdd0..c64cfa6 100644 --- a/patterns/creational/prototype/docs/implementation.md +++ b/patterns/creational/prototype/docs/implementation.md @@ -70,5 +70,5 @@ rush = menu.create("nightly-sales", fmt="csv") # fresh, tweaked, template safe scheduler shape above, end to end — run it with: ```bash -uv run python -m patterns.creational.prototype.examples.report_job_templates +uv run python -m patterns.creational.prototype.examples.report_job_templates.main ``` diff --git a/patterns/creational/prototype/examples/report_job_templates/__main__.py b/patterns/creational/prototype/examples/report_job_templates/main.py similarity index 100% rename from patterns/creational/prototype/examples/report_job_templates/__main__.py rename to patterns/creational/prototype/examples/report_job_templates/main.py diff --git a/patterns/creational/singleton/README.md b/patterns/creational/singleton/README.md index 1c216c4..301415f 100644 --- a/patterns/creational/singleton/README.md +++ b/patterns/creational/singleton/README.md @@ -28,5 +28,5 @@ seam for tests. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.creational.singleton.examples.app_config +uv run python -m patterns.creational.singleton.examples.app_config.main ``` diff --git a/patterns/creational/singleton/docs/implementation.md b/patterns/creational/singleton/docs/implementation.md index 92e2ddf..b1ec8f6 100644 --- a/patterns/creational/singleton/docs/implementation.md +++ b/patterns/creational/singleton/docs/implementation.md @@ -74,5 +74,5 @@ with lazy build, cached reads, env re-read after reset, and an injected mapping for tests — run it with: ```bash -uv run python -m patterns.creational.singleton.examples.app_config +uv run python -m patterns.creational.singleton.examples.app_config.main ``` diff --git a/patterns/creational/singleton/examples/app_config/__main__.py b/patterns/creational/singleton/examples/app_config/main.py similarity index 100% rename from patterns/creational/singleton/examples/app_config/__main__.py rename to patterns/creational/singleton/examples/app_config/main.py diff --git a/patterns/modern/async_producer_consumer/README.md b/patterns/modern/async_producer_consumer/README.md index 0305c50..9665a88 100644 --- a/patterns/modern/async_producer_consumer/README.md +++ b/patterns/modern/async_producer_consumer/README.md @@ -26,5 +26,5 @@ Fan I/O-bound work out to N workers over a bounded queue — backpressure by | [`tests/`](tests/) | Behavioral tests for the pool and the mini-project | ```bash -uv run python -m patterns.modern.async_producer_consumer.examples.feed_fetcher +uv run python -m patterns.modern.async_producer_consumer.examples.feed_fetcher.main ``` diff --git a/patterns/modern/async_producer_consumer/examples/feed_fetcher/__main__.py b/patterns/modern/async_producer_consumer/examples/feed_fetcher/main.py similarity index 100% rename from patterns/modern/async_producer_consumer/examples/feed_fetcher/__main__.py rename to patterns/modern/async_producer_consumer/examples/feed_fetcher/main.py diff --git a/patterns/modern/async_producer_consumer/tests/test_feed_fetcher.py b/patterns/modern/async_producer_consumer/tests/test_feed_fetcher.py index 8e975e8..baffca4 100644 --- a/patterns/modern/async_producer_consumer/tests/test_feed_fetcher.py +++ b/patterns/modern/async_producer_consumer/tests/test_feed_fetcher.py @@ -4,11 +4,11 @@ import pytest -from patterns.modern.async_producer_consumer.examples.feed_fetcher.__main__ import main from patterns.modern.async_producer_consumer.examples.feed_fetcher.fetcher import ( fetch_all, summarize, ) +from patterns.modern.async_producer_consumer.examples.feed_fetcher.main import main from patterns.modern.async_producer_consumer.examples.feed_fetcher.models import Feed from patterns.modern.async_producer_consumer.pattern import Shutdown diff --git a/patterns/modern/context_manager/README.md b/patterns/modern/context_manager/README.md index 69b6b14..e87871b 100644 --- a/patterns/modern/context_manager/README.md +++ b/patterns/modern/context_manager/README.md @@ -25,5 +25,5 @@ Pair acquire with release on every exit path, structurally — Python's RAII. | [`tests/`](tests/) | Behavioral tests for both managers and the mini-project | ```bash -uv run python -m patterns.modern.context_manager.examples.atomic_deploy +uv run python -m patterns.modern.context_manager.examples.atomic_deploy.main ``` diff --git a/patterns/modern/context_manager/examples/atomic_deploy/__main__.py b/patterns/modern/context_manager/examples/atomic_deploy/main.py similarity index 100% rename from patterns/modern/context_manager/examples/atomic_deploy/__main__.py rename to patterns/modern/context_manager/examples/atomic_deploy/main.py diff --git a/patterns/modern/context_manager/tests/test_atomic_deploy.py b/patterns/modern/context_manager/tests/test_atomic_deploy.py index b47ff47..6bd5e8c 100644 --- a/patterns/modern/context_manager/tests/test_atomic_deploy.py +++ b/patterns/modern/context_manager/tests/test_atomic_deploy.py @@ -6,12 +6,12 @@ import pytest -from patterns.modern.context_manager.examples.atomic_deploy.__main__ import main from patterns.modern.context_manager.examples.atomic_deploy.deploy import ( ReleaseError, deploy, require_nonempty, ) +from patterns.modern.context_manager.examples.atomic_deploy.main import main V1 = {"app.toml": "retries = 3\n", "logging.toml": "level = 'info'\n"} diff --git a/patterns/modern/dependency_injection/README.md b/patterns/modern/dependency_injection/README.md index 104bc6a..04a7619 100644 --- a/patterns/modern/dependency_injection/README.md +++ b/patterns/modern/dependency_injection/README.md @@ -26,5 +26,5 @@ a keyword argument with a production default is the whole mechanism. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.modern.dependency_injection.examples.invoice_reminders +uv run python -m patterns.modern.dependency_injection.examples.invoice_reminders.main ``` diff --git a/patterns/modern/dependency_injection/docs/implementation.md b/patterns/modern/dependency_injection/docs/implementation.md index bd6038f..333d653 100644 --- a/patterns/modern/dependency_injection/docs/implementation.md +++ b/patterns/modern/dependency_injection/docs/implementation.md @@ -83,5 +83,5 @@ service at a real composition root, with the demo pinning the clock through the same seam the tests use: ```bash -uv run python -m patterns.modern.dependency_injection.examples.invoice_reminders +uv run python -m patterns.modern.dependency_injection.examples.invoice_reminders.main ``` diff --git a/patterns/modern/dependency_injection/examples/invoice_reminders/__main__.py b/patterns/modern/dependency_injection/examples/invoice_reminders/main.py similarity index 100% rename from patterns/modern/dependency_injection/examples/invoice_reminders/__main__.py rename to patterns/modern/dependency_injection/examples/invoice_reminders/main.py diff --git a/patterns/modern/dependency_injection/tests/test_invoice_reminders.py b/patterns/modern/dependency_injection/tests/test_invoice_reminders.py index 1320748..c31f649 100644 --- a/patterns/modern/dependency_injection/tests/test_invoice_reminders.py +++ b/patterns/modern/dependency_injection/tests/test_invoice_reminders.py @@ -6,7 +6,6 @@ import pytest -from patterns.modern.dependency_injection.examples.invoice_reminders.__main__ import main from patterns.modern.dependency_injection.examples.invoice_reminders.adapters import ( ConsoleMail, InMemoryInvoices, @@ -15,6 +14,7 @@ build_service, sample_invoices, ) +from patterns.modern.dependency_injection.examples.invoice_reminders.main import main from patterns.modern.dependency_injection.pattern import Invoice diff --git a/patterns/modern/registry/README.md b/patterns/modern/registry/README.md index 319da0d..51496c9 100644 --- a/patterns/modern/registry/README.md +++ b/patterns/modern/registry/README.md @@ -26,5 +26,5 @@ for `if/elif` dispatch. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.modern.registry.examples.export_plugins +uv run python -m patterns.modern.registry.examples.export_plugins.main ``` diff --git a/patterns/modern/registry/docs/implementation.md b/patterns/modern/registry/docs/implementation.md index a9e9a7c..610ded1 100644 --- a/patterns/modern/registry/docs/implementation.md +++ b/patterns/modern/registry/docs/implementation.md @@ -82,5 +82,5 @@ including a plugin in its own module whose `__init__` import is the documented fix for the import-time caveat: ```bash -uv run python -m patterns.modern.registry.examples.export_plugins +uv run python -m patterns.modern.registry.examples.export_plugins.main ``` diff --git a/patterns/modern/registry/examples/export_plugins/__main__.py b/patterns/modern/registry/examples/export_plugins/main.py similarity index 100% rename from patterns/modern/registry/examples/export_plugins/__main__.py rename to patterns/modern/registry/examples/export_plugins/main.py diff --git a/patterns/modern/registry/tests/test_export_plugins.py b/patterns/modern/registry/tests/test_export_plugins.py index 54f7274..f280176 100644 --- a/patterns/modern/registry/tests/test_export_plugins.py +++ b/patterns/modern/registry/tests/test_export_plugins.py @@ -6,8 +6,8 @@ import pytest -from patterns.modern.registry.examples.export_plugins.__main__ import main from patterns.modern.registry.examples.export_plugins.exporters import EXPORTERS, export +from patterns.modern.registry.examples.export_plugins.main import main from patterns.modern.registry.pattern import UnknownKeyError ROWS = [{"name": "ada", "role": "eng"}, {"name": "grace", "role": "ops"}] diff --git a/patterns/modern/repository/README.md b/patterns/modern/repository/README.md index 5268897..1bd313f 100644 --- a/patterns/modern/repository/README.md +++ b/patterns/modern/repository/README.md @@ -26,5 +26,5 @@ real adapter both satisfy it, held together by shared contract tests. | [`tests/`](tests/) | Domain tests on the fake; one contract suite parametrized over both adapters | ```bash -uv run python -m patterns.modern.repository.examples.invoice_ledger +uv run python -m patterns.modern.repository.examples.invoice_ledger.main ``` diff --git a/patterns/modern/repository/docs/implementation.md b/patterns/modern/repository/docs/implementation.md index 8772f17..8e4678c 100644 --- a/patterns/modern/repository/docs/implementation.md +++ b/patterns/modern/repository/docs/implementation.md @@ -76,5 +76,5 @@ adapter and prints identical domain answers from both backends; the shared contract tests live in [`tests/test_invoice_ledger.py`](../tests/test_invoice_ledger.py): ```bash -uv run python -m patterns.modern.repository.examples.invoice_ledger +uv run python -m patterns.modern.repository.examples.invoice_ledger.main ``` diff --git a/patterns/modern/repository/examples/invoice_ledger/__main__.py b/patterns/modern/repository/examples/invoice_ledger/main.py similarity index 100% rename from patterns/modern/repository/examples/invoice_ledger/__main__.py rename to patterns/modern/repository/examples/invoice_ledger/main.py diff --git a/patterns/modern/repository/tests/test_invoice_ledger.py b/patterns/modern/repository/tests/test_invoice_ledger.py index e2b1e2f..810489a 100644 --- a/patterns/modern/repository/tests/test_invoice_ledger.py +++ b/patterns/modern/repository/tests/test_invoice_ledger.py @@ -14,7 +14,7 @@ import pytest -from patterns.modern.repository.examples.invoice_ledger.__main__ import main +from patterns.modern.repository.examples.invoice_ledger.main import main from patterns.modern.repository.examples.invoice_ledger.sqlite_repo import SqliteInvoices from patterns.modern.repository.pattern import ( InMemoryInvoices, diff --git a/patterns/principle/composition_over_inheritance/README.md b/patterns/principle/composition_over_inheritance/README.md index 250293c..8374771 100644 --- a/patterns/principle/composition_over_inheritance/README.md +++ b/patterns/principle/composition_over_inheritance/README.md @@ -26,5 +26,5 @@ load-bearing idea behind the rest of this catalog. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.principle.composition_over_inheritance.examples.notification_router +uv run python -m patterns.principle.composition_over_inheritance.examples.notification_router.main ``` diff --git a/patterns/principle/composition_over_inheritance/docs/implementation.md b/patterns/principle/composition_over_inheritance/docs/implementation.md index 3e1c851..75d1984 100644 --- a/patterns/principle/composition_over_inheritance/docs/implementation.md +++ b/patterns/principle/composition_over_inheritance/docs/implementation.md @@ -70,5 +70,5 @@ severity ≥ 2, deduped JSON to a webhook at severity ≥ 4 — one `Notifier` class, zero combination subclasses. Run it: ```bash -uv run python -m patterns.principle.composition_over_inheritance.examples.notification_router +uv run python -m patterns.principle.composition_over_inheritance.examples.notification_router.main ``` diff --git a/patterns/principle/composition_over_inheritance/examples/notification_router/__main__.py b/patterns/principle/composition_over_inheritance/examples/notification_router/main.py similarity index 100% rename from patterns/principle/composition_over_inheritance/examples/notification_router/__main__.py rename to patterns/principle/composition_over_inheritance/examples/notification_router/main.py diff --git a/patterns/principle/composition_over_inheritance/tests/test_notification_router.py b/patterns/principle/composition_over_inheritance/tests/test_notification_router.py index de3189b..28d6dc1 100644 --- a/patterns/principle/composition_over_inheritance/tests/test_notification_router.py +++ b/patterns/principle/composition_over_inheritance/tests/test_notification_router.py @@ -7,9 +7,6 @@ import pytest -from patterns.principle.composition_over_inheritance.examples.notification_router.__main__ import ( - main, -) from patterns.principle.composition_over_inheritance.examples.notification_router.axes import ( Dedup, FakeWebhook, @@ -19,6 +16,9 @@ min_severity, plain_text, ) +from patterns.principle.composition_over_inheritance.examples.notification_router.main import ( + main, +) from patterns.principle.composition_over_inheritance.examples.notification_router.models import ( Alert, ) diff --git a/patterns/python/global_object/README.md b/patterns/python/global_object/README.md index 956ec11..3d72c4e 100644 --- a/patterns/python/global_object/README.md +++ b/patterns/python/global_object/README.md @@ -26,5 +26,5 @@ freely, expensive things lazily, mutation only where it is the documented job. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.python.global_object.examples.settings_module +uv run python -m patterns.python.global_object.examples.settings_module.main ``` diff --git a/patterns/python/global_object/docs/implementation.md b/patterns/python/global_object/docs/implementation.md index 216b248..25862b5 100644 --- a/patterns/python/global_object/docs/implementation.md +++ b/patterns/python/global_object/docs/implementation.md @@ -65,5 +65,5 @@ of each kind — constant, prebuilt regex, lazy zone table — and a test that proves import does no work. Run it: ```bash -uv run python -m patterns.python.global_object.examples.settings_module +uv run python -m patterns.python.global_object.examples.settings_module.main ``` diff --git a/patterns/python/global_object/examples/settings_module/__main__.py b/patterns/python/global_object/examples/settings_module/main.py similarity index 100% rename from patterns/python/global_object/examples/settings_module/__main__.py rename to patterns/python/global_object/examples/settings_module/main.py diff --git a/patterns/python/global_object/tests/test_settings_module.py b/patterns/python/global_object/tests/test_settings_module.py index 8eb64b6..0f7f1b2 100644 --- a/patterns/python/global_object/tests/test_settings_module.py +++ b/patterns/python/global_object/tests/test_settings_module.py @@ -14,7 +14,7 @@ import pytest from patterns.python.global_object.examples.settings_module import settings -from patterns.python.global_object.examples.settings_module.__main__ import main +from patterns.python.global_object.examples.settings_module.main import main from patterns.python.global_object.examples.settings_module.shipping import ( is_valid_slug, shipping_zone, diff --git a/patterns/python/prebound_method/README.md b/patterns/python/prebound_method/README.md index adb1060..2c518b6 100644 --- a/patterns/python/prebound_method/README.md +++ b/patterns/python/prebound_method/README.md @@ -26,5 +26,5 @@ isolation. **Verdict: pythonic** — the stdlib's own favorite move. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.python.prebound_method.examples.metrics +uv run python -m patterns.python.prebound_method.examples.metrics.main ``` diff --git a/patterns/python/prebound_method/docs/implementation.md b/patterns/python/prebound_method/docs/implementation.md index 3f296f8..1382ce7 100644 --- a/patterns/python/prebound_method/docs/implementation.md +++ b/patterns/python/prebound_method/docs/implementation.md @@ -65,5 +65,5 @@ def register(name, handler): ... # ... and its functions, drifting apart `MetricsCollector`, with the class public for isolated collectors. Run it: ```bash -uv run python -m patterns.python.prebound_method.examples.metrics +uv run python -m patterns.python.prebound_method.examples.metrics.main ``` diff --git a/patterns/python/prebound_method/examples/metrics/__main__.py b/patterns/python/prebound_method/examples/metrics/main.py similarity index 100% rename from patterns/python/prebound_method/examples/metrics/__main__.py rename to patterns/python/prebound_method/examples/metrics/main.py diff --git a/patterns/python/prebound_method/tests/test_metrics.py b/patterns/python/prebound_method/tests/test_metrics.py index 9617d38..3f64595 100644 --- a/patterns/python/prebound_method/tests/test_metrics.py +++ b/patterns/python/prebound_method/tests/test_metrics.py @@ -7,8 +7,8 @@ import pytest from patterns.python.prebound_method.examples.metrics import api -from patterns.python.prebound_method.examples.metrics.__main__ import main from patterns.python.prebound_method.examples.metrics.collector import MetricsCollector +from patterns.python.prebound_method.examples.metrics.main import main from patterns.python.prebound_method.pattern import shares_instance diff --git a/patterns/python/sentinel_object/README.md b/patterns/python/sentinel_object/README.md index e7d42be..5f44259 100644 --- a/patterns/python/sentinel_object/README.md +++ b/patterns/python/sentinel_object/README.md @@ -27,5 +27,5 @@ per meaning, compared with `is`. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.python.sentinel_object.examples.layered_config +uv run python -m patterns.python.sentinel_object.examples.layered_config.main ``` diff --git a/patterns/python/sentinel_object/docs/implementation.md b/patterns/python/sentinel_object/docs/implementation.md index 6a6d5d4..84aeb4e 100644 --- a/patterns/python/sentinel_object/docs/implementation.md +++ b/patterns/python/sentinel_object/docs/implementation.md @@ -73,5 +73,5 @@ CLI ← file ← defaults where a stored `None` means "explicitly disabled", and hands back a `NullNotifier` so callers never branch. Run it: ```bash -uv run python -m patterns.python.sentinel_object.examples.layered_config +uv run python -m patterns.python.sentinel_object.examples.layered_config.main ``` diff --git a/patterns/python/sentinel_object/examples/layered_config/__main__.py b/patterns/python/sentinel_object/examples/layered_config/main.py similarity index 100% rename from patterns/python/sentinel_object/examples/layered_config/__main__.py rename to patterns/python/sentinel_object/examples/layered_config/main.py diff --git a/patterns/python/sentinel_object/tests/test_layered_config.py b/patterns/python/sentinel_object/tests/test_layered_config.py index 478cc92..f3180e5 100644 --- a/patterns/python/sentinel_object/tests/test_layered_config.py +++ b/patterns/python/sentinel_object/tests/test_layered_config.py @@ -4,8 +4,8 @@ import pytest -from patterns.python.sentinel_object.examples.layered_config.__main__ import main from patterns.python.sentinel_object.examples.layered_config.config import LayeredConfig +from patterns.python.sentinel_object.examples.layered_config.main import main from patterns.python.sentinel_object.examples.layered_config.notifier import ( EmailNotifier, NullNotifier, diff --git a/patterns/structural/adapter/README.md b/patterns/structural/adapter/README.md index 87b5653..cda289c 100644 --- a/patterns/structural/adapter/README.md +++ b/patterns/structural/adapter/README.md @@ -26,5 +26,5 @@ reconcile interfaces you don't control. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.adapter.examples.payment_gateways +uv run python -m patterns.structural.adapter.examples.payment_gateways.main ``` diff --git a/patterns/structural/adapter/docs/implementation.md b/patterns/structural/adapter/docs/implementation.md index 119e6bd..afb4d50 100644 --- a/patterns/structural/adapter/docs/implementation.md +++ b/patterns/structural/adapter/docs/implementation.md @@ -74,5 +74,5 @@ class StripeAdapter(DelegatingAdapter[StripeLikeClient]): mismatched fake vendor SDKs behind one `PaymentProcessor` — run it with: ```bash -uv run python -m patterns.structural.adapter.examples.payment_gateways +uv run python -m patterns.structural.adapter.examples.payment_gateways.main ``` diff --git a/patterns/structural/adapter/examples/payment_gateways/__main__.py b/patterns/structural/adapter/examples/payment_gateways/main.py similarity index 100% rename from patterns/structural/adapter/examples/payment_gateways/__main__.py rename to patterns/structural/adapter/examples/payment_gateways/main.py diff --git a/patterns/structural/adapter/tests/test_payment_gateways.py b/patterns/structural/adapter/tests/test_payment_gateways.py index 5286ac9..3a0d58b 100644 --- a/patterns/structural/adapter/tests/test_payment_gateways.py +++ b/patterns/structural/adapter/tests/test_payment_gateways.py @@ -10,13 +10,13 @@ import pytest -from patterns.structural.adapter.examples.payment_gateways.__main__ import main from patterns.structural.adapter.examples.payment_gateways.adapters import ( PaymentProcessor, PayPalAdapter, StripeAdapter, ) from patterns.structural.adapter.examples.payment_gateways.checkout import checkout +from patterns.structural.adapter.examples.payment_gateways.main import main from patterns.structural.adapter.examples.payment_gateways.vendors import ( PayPalLikeGateway, StripeLikeClient, diff --git a/patterns/structural/bridge/README.md b/patterns/structural/bridge/README.md index ac09dfc..984534d 100644 --- a/patterns/structural/bridge/README.md +++ b/patterns/structural/bridge/README.md @@ -26,5 +26,5 @@ alternative** — composition with dependency injection *is* the bridge. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.bridge.examples.notification_center +uv run python -m patterns.structural.bridge.examples.notification_center.main ``` diff --git a/patterns/structural/bridge/docs/implementation.md b/patterns/structural/bridge/docs/implementation.md index a9c92ff..915f054 100644 --- a/patterns/structural/bridge/docs/implementation.md +++ b/patterns/structural/bridge/docs/implementation.md @@ -77,5 +77,5 @@ notifier.alert("critical", "db pool exhausted") alerts and digests for three teams over three transports — run it with: ```bash -uv run python -m patterns.structural.bridge.examples.notification_center +uv run python -m patterns.structural.bridge.examples.notification_center.main ``` diff --git a/patterns/structural/bridge/examples/notification_center/__main__.py b/patterns/structural/bridge/examples/notification_center/main.py similarity index 100% rename from patterns/structural/bridge/examples/notification_center/__main__.py rename to patterns/structural/bridge/examples/notification_center/main.py diff --git a/patterns/structural/bridge/tests/test_notification_center.py b/patterns/structural/bridge/tests/test_notification_center.py index 24fb6b2..732186c 100644 --- a/patterns/structural/bridge/tests/test_notification_center.py +++ b/patterns/structural/bridge/tests/test_notification_center.py @@ -4,11 +4,11 @@ import pytest -from patterns.structural.bridge.examples.notification_center.__main__ import main from patterns.structural.bridge.examples.notification_center.center import ( NotificationCenter, TeamChannel, ) +from patterns.structural.bridge.examples.notification_center.main import main from patterns.structural.bridge.pattern import EmailTransport, SlackTransport, SmsTransport diff --git a/patterns/structural/composite/README.md b/patterns/structural/composite/README.md index eff8a1a..1fbcc6f 100644 --- a/patterns/structural/composite/README.md +++ b/patterns/structural/composite/README.md @@ -26,5 +26,5 @@ for trees; keep the leaf's interface honest. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.composite.examples.org_chart +uv run python -m patterns.structural.composite.examples.org_chart.main ``` diff --git a/patterns/structural/composite/docs/implementation.md b/patterns/structural/composite/docs/implementation.md index cf3edb4..468877e 100644 --- a/patterns/structural/composite/docs/implementation.md +++ b/patterns/structural/composite/docs/implementation.md @@ -86,5 +86,5 @@ assert project.total() == 16 cost up a nested org chart — run it with: ```bash -uv run python -m patterns.structural.composite.examples.org_chart +uv run python -m patterns.structural.composite.examples.org_chart.main ``` diff --git a/patterns/structural/composite/examples/org_chart/__main__.py b/patterns/structural/composite/examples/org_chart/main.py similarity index 100% rename from patterns/structural/composite/examples/org_chart/__main__.py rename to patterns/structural/composite/examples/org_chart/main.py diff --git a/patterns/structural/composite/tests/test_org_chart.py b/patterns/structural/composite/tests/test_org_chart.py index 0237dbd..8521805 100644 --- a/patterns/structural/composite/tests/test_org_chart.py +++ b/patterns/structural/composite/tests/test_org_chart.py @@ -4,7 +4,7 @@ import pytest -from patterns.structural.composite.examples.org_chart.__main__ import main +from patterns.structural.composite.examples.org_chart.main import main from patterns.structural.composite.examples.org_chart.org import Department, Employee, OrgMetrics diff --git a/patterns/structural/decorator/README.md b/patterns/structural/decorator/README.md index b85a755..d76070d 100644 --- a/patterns/structural/decorator/README.md +++ b/patterns/structural/decorator/README.md @@ -27,5 +27,5 @@ language absorbed the pattern into `@decorator` syntax. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.decorator.examples.resilient_client +uv run python -m patterns.structural.decorator.examples.resilient_client.main ``` diff --git a/patterns/structural/decorator/docs/implementation.md b/patterns/structural/decorator/docs/implementation.md index 1fe7160..b8c29ab 100644 --- a/patterns/structural/decorator/docs/implementation.md +++ b/patterns/structural/decorator/docs/implementation.md @@ -72,5 +72,5 @@ measured around the whole hardened call at the edge, not baked between the layers — slot it outermost when you want it: ```bash -uv run python -m patterns.structural.decorator.examples.resilient_client +uv run python -m patterns.structural.decorator.examples.resilient_client.main ``` diff --git a/patterns/structural/decorator/examples/resilient_client/__main__.py b/patterns/structural/decorator/examples/resilient_client/main.py similarity index 100% rename from patterns/structural/decorator/examples/resilient_client/__main__.py rename to patterns/structural/decorator/examples/resilient_client/main.py diff --git a/patterns/structural/facade/README.md b/patterns/structural/facade/README.md index 55deb05..35ab5e5 100644 --- a/patterns/structural/facade/README.md +++ b/patterns/structural/facade/README.md @@ -26,5 +26,5 @@ is a module-level function, and the subsystem stays public beside it. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.facade.examples.order_checkout +uv run python -m patterns.structural.facade.examples.order_checkout.main ``` diff --git a/patterns/structural/facade/docs/implementation.md b/patterns/structural/facade/docs/implementation.md index 1606c74..d469570 100644 --- a/patterns/structural/facade/docs/implementation.md +++ b/patterns/structural/facade/docs/implementation.md @@ -61,5 +61,5 @@ Unusually for this catalog, the pattern package carries the whole domain: processing and the full-controls bypass around it: ```bash -uv run python -m patterns.structural.facade.examples.order_checkout +uv run python -m patterns.structural.facade.examples.order_checkout.main ``` diff --git a/patterns/structural/facade/examples/order_checkout/__main__.py b/patterns/structural/facade/examples/order_checkout/main.py similarity index 100% rename from patterns/structural/facade/examples/order_checkout/__main__.py rename to patterns/structural/facade/examples/order_checkout/main.py diff --git a/patterns/structural/flyweight/README.md b/patterns/structural/flyweight/README.md index 54e9830..67b8e2d 100644 --- a/patterns/structural/flyweight/README.md +++ b/patterns/structural/flyweight/README.md @@ -27,5 +27,5 @@ frozen, prefer an explicit factory over `__new__` tricks. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.flyweight.examples.glyph_styles +uv run python -m patterns.structural.flyweight.examples.glyph_styles.main ``` diff --git a/patterns/structural/flyweight/docs/implementation.md b/patterns/structural/flyweight/docs/implementation.md index f7fb1d3..0b07dc4 100644 --- a/patterns/structural/flyweight/docs/implementation.md +++ b/patterns/structural/flyweight/docs/implementation.md @@ -64,5 +64,5 @@ document at two live `Style` objects and pins both the identity sharing and the ceiling in tests: ```bash -uv run python -m patterns.structural.flyweight.examples.glyph_styles +uv run python -m patterns.structural.flyweight.examples.glyph_styles.main ``` diff --git a/patterns/structural/flyweight/examples/glyph_styles/__main__.py b/patterns/structural/flyweight/examples/glyph_styles/main.py similarity index 100% rename from patterns/structural/flyweight/examples/glyph_styles/__main__.py rename to patterns/structural/flyweight/examples/glyph_styles/main.py diff --git a/patterns/structural/proxy/README.md b/patterns/structural/proxy/README.md index 0378966..361f91c 100644 --- a/patterns/structural/proxy/README.md +++ b/patterns/structural/proxy/README.md @@ -26,5 +26,5 @@ power, the disguise is skin-deep. | [`tests/`](tests/) | Behavioral tests for the pattern and the mini-project | ```bash -uv run python -m patterns.structural.proxy.examples.db_gateway +uv run python -m patterns.structural.proxy.examples.db_gateway.main ``` diff --git a/patterns/structural/proxy/docs/implementation.md b/patterns/structural/proxy/docs/implementation.md index 9f07804..e85c85a 100644 --- a/patterns/structural/proxy/docs/implementation.md +++ b/patterns/structural/proxy/docs/implementation.md @@ -63,5 +63,5 @@ side can ignore. role-protection over a lazy warehouse connection: ```bash -uv run python -m patterns.structural.proxy.examples.db_gateway +uv run python -m patterns.structural.proxy.examples.db_gateway.main ``` diff --git a/patterns/structural/proxy/examples/db_gateway/__main__.py b/patterns/structural/proxy/examples/db_gateway/main.py similarity index 100% rename from patterns/structural/proxy/examples/db_gateway/__main__.py rename to patterns/structural/proxy/examples/db_gateway/main.py diff --git a/src/design_patterns/catalog.py b/src/design_patterns/catalog.py index 985b821..bb7a707 100644 --- a/src/design_patterns/catalog.py +++ b/src/design_patterns/catalog.py @@ -64,14 +64,14 @@ def docs(self) -> dict[str, Path]: } def examples(self) -> dict[str, Path]: - """Runnable mini-project packages: ``examples//`` with a ``__main__.py``.""" + """Runnable mini-project packages: ``examples//`` with a ``main.py``.""" examples_dir = self.path / "examples" if not examples_dir.is_dir(): return {} return { child.name: child for child in sorted(examples_dir.iterdir()) - if child.is_dir() and (child / "__main__.py").is_file() + if child.is_dir() and (child / "main.py").is_file() } def sources(self) -> dict[str, Path]: @@ -161,8 +161,12 @@ def _validate_shape(pattern: Pattern, readme: Path) -> None: raise CatalogError(f"{readme}: module unit's pattern/ package has no __init__.py") examples = pattern.examples() if not examples: + raise CatalogError(f"{readme}: module unit ships no runnable examples//main.py") + if dunder_mains := sorted(unit.rglob("__main__.py")): raise CatalogError( - f"{readme}: module unit ships no runnable examples//__main__.py" + f"{readme}: __main__.py is banned " + f"({[str(f.relative_to(unit)) for f in dunder_mains]}) — " + "entry points are main.py, run via python -m .main" ) for stray in _empty_inits(unit): raise CatalogError( diff --git a/src/design_patterns/mcp/sandbox.py b/src/design_patterns/mcp/sandbox.py index ffb9ffa..441f5cb 100644 --- a/src/design_patterns/mcp/sandbox.py +++ b/src/design_patterns/mcp/sandbox.py @@ -61,15 +61,15 @@ def _run_module(repo_root: str, module: str) -> RunResult: def run_example_package(catalog: Catalog, pattern_id: str, example: str) -> RunResult: - """Execute a unit's ``examples/`` mini-project package in a subprocess.""" + """Execute a unit's ``examples/`` mini-project in a subprocess.""" pattern = catalog.get(pattern_id) # KeyError for unknown ids -- by design examples = pattern.examples() if example not in examples: raise KeyError(f"{pattern_id} has no example {example!r} (has: {sorted(examples)})") path = examples[example] # resolved by the catalog, never by the caller - if not (path / "__main__.py").is_file(): # a real check, not an assert: survives python -O - raise FileNotFoundError(f"catalog names {path} but it has no __main__.py") + if not (path / "main.py").is_file(): # a real check, not an assert: survives python -O + raise FileNotFoundError(f"catalog names {path} but it has no main.py") repo_root = pattern.path.parents[2] - module = f"patterns.{pattern.group}.{pattern.slug}.examples.{example}" + module = f"patterns.{pattern.group}.{pattern.slug}.examples.{example}.main" return _run_module(str(repo_root), module) diff --git a/tests/conftest.py b/tests/conftest.py index 5c04ec4..9fffa75 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,7 +33,7 @@ def write_module_unit(root: Path) -> Path: (docs / f"{name}.md").write_text(f"# {name} of Thing\n") project = unit / "examples" / "demo" project.mkdir(parents=True) - (project / "__main__.py").write_text( + (project / "main.py").write_text( "from patterns.creational.thing.pattern.thing import build\n\nprint(build())\n" ) tests = unit / "tests" diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 5662fb0..e86281d 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -140,7 +140,7 @@ def _write_module_unit(root: Path, group: str, slug: str, frontmatter: str) -> P (docs / f"{name}.md").write_text(f"# {name}\n") project = unit / "examples" / "demo" project.mkdir(parents=True) - (project / "__main__.py").write_text("print('demo ran')\n") + (project / "main.py").write_text("print('demo ran')\n") tests = unit / "tests" tests.mkdir() (tests / "test_thing.py").write_text("def test_ok() -> None:\n assert True\n") @@ -163,7 +163,7 @@ def test_missing_doc_fails(self, tmp_path: Path) -> None: def test_no_example_fails(self, tmp_path: Path) -> None: unit = _write_module_unit(tmp_path, "creational", "thing", GOOD) - (unit / "examples" / "demo" / "__main__.py").unlink() + (unit / "examples" / "demo" / "main.py").unlink() with pytest.raises(CatalogError, match="no runnable examples"): load_catalog(tmp_path) @@ -173,6 +173,12 @@ def test_empty_init_in_example_fails(self, tmp_path: Path) -> None: with pytest.raises(CatalogError, match=r"delete empty __init__\.py"): load_catalog(tmp_path) + def test_dunder_main_is_banned(self, tmp_path: Path) -> None: + unit = _write_module_unit(tmp_path, "creational", "thing", GOOD) + (unit / "examples" / "demo" / "__main__.py").write_text("print('old style')\n") + with pytest.raises(CatalogError, match=r"__main__\.py is banned"): + load_catalog(tmp_path) + def test_empty_tests_fails(self, tmp_path: Path) -> None: unit = _write_module_unit(tmp_path, "creational", "thing", GOOD) (unit / "tests" / "test_thing.py").unlink() diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index 62f97c1..6ec4502 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -34,7 +34,7 @@ def test_failing_example_reports_not_raises(self, tmp_path: Path) -> None: # A crashing demo must come back as a RunResult, not an exception. root = tmp_path / "patterns" unit = write_module_unit(root) - (unit / "examples" / "demo" / "__main__.py").write_text( + (unit / "examples" / "demo" / "main.py").write_text( "import sys\n\nprint('about to fail')\nsys.exit(3)\n" ) result = run_example_package(load_catalog(root), "creational/thing", "demo")