Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 3 additions & 31 deletions airflow_dbt_python/hooks/localfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from __future__ import annotations

import shutil
import sys
from functools import partial
from pathlib import Path
from typing import Optional
Expand Down Expand Up @@ -127,33 +126,6 @@ def copy(

copy_function = partial(self.copy_one, replace=replace)

if sys.version_info.major == 3 and sys.version_info.minor < 8:
py37_copytree(source, destination, replace)
else:
shutil.copytree( # type: ignore
source, destination, copy_function=copy_function, dirs_exist_ok=True
)


def py37_copytree(source: URL, destination: URL, replace: bool = True):
Copy link
Owner

@tomasfarias tomasfarias Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Happy to see this workaround removed. Fully behind dropping support for Python 3.7.

"""A (probably) poor attempt at replicating shutil.copytree for Python 3.7.
shutil.copytree is available in Python 3.7, however it doesn't have the
dirs_exist_ok parameter, and we really need that. If the destination path doesn't
exist, we can use shutil.copytree, however if it does then we need to copy files
one by one and make any subdirectories ourselves.
"""
if destination.exists():
for url in source:
if url.is_dir():
continue

target_url = destination / url.relative_to(source)
if target_url.exists() and not replace:
# shutil.copy replaces by default
continue

target_url.parent.mkdir(exist_ok=True, parents=True)
shutil.copy(url, target_url)
else:
shutil.copytree(source, destination)
shutil.copytree( # type: ignore
source, destination, copy_function=copy_function, dirs_exist_ok=True
)
47 changes: 1 addition & 46 deletions tests/hooks/test_localfs_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from pathlib import Path
from zipfile import ZipFile

from airflow_dbt_python.hooks.localfs import DbtLocalFsRemoteHook, py37_copytree
from airflow_dbt_python.hooks.localfs import DbtLocalFsRemoteHook
from airflow_dbt_python.utils.url import URL


Expand Down Expand Up @@ -184,48 +184,3 @@ def test_upload_dbt_project_to_zip_file(tmpdir, test_files):
remote.upload_dbt_project(test_files[0].parent.parent, zip_path)

assert zip_path.exists()


def test_py37_copytree(test_files, tmpdir):
"""The Python 3.7 workaround should produce the same results as copytree."""
py37_dir = tmpdir / "py37_copytree_target"
assert not py37_dir.exists()
copytree_dir = tmpdir / "copytree_target"
assert not copytree_dir.exists()

shutil.copytree(URL(test_files[0].parent.parent), URL(copytree_dir))
py37_copytree(URL(test_files[0].parent.parent), URL(py37_dir))

for path in Path(copytree_dir).glob("**/*"):
if path.is_dir():
continue

py37_path = py37_dir / path.relative_to(copytree_dir)
assert py37_path.exists()


def test_py37_copytree_no_replace(test_files, tmpdir):
"""The Python 3.7 workaround should produce the same results as copytree."""
source = test_files[0].parent.parent
py37_copytree(URL(source), URL(source), replace=False)

all_paths = [p for p in source.glob("**/*") if not p.is_dir()]
assert len(all_paths) == 4


def test_py37_copytree_if_exists(test_files, tmpdir):
"""The Python 3.7 workaround should produce the same results as copytree."""
py37_dir = tmpdir / "py37_copytree_target"
py37_dir.mkdir()

assert py37_dir.exists()

source = test_files[0].parent.parent
py37_copytree(URL(source), URL(py37_dir))

for path in source.glob("**/*"):
if path.is_dir():
continue

py37_path = py37_dir / path.relative_to(source)
assert py37_path.exists()