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)

Reply via email to