Skip to content
Open
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
114 changes: 57 additions & 57 deletions pixi.lock

Large diffs are not rendered by default.

50 changes: 25 additions & 25 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,40 +138,40 @@ unsafe-fixes = false
[tool.ruff.lint]
select = ["ALL"]
extend-ignore = [
"ANN001", # missing type annotation
"ANN002", # missing type annotation for `*args`
"ANN003", # missing type annotation for `**kwargs`
"ANN201", # missing return type annotation for public function
"ANN202", # missing return type annotation for private function
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed in `return_annotation`
"D100", # missing docstring in public module
"D103", # missing docstring in public function
"D104", # missing docstring in public package
"COM812", # ignored to avoid conflict with formatter
"EM101", # exception must not use a string literal
"EM102", # exception must not use an f-string literal
"FIX002", # line contains a todo
"PLR0913", # too many arguments to function call
"PLR2004", # magic value used in comparison
"S101", # use of `assert` detected
"FBT001", # Boolean-typed positional argument in function definition
"FBT002", # Boolean default positional argument in function definition
"ISC001", # ignored to avoid conflict with formatter
"TRY003", # "long" messages outside exception class.

# Rules to be fixed in the future
# ----------------------------------------------------------------------------------

"FBT001",
"FBT002",


# Rules ignored to avoid conflict with formatter
# ----------------------------------------------------------------------------------
"COM812",
"ISC001",
]

[tool.ruff.lint.per-file-ignores]
"docs/source/conf.py" = ["INP001", "ERA001", "RUF100"]
"docs/*" = [
"D100", # missing docstring in public module
"D103", # missing docstring in public function
"D104", # missing docstring in public package
]
"src/dags/tree/__init__.py" = ["RUF022"]
"tests/*" = ["D401", "FBT001", "INP001", "PLC2401"]
"tests/*" = [
"ANN001", # missing type annotation
"ANN002", # missing type annotation for `*args`
"ANN003", # missing type annotation for `**kwargs`
"ANN201",
"ANN202",
"D100", # missing docstring in public module
"D103", # missing docstring in public function
"D104", # missing docstring in public package
"D401",
"FBT001",
"INP001",
"PLC2401",
"PLR2004", # magic value used in comparison
"S101", # use of `assert` detected
]
"tests/test_dag.py" = ["ARG001"]


Expand Down
2 changes: 2 additions & 0 deletions src/dags/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Tools to create executable DAGs from interdependent functions."""

from __future__ import annotations

from dags.annotations import get_annotations, get_free_arguments
Expand Down
29 changes: 29 additions & 0 deletions src/dags/annotations.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Utilities for extracting and verifying function annotations."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Literal, overload
Expand All @@ -14,6 +16,19 @@
def get_free_arguments(
func: Callable[..., Any],
) -> list[str]:
"""Get the names of all free (non-partialled) arguments of a function.

For regular functions, this returns all parameter names. For partial functions,
arguments that have been bound via keywords are excluded.

Args:
func: The function to inspect.

Returns
-------
A list of argument names that are not bound.

"""
arguments = list(inspect.signature(func).parameters)
if isinstance(func, functools.partial):
# arguments that are partialled by position are not part of the signature
Expand Down Expand Up @@ -84,6 +99,20 @@ def get_annotations(
def verify_annotations_are_strings(
annotations: dict[str, str], function_name: str
) -> None:
"""Verify that all type annotations are strings.

Raises NonStringAnnotationError with a helpful message if any annotation
is not a string, suggesting the use of `from __future__ import annotations`.

Args:
annotations: Dictionary of annotation names to their values.
function_name: Name of the function, used in error messages.

Raises
------
NonStringAnnotationError: If any annotation value is not a string.

"""
# If all annotations are strings, we are done.
if all(isinstance(v, str) for v in annotations.values()):
return
Expand Down
6 changes: 4 additions & 2 deletions src/dags/dag.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Core DAG functionality for combining interdependent functions."""

from __future__ import annotations

import functools
Expand Down Expand Up @@ -92,7 +94,7 @@ def return_annotation(self) -> str:
return self.annotations["return"]


def concatenate_functions(
def concatenate_functions( # noqa: PLR0913
functions: dict[str, Callable[..., Any]] | list[Callable[..., Any]],
targets: str | list[str] | None = None,
*,
Expand Down Expand Up @@ -220,7 +222,7 @@ def create_dag(
return dag


def _create_combined_function_from_dag(
def _create_combined_function_from_dag( # noqa: PLR0913
dag: nx.DiGraph[str],
functions: dict[str, Callable[..., Any]] | list[Callable[..., Any]],
targets: str | list[str] | None,
Expand Down
2 changes: 2 additions & 0 deletions src/dags/output.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Output format converters for concatenated functions."""

from __future__ import annotations

import functools
Expand Down
6 changes: 4 additions & 2 deletions src/dags/signature.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Utilities for function signature manipulation."""

from __future__ import annotations

import functools
Expand Down Expand Up @@ -167,7 +169,7 @@ def _fail_if_duplicated_arguments(
) -> None:
problematic = present_args & present_kwargs
if problematic:
s = "s" if len(problematic) >= 2 else ""
s = "s" if len(problematic) >= 2 else "" # noqa: PLR2004
problem_str = ", ".join(list(problematic))
msg = f"{funcname}() got multiple values for argument{s} {problem_str}"
raise InvalidFunctionArgumentsError(msg)
Expand All @@ -178,7 +180,7 @@ def _fail_if_invalid_keyword_arguments(
) -> None:
problematic = present_kwargs - valid_kwargs
if problematic:
s = "s" if len(problematic) >= 2 else ""
s = "s" if len(problematic) >= 2 else "" # noqa: PLR2004
problem_str = ", ".join(list(problematic))
msg = f"{funcname}() got unexpected keyword argument{s} {problem_str}"
raise InvalidFunctionArgumentsError(msg)
Expand Down
2 changes: 1 addition & 1 deletion src/dags/tree/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)


def fail_if_paths_are_invalid(
def fail_if_paths_are_invalid( # noqa: PLR0913
functions: NestedFunctionDict | None = None,
abs_qnames_functions: QNameFunctionDict | None = None,
data_tree: NestedStructureDict | None = None,
Expand Down
2 changes: 2 additions & 0 deletions src/dags/typing.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Type definitions used across the dags package."""

from __future__ import annotations

from typing import TYPE_CHECKING, ParamSpec, TypeVar
Expand Down