|
12 | 12 | from unittest import mock |
13 | 13 |
|
14 | 14 | import base_cli |
| 15 | +from base_cli import _lifecycle_install |
15 | 16 | from base_cli._runtime import RuntimeDirectoryError |
16 | 17 | from base_cli.testing import invoke |
17 | 18 |
|
@@ -63,6 +64,105 @@ def count_cleanup() -> None: |
63 | 64 |
|
64 | 65 | @unittest.skipUnless(importlib.util.find_spec("click"), "Click is not installed") |
65 | 66 | class ClickTreeAttachmentTests(unittest.TestCase): |
| 67 | + def test_lifecycle_metadata_is_keyed_by_context_identity(self) -> None: |
| 68 | + class FakeContext: |
| 69 | + def __init__(self) -> None: |
| 70 | + self.meta: dict[object, Any] = {} |
| 71 | + |
| 72 | + def get_parameter_source(self, name: str) -> None: |
| 73 | + del name |
| 74 | + return None |
| 75 | + |
| 76 | + first = FakeContext() |
| 77 | + second = FakeContext() |
| 78 | + parameter = type("Parameter", (), {"name": "environment"})() |
| 79 | + |
| 80 | + _lifecycle_install._capture_lifecycle_option(first, parameter, "first", key="environment") |
| 81 | + _lifecycle_install._capture_lifecycle_option(second, parameter, "second", key="environment") |
| 82 | + |
| 83 | + captures = first.meta[next(key for key in first.meta if key is not None)] |
| 84 | + self.assertIn(first, captures) |
| 85 | + self.assertNotIn(id(first), captures) |
| 86 | + self.assertEqual(captures[first]["environment"].value, "first") |
| 87 | + second_captures = second.meta[next(key for key in second.meta if key is not None)] |
| 88 | + self.assertIn(second, second_captures) |
| 89 | + self.assertEqual(second_captures[second]["environment"].value, "second") |
| 90 | + |
| 91 | + def test_chained_click_contexts_keep_lifecycle_values_through_teardown(self) -> None: |
| 92 | + import click |
| 93 | + |
| 94 | + observed: list[tuple[str, str | None, int]] = [] |
| 95 | + closed: list[str] = [] |
| 96 | + |
| 97 | + def capture_environment(click_context: Any, parameter: Any, value: Any) -> Any: |
| 98 | + return _lifecycle_install._capture_lifecycle_option( |
| 99 | + click_context, |
| 100 | + parameter, |
| 101 | + value, |
| 102 | + key="environment", |
| 103 | + ) |
| 104 | + |
| 105 | + @click.group(name="pipeline", chain=True) |
| 106 | + def pipeline() -> None: |
| 107 | + pass |
| 108 | + |
| 109 | + @pipeline.command(name="first") |
| 110 | + @click.option("--environment", callback=capture_environment) |
| 111 | + def first(environment: str | None) -> None: |
| 112 | + del environment |
| 113 | + click_context = click.get_current_context() |
| 114 | + captures = click_context.meta[_lifecycle_install._LIFECYCLE_CAPTURE_META_KEY] |
| 115 | + observed.append( |
| 116 | + ( |
| 117 | + "first", |
| 118 | + captures[click_context]["environment"].value, |
| 119 | + len(captures), |
| 120 | + ) |
| 121 | + ) |
| 122 | + click_context.call_on_close(lambda: closed.append("first")) |
| 123 | + |
| 124 | + @pipeline.command(name="second") |
| 125 | + @click.option("--environment", callback=capture_environment) |
| 126 | + def second(environment: str | None) -> None: |
| 127 | + del environment |
| 128 | + click_context = click.get_current_context() |
| 129 | + captures = click_context.meta[_lifecycle_install._LIFECYCLE_CAPTURE_META_KEY] |
| 130 | + observed.append( |
| 131 | + ( |
| 132 | + "second", |
| 133 | + captures[click_context]["environment"].value, |
| 134 | + len(captures), |
| 135 | + ) |
| 136 | + ) |
| 137 | + self.assertEqual(closed, ["first"]) |
| 138 | + click_context.call_on_close(lambda: closed.append("second")) |
| 139 | + |
| 140 | + app = base_cli.App(name="pipeline", log_to_file=False) |
| 141 | + app.attach(pipeline) |
| 142 | + |
| 143 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 144 | + result = invoke( |
| 145 | + app, |
| 146 | + [ |
| 147 | + "first", |
| 148 | + "--environment", |
| 149 | + "first-env", |
| 150 | + "second", |
| 151 | + "--environment", |
| 152 | + "second-env", |
| 153 | + ], |
| 154 | + home=Path(tmpdir), |
| 155 | + ) |
| 156 | + |
| 157 | + self.assertEqual(result.exit_code, 0, result.output) |
| 158 | + self.assertEqual( |
| 159 | + [(name, value) for name, value, _capture_count in observed], |
| 160 | + [("first", "first-env"), ("second", "second-env")], |
| 161 | + ) |
| 162 | + self.assertGreaterEqual(observed[0][2], 2) |
| 163 | + self.assertEqual(observed[0][2], observed[1][2]) |
| 164 | + self.assertEqual(closed, ["first", "second"]) |
| 165 | + |
66 | 166 | def test_prebuilt_single_command_preserves_click_contract_and_lifecycle(self) -> None: |
67 | 167 | import click |
68 | 168 |
|
|
0 commit comments