apache-airflow-providers-exasol 4.8.0rc1__tar.gz → 4.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. apache_airflow_providers_exasol-4.9.0/NOTICE +5 -0
  2. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/PKG-INFO +25 -21
  3. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/README.rst +12 -11
  4. apache_airflow_providers_exasol-4.9.0/docs/.latest-doc-only-change.txt +1 -0
  5. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/changelog.rst +83 -0
  6. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/index.rst +12 -11
  7. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/provider.yaml +8 -1
  8. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/pyproject.toml +13 -15
  9. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/__init__.py +1 -1
  10. {apache_airflow_providers_exasol-4.8.0rc1/tests/unit → apache_airflow_providers_exasol-4.9.0/src/airflow/providers}/__init__.py +1 -1
  11. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/__init__.py +3 -3
  12. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/get_provider_info.py +1 -0
  13. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/hooks/exasol.py +50 -9
  14. {apache_airflow_providers_exasol-4.8.0rc1/src/airflow/providers → apache_airflow_providers_exasol-4.9.0/tests/system}/__init__.py +1 -1
  15. apache_airflow_providers_exasol-4.9.0/tests/unit/__init__.py +17 -0
  16. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/hooks/test_exasol.py +86 -2
  17. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/hooks/test_sql.py +22 -18
  18. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/operators/test_exasol_sql.py +1 -1
  19. apache_airflow_providers_exasol-4.8.0rc1/docs/.latest-doc-only-change.txt +0 -1
  20. apache_airflow_providers_exasol-4.8.0rc1/tests/system/exasol/__init__.py +0 -16
  21. {apache_airflow_providers_exasol-4.8.0rc1/src/airflow/providers/exasol → apache_airflow_providers_exasol-4.9.0}/LICENSE +0 -0
  22. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/commits.rst +0 -0
  23. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/conf.py +0 -0
  24. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/installing-providers-from-sources.rst +0 -0
  25. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/integration-logos/Exasol.png +0 -0
  26. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/operators.rst +0 -0
  27. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/docs/security.rst +0 -0
  28. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/hooks/__init__.py +0 -0
  29. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/operators/__init__.py +0 -0
  30. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/src/airflow/providers/exasol/operators/exasol.py +0 -0
  31. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/conftest.py +0 -0
  32. {apache_airflow_providers_exasol-4.8.0rc1/tests/system → apache_airflow_providers_exasol-4.9.0/tests/system/exasol}/__init__.py +0 -0
  33. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/system/exasol/example_exasol.py +0 -0
  34. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/__init__.py +0 -0
  35. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/hooks/__init__.py +0 -0
  36. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/operators/__init__.py +0 -0
  37. {apache_airflow_providers_exasol-4.8.0rc1 → apache_airflow_providers_exasol-4.9.0}/tests/unit/exasol/operators/test_exasol.py +0 -0
@@ -0,0 +1,5 @@
1
+ Apache Airflow
2
+ Copyright 2016-2025 The Apache Software Foundation
3
+
4
+ This product includes software developed at
5
+ The Apache Software Foundation (http://www.apache.org/).
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apache-airflow-providers-exasol
3
- Version: 4.8.0rc1
3
+ Version: 4.9.0
4
4
  Summary: Provider package apache-airflow-providers-exasol for Apache Airflow
5
5
  Keywords: airflow-provider,exasol,airflow,integration
6
6
  Author-email: Apache Software Foundation <dev@airflow.apache.org>
7
7
  Maintainer-email: Apache Software Foundation <dev@airflow.apache.org>
8
- Requires-Python: ~=3.9
8
+ Requires-Python: >=3.10
9
9
  Description-Content-Type: text/x-rst
10
+ License-Expression: Apache-2.0
10
11
  Classifier: Development Status :: 5 - Production/Stable
11
12
  Classifier: Environment :: Console
12
13
  Classifier: Environment :: Web Environment
@@ -14,19 +15,21 @@ Classifier: Intended Audience :: Developers
14
15
  Classifier: Intended Audience :: System Administrators
15
16
  Classifier: Framework :: Apache Airflow
16
17
  Classifier: Framework :: Apache Airflow :: Provider
17
- Classifier: License :: OSI Approved :: Apache Software License
18
- Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: System :: Monitoring
23
- Requires-Dist: apache-airflow>=2.10.0rc1
24
- Requires-Dist: apache-airflow-providers-common-sql>=1.26.0rc1
25
- Requires-Dist: pyexasol>=0.5.1
26
- Requires-Dist: pandas>=2.1.2,<2.2
23
+ License-File: LICENSE
24
+ License-File: NOTICE
25
+ Requires-Dist: apache-airflow>=2.11.0
26
+ Requires-Dist: apache-airflow-providers-common-sql>=1.26.0
27
+ Requires-Dist: pyexasol>=0.26.0
28
+ Requires-Dist: pandas>=2.1.2; python_version <"3.13"
29
+ Requires-Dist: pandas>=2.2.3; python_version >="3.13"
27
30
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
28
- Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-exasol/4.8.0/changelog.html
29
- Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-exasol/4.8.0
31
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/changelog.html
32
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0
30
33
  Project-URL: Mastodon, https://fosstodon.org/@airflow
31
34
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
32
35
  Project-URL: Source Code, https://github.com/apache/airflow
@@ -57,7 +60,7 @@ Project-URL: YouTube, https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/
57
60
 
58
61
  Package ``apache-airflow-providers-exasol``
59
62
 
60
- Release: ``4.8.0``
63
+ Release: ``4.9.0``
61
64
 
62
65
 
63
66
  `Exasol <https://www.exasol.com/>`__
@@ -70,28 +73,29 @@ This is a provider package for ``exasol`` provider. All classes for this provide
70
73
  are in ``airflow.providers.exasol`` python package.
71
74
 
72
75
  You can find package information and changelog for the provider
73
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.8.0/>`_.
76
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/>`_.
74
77
 
75
78
  Installation
76
79
  ------------
77
80
 
78
- You can install this package on top of an existing Airflow 2 installation (see ``Requirements`` below
81
+ You can install this package on top of an existing Airflow installation (see ``Requirements`` below
79
82
  for the minimum Airflow version supported) via
80
83
  ``pip install apache-airflow-providers-exasol``
81
84
 
82
- The package supports the following python versions: 3.9,3.10,3.11,3.12
85
+ The package supports the following python versions: 3.10,3.11,3.12,3.13
83
86
 
84
87
  Requirements
85
88
  ------------
86
89
 
87
- ======================================= ==================
90
+ ======================================= =====================================
88
91
  PIP package Version required
89
- ======================================= ==================
90
- ``apache-airflow`` ``>=2.10.0``
92
+ ======================================= =====================================
93
+ ``apache-airflow`` ``>=2.11.0``
91
94
  ``apache-airflow-providers-common-sql`` ``>=1.26.0``
92
- ``pyexasol`` ``>=0.5.1``
93
- ``pandas`` ``>=2.1.2,<2.2``
94
- ======================================= ==================
95
+ ``pyexasol`` ``>=0.26.0``
96
+ ``pandas`` ``>=2.1.2; python_version < "3.13"``
97
+ ``pandas`` ``>=2.2.3; python_version >= "3.13"``
98
+ ======================================= =====================================
95
99
 
96
100
  Cross provider package dependencies
97
101
  -----------------------------------
@@ -113,5 +117,5 @@ Dependent package
113
117
  ============================================================================================================ ==============
114
118
 
115
119
  The changelog for the provider package can be found in the
116
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.8.0/changelog.html>`_.
120
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/changelog.html>`_.
117
121
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  Package ``apache-airflow-providers-exasol``
25
25
 
26
- Release: ``4.8.0``
26
+ Release: ``4.9.0``
27
27
 
28
28
 
29
29
  `Exasol <https://www.exasol.com/>`__
@@ -36,28 +36,29 @@ This is a provider package for ``exasol`` provider. All classes for this provide
36
36
  are in ``airflow.providers.exasol`` python package.
37
37
 
38
38
  You can find package information and changelog for the provider
39
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.8.0/>`_.
39
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/>`_.
40
40
 
41
41
  Installation
42
42
  ------------
43
43
 
44
- You can install this package on top of an existing Airflow 2 installation (see ``Requirements`` below
44
+ You can install this package on top of an existing Airflow installation (see ``Requirements`` below
45
45
  for the minimum Airflow version supported) via
46
46
  ``pip install apache-airflow-providers-exasol``
47
47
 
48
- The package supports the following python versions: 3.9,3.10,3.11,3.12
48
+ The package supports the following python versions: 3.10,3.11,3.12,3.13
49
49
 
50
50
  Requirements
51
51
  ------------
52
52
 
53
- ======================================= ==================
53
+ ======================================= =====================================
54
54
  PIP package Version required
55
- ======================================= ==================
56
- ``apache-airflow`` ``>=2.10.0``
55
+ ======================================= =====================================
56
+ ``apache-airflow`` ``>=2.11.0``
57
57
  ``apache-airflow-providers-common-sql`` ``>=1.26.0``
58
- ``pyexasol`` ``>=0.5.1``
59
- ``pandas`` ``>=2.1.2,<2.2``
60
- ======================================= ==================
58
+ ``pyexasol`` ``>=0.26.0``
59
+ ``pandas`` ``>=2.1.2; python_version < "3.13"``
60
+ ``pandas`` ``>=2.2.3; python_version >= "3.13"``
61
+ ======================================= =====================================
61
62
 
62
63
  Cross provider package dependencies
63
64
  -----------------------------------
@@ -79,4 +80,4 @@ Dependent package
79
80
  ============================================================================================================ ==============
80
81
 
81
82
  The changelog for the provider package can be found in the
82
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.8.0/changelog.html>`_.
83
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/changelog.html>`_.
@@ -0,0 +1 @@
1
+ a87cfe8a77459d74f63b7a108913d4f7ab3aa259
@@ -27,6 +27,89 @@
27
27
  Changelog
28
28
  ---------
29
29
 
30
+ 4.9.0
31
+ .....
32
+
33
+ .. note::
34
+ This release of provider is only available for Airflow 2.11+ as explained in the
35
+ Apache Airflow providers support policy <https://github.com/apache/airflow/blob/main/PROVIDERS.rst#minimum-supported-version-of-airflow-for-community-managed-providers>_.
36
+
37
+ Misc
38
+ ~~~~
39
+
40
+ * ``Bump minimum Airflow version in providers to Airflow 2.11.0 (#58612)``
41
+
42
+ .. Below changes are excluded from the changelog. Move them to
43
+ appropriate section above if needed. Do not delete the lines(!):
44
+
45
+ 4.8.4
46
+ .....
47
+
48
+ Misc
49
+ ~~~~
50
+
51
+ * ``Convert all airflow distributions to be compliant with ASF requirements (#58138)``
52
+
53
+ Doc-only
54
+ ~~~~~~~~
55
+
56
+ * ``Fix documentation/provider.yaml consistencies (#57283)``
57
+
58
+ .. Below changes are excluded from the changelog. Move them to
59
+ appropriate section above if needed. Do not delete the lines(!):
60
+ * ``Delete all unnecessary LICENSE Files (#58191)``
61
+ * ``Fix the '__init__.py' for tests (#58211)``
62
+ * ``Enable PT006 rule to 19 files in providers (airbyte, alibaba, atlassian, papermill, presto, redis, singularity, sqlite, tableau, vertica, weaviate, elasticsearch, exasol) (#57986)``
63
+ * ``Prepare release for Oct 2025 wave of providers (#57029)``
64
+ * ``Enable PT011 rule to prvoider tests (#56277)``
65
+ * ``Remove placeholder Release Date in changelog and index files (#56056)``
66
+
67
+ 4.8.3
68
+ .....
69
+
70
+
71
+ Bug Fixes
72
+ ~~~~~~~~~
73
+
74
+ * ``Fix SqlAlchemy URI Construction in ExasolHook (#53798)``
75
+
76
+ .. Below changes are excluded from the changelog. Move them to
77
+ appropriate section above if needed. Do not delete the lines(!):
78
+ * ``Prepare release for Sep 2025 1st wave of providers (#55203)``
79
+ * ``Fix Airflow 2 reference in README/index of providers (#55240)``
80
+ * ``Switch pre-commit to prek (#54258)``
81
+
82
+ 4.8.2
83
+ .....
84
+
85
+ Misc
86
+ ~~~~
87
+
88
+ * ``Add Python 3.13 support for Airflow. (#46891)``
89
+ * ``Refactoring get con part dbapihook in providers (#53335)``
90
+ * ``Cleanup mypy ignore in exasol provider where possible (#53278)``
91
+ * ``Remove type ignore across codebase after mypy upgrade (#53243)``
92
+ * ``Remove upper-binding for "python-requires" (#52980)``
93
+ * ``Temporarily switch to use >=,< pattern instead of '~=' (#52967)``
94
+
95
+ .. Below changes are excluded from the changelog. Move them to
96
+ appropriate section above if needed. Do not delete the lines(!):
97
+
98
+ 4.8.1
99
+ .....
100
+
101
+ Misc
102
+ ~~~~
103
+
104
+ * ``Drop support for Python 3.9 (#52072)``
105
+ * ``Bump upper binding on pandas in all providers (#52060)``
106
+ * ``Bump pyexasol>=0.26.0 (#51838)``
107
+
108
+ .. Below changes are excluded from the changelog. Move them to
109
+ appropriate section above if needed. Do not delete the lines(!):
110
+ * ``Clean up messy default connection overrides in provider tests (#52137)``
111
+ * ``Remove pytest.mark.db_test: exasol (#52057)``
112
+
30
113
  4.8.0
31
114
  .....
32
115
 
@@ -75,7 +75,7 @@ apache-airflow-providers-exasol package
75
75
  `Exasol <https://www.exasol.com/>`__
76
76
 
77
77
 
78
- Release: 4.8.0
78
+ Release: 4.9.0
79
79
 
80
80
  Provider package
81
81
  ----------------
@@ -86,23 +86,24 @@ All classes for this package are included in the ``airflow.providers.exasol`` py
86
86
  Installation
87
87
  ------------
88
88
 
89
- You can install this package on top of an existing Airflow 2 installation via
89
+ You can install this package on top of an existing Airflow installation via
90
90
  ``pip install apache-airflow-providers-exasol``.
91
91
  For the minimum Airflow version supported, see ``Requirements`` below.
92
92
 
93
93
  Requirements
94
94
  ------------
95
95
 
96
- The minimum Apache Airflow version supported by this provider distribution is ``2.10.0``.
96
+ The minimum Apache Airflow version supported by this provider distribution is ``2.11.0``.
97
97
 
98
- ======================================= ==================
98
+ ======================================= =====================================
99
99
  PIP package Version required
100
- ======================================= ==================
101
- ``apache-airflow`` ``>=2.10.0``
100
+ ======================================= =====================================
101
+ ``apache-airflow`` ``>=2.11.0``
102
102
  ``apache-airflow-providers-common-sql`` ``>=1.26.0``
103
- ``pyexasol`` ``>=0.5.1``
104
- ``pandas`` ``>=2.1.2,<2.2``
105
- ======================================= ==================
103
+ ``pyexasol`` ``>=0.26.0``
104
+ ``pandas`` ``>=2.1.2; python_version < "3.13"``
105
+ ``pandas`` ``>=2.2.3; python_version >= "3.13"``
106
+ ======================================= =====================================
106
107
 
107
108
  Cross provider package dependencies
108
109
  -----------------------------------
@@ -129,5 +130,5 @@ Downloading official packages
129
130
  You can download officially released packages and verify their checksums and signatures from the
130
131
  `Official Apache Download site <https://downloads.apache.org/airflow/providers/>`_
131
132
 
132
- * `The apache-airflow-providers-exasol 4.8.0 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0.tar.gz.sha512>`__)
133
- * `The apache-airflow-providers-exasol 4.8.0 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.8.0-py3-none-any.whl.sha512>`__)
133
+ * `The apache-airflow-providers-exasol 4.9.0 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0.tar.gz.sha512>`__)
134
+ * `The apache-airflow-providers-exasol 4.9.0 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_exasol-4.9.0-py3-none-any.whl.sha512>`__)
@@ -22,12 +22,17 @@ description: |
22
22
  `Exasol <https://www.exasol.com/>`__
23
23
 
24
24
  state: ready
25
- source-date-epoch: 1747132739
25
+ source-date-epoch: 1764110080
26
26
  # Note that those versions are maintained by release manager - do not update them manually
27
27
  # with the exception of case where other provider in sources has >= new provider version.
28
28
  # In such case adding >= NEW_VERSION and bumping to NEW_VERSION in a provider have
29
29
  # to be done in the same PR
30
30
  versions:
31
+ - 4.9.0
32
+ - 4.8.4
33
+ - 4.8.3
34
+ - 4.8.2
35
+ - 4.8.1
31
36
  - 4.8.0
32
37
  - 4.7.5
33
38
  - 4.7.4
@@ -72,6 +77,8 @@ versions:
72
77
  integrations:
73
78
  - integration-name: Exasol
74
79
  external-doc-url: https://docs.exasol.com/home.htm
80
+ how-to-guide:
81
+ - /docs/apache-airflow-providers-exasol/operators.rst
75
82
  logo: /docs/integration-logos/Exasol.png
76
83
  tags: [software]
77
84
 
@@ -25,9 +25,11 @@ build-backend = "flit_core.buildapi"
25
25
 
26
26
  [project]
27
27
  name = "apache-airflow-providers-exasol"
28
- version = "4.8.0rc1"
28
+ version = "4.9.0"
29
29
  description = "Provider package apache-airflow-providers-exasol for Apache Airflow"
30
30
  readme = "README.rst"
31
+ license = "Apache-2.0"
32
+ license-files = ['LICENSE', 'NOTICE']
31
33
  authors = [
32
34
  {name="Apache Software Foundation", email="dev@airflow.apache.org"},
33
35
  ]
@@ -43,28 +45,24 @@ classifiers = [
43
45
  "Intended Audience :: System Administrators",
44
46
  "Framework :: Apache Airflow",
45
47
  "Framework :: Apache Airflow :: Provider",
46
- "License :: OSI Approved :: Apache Software License",
47
- "Programming Language :: Python :: 3.9",
48
48
  "Programming Language :: Python :: 3.10",
49
49
  "Programming Language :: Python :: 3.11",
50
50
  "Programming Language :: Python :: 3.12",
51
+ "Programming Language :: Python :: 3.13",
51
52
  "Topic :: System :: Monitoring",
52
53
  ]
53
- requires-python = "~=3.9"
54
+ requires-python = ">=3.10"
54
55
 
55
56
  # The dependencies should be modified in place in the generated file.
56
57
  # Any change in the dependencies is preserved when the file is regenerated
57
- # Make sure to run ``breeze static-checks --type update-providers-dependencies --all-files``
58
+ # Make sure to run ``prek update-providers-dependencies --all-files``
58
59
  # After you modify the dependencies, and rebuild your Breeze CI image with ``breeze ci-image build``
59
60
  dependencies = [
60
- "apache-airflow>=2.10.0rc1",
61
- "apache-airflow-providers-common-sql>=1.26.0rc1",
62
- "pyexasol>=0.5.1",
63
- # In pandas 2.2 minimal version of the sqlalchemy is 2.0
64
- # https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#increased-minimum-versions-for-dependencies
65
- # However Airflow not fully supports it yet: https://github.com/apache/airflow/issues/28723
66
- # In addition FAB also limit sqlalchemy to < 2.0
67
- "pandas>=2.1.2,<2.2",
61
+ "apache-airflow>=2.11.0",
62
+ "apache-airflow-providers-common-sql>=1.26.0",
63
+ "pyexasol>=0.26.0",
64
+ 'pandas>=2.1.2; python_version <"3.13"',
65
+ 'pandas>=2.2.3; python_version >="3.13"',
68
66
  ]
69
67
 
70
68
  [dependency-groups]
@@ -102,8 +100,8 @@ apache-airflow-providers-common-sql = {workspace = true}
102
100
  apache-airflow-providers-standard = {workspace = true}
103
101
 
104
102
  [project.urls]
105
- "Documentation" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-exasol/4.8.0"
106
- "Changelog" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-exasol/4.8.0/changelog.html"
103
+ "Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0"
104
+ "Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-exasol/4.9.0/changelog.html"
107
105
  "Bug Tracker" = "https://github.com/apache/airflow/issues"
108
106
  "Source Code" = "https://github.com/apache/airflow"
109
107
  "Slack Chat" = "https://s.apache.org/airflow-slack"
@@ -14,4 +14,4 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- __path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore
17
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -14,4 +14,4 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- __path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore
17
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -29,11 +29,11 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "4.8.0"
32
+ __version__ = "4.9.0"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
- "2.10.0"
35
+ "2.11.0"
36
36
  ):
37
37
  raise RuntimeError(
38
- f"The package `apache-airflow-providers-exasol:{__version__}` needs Apache Airflow 2.10.0+"
38
+ f"The package `apache-airflow-providers-exasol:{__version__}` needs Apache Airflow 2.11.0+"
39
39
  )
@@ -30,6 +30,7 @@ def get_provider_info():
30
30
  {
31
31
  "integration-name": "Exasol",
32
32
  "external-doc-url": "https://docs.exasol.com/home.htm",
33
+ "how-to-guide": ["/docs/apache-airflow-providers-exasol/operators.rst"],
33
34
  "logo": "/docs/integration-logos/Exasol.png",
34
35
  "tags": ["software"],
35
36
  }
@@ -17,13 +17,14 @@
17
17
  # under the License.
18
18
  from __future__ import annotations
19
19
 
20
- from collections.abc import Iterable, Mapping, Sequence
20
+ from collections.abc import Callable, Iterable, Mapping, Sequence
21
21
  from contextlib import closing
22
- from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
22
+ from typing import TYPE_CHECKING, Any, TypeVar, overload
23
23
 
24
24
  import pyexasol
25
25
  from deprecated import deprecated
26
26
  from pyexasol import ExaConnection, ExaStatement
27
+ from sqlalchemy.engine import URL
27
28
 
28
29
  from airflow.exceptions import AirflowProviderDeprecationWarning
29
30
  from airflow.providers.common.sql.hooks.handlers import return_single_query_results
@@ -53,14 +54,15 @@ class ExasolHook(DbApiHook):
53
54
  conn_type = "exasol"
54
55
  hook_name = "Exasol"
55
56
  supports_autocommit = True
57
+ DEFAULT_SQLALCHEMY_SCHEME = "exa+websocket" # sqlalchemy-exasol dialect
56
58
 
57
- def __init__(self, *args, **kwargs) -> None:
59
+ def __init__(self, *args, sqlalchemy_scheme: str | None = None, **kwargs) -> None:
58
60
  super().__init__(*args, **kwargs)
59
61
  self.schema = kwargs.pop("schema", None)
62
+ self._sqlalchemy_scheme = sqlalchemy_scheme
60
63
 
61
64
  def get_conn(self) -> ExaConnection:
62
- conn_id = self.get_conn_id()
63
- conn = self.get_connection(conn_id)
65
+ conn = self.get_connection(self.get_conn_id())
64
66
  conn_args = {
65
67
  "dsn": f"{conn.host}:{conn.port}",
66
68
  "user": conn.login,
@@ -75,6 +77,46 @@ class ExasolHook(DbApiHook):
75
77
  conn = pyexasol.connect(**conn_args)
76
78
  return conn
77
79
 
80
+ @property
81
+ def sqlalchemy_scheme(self) -> str:
82
+ """Sqlalchemy scheme either from constructor, connection extras or default."""
83
+ extra_scheme = self.connection is not None and self.connection_extra_lower.get("sqlalchemy_scheme")
84
+ sqlalchemy_scheme = self._sqlalchemy_scheme or extra_scheme or self.DEFAULT_SQLALCHEMY_SCHEME
85
+ if sqlalchemy_scheme not in ["exa+websocket", "exa+pyodbc", "exa+turbodbc"]:
86
+ raise ValueError(
87
+ f"sqlalchemy_scheme in connection extra should be one of 'exa+websocket', 'exa+pyodbc' or 'exa+turbodbc', "
88
+ f"but got '{sqlalchemy_scheme}'. See https://github.com/exasol/sqlalchemy-exasol?tab=readme-ov-file#using-sqlalchemy-with-exasol-db for more details."
89
+ )
90
+ return sqlalchemy_scheme
91
+
92
+ @property
93
+ def sqlalchemy_url(self) -> URL:
94
+ """
95
+ Return a Sqlalchemy.engine.URL object from the connection.
96
+
97
+ :return: the extracted sqlalchemy.engine.URL object.
98
+ """
99
+ connection = self.connection
100
+ query = connection.extra_dejson
101
+ query = {k: v for k, v in query.items() if k.lower() != "sqlalchemy_scheme"}
102
+ return URL.create(
103
+ drivername=self.sqlalchemy_scheme,
104
+ username=connection.login,
105
+ password=connection.password,
106
+ host=connection.host,
107
+ port=connection.port,
108
+ database=self.schema or connection.schema,
109
+ query=query,
110
+ )
111
+
112
+ def get_uri(self) -> str:
113
+ """
114
+ Extract the URI from the connection.
115
+
116
+ :return: the extracted uri.
117
+ """
118
+ return self.sqlalchemy_url.render_as_string(hide_password=False)
119
+
78
120
  def _get_pandas_df(
79
121
  self, sql, parameters: Iterable | Mapping[str, Any] | None = None, **kwargs
80
122
  ) -> pd.DataFrame:
@@ -199,7 +241,7 @@ class ExasolHook(DbApiHook):
199
241
  )
200
242
  return cols
201
243
 
202
- @overload # type: ignore[override]
244
+ @overload
203
245
  def run(
204
246
  self,
205
247
  sql: str | Iterable[str],
@@ -268,9 +310,8 @@ class ExasolHook(DbApiHook):
268
310
  self.log.info("Running statement: %s, parameters: %s", sql_statement, parameters)
269
311
  with closing(conn.execute(sql_statement, parameters)) as exa_statement:
270
312
  if handler is not None:
271
- result = self._make_common_data_structure( # type: ignore[attr-defined]
272
- handler(exa_statement)
273
- )
313
+ result = self._make_common_data_structure(handler(exa_statement))
314
+
274
315
  if return_single_query_results(sql, return_last, split_statements):
275
316
  _last_result = result
276
317
  _last_columns = self.get_description(exa_statement)
@@ -14,4 +14,4 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- __path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore
17
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -0,0 +1,17 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -63,6 +63,91 @@ class TestExasolHookConn:
63
63
  assert kwargs["encryption"] is True
64
64
 
65
65
 
66
+ class TestExasolHookSqlalchemy:
67
+ def get_connection(self, extra: dict | None = None) -> models.Connection:
68
+ return models.Connection(
69
+ login="login",
70
+ password="password",
71
+ host="host",
72
+ port=1234,
73
+ schema="schema",
74
+ extra=extra,
75
+ )
76
+
77
+ @pytest.mark.parametrize(
78
+ ("init_scheme", "extra_scheme", "expected_result", "expect_error"),
79
+ [
80
+ (None, None, "exa+websocket", False),
81
+ ("exa+pyodbc", None, "exa+pyodbc", False),
82
+ (None, "exa+turbodbc", "exa+turbodbc", False),
83
+ ("exa+invalid", None, None, True),
84
+ (None, "exa+invalid", None, True),
85
+ ],
86
+ ids=[
87
+ "default",
88
+ "from_init_arg",
89
+ "from_extra",
90
+ "invalid_from_init_arg",
91
+ "invalid_from_extra",
92
+ ],
93
+ )
94
+ def test_sqlalchemy_scheme_property(self, init_scheme, extra_scheme, expected_result, expect_error):
95
+ hook = ExasolHook(sqlalchemy_scheme=init_scheme) if init_scheme else ExasolHook()
96
+ connection = self.get_connection(extra={"sqlalchemy_scheme": extra_scheme} if extra_scheme else None)
97
+ hook.get_connection = mock.Mock(return_value=connection)
98
+
99
+ if not expect_error:
100
+ assert hook.sqlalchemy_scheme == expected_result
101
+ else:
102
+ with pytest.raises(ValueError, match="sqlalchemy_scheme in connection extra should be one of"):
103
+ _ = hook.sqlalchemy_scheme
104
+
105
+ @pytest.mark.parametrize(
106
+ ("hook_scheme", "extra", "expected_url"),
107
+ [
108
+ (None, {}, "exa+websocket://login:password@host:1234/schema"),
109
+ (
110
+ None,
111
+ {"CONNECTIONLCALL": "en_US.UTF-8", "driver": "EXAODBC"},
112
+ "exa+websocket://login:password@host:1234/schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC",
113
+ ),
114
+ (
115
+ None,
116
+ {"sqlalchemy_scheme": "exa+turbodbc", "CONNECTIONLCALL": "en_US.UTF-8", "driver": "EXAODBC"},
117
+ "exa+turbodbc://login:password@host:1234/schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC",
118
+ ),
119
+ (
120
+ "exa+pyodbc",
121
+ {
122
+ "sqlalchemy_scheme": "exa+turbodbc", # should be overridden
123
+ "CONNECTIONLCALL": "en_US.UTF-8",
124
+ "driver": "EXAODBC",
125
+ },
126
+ "exa+pyodbc://login:password@host:1234/schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC",
127
+ ),
128
+ ],
129
+ ids=[
130
+ "default",
131
+ "default_with_extra",
132
+ "scheme_from_extra_turbodbc",
133
+ "scheme_from_hook",
134
+ ],
135
+ )
136
+ def test_sqlalchemy_url_property(self, hook_scheme, extra, expected_url):
137
+ hook = ExasolHook(sqlalchemy_scheme=hook_scheme) if hook_scheme else ExasolHook()
138
+ hook.get_connection = mock.Mock(return_value=self.get_connection(extra=extra))
139
+ assert hook.sqlalchemy_url.render_as_string(hide_password=False) == expected_url
140
+
141
+ def test_get_uri(self):
142
+ hook = ExasolHook()
143
+ connection = self.get_connection(extra={"CONNECTIONLCALL": "en_US.UTF-8", "driver": "EXAODBC"})
144
+ hook.get_connection = mock.Mock(return_value=connection)
145
+ assert (
146
+ hook.get_uri()
147
+ == "exa+websocket://login:password@host:1234/schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC"
148
+ )
149
+
150
+
66
151
  class TestExasolHook:
67
152
  def setup_method(self):
68
153
  self.cur = mock.MagicMock(rowcount=lambda: 0)
@@ -128,9 +213,8 @@ class TestExasolHook:
128
213
  self.conn.commit.assert_not_called()
129
214
 
130
215
  def test_run_no_queries(self):
131
- with pytest.raises(ValueError) as err:
216
+ with pytest.raises(ValueError, match="List of SQL statements is empty"):
132
217
  self.db_hook.run(sql=[])
133
- assert err.value.args[0] == "List of SQL statements is empty"
134
218
 
135
219
  def test_no_result_set(self):
136
220
  """Queries like DROP and SELECT are of type rowCount (not resultSet),
@@ -27,10 +27,6 @@ import pytest
27
27
  from airflow.models import Connection
28
28
  from airflow.providers.common.sql.hooks.handlers import fetch_all_handler
29
29
  from airflow.providers.exasol.hooks.exasol import ExasolHook
30
- from airflow.utils.session import provide_session
31
-
32
- pytestmark = pytest.mark.db_test
33
-
34
30
 
35
31
  TASK_ID = "sql-operator"
36
32
  HOST = "host"
@@ -43,17 +39,18 @@ class ExasolHookForTests(ExasolHook):
43
39
  get_conn = MagicMock(name="conn")
44
40
 
45
41
 
46
- @provide_session
47
42
  @pytest.fixture(autouse=True)
48
- def create_connection(session):
49
- conn = session.query(Connection).filter(Connection.conn_id == DEFAULT_CONN_ID).first()
50
- if conn is None:
51
- conn = Connection(conn_id=DEFAULT_CONN_ID)
52
- conn.host = HOST
53
- conn.login = None
54
- conn.password = PASSWORD
55
- conn.extra = None
56
- session.commit()
43
+ def create_connection(create_connection_without_db):
44
+ create_connection_without_db(
45
+ Connection(
46
+ conn_id=DEFAULT_CONN_ID,
47
+ conn_type="exasol",
48
+ host=HOST,
49
+ login=None,
50
+ password=PASSWORD,
51
+ extra=None,
52
+ )
53
+ )
57
54
 
58
55
 
59
56
  @pytest.fixture
@@ -72,8 +69,16 @@ index = 0
72
69
 
73
70
 
74
71
  @pytest.mark.parametrize(
75
- "return_last, split_statements, sql, cursor_calls,"
76
- "cursor_descriptions, cursor_results, hook_descriptions, hook_results, ",
72
+ (
73
+ "return_last",
74
+ "split_statements",
75
+ "sql",
76
+ "cursor_calls",
77
+ "cursor_descriptions",
78
+ "cursor_results",
79
+ "hook_descriptions",
80
+ "hook_results",
81
+ ),
77
82
  [
78
83
  pytest.param(
79
84
  True,
@@ -288,6 +293,5 @@ def test_query(
288
293
  def test_no_query(empty_statement):
289
294
  dbapi_hook = ExasolHookForTests()
290
295
  dbapi_hook.get_conn.return_value.cursor.rowcount = lambda: 0
291
- with pytest.raises(ValueError) as err:
296
+ with pytest.raises(ValueError, match="List of SQL statements is empty"):
292
297
  dbapi_hook.run(sql=empty_statement)
293
- assert err.value.args[0] == "List of SQL statements is empty"
@@ -41,7 +41,7 @@ class Row2(NamedTuple):
41
41
 
42
42
 
43
43
  @pytest.mark.parametrize(
44
- "sql, return_last, split_statement, hook_results, hook_descriptions, expected_results",
44
+ ("sql", "return_last", "split_statement", "hook_results", "hook_descriptions", "expected_results"),
45
45
  [
46
46
  pytest.param(
47
47
  "select * from dummy",
@@ -1 +0,0 @@
1
- 7b2ec33c7ad4998d9c9735b79593fcdcd3b9dd1f
@@ -1,16 +0,0 @@
1
- # Licensed to the Apache Software Foundation (ASF) under one
2
- # or more contributor license agreements. See the NOTICE file
3
- # distributed with this work for additional information
4
- # regarding copyright ownership. The ASF licenses this file
5
- # to you under the Apache License, Version 2.0 (the
6
- # "License"); you may not use this file except in compliance
7
- # with the License. You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing,
12
- # software distributed under the License is distributed on an
13
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
- # KIND, either express or implied. See the License for the
15
- # specific language governing permissions and limitations
16
- # under the License.