Deepyaman Datta created FLINK-40529:
---------------------------------------
Summary: PyFlink @Deprecated decorator warns at import time
instead of on use
Key: FLINK-40529
URL: https://issues.apache.org/jira/browse/FLINK-40529
Project: Flink
Issue Type: Bug
Affects Versions: 2.1.3, 2.2.1, 2.3.0, 2.1.2, 2.1.1, 2.2.0, 2.1.0
Reporter: Deepyaman Datta
h2. Problem
{\{Deprecated}} in \{{flink-python/pyflink/util/api_stability_decorators.py}}
calls \{{warnings.warn(...)}} inside \{{__call__}}. Since
\{{@Deprecated(since=...)}} constructs the decorator instance and the decorator
syntax then invokes \{{__call__}} to *apply* it, the warning fires at
decoration time — that is, at import — and the decorated function/class is
returned unwrapped.
As a result:
* Importing \{{pyflink.table}} emits a \{{DeprecationWarning}} for every
deprecated API it defines, whether or not the user ever touches them.
* Actually calling a deprecated API emits nothing.
* \{{stacklevel=2}} points at the decoration site inside PyFlink's own source,
not at user code.
h2. How to reproduce
{code}
$ cd flink-python
$ python -W error::DeprecationWarning -c "import pyflink.table"
Traceback (most recent call last):
...
File ".../pyflink/table/table_schema.py", line 28, in <module>
@Deprecated(since="2.1.0", detail="""
File ".../pyflink/util/api_stability_decorators.py", line 141, in __call__
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
DeprecationWarning: TableSchema has been deprecated since version 2.1.0. ...
{code}
Conversely, calling \{{Table.get_schema()}} or instantiating \{{TableSchema}}
produces no warning at all.
FLINK-37365, which introduced these decorators, describes the intended
behaviour as warning "at runtime on their invocation", so this appears to be an
oversight rather than a deliberate choice.
h2. Related defects in the same decorator
* \{{get_directive}} calls \{{dedent(self.detail)}} unguarded, so
\{{@Deprecated(since="1.0.0")}} without a \{{detail}} — the form used in the
decorator's own docstring example — raises \{{AttributeError}}.
* On Python 3.9/3.10, \{{staticmethod}}/\{{classmethod}} objects do not expose
\{{__qualname__}}, and \{{property}} objects reject attribute assignment, so
decorating either one raises.
h2. Proposed fix
Make \{{Deprecated}} warn on *use* instead of on decoration. PyFlink supports
Python >= 3.9 (\{{setup.py}}, \{{tox.ini}}), so \{{warnings.deprecated}} (PEP
702) is not available; implement it by hand, mirroring PEP 702's semantics
where reasonable.
* *Functions*: return a \{{functools.wraps}}-preserving wrapper that warns with
a \{{stacklevel}} pointing at the caller.
* *Classes*: return the class unchanged — replacing it would break
\{{isinstance}} checks and subclassing — and wrap \{{__init__}} on the class
instead. Following PEP 702, only instantiating the deprecated class itself
warns, which also avoids warning twice when a deprecated class inherits the
\{{__init__}} of a deprecated base class.
* *\{{staticmethod}}/\{{classmethod}}*: decorate the wrapped function and
re-package the descriptor. Properties, ABCs and \{{Enum}} subclasses must not
raise; where wrapping is not safe, degrade to applying the docstring directive
only.
* The message format, the \{{DeprecationWarning}} category, the
docstring/Sphinx-directive behaviour and the \{{__stability_decorators}}
attribute (introspected by \{{PythonAPICompletenessTestCase}} in
\{{pyflink/testing/test_case_utils.py}}) stay as they are, and
\{{Experimental}}, \{{Internal}}, \{{Public}} and \{{PublicEvolving}} are
unaffected.
h2. Tests
Add \{{pyflink/util/tests/test_api_stability_decorators.py}} covering: no
warning at import/decoration (including a regression test that imports
\{{pyflink.table}} in a fresh interpreter); warning on function call and on
class instantiation; attribution to the caller's frame; docstring directives
still applied; \{{__stability_decorators}} still populated; the other four
decorators still silent. \{{pyflink/util}} is not currently in the list of
modules that \{{dev/integration_test.sh}} runs, so it needs to be added there
for the new tests to run in CI.
----
_Description generated by Claude Code._
I ran into this while upgrading Ibis' Flink test infrastructure from PyFlink
1.20.2 to 2.3.0 in
[ibis-project/ibis#12093|https://github.com/ibis-project/ibis/pull/12093]. Ibis
runs pytest with \{{filterwarnings = ["error", ...]}}, so once the import
started emitting these warnings, every test that imports \{{pyflink.table}}
failed on deprecations for APIs we never call.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)