Repository: devopshq/artifactory

Issue:
path.stat() doesn't handle ArtifactoryPath() properly
Hi,

Here is my environment:

````
python 3.8.4
dohq-artifactory 0.8.4
````

I want to get the stats of 2 files. I have a piece of code looking like this:

````python
from artifactory import ArtifactoryPath

path1 = ArtifactoryPath(
    "https://artifactory.domain.com/artifactory/repo-docker-local/rdl/rd-image/0.2.0/manifest.json", apikey=token
)

path2 = ArtifactoryPath(
    "https://artifactory.domain.com/artifactory/repo-docker-local/rdl/artifactory.domain.com/repo-docker/rd/rd-image/0.2.0/manifest.json", apikey=token
)

# Get FileStat
stat1 = path1.stat()    # works
print(stat1)
stat2 = path2.stat()    # error
print(stat2)
````

As you can see the path in `path2` is containing 2 times the artifactory host `artifactory.domain.com`.
In the error message below I can see it tries to fetch the file but only using half the real path: it crops everything before the second `artifactory.domain.com`.

````bash
$ python tests.py --token=****

ArtifactoryFileStat(ctime='[...]', mtime='[...]', created_by='[...]', modified_by='[...]') # print(stat1), shorted for libility

Traceback (most recent call last):
  File "[...]/.env/lib/python3.8/site-packages/dohq_artifactory/exception.py", line 20, in raise_for_status
    response.raise_for_status()
  File "[...]/.env/lib/python3.8/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://artifactory.domain.com/repo-docker/rd/rd-image/0.2.0/manifest.json

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tests.py", line 35, in <module>
    stats2 = path2.stat()
  File "[...]/.env/lib/python3.8/site-packages/artifactory.py", line 1610, in stat
    return self._accessor.stat(pathobj=pathobj)
  File "[...]/.env/lib/python3.8/site-packages/artifactory.py", line 878, in stat
    jsn = self.get_stat_json(pathobj)
  File "[...]/.env/lib/python3.8/site-packages/artifactory.py", line 869, in get_stat_json
    raise_for_status(response)
  File "[...]/.env/lib/python3.8/site-packages/dohq_artifactory/exception.py", line 28, in raise_for_status
    raise ArtifactoryException(str(exception)) from exception
dohq_artifactory.exception.ArtifactoryException: 404 Client Error: Not Found for url: https://artifactory.domain.com/repo-docker/rd/rd-image/0.2.0/manifest.json
````

I cannot change the files names. It is not in my perimeter.

To identify where the problem comes from I tried the same thing but using JFrog Web API and it works:

````bash
$ curl -u user:passwd https://artifactory.domain.com/artifactory/api/storage/repo-docker-local/rdl/artifactory.domain.com/repo-docker/rdl/rd-image/0.2.0 -v
* About to connect() to artifactory.domain.com port <port> (#0)
*   Trying <ip>...
* Connected to artifactory.domain.com (<ip>) port <port> (#0)
* [...]
> GET /artifactory/api/storage/repo-docker-local/rdl/artifactory.domain.com/repo-docker/rdl/rd-image/0.2.0 HTTP/1.1
> Authorization: Basic xxxx
> User-Agent: <user_agent>
> Host: artifactory.domain.com
> Accept: */*
>
< HTTP/1.1 200
< Date: Wed, 17 May 2023 11:45:28 GMT
< Content-Type: application/vnd.org.jfrog.artifactory.storage.FolderInfo+json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-JFrog-Version: <jfrog_version>
< X-Artifactory-Id: <artifactory_id>
< X-Artifactory-Node-Id: <artifactory_node_id>
< Cache-Control: no-store
< Set-Cookie: <cookie>
<
{
  "repo" : "repo-docker-local",
  "path" : "/rdl/artifactory.domain.com/repo-docker/rdl/rd-image/0.2.0",
  "created" : "<created>",
  "createdBy" : "<createdBy>",
  "lastModified" : "<lastModified>",
  "modifiedBy" : "<modifiedBy>",
  "lastUpdated" : "<lastUpdated>",
  "children" : [{
    "uri" : "/manifest.json",
    "folder" : false,
    [...]
  }],
  "uri" : "https://artifactory.domain.com:<port>/artifactory/api/storage/repo-docker-local/rdl/artifactory.domain.com/repo-docker/rdl/rd-image/0.2.0"
}
* Connection #0 to host artifactory.domain.com left intact
````

For me the problem is from how the `stat()` method parses an ArtifactoryPath.

Do you have any solutions ?


Comments:

Patch:
diff --git a/artifactory.py b/artifactory.py
index 4325018..69bc540 100755
--- a/artifactory.py
+++ b/artifactory.py
@@ -410,13 +410,13 @@ def quote_url(url):
     parsed_url = urllib3.util.parse_url(url)
     if parsed_url.port:
         quoted_path = requests.utils.quote(
-            url.rpartition(f"{parsed_url.host}:{parsed_url.port}")[2]
+            url.partition(f"{parsed_url.host}:{parsed_url.port}")[2]
         )
         quoted_url = (
             f"{parsed_url.scheme}://{parsed_url.host}:{parsed_url.port}{quoted_path}"
         )
     else:
-        quoted_path = requests.utils.quote(url.rpartition(parsed_url.host)[2])
+        quoted_path = requests.utils.quote(url.partition(parsed_url.host)[2])
         quoted_url = f"{parsed_url.scheme}://{parsed_url.host}{quoted_path}"
 
     return quoted_url


Test patch:
diff --git a/tests/unit/test_artifactory_path.py b/tests/unit/test_artifactory_path.py
index f2f57d9..16e528d 100644
--- a/tests/unit/test_artifactory_path.py
+++ b/tests/unit/test_artifactory_path.py
@@ -100,6 +100,10 @@ class ArtifactoryFlavorTest(unittest.TestCase):
         check(
             "https://example.com/artifactory/foo", "https://example.com/artifactory/foo"
         )
+        check(
+            "https://example.com/artifactory/foo/example.com/bar",
+            "https://example.com/artifactory/foo/example.com/bar"
+        )
         check(
             "https://example.com/artifactory/foo/#1",
             "https://example.com/artifactory/foo/%231",