Skip to content
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
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ max-line-length = 119
copyright-check = True
select = E,F,W,C
copyright-regexp=Copyright \(c\) Facebook, Inc. and its affiliates. All Rights Reserved
ignore=W503,E203,E231,E701,E704
ignore=W503,E203,E231,E241,E701,E704
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why ignore E241?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was hitting commit-blocking false positives on some literal string patterns. Black would already reformat any code that would have actually violated this

12 changes: 12 additions & 0 deletions .github/workflows/core_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/macos"
Expand All @@ -47,6 +50,9 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/linux"
Expand All @@ -67,6 +73,9 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows"
Expand All @@ -86,6 +95,9 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/linux"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ pip-wheel-metadata
/.dmypy.json
TODO.txt
/venv
/.venv
35 changes: 29 additions & 6 deletions hydra/_internal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,12 +544,35 @@ class LazyCompletionHelp:
def __repr__(self) -> str:
return f"Install or Uninstall shell completion:\n{_get_completion_help()}"

parser.add_argument(
"--shell-completion",
"-sc",
action="store_true",
help=LazyCompletionHelp(), # type: ignore
)
if sys.version_info >= (3, 14):
# Python 3.14+ adds help message validation via ArgumentParser._check_help,
# which calls formatter._expand_help(action) to validate that action.help
# is a string that can be used in string formatting (e.g., "text %(prog)s").
# This breaks our LazyCompletionHelp because `_expand_help` expects an actual
# string.
# It is safe to disable this validation temporarily because LazyCompletionHelp.
# __repr__() returns a plain string (no % formatting)
original_check_help = argparse.ArgumentParser._check_help # type: ignore
argparse.ArgumentParser._check_help = ( # type: ignore
lambda self, action: None
)
try:
parser.add_argument(
"--shell-completion",
"-sc",
action="store_true",
help=LazyCompletionHelp(), # type: ignore
)
finally:
# Immediately restore normal validation
argparse.ArgumentParser._check_help = original_check_help # type: ignore
else:
parser.add_argument(
"--shell-completion",
"-sc",
action="store_true",
help=LazyCompletionHelp(), # type: ignore
)

parser.add_argument(
"--config-path",
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

BASE = os.path.abspath(os.path.dirname(__file__))

DEFAULT_PYTHON_VERSIONS = ["3.9", "3.10", "3.11"]
DEFAULT_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
DEFAULT_OS_NAMES = ["Linux", "MacOS", "Windows"]

PYTHON_VERSIONS = os.environ.get(
Expand Down
10 changes: 8 additions & 2 deletions tests/test_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,20 @@ def test_missing_init_py_error(hydra_restore_singletons: Any) -> None:


def test_missing_bad_config_dir_error(hydra_restore_singletons: Any) -> None:
# Use a platform-appropriate absolute path that doesn't exist
if sys.platform == "win32":
bad_dir = "C:\\no_way_in_hell_1234567890"
else:
bad_dir = "/no_way_in_hell_1234567890"

expected = (
"Primary config directory not found."
"\nCheck that the config directory '/no_way_in_hell_1234567890' exists and readable"
f"\nCheck that the config directory '{bad_dir}' exists and readable"
)

with raises(Exception, match=re.escape(expected)):
with initialize_config_dir(
config_dir="/no_way_in_hell_1234567890",
config_dir=bad_dir,
version_base=None,
):
hydra = GlobalHydra.instance().hydra
Expand Down
53 changes: 45 additions & 8 deletions tests/test_hydra.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,24 @@ def test_help(
assert_text_same(result, expected.format(script=script))


def test_shell_completion_help(tmpdir: Path) -> None:
"""Test that --shell-completion --help works (regression test for Python 3.14+ argparse)."""
# This test ensures that the LazyCompletionHelp workaround in utils.py works correctly
# In Python 3.14+, argparse validates that help is a string, but we use a lazy callable
# The workaround temporarily disables _check_help validation
cmd = [
"examples/tutorials/basic/your_first_hydra_app/1_simple_cli/my_app.py",
f'hydra.run.dir="{str(tmpdir)}"',
"hydra.job.chdir=True",
"--shell-completion",
"--help",
]
result, _err = run_python_script(cmd)
# When both flags are present, --help takes precedence and shows help text
assert "powered by hydra" in result.lower()
assert not _err


@mark.parametrize(
"overrides,expected",
[
Expand Down Expand Up @@ -1287,22 +1305,41 @@ def test_app_with_error_exception_sanitized(tmpdir: Any, monkeypatch: Any) -> No
f"hydra.sweep.dir={tmpdir}",
"hydra.job.chdir=True",
]
expected_regex = dedent(
r"""

# Python 3.12 introduced enhanced error messages that suggest similar attribute
# names for AttributeError. Unfortunately, it suggests private attributes like
# '_return_value'
# Python 3.13+ fixes this by not suggesting private attributes.
if sys.version_info[:2] == (3, 12):
suggestion_suffix = r". Did you mean: '_return_value'\?"
else:
suggestion_suffix = r""

traceback_line = r"foo\(cfg\)"

if sys.version_info >= (3, 13):
# Python 3.13 changed the traceback format for error indicators
traceback_line += r"\n ~~~\^\^+\^+"

expected_regex = (
dedent(
r"""
Error executing job with overrides: \[\]
Traceback \(most recent call last\):
File ".*my_app\.py", line 13, in my_app
foo\(cfg\)
{traceback_line}
File ".*my_app\.py", line 8, in foo
cfg\.foo = "bar" # does not exist in the config(
\^+)?
cfg\.foo = "bar" # does not exist in the config(\n \^+)?
omegaconf\.errors\.ConfigAttributeError: Key 'foo' is not in struct
full_key: foo
object_type=dict
object_type=dict{suggestion_suffix}

Set the environment variable HYDRA_FULL_ERROR=1 for a complete stack trace\.
"""
).strip()
)
.strip()
.format(traceback_line=traceback_line, suggestion_suffix=suggestion_suffix)
)

ret = run_with_error(cmd)
assert_multiline_regex_search(expected_regex, ret)
Expand Down Expand Up @@ -1569,7 +1606,7 @@ def test_frozen_primary_config(
^Error executing job with overrides: \[\]\n?
Traceback \(most recent call last\):
File "\S*[/\\]my_app.py", line 10, in my_app
deprecation_warning\("Feature FooBar is deprecated"\)
deprecation_warning\("Feature FooBar is deprecated"\)(\n [~\^]+)?
File "\S*\.py", line 11, in deprecation_warning
raise HydraDeprecationError\(.*\)
hydra\.errors\.HydraDeprecationError: Feature FooBar is deprecated
Expand Down
3 changes: 1 addition & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,7 @@ def test_success(self) -> None:
r"""
Traceback \(most recent call last\):
File "[^"]+", line \d+, in job_calling_omconf
OmegaConf.resolve\(123\) # type: ignore(
\^+)?
OmegaConf.resolve\(123\) # type: ignore(\n [~\^]+)?
ValueError: Invalid config type \(int\), expected an OmegaConf Container

Set the environment variable HYDRA_FULL_ERROR=1 for a complete stack trace\.
Expand Down
Loading