Skip to content

tempfile: minor changes to handle tempfile encoding exceptions - #4753

Merged
bdbaddog merged 14 commits into
SCons:masterfrom
jcbrill:jbrill-tempfile-encoding
Aug 12, 2025
Merged

tempfile: minor changes to handle tempfile encoding exceptions#4753
bdbaddog merged 14 commits into
SCons:masterfrom
jcbrill:jbrill-tempfile-encoding

Conversation

@jcbrill

@jcbrill jcbrill commented Aug 7, 2025

Copy link
Copy Markdown
Contributor

Provide explicit error messages for tempfile encoding failures and reduce the likelihood of an open tempfile file handle at exit.

Based on discussions #4749, #4746, and #4752.

Changes to TempFileMunge (SCons\.Platform\.__init__.py):

  • Move encoding the tempfile contents before creating the tempfile due to possible encoding errors.
  • Ensure the tempfile file handle is closed after writing the tempfile contents.
  • Raise a TempFileEncodeError exception when the tempfile contents encoding fails for known exception types.

Example TempFileEncodeError exceptions:

  • UnicodeEncodeError (user-defined encoding):

    scons: *** [src\main.obj] TempFileEncodeError : [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    Traceback (most recent call last):
    ...
    SCons.Platform.TempFileEncodeError: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    
  • LookupError (user-defined encoding):

    scons: *** [src\main.obj] TempFileEncodeError : [LookupError] unknown encoding: notinstalled
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
    Traceback (most recent call last):
    ...
    SCons.Platform.TempFileEncodeError: [LookupError] unknown encoding: notinstalled
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
    
  • TypeError (user-defined encoding):

    scons: *** [src\main.obj] TempFileEncodeError : [TypeError] bytes() argument 'encoding' must be str, not None
      TempFileMunge encoding: env['TEMPFILEENCODING'] = None
    Traceback (most recent call last):
    ...
    SCons.Platform.TempFileEncodeError: [TypeError] bytes() argument 'encoding' must be str, not None
      TempFileMunge encoding: env['TEMPFILEENCODING'] = None
    
  • UnicodeError (user-modified default encoding):

    scons: *** [src\main.obj] TempFileEncodeError : [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: default = 'cp1252'
    Traceback (most recent call last):
    ...
    SCons.Platform.TempFileEncodeError: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: default = 'cp1252'
    

The TempFileEncodeError exception text contains:

  • The exception name (TempFileEncodeError) is an indication what failed.
  • The underlying exception name ([UnicodeEncodeError]) and reason is an indication of why it failed.
  • The TempFileMunge line indicates if a user-defined encoding value was defined or the default encoding value was used and/or modified.

If nothing else, this should make it easier to decipher what us happening should the exception text be posted in the mailing list or in an issue.

Not addressed:

  • The tempfile removal invocation (i.e., os.remove(file)) in the atexit handler has not been changed. Moving the tempfile encoding before the tempfile is opened and closing the tempfile on write errors should reduce the likelihood of errors upon removal. However, anti-virus software could always be a problem.

Feature complete: no additional changes planned.

Contributor Checklist:

  • I have created a new test or updated the unit tests to cover the new/changed functionality.
  • I have updated CHANGES.txt and RELEASE.txt (and read the README.rst).
  • I have updated the appropriate documentation

jcbrill added 7 commits August 5, 2025 12:09
* add file-level default encoding,
* move encoding contents before creating temporary file,
* extend handled exception type messages,
* ensure closing temporary file handle.
…iable for encoding. Only encoding bytes should be cause of an exception.
… simplify temp file encoding exception handler
@jcbrill jcbrill changed the title tempfile: minor changes handle tempfile encoding exceptions tempfile: minor changes to handle tempfile encoding exceptions Aug 7, 2025
@mwichmann

Copy link
Copy Markdown
Collaborator

Example TempFileEncodeError exceptions:

* `LookupError` (user-defined encoding):
  ```
  scons: *** [src\main.obj] TempFileEncodeError : [LookupError] unknown encoding: notinstalled
    TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
  Traceback (most recent call last):
  ...
  SCons.Platform.TempFileEncodeError: [LookupError] unknown encoding: notinstalled
    TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
  ```

* `TypeError` (user-defined encoding):
  ```
  scons: *** [src\main.obj] TempFileEncodeError : [TypeError] bytes() argument 'encoding' must be str, not None
    TempFileMunge encoding: env['TEMPFILEENCODING'] = None
  Traceback (most recent call last):
  ...
  SCons.Platform.TempFileEncodeError: [TypeError] bytes() argument 'encoding' must be str, not None
    TempFileMunge encoding: env['TEMPFILEENCODING'] = None
  ```

IMO these two aren't necessary to catch. Python will generate an understandable error message (in fact, that message is encapsulated in the ones shown)

@jcbrill

jcbrill commented Aug 7, 2025

Copy link
Copy Markdown
Contributor Author
scons: *** [src\main.obj] LookupError : unknown encoding: notinstalled
Traceback (most recent call last):
  File "S:\SCons\scons-master\scripts\..\SCons\Taskmaster\__init__.py", line 246, in execute
    self.targets[0].build()
  File "S:\SCons\scons-master\scripts\..\SCons\Node\__init__.py", line 774, in build
    self.get_executor()(self, **kw)
  File "S:\SCons\scons-master\scripts\..\SCons\Executor.py", line 386, in __call__
    return _do_execute_map[self._do_execute](self, target, kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Executor.py", line 119, in execute_action_list
    status = act(*args, **kw)
             ^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Action.py", line 1273, in __call__
    return act(
           ^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Action.py", line 1342, in __call__
    return c.__call__(self, target, source, env, *args, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Action.py", line 706, in __call__
    cmd = self.strfunction(target, source, env, executor)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Action.py", line 1037, in strfunction
    cmd_list, ignore, silent = self.process(target, source, env, executor, overrides=overrides)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Action.py", line 1008, in process
    result = env.subst_list(self.cmd_list, SUBST_CMD, executor=executor, overrides=overrides)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Environment.py", line 741, in subst_list
    return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 932, in scons_subst_list
    ls.substitute(strSubst, lvars, 0)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 645, in substitute
    self.expand(a, lvars, within_list)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 593, in expand
    self.substitute(s, lv, 0)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 645, in substitute
    self.expand(a, lvars, within_list)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 593, in expand
    self.substitute(s, lv, 0)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 647, in substitute
    self.expand(args, lvars, within_list)
  File "S:\SCons\scons-master\scripts\..\SCons\Subst.py", line 608, in expand
    s = s(target=lvars['TARGETS'],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\scons-master\scripts\..\SCons\Platform\__init__.py", line 288, in __call__
    os.write(fd, bytes(join_char.join(args) + "\n", encoding=encoding))
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LookupError: unknown encoding: notinstalled

I wouldn't call the above real-world exception traceback particularly user-friendly or necessarily clear compared to an explicit exception type given the encoding may be set in third-party code.

If directed, will change though.

Edit: traceback from master and not PR.

@bdbaddog

bdbaddog commented Aug 7, 2025

Copy link
Copy Markdown
Contributor

@jcbrill I'd keep your lookuperror handler. Nobody wants a giant stack trace when running a build tool, and we can always reenable with --debug=stacktrace right?

@mwichmann

Copy link
Copy Markdown
Collaborator

The tests I did on an earlier iteration of this didn't do that (generate full stacktrace)... don't know what's different.

@jcbrill

jcbrill commented Aug 7, 2025

Copy link
Copy Markdown
Contributor Author

I'd keep your lookuperror handler. Nobody wants a giant stack trace when running a build tool, and we can always reenable with --debug=stacktrace right?

At present, the current iteration will return the giant stack trace as well.

To keep the listings short above, I manually removed text and replaced it with ...:

Traceback (most recent call last):
...  <===== lots of stuff manually removed
SCons.Platform.TempFileEncodeError: [LookupError] unknown encoding: notinstalled

The tests I did on an earlier iteration of this didn't do that (generate full stacktrace)... don't know what's different.

Unfortunately, that appears to be my doing.

Earlier iterations raised an SCons.Errors.UserError instance. The current iteration returns an exception instance derived from Exception.

When derived from SCons.Erorrs.UserError, it appears that the exception type name is suppressed in the SCons output (i.e., only the reason and not the exception name and reason):

scons: Building targets ...
scons: *** [src\main.obj] [LookupError] unknown encoding: notinstalled
  TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
scons: building terminated because of errors.

It is useful to have the exception name in the exception message. It seemed redundant to add it to the reason but it would keep the exception listing short.

Something along the lines of the following would be equivalent to the spirit of the current PR.

Possible reason text:

scons: Building targets ...
scons: *** [src\main.obj] TempFileEncodeError [LookupError] unknown encoding: badencodingname
  TempFileMunge encoding: env['TEMPFILEENCODING'] = 'badencodingname'
scons: building terminated because of errors.

The test file would have to be updated accordingly.

Not handling LookupError or TypeError should return the giant stack trace though.

The key change in the PR was to quickly identify the root cause and location of the exception from the name and reason.

I can try and fix it today and push an update soon.

…s.Errors.UserError instead of Exception

Changes:
* TempFileEncodeError derived from SCons.Errors.UserError instead of Exception.
* Add TempFileEncodeError class name to exception reason text.
* Update tests for expected exception reason text changes.
@jcbrill

jcbrill commented Aug 7, 2025

Copy link
Copy Markdown
Contributor Author

I have revised the code, the test file, and pushed the changes.

The four examples with the revised code:

  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError [LookupError] unknown encoding: notinstalled
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError [TypeError] bytes() argument 'encoding' must be str, not None
      TempFileMunge encoding: env['TEMPFILEENCODING'] = None
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: default = 'cp1252'
    scons: building terminated because of errors.
    

The revised code with with --debug=stacktrace:

scons: *** [src\main.obj] TempFileEncodeError [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
  TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
scons: internal stack trace:
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Taskmaster\Job.py", line 737, in _work
    task.execute()
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Script\Main.py", line 224, in execute
    SCons.Taskmaster.OutOfDateTask.execute(self)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Taskmaster\__init__.py", line 246, in execute
    self.targets[0].build()
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Node\__init__.py", line 774, in build
    self.get_executor()(self, **kw)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Executor.py", line 386, in __call__
    return _do_execute_map[self._do_execute](self, target, kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Executor.py", line 119, in execute_action_list
    status = act(*args, **kw)
             ^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1273, in __call__
    return act(
           ^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1342, in __call__
    return c.__call__(self, target, source, env, *args, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 706, in __call__
    cmd = self.strfunction(target, source, env, executor)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1037, in strfunction
    cmd_list, ignore, silent = self.process(target, source, env, executor, overrides=overrides)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1008, in process
    result = env.subst_list(self.cmd_list, SUBST_CMD, executor=executor, overrides=overrides)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Environment.py", line 741, in subst_list
    return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 932, in scons_subst_list
    ls.substitute(strSubst, lvars, 0)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 645, in substitute
    self.expand(a, lvars, within_list)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 593, in expand
    self.substitute(s, lv, 0)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 645, in substitute
    self.expand(a, lvars, within_list)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 593, in expand
    self.substitute(s, lv, 0)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 647, in substitute
    self.expand(args, lvars, within_list)
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 608, in expand
    s = s(target=lvars['TARGETS'],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Platform\__init__.py", line 273, in __call__
    raise new_exc(err_msg) from None
scons: building terminated because of errors.

@mwichmann

Copy link
Copy Markdown
Collaborator

If the new error inherits from UserError it will automatically shorten the traceback.

@jcbrill

jcbrill commented Aug 8, 2025

Copy link
Copy Markdown
Contributor Author

If the new error inherits from UserError it will automatically shorten the traceback.

It'd be nice if it didn't suppress the exception name for subclasses though as the name can convey useful information.

Something like this:
*scons: *** [src\main.obj] MyUserErrorName: reason
instead of:
scons: *** [src\main.obj] reason

The TempFileEncodeError class name was added to the reason string in the handler.

…construction to prefix reason text with class name.
@jcbrill

jcbrill commented Aug 8, 2025

Copy link
Copy Markdown
Contributor Author

No other changes are planned unless requested/directed subject to the CI tests passing.

There were minor changes to the TempFileEncodeError exception class and exception reason text. The tests were updated accordingly.

I'd keep your lookuperror handler. Nobody wants a giant stack trace when running a build tool, and we can always reenable with --debug=stacktrace right?

Correct (see last example below).

Example output from test runs based on the latest commit:

  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError: [LookupError] unknown encoding: notinstalled
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError: [TypeError] bytes() argument 'encoding' must be str, not None
      TempFileMunge encoding: env['TEMPFILEENCODING'] = None
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: default = 'cp1252'
    scons: building terminated because of errors.
    
  • With --debug=stacktrace:

    scons: Building targets ...
    scons: *** [src\main.obj] TempFileEncodeError: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 85-86: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    scons: internal stack trace:
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Taskmaster\Job.py", line 737, in _work
        task.execute()
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Script\Main.py", line 224, in execute
        SCons.Taskmaster.OutOfDateTask.execute(self)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Taskmaster\__init__.py", line 246, in execute
        self.targets[0].build()
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Node\__init__.py", line 774, in build
        self.get_executor()(self, **kw)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Executor.py", line 386, in __call__
        return _do_execute_map[self._do_execute](self, target, kw)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Executor.py", line 119, in execute_action_list
        status = act(*args, **kw)
                 ^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1273, in __call__
        return act(
               ^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1342, in __call__
        return c.__call__(self, target, source, env, *args, **kw)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 706, in __call__
        cmd = self.strfunction(target, source, env, executor)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1037, in strfunction
        cmd_list, ignore, silent = self.process(target, source, env, executor, overrides=overrides)
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Action.py", line 1008, in process
        result = env.subst_list(self.cmd_list, SUBST_CMD, executor=executor, overrides=overrides)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Environment.py", line 741, in subst_list
        return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 932, in scons_subst_list
        ls.substitute(strSubst, lvars, 0)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 645, in substitute
        self.expand(a, lvars, within_list)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 593, in expand
        self.substitute(s, lv, 0)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 645, in substitute
        self.expand(a, lvars, within_list)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 593, in expand
        self.substitute(s, lv, 0)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 647, in substitute
        self.expand(args, lvars, within_list)
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Subst.py", line 608, in expand
        s = s(target=lvars['TARGETS'],
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "S:\SCons\jbrill-tempfile-encoding\scripts\..\SCons\Platform\__init__.py", line 276, in __call__
        raise TempFileEncodeError.factory(err_msg)
    scons: building terminated because of errors.
    

Comment thread SCons/Platform/__init__.py Outdated
TEMPFILE_DEFAULT_ENCODING = "utf-8"


class TempFileEncodeError(SCons.Errors.UserError):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not really consistent on whether to define Error classes near the point of use, or in Errors.py. That question doesn't have to be resolved here (i.e., consider it a question, not a blocker). The Configure subsystem and the MSVC tool certainly define their own; many are also centralized into Errors.py.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this particular case, it could just as easily be an instance of SCons.Errors.UserError with the message/reason prefixed with something along the lines tempfile encoding error: [UnicodeError] ... instead of a standalone exception. I'm not sure this is an exception a user is interested in catching as there really is no way to to "fix it".

@mwichmann

Copy link
Copy Markdown
Collaborator

I have some other minor rearrangements ("better flow", not functional changes) in the base platform module, but I'm not going to try to sneak them into this. This actual code changes look okay to me.

Comment thread test/TEMPFILE/TEMPFILEENCODING.py Outdated
tempfileencoding = {tempfileencoding}
defaultencoding = {defaultencoding}

DefaultEnvironment()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add tools=[] here? It will speed up the test by avoiding tool initializations..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. That is a bug.

Comment thread test/TEMPFILE/TEMPFILEENCODING.py
…s.Error.UserError instance and move generated test SConstruct to fixture.

Changes:
* Remove TempFileEncodeError and replace with instance of UserError.
* Move SConstruct template to fixture and add command-line arguments for test configuration.
* Rework test script to use fixture and match revised exception text.
@jcbrill

jcbrill commented Aug 12, 2025

Copy link
Copy Markdown
Contributor Author

Revised exception examples error messages:

  • scons: Building targets ...
    scons: *** [src\main.obj] tempfile encoding error: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 91-92: character maps to <undefined>
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'cp1252'
    scons: building terminated because of errors.
    
  • scons: *** [src\main.obj] tempfile encoding error: [LookupError] unknown encoding: notinstalled
      TempFileMunge encoding: env['TEMPFILEENCODING'] = 'notinstalled'
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] tempfile encoding error: [TypeError] bytes() argument 'encoding' must be str, not None
      TempFileMunge encoding: env['TEMPFILEENCODING'] = None
    scons: building terminated because of errors.
    
  • scons: Building targets ...
    scons: *** [src\main.obj] tempfile encoding error: [UnicodeEncodeError] 'charmap' codec can't encode characters in position 91-92: character maps to <undefined>
      TempFileMunge encoding: default = 'cp1252'
    scons: building terminated because of errors.
    

@bdbaddog

Copy link
Copy Markdown
Contributor

Looks great. I'll merge once CI tests complete successfully as expected!

@bdbaddog
bdbaddog merged commit d51e125 into SCons:master Aug 12, 2025
9 checks passed
@jcbrill
jcbrill deleted the jbrill-tempfile-encoding branch August 12, 2025 09:45
@mwichmann mwichmann added the TEMPFILE Long-command-line handling with TEMPFILE label Aug 12, 2025
@mwichmann mwichmann added this to 4.10 Aug 12, 2025
@mwichmann mwichmann moved this to Done in 4.10 Aug 12, 2025
@mwichmann mwichmann added this to the NextRelease milestone Aug 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TEMPFILE Long-command-line handling with TEMPFILE

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants