rspec/rules/S5795/python/rule.adoc
2022-02-04 16:28:24 +00:00

78 lines
2.9 KiB
Plaintext

Identity operators ``++is++`` and ``++is not++`` check if the same object is on both sides, i.e. ``++a is b++`` returns ``++True++`` if ``++id(a) == id(b)++``.
Integers, bytes, floats, strings, frozensets and tuples should not be compared with identity operators because the result may not be as expected. If you need to compare these types you should use instead equality operators ``++==++`` or ``++!=++``.
The CPython interpreter caches certain builtin values for integers, bytes, floats, strings, frozensets and tuples. For example, the literal ``++1++`` will create the same object as ``++int("1")++``, which means that ``++1 is int("1")++`` is True. However this works only by chance as other integer values are not cached, for example ``++int("1000") is 1000++`` will always be ``++False++``. This behavior is not part of Python language specification and could vary between interpreters. CPython 3.8 even https://docs.python.org/3.8/whatsnew/3.8.html#changes-in-python-behavior[warns about comparing literals using identity operators].
The only case where using the "is" operator with a cached type is ok is with "interned" strings. Note however that interned strings don't necessarily have the same identity as string literals.
This rule raises an issue when at least one operand of an identity operator:
* is of type ``++int++``, ``++bytes++``, ``++float++``, ``++frozenset++`` or ``++tuple++``.
* or it is a string literal.
== Noncompliant Code Example
[source,python]
----
def literal_comparison(param):
param is 2000 # Noncompliant
literal_comparison(2000) # will return True
literal_comparison(int("2000")) # will return False
() is tuple() # Noncompliant. Always True
(1,) is tuple([1]) # Noncompliant. Always False
from sys import intern
with open("test.txt") as f: # test.txt contains "blabla\n"
text = f.read()
intern(text) is "blabla\n" # Noncompliant. Always False
----
== Compliant Solution
[source,python]
----
def literal_comparison(param):
param == 2000
literal_comparison(2000) # will return True
literal_comparison(int("2000")) # will return True
() == tuple() # Always True
(1,) == tuple([1]) # Always True
from sys import intern
with open("tmp/test.txt") as f: # test.txt contains "blabla\n"
text = f.read()
intern(text) is intern("blabla\n") # Always True
----
== See
* https://adamj.eu/tech/2020/01/21/why-does-python-3-8-syntaxwarning-for-is-literal/[Why does Python 3.8 log a SyntaxWarning for 'is' with literals?] - Adam Johnson
* https://treyhunner.com/2019/03/unique-and-sentinel-values-in-python/#Equality_vs_identity[Equality vs identity] - Trey Hunner
* https://docs.python.org/3.7/library/sys.html?highlight=sys.intern#sys.intern[Python documentation - sys.intern]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
include::message.adoc[]
include::highlighting.adoc[]
endif::env-github,rspecator-view[]