The JFrog Artifactory integration with the Python Package Index (PyPI) allows you to manage PyPI packages in Artifactory. PyPI is a repository of software for the Python programming language. For more information, see the Python Packaging User Guide and Common Questions on the PyPI website.

PyPI_diagram2.png

Artifactory fully supports PyPI repositories, including:

  • Provisioning PyPI packages from Artifactory to the Poetry, Twine, pip, and uv command line tools from all repository types.
  • Calculating metadata for PyPI packages hosted in Artifactory's local repositories.
  • Accessing remote PyPI repositories (such as https://pypi.org/) through Remote Repositories, which provide proxy and caching functionality.
  • Accessing multiple PyPI repositories from a single URL by aggregating them under a Virtual Repository.
  • Compatibility with the setuptools (and its predecessor distutils) libraries for uploading PyPI packages.

Get Started with PyPI

To get started working with PyPI, complete the following main steps:

  1. Create a PyPI Repository
  2. Connect Your PyPI Client to Artifactory
  3. Publish and install PyPI packages using a supported PyPI client:

📘

Note

As Artifactory supports legacy PyPI, both underscores (_) and dots (.) are allowed in package names. However, they will not be aggregated into one package.

For example, jfrog.pypi and jfrog_pypi will be treated as two separate packages with the same behavior as in the PyPI registry.

Create a PyPI Repository

This topic describes how to create a PyPI Repository. This is required before publishing and installing PyPI packages. There are three primary types of repositories:

  • Local repositories: Where you store and share 1st and 2nd party packages with your organization
  • Remote repositories: Enable you to download from any remote location, including external package registries or other Artifactory instances
  • Virtual repositories: Enable aggregating remote and local repositories, enabling your organization to scale by providing a single URL that allows access to multiple repositories and types

For more information on JFrog repositories, see Repository Management.

Prerequisite: You need Admin or Project Admin permissions to create a PyPI repository. If you don't have Admin permissions, the option will not be available.

To create a PyPI repository:

  1. In the Administration tab, click Repositories.

    CreateAnsibleLocal1.png

  2. Click Create a Repository and then select the repository type you want to create (local, remote, or virtual).

  3. In the Select Package Type window, click PyPI.

  4. Configure the repository, as described below:

  5. Click Create Repository.

Connect Your PyPI Client to Artifactory

This topic provides details on configuring PyPI to work with Artifactory.

Prerequisite: Before connecting your PyPI client to Artifactory, you must have an existing PyPI repository in Artifactory. For more information, see Create a PyPI Repository.

Supported PyPI clients:

  • For installing and publishing packages: Poetry (from version 1.2.0 and later), uv (from version 0.8.15 and later)
  • For installing packages only: Pip (from version 20.2 and later)
  • For publishing packages only: Twine (from version 3.3.0 and later)

Poetry Client

The following topics describe how to connect Poetry as well as install and publish packages:

Connect the Poetry PyPI Client to Artifactory

The Poetry client can be used to both install and publish Python packages.

To connect the Poetry PyPI Client to Artifactory:

  1. In the command line, configure the upload endpoint for your publishable repository with the following commands:

    poetry config repositories.artifactory-<REPOSITORY_NAME>
    https://<YOUR_JFROG_DOMAIN>/artifactory/api/pypi/<REPOSITORY_KEY>
    
    poetry config http-basic.artifactory-<REPOSITORY_NAME> <USER> <PASSWORD/TOKEN>

    For example:

    poetry config repositories.artifactory-pypi-local
    https://my-awesome.jfrog.io/artifactory/api/pypi/pypi-local
    
    poetry config http-basic.artifactory-pypi-local johnfrog RANDOM_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT
  2. Add the repository source with the source add command as follows:

    poetry config http-basic.<REPOSITORY_NAME> <USER><PASSWORD/TOKEN>
    
    poetry source add <REPOSITORY_NAME> https://<YOUR_JFROG_DOMAIN>/artifactory/api/pypi/<REPOSITORY_NAME>/simple

    For example:

    poetry config http-basic.pypi-local johnfrog RANDOM_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT
    
    poetry source add pypi-local https://my-awesome.jfrog.io/artifactory/api/pypi/pypi-local/simple
📘

Note

The source add command inserts the URL into the project's pyproject.toml.

Next steps:

Install Python Packages with Poetry

To install Python packages with Poetry:

Run the following command:

poetry add --source <REPOSITORY_NAME> <PACKAGE_NAME>

For example:

poetry add --source pypi-local my_pypi_package

Publish Python Packages with Poetry

To publish Python packages with Poetry:

Run the following command:

poetry publish --repository artifactory-<REPOSITORY_NAME> --dist-dir dist

For example:

poetry publish --repository artifactory-pypi-local --dist-dir dist

Twine Client

The following topics describe how to connect the Twine client and publish packages:

Connect the Twine PyPI Client to Artifactory

The Twine client can be used to publish Python packages on PyPI.

To connect the Twine PyPI client with Artifactory:

To deploy packages using Twine, add an Artifactory repository to the .pypirc file (usually located in your home directory) as follows:

[distutils]
index-servers = <REPOSITORY_NAME>
[<REPO_NAME>]
repository: https://<YOUR_JFROG_DOMAIN>/artifactory/api/pypi/<REPOSITORY_NAME>
username: <USER>
password: <PASSWORD/TOKEN>

For example:

[distutils]
index-servers = pypi-local
[pypi-local]
repository: https://my-awesome.jfrog.io/artifactory/api/pypi/pypi-local
username: johnfrog
password: RANDOM_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT

Next step:

Publish Python Packages with Twine

To publish (upload) Python packages with Twine:

To upload a Python package to Artifactory, use the following command:

twine upload --repository <REPOSITORY_NAME> <PATH_TO_FILE>

For example:

twine upload --repository pypi-local ~/workspace/my_file.tar.gz

Pip Client

The following topics describe how to connect the Pip client and install packages:

Connect the Pip PyPI Client to Artifactory

The pip client can be used to install Python packages on your local system.

Running on Windows

To use Artifactory PyPI repositories on Windows, you must set the required environment variables for Python and pip.

On Windows, the per-user configuration file is named pip.ini and is located at %APPDATA%\pip\pip.ini. A legacy per-user configuration file at %HOME%\pip\pip.ini is also respected.

The pip.ini file replaces the pip.conf file used on other operating systems.

To connect the pip PyPI client with Artifactory:

Add the following to ~/.pip/pip.conf:

[global]
index-url = https://<USER>:<PASSWORD/TOKEN>@<YOUR_JFROG_DOMAIN>/artifactory/api/pypi/<REPO_NAME>/simple

For example:

[global]
index-url = https://johnfrog:[email protected]/artifactory/api/pypi/pypi-local/simple
📘

Note

If credentials are required, they should be embedded in the URL.

Next step:

Install Python Packages with Pip

To install Python packages with the pip client:

Run the following command:

pip3 install <PACKAGE_NAME>

For example:

pip3 install my_pypi_package

uv Client

The following topics describe how to connect the uv client to Artifactory and use it to manage Python packages:

Connect the uv Client to Artifactory

The uv client can be used to publish Python packages on PyPI. For more information, see uv.

To connect uv to Artifactory, complete the following steps:

  1. Set up authentication
  2. Add environment variables for publishing
  3. Configure default registry
Authenticate the uv Client to Artifactory

Authenticate the uv client to Artifactory to be able to read and install Python packages from your repositories. There are two ways to authenticate the uv client to Artifactory:

Connect uv to Artifactory using uv auth login

Use uv auth login to securely store your Artifactory credentials and connect the uv client to Artifactory.

To connect the uv client to Artifactory using uv auth login:

  • Run the following command:

    uv auth login https://[JFrogPlatformURL]/artifactory/api/pypi/<REPO> --username <USERNAME> --password <AUTH>

    Where:

    • [JFrogPlatformURL]: The URL of your Artifactory service endpoint
    • <REPO>: The name of the target repository
    • <USERNAME>: Your Artifactory username
    • <AUTH>: Your Artifactory identity token

    For example:

    uv auth login https://company.jfrog.io/artifactory/api/pypi/pypi-local --username jeffry --RANDOM_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT
📘

Note

You can also use JFrog Set me up to copy the snippet populated with your token and environment. For more information, see Use Artifactory Set Me Up for Configuring Package Manager Clients.

Connect uv to Artifactory using .netrc

You can manually configure uv to connect to Artifactory by storing your credentials in the .netrc file.

To manually connect the uv client to Artifactory:

  1. Open your .netrc file in a text editor. The file location varies by operating system:

    • Linux/Unix: ~/.netrc
    • Windows: %USERPROFILE%\_netrc
  2. Add the following snippet to the file:

    machine [JFrogPlatformURL]
    login <USERNAME>
    password <AUTH>

    Where:

    • [JFrogPlatformURL]: The hostname of your JFrog service endpoint
    • <USERNAME>: Your Artifactory username
    • <AUTH>: Your Artifactory identity token

    For example:

    machine company.jfrog.io
    login jeffry
    password RANDOM_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT
  3. Save the changes to the file.

📘

Note

You can also use JFrog Set me up to copy the snippet populated with your token and environment. For more information, see Use Artifactory Set Me Up for Configuring Package Manager Clients.

Add uv Environment Variables for Publishing

While authenticating the uv client allows you to install packages, you need to add environment variables to be able to publish packages to Artifactory.

To add environment variables for publishing:

  • Run the following command:

    export UV_PUBLISH_USERNAME=<USERNAME>
    export UV_PUBLISH_PASSWORD=<AUTH>

    Where:

    • <USERNAME>: Your Artifactory username
    • <AUTH>: The Artifactory identity token you want to use for publish permissions

    For example:

    export UV_PUBLISH_USERNAME=jeffry
    export UV_PUBLISH_PASSWORD=PUBLISH_TOKEN_YWRtaW46QVBBVWJjTExkZTU4WT
📘

Note

You can also use JFrog Set me up to copy the snippet populated with your token and environment. For more information, see Use Artifactory Set Me Up for Configuring Package Manager Clients.

Add Artifactory as the Default Registry for uv

To publish to Artifactory using the uv client, configure Artifactory as the default registry in the uv.toml file.

To add Artifactory as your default registry:

  1. Open the uv.toml file in a text editor. The location of the file varies by operating system:

    • Linux/Unix: ~/.config/uv/uv.toml
    • Windows: %APPDATA%\uv\uv.toml

    If the file doesn't exist, create it in the appropriate location.

  2. Add the following snippet to the uv.toml file:

    [[index]]
    name="<REPO>"
    publish-url = "https://[JFrogPlatformURL]/artifactory/api/pypi/<REPO>"
    url = "https://[JFrogPlatformURL]/artifactory/api/pypi/<REPO>/simple"
    default = true

    Where:

    • <REPO>: The name of the target repository
    • [JFrogPlatformURL]: The hostname for your JFrog service endpoint

    For example:

    [[index]]
    name="pypi_local"
    publish-url = "https://company.jfrog.io/artifactory/api/pypi/pypi_local"
    url = "https://company.jfrog.io/artifactory/api/pypi/pypi_local/simple"
    default = true
  3. Save the changes to the file.

📘

Note

You can also use JFrog Set me up to copy the snippet populated with your token and environment. For more information, see Use Artifactory Set Me Up for Configuring Package Manager Clients.

Next step:

Install Python Packages with uv

You can use the uv client to install Python packages from Artifactory.

To resolve packages from Artifactory with uv:

  • Run one of the following commands:

    • Using the uv-native project API:

      uv add <PACKAGE>

      Where <PACKAGE> is the name of the package you want to resolve. For example:

      uv add pandas
    • Using the pip compatibility API:

      uv pip install <PACKAGE>

      Where <PACKAGE> is the name of the package you want to install. For example:

      uv pip install flask

Publish Python Packages with uv

You can use uv to natively publish Python packages to Artifactory.

To publish to Artifactory with uv:

  • Run the following command:

    uv publish <FILE_PATH> --index <REPO>

    Where:

    • <FILE_PATH>: The file path to the project you want to publish
    • <REPO>: The name of the target repository in Artifactory

    For example:

    uv publish dist/* --index pypi_local

Additional PyPI Information

The following additional actions are available with PyPI repositories:

Use PyPI Package Naming Tools

Artifactory provides the following tools for naming PyPI packages correctly:

Used either together or separately, these tools help you keep a consistent naming method for PyPI artifacts to reduce potential package name duplications.

Use PyPI Enforce Layout

The PyPI Enforce Layout feature ensures that uploaded packages adhere to a standardized physical directory structure, filenames, and internal metadata, to promote a consistent and compliant repository.

JFrog recommends using this feature in conjunction with File Path Name Normalization. For more information, see Use File Path Name Normalization and Using Both File Path Name Normalization and Enforce Layout.

PyPI Enforce Layout is available in Artifactory starting from version 7.70.2.

📘

Backward Compatibility

Enforce Layout applies to all PyPI repositories in your Artifactory instance. Previously uploaded packages that already exist in your instance will not be updated but if you attempt to upload a newer version or overwrite an existing one you will be prevented from doing so since the file path name sections must match the metadata values.

This feature enables you to restrict uploads to artifacts where the file name and version in the path match the package name and version stored in the artifact’s metadata.

The following examples explain how this feature applies without File Path Name Normalization:

enforce_layout_table.png

To enable PyPI Enforce Layout, in the artifactory.system.properties settings file set the system property:

artifactory.pypi.enforce.layout = true

Artifactory will return an error when trying to perform actions on an artifact with a name that does not match the package metadata, with a description of the issue:

ErrorDescription
403Action prevented due to Enforce Layout policy - package file name/version <ARTIFACT_NAME> does not match the ” + package internal name/version <ARTIFACT_NAME> in the metadata file
403Action prevented due to Enforce Layout policy - could not find metadata file to compare

Use PyPI File Path Name Normalization

The PyPI File Path Name Normalization feature brings Artifactory closer in alignment with the PyPI.org registry by enforcing a consistent naming method for PyPI artifacts, which reduces potential package name duplications due to the use of non-normalized characters.

JFrog recommends using this feature in conjunction with PyPI Enforce Layout. For more information, see Use PyPI Enforce Layout and Using Both PyPI File Path Name Normalization and Enforce Layout.

PyPI Enforce Layout is available in Artifactory starting from version 7.70.2.

📘

Backward Compatibility

File Path Name Normalization applies to all PyPI repositories in your Artifactory instance, previously uploaded packages that already exist in your instance will not be updated, but if you attempt to upload a newer version or overwrite an existing one you will be prevented from doing so since the file path name does not conform to the normalization rules.

When this feature is enabled, Artifactory blocks the deployment of files with path name/distribution values that do not conform to standard PyPI naming conventions, as specified in the Python documentation pages (see Distribution File Name and File Format).

{NAME}-{VERSION}-{OPTIONAL-PARAMETERS}.tar.gz
{DISTRIBUTION}-{VERSION}-{OPTIONAL-PARAMETERS}.whl

The most common example of how this feature provides value is the prevention of the dash (-), which is considered a Reserved Character between the different segments of the naming path (name/distribution, version, optional parameters, etc.), from being used within the name/distribution segment.

The table below describes which names would be allowed when File Path Name Normalization is enabled:

Normalization_table.png

To enable PyPI File Path Name Normalization, in the artifactory.system.properties settings file set the following system property:

artifactory.pypi.enforce.naming.normalization = true 

Artifactory will return an error when trying to perform actions on an artifact whose name does not match the naming conventions, with a description of the issue:

ErrorDescription
403Action prevented due to artifact file name normalization requirements - artifact name <ARTIFACT_NAME> does not conform to normalized requirements

Use Both PyPI File Path Naming Normalization and Enforce Layout

PyPI Path Name Normalization and PyPI Enforce Layout work both independently and together to create a safety net for correct package naming.

The Enforce Layout setting ensures that the file path and metadata have matching properties, while the File Path Name Normalization setting verifies that the names conform to standard PyPI naming conventions. Using both features will ensure that you do not have duplicate or redundant packages.

❗️

Important

Please note that although PyPI allows the use of the dash in the internal metadata names, our enforce layout feature is strict, and your metadata names must always match the file path name.

For example, this will be the behavior when both File Path Name Normalization and Enforce Layout are enabled:

Normalization_and_enforce_layout_table.png

Search for PyPI Packages

Use Pip Search

Artifactory supports search using the pip search command in local and virtual repositories. For example:

$ pip search frog-fu --index http://localhost:8081/artifactory/api/pypi/pypi-virtual/
frog-fu                   - 0.2a
  INSTALLED: 0.2a (latest)

$ pip search irbench --index http://localhost:8081/artifactory/api/pypi/pypi-virtual/
irbench                   - Image Retrieval Benchmark.

In this example, frog-fu and irbench are two locally installed packages in different local repositories, and both repositories are aggregated by the pypi-virtual repository.

📘

Note

Since pip search has been deprecated, Artifactory no longer supports pip search in remote repositories, including Smart Remote repositories. As such, if a virtual repository includes remote repositories, those remote repositories will not be scanned for search results.

📘

Specifying the index

When using the search command, the index should be specified explicitly (without the /simple at the end), as pip will ignore the index-url variable in its pip.conf file.

Use Artifactory Search

PyPI packages can also be searched for using Artifactory's Property Search. All PyPI packages have the properties pypi.name, pypi.version and pypi.summary set by the uploading client, or later during indexing for supported file types.

View Metadata of PyPI Packages

Artifactory lets you view selected metadata for a PyPI package directly from the UI.

In the Artifacts module Tree Browser, drill down to select the file you want to inspect. The metadata is displayed in the PyPI Info tab.

PyPIInfo.jpg

Work with PyPI Remote Repositories with the Custom Registry Suffix

You can set a custom suffix instead of the default suffix, for example, when working with DevPi.

To set the devpi registry suffix to the server suffix:

When creating the repository, enter the same root URL for both the URL and Registry URL fields. For example: http://m.devpi.net.

PyPI_remote-devpi_repo.png

To search, include the required scope in the index URL (as in the devpi example, it could be root/pypi).

$ pip search frog-fu --index http://localhost:8081/artifactory/api/pypi/devpi/root/pypi/

To install, include the desired scope in the index URL (as in the devpi example, it could be root/pypi).

$ pip install frog-bar -i http://localhost:8081/artifactory/api/pypi/devpi/root/pypi/simple

Resolve from Artifactory Using Pip

To install the pip command line tool refer to pip documentation. We recommend using virtualenv to separate your environment when installing PIP.

📘

Using a Valid SSL Certificate with pip and Artifactory

pip uses packages from the local cache, (i.e. from the machine on which the pip client is running on), only if the download URL of the package is a trusted host with a valid SSL certificate. This means that if your Artifactory instance is not running with a valid SSL certificate, requests for packages will always first reach Artifactory even if the packages exist on the local cache.

To display code snippets you can use to configure pip and setup.py to use your PyPI repository, select the repository and then click Set Me Up.

PyPISetMeUp.jpg

Specify the PyPI Repository on the Command Line

📘

Index URL

When accessing a PyPI repository through Artifactory, the repository URL should be prefixed with api/pypi in the path. This applies to all pip commands and distutils URLs including pip install.

When using pip to resolve PyPI packages it must point to <Artifactory URL>/api/pypi/<repository key>/simple .

For example, if you are using Artifactory standalone or as a local service, you would access your PyPI repositories using the following URL:

http://localhost:8081/artifactory/api/pypi/<repository key>/simple

Or, if you are using Artifactory Cloud, the URL would be:

https://<server name>.jfrog.io/artifactory/api/pypi/<repository key>/simple

Once pip is installed, it can be used to specify the URL of the repository from which to resolve:

Installing with full repository URL

$ pip install frog-bar -i http://localhost:8081/artifactory/api/pypi/pypi-local/simple
📘

Note

The default configuration snippet for Nginx and Apache using reverse proxy contains the X-JFrog-Override-Base-Url by default. If reverse proxy is not used in your environment, you need to add the header manually to the request, for example:

-H "X-JFrog-Override-Base-Url: http://$ART_HOST"

Instead of adding the header manually, you can also run the request on port 8081, or add a slash (/) at the end of the request:

http://$ART_HOST/artifactory/api/pypi/pypi-virtual/simple/

Use PyPI Credentials

Due to its design, pip does not support reading credentials from a file. Credentials can be supplied as part of the URL, for example http://<username>:<password>@localhost:8081/artifactory/api/pypi/pypi-local/simple. The password can be omitted (with the preceding colon), and in this case, the user will be prompted to enter credentials interactively.

Use a Pip Configuration File

Aliases for different repositories can be specified through a pip configuration file, ~/.pip/pip.conf . The file contains configuration parameters per repository, for example:

~/.pip/pip.conf

[global]
index-url = http://user:password@localhost:8081/artifactory/api/pypi/pypi-virtual/simple

For more information, please refer to PIP User Guide.

Use a Pip Requirements File

A requirements file contains a list of packages to install. Usually, these are dependencies for the current package. It can be created manually or using the pip freeze command. The index URL can be specified in the first line of the file, For example:

requirements.txt

--index-url http://localhost:8081/artifactory/api/pypi/pypi-local/simple
PyYAML==3.11
argparse==1.2.1
frog-bar==0.2
frog-fu==0.2a
nltk==2.0.4
wsgiref==0.1.2

Additional PyPI Publishing Tools

📘

Limitation

Users should not deploy packages in Artifactory to repositories in the paths pypi-repo/packages/** or pypi-repo/simple/** , as these are saved namespaces used by Artifactory for internal logic. If you attempt to deploy to these paths, Artifactory returns a 400 error.

In addition to the PyPI clients supported by Artifactory, there are several alternative methods for publishing PyPI packages to Artifactory:

Use PyPI distutils or setuptools

📘

setuptools vs. distutils and python versions

Artifactory is agnostic to whether you use setuptools or distutils, and also to the version or implementation of Python your project uses.

The following instruction were written for Python 2.7 and setuptools in mind. Using different version of Python, or different tools such zest, distutils and others may require minor modification to the instructions below.

Uploading to Artifactory using a setup.py script is supported in a similar way to uploading to PyPI. First, you need to add Artifactory as an index server for your user.

For instructions on using setuptools to package Python projects and create a setup.py script, please refer to the setuptools documentation and this tutorial project.

Create the $HOME/.pypirc File

To upload to Artifactory, an entry for each repository needs to be made in $HOME/.pypirc as follows:

[distutils]
index-servers =
    local
    pypi

[pypi]
repository: https://pypi.org/pypi
username: mrBagthrope
password: notToBeSeen

[local]
repository: http://localhost:8081/artifactory/api/pypi/pypi-local
username: admin
password: password

Notice that the URL does not end with /simple.

📘

The HOME environment variable

setuptools requires that the .pypirc file be found under $HOME/.pypirc, using the HOME environment variable.

On unix-like systems this is usually set by your system to /home/yourusername/ but in certain environments such as build servers you will have to set it manually.

On Windows it must be set manually.

Use .netrc to Upload PyPI Packages

Uploading PyPI packages to Artifactory using a .netrc file credentials is supported. For more information on the .netrc file , see The .netrc file and Purpose of the '.netrc' file.

📘

Note

To use the .netrc file:

  1. To create a .netrc file in your root directory and set it with the minimum required permissions (Chmod 600), if you do not have one already, run the following command:

    touch ~/.netrc
  2. Run the following command to populate the .netrc file with your Artifactory URL and credentials according to the .netrc file format:

📘

Note

Make sure to replace the placeholders in bold with your own Artifactory URL, username, and password or identity token.

echo "machine <ARTIFACTORY_URL> login <USERNAME> password <PASSWORD/TOKEN>" > ~/.netrc

Upload Authenticated PyPI Packages to JFrog Artifactory

To upload authenticated PyPI packages to JFrog Artifactory:

  1. Create the .pypirc file using the following code, and add your access token from JFrog Artifactory for the username pypiadmin..

    [distutils]
    index-servers =
       private-repository
     
    [private-repository]
    repository = https://soleng.jfrog.io/artifactory/api/pypi/demo-pypi-local
    username = pypiadmin
    password = eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJsYkpadzNJUU13WXBBSWNRa01RRjN0dlA2Yml5M3dWcXdrQ0txUkxLaXhRIn0.eyJleHQiOiJ7XCJyZXZvY2FibGVcIjpcInRydWVcIn0iLCJzdWIiOiJqZmFjQDAxZTlycTMza3ljMHQxMWtwangybmcwemo1XC91c2Vyc1wvcHlwaWFkbWluIiwic2NwIjoiYXBwbGllZC1wZXJtaXNzaW9uc1wvYWRtaW4iLCJhdWQiOiIqQCoiLCJpc3MiOiJqZmZlQDAwMCIsImV4cCI6MTY4OTY5ODA5MywiaWF0IjoxNjU4MTYyMDkzLCJqdGkiOiJiZWY1YWY5Ni0zNTkyLTRiOTQtYWYyNS1kZmIxY2U1ZGU4YzIifQ.NI5OXpw0NHs7Asd3f_sY3tMyzM-2_07c3WyWEpbJrDPxO8eKoLRp10vGEF8Jo3HyRQ0H7Ybzf2-Cn8wf9yFMo4UGlgxGfm7_yc24xWVLCINjg0B2A5YRSvAetwdT2wgVPvEMUqCPSCU4_SGgGg606lIDxxImRfgZWwFn-wHfU8b8dCV7EV4dXGvH7iVb33W2JguE9KIQFP7lKkQlaErO6pGFNuPfOx1JbJllHh0oRpAPzvykda2i6Q2q3ZCObJZ9Rp8NqZYQfEm42YtIaOvAlE5fGepZgDjzHaaLcztJDHoR-BWjMiDfP0LRThHASo7F52t8p3vsZHW5NEov_trFrQ
  2. Copy the .pypirc file to your Users folder.

    • For macOS users, create the file under %USERPROFILE%
    • For Windows users, create the file under c:\users\<name>
  3. Validate that the setup.py file contains all of the following fields. It should be automatically created, but if it is not, add the file to your root folder with the following content.

    #!/usr/bin/env python
     
    from setuptools import setup
     
    setup(
        name='demo-python-example',
        version='1.0',
        description='Project example for building Python project with JFrog products',
        author='JFrog',
        author_email='[email protected]',
        packages=['helloworld'],
        install_requires=['PyYAML>3.11', 'nltk'],
    )
  4. Create a PyPI package.

    py setup.py sdist bdist_wheel

    The following displays an example output.

    C:\Users\johnk\helloworld>py setup.py sdist bdist_wheel
    running sdist
    running egg_info
    writing demo_python_example.egg-info\PKG-INFO
    writing dependency_links to demo_python_example.egg-info\dependency_links.txt
    writing requirements to demo_python_example.egg-info\requires.txt
    writing top-level names to demo_python_example.egg-info\top_level.txt
    reading manifest file 'demo_python_example.egg-info\SOURCES.txt'
    writing manifest file 'demo_python_example.egg-info\SOURCES.txt'
    warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
    running check
    creating demo-python-example-1.0
    creating demo-python-example-1.0\demo_python_example.egg-info
    creating demo-python-example-1.0\helloworld
    copying files to demo-python-example-1.0...
    copying setup.py -> demo-python-example-1.0
    copying demo_python_example.egg-info\PKG-INFO -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\SOURCES.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\dependency_links.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\requires.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\top_level.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying helloworld\app.py -> demo-python-example-1.0\helloworld
    Writing demo-python-example-1.0\setup.cfg
    Creating tar archive
    removing 'demo-python-example-1.0' (and everything under it)
    running bdist_wheel
    running build
    running build_py
    C:\Users\johnk\installs\lib\site-packages\setuptools\command\install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
      warnings.warn(
    installing to build\bdist.win-amd64\wheel
    running install
    running install_lib
    creating build\bdist.win-amd64\wheel
    creating build\bdist.win-amd64\wheel\helloworld
    copying build\lib\helloworld\app.py -> build\bdist.win-amd64\wheel\.\helloworld
    running install_egg_info
    Copying demo_python_example.egg-info to build\bdist.win-amd64\wheel\.\demo_python_example-1.0-py3.10.egg-info
    running install_scripts
    creating build\bdist.win-amd64\wheel\demo_python_example-1.0.dist-info\WHEEL
    creating 'dist\demo_python_example-1.0-py3-none-any.whl' and adding 'build\bdist.win-amd64\wheel' to it
    adding 'helloworld/app.py'
    adding 'demo_python_example-1.0.dist-info/METADATA'
    adding 'demo_python_example-1.0.dist-info/WHEEL'
    adding 'demo_python_example-1.0.dist-info/top_level.txt'
    adding 'demo_python_example-1.0.dist-info/RECORD'
    removing build\bdist.win-amd64\wheel
    C:\Users\johnk\helloworld>
  5. Upload the package to JFrog Artifactory.

    py setup.py sdist upload -r private-repository

    The following displays an example output.

    C:\Users\johnk\helloworld>py setup.py sdist bdist_wheel
    running sdist
    running egg_info
    writing demo_python_example.egg-info\PKG-INFO
    writing dependency_links to demo_python_example.egg-info\dependency_links.txt
    writing requirements to demo_python_example.egg-info\requires.txt
    writing top-level names to demo_python_example.egg-info\top_level.txt
    reading manifest file 'demo_python_example.egg-info\SOURCES.txt'
    writing manifest file 'demo_python_example.egg-info\SOURCES.txt'
    warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
    running check
    creating demo-python-example-1.0
    creating demo-python-example-1.0\demo_python_example.egg-info
    creating demo-python-example-1.0\helloworld
    copying files to demo-python-example-1.0...
    copying setup.py -> demo-python-example-1.0
    copying demo_python_example.egg-info\PKG-INFO -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\SOURCES.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\dependency_links.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\requires.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying demo_python_example.egg-info\top_level.txt -> demo-python-example-1.0\demo_python_example.egg-info
    copying helloworld\app.py -> demo-python-example-1.0\helloworld
    Writing demo-python-example-1.0\setup.cfg
    Creating tar archive
    removing 'demo-python-example-1.0' (and everything under it)
    running bdist_wheel
    running build
    running build_py
    C:\Users\johnk\installs\lib\site-packages\setuptools\command\install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
      warnings.warn(
    installing to build\bdist.win-amd64\wheel
    running install
    running install_lib
    creating build\bdist.win-amd64\wheel
    creating build\bdist.win-amd64\wheel\helloworld
    copying build\lib\helloworld\app.py -> build\bdist.win-amd64\wheel\.\helloworld
    running install_egg_info
    Copying demo_python_example.egg-info to build\bdist.win-amd64\wheel\.\demo_python_example-1.0-py3.10.egg-info
    running install_scripts
    creating build\bdist.win-amd64\wheel\demo_python_example-1.0.dist-info\WHEEL
    creating 'dist\demo_python_example-1.0-py3-none-any.whl' and adding 'build\bdist.win-amd64\wheel' to it
    adding 'helloworld/app.py'
    adding 'demo_python_example-1.0.dist-info/METADATA'
    adding 'demo_python_example-1.0.dist-info/WHEEL'
    adding 'demo_python_example-1.0.dist-info/top_level.txt'
    adding 'demo_python_example-1.0.dist-info/RECORD'
    removing build\bdist.win-amd64\wheel
    C:\Users\johnk\helloworld>
  6. Your package was uploaded successfully to the JFrog Platform. Check it out by navigating to Artifactory > Artifacts in the Platform module.

Upload PyPI Egg and Wheel Packages

After creating a .pypirc file and a setup.py script at the root of your project, you can upload your egg (tar.gz) packages as follows:

~/python_project $ python setup.py sdist upload -r local

If you are using wheel (whl) you can upload your packaged as follows:

~/python_project $ python setup.py bdist_wheel upload -r local

Or if you wish to use both egg (tar.gz) and wheel (whl), you can upload them as follows:

~/python_project $ python setup.py sdist bdist_wheel upload -r local

Where local is the name of the section in your .pypirc file that points to your Artifactory PyPI repository.

📘

Default upload

By default, both setuptools and distutils will upload to https://pypi.org/pypi if no repository is specified.

📘

The 'register' command should be omitted

When uploading directly to pypi.org , the documentation states that your package must first be registered by callingpython setup.py register.

When uploading to Artifactory this is neither required nor supported and should be omitted.

Publish PyPI Packages Manually Using the Web UI or the REST API

PyPI packages can also be uploaded manually using the Web UI or the Artifactory REST API. For Artifactory to handle those packages correctly as PyPI packages, they must be uploaded with pypi.name and pypi.version properties.

📘

Automatic extraction of properties

While indexing the newly uploaded packages Artifactory will automatically try to extract required properties from the package metadata saved in the file. Note that not all supported files can be extracted.

Currently, only zip , tar, tgz, tar.gz, tar.bz2, egg and whl files can be extracted for metadata.

In addition, indexing starts after a 60 second quiet period, counting from the last upload to the current repository.

Enable JSON Indexing in PyPI Repositories

PyPI repositories in Artifactory support JSON indexing through the PyPI Simple JSON API. When JSON indexing is enabled, PyPI repositories in Artifactory return JSON-formatted indexes. JSON formatting is supported for local, remote, and virtual repositories, with local repositories continuing to index both in JSON and HTML.

Prerequisites: You must have Admin or Project Admin permissions to configure Artifactory settings.

To enable JSON indexing:

  1. On the Administration module, navigate to Artifactory Settings > Packages Settings.
  2. Under PyPI, select the Enable simple json format checkbox.
  3. Click Save.

Limitations

The following items are limitations of the PyPI Simple JSON API in PyPI in Artifactory:

  • Fallback logic: If JSON is requested but not available, the system falls back to HTML. If HTML is requested but not available, the system returns nothing.
  • Virtual repositories: Virtual repositories only serve JSON if all included sub-repositories can provide it. If any sub-repository is restricted to HTML, the virtual repository responds with HTML to maintain consistency.
  • Offline availability risks: If JSON indexing is disabled after caching indexes from a remote source, you may lose access to those indexes if that source is offline. Because Artifactory cannot fetch the required HTML equivalents from an offline repository, the packages remain unavailable until the connection is restored.