| # Licensed to the Apache Software Foundation (ASF) under one | |
| # or more contributor license agreements. See the NOTICE file | |
| # distributed with this work for additional information | |
| # regarding copyright ownership. The ASF licenses this file | |
| # to you under the Apache License, Version 2.0 (the | |
| # "License"); you may not use this file except in compliance | |
| # with the License. You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, | |
| # software distributed under the License is distributed on an | |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| # KIND, either express or implied. See the License for the | |
| # specific language governing permissions and limitations | |
| # under the License. | |
| from __future__ import annotations | |
| import doctest | |
| import inspect | |
| import io | |
| import pyarrow as pa | |
| from datafusion import SessionContext | |
| from datafusion.extensions import PhysicalOptimizerRuleExportable | |
| from datafusion_ffi_example import MyPhysicalOptimizerRule | |
| def test_ffi_physical_optimizer_rule_runs_during_planning(): | |
| """A rule added via add_physical_optimizer_rule is invoked while the | |
| physical plan is built, and the query still returns correct results.""" | |
| rule = MyPhysicalOptimizerRule() | |
| ctx = SessionContext() | |
| ctx.add_physical_optimizer_rule(rule) | |
| batch = pa.RecordBatch.from_arrays( | |
| [pa.array([1, 2, 3])], | |
| names=["a"], | |
| ) | |
| ctx.register_record_batches("t", [[batch]]) | |
| before = rule.optimize_calls() | |
| result = ctx.sql("SELECT a FROM t").collect() | |
| after = rule.optimize_calls() | |
| assert after > before, ( | |
| f"Expected user FFI physical optimizer rule to fire, " | |
| f"before={before} after={after}" | |
| ) | |
| assert result[0].column(0).to_pylist() == [1, 2, 3] | |
| def test_physical_optimizer_rule_docstring_example_still_runs(): | |
| """Run the ``PhysicalOptimizerRuleExportable`` docstring example verbatim. | |
| The example is marked ``+SKIP`` because the main suite has no built FFI | |
| extension to import, which is exactly how such an example rots. Here the | |
| statements are parsed out of the live docstring, the skip is dropped, and | |
| each one is executed. | |
| Only the ``+SKIP`` statements are taken. The docstring also carries a | |
| runnable example above them, which the main suite already executes under | |
| ``--doctest-modules``; running it again here would prove nothing. | |
| Only ``ctx`` is supplied, because the skipped statements go on using the | |
| context the runnable block opened. Everything else resolves for real: a | |
| renamed class, a changed signature, or a wrong expected output in the | |
| docstring fails here. | |
| """ | |
| examples = [ | |
| example | |
| for example in doctest.DocTestParser().get_examples( | |
| inspect.getdoc(PhysicalOptimizerRuleExportable) | |
| ) | |
| if example.options.pop(doctest.SKIP, False) | |
| ] | |
| assert examples, "docstring has no skipped examples to check" | |
| test = doctest.DocTest( | |
| examples, | |
| {"ctx": SessionContext()}, | |
| "PhysicalOptimizerRuleExportable", | |
| None, | |
| None, | |
| None, | |
| ) | |
| output = io.StringIO() | |
| results = doctest.DocTestRunner().run(test, out=output.write) | |
| assert results.failed == 0, output.getvalue() |