Skip to content

Commit

Permalink
GH-467: fix ArtifactoryPath.__reduce__ for python 3.9 and below (#468)
Browse files Browse the repository at this point in the history
* GH-467: override `__deepcopy__()` method for python 3.9 and lower compatibility

* GH-467 (review): move version checking to compatibility module

* GH-467 (review): add integration test

* GH-467 (review): pre-commit fixes

---------

Co-authored-by: allburov <[email protected]>
  • Loading branch information
miskeens and allburov authored Jan 16, 2025
1 parent 376e2c9 commit 176e67b
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 16 deletions.
39 changes: 34 additions & 5 deletions artifactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
pure paths can be used.
"""
import collections
import copy
import datetime
import errno
import fnmatch
Expand All @@ -33,8 +34,8 @@
import pathlib
import platform
import re
import sys
import urllib.parse
from itertools import chain
from itertools import islice

import dateutil.parser
Expand All @@ -50,6 +51,7 @@
from dohq_artifactory.admin import User
from dohq_artifactory.auth import XJFrogArtApiAuth
from dohq_artifactory.auth import XJFrogArtBearerAuth
from dohq_artifactory.compat import * # noqa: this helper only contains version flags
from dohq_artifactory.exception import ArtifactoryException
from dohq_artifactory.exception import raise_for_status
from dohq_artifactory.logger import logger
Expand Down Expand Up @@ -1537,9 +1539,7 @@ class ArtifactoryPath(pathlib.Path, PureArtifactoryPath):
on regular constructors, but rather on templates.
"""

if sys.version_info.major == 3 and sys.version_info.minor >= 10:
# see changes in pathlib.Path, slots are no more applied
# https://github.com/python/cpython/blob/ce121fd8755d4db9511ce4aab39d0577165e118e/Lib/pathlib.py#L952
if IS_PYTHON_3_10_OR_NEWER:
_accessor = _artifactory_accessor
else:
# in 3.9 and below Pathlib limits what members can be present in 'Path' class
Expand Down Expand Up @@ -1665,6 +1665,35 @@ def __reduce__(self):
pathlib_reduce = super().__reduce__()
return pathlib_reduce[0], pathlib_reduce[1], self.__dict__

def __deepcopy__(self, memo):
"""
Adapted from https://gist.github.com/orbingol/5cbcee7cafcf4e26447d87fe36b6467a#file-copy_deepcopy-py-L65
"""
# Create a new instance
result = self.__class__.__new__(self.__class__)

# Don't copy self reference
memo[id(self)] = result

# Don't copy the cache - if it exists
if hasattr(self, "_cache"):
memo[id(self._cache)] = self._cache.__new__(dict)

# Get all __slots__ of the derived class
slots = chain.from_iterable(
getattr(s, "__slots__", []) for s in self.__class__.__mro__
)

# Deep copy all other attributes
for var in slots:
# Since we process the whole inheritance chain from __mro__, there might be some attributes from parent
# classes missing in the current object. Marking these attributes as "undefined-attribute" to skip assigning
if getattr(self, var, "undefined-attribute") != "undefined-attribute":
setattr(result, var, copy.deepcopy(getattr(self, var), memo))

# Return updated instance
return result

@property
def top(self):
obj = ArtifactoryPath(self.drive)
Expand Down Expand Up @@ -1882,7 +1911,7 @@ def __rtruediv__(self, key):
obj.timeout = self.timeout
return obj

if sys.version_info < (3,):
if IS_PYTHON_2:
__div__ = __truediv__
__rdiv__ = __rtruediv__

Expand Down
14 changes: 7 additions & 7 deletions dohq_artifactory/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import random
import re
import string
import sys
import time
import warnings

import jwt
from dateutil.parser import isoparse

from dohq_artifactory.compat import * # noqa: this helper only contains version flags
from dohq_artifactory.exception import ArtifactoryException
from dohq_artifactory.exception import raise_for_status
from dohq_artifactory.logger import logger
Expand Down Expand Up @@ -51,10 +51,10 @@ def _new_function_with_secret_module(pw_len=16):
return "".join(secrets.choice(string.ascii_letters) for i in range(pw_len))


if sys.version_info < (3, 6):
generate_password = _old_function_for_secret
else:
if IS_PYTHON_3_6_OR_NEWER:
generate_password = _new_function_with_secret_module
else:
generate_password = _old_function_for_secret


def deprecation(message):
Expand Down Expand Up @@ -656,7 +656,7 @@ def __truediv__(self, key):
def __rtruediv__(self, key):
return self.path.__truediv__(key)

if sys.version_info < (3,):
if IS_PYTHON_2:
__div__ = __truediv__
__rdiv__ = __rtruediv__

Expand Down Expand Up @@ -865,7 +865,7 @@ def __init__(
package_type=Repository.GENERIC,
*,
packageType=None,
default_deployment_repo_name=None
default_deployment_repo_name=None,
):
super(RepositoryVirtual, self).__init__(artifactory)
self.name = name
Expand Down Expand Up @@ -895,7 +895,7 @@ def _create_json(self):
"packageType": self.package_type,
"repositories": self._repositories,
"notes": self.notes,
"defaultDeploymentRepo": self.default_deployment_repo_name
"defaultDeploymentRepo": self.default_deployment_repo_name,
}

return data_json
Expand Down
7 changes: 7 additions & 0 deletions dohq_artifactory/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import sys

IS_PYTHON_2 = sys.version_info < (3,)
IS_PYTHON_3_6_OR_NEWER = sys.version_info >= (3, 6)
# see changes in pathlib.Path, slots are no more applied
# https://github.com/python/cpython/blob/ce121fd8755d4db9511ce4aab39d0577165e118e/Lib/pathlib.py#L952
IS_PYTHON_3_10_OR_NEWER = sys.version_info >= (3, 10)
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import os
import sys

import pytest

Expand All @@ -9,8 +8,9 @@
from dohq_artifactory import PermissionTarget
from dohq_artifactory import RepositoryLocal
from dohq_artifactory import User
from dohq_artifactory.compat import * # noqa: this helper only contains version flags

if sys.version_info[0] < 3:
if IS_PYTHON_2:
import ConfigParser as configparser
else:
import configparser
Expand Down
14 changes: 12 additions & 2 deletions tests/integration/test_integration_artifactory_path.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import copy
import tempfile

import pytest

import artifactory
from artifactory import sha1sum
from artifactory import sha256sum
from dohq_artifactory.compat import * # noqa: this helper only contains version flags

if sys.version_info[0] < 3:
if IS_PYTHON_2:
import StringIO as io
else:
import io
Expand Down Expand Up @@ -281,3 +282,12 @@ def test_read_and_write(path):
assert p.read_text() == "Some test string ensure length > 32"

p.unlink()


def test_deepcopy(path):
p = path("/integration-artifactory-path-repo/foo")
p2 = copy.deepcopy(p)

assert p2 == p
# different object, same value
assert not (p2 is p)

0 comments on commit 176e67b

Please sign in to comment.