== Why is this an issue?

A format string is a string that contains placeholders, usually represented by special characters such as "%s" or "{}", depending on the technology in use. These placeholders are replaced by values when the string is printed or logged. Thus, it is required that a string is valid and arguments match replacement fields in this string.

This applies to https://docs.python.org/3/tutorial/inputoutput.html#old-string-formatting[the % operator], the https://docs.python.org/3/tutorial/inputoutput.html#the-string-format-method[str.format] method, and loggers from the https://docs.python.org/3/library/logging.html[logging] module. Internally, the latter use the `%-formatting`. The only difference is that they will log an error instead of raising an exception when the provided arguments are invalid.

Formatted string literals (also called "f-strings"; available since Python 3.6) are generally simpler to use, and any syntax mistake will cause a failure at compile time. However, it is easy to forget curly braces, which will not lead to any detectable errors.

This rule raises an issue when:

* A string formatted with `%` will not return the expected text because some arguments are unused.
* A string formatted with `str.format` will not return the expected string because some arguments are unused.
* An "f-string" doesn't contain any replacement field, which probably means some curly braces are missing.
* Loggers will log an error because their message is not formatted properly.

Rule S2275 covers cases where formatting a string will raise an exception.

== How to fix it

A `printf-`-style format string is a string that contains placeholders, which are replaced by values when the string is printed or logged. Mismatch in the format specifiers and the arguments provided can lead to incorrect strings being created.

To avoid issues, a developer should ensure that the provided arguments match format specifiers.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
"Error %(message)s" % {"message": "something failed", "extra": "some dead code"}  # Noncompliant. Remove the unused argument "extra" or add a replacement field.

"Error: User {} has not been able to access []".format("Alice", "MyFile")  # Noncompliant. Remove 1 unexpected argument or add a replacement field.

user = "Alice"
resource = "MyFile"
message = f"Error: User [user] has not been able to access [resource]"  # Noncompliant. Add replacement fields or use a normal string instead of an f-string.

import logging
logging.error("Error: User %s has not been able to access %s", "Alice")  # Noncompliant. Add 1 missing argument.
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
"Error %(message)s" % {"message": "something failed"}

"Error: User {} has not been able to access {}".format("Alice", "MyFile")

user = "Alice"
resource = "MyFile"
message = f"Error: User {user} has not been able to access {resource}"

import logging
logging.error("Error: User %s has not been able to access %s", "Alice", "MyFile")
----

== Resources

* https://docs.python.org/3/library/string.html#format-string-syntax[Python documentation - Format String Syntax]
* https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting[Python documentation - printf-style String Formatting]
* https://docs.python.org/3/howto/logging.html#loggers[Python documentation - Loggers]
* https://docs.python.org/3/howto/logging-cookbook.html#using-particular-formatting-styles-throughout-your-application[Python documentation - Using particular formatting styles throughout your application]
* https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals[Python documentation - Formatted string literals]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

=== Message

* Add replacement fields or use a normal string instead of an f-string.
* Remove this unused argument.
* Remove X unused positional arguments.
* Fix this formatted string's syntax.
* Name unnamed replacement field(s).
* Replace formatting argument(s) with a mapping; Replacement fields are named.
* Use only positional or named fields, don't mix them.
* Replace this value with a number as "%d" requires.
* Replace this value with an integer as "%X" requires.
* Replace this value with an integer as "*" requires.
* Add X missing argument(s).
* Remove X unexpected argument(s); format string expects Y arguments.
* Replace this key; %-format accepts only string keys.
* Provide a value for field "X".
* Remove this unused argument or add a replacement field.


'''
== Comments And Links
(visible only on this page)

include::../comments-and-links.adoc[]

endif::env-github,rspecator-view[]
