ARTIFACTORY: How to Upload a PyPI Package to Artifactory via REST API

ARTIFACTORY: How to Upload a PyPI Package to Artifactory via REST API

AuthorFullName__c
Jeremy Leopold
articleNumber
000006356
ft:sourceType
Salesforce
FirstPublishedDate
2025-02-27T11:01:31Z
lastModifiedDate
2025-02-26
VersionNumber
2
Objective

When attempting to upload specific PyPI packages to designated subfolders for permission control and organization, neither twine nor sdist support direct uploads to subfolders. This is because the PyPI API in Artifactory does not inherently allow subfolder creation. Instead, folder management is handled through the standard repository API. As a result, the typical PUT or POST methods that would normally create a folder in Artifactory will not function within the /artifactory/api/pypi/... context.

As a work around, you might consider uploading PyPI packages to Artifactory using the Deploy Artifact REST API via cURL, while ensuring that the correct metadata (such as pypi.name and pypi.version) is included for proper handling.


Steps to Upload a PyPI Package Using cURL

1. Ensure Correct Metadata
As mentioned in our documentation: Publish PyPI Packages Manually Using REST, when uploading a PyPI package to Artifactory, it is essential to include the pypi.name and pypi.version properties in the deployment URL. This allows Artifactory to correctly handle the package as a PyPI artifact.

Kindly note that when Using Properties in Deployment and Resolution, Artifactory supports the Matrix Parameter standard.

2. Prepare the cURL Command
In this example, we will demonstrate uploading the peppercorn.whl file (retrieved from the remote-cache repository after installing peppercorn).
 
To upload the .whl file, use the following cURL command template. Replace the placeholders with the actual values for your environment:
curl -X PUT "http://<JFROG_HOST>/artifactory/<REPO_NAME>;pypi.name=<PACKAGE_NAME>;pypi.version=<PACKAGE_VERSION>"/<PACKAGE_PATH> \
     -H "Content-Type: application/json" \
     -u <ARTIFACTORY_USER>:<ARTIFACTORY_PASSWORD> \
     -T <PACKAGE_FILE_PATH>
3. Run the cURL Command
When the command is executed, Artifactory will process the upload and provide a response. You should see a 201 Created status code if the upload is successful.

Example command and response:
curl -v -X PUT "http://<JFrog_HOST>:8082/artifactory/jeremyl-pypi-local;pypi.name=peppercorn;pypi.version=0.6"/peppercorn/peppercorn-0.6-py3-none-any.whl \
     -H "Content-Type: application/json" \
     -u admin-user:password \
     -T peppercorn-0.6-py3-none-any.whl

*   Trying <JFrog_HOST>:8082...
* Connected to <JFrog_HOST> (<JFrog_HOST>) port 8082
* Server auth using Basic with user 'admin'
> PUT /artifactory/jeremyl-pypi-local;pypi.name=peppercorn;pypi.version=0.6/peppercorn/peppercorn-0.6-py3-none-any.whl HTTP/1.1
> Host: <JFrog_HOST>:8082
> Authorization: Basic YWRtaW46UGFzc3dvcmQxIQ==
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 4796
> 
* upload completely sent off: 4796 bytes
< HTTP/1.1 201 Created
< Content-Type: application/vnd.org.jfrog.artifactory.storage.ItemCreated+json;charset=ISO-8859-1
< Date: Tue, 04 Feb 2025 07:48:06 GMT
< Location: http://<JFrog_HOST>:8082/artifactory/jeremyl-pypi-local/peppercorn/peppercorn-0.6-py3-none-any.whl
< X-Artifactory-Id: 2a1532ff3453e832:-481af2ed:194cfc95e26:-8000
< X-Artifactory-Node-Id: 3e62cc23c644
< X-Checksum-Sha256: 46125cad688a9cf3b08e463bcb797891ee73ece93602a8ea6f14e40d1042d454
< X-Jfrog-Version: Artifactory/7.98.12 79007900
< Transfer-Encoding: chunked
< 
{
  "repo" : "jeremyl-pypi-local",
  "path" : "/peppercorn/peppercorn-0.6-py3-none-any.whl",
  "created" : "2025-02-04T07:48:06.274Z",
  "createdBy" : "admin",
  "downloadUri" : "http://<JFrog_HOST>:8082/artifactory/jeremyl-pypi-local/peppercorn/peppercorn-0.6-py3-none-any.whl",
  "mimeType" : "application/octet-stream",
  "size" : "4796",
  "checksums" : {
    "sha1" : "a0d499a7570e49978bcd3dcfbca1debd2ed95f23",
    "md5" : "9c91aab388c9adc1617c9404ddb48a7a",
    "sha256" : "46125cad688a9cf3b08e463bcb797891ee73ece93602a8ea6f14e40d1042d454"
  },
  "originalChecksums" : {
    "sha256" : "46125cad688a9cf3b08e463bcb797891ee73ece93602a8ea6f14e40d1042d454"
  },
  "uri" : "http://<JFrog_HOST>:8082/artifactory/jeremyl-pypi-local/peppercorn/peppercorn-0.6-py3-none-any.whl"
* Connection #0 to host <JFrog_HOST> left intact
}
4. Verify the Upload
Observe the package uploaded including the appropriate properties, with its metadata indexed successfully. 
 
User-added image 


5. Access the Uploaded Package
Following the download URI provided in the Location response header. This URL allows you to download the package from the Artifactory repository.

In this case, the download URL is:
http://<JFrog_HOST>:8082/artifactory/jeremyl-pypi-local/peppercorn/peppercorn-0.6-py3-none-any.whl
User-added image 
6. Install The Package
Configure the client according to the repository Set Me Up instructions. For example: 
cat ~/.pip/pip.conf

[global]
index-url = http://user:cmV…Jy@<JFROG_HOST>:8081/artifactory/api/pypi/jeremyl-pypi-local/simple
Run the install command:
pip3 install peppercorn --trusted-host <JFROG_HOST>

Looking in indexes: http://user:****@<JFROG_HOST>:8081/artifactory/api/pypi/jeremyl-pypi-local/simple
Collecting peppercorn
  Downloading http://<JFROG_HOST>:8081/artifactory/api/pypi/jeremyl-pypi-local/peppercorn/peppercorn-0.6-py3-none-any.whl (4.8 kB)
Installing collected packages: peppercorn
Successfully installed peppercorn-0.6
Conclusion

Following the steps outlined above will allow you to manually upload a PyPI package to Artifactory using the REST API and cURL, ensuring that the correct metadata is included for proper handling and accessibility. Additionally, it was demonstrated how to successfully install the package, confirming the functionality of the upload and its accessibility in the Artifactory repository.