Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codemod for pos-only self parameters #218

Merged
merged 2 commits into from
Nov 28, 2024
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
1 change: 1 addition & 0 deletions codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ where `$NAME` is the name is the name of the codemod, which can be one of:

- `AnnotateMissing` - Sets the default return type to `None`, and sets the other missing annotations to `scipy._typing.Untyped`.
- `FixTrailingComma` - Adds a trailing comma to parameters that don't fit on one line, so that ruff formats them correctly.
- `PosOnlySelf` - nsures `self` is a positional-only parameter.

> [!NOTE]
> The codemods require `libcst`, which is installable through the **optional** `codegen` dependency group:
Expand Down
50 changes: 48 additions & 2 deletions codegen/mods.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, /, context: CodemodContext) -> None:
super().__init__(context)

@override
def leave_Module(self, original_node: cst.Module, updated_node: cst.Module) -> cst.Module:
def leave_Module(self, /, original_node: cst.Module, updated_node: cst.Module) -> cst.Module:
if not self.updated:
raise SkipFile("unchanged")

Expand Down Expand Up @@ -100,7 +100,7 @@ class FixTrailingComma(_BaseMod):
DESCRIPTION = "Adds a trailing comma to parameters that don't fit on one line, so that ruff formats them correctly."

@override
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef:
def leave_FunctionDef(self, /, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef:
params = updated_node.params.params

if (
Expand All @@ -119,3 +119,49 @@ def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.Fu
return updated_node.with_deep_changes(params[-1], comma=cst.Comma())

return updated_node


@final
class PosOnlySelf(_BaseMod):
DESCRIPTION = "Ensures `self` is a positional-only parameter."

class_depth: int

@override
def __init__(self, /, context: CodemodContext) -> None:
self.class_depth = 0
super().__init__(context)

@override
def visit_ClassDef(self, node: cst.ClassDef) -> bool | None:
self.class_depth += 1
return super().visit_ClassDef(node)

@override
def leave_ClassDef(self, original_node: cst.ClassDef, updated_node: cst.ClassDef) -> cst.ClassDef:
self.class_depth -= 1
return updated_node

@override
def leave_FunctionDef(self, /, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef:
params = updated_node.params
if not self.class_depth or params.posonly_params or not params.params:
return updated_node

decorators = {get_full_name_for_node(d.decorator) for d in updated_node.decorators}
if "staticmethod" in decorators or "classmethod" in decorators:
return updated_node

# TODO(jorenham): pos-only `@property` setter value parameters

self_param = params.params[0]
if self_param.name.value != "self":
return updated_node

self.updated += 1
return updated_node.with_changes(
params=params.with_changes(
params=params.params[1:],
posonly_params=(self_param,),
)
)
2 changes: 1 addition & 1 deletion scipy-stubs/_lib/_ccallback.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class _CFFIType(Protocol):
def is_integer_type(self, /) -> bool: ...
def has_c_name(self, /) -> bool: ...
def get_c_name(self, /, replace_with: str = "", context: str = "a C file", quals: int = 0) -> str: ...
def get_cached_btype(self, ffi: object, finishlist: list[object], can_delay: bool = False) -> _CFFIBackendType: ...
def get_cached_btype(self, /, ffi: object, finishlist: list[object], can_delay: bool = False) -> _CFFIBackendType: ...

# virtual
def build_backend_type(self, /, ffi: object, finishlist: list[object]) -> _CFFIBackendType: ...
Expand Down
22 changes: 11 additions & 11 deletions scipy-stubs/_lib/_docscrape.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ class Parameter(NamedTuple):
class Reader:
def __init__(self, /, data: str | list[str]) -> None: ...
def __getitem__(self, n: op.CanIndex, /) -> str: ...
def reset(self) -> None: ...
def read(self) -> str: ...
def seek_next_non_empty_line(self) -> None: ...
def eof(self) -> bool: ...
def read_to_condition(self, condition_func: Callable[[str], object]) -> list[str]: ...
def read_to_next_empty_line(self) -> list[str]: ...
def read_to_next_unindented_line(self) -> list[str]: ...
def peek(self, n: int = 0) -> str: ...
def is_empty(self) -> bool: ...
def reset(self, /) -> None: ...
def read(self, /) -> str: ...
def seek_next_non_empty_line(self, /) -> None: ...
def eof(self, /) -> bool: ...
def read_to_condition(self, /, condition_func: Callable[[str], object]) -> list[str]: ...
def read_to_next_empty_line(self, /) -> list[str]: ...
def read_to_next_unindented_line(self, /) -> list[str]: ...
def peek(self, /, n: int = 0) -> str: ...
def is_empty(self, /) -> bool: ...

class NumpyDocString(Mapping[str, _SectionValue]):
empty_description: ClassVar[str] = ".."
Expand Down Expand Up @@ -58,9 +58,9 @@ class ClassDoc(NumpyDocString):
extra_public_methods: ClassVar[Sequence[str]]
show_inherited_members: Final[bool]
@property
def methods(self) -> list[str]: ...
def methods(self, /) -> list[str]: ...
@property
def properties(self) -> list[str]: ...
def properties(self, /) -> list[str]: ...
def __init__(
self,
/,
Expand Down
14 changes: 7 additions & 7 deletions scipy-stubs/_lib/_testutils.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class _TestPythranFunc:
arguments: dict[int, tuple[onp.Array, list[np.generic]]]
partialfunc: Callable[..., object] | None
expected: object | None
def setup_method(self) -> None: ...
def get_optional_args(self, func: Callable[..., object]) -> dict[str, object]: ...
def get_max_dtype_list_length(self) -> int: ...
def get_dtype(self, dtype_list: Sequence[np.generic], dtype_idx: int) -> np.generic: ...
def test_all_dtypes(self) -> None: ...
def test_views(self) -> None: ...
def test_strided(self) -> None: ...
def setup_method(self, /) -> None: ...
def get_optional_args(self, /, func: Callable[..., object]) -> dict[str, object]: ...
def get_max_dtype_list_length(self, /) -> int: ...
def get_dtype(self, /, dtype_list: Sequence[np.generic], dtype_idx: int) -> np.generic: ...
def test_all_dtypes(self, /) -> None: ...
def test_views(self, /) -> None: ...
def test_strided(self, /) -> None: ...

def check_free_memory(free_mb: float) -> None: ...
10 changes: 5 additions & 5 deletions scipy-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ NormalizationMode: TypeAlias = Literal["backward", "ortho", "forward"]
@type_check_only
class _FortranFunction(Protocol):
@property
def dtype(self) -> np.dtype[np.number[Any]]: ...
def dtype(self, /) -> np.dtype[np.number[Any]]: ...
@property
def int_dtype(self) -> np.dtype[np.integer[Any]]: ...
def int_dtype(self, /) -> np.dtype[np.integer[Any]]: ...
@property
def module_name(self) -> LiteralString: ...
def module_name(self, /) -> LiteralString: ...
@property
def prefix(self) -> LiteralString: ...
def prefix(self, /) -> LiteralString: ...
@property
def typecode(self) -> LiteralString: ...
def typecode(self, /) -> LiteralString: ...
def __call__(self, /, *args: object, **kwargs: object) -> object: ...
8 changes: 5 additions & 3 deletions scipy-stubs/integrate/_ivp/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class OdeSolver:
@overload
def __init__(
self,
/,
fun: Callable[[float, onp.ArrayND[np.float64]], onp.ToFloatND],
t0: onp.ToFloatND,
y0: onp.ToFloatND,
Expand All @@ -36,6 +37,7 @@ class OdeSolver:
@overload
def __init__(
self,
/,
fun: Callable[[float, onp.ArrayND[np.float64 | np.complex128]], onp.ToComplexND],
t0: onp.ToFloat,
y0: onp.ToComplexND,
Expand All @@ -44,9 +46,9 @@ class OdeSolver:
support_complex: Literal[True],
) -> None: ...
@property
def step_size(self) -> float | None: ...
def step(self) -> str | None: ...
def dense_output(self) -> ConstantDenseOutput: ...
def step_size(self, /) -> float | None: ...
def step(self, /) -> str | None: ...
def dense_output(self, /) -> ConstantDenseOutput: ...

class DenseOutput:
t_old: Final[float]
Expand Down
14 changes: 7 additions & 7 deletions scipy-stubs/interpolate/_bsplines.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ class BSpline:
extrapolate: Untyped
axis: Untyped
@property
def tck(self) -> Untyped: ...
def __init__(self, t: Untyped, c: Untyped, k: Untyped, extrapolate: bool = True, axis: int = 0) -> None: ...
def __call__(self, x: Untyped, nu: int = 0, extrapolate: Untyped | None = None) -> Untyped: ...
def derivative(self, nu: int = 1) -> Untyped: ...
def antiderivative(self, nu: int = 1) -> Untyped: ...
def integrate(self, a: Untyped, b: Untyped, extrapolate: Untyped | None = None) -> Untyped: ...
def insert_knot(self, x: Untyped, m: int = 1) -> Untyped: ...
def tck(self, /) -> Untyped: ...
def __init__(self, /, t: Untyped, c: Untyped, k: Untyped, extrapolate: bool = True, axis: int = 0) -> None: ...
def __call__(self, /, x: Untyped, nu: int = 0, extrapolate: Untyped | None = None) -> Untyped: ...
def derivative(self, /, nu: int = 1) -> Untyped: ...
def antiderivative(self, /, nu: int = 1) -> Untyped: ...
def integrate(self, /, a: Untyped, b: Untyped, extrapolate: Untyped | None = None) -> Untyped: ...
def insert_knot(self, /, x: Untyped, m: int = 1) -> Untyped: ...
@classmethod
def basis_element(cls, t: Untyped, extrapolate: bool = True) -> Untyped: ...
@classmethod
Expand Down
9 changes: 7 additions & 2 deletions scipy-stubs/interpolate/_cubic.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class CubicHermiteSpline(PPoly[_CT_co]):
@overload
def __init__(
self: CubicHermiteSpline[np.float64],
/,
x: onp.ToFloat1D,
y: onp.ToFloatND,
dydx: onp.ToFloatND,
Expand All @@ -36,6 +37,7 @@ class CubicHermiteSpline(PPoly[_CT_co]):
@overload
def __init__(
self: CubicHermiteSpline[np.float64 | np.complex128],
/,
x: onp.ToFloat1D,
y: onp.ToComplexND,
dydx: onp.ToComplexND,
Expand All @@ -44,11 +46,12 @@ class CubicHermiteSpline(PPoly[_CT_co]):
) -> None: ...

class PchipInterpolator(CubicHermiteSpline[np.float64]):
def __init__(self, x: onp.ToFloat1D, y: onp.ToFloatND, axis: _ToAxis = 0, extrapolate: bool | None = None) -> None: ...
def __init__(self, /, x: onp.ToFloat1D, y: onp.ToFloatND, axis: _ToAxis = 0, extrapolate: bool | None = None) -> None: ...

class Akima1DInterpolator(CubicHermiteSpline[np.float64]):
def __init__(
self,
/,
x: onp.ToFloat1D,
y: onp.ToFloatND,
axis: _ToAxis = 0,
Expand All @@ -59,7 +62,7 @@ class Akima1DInterpolator(CubicHermiteSpline[np.float64]):

# the following (class)methods will raise `NotImplementedError` when called
@override
def extend(self, c: object, x: object, right: object = True) -> Never: ...
def extend(self, /, c: object, x: object, right: object = True) -> Never: ...
@classmethod
@override
def from_spline(cls, tck: object, extrapolate: object = ...) -> Never: ...
Expand All @@ -71,6 +74,7 @@ class CubicSpline(CubicHermiteSpline[_CT_co], Generic[_CT_co]):
@overload
def __init__(
self: CubicSpline[np.float64],
/,
x: onp.ToFloat1D,
y: onp.ToFloatND,
axis: _ToAxis = 0,
Expand All @@ -80,6 +84,7 @@ class CubicSpline(CubicHermiteSpline[_CT_co], Generic[_CT_co]):
@overload
def __init__(
self: CubicSpline[np.float64 | np.complex128],
/,
x: onp.ToFloat1D,
y: onp.ToComplexND,
axis: _ToAxis = 0,
Expand Down
Loading