diff --git a/doc/data/messages/i/invalid-envvar-default/bad.py b/doc/data/messages/i/invalid-envvar-default/bad.py index 9b564b9c8c..f87b0de304 100644 --- a/doc/data/messages/i/invalid-envvar-default/bad.py +++ b/doc/data/messages/i/invalid-envvar-default/bad.py @@ -1,3 +1,4 @@ import os env = os.getenv("SECRET_KEY", 1) # [invalid-envvar-default] +env = os.environ.get("SECRET_KEY", 1) # [invalid-envvar-default] diff --git a/doc/data/messages/i/invalid-envvar-default/good.py b/doc/data/messages/i/invalid-envvar-default/good.py index 103925941e..bcc41cb13c 100644 --- a/doc/data/messages/i/invalid-envvar-default/good.py +++ b/doc/data/messages/i/invalid-envvar-default/good.py @@ -1,3 +1,4 @@ import os env = os.getenv("SECRET_KEY", "1") +env = os.environ.get("SECRET_KEY", "1") diff --git a/doc/data/messages/i/invalid-envvar-value/bad.py b/doc/data/messages/i/invalid-envvar-value/bad.py index 56e60fe700..38bd20aded 100644 --- a/doc/data/messages/i/invalid-envvar-value/bad.py +++ b/doc/data/messages/i/invalid-envvar-value/bad.py @@ -1,3 +1,4 @@ import os os.getenv(1) # [invalid-envvar-value] +os.environ.get(1) # [invalid-envvar-value] diff --git a/doc/data/messages/i/invalid-envvar-value/good.py b/doc/data/messages/i/invalid-envvar-value/good.py index fd082ecfd8..dbc6fedb0c 100644 --- a/doc/data/messages/i/invalid-envvar-value/good.py +++ b/doc/data/messages/i/invalid-envvar-value/good.py @@ -1,3 +1,4 @@ import os os.getenv("1") +os.environ.get("1") diff --git a/doc/whatsnew/fragments/10092.false_negative b/doc/whatsnew/fragments/10092.false_negative new file mode 100644 index 0000000000..764aa756ea --- /dev/null +++ b/doc/whatsnew/fragments/10092.false_negative @@ -0,0 +1,3 @@ +Fix false negative for `invalid-envvar-default` to detect `os.environ.get`. + +Closes #10092 diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index a4b89b6fd0..f1d05f73a4 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -692,6 +692,14 @@ def visit_call(self, node: nodes.Call) -> None: self._check_for_check_kw_in_run(node) elif name in DEBUG_BREAKPOINTS: self.add_message("forgotten-debug-statement", node=node) + elif ( + isinstance(inferred, astroid.BoundMethod) + and self._is_os_environ_get(node) + and utils.is_builtin_object(inferred) + ): + # when os.environ.get() is inferred it creates a "builtins.dict" and "_collections_abc.Mapping" + self._check_env_function(node, inferred) + self.check_deprecated_method(node, inferred) @utils.only_required_for_messages("boolean-datetime") @@ -892,7 +900,9 @@ def _check_open_call( "unspecified-encoding", node=node, confidence=confidence ) - def _check_env_function(self, node: nodes.Call, infer: nodes.FunctionDef) -> None: + def _check_env_function( + self, node: nodes.Call, infer: nodes.FunctionDef | astroid.BoundMethod + ) -> None: env_name_kwarg = "key" env_value_kwarg = "default" if node.keywords: @@ -934,7 +944,7 @@ def _check_env_function(self, node: nodes.Call, infer: nodes.FunctionDef) -> Non def _check_invalid_envvar_value( self, node: nodes.Call, - infer: nodes.FunctionDef, + infer: nodes.FunctionDef | astroid.BoundMethod, message: str, call_arg: InferenceResult | None, allow_none: bool, @@ -942,6 +952,14 @@ def _check_invalid_envvar_value( if call_arg is None or isinstance(call_arg, util.UninferableBase): return + if ( + message == "invalid-envvar-default" + and isinstance(node.parent, nodes.Call) + and isinstance(node.parent.func, nodes.Name) + and node.parent.func.name in {"int", "float"} + ): + return + name = infer.qname() if isinstance(call_arg, nodes.Const): emit = False @@ -950,9 +968,34 @@ def _check_invalid_envvar_value( elif not isinstance(call_arg.value, str): emit = True if emit: - self.add_message(message, node=node, args=(name, call_arg.pytype())) + self.add_message( + message, + node=node, + args=( + "os.environ.get" if self._is_os_environ_get(node) else name, + call_arg.pytype(), + ), + ) else: - self.add_message(message, node=node, args=(name, call_arg.pytype())) + self.add_message( + message, + node=node, + args=( + "os.environ.get" if self._is_os_environ_get(node) else name, + call_arg.pytype(), + ), + ) + + def _is_os_environ_get(self, node: nodes.Call) -> bool: + try: + return ( + isinstance(node.func, nodes.Attribute) + and node.func.attrname == "get" + and node.func.expr.attrname == "environ" + and node.func.expr.expr.name == "os" + ) + except AttributeError: + return False def deprecated_methods(self) -> set[str]: return self._deprecated_methods diff --git a/tests/functional/i/invalid/invalid_envvar_value.py b/tests/functional/i/invalid/invalid_envvar_value.py index 3c1a8882bf..e4fde6d9fb 100644 --- a/tests/functional/i/invalid/invalid_envvar_value.py +++ b/tests/functional/i/invalid/invalid_envvar_value.py @@ -1,4 +1,5 @@ # pylint: disable=useless-return,missing-docstring +import os from os import getenv @@ -57,6 +58,8 @@ def deep_function_returning_bytes(): getenv(key=function_returning_string()) getenv('TEST', "value") +int(getenv("TEST", 1)) +float(getenv("TEST", 1.0)) getenv('TEST', []) # [invalid-envvar-default] getenv('TEST', None) getenv('TEST', b"123") # [invalid-envvar-default] @@ -66,6 +69,8 @@ def deep_function_returning_bytes(): getenv('TEST', function_returning_bytes()) # [invalid-envvar-default] getenv('TEST', default="value") +int(getenv("TEST", default=1)) +float(getenv("TEST", default=1.0)) getenv('TEST', default=[]) # [invalid-envvar-default] getenv('TEST', default=None) getenv('TEST', default=b"123") # [invalid-envvar-default] @@ -76,9 +81,76 @@ def deep_function_returning_bytes(): getenv(key='TEST') getenv(key='TEST', default="value") +int(getenv(key="TEST", default=1)) +float(getenv(key="TEST", default=1.0)) getenv(key='TEST', default=b"value") # [invalid-envvar-default] getenv(key='TEST', default=["Crap"]) # [invalid-envvar-default] getenv(key='TEST', default=function_returning_list()) # [invalid-envvar-default] getenv(key='TEST', default=function_returning_none()) getenv(key='TEST', default=function_returning_string()) getenv(key='TEST', default=function_returning_bytes()) # [invalid-envvar-default] + +os.environ.get() # pylint: disable=no-value-for-parameter + +os.environ.get(b"TEST") # [invalid-envvar-value] +os.environ.get("TEST") +os.environ.get(None) # [invalid-envvar-value] +os.environ.get(["Crap"]) # [invalid-envvar-value] +os.environ.get(function_returning_bytes()) # [invalid-envvar-value] +os.environ.get(deep_function_returning_bytes()) # [invalid-envvar-value] +os.environ.get(function_returning_list()) # [invalid-envvar-value] +os.environ.get(function_returning_none()) # [invalid-envvar-value] +os.environ.get(function_returning_string()) +os.environ.get(deep_function_returning_string()) + +os.environ.get(b"TEST", "default") # [invalid-envvar-value] +os.environ.get("TEST", "default") +os.environ.get(None, "default") # [invalid-envvar-value] +os.environ.get(["Crap"], "default") # [invalid-envvar-value] +os.environ.get(function_returning_bytes(), "default") # [invalid-envvar-value] +os.environ.get(function_returning_list(), "default") # [invalid-envvar-value] +os.environ.get(function_returning_none(), "default") # [invalid-envvar-value] +os.environ.get(function_returning_string(), "default") + +os.environ.get(key=b"TEST") # [invalid-envvar-value] +os.environ.get(key="TEST") +os.environ.get(key=None) # [invalid-envvar-value] +os.environ.get(key=["Crap"]) # [invalid-envvar-value] +os.environ.get(key=function_returning_bytes()) # [invalid-envvar-value] +os.environ.get(key=function_returning_list()) # [invalid-envvar-value] +os.environ.get(key=function_returning_none()) # [invalid-envvar-value] +os.environ.get(key=function_returning_string()) + +os.environ.get('TEST', "value") +int(os.environ.get("TEST", 1)) +float(os.environ.get("TEST", 1.0)) +os.environ.get('TEST', []) # [invalid-envvar-default] +os.environ.get('TEST', None) +os.environ.get('TEST', b"123") # [invalid-envvar-default] +os.environ.get('TEST', function_returning_list()) # [invalid-envvar-default] +os.environ.get('TEST', function_returning_none()) +os.environ.get('TEST', function_returning_string()) +os.environ.get('TEST', function_returning_bytes()) # [invalid-envvar-default] + +os.environ.get('TEST', default="value") +int(os.environ.get("TEST", default=1)) +float(os.environ.get("TEST", default=1.0)) +os.environ.get('TEST', default=[]) # [invalid-envvar-default] +os.environ.get('TEST', default=None) +os.environ.get('TEST', default=b"123") # [invalid-envvar-default] +os.environ.get('TEST', default=function_returning_list()) # [invalid-envvar-default] +os.environ.get('TEST', default=function_returning_none()) +os.environ.get('TEST', default=function_returning_string()) +os.environ.get('TEST', default=function_returning_bytes()) # [invalid-envvar-default] + +os.environ.get(key='TEST') +os.environ.get(key='TEST', default="value") +int(os.environ.get(key="TEST", default=1)) +float(os.environ.get(key="TEST", default=1.0)) +os.environ.get(key='TEST', default=b"value") # [invalid-envvar-default] +os.environ.get(key='TEST', default=["Crap"]) # [invalid-envvar-default] +os.environ.get(key='TEST', default=function_returning_list()) # [invalid-envvar-default] +os.environ.get(key='TEST', default=function_returning_none()) +os.environ.get(key='TEST', default=function_returning_string()) +os.environ.get(key='TEST', default=function_returning_bytes()) # [invalid-envvar-default] +os.environ.get(key='TEST', default=function_returning_bytes()) # [invalid-envvar-default] diff --git a/tests/functional/i/invalid/invalid_envvar_value.txt b/tests/functional/i/invalid/invalid_envvar_value.txt index 21a20826e7..22c6ae9288 100644 --- a/tests/functional/i/invalid/invalid_envvar_value.txt +++ b/tests/functional/i/invalid/invalid_envvar_value.txt @@ -1,31 +1,63 @@ -invalid-envvar-value:30:0:30:15::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:32:0:32:12::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-value:33:0:33:16::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:34:0:34:34::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:35:0:35:39::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:36:0:36:33::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:37:0:37:33::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-value:41:0:41:26::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:43:0:43:23::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-value:44:0:44:27::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:45:0:45:45::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:46:0:46:44::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:47:0:47:44::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-value:50:0:50:19::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:52:0:52:16::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-value:53:0:53:20::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:54:0:54:38::os.getenv does not support builtins.bytes type argument:UNDEFINED -invalid-envvar-value:55:0:55:37::os.getenv does not support builtins.list type argument:UNDEFINED -invalid-envvar-value:56:0:56:37::os.getenv does not support builtins.NoneType type argument:UNDEFINED -invalid-envvar-default:60:0:60:18::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:62:0:62:22::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED -invalid-envvar-default:63:0:63:41::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:66:0:66:42::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED -invalid-envvar-default:69:0:69:26::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:71:0:71:30::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED -invalid-envvar-default:72:0:72:49::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:75:0:75:50::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED -invalid-envvar-default:79:0:79:36::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED -invalid-envvar-default:80:0:80:36::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:81:0:81:53::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED -invalid-envvar-default:84:0:84:54::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-value:31:0:31:15::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:33:0:33:12::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:34:0:34:16::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:35:0:35:34::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:36:0:36:39::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:37:0:37:33::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:38:0:38:33::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:42:0:42:26::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:44:0:44:23::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:45:0:45:27::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:46:0:46:45::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:47:0:47:44::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:48:0:48:44::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:51:0:51:19::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:53:0:53:16::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:54:0:54:20::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:55:0:55:38::os.getenv does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:56:0:56:37::os.getenv does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:57:0:57:37::os.getenv does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-default:63:0:63:18::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:65:0:65:22::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:66:0:66:41::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:69:0:69:42::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:74:0:74:26::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:76:0:76:30::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:77:0:77:49::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:80:0:80:50::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:86:0:86:36::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:87:0:87:36::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:88:0:88:53::os.getenv default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:91:0:91:54::os.getenv default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-value:95:0:95:23::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:97:0:97:20::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:98:0:98:24::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:99:0:99:42::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:100:0:100:47::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:101:0:101:41::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:102:0:102:41::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:106:0:106:34::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:108:0:108:31::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:109:0:109:35::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:110:0:110:53::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:111:0:111:52::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:112:0:112:52::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:115:0:115:27::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:117:0:117:24::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-value:118:0:118:28::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:119:0:119:46::os.environ.get does not support builtins.bytes type argument:UNDEFINED +invalid-envvar-value:120:0:120:45::os.environ.get does not support builtins.list type argument:UNDEFINED +invalid-envvar-value:121:0:121:45::os.environ.get does not support builtins.NoneType type argument:UNDEFINED +invalid-envvar-default:127:0:127:26::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:129:0:129:30::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:130:0:130:49::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:133:0:133:50::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:138:0:138:34::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:140:0:140:38::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:141:0:141:57::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:144:0:144:58::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:150:0:150:44::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:151:0:151:44::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:152:0:152:61::os.environ.get default type is builtins.list. Expected str or None.:UNDEFINED +invalid-envvar-default:155:0:155:62::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED +invalid-envvar-default:156:0:156:62::os.environ.get default type is builtins.bytes. Expected str or None.:UNDEFINED