Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ lint.per-file-ignores."src/usethis/_ui/interface/**/*.py" = [ "PLR0913" ]
lint.per-file-ignores."tests/**" = [ "D", "INP", "S603", "TC" ]
lint.flake8-bugbear.extend-immutable-calls = [ "typer.Argument", "typer.Option" ]
lint.flake8-builtins.strict-checking = true
lint.flake8-tidy-imports.banned-api."functools.singledispatch".msg = "Use if-branch isinstance logic instead of singledispatch."
lint.flake8-tidy-imports.banned-api."functools.singledispatchmethod".msg = "Use if-branch isinstance logic instead of singledispatchmethod."
lint.flake8-tidy-imports.banned-api."typer.testing.CliRunner".msg = "Use `usethis._test.CliRunner` instead of `typer.CliRunner`."
lint.flake8-type-checking.quote-annotations = true
lint.flake8-type-checking.runtime-evaluated-base-classes = [ "pydantic.BaseModel" ]
Expand Down
19 changes: 6 additions & 13 deletions src/usethis/_file/ini/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import configparser
import re
from functools import singledispatch
from typing import TYPE_CHECKING

from configupdater import ConfigUpdater as INIDocument
Expand Down Expand Up @@ -533,21 +532,15 @@ def remove_from_list(self, *, keys: Sequence[Key], values: list[str]) -> None:
self.commit(root)


@singledispatch
def _as_dict(
value: INIDocument | Section,
) -> dict[str, dict[str, Any]] | dict[str, Any]:
raise NotImplementedError


@_as_dict.register(INIDocument)
def _(value: INIDocument) -> dict[str, dict[str, Any]]:
return {k: _as_dict(v) for k, v in value.items()}


@_as_dict.register(Section)
def _(value: Section) -> dict[str, Any]:
return {option.key: option.value for option in value.iter_options()}
if isinstance(value, INIDocument):
return {k: _as_dict(v) for k, v in value.items()}
elif isinstance(value, Section):
return {option.key: option.value for option in value.iter_options()}
else:
assert_never(value)


def _remove_option(updater: INIDocument, section_key: str, option_key: str) -> bool:
Expand Down
271 changes: 107 additions & 164 deletions src/usethis/_integrations/ci/bitbucket/pipeweld.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from functools import singledispatch
from typing import TYPE_CHECKING
from uuid import uuid4

from typing_extensions import assert_never
Expand All @@ -16,7 +16,10 @@
)
from usethis._integrations.ci.bitbucket.schema_utils import step1tostep
from usethis._integrations.ci.bitbucket.yaml import BitbucketPipelinesYAMLManager
from usethis._pipeweld.ops import InsertParallel, InsertSuccessor, Instruction
from usethis._pipeweld.ops import InsertParallel, InsertSuccessor

if TYPE_CHECKING:
from usethis._pipeweld.ops import Instruction


def get_pipeweld_step(step: schema.Step) -> str:
Expand Down Expand Up @@ -48,54 +51,45 @@ def get_pipeweld_pipeline_from_default(
)


@singledispatch
def get_pipeweld_object(
item: schema.StepItem | schema.ParallelItem | schema.StageItem,
) -> (
str | usethis._pipeweld.containers.Parallel | usethis._pipeweld.containers.DepGroup
):
raise NotImplementedError


@get_pipeweld_object.register
def _(item: schema.StepItem):
return get_pipeweld_step(item.step)


@get_pipeweld_object.register
def _(item: schema.ParallelItem):
parallel_steps: set[str] = set()

if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
step_items = item.parallel.root.root
elif isinstance(item.parallel.root, schema.ParallelExpanded):
step_items = item.parallel.root.steps.root
if isinstance(item, schema.StepItem):
return get_pipeweld_step(item.step)
elif isinstance(item, schema.ParallelItem):
parallel_steps: set[str] = set()

if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
step_items = item.parallel.root.root
elif isinstance(item.parallel.root, schema.ParallelExpanded):
step_items = item.parallel.root.steps.root
else:
assert_never(item.parallel.root)

for step_item in step_items:
parallel_steps.add(get_pipeweld_step(step_item.step))

return usethis._pipeweld.containers.Parallel(frozenset(parallel_steps))
elif isinstance(item, schema.StageItem):
depgroup_steps: list[str] = []

if item.stage.name is not None:
name = item.stage.name
else:
assert_never(item.parallel.root)

for step_item in step_items:
parallel_steps.add(get_pipeweld_step(step_item.step))

return usethis._pipeweld.containers.Parallel(frozenset(parallel_steps))
name = str(f"Unnamed Stage {uuid4()}")

for step in item.stage.steps:
depgroup_steps.append(get_pipeweld_step(step1tostep(step)))

@get_pipeweld_object.register
def _(item: schema.StageItem):
depgroup_steps: list[str] = []

if item.stage.name is not None:
name = item.stage.name
return usethis._pipeweld.containers.DepGroup(
series=usethis._pipeweld.containers.series(*depgroup_steps),
config_group=name,
)
else:
name = str(f"Unnamed Stage {uuid4()}")

for step in item.stage.steps:
depgroup_steps.append(get_pipeweld_step(step1tostep(step)))

return usethis._pipeweld.containers.DepGroup(
series=usethis._pipeweld.containers.series(*depgroup_steps),
config_group=name,
)
assert_never(item)


def apply_pipeweld_instruction(
Expand Down Expand Up @@ -218,7 +212,6 @@ def _extract_step_from_items(
raise MissingStepError(msg)


@singledispatch
def _extract_step_from_item(
item: schema.StepItem | schema.ParallelItem | schema.StageItem,
*,
Expand All @@ -227,26 +220,25 @@ def _extract_step_from_item(
idx: int,
) -> schema.Step | None:
"""Extract a step from an item, potentially modifying the items list."""
raise NotImplementedError


@_extract_step_from_item.register
def _(
item: schema.StepItem,
*,
step_name: str,
items: list[schema.StepItem | schema.ParallelItem | schema.StageItem],
idx: int,
) -> schema.Step | None:
if get_pipeweld_step(item.step) == step_name:
# Remove this item from the list
items.pop(idx)
return item.step
return None
if isinstance(item, schema.StepItem):
if get_pipeweld_step(item.step) == step_name:
# Remove this item from the list
items.pop(idx)
return item.step
return None
elif isinstance(item, schema.ParallelItem):
return _extract_step_from_parallel_item(
item, step_name=step_name, items=items, idx=idx
)
elif isinstance(item, schema.StageItem):
# We don't extract steps from within stages as they represent deployment
# stages and their internal structure should be preserved.
return None
else:
assert_never(item)


@_extract_step_from_item.register
def _(
def _extract_step_from_parallel_item(
item: schema.ParallelItem,
*,
step_name: str,
Expand Down Expand Up @@ -278,21 +270,6 @@ def _(
return None


@_extract_step_from_item.register
def _(
# https://github.com/astral-sh/ruff/issues/18654
item: schema.StageItem, # noqa: ARG001
*,
step_name: str, # noqa: ARG001
items: list[schema.StepItem | schema.ParallelItem | schema.StageItem], # noqa: ARG001
idx: int, # noqa: ARG001
) -> schema.Step | None:
# We don't extract steps from within stages as they represent deployment
# stages and their internal structure should be preserved.
return None


@singledispatch
def _insert_parallel_step(
item: schema.StepItem | schema.ParallelItem | schema.StageItem,
*,
Expand All @@ -305,103 +282,69 @@ def _insert_parallel_step(
This function handles the logic of converting a single step to a parallel block
or adding to an existing parallel block.
"""
raise NotImplementedError


@_insert_parallel_step.register
def _(
item: schema.StepItem,
*,
items: list[schema.StepItem | schema.ParallelItem | schema.StageItem],
idx: int,
step_to_insert: schema.Step,
) -> None:
# Replace the single step with a parallel block containing both steps
parallel_item = schema.ParallelItem(
parallel=schema.Parallel(
schema.ParallelSteps(
[
schema.StepItem(step=item.step),
schema.StepItem(step=step_to_insert),
]
if isinstance(item, schema.StepItem):
# Replace the single step with a parallel block containing both steps
parallel_item = schema.ParallelItem(
parallel=schema.Parallel(
schema.ParallelSteps(
[
schema.StepItem(step=item.step),
schema.StepItem(step=step_to_insert),
]
)
)
)
)
items[idx] = parallel_item


@_insert_parallel_step.register
def _(
item: schema.ParallelItem,
*,
# https://github.com/astral-sh/ruff/issues/18654
items: list[schema.StepItem | schema.ParallelItem | schema.StageItem], # noqa: ARG001
idx: int, # noqa: ARG001
step_to_insert: schema.Step,
) -> None:
if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
# Add to the existing list of parallel steps
item.parallel.root.root.append(schema.StepItem(step=step_to_insert))
elif isinstance(item.parallel.root, schema.ParallelExpanded):
# Add to the expanded parallel steps
item.parallel.root.steps.root.append(schema.StepItem(step=step_to_insert))
else:
assert_never(item.parallel.root)


@_insert_parallel_step.register
def _(
item: schema.StageItem,
*,
items: list[schema.StepItem | schema.ParallelItem | schema.StageItem],
idx: int,
step_to_insert: schema.Step,
) -> None:
# StageItems are trickier since they aren't supported in ParallelSteps. But we
# never need to add them in practice anyway. The only reason this is really here
# is for type safety.
raise NotImplementedError
items[idx] = parallel_item
elif isinstance(item, schema.ParallelItem):
if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
# Add to the existing list of parallel steps
item.parallel.root.root.append(schema.StepItem(step=step_to_insert))
elif isinstance(item.parallel.root, schema.ParallelExpanded):
# Add to the expanded parallel steps
item.parallel.root.steps.root.append(
schema.StepItem(step=step_to_insert)
)
else:
assert_never(item.parallel.root)
elif isinstance(item, schema.StageItem):
# StageItems are trickier since they aren't supported in ParallelSteps. But we
# never need to add them in practice anyway. The only reason this is really here
# is for type safety.
raise NotImplementedError
else:
assert_never(item)


@singledispatch
def _is_insertion_necessary(
item: schema.StepItem | schema.ParallelItem | schema.StageItem,
*,
instruction: Instruction,
) -> bool:
raise NotImplementedError


@_is_insertion_necessary.register
def _(item: schema.StepItem, *, instruction: Instruction):
return get_pipeweld_step(item.step) == instruction.after


@_is_insertion_necessary.register
def _(item: schema.ParallelItem, *, instruction: Instruction):
if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
step_items = item.parallel.root.root
elif isinstance(item.parallel.root, schema.ParallelExpanded):
step_items = item.parallel.root.steps.root
else:
assert_never(item.parallel.root)

for step_item in step_items:
if get_pipeweld_step(step_item.step) == instruction.after:
if isinstance(item, schema.StepItem):
return get_pipeweld_step(item.step) == instruction.after
elif isinstance(item, schema.ParallelItem):
if item.parallel is not None:
if isinstance(item.parallel.root, schema.ParallelSteps):
step_items = item.parallel.root.root
elif isinstance(item.parallel.root, schema.ParallelExpanded):
step_items = item.parallel.root.steps.root
else:
assert_never(item.parallel.root)

for step_item in step_items:
if get_pipeweld_step(step_item.step) == instruction.after:
return True
return False
elif isinstance(item, schema.StageItem):
step1s = item.stage.steps.copy()

for step1 in step1s:
step = step1tostep(step1)

if get_pipeweld_step(step) == instruction.after:
return True
return False


@_is_insertion_necessary.register
def _(item: schema.StageItem, *, instruction: Instruction):
step1s = item.stage.steps.copy()

for step1 in step1s:
step = step1tostep(step1)

if get_pipeweld_step(step) == instruction.after:
return True

return False
return False
else:
assert_never(item)
Loading