Skip to content

Commit

Permalink
Merge pull request #4643 from Repiteo/future-annotations
Browse files Browse the repository at this point in the history
Integrate `from __future__ import annotations`
  • Loading branch information
bdbaddog authored Nov 16, 2024
2 parents 630ee1c + 3829f45 commit 45a4d57
Show file tree
Hide file tree
Showing 47 changed files with 351 additions and 281 deletions.
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER

From Thaddeus Crews:
- Ruff/Mypy: Excluded items now synced.
- Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to
detect and upgrade legacy type-hint syntax.
- Removed "SCons.Util.sctyping.py", as the functionality can now be substituted
via top-level `from __future__ import annotations`.

From Alex James:
- On Darwin, PermissionErrors are now handled while trying to access
Expand Down
6 changes: 6 additions & 0 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ DEVELOPMENT

- Ruff/Mypy: Excluded items now synced.

- Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to
detect and upgrade legacy type-hint syntax.

- Removed "SCons.Util.sctyping.py", as the functionality can now be substituted
via top-level `from __future__ import annotations`.

Thanks to the following contributors listed below for their contributions to this release.
==========================================================================================
.. code-block:: text
Expand Down
86 changes: 44 additions & 42 deletions SCons/Action.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
"""

from __future__ import annotations

import inspect
import os
import pickle
Expand All @@ -109,7 +111,7 @@
from abc import ABC, abstractmethod
from collections import OrderedDict
from subprocess import DEVNULL, PIPE
from typing import List, Optional, Tuple
from typing import TYPE_CHECKING

import SCons.Debug
import SCons.Errors
Expand All @@ -120,7 +122,9 @@
from SCons.Debug import logInstanceCreation
from SCons.Subst import SUBST_CMD, SUBST_RAW, SUBST_SIG
from SCons.Util import is_String, is_List
from SCons.Util.sctyping import ExecutorType

if TYPE_CHECKING:
from SCons.Executor import Executor

class _null:
pass
Expand Down Expand Up @@ -481,9 +485,7 @@ def _do_create_action(act, kw):
return None


# TODO: from __future__ import annotations once we get to Python 3.7 base,
# to avoid quoting the defined-later classname
def _do_create_list_action(act, kw) -> "ListAction":
def _do_create_list_action(act, kw) -> ListAction:
"""A factory for list actions.
Convert the input list *act* into Actions and then wrap them in a
Expand Down Expand Up @@ -529,7 +531,7 @@ def __call__(
show=_null,
execute=_null,
chdir=_null,
executor: Optional[ExecutorType] = None,
executor: Executor | None = None,
):
raise NotImplementedError

Expand All @@ -541,15 +543,15 @@ def no_batch_key(self, env, target, source):

batch_key = no_batch_key

def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str:
def genstring(self, target, source, env, executor: Executor | None = None) -> str:
return str(self)

@abstractmethod
def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
raise NotImplementedError

@abstractmethod
def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
raise NotImplementedError

def get_contents(self, target, source, env):
Expand Down Expand Up @@ -601,10 +603,10 @@ def presub_lines(self, env):
self.presub_env = None # don't need this any more
return lines

def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_varlist(self, target, source, env, executor: Executor | None = None):
return self.varlist

def get_targets(self, env, executor: Optional[ExecutorType]):
def get_targets(self, env, executor: Executor | None):
"""
Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
by this action.
Expand Down Expand Up @@ -658,7 +660,7 @@ def __call__(self, target, source, env,
show=_null,
execute=_null,
chdir=_null,
executor: Optional[ExecutorType] = None):
executor: Executor | None = None):
if not is_List(target):
target = [target]
if not is_List(source):
Expand Down Expand Up @@ -742,10 +744,10 @@ def __call__(self, target, source, env,
# an ABC like parent ActionBase, but things reach in and use it. It's
# not just unittests or we could fix it up with a concrete subclass there.

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
raise NotImplementedError

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
raise NotImplementedError


Expand Down Expand Up @@ -1010,7 +1012,7 @@ def __str__(self) -> str:
return str(self.cmd_list)


def process(self, target, source, env, executor=None, overrides: Optional[dict] = None) -> Tuple[List, bool, bool]:
def process(self, target, source, env, executor: Executor | None = None, overrides: dict | None = None) -> tuple[list, bool, bool]:
if executor:
result = env.subst_list(self.cmd_list, SUBST_CMD, executor=executor, overrides=overrides)
else:
Expand All @@ -1031,7 +1033,7 @@ def process(self, target, source, env, executor=None, overrides: Optional[dict]
pass
return result, ignore, silent

def strfunction(self, target, source, env, executor: Optional[ExecutorType] = None, overrides: Optional[dict] = None) -> str:
def strfunction(self, target, source, env, executor: Executor | None = None, overrides: dict | None = None) -> str:
if self.cmdstr is None:
return None
if self.cmdstr is not _null:
Expand All @@ -1046,7 +1048,7 @@ def strfunction(self, target, source, env, executor: Optional[ExecutorType] = No
return ''
return _string_from_cmd_list(cmd_list[0])

def execute(self, target, source, env, executor: Optional[ExecutorType] = None):
def execute(self, target, source, env, executor: Executor | None = None):
"""Execute a command action.
This will handle lists of commands as well as individual commands,
Expand Down Expand Up @@ -1108,7 +1110,7 @@ def execute(self, target, source, env, executor: Optional[ExecutorType] = None):
command=cmd_line)
return 0

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
Expand All @@ -1123,7 +1125,7 @@ def get_presig(self, target, source, env, executor: Optional[ExecutorType] = Non
return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
return env.subst_target_source(cmd, SUBST_SIG, target, source)

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
"""Return the implicit dependencies of this action's command line."""
icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
if is_String(icd) and icd[:1] == '$':
Expand All @@ -1145,7 +1147,7 @@ def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType
# lightweight dependency scanning.
return self._get_implicit_deps_lightweight(target, source, env, executor)

def _get_implicit_deps_lightweight(self, target, source, env, executor: Optional[ExecutorType]):
def _get_implicit_deps_lightweight(self, target, source, env, executor: Executor | None):
"""
Lightweight dependency scanning involves only scanning the first entry
in an action string, even if it contains &&.
Expand All @@ -1166,7 +1168,7 @@ def _get_implicit_deps_lightweight(self, target, source, env, executor: Optional
res.append(env.fs.File(d))
return res

def _get_implicit_deps_heavyweight(self, target, source, env, executor: Optional[ExecutorType],
def _get_implicit_deps_heavyweight(self, target, source, env, executor: Executor | None,
icd_int):
"""
Heavyweight dependency scanning involves scanning more than just the
Expand Down Expand Up @@ -1234,7 +1236,7 @@ def __init__(self, generator, kw) -> None:
self.varlist = kw.get('varlist', ())
self.targets = kw.get('targets', '$TARGETS')

def _generate(self, target, source, env, for_signature, executor: Optional[ExecutorType] = None):
def _generate(self, target, source, env, for_signature, executor: Executor | None = None):
# ensure that target is a list, to make it easier to write
# generator functions:
if not is_List(target):
Expand Down Expand Up @@ -1265,11 +1267,11 @@ def __str__(self) -> str:
def batch_key(self, env, target, source):
return self._generate(target, source, env, 1).batch_key(env, target, source)

def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str:
def genstring(self, target, source, env, executor: Executor | None = None) -> str:
return self._generate(target, source, env, 1, executor).genstring(target, source, env)

def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
show=_null, execute=_null, chdir=_null, executor: Optional[ExecutorType] = None):
show=_null, execute=_null, chdir=_null, executor: Executor | None = None):
act = self._generate(target, source, env, 0, executor)
if act is None:
raise SCons.Errors.UserError(
Expand All @@ -1281,21 +1283,21 @@ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
target, source, env, exitstatfunc, presub, show, execute, chdir, executor
)

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
return self._generate(target, source, env, 1, executor).get_presig(target, source, env)

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)

def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_varlist(self, target, source, env, executor: Executor | None = None):
return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)

def get_targets(self, env, executor: Optional[ExecutorType]):
def get_targets(self, env, executor: Executor | None):
return self._generate(None, None, env, 1, executor).get_targets(env, executor)


Expand Down Expand Up @@ -1341,22 +1343,22 @@ def _generate_cache(self, env):
raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
return gen_cmd

def _generate(self, target, source, env, for_signature, executor: Optional[ExecutorType] = None):
def _generate(self, target, source, env, for_signature, executor: Executor | None = None):
return self._generate_cache(env)

def __call__(self, target, source, env, *args, **kw):
c = self.get_parent_class(env)
return c.__call__(self, target, source, env, *args, **kw)

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
c = self.get_parent_class(env)
return c.get_presig(self, target, source, env)

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
c = self.get_parent_class(env)
return c.get_implicit_deps(self, target, source, env)

def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_varlist(self, target, source, env, executor: Executor | None = None):
c = self.get_parent_class(env)
return c.get_varlist(self, target, source, env, executor)

Expand Down Expand Up @@ -1389,7 +1391,7 @@ def function_name(self):
except AttributeError:
return "unknown_python_function"

def strfunction(self, target, source, env, executor: Optional[ExecutorType] = None):
def strfunction(self, target, source, env, executor: Executor | None = None):
if self.cmdstr is None:
return None
if self.cmdstr is not _null:
Expand Down Expand Up @@ -1430,7 +1432,7 @@ def __str__(self) -> str:
return str(self.execfunction)
return "%s(target, source, env)" % name

def execute(self, target, source, env, executor: Optional[ExecutorType] = None):
def execute(self, target, source, env, executor: Executor | None = None):
exc_info = (None,None,None)
try:
if executor:
Expand Down Expand Up @@ -1461,14 +1463,14 @@ def execute(self, target, source, env, executor: Optional[ExecutorType] = None):
# more information about this issue.
del exc_info

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
"""Return the signature contents of this callable action."""
try:
return self.gc(target, source, env)
except AttributeError:
return self.funccontents

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
return []

class ListAction(ActionBase):
Expand All @@ -1485,7 +1487,7 @@ def list_of_actions(x):
self.varlist = ()
self.targets = '$TARGETS'

def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str:
def genstring(self, target, source, env, executor: Executor | None = None) -> str:
return '\n'.join([a.genstring(target, source, env) for a in self.list])

def __str__(self) -> str:
Expand All @@ -1495,15 +1497,15 @@ def presub_lines(self, env):
return SCons.Util.flatten_sequence(
[a.presub_lines(env) for a in self.list])

def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_presig(self, target, source, env, executor: Executor | None = None):
"""Return the signature contents of this action list.
Simple concatenation of the signatures of the elements.
"""
return b"".join([bytes(x.get_contents(target, source, env)) for x in self.list])

def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
show=_null, execute=_null, chdir=_null, executor: Optional[ExecutorType] = None):
show=_null, execute=_null, chdir=_null, executor: Executor | None = None):
if executor:
target = executor.get_all_targets()
source = executor.get_all_sources()
Expand All @@ -1514,13 +1516,13 @@ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
return stat
return 0

def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_implicit_deps(self, target, source, env, executor: Executor | None = None):
result = []
for act in self.list:
result.extend(act.get_implicit_deps(target, source, env))
return result

def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None):
def get_varlist(self, target, source, env, executor: Executor | None = None):
result = OrderedDict()
for act in self.list:
for var in act.get_varlist(target, source, env, executor):
Expand Down Expand Up @@ -1586,7 +1588,7 @@ def subst_kw(self, target, source, env):
kw[key] = self.subst(self.kw[key], target, source, env)
return kw

def __call__(self, target, source, env, executor: Optional[ExecutorType] = None):
def __call__(self, target, source, env, executor: Executor | None = None):
args = self.subst_args(target, source, env)
kw = self.subst_kw(target, source, env)
return self.parent.actfunc(*args, **kw)
Expand Down
12 changes: 8 additions & 4 deletions SCons/ActionTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
# contents, so try to minimize changes by defining them here, before we
# even import anything.

from __future__ import annotations

def GlobalFunc() -> None:
pass

Expand All @@ -43,13 +45,15 @@ def __call__(self) -> None:
import unittest
from unittest import mock
from subprocess import PIPE
from typing import Optional
from typing import TYPE_CHECKING

import SCons.Action
import SCons.Environment
import SCons.Errors
from SCons.Action import scons_subproc_run
from SCons.Util.sctyping import ExecutorType

if TYPE_CHECKING:
from SCons.Executor import Executor

import TestCmd

Expand Down Expand Up @@ -1701,11 +1705,11 @@ def __call__(self, target, source, env) -> int:
c = test.read(outfile, 'r')
assert c == "class1b\n", c

def build_it(target, source, env, executor: Optional[ExecutorType] = None, self=self) -> int:
def build_it(target, source, env, executor: Executor | None = None, self=self) -> int:
self.build_it = 1
return 0

def string_it(target, source, env, executor: Optional[ExecutorType] = None, self=self):
def string_it(target, source, env, executor: Executor | None = None, self=self):
self.string_it = 1
return None

Expand Down
Loading

0 comments on commit 45a4d57

Please sign in to comment.