| # 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. |
| |
| [project] |
| name = "apache-airflow-ctl" |
| dynamic = ["version"] |
| description = "Apache Airflow command line tool for communicating with an Apache Airflow, using the API." |
| readme = { file = "README.md", content-type = "text/markdown" } |
| license = "Apache-2.0" |
| license-files = ["LICENSE", "NOTICE"] |
| # We do not want to upper-bind Python version unnecessarily. Airflow-ctl is a small tool with |
| # minimal dependencies and does not use sophisticated Python features, so it should work with |
| # new Python versions once all dependencies are updated to support them. |
| requires-python = ">=3.10,!=3.15" |
| dependencies = [ |
| # TODO there could be still missing deps such as airflow-core |
| "argcomplete>=1.10", |
| # Remove the <1.0 cap after migrating to httpx 1.x (ground-up API rewrite — no HTTPStatusError etc.); |
| # tracked at https://github.com/apache/airflow/issues/65609 |
| "httpx>=0.27.0,<1.0", |
| "keyring>=25.7.0", |
| "lazy-object-proxy>=1.2.0", |
| "methodtools>=0.4.7", |
| "platformdirs>=4.3.6", |
| "pydantic>=2.11.0", # https://github.com/apache/airflow/issues/56482 |
| "pyyaml>=6.0.3", |
| "rich-argparse>=1.0.0", |
| "structlog>=26.1.0", |
| "uuid6>=2024.7.10", |
| "tabulate>=0.9.0", |
| "tenacity>=9.1.4", |
| ] |
| |
| classifiers = [ |
| "Framework :: Apache Airflow", |
| ] |
| |
| [project.urls] |
| "Bug Tracker" = "https://github.com/apache/airflow/issues" |
| Documentation = "https://airflow.apache.org/docs/apache-airflow-ctl/stable/index.html" |
| Downloads = "https://archive.apache.org/dist/airflow/airflow-ctl/" |
| Homepage = "https://airflow.apache.org/" |
| "Release Notes" = "https://airflow.apache.org/docs/apache-airflow-ctl/stable/changelog.html" |
| "Slack Chat" = "https://s.apache.org/airflow-slack" |
| "Source Code" = "https://github.com/apache/airflow" |
| LinkedIn = "https://www.linkedin.com/company/apache-airflow/" |
| Mastodon = "https://fosstodon.org/@airflow" |
| Bluesky = "https://bsky.app/profile/apache-airflow.bsky.social" |
| YouTube = "https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/" |
| |
| |
| [project.optional-dependencies] |
| "dev" = [ |
| "keyrings.alt>=5.0.2", |
| ] |
| |
| [project.scripts] |
| airflowctl = "airflowctl.__main__:main" |
| # Redundant definition allowing direct execution via `uvx apache-airflow-ctl ...` |
| apache-airflow-ctl = "airflowctl.__main__:main" |
| |
| [build-system] |
| requires = [ |
| "hatchling==1.32.4", |
| "packaging==26.3", |
| "pathspec==1.1.1", |
| "pluggy==1.6.0", |
| "tomli==2.4.1; python_version < '3.11'", |
| "tomlkit==0.15.1", |
| "trove-classifiers==2026.9.21.13", |
| ] |
| build-backend = "hatchling.build" |
| |
| [tool.hatch.version] |
| path = "src/airflowctl/__init__.py" |
| |
| [tool.hatch.build.targets.wheel] |
| packages = ["src/airflowctl"] |
| |
| [tool.ruff] |
| extend = "../pyproject.toml" |
| src = ["src"] |
| namespace-packages = ["src/airflowctl"] |
| |
| [tool.ruff.lint.per-file-ignores] |
| |
| # Ignore Doc rules et al for anything outside of tests |
| "!src/*" = ["D", "TID253", "S101", "TRY002"] |
| |
| # Ignore the pytest rules outside the tests folder - https://github.com/astral-sh/ruff/issues/14205 |
| "!tests/*" = ["PT"] |
| |
| # Pycharm barfs if this "stub" file has future imports |
| "src/airflowctl/__init__.py" = ["I002"] |
| |
| # Generated file, be less strict |
| "src/airflowctl/api/datamodels/generated.py" = ["UP007", "D101", "D200"] |
| "src/airflowctl/api/datamodels/auth_generated.py" = ["UP007", "D101", "D200"] |
| |
| [tool.coverage.run] |
| branch = true |
| relative_files = true |
| source = ["src/airflowctl"] |
| include_namespace_packages = true |
| |
| [tool.coverage.report] |
| skip_empty = true |
| exclude_also = [ |
| "def __repr__", |
| "raise AssertionError", |
| "raise NotImplementedError", |
| "if __name__ == .__main__.:", |
| "@(abc\\.)?abstractmethod", |
| "@(typing(_extensions)?\\.)?overload", |
| "if (typing(_extensions)?\\.)?TYPE_CHECKING:", |
| ] |
| |
| [dependency-groups] |
| # To build docs: |
| # |
| # uv run --group docs build-docs |
| # |
| # To enable auto-refreshing build with server: |
| # |
| # uv run --group docs build-docs --autobuild |
| # |
| # To see more options: |
| # |
| # uv run --group docs build-docs --help |
| # |
| docs = [ |
| "apache-airflow-devel-common[docs]" |
| ] |
| dev = [ |
| "apache-airflow-ctl[dev]", |
| "apache-airflow-devel-common", |
| ] |
| codegen = [ |
| "datamodel-code-generator[http]>=0.71.0", |
| "apache-airflow-devel-common" |
| ] |
| |
| # uv run --verbose --group codegen --project apache-airflow-ctl --directory airflow-ctl/ datamodel-codegen --url="http://0.0.0.0:28080/auth/openapi.json" --output=src/airflowctl/api/datamodels/auth_generated.py |
| mypy = [ |
| "apache-airflow-devel-common[mypy]", |
| ] |
| |
| [tool.datamodel-codegen] |
| capitalise-enum-members=true # `State.RUNNING` not `State.running` |
| disable-timestamp=true |
| enable-version-header=true |
| enum-field-as-literal='one' # When a single enum member, make it output a `Literal["..."]` |
| input-file-type='openapi' |
| output-model-type='pydantic_v2.BaseModel' |
| output-datetime-class='datetime' |
| target-python-version='3.10' |
| use-annotated=true |
| use-default=true |
| use-double-quotes=true |
| use-schema-description=true # Desc becomes class doc comment |
| use-standard-collections=true # list[] not List[] |
| use-subclass-enum=true # enum, not union of Literals |
| use-union-operator=true # +annotations, not `Union[]` |
| |
| input = "../airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml" |
| output = "src/airflowctl/api/datamodels/generated.py" |
| |
| ## pytest settings ## |
| [tool.pytest] |
| addopts = [ |
| "--tb=short", |
| "-rasl", |
| "--verbosity=2", |
| # Disable `flaky` plugin for pytest. This plugin conflicts with `rerunfailures` because provide the same marker. |
| "-p", "no:flaky", |
| # Disable `nose` builtin plugin for pytest. This feature is deprecated in 7.2 and will be removed in pytest>=8 |
| "-p", "no:nose", |
| # Disable support of a legacy `LocalPath` in favor of stdlib `pathlib.Path`. |
| "-p", "no:legacypath", |
| # Disable warnings summary, because we use our warning summary. |
| "--disable-warnings", |
| "--asyncio-mode=strict", |
| ] |
| markers = [ |
| "flaky(reruns, reruns_delay): retry test on failure using pytest-rerunfailures" |
| ] |
| norecursedirs = [ |
| ".eggs", |
| ] |
| log_level = "INFO" |
| filterwarnings = [ |
| "error::pytest.PytestCollectionWarning", |
| ] |
| python_files = [ |
| "test_*.py", |
| ] |
| testpaths = [ |
| "tests", |
| ] |
| asyncio_default_fixture_loop_scope = "function" |
| |
| pythonpath = ["tests"] |
| |
| # Keep temporary directories (created by `tmp_path`) for 2 recent runs only failed tests. |
| tmp_path_retention_count = "2" |
| tmp_path_retention_policy = "failed" |
| |
| [tool.uv] |
| required-version = ">=0.11.8" |
| |
| [tool.uv.sources] |
| apache-airflow-devel-common = { workspace = true } |