Skip to content

Commit 1d5134a

Browse files
authored
Refresh README, examples and changelog with recent changes (#78)
This PR refreshes README, examples of our and changelog to better emphasise recent changes in the validation/open API of ours clarifying read-only properties in our API. Specifically DAGRun dag_id has not been marked as read-only so far which was a bug, and some of our "dev" examples set the read only properties, which could mislead our users. We've recently clarified that those properties are read-only and we are now updating the API documentation and examples to show that the good way of using related APIs. Also the examples have been refrehsed and modernized a bit as well as the dev example has been synchronized automatically (via pre-commit) to make sure it is the same in the README and in dev where we are using it to run the tests with the API. Minimum version of Python have been set to 3.7 and CHANGELOG was updated to reflect all the changes, as well as it has been exposed in Project_URL of the package in PyPI. Link in README have been changed to URL so that they are properly rendered in README in `pypi` package documentation (currently following the links to other filess in the repo there fails.
1 parent 3057a39 commit 1d5134a

6 files changed

+224
-56
lines changed

.pre-commit-config.yaml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ default_language_version:
2121
python: python3
2222
minimum_pre_commit_version: "1.20.0"
2323
repos:
24+
- repo: meta
25+
hooks:
26+
- id: identity
27+
name: Print input to the static check hooks for troubleshooting
28+
- id: check-hooks-apply
29+
name: Check if all hooks apply to the repository
2430
- repo: https://github.com/Lucas-C/pre-commit-hooks
25-
rev: v1.1.9
31+
rev: v1.5.1
2632
hooks:
2733
- id: insert-license
2834
name: Add license for all md files
@@ -66,4 +72,13 @@ repos:
6672
- license-templates/LICENSE.txt
6773
- --fuzzy-match-generates-todo
6874
files: >
69-
\.properties$|\.cfg$|\.conf$|\.ini$|\.txt$$
75+
\.properties$|\.cfg$|\.conf$|\.ini$|\.txt$$
76+
- repo: local
77+
hooks:
78+
- id: update-example-in-readme
79+
name: Update example python script in README.md
80+
entry: ./dev/insert_readme_example.py
81+
language: python
82+
files: ^dev/test_python_client.py$|^README.md$
83+
pass_filenames: false
84+
require_serial: true

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
-->
1919

2020
# v2.6.0
21-
Apache Airflow API version: 2.6.0
21+
22+
Apache Airflow API version: 2.6.0
2223

2324
### Major changes:
2425

25-
- NA
26+
- Minimum Python version is 3.7
27+
- DAGRun dag_id parameter is properly validated as read-only and setting it might result in an error:
28+
"`dag_id` is a read-only attribute" This might break some workflows that used examples from the documentation.
2629

2730
### Major Fixes
2831

README.md

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,12 @@
1919

2020
# Apache Airflow Python Client
2121

22-
> **_NOTE:_** The Apache Airflow Client is still under active development and some methods
23-
> or APIs might be broken. Please raise an issue in github if you encounter any such issues.
24-
25-
26-
2722
## Requirements.
2823

29-
Python >= 3.6
24+
Python >= 3.7
3025

3126
## Installation & Usage
27+
3228
### pip install
3329

3430
You can install directly using pip:
@@ -41,7 +37,7 @@ pip install apache-airflow-client
4137

4238
Or install via [Setuptools](http://pypi.python.org/pypi/setuptools).
4339

44-
```sh
40+
```shell
4541
git clone [email protected]:apache/airflow-client-python.git
4642
cd airflow-client-python
4743
python setup.py install --user
@@ -53,40 +49,116 @@ Then import the package:
5349
import airflow_client.client
5450
```
5551

52+
## Changelog
53+
54+
See [CHANGELOG.md](https://github.com/apache/airflow-client-python/blob/main/CHANGELOG.md) for keeping
55+
track on what has changed in the client.
56+
57+
5658
## Getting Started
5759

58-
Please follow the [installation procedure](#installation--usage) and then run the following:
60+
Please follow the [installation procedure](#installation--usage) and then run the following
61+
example python script:
5962

6063
```python
61-
import airflow_client.client
62-
from pprint import pprint
63-
from airflow_client.client.api import config_api
64+
import uuid
6465
66+
import airflow_client.client
67+
try:
68+
# If you have rich installed, you will have nice colored output of the API responses
69+
from rich import print
70+
except ImportError:
71+
print("Output will not be colored. Please install rich to get colored output: `pip install rich`")
72+
pass
73+
from airflow_client.client.api import config_api, dag_api, dag_run_api
74+
from airflow_client.client.model.dag_run import DAGRun
75+
76+
# The client must use the authentication and authorization parameters
77+
# in accordance with the API server security policy.
78+
# Examples for each auth method are provided below, use the example that
79+
# satisfies your auth use case.
6580
#
66-
# In case of the basic authentication below. Make sure:
67-
# - Airflow is configured with the basic_auth as backend:
68-
# auth_backend = airflow.api.auth.backend.basic_auth
69-
# - Make sure that the client has been generated with securitySchema Basic.
81+
# In case of the basic authentication below, make sure that Airflow is
82+
# configured also with the basic_auth as backend additionally to regular session backend needed
83+
# by the UI. In the `[api]` section of your `airflow.cfg` set:
84+
#
85+
# auth_backend = airflow.api.auth.backend.session,airflow.api.auth.backend.basic_auth
86+
#
87+
# Make sure that your user/name are configured properly - using the user/password that has admin
88+
# privileges in Airflow
7089
7190
# Configure HTTP basic authorization: Basic
7291
configuration = airflow_client.client.Configuration(
73-
host="http://localhost/api/v1",
92+
host="http://localhost:8080/api/v1",
7493
username='admin',
7594
password='admin'
7695
)
7796
97+
# Make sure in the [core] section, the `load_examples` config is set to True in your airflow.cfg
98+
# or AIRFLOW__CORE__LOAD_EXAMPLES environment variable set to True
99+
DAG_ID = "example_bash_operator"
78100
79101
# Enter a context with an instance of the API client
80102
with airflow_client.client.ApiClient(configuration) as api_client:
81-
# Create an instance of the API class
82-
api_instance = config_api.ConfigApi(api_client)
83103
104+
errors = False
105+
106+
print('[blue]Getting DAG list')
107+
dag_api_instance = dag_api.DAGApi(api_client)
108+
try:
109+
api_response = dag_api_instance.get_dags()
110+
print(api_response)
111+
except airflow_client.client.OpenApiException as e:
112+
print("[red]Exception when calling DagAPI->get_dags: %s\n" % e)
113+
errors = True
114+
else:
115+
print('[green]Getting DAG list successful')
116+
117+
118+
print('[blue]Getting Tasks for a DAG')
119+
try:
120+
api_response = dag_api_instance.get_tasks(DAG_ID)
121+
print(api_response)
122+
except airflow_client.client.exceptions.OpenApiException as e:
123+
print("[red]Exception when calling DagAPI->get_tasks: %s\n" % e)
124+
errors = True
125+
else:
126+
print('[green]Getting Tasks successful')
127+
128+
129+
print('[blue]Triggering a DAG run')
130+
dag_run_api_instance = dag_run_api.DAGRunApi(api_client)
131+
try:
132+
# Create a DAGRun object (no dag_id should be specified because it is read-only property of DAGRun)
133+
# dag_run id is generated randomly to allow multiple executions of the script
134+
dag_run = DAGRun(
135+
dag_run_id='some_test_run_' + uuid.uuid4().hex,
136+
)
137+
api_response = dag_run_api_instance.post_dag_run(DAG_ID, dag_run)
138+
print(api_response)
139+
except airflow_client.client.exceptions.OpenApiException as e:
140+
print("[red]Exception when calling DAGRunAPI->post_dag_run: %s\n" % e)
141+
errors = True
142+
else:
143+
print('[green]Posting DAG Run successful')
144+
145+
# Get current configuration. Note, this is disabled by default with most installation.
146+
# You need to set `expose_config = True` in Airflow configuration in order to retrieve configuration.
147+
conf_api_instance = config_api.ConfigApi(api_client)
84148
try:
85-
# Get current configuration
86-
api_response = api_instance.get_config()
87-
pprint(api_response)
88-
except airflow_client.client.ApiException as e:
89-
print("Exception when calling ConfigApi->get_config: %s\n" % e)
149+
api_response = conf_api_instance.get_config()
150+
print(api_response)
151+
except airflow_client.client.OpenApiException as e:
152+
print("[red]Exception when calling ConfigApi->get_config: %s\n" % e)
153+
errors = True
154+
else:
155+
print('[green]Config retrieved successfully')
156+
157+
if errors:
158+
print ('\n[red]There were errors while running the script - see above for details')
159+
else:
160+
print ('\n[green]Everything went well')
90161
```
91162
92-
See [README](./airflow_client/README.md#documentation-for-api-endpoints) for full client API documentation.
163+
See [README](https://github.com/apache/airflow-client-python/blob/main/README.md#documentation-for-api-endpoints)
164+
for full client API documentation.

dev/insert_readme_example.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python3
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
19+
from pathlib import Path
20+
21+
BASE_DIR = Path(__file__).resolve().parent.parent
22+
23+
if __name__ == '__main__':
24+
example = (BASE_DIR / "dev" / "test_python_client.py").read_text().splitlines()
25+
example = example[example.index("# under the License.")+1:] # Remove license header
26+
readme_lines = (BASE_DIR / "README.md").read_text().splitlines()
27+
result_lines = []
28+
skip_lines = False
29+
changes_made = False
30+
for line in readme_lines:
31+
if line.strip().startswith("example python script:"):
32+
result_lines.append(line)
33+
result_lines.append("")
34+
result_lines.append("```python")
35+
result_lines.extend(example)
36+
result_lines.append("```")
37+
skip_lines = True
38+
changes_made = True
39+
else:
40+
if not skip_lines:
41+
result_lines.append(line)
42+
if line.strip() == "```":
43+
skip_lines = False
44+
(BASE_DIR / "README.md").write_text("\n".join(result_lines))
45+
if not changes_made:
46+
raise Exception("Could not find example python script in README.md to replace. "
47+
"Please make sure `example python script:` line is in README.md")

dev/test_python_client.py

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17+
import uuid
18+
1719
import airflow_client.client
18-
from pprint import pprint
20+
try:
21+
# If you have rich installed, you will have nice colored output of the API responses
22+
from rich import print
23+
except ImportError:
24+
print("Output will not be colored. Please install rich to get colored output: `pip install rich`")
25+
pass
1926
from airflow_client.client.api import config_api, dag_api, dag_run_api
2027
from airflow_client.client.model.dag_run import DAGRun
2128

@@ -25,11 +32,13 @@
2532
# satisfies your auth use case.
2633
#
2734
# In case of the basic authentication below, make sure that Airflow is
28-
# configured with the basic_auth as backend:
35+
# configured also with the basic_auth as backend additionally to regular session backend needed
36+
# by the UI. In the `[api]` section of your `airflow.cfg` set:
2937
#
30-
# auth_backend = airflow.api.auth.backend.basic_auth
38+
# auth_backend = airflow.api.auth.backend.session,airflow.api.auth.backend.basic_auth
3139
#
32-
# Make sure that your user/name are configured properly
40+
# Make sure that your user/name are configured properly - using the user/password that has admin
41+
# privileges in Airflow
3342

3443
# Configure HTTP basic authorization: Basic
3544
configuration = airflow_client.client.Configuration(
@@ -38,46 +47,67 @@
3847
password='admin'
3948
)
4049

41-
dag_id = "example_bash_operator"
50+
# Make sure in the [core] section, the `load_examples` config is set to True in your airflow.cfg
51+
# or AIRFLOW__CORE__LOAD_EXAMPLES environment variable set to True
52+
DAG_ID = "example_bash_operator"
4253

4354
# Enter a context with an instance of the API client
4455
with airflow_client.client.ApiClient(configuration) as api_client:
45-
# Get current configuration
46-
conf_api_instance = config_api.ConfigApi(api_client)
47-
try:
48-
api_response = conf_api_instance.get_config()
49-
pprint(api_response)
50-
except airflow_client.client.OpenApiException as e:
51-
print("Exception when calling ConfigApi->get_config: %s\n" % e)
5256

57+
errors = False
5358

54-
# Get dag list
59+
print('[blue]Getting DAG list')
5560
dag_api_instance = dag_api.DAGApi(api_client)
5661
try:
5762
api_response = dag_api_instance.get_dags()
58-
pprint(api_response)
63+
print(api_response)
5964
except airflow_client.client.OpenApiException as e:
60-
print("Exception when calling DagAPI->get_dags: %s\n" % e)
65+
print("[red]Exception when calling DagAPI->get_dags: %s\n" % e)
66+
errors = True
67+
else:
68+
print('[green]Getting DAG list successful')
6169

6270

63-
# Get tasks for a DAG (TODO: issue#20)
71+
print('[blue]Getting Tasks for a DAG')
6472
try:
65-
api_response = dag_api_instance.get_tasks(dag_id)
66-
pprint(api_response)
73+
api_response = dag_api_instance.get_tasks(DAG_ID)
74+
print(api_response)
6775
except airflow_client.client.exceptions.OpenApiException as e:
68-
print("Exception when calling DagAPI->get_tasks: %s\n" % e)
76+
print("[red]Exception when calling DagAPI->get_tasks: %s\n" % e)
77+
errors = True
78+
else:
79+
print('[green]Getting Tasks successful')
6980

7081

71-
# Trigger a dag run (TODO: issue#21)
82+
print('[blue]Triggering a DAG run')
7283
dag_run_api_instance = dag_run_api.DAGRunApi(api_client)
7384
try:
74-
# Create a DAGRun object
85+
# Create a DAGRun object (no dag_id should be specified because it is read-only property of DAGRun)
86+
# dag_run id is generated randomly to allow multiple executions of the script
7587
dag_run = DAGRun(
76-
dag_run_id='some_test_run',
77-
dag_id=dag_id,
78-
external_trigger=True,
88+
dag_run_id='some_test_run_' + uuid.uuid4().hex,
7989
)
80-
api_response = dag_run_api_instance.post_dag_run(dag_id, dag_run)
81-
pprint(api_response)
90+
api_response = dag_run_api_instance.post_dag_run(DAG_ID, dag_run)
91+
print(api_response)
8292
except airflow_client.client.exceptions.OpenApiException as e:
83-
print("Exception when calling DAGRunAPI->post_dag_run: %s\n" % e)
93+
print("[red]Exception when calling DAGRunAPI->post_dag_run: %s\n" % e)
94+
errors = True
95+
else:
96+
print('[green]Posting DAG Run successful')
97+
98+
# Get current configuration. Note, this is disabled by default with most installation.
99+
# You need to set `expose_config = True` in Airflow configuration in order to retrieve configuration.
100+
conf_api_instance = config_api.ConfigApi(api_client)
101+
try:
102+
api_response = conf_api_instance.get_config()
103+
print(api_response)
104+
except airflow_client.client.OpenApiException as e:
105+
print("[red]Exception when calling ConfigApi->get_config: %s\n" % e)
106+
errors = True
107+
else:
108+
print('[green]Config retrieved successfully')
109+
110+
if errors:
111+
print ('\n[red]There were errors while running the script - see above for details')
112+
else:
113+
print ('\n[green]Everything went well')

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ license_files =
3232
NOTICE
3333
project_urls =
3434
Documentation=https://airflow.apache.org/docs/apache-airflow/stable/stable-rest-api-ref.html
35+
Changelog=https://github.com/apache/airflow-client-python/CHANGELOG.md
3536
Bug Tracker=https://github.com/apache/airflow-client-python/issues
3637
Source Code=https://github.com/apache/airflow-client-python
3738

3839
[options]
3940
zip_safe = False
4041
include_package_data = True
41-
python_requires = ~=3.6
42+
python_requires = ~=3.7

0 commit comments

Comments
 (0)