diff --git a/git/config.py b/git/config.py index aef881d2e..0da90bab2 100644 --- a/git/config.py +++ b/git/config.py @@ -10,36 +10,34 @@ import abc import configparser as cp import fnmatch -from functools import wraps import inspect -from io import BufferedReader, IOBase import logging import os import os.path as osp import re import sys - -from git.compat import defenc, force_text -from git.util import LockFile +from functools import wraps +from io import BufferedReader, IOBase # typing------------------------------------------------------- - from typing import ( + IO, + TYPE_CHECKING, Any, Callable, + Dict, Generic, - IO, List, - Dict, Sequence, - TYPE_CHECKING, Tuple, TypeVar, Union, cast, ) -from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, assert_never, _T +from git.compat import defenc, force_text +from git.types import _T, ConfigLevels_Tup, Lit_config_levels, PathLike, assert_never +from git.util import LockFile if TYPE_CHECKING: from io import BytesIO @@ -958,9 +956,9 @@ def _string_to_value(self, valuestr: str) -> Union[int, float, str, bool]: # Try boolean values as git uses them. vl = valuestr.lower() - if vl == "false": + if vl in ("false", "no", "off"): return False - if vl == "true": + if vl in ("true", "yes", "on"): return True if not isinstance(valuestr, str): diff --git a/test/test_config.py b/test/test_config.py index 3107d8074..3031721de 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -167,6 +167,38 @@ def test_backslash_line_continuation(self): key = "co" if section == "alias" else "k" self.assertEqual(config.get_value(section, key), expected) + def test_get_value_reads_git_boolean_spellings(self): + # git accepts yes/no and on/off as well as true/false, and getboolean on + # this class already did. get_value returned them as strings, so "no" and + # "off" arrived as non-empty (truthy) values for a caller testing them. + cases = [ + (b"true", True), + (b"TRUE", True), + (b"yes", True), + (b"Yes", True), + (b"on", True), + (b"On", True), + (b"false", False), + (b"no", False), + (b"off", False), + (b"Off", False), + ] + for raw, expected in cases: + config_file = io.BytesIO(b"[core]\n\tflag = " + raw + b"\n") + config_file.name = "boolean_spellings.config" + config = GitConfigParser(config_file) + config.read() + self.assertIs(config.get_value("core", "flag"), expected, raw.decode()) + # The two accessors must not disagree about the same value. + self.assertIs(config.getboolean("core", "flag"), expected, raw.decode()) + + # A value that is not a boolean at all still comes back untouched. + config_file = io.BytesIO(b"[core]\n\tflag = meld\n") + config_file.name = "boolean_spellings.config" + config = GitConfigParser(config_file) + config.read() + self.assertEqual(config.get_value("core", "flag"), "meld") + @with_rw_directory def test_comment_backslash_does_not_continue_value(self, rw_dir): config_path = osp.join(rw_dir, "config")