PY-82168: Implement inspection and quick fixes for dataframe and series in the conditions

(cherry picked from commit 658f2d5609852b00b7e1011c3841c7266c202686)

IJ-MR-168659

GitOrigin-RevId: 66806362570ef564b1495ba5a9afcd32a50859fb
This commit is contained in:
Ilia Zakoulov
2025-07-09 16:43:11 +02:00
committed by intellij-monorepo-bot
parent 5ad156f695
commit a8675a61ac
23 changed files with 984 additions and 1 deletions

View File

@@ -0,0 +1,26 @@
<html>
<body>
Write your description here.
Start the description with a verb in 3rd person singular, like reports, detects, highlights.
In the first sentence, briefly explain what exactly the inspection helps you detect.
Make sure the sentence is not very long and complicated.
<p>
The first sentence must be in a dedicated paragraph separated from the rest of the text. This will make the description easier to read.
Make sure the description doesnt just repeat the inspection title.
</p>
<p>
See https://plugins.jetbrains.com/docs/intellij/inspections.html#descriptions for more information.
</p>
<p>
Embed code snippets:
</p>
<pre><code>
// automatically highlighted according to inspection registration 'language' attribute
</code></pre>
<!-- tooltip end -->
<p>Text after this comment will only be shown in the settings of the inspection.</p>
<p>To open related settings directly from the description, add a link with `settings://$` optionally followed by `?$` to pre-select a UI
element.</p>
</body>
</html>

View File

@@ -249,7 +249,10 @@
implementationClass="com.jetbrains.python.inspections.PyRelativeImportInspection"/>
<localInspection language="Python" shortName="PyPandasSeriesToListInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
key="INSP.pandas.series.values.replace.with.tolist" enabledByDefault="true" level="WARNING"
implementationClass="com.jetbrains.python.inspections.PyPandasSeriesToListInspection"/>
implementationClass="com.jetbrains.python.inspections.PyPandasSeriesToListInspection" groupKey="INSP.GROUP.pandas"/>
<localInspection language="Python" shortName="PyPandasTruthValueIsAmbiguousInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
key="INSP.pandas.truth.value.is.ambiguous.df" enabledByDefault="true" level="WARNING"
implementationClass="com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection" groupKey="INSP.GROUP.pandas"/>
<listSplitJoinContext language="Python" implementationClass="com.jetbrains.python.codeInsight.editorActions.PyListSplitJoinContext"/>

View File

@@ -0,0 +1,31 @@
<html>
<body>
<p>
Reports ambiguous usage of a pandas <code>DataFrame</code> or <code>Series</code> in a boolean context,
such as <code>if</code>, <code>while</code>, or logical expressions.
This typically leads to the runtime error:
<code>ValueError: The truth value of a DataFrame is ambiguous</code>.
</p>
<p>
In pandas, expressions like <code>df</code> or <code>df == other</code> do not return a single boolean value,
but rather a DataFrame or Series of booleans. Using these in control flow without explicit reduction
(e.g., <code>.any()</code>, <code>.all()</code>, or <code>.empty</code>) is ambiguous and will raise an exception.
</p>
<p><b>Example:</b></p>
<pre style="font-family: monospace">
if df: # ❌ Raises ValueError: The truth value of a DataFrame is ambiguous
print("DataFrame exists")
if not df.empty: # ✅ Checks if DataFrame has any rows
print("DataFrame exists")
</pre>
<p>
When the quick-fix is applied, the condition is replaced with an appropriate reducer
like <code>.any()</code>, <code>.all()</code>, or <code>.empty</code> depending on the context.
</p>
</body>
</html>

View File

@@ -1279,8 +1279,13 @@ INSP.class.var.is.not.allowed.here='ClassVar' is not allowed here
INSP.class.var.not.a.valid.type=Not a valid type
# Pandas-Specific inspections and quick fixes
INSP.GROUP.pandas=Pandas
INSP.pandas.series.values.replace.with.tolist=Method Series.to_list() is recommended
QFIX.pandas.series.values.replace.with.tolist=Replace list(Series.values) with Series.to_list()
INSP.pandas.truth.value.is.ambiguous.df=The truth value of a DataFrame is ambiguous
INSP.pandas.truth.value.is.ambiguous.series=The truth value of a Series is ambiguous
QFIX.pandas.truth.value.is.ambiguous.emptiness.check=Replace with explicit emptiness check
QFIX.pandas.truth.value.is.ambiguous.none.check=Replace with explicit None check
# PyTypeParameterListAnnotator
type.param.list.annotator.type.parameter.already.defined=Type parameter with name ''{0}'' is already defined in this type parameter list

View File

@@ -0,0 +1,140 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.inspections
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import com.jetbrains.python.PyNames
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.types.PyUnionType
import com.jetbrains.python.psi.types.TypeEvalContext
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
class PyPandasTruthValueIsAmbiguousInspection : PyInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return Visitor(holder, PyInspectionVisitor.getContext(session))
}
private class Visitor(private val holder: ProblemsHolder, context: TypeEvalContext) : PyInspectionVisitor(holder, context) {
override fun visitPyAssertStatement(node: PyAssertStatement) {
super.visitPyAssertStatement(node)
val expression = node.getArguments().firstOrNull() ?: return
reportProblems(expression)
}
override fun visitPyConditionalStatementPart(node: PyConditionalStatementPart) {
super.visitPyConditionalStatementPart(node)
val condition = node.condition ?: return
reportProblems(condition)
}
private fun reportProblems(expression: PyExpression) {
if (expression is PyBinaryExpression) {
expression.accept(BinaryExpressionRecursiveVisitor(holder, myTypeEvalContext))
}
else {
expression.reportProblemIfPandasAmbiguousTarget(holder, myTypeEvalContext)
}
}
}
private class BinaryExpressionRecursiveVisitor(private val holder: ProblemsHolder, private val context: TypeEvalContext) : PyRecursiveElementVisitor() {
override fun visitPyBinaryExpression(node: PyBinaryExpression) {
val isIdentity = node.isOperator(PyNames.IS) || node.isOperator("isnot")
if (!isIdentity) {
val suggestedQuickFixes = node.isOperator(PyNames.AND) || node.isOperator(PyNames.OR) || node.isOperator("==")
node.leftExpression?.reportProblemIfPandasAmbiguousTarget(holder, context, suggestedQuickFixes)
node.rightExpression?.reportProblemIfPandasAmbiguousTarget(holder, context, suggestedQuickFixes)
}
super.visitPyBinaryExpression(node)
}
}
}
private fun PyExpression?.reportProblemIfPandasAmbiguousTarget(holder: ProblemsHolder, typeEvalContext: TypeEvalContext, suggestQuickFixes: Boolean = true) {
this ?: return
val pandasAmbiguousTarget = when (this) {
is PyPrefixExpression -> this.operand.resolvePandasAmbiguousTarget(typeEvalContext)
is PyReferenceExpression -> this.resolvePandasAmbiguousTarget(typeEvalContext)
else -> null
} ?: return
val quickFixes = when {
!suggestQuickFixes -> emptyArray()
this is PyPrefixExpression -> arrayOf(NotPrefixExpressionReplaceWithEmptyCheckQuickFix())
this is PyReferenceExpression -> arrayOf(
ReferenceExpressionReplaceWithEmptyCheckQuickFix(),
ReferenceExpressionReplaceWithExplicitNotNoneCheckQuickFix()
)
else -> emptyArray()
}
holder.registerProblem(this, PyPsiBundle.message(pandasAmbiguousTarget.inspectionBundleName),
*quickFixes)
}
private enum class PandasAmbiguousTarget(
val qualifiedName: String,
val inspectionBundleName: String,
) {
DATA_FRAME(
qualifiedName = "pandas.core.frame.DataFrame",
inspectionBundleName = "INSP.pandas.truth.value.is.ambiguous.df"
),
SERIES(
qualifiedName = "pandas.core.series.Series",
inspectionBundleName = "INSP.pandas.truth.value.is.ambiguous.series"
)
}
private fun PyExpression?.resolvePandasAmbiguousTarget(typeEvalContext: TypeEvalContext): PandasAmbiguousTarget? {
this ?: return null
val type = typeEvalContext.getType(this)
val typesToCheck = if (type is PyUnionType) {
type.members
}
else {
listOf(type)
}
return typesToCheck.firstNotNullOfOrNull { memberType ->
val qualifiedName = memberType?.declarationElement?.qualifiedName
PandasAmbiguousTarget.entries.find { it.qualifiedName == qualifiedName }
}
}
private class ReferenceExpressionReplaceWithEmptyCheckQuickFix() : LocalQuickFix {
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement as? PyReferenceExpression ?: return
val factory = PyElementGenerator.getInstance(project)
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "not ${element.text}.empty")
element.replace(newElement)
}
}
private class ReferenceExpressionReplaceWithExplicitNotNoneCheckQuickFix() : LocalQuickFix {
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.none.check")
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement as? PyReferenceExpression ?: return
val factory = PyElementGenerator.getInstance(project)
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "${element.text} is not None")
element.replace(newElement)
}
}
private class NotPrefixExpressionReplaceWithEmptyCheckQuickFix() : LocalQuickFix {
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement as? PyPrefixExpression ?: return
val operand = element.operand ?: return
val factory = PyElementGenerator.getInstance(project)
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "${operand.text}.empty")
element.replace(newElement)
}
}

View File

@@ -0,0 +1,37 @@
from typing import Optional
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
other_df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning>: ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> == None: ...
if <warning descr="The truth value of a DataFrame is ambiguous">not df</warning>: ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> == <warning descr="The truth value of a DataFrame is ambiguous">other_df</warning>: ...
df_1: Optional[pd.DataFrame] = df
if <warning descr="The truth value of a DataFrame is ambiguous">df_1</warning>: ...
def e(): ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> or e(): ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> and <warning descr="The truth value of a DataFrame is ambiguous">other_df</warning>: ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> > 5: ...
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> and (<warning descr="The truth value of a DataFrame is ambiguous">df</warning> or <warning descr="The truth value of a DataFrame is ambiguous">df</warning>): ...
while <warning descr="The truth value of a DataFrame is ambiguous">df</warning>: ...
assert <warning descr="The truth value of a DataFrame is ambiguous">df</warning>
assert <warning descr="The truth value of a DataFrame is ambiguous">not df</warning>
if df is None: ...
if df.empty: ...
if not df.empty: ...

View File

@@ -0,0 +1,2 @@
from core.frame import DataFrame
from core.series import Series

View File

@@ -0,0 +1,18 @@
from series import Series
class NDFrame:
...
class DataFrame:
def __getitem__(self, key):
if key == "1":
return None
if key == "2":
return Series()
if key == "3":
return NDFrame()
if key == "4":
return DataFrame()

View File

@@ -0,0 +1,292 @@
class property(object):
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
"""
def deleter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different deleter. """
pass
def getter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different getter. """
pass
def setter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different setter. """
pass
def __delete__(self, *args, **kwargs): # real signature unknown
""" Delete an attribute of instance. """
pass
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
def __get__(self, *args, **kwargs): # real signature unknown
""" Return an attribute of instance, which is of type owner. """
pass
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
# (copied from class doc)
"""
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
def __set__(self, *args, **kwargs): # real signature unknown
""" Set an attribute of instance to value. """
pass
fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
class IndexOpsMixin():
"""
Common ops mixin to support a unified interface / docs for Series / Index
"""
def tolist(self):
"""
Return a list of the values.
These are each a scalar type, which is a Python scalar
(for str, int, float) or a pandas scalar
(for Timestamp/Timedelta/Interval/Period)
Returns
-------
list
See Also
--------
numpy.ndarray.tolist : Return the array as an a.ndim-levels deep
nested list of Python scalars.
"""
# return self._values.tolist()
...
to_list = tolist
class NDFrame:
...
class Series(IndexOpsMixin, NDFrame):
"""
One-dimensional ndarray with axis labels (including time series).
Labels need not be unique but must be a hashable type. The object
supports both integer- and label-based indexing and provides a host of
methods for performing operations involving the index. Statistical
methods from ndarray have been overridden to automatically exclude
missing data (currently represented as NaN).
Operations between Series (+, -, /, \\*, \\*\\*) align values based on their
associated index values-- they need not be the same length. The result
index will be the sorted union of the two indexes.
Parameters
----------
data : array-like, Iterable, dict, or scalar value
Contains data stored in Series. If data is a dict, argument order is
maintained.
index : array-like or Index (1d)
Values must be hashable and have the same length as `data`.
Non-unique index values are allowed. Will default to
RangeIndex (0, 1, 2, ..., n) if not provided. If data is dict-like
and index is None, then the keys in the data are used as the index. If the
index is not None, the resulting Series is reindexed with the index values.
dtype : str, numpy.dtype, or ExtensionDtype, optional
Data type for the output Series. If not specified, this will be
inferred from `data`.
See the :ref:`user guide <basics.dtypes>` for more usages.
name : str, optional
The name to give to the Series.
copy : bool, default False
Copy input data. Only affects Series or 1d ndarray input. See examples.
Examples
--------
Constructing Series from a dictionary with an Index specified
# >>> d = {'a': 1, 'b': 2, 'c': 3}
# >>> ser = pd.Series(data=d, index=['a', 'b', 'c'])
# >>> ser
a 1
b 2
c 3
dtype: int64
The keys of the dictionary match with the Index values, hence the Index
values have no effect.
# >>> d = {'a': 1, 'b': 2, 'c': 3}
# >>> ser = pd.Series(data=d, index=['x', 'y', 'z'])
# >>> ser
x NaN
y NaN
z NaN
dtype: float64
Note that the Index is first build with the keys from the dictionary.
After this the Series is reindexed with the given Index values, hence we
get all NaN as a result.
Constructing Series from a list with `copy=False`.
# >>> r = [1, 2]
# >>> ser = pd.Series(r, copy=False)
# >>> ser.iloc[0] = 999
# >>> r
[1, 2]
# >>> ser
0 999
1 2
dtype: int64
Due to input data type the Series has a `copy` of
the original data even though `copy=False`, so
the data is unchanged.
Constructing Series from a 1d ndarray with `copy=False`.
# >>> r = np.array([1, 2])
# >>> ser = pd.Series(r, copy=False)
# >>> ser.iloc[0] = 999
# >>> r
array([999, 2])
# >>> ser
0 999
1 2
dtype: int64
Due to input data type the Series has a `view` on
the original data, so
the data is changed as well.
"""
def __init__(
self,
data=None,
index=None,
dtype = None,
name=None,
copy = False,
fastpath = False,
):
...
@property
def values(self):
"""
Return Series as ndarray or ndarray-like depending on the dtype.
.. warning::
We recommend using :attr:`Series.array` or
:meth:`Series.to_numpy`, depending on whether you need
a reference to the underlying data or a NumPy array.
Returns
-------
numpy.ndarray or ndarray-like
See Also
--------
Series.array : Reference to the underlying data.
Series.to_numpy : A NumPy array representing the underlying data.
Examples
--------
# >>> pd.Series([1, 2, 3]).values
array([1, 2, 3])
# >>> pd.Series(list('aabc')).values
array(['a', 'a', 'b', 'c'], dtype=object)
# >>> pd.Series(list('aabc')).astype('category').values
['a', 'a', 'b', 'c']
Categories (3, object): ['a', 'b', 'c']
Timezone aware datetime data is converted to UTC:
# >>> pd.Series(pd.date_range('20130101', periods=3,
... tz='US/Eastern')).values
array(['2013-01-01T05:00:00.000000000',
'2013-01-02T05:00:00.000000000',
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
"""
# return self._mgr.external_values()
...

View File

@@ -0,0 +1,37 @@
from typing import Optional
import pandas as pd
series = pd.Series([1, 2, 3])
other_series = pd.Series([4, 5, 6])
if <warning descr="The truth value of a Series is ambiguous">series</warning>: ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> == None: ...
if <warning descr="The truth value of a Series is ambiguous">not series</warning>: ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> == <warning descr="The truth value of a Series is ambiguous">other_series</warning>: ...
series_1: Optional[pd.Series] = series
if <warning descr="The truth value of a Series is ambiguous">series_1</warning>: ...
def e(): ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> or e(): ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> and <warning descr="The truth value of a Series is ambiguous">other_series</warning>: ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> > 5: ...
if <warning descr="The truth value of a Series is ambiguous">series</warning> and (<warning descr="The truth value of a Series is ambiguous">series</warning> or <warning descr="The truth value of a Series is ambiguous">series</warning>): ...
while <warning descr="The truth value of a Series is ambiguous">series</warning>: ...
assert <warning descr="The truth value of a Series is ambiguous">series</warning>
assert <warning descr="The truth value of a Series is ambiguous">not series</warning>
if series is None: ...
if series.empty: ...
if not series.empty: ...

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if <warning descr="The truth value of a DataFrame is ambiguous">d<caret>f</warning>:
pass

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if not df.empty:
pass

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if <warning descr="The truth value of a DataFrame is ambiguous">not d<caret>f</warning>:
pass

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if df.empty:
pass

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if <warning descr="The truth value of a DataFrame is ambiguous">d<caret>f</warning>:
pass

View File

@@ -0,0 +1,5 @@
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
if df is not None:
pass

View File

@@ -0,0 +1,2 @@
from series import Series
from core.frame import DataFrame

View File

@@ -0,0 +1,18 @@
from series import Series
class NDFrame:
...
class DataFrame:
def __getitem__(self, key):
if key == "1":
return None
if key == "2":
return Series()
if key == "3":
return NDFrame()
if key == "4":
return DataFrame()

View File

@@ -0,0 +1,292 @@
class property(object):
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
"""
def deleter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different deleter. """
pass
def getter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different getter. """
pass
def setter(self, *args, **kwargs): # real signature unknown
""" Descriptor to obtain a copy of the property with a different setter. """
pass
def __delete__(self, *args, **kwargs): # real signature unknown
""" Delete an attribute of instance. """
pass
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
def __get__(self, *args, **kwargs): # real signature unknown
""" Return an attribute of instance, which is of type owner. """
pass
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
# (copied from class doc)
"""
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
def __set__(self, *args, **kwargs): # real signature unknown
""" Set an attribute of instance to value. """
pass
fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
class IndexOpsMixin():
"""
Common ops mixin to support a unified interface / docs for Series / Index
"""
def tolist(self):
"""
Return a list of the values.
These are each a scalar type, which is a Python scalar
(for str, int, float) or a pandas scalar
(for Timestamp/Timedelta/Interval/Period)
Returns
-------
list
See Also
--------
numpy.ndarray.tolist : Return the array as an a.ndim-levels deep
nested list of Python scalars.
"""
# return self._values.tolist()
...
to_list = tolist
class NDFrame:
...
class Series(IndexOpsMixin, NDFrame):
"""
One-dimensional ndarray with axis labels (including time series).
Labels need not be unique but must be a hashable type. The object
supports both integer- and label-based indexing and provides a host of
methods for performing operations involving the index. Statistical
methods from ndarray have been overridden to automatically exclude
missing data (currently represented as NaN).
Operations between Series (+, -, /, \\*, \\*\\*) align values based on their
associated index values-- they need not be the same length. The result
index will be the sorted union of the two indexes.
Parameters
----------
data : array-like, Iterable, dict, or scalar value
Contains data stored in Series. If data is a dict, argument order is
maintained.
index : array-like or Index (1d)
Values must be hashable and have the same length as `data`.
Non-unique index values are allowed. Will default to
RangeIndex (0, 1, 2, ..., n) if not provided. If data is dict-like
and index is None, then the keys in the data are used as the index. If the
index is not None, the resulting Series is reindexed with the index values.
dtype : str, numpy.dtype, or ExtensionDtype, optional
Data type for the output Series. If not specified, this will be
inferred from `data`.
See the :ref:`user guide <basics.dtypes>` for more usages.
name : str, optional
The name to give to the Series.
copy : bool, default False
Copy input data. Only affects Series or 1d ndarray input. See examples.
Examples
--------
Constructing Series from a dictionary with an Index specified
# >>> d = {'a': 1, 'b': 2, 'c': 3}
# >>> ser = pd.Series(data=d, index=['a', 'b', 'c'])
# >>> ser
a 1
b 2
c 3
dtype: int64
The keys of the dictionary match with the Index values, hence the Index
values have no effect.
# >>> d = {'a': 1, 'b': 2, 'c': 3}
# >>> ser = pd.Series(data=d, index=['x', 'y', 'z'])
# >>> ser
x NaN
y NaN
z NaN
dtype: float64
Note that the Index is first build with the keys from the dictionary.
After this the Series is reindexed with the given Index values, hence we
get all NaN as a result.
Constructing Series from a list with `copy=False`.
# >>> r = [1, 2]
# >>> ser = pd.Series(r, copy=False)
# >>> ser.iloc[0] = 999
# >>> r
[1, 2]
# >>> ser
0 999
1 2
dtype: int64
Due to input data type the Series has a `copy` of
the original data even though `copy=False`, so
the data is unchanged.
Constructing Series from a 1d ndarray with `copy=False`.
# >>> r = np.array([1, 2])
# >>> ser = pd.Series(r, copy=False)
# >>> ser.iloc[0] = 999
# >>> r
array([999, 2])
# >>> ser
0 999
1 2
dtype: int64
Due to input data type the Series has a `view` on
the original data, so
the data is changed as well.
"""
def __init__(
self,
data=None,
index=None,
dtype = None,
name=None,
copy = False,
fastpath = False,
):
...
@property
def values(self):
"""
Return Series as ndarray or ndarray-like depending on the dtype.
.. warning::
We recommend using :attr:`Series.array` or
:meth:`Series.to_numpy`, depending on whether you need
a reference to the underlying data or a NumPy array.
Returns
-------
numpy.ndarray or ndarray-like
See Also
--------
Series.array : Reference to the underlying data.
Series.to_numpy : A NumPy array representing the underlying data.
Examples
--------
# >>> pd.Series([1, 2, 3]).values
array([1, 2, 3])
# >>> pd.Series(list('aabc')).values
array(['a', 'a', 'b', 'c'], dtype=object)
# >>> pd.Series(list('aabc')).astype('category').values
['a', 'a', 'b', 'c']
Categories (3, object): ['a', 'b', 'c']
Timezone aware datetime data is converted to UTC:
# >>> pd.Series(pd.date_range('20130101', periods=3,
... tz='US/Eastern')).values
array(['2013-01-01T05:00:00.000000000',
'2013-01-02T05:00:00.000000000',
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
"""
# return self._mgr.external_values()
...

View File

@@ -0,0 +1,20 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.inspections
import com.jetbrains.python.fixtures.PyInspectionTestCase
class PyPandasTruthValueIsAmbiguousInspectionTest : PyInspectionTestCase() {
override fun getInspectionClass(): Class<out PyInspection?> {
return PyPandasTruthValueIsAmbiguousInspection::class.java
}
fun testDataFrame() {
myFixture.copyDirectoryToProject("inspections/PyPandasTruthValueIsAmbiguousInspection/pandas", "pandas")
doTest()
}
fun testSeries() {
myFixture.copyDirectoryToProject("inspections/PyPandasTruthValueIsAmbiguousInspection/pandas", "pandas")
doTest()
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.quickFixes
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.PyQuickFixTestCase
import com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection
class PyPandasTruthValueIsAmbiguousQuickFixTest : PyQuickFixTestCase() {
private val emptinessQuickFixName = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
private val noneQuickFixName = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.none.check")
@Throws(Exception::class)
override fun setUp() {
super.setUp()
myFixture.copyDirectoryToProject("", "")
}
fun testEmptyCheck() {
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, emptinessQuickFixName)
}
fun testNotNone() {
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, noneQuickFixName)
}
fun testNegativeEmptyCheck() {
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, emptinessQuickFixName)
}
}