apache-airflow-providers-sftp 5.3.1rc1__tar.gz → 5.3.2rc1__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 (48) hide show
  1. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/PKG-INFO +8 -9
  2. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/README.rst +4 -4
  3. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/changelog.rst +20 -0
  4. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/index.rst +3 -3
  5. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/provider.yaml +2 -1
  6. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/pyproject.toml +4 -5
  7. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/__init__.py +1 -1
  8. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/decorators/sensors/sftp.py +1 -2
  9. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/hooks/sftp.py +13 -7
  10. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/operators/sftp.py +1 -1
  11. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/sensors/sftp.py +3 -3
  12. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/version_compat.py +8 -0
  13. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/hooks/test_sftp.py +78 -11
  14. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/.latest-doc-only-change.txt +0 -0
  15. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/commits.rst +0 -0
  16. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/conf.py +0 -0
  17. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/connections/sftp.rst +0 -0
  18. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/installing-providers-from-sources.rst +0 -0
  19. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/integration-logos/SFTP.png +0 -0
  20. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/security.rst +0 -0
  21. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/docs/sensors/sftp_sensor.rst +0 -0
  22. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/__init__.py +0 -0
  23. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/__init__.py +0 -0
  24. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/LICENSE +0 -0
  25. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/decorators/__init__.py +0 -0
  26. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/decorators/sensors/__init__.py +0 -0
  27. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/get_provider_info.py +0 -0
  28. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/hooks/__init__.py +0 -0
  29. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/operators/__init__.py +0 -0
  30. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/sensors/__init__.py +0 -0
  31. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/triggers/__init__.py +0 -0
  32. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/src/airflow/providers/sftp/triggers/sftp.py +0 -0
  33. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/conftest.py +0 -0
  34. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/system/__init__.py +0 -0
  35. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/system/sftp/__init__.py +0 -0
  36. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/system/sftp/example_sftp_sensor.py +0 -0
  37. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/__init__.py +0 -0
  38. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/__init__.py +0 -0
  39. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/decorators/__init__.py +0 -0
  40. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/decorators/sensors/__init__.py +0 -0
  41. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/decorators/sensors/test_sftp.py +0 -0
  42. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/hooks/__init__.py +0 -0
  43. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/operators/__init__.py +0 -0
  44. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/operators/test_sftp.py +0 -0
  45. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/sensors/__init__.py +0 -0
  46. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/sensors/test_sftp.py +0 -0
  47. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/triggers/__init__.py +0 -0
  48. {apache_airflow_providers_sftp-5.3.1rc1 → apache_airflow_providers_sftp-5.3.2rc1}/tests/unit/sftp/triggers/test_sftp.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apache-airflow-providers-sftp
3
- Version: 5.3.1rc1
3
+ Version: 5.3.2rc1
4
4
  Summary: Provider package apache-airflow-providers-sftp for Apache Airflow
5
5
  Keywords: airflow-provider,sftp,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
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Environment :: Console
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: System Administrators
15
15
  Classifier: Framework :: Apache Airflow
16
16
  Classifier: Framework :: Apache Airflow :: Provider
17
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
@@ -27,8 +26,8 @@ Requires-Dist: asyncssh>=2.12.0
27
26
  Requires-Dist: apache-airflow-providers-common-compat ; extra == "common-compat"
28
27
  Requires-Dist: apache-airflow-providers-openlineage ; extra == "openlineage"
29
28
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
30
- Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.1/changelog.html
31
- Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.1
29
+ Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.2/changelog.html
30
+ Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.2
32
31
  Project-URL: Mastodon, https://fosstodon.org/@airflow
33
32
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
34
33
  Project-URL: Source Code, https://github.com/apache/airflow
@@ -61,7 +60,7 @@ Provides-Extra: openlineage
61
60
 
62
61
  Package ``apache-airflow-providers-sftp``
63
62
 
64
- Release: ``5.3.1``
63
+ Release: ``5.3.2``
65
64
 
66
65
 
67
66
  `SSH File Transfer Protocol (SFTP) <https://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/>`__
@@ -74,7 +73,7 @@ This is a provider package for ``sftp`` provider. All classes for this provider
74
73
  are in ``airflow.providers.sftp`` python package.
75
74
 
76
75
  You can find package information and changelog for the provider
77
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.1/>`_.
76
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.2/>`_.
78
77
 
79
78
  Installation
80
79
  ------------
@@ -83,7 +82,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
83
82
  for the minimum Airflow version supported) via
84
83
  ``pip install apache-airflow-providers-sftp``
85
84
 
86
- 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
87
86
 
88
87
  Requirements
89
88
  ------------
@@ -119,5 +118,5 @@ Dependent package
119
118
  ================================================================================================================== =================
120
119
 
121
120
  The changelog for the provider package can be found in the
122
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.1/changelog.html>`_.
121
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.2/changelog.html>`_.
123
122
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  Package ``apache-airflow-providers-sftp``
25
25
 
26
- Release: ``5.3.1``
26
+ Release: ``5.3.2``
27
27
 
28
28
 
29
29
  `SSH File Transfer Protocol (SFTP) <https://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/>`__
@@ -36,7 +36,7 @@ This is a provider package for ``sftp`` provider. All classes for this provider
36
36
  are in ``airflow.providers.sftp`` 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-sftp/5.3.1/>`_.
39
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.2/>`_.
40
40
 
41
41
  Installation
42
42
  ------------
@@ -45,7 +45,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
45
45
  for the minimum Airflow version supported) via
46
46
  ``pip install apache-airflow-providers-sftp``
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
49
49
 
50
50
  Requirements
51
51
  ------------
@@ -81,4 +81,4 @@ Dependent package
81
81
  ================================================================================================================== =================
82
82
 
83
83
  The changelog for the provider package can be found in the
84
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.1/changelog.html>`_.
84
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-sftp/5.3.2/changelog.html>`_.
@@ -27,6 +27,26 @@
27
27
  Changelog
28
28
  ---------
29
29
 
30
+ 5.3.2
31
+ .....
32
+
33
+ Bug Fixes
34
+ ~~~~~~~~~
35
+
36
+ * ``bugfix: removed cache for proxycommand in SFTPHook (#52641)``
37
+
38
+ Misc
39
+ ~~~~
40
+
41
+ * ``Move 'BaseHook' implementation to task SDK (#51873)``
42
+ * ``Replace 'models.BaseOperator' to Task SDK one for SFTP (#52435)``
43
+ * ``Drop support for Python 3.9 (#52072)``
44
+ * ``Use BaseSensorOperator from task sdk in providers (#52296)``
45
+
46
+ .. Below changes are excluded from the changelog. Move them to
47
+ appropriate section above if needed. Do not delete the lines(!):
48
+ * ``Separate out creation of default Connections for tests and non-tests (#52129)``
49
+
30
50
  5.3.1
31
51
  .....
32
52
 
@@ -70,7 +70,7 @@ apache-airflow-providers-sftp package
70
70
  `SSH File Transfer Protocol (SFTP) <https://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/>`__
71
71
 
72
72
 
73
- Release: 5.3.1
73
+ Release: 5.3.2
74
74
 
75
75
  Provider package
76
76
  ----------------
@@ -126,5 +126,5 @@ Downloading official packages
126
126
  You can download officially released packages and verify their checksums and signatures from the
127
127
  `Official Apache Download site <https://downloads.apache.org/airflow/providers/>`_
128
128
 
129
- * `The apache-airflow-providers-sftp 5.3.1 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1.tar.gz.sha512>`__)
130
- * `The apache-airflow-providers-sftp 5.3.1 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.1-py3-none-any.whl.sha512>`__)
129
+ * `The apache-airflow-providers-sftp 5.3.2 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2.tar.gz.sha512>`__)
130
+ * `The apache-airflow-providers-sftp 5.3.2 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_sftp-5.3.2-py3-none-any.whl.sha512>`__)
@@ -22,12 +22,13 @@ description: |
22
22
  `SSH File Transfer Protocol (SFTP) <https://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/>`__
23
23
 
24
24
  state: ready
25
- source-date-epoch: 1749896917
25
+ source-date-epoch: 1751474190
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
+ - 5.3.2
31
32
  - 5.3.1
32
33
  - 5.3.0
33
34
  - 5.2.1
@@ -25,7 +25,7 @@ build-backend = "flit_core.buildapi"
25
25
 
26
26
  [project]
27
27
  name = "apache-airflow-providers-sftp"
28
- version = "5.3.1rc1"
28
+ version = "5.3.2rc1"
29
29
  description = "Provider package apache-airflow-providers-sftp for Apache Airflow"
30
30
  readme = "README.rst"
31
31
  authors = [
@@ -44,13 +44,12 @@ classifiers = [
44
44
  "Framework :: Apache Airflow",
45
45
  "Framework :: Apache Airflow :: Provider",
46
46
  "License :: OSI Approved :: Apache Software License",
47
- "Programming Language :: Python :: 3.9",
48
47
  "Programming Language :: Python :: 3.10",
49
48
  "Programming Language :: Python :: 3.11",
50
49
  "Programming Language :: Python :: 3.12",
51
50
  "Topic :: System :: Monitoring",
52
51
  ]
53
- requires-python = "~=3.9"
52
+ requires-python = "~=3.10"
54
53
 
55
54
  # The dependencies should be modified in place in the generated file.
56
55
  # Any change in the dependencies is preserved when the file is regenerated
@@ -110,8 +109,8 @@ apache-airflow-providers-common-sql = {workspace = true}
110
109
  apache-airflow-providers-standard = {workspace = true}
111
110
 
112
111
  [project.urls]
113
- "Documentation" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.1"
114
- "Changelog" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.1/changelog.html"
112
+ "Documentation" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.2"
113
+ "Changelog" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-sftp/5.3.2/changelog.html"
115
114
  "Bug Tracker" = "https://github.com/apache/airflow/issues"
116
115
  "Source Code" = "https://github.com/apache/airflow"
117
116
  "Slack Chat" = "https://s.apache.org/airflow-slack"
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "5.3.1"
32
+ __version__ = "5.3.2"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "2.10.0"
@@ -17,8 +17,7 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- from collections.abc import Sequence
21
- from typing import Callable
20
+ from collections.abc import Callable, Sequence
22
21
 
23
22
  from airflow.providers.sftp.version_compat import AIRFLOW_V_3_0_PLUS
24
23
 
@@ -24,20 +24,24 @@ import datetime
24
24
  import os
25
25
  import stat
26
26
  import warnings
27
- from collections.abc import Generator, Sequence
27
+ from collections.abc import Callable, Generator, Sequence
28
28
  from contextlib import contextmanager
29
29
  from fnmatch import fnmatch
30
30
  from io import BytesIO
31
31
  from pathlib import Path
32
- from typing import TYPE_CHECKING, Any, Callable
32
+ from typing import TYPE_CHECKING, Any
33
33
 
34
34
  import asyncssh
35
35
  from asgiref.sync import sync_to_async
36
36
 
37
37
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
38
- from airflow.hooks.base import BaseHook
39
38
  from airflow.providers.ssh.hooks.ssh import SSHHook
40
39
 
40
+ try:
41
+ from airflow.sdk import BaseHook
42
+ except ImportError:
43
+ from airflow.hooks.base import BaseHook # type: ignore[attr-defined,no-redef]
44
+
41
45
  if TYPE_CHECKING:
42
46
  from paramiko import SSHClient
43
47
  from paramiko.sftp_attr import SFTPAttributes
@@ -148,6 +152,8 @@ class SFTPHook(SSHHook):
148
152
  self._sftp_conn = None
149
153
  self._ssh_conn.close()
150
154
  self._ssh_conn = None
155
+ if hasattr(self, "host_proxy"):
156
+ del self.host_proxy
151
157
 
152
158
  def get_conn_count(self) -> int:
153
159
  """Get the number of open connections."""
@@ -723,9 +729,9 @@ class SFTPHookAsync(BaseHook):
723
729
  """
724
730
  conn = await sync_to_async(self.get_connection)(self.sftp_conn_id)
725
731
  if conn.extra is not None:
726
- self._parse_extras(conn)
732
+ self._parse_extras(conn) # type: ignore[arg-type]
727
733
 
728
- conn_config = {
734
+ conn_config: dict[str, Any] = {
729
735
  "host": conn.host,
730
736
  "port": conn.port,
731
737
  "username": conn.login,
@@ -737,10 +743,10 @@ class SFTPHookAsync(BaseHook):
737
743
  if self.known_hosts.lower() == "none":
738
744
  conn_config.update(known_hosts=None)
739
745
  else:
740
- conn_config.update(known_hosts=self.known_hosts)
746
+ conn_config.update(known_hosts=self.known_hosts) # type: ignore
741
747
  if self.private_key:
742
748
  _private_key = asyncssh.import_private_key(self.private_key, self.passphrase)
743
- conn_config.update(client_keys=[_private_key])
749
+ conn_config["client_keys"] = [_private_key]
744
750
  if self.passphrase:
745
751
  conn_config.update(passphrase=self.passphrase)
746
752
  ssh_client_conn = await asyncssh.connect(**conn_config)
@@ -28,8 +28,8 @@ from typing import Any
28
28
  import paramiko
29
29
 
30
30
  from airflow.exceptions import AirflowException
31
- from airflow.models import BaseOperator
32
31
  from airflow.providers.sftp.hooks.sftp import SFTPHook
32
+ from airflow.providers.sftp.version_compat import BaseOperator
33
33
 
34
34
 
35
35
  class SFTPOperation:
@@ -20,9 +20,9 @@
20
20
  from __future__ import annotations
21
21
 
22
22
  import os
23
- from collections.abc import Sequence
23
+ from collections.abc import Callable, Sequence
24
24
  from datetime import datetime, timedelta
25
- from typing import TYPE_CHECKING, Any, Callable
25
+ from typing import TYPE_CHECKING, Any
26
26
 
27
27
  from paramiko.sftp import SFTP_NO_SUCH_FILE
28
28
 
@@ -30,7 +30,7 @@ from airflow.configuration import conf
30
30
  from airflow.exceptions import AirflowException
31
31
  from airflow.providers.sftp.hooks.sftp import SFTPHook
32
32
  from airflow.providers.sftp.triggers.sftp import SFTPTrigger
33
- from airflow.sensors.base import BaseSensorOperator, PokeReturnValue
33
+ from airflow.providers.sftp.version_compat import BaseSensorOperator, PokeReturnValue
34
34
  from airflow.utils.timezone import convert_to_utc, parse
35
35
 
36
36
  if TYPE_CHECKING:
@@ -33,3 +33,11 @@ def get_base_airflow_version_tuple() -> tuple[int, int, int]:
33
33
 
34
34
 
35
35
  AIRFLOW_V_3_0_PLUS = get_base_airflow_version_tuple() >= (3, 0, 0)
36
+
37
+ if AIRFLOW_V_3_0_PLUS:
38
+ from airflow.sdk import BaseOperator, BaseSensorOperator, PokeReturnValue
39
+ else:
40
+ from airflow.models import BaseOperator
41
+ from airflow.sensors.base import BaseSensorOperator, PokeReturnValue # type: ignore[no-redef]
42
+
43
+ __all__ = ["AIRFLOW_V_3_0_PLUS", "BaseOperator", "BaseSensorOperator", "PokeReturnValue"]
@@ -34,9 +34,6 @@ from paramiko.sftp_client import SFTPClient
34
34
  from airflow.exceptions import AirflowException
35
35
  from airflow.models import Connection
36
36
  from airflow.providers.sftp.hooks.sftp import SFTPHook, SFTPHookAsync
37
- from airflow.utils.session import provide_session
38
-
39
- pytestmark = pytest.mark.db_test
40
37
 
41
38
 
42
39
  def generate_host_key(pkey: paramiko.PKey):
@@ -62,13 +59,31 @@ TEST_KEY_FILE = "~/.ssh/id_rsa"
62
59
 
63
60
 
64
61
  class TestSFTPHook:
65
- @provide_session
66
- def update_connection(self, login, session=None):
67
- connection = session.query(Connection).filter(Connection.conn_id == "sftp_default").first()
68
- old_login = connection.login
69
- connection.login = login
70
- connection.extra = "" # clear out extra so it doesn't look for a key file
71
- session.commit()
62
+ def update_connection(self, login):
63
+ import os
64
+
65
+ # Get the current connection from environment variable to find the old login
66
+ old_connection = os.environ.get("AIRFLOW_CONN_SFTP_DEFAULT")
67
+ old_login = "airflow" # default fallback
68
+
69
+ if old_connection:
70
+ try:
71
+ old_conn = Connection.from_json(old_connection)
72
+ old_login = old_conn.login
73
+ except Exception:
74
+ pass
75
+
76
+ # Set the connection as an environment variable
77
+ new_connection = Connection(
78
+ conn_id="sftp_default",
79
+ conn_type="sftp",
80
+ host="localhost",
81
+ login=login,
82
+ password="airflow",
83
+ extra="", # clear out extra so it doesn't look for a key file
84
+ )
85
+ os.environ[f"AIRFLOW_CONN_{new_connection.conn_id.upper()}"] = new_connection.as_json()
86
+
72
87
  return old_login
73
88
 
74
89
  def _create_additional_test_file(self, file_name):
@@ -123,6 +138,48 @@ class TestSFTPHook:
123
138
  assert self.hook.get_conn_count() == 0
124
139
  assert self.hook.conn is None
125
140
 
141
+ @patch("paramiko.SSHClient")
142
+ @patch("paramiko.ProxyCommand")
143
+ @patch("airflow.providers.sftp.hooks.sftp.SFTPHook.get_connection")
144
+ def test_proxy_command_cache_invalidated_after_connection_closed(
145
+ self, mock_get_connection, mock_proxy_command, mock_ssh_client
146
+ ):
147
+ """
148
+ Assert that the ProxyCommand gets invalidated after the connection is closed
149
+ """
150
+
151
+ mock_connection = MagicMock()
152
+ mock_connection.login = "user"
153
+ mock_connection.password = None
154
+ mock_connection.host = "example.com"
155
+ mock_connection.port = 22
156
+ mock_connection.extra = None
157
+ mock_get_connection.return_value = mock_connection
158
+
159
+ mock_sftp_client = MagicMock(spec=SFTPClient)
160
+ mock_ssh_client.open_sftp.return_value = mock_sftp_client
161
+
162
+ mock_transport = MagicMock()
163
+ mock_ssh_client.return_value.get_transport.return_value = mock_transport
164
+ mock_proxy_command.return_value = MagicMock()
165
+
166
+ host_proxy_cmd = "ncat --proxy-auth proxy_user:**** --proxy proxy_host:port %h %p"
167
+ prev_proxy_command = None
168
+
169
+ hook = SFTPHook(
170
+ remote_host="example.com",
171
+ username="user",
172
+ host_proxy_cmd=host_proxy_cmd,
173
+ )
174
+
175
+ with hook.get_managed_conn() as _:
176
+ assert hasattr(self.hook, "host_proxy")
177
+ prev_proxy_command = hook.host_proxy
178
+
179
+ mock_proxy_command.return_value = MagicMock()
180
+
181
+ assert prev_proxy_command != hook.host_proxy
182
+
126
183
  @patch("airflow.providers.ssh.hooks.ssh.SSHHook.get_conn")
127
184
  def test_get_close_conn(self, mock_get_conn):
128
185
  mock_sftp_client = MagicMock(spec=SFTPClient)
@@ -549,7 +606,17 @@ class TestSFTPHook:
549
606
 
550
607
  @patch("paramiko.SSHClient")
551
608
  @patch("paramiko.ProxyCommand")
552
- def test_sftp_hook_with_proxy_command(self, mock_proxy_command, mock_ssh_client):
609
+ @patch("airflow.providers.sftp.hooks.sftp.SFTPHook.get_connection")
610
+ def test_sftp_hook_with_proxy_command(self, mock_get_connection, mock_proxy_command, mock_ssh_client):
611
+ # Mock the connection to not have a password
612
+ mock_connection = MagicMock()
613
+ mock_connection.login = "user"
614
+ mock_connection.password = None
615
+ mock_connection.host = "example.com"
616
+ mock_connection.port = 22
617
+ mock_connection.extra = None
618
+ mock_get_connection.return_value = mock_connection
619
+
553
620
  mock_sftp_client = MagicMock(spec=SFTPClient)
554
621
  mock_ssh_client.open_sftp.return_value = mock_sftp_client
555
622