Skip to content

Commit 55b386b

Browse files
authored
Merge pull request SCons#4485 from Raymo111/patch-1
Fix SCons#3935 OSErrors being hidden
2 parents 7e64842 + 606f6c2 commit 55b386b

File tree

10 files changed

+56
-40
lines changed

10 files changed

+56
-40
lines changed

CHANGES.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@ NOTE: 4.3.0 now requires Python 3.6.0 and above. Python 3.5.x is no longer suppo
99

1010
RELEASE VERSION/DATE TO BE FILLED IN LATER
1111

12-
From Mats Wichmann:
12+
From Raymond Li:
13+
- Fix issue #3935: OSErrors are now no longer hidden during execution of
14+
Actions. All exceptions during the execution of an Action are now
15+
returned by value rather than by raising an exception, for more
16+
consistent behavior.
17+
NOTE: With this change, user created Actions should now catch and handle
18+
expected exceptions (whereas previously many of these were silently
19+
caught and suppressed by the SCons Action exection code).
1320

21+
From Mats Wichmann:
1422
- Updated Value Node docs and tests.
1523
- Python 3.13 compat: re.sub deprecated count, flags as positional args,
1624
caused update-release-info test to fail.

RELEASE.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
3131
json output is also sorted, to match the default display.
3232
- Python 3.13 changes the behavior of isabs() on Windows. Adjust SCons
3333
usage of this in NodeInfo classes to avoid test problems.
34+
- All exceptions during the execution of an Action are now returned by value
35+
rather than by raising an exception, for more consistent behavior.
36+
NOTE: With this change, user created Actions should now catch and handle
37+
expected exceptions (whereas previously many of these were silently caught
38+
and suppressed by the SCons Action exection code).
3439

3540
FIXES
3641
-----
3742

38-
- List fixes of outright bugs
43+
- OSErrors are now no longer hidden during the execution of Actions.
3944

4045
IMPROVEMENTS
4146
------------

SCons/Action.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,16 +1454,6 @@ def execute(self, target, source, env, executor: Optional[ExecutorType] = None):
14541454
except TypeError:
14551455
result.command=self.strfunction(target, source, env)
14561456

1457-
# FIXME: This maintains backward compatibility with respect to
1458-
# which type of exceptions were returned by raising an
1459-
# exception and which ones were returned by value. It would
1460-
# probably be best to always return them by value here, but
1461-
# some codes do not check the return value of Actions and I do
1462-
# not have the time to modify them at this point.
1463-
if (exc_info[1] and
1464-
not isinstance(exc_info[1], EnvironmentError)):
1465-
raise result
1466-
14671457
return result
14681458
finally:
14691459
# Break the cycle between the traceback object and this

SCons/CacheDirTests.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def __setitem__(self, name, value) -> None:
164164
# so that _readconfig* will try to rewrite it
165165
old_config = os.path.join(self._CacheDir.path, "config")
166166
os.remove(old_config)
167-
167+
168168
try:
169169
self._CacheDir._readconfig(self._CacheDir.path)
170170
assert False, "Should have raised exception and did not"
@@ -315,23 +315,17 @@ def mkdir(dir, mode: int=0) -> None:
315315
old_warn_exceptions = SCons.Warnings.warningAsException(1)
316316
SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning)
317317

318-
try:
319-
cd_f7 = self.test.workpath("cd.f7")
320-
self.test.write(cd_f7, "cd.f7\n")
321-
f7 = self.File(cd_f7, 'f7_bsig')
322-
323-
warn_caught = 0
324-
try:
325-
f7.push_to_cache()
326-
except SCons.Errors.BuildError as e:
327-
assert e.exc_info[0] == SCons.Warnings.CacheWriteErrorWarning
328-
warn_caught = 1
329-
assert warn_caught
330-
finally:
331-
shutil.copy2 = save_copy2
332-
os.mkdir = save_mkdir
333-
SCons.Warnings.warningAsException(old_warn_exceptions)
334-
SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning)
318+
cd_f7 = self.test.workpath("cd.f7")
319+
self.test.write(cd_f7, "cd.f7\n")
320+
f7 = self.File(cd_f7, 'f7_bsig')
321+
322+
warn_caught = 0
323+
r = f7.push_to_cache()
324+
assert r.exc_info[0] == SCons.Warnings.CacheWriteErrorWarning
325+
shutil.copy2 = save_copy2
326+
os.mkdir = save_mkdir
327+
SCons.Warnings.warningAsException(old_warn_exceptions)
328+
SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning)
335329

336330
def test_no_strfunction(self) -> None:
337331
"""Test handling no strfunction() for an action."""

SCons/Defaults.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,15 @@ def copy_func(dest, src, symlinks: bool=True) -> int:
291291

292292
elif os.path.islink(src):
293293
if symlinks:
294-
os.symlink(os.readlink(src), dest)
294+
try:
295+
os.symlink(os.readlink(src), dest)
296+
except FileExistsError:
297+
raise SCons.Errors.BuildError(
298+
errstr=(
299+
f'Error: Copy() called to create symlink at "{dest}",'
300+
' but a file already exists at that location.'
301+
)
302+
)
295303
return 0
296304

297305
return copy_func(dest, os.path.realpath(src))

SCons/Node/FS.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,11 @@ def LocalString(target, source, env) -> str:
326326

327327
def UnlinkFunc(target, source, env) -> int:
328328
t = target[0]
329-
t.fs.unlink(t.get_abspath())
329+
file = t.get_abspath()
330+
try:
331+
t.fs.unlink(file)
332+
except FileNotFoundError:
333+
pass
330334
return 0
331335

332336
Unlink = SCons.Action.Action(UnlinkFunc, None)
@@ -2995,7 +2999,7 @@ def _createDir(self) -> None:
29952999
# created.
29963000
self.dir._create()
29973001

2998-
def push_to_cache(self) -> None:
3002+
def push_to_cache(self) -> bool:
29993003
"""Try to push the node into a cache
30003004
"""
30013005
# This should get called before the Nodes' .built() method is
@@ -3006,10 +3010,10 @@ def push_to_cache(self) -> None:
30063010
# the node to cache so that the memoization of the self.exists()
30073011
# return value doesn't interfere.
30083012
if self.nocache:
3009-
return
3013+
return None
30103014
self.clear_memoized_values()
30113015
if self.exists():
3012-
self.get_build_env().get_CacheDir().push(self)
3016+
return self.get_build_env().get_CacheDir().push(self)
30133017

30143018
def retrieve_from_cache(self) -> bool:
30153019
"""Try to retrieve the node's content from a cache
@@ -3185,12 +3189,16 @@ def remove(self):
31853189
return None
31863190

31873191
def do_duplicate(self, src):
3192+
"""Create a duplicate of this file from the specified source."""
31883193
self._createDir()
31893194
if SCons.Node.print_duplicate:
31903195
print(f"dup: relinking variant '{self}' from '{src}'")
31913196
Unlink(self, None, None)
3192-
e = Link(self, src, None)
3193-
if isinstance(e, SCons.Errors.BuildError):
3197+
try:
3198+
e = Link(self, src, None)
3199+
if isinstance(e, SCons.Errors.BuildError):
3200+
raise e
3201+
except SCons.Errors.BuildError as e:
31943202
raise SCons.Errors.StopError(f"Cannot duplicate `{src.get_internal_path()}' in `{self.dir._path}': {e.errstr}.")
31953203
self.linked = 1
31963204
# The Link() action may or may not have actually

SCons/Node/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ def reset_executor(self) -> None:
678678
except AttributeError:
679679
pass
680680

681-
def push_to_cache(self) -> None:
681+
def push_to_cache(self) -> bool:
682682
"""Try to push a node into a cache
683683
"""
684684
pass

SCons/SConfTests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def is_up_to_date(self) -> bool:
207207
return False
208208
def prepare(self) -> None:
209209
pass
210-
def push_to_cache(self) -> None:
210+
def push_to_cache(self) -> bool:
211211
pass
212212
def retrieve_from_cache(self) -> bool:
213213
return False

SCons/Taskmaster/TaskmasterTests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def targets(self, node):
7272
def disambiguate(self):
7373
return self
7474

75-
def push_to_cache(self) -> None:
75+
def push_to_cache(self) -> bool:
7676
pass
7777

7878
def retrieve_from_cache(self) -> bool:

SCons/Variables/PathVariable.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ def PathIsDirCreate(key, val, env) -> None:
109109
except PermissionError:
110110
m = 'Path for option %s could not be created: %s'
111111
raise SCons.Errors.UserError(m % (key, val))
112+
except OSError:
113+
m = 'Path for option %s could not be created: %s'
114+
raise SCons.Errors.UserError(m % (key, val))
112115

113116
@staticmethod
114117
def PathIsFile(key, val, env) -> None:

0 commit comments

Comments
 (0)