Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple pypi.url #1310

Closed
andreas-vester opened this issue Aug 8, 2022 · 28 comments · Fixed by #1597
Closed

Multiple pypi.url #1310

andreas-vester opened this issue Aug 8, 2022 · 28 comments · Fixed by #1597
Labels
⭐ enhancement Improvements for existing features

Comments

@andreas-vester
Copy link

Is your feature request related to a problem? Please describe.

I need to define multiple pypi mirrors. As far as I understand it, using pdm.config pypi.url, I can set ONE mirror.

Describe the solution you'd like

There should be a way to define multiple pypi mirrors.

Alternatively, when adding a package, there should be a parameter available that specifies the mirror that will be used to look for the package.

@andreas-vester andreas-vester added the ⭐ enhancement Improvements for existing features label Aug 8, 2022
@frostming
Copy link
Collaborator

@andreas-vester
Copy link
Author

What I don't like about this solution is the fact I don't want to reveal any internal server names in my pyproject.toml file. If I do that, committing to VCS doesn't make sense anymore, in my view. If somebody from outside decides to fork the repo, he has some server in the pyproject.toml that doesn't make any sense to him. He won't be able to access our internal servers anyway. And maybe he needs to define his own internal servers.

To cut a long story short, I think setting multiple pypi mirrors in pdm's config makes much more sense. It's a private configuration, so the details shouldn't be put into a publicy available file.

@pawamoy
Copy link
Contributor

pawamoy commented Aug 8, 2022

I agree. I'm lucky enough to have a virtual repository that maps to several others, so I can just set one pypi url, but if that wasn't the case I'd be asking for the same thing. Setting the repositories in pyproject.toml is not an acceptable workaround in my case, and it's actually one of the reason I moved away from Poetry and tried PDM 😄

@frostming
Copy link
Collaborator

@andreas-vester As you said the mirror URLs are private settings, so all packages are supposed to be able to install if others don't have such private settings. Is that right?

@andreas-vester
Copy link
Author

Well yes, this is right.

However, my private settings (i.e. server names) are of no use to other users. Besides, I don't want to reveal any internal names in pyproject.toml for others to see. As @pawamoy pointed out, this is the same behavior as can be seen with poetry.

When using pip, those settings are stored in pip.ini, where I can specify index-url or extra-index-url. As far as I understand it, pdm is using pip under the hood, isn't it? Would it be possible to use pip.ini's settings as a workaround?

A better/other solution could be to have dedicated pdm settings, something like

  • pypi.url1
  • pypi.url2
  • pypi.url3
  • ...

What do you think?

@frostming
Copy link
Collaborator

frostming commented Aug 9, 2022

A better/other solution could be to have dedicated pdm settings, something like

  • pypi.url1
  • pypi.url2
  • pypi.url3
  • ...

What do you think?

I agree that there is scenario for multiple private index sources, but I am struggling with how to make the UX better. The above proposal doesn't look good to me.

As far as I understand it, pdm is using pip under the hood, isn't it? Would it be possible to use pip.ini's settings as a workaround?

No, PDM no longer uses pip under the hood.

@andreas-vester
Copy link
Author

andreas-vester commented Aug 9, 2022

OK, agreeing on putting the settings outside the pyproject.toml is a very good step. This would clearly differentiate pdm from poetry.

I'm not an expert, but another possibility (other than pdm config) could potentially be working with environment variables. You're already using environment variables in the dependency specification. So why not taking all information from these variables?

Let's say you have a .env file with some variables like

  • PRIVATE_PYPI_URL_SERVER1="https://some.private.pypi.mirror.com"
  • PRIVATE_PYPI_URL_SERVER2="https://another.private.pypi.mirror.com"

You could identify the relevant variables with the prefix PRIVATE_PYPI_URL.

Just a very first suggestion...

@lofidevops
Copy link

@andreas-vester One option might be to:

  • Define mirrors in pyproject.toml with paths like https://source.private
  • Modify your /etc/hosts to map the fake domain source.private to the appropriate IP address

...this shifts responsibility from pdm to your development environment, and you can safely store the fake domain in version control. It even allows you to define different sources in development and production (if you want). If you're working on a team, you might want to provide everyone with a script that does the job.

@pawamoy
Copy link
Contributor

pawamoy commented Aug 16, 2022

Since PDM was previously reading pip's config, why not supporting the same kind of settings: a main index and a list of extra ones? Or, since all indexes are checked anyway, support setting pypi.url both as a string (single index) or a list of strings (multiple indexes)?

Single index

pdm config pypi.url https://singleindex
[pypi]
url = "https://singleindex"

Multiple indexes

pdm config pypi.url https://index1 https://index2
[pypi]
url = [
    "https://index1",
    "https://index2",
]

@frostming
Copy link
Collaborator

frostming commented Aug 16, 2022

I've read all the proposals but none is satisfying.

@andreas-vester introduced .env files to configure pdm, which I strongly -1. Users are already confused by project metadata(pyproject.toml) and system/user/project settings. .env file is only a replacement for environment variables used as temporary modifiers, rather than configurations of any kind.

@lofidevops offers a workaround to solve the described problem at present.

@pawamoy Your proposal looks almost good but remember we also support pypi.verify_ssl(boolean) config item, and ideally, it should be set for each index instead of all. As a result, this solution becomes less useful.

Although I admit the usefulness of this feature, I can't come up with a good UI to read and write the settings. More ideas are welcome.


UPDATE: based on @pawamoy 's proposal, maybe we can deprecate the pypi.verify_ssl config and derive it from the URL scheme(verify_ssl = true for https and false otherwise), but that will bring a side-effect that users can't disable the SSL verification against an HTTPS index.

@pawamoy
Copy link
Contributor

pawamoy commented Aug 16, 2022

Ah, indeed, I didn't think about other related settings. Supporting multiple indexes would then require a more drastic change I guess, something like:

[pypi.private-server-1]
url = "https://index1"
verify_ssl = true

[pypi.private-server-2]
url = "https://index2"
verify_ssl = false

@kanbara
Copy link

kanbara commented Aug 24, 2022

i also have the case where this would be needed, eagerly awaiting a solution.

@clemux
Copy link

clemux commented Oct 14, 2022

Same here, for projects using pypi + a company private repository.

@frostming
Copy link
Collaborator

frostming commented Oct 14, 2022

Please be noted this feature only applies to those projects that can be installed successfully even if other people change the local PyPI URL(private config).

That is to say, if your project depends on some packages that are only available on a private index, making the source not interchangeable, just use the tool.pdm.source array in pyproject.toml(shared config).

@andreas-vester
Copy link
Author

One option might be to:

* Define mirrors in pyproject.toml with paths like `https://source.private`

* Modify your `/etc/hosts` to map the fake domain `source.private` to the appropriate IP address

...this shifts responsibility from pdm to your development environment, and you can safely store the fake domain in version control. It even allows you to define different sources in development and production (if you want). If you're working on a team, you might want to provide everyone with a script that does the job.

@lofidevops I don't want to put any internal (or fake) server names in pyproject.toml. If I commit this to GitHub and somebody clones the repo, those server names wouldn't be of any use for him.

Or did I miss your point??

@andreas-vester
Copy link
Author

Please be noted this feature only applies to those projects that can be installed successfully even if other people change the local PyPI URL(private config).

That is to say, if your project depends on some packages that are only available on a private index, making the source not interchangeable, just use the tool.pdm.source array in pyproject.toml(shared config).

I think it is clear that if you use some libraries that are exclusively available to you (via a private repo), that publishing this project and expect others to be able to successfully clone it is not possible.

However, my issue is that we use a pypi mirror in our company. That is, I am looking for a way to define all dependencies in pyproject.toml, but specifiying the source server should be done in a private (configuration) file. This way, I can install from my private mirror, while people from outside the company simply use the standard pypi source.

@andreas-vester
Copy link
Author

@pawamoy I found your blog entry somewhat-modern-python-development, where you mentioned the following:

Then I learned about PDM. It had all the good things Poetry had, and it removed the previously mentioned pain points, namely: it was able to read pip's configuration. It made using private indexes so easy: setup pip's index-url and trusted-host and you're done. Your own configuration does not contaminate other users through pyproject.toml (note: PDM 2.0 doesn't read pip's configuration anymore, but you can still configure your index outside of pyproject.toml).

The very last sentence drew my particular attention and I am wondering what you exactly mean? How you do configure your index outside pyproject.toml?

@pawamoy
Copy link
Contributor

pawamoy commented Oct 16, 2022

@andreas-vester PDM currently allows to set exactly one URL for the default PyPI-like index to use: pdm config pypi.url https://.... This config lands in ~/.config/pdm/config.toml. That's what I meant in the blog post :)

@frostming
Copy link
Collaborator

frostming commented Oct 17, 2022

I think it is clear that if you use some libraries that are exclusively available to you (via a private repo), that publishing this project and expect others to be able to successfully clone it is not possible.

@andreas-vester Chances are you may also share the project with someone inside your company, or deploy it to another machine, right? If you depend on a private library but don't specify the source in pyproject.toml, you push all others with the same local config, otherwise, the project can't be installed on their machines.

@frostming
Copy link
Collaborator

Currently, PDM supports two kinds of package sources:

  • Local source(single, defined in $PDM_CONFIG/config.toml)
  • Shared sources(multiple, defined in tool.pdm.source in pyproject.toml)

The local source can be overridden by the shared source with pypi key.

One should notice that multiple local sources will make the situation complicated and I haven't figured out a clean way to make them coexist.

  • What will be overridden by the pypi shared source after the change?
  • Is it possible to override the other local sources? If yes, how? For example, local and shared both contain three sources, what is the final list of source URLs to find packages from? Will there be any deduplication?
  • the pdm config CLI

@lofidevops
Copy link

I don't want to put any internal (or fake) server names in pyproject.toml. If I commit this to GitHub and somebody clones the repo, those server names wouldn't be of any use for him.

Or did I miss your point??

@andreas-vester You didn't miss my point, I was assuming only private team members had access to your repo. So my proposal doesn't fit your use-case. Sorry about that! But it looks like the subsequent comments are zeroing in a solution. Good luck!

@racc
Copy link
Contributor

racc commented Dec 7, 2022

Our organisation would really appreciate this feature, since we have a build server which needs to talk to multiple private repos which requires authentication (so we can't store user/pass in the pyproject.toml).
We were using the extra-index-url of pip when using PDM v1 previously...

What will be overridden by the pypi shared source after the change?
Is it possible to override the other local sources? If yes, how? For example, local and shared both contain three sources, what is the final list of source URLs to find packages from? Will there be any deduplication?

With regards to the above would it be possible to have some sort of hierarchy to derive the list and order of sources?
I.e. read from pyproject.toml first (most specific) then config.toml (least specific)
And then concatenate the list of pypi.indexes with their own urls ?

@frostming
Copy link
Collaborator

(so we can't store user/pass in the pyproject.toml).

If it is the only concern you can use environment variables

@lukaszmoroz
Copy link
Contributor

lukaszmoroz commented Dec 16, 2022

I have a similar need, but mainly with authentication and not with server names. The problem with environment variables I have is that while it works great in CI, when installing locally it requires meddling with your shell configuration and it's not very portable (the variable names may conflict for different users and repos).

Ideally I'd like to have user-wide config #1310 (comment) where I can store the auth part or have the source urls in pyproject but make auth work with a keyring (like artifacts-keyring) and also with env variables or in the worst case enable dotenv support for installs.

What will be overridden by the pypi shared source after the change?

I'd guess that if someone wants to disable the default index then it's because the shared source config is self contained, and the local config will not be taken into account, however the way it's configured may not make much sense now. Maybe add an item under tool.pdm.resolution that sets this behavior?

Is it possible to override the other local sources? If yes, how? For example, local and shared both contain three sources, what is the final list of source URLs to find packages from? Will there be any deduplication?

Do similar thing to what pip is doing when reading from various config files would be my guess (i.e. just concat?), but prefer shared sources first when respect-source-order.

the pdm config CLI

The config may support both pypi.url and the new pypi.<name>.url if you want to keep backward compatibility. Then there will be a distinction similar to index-url / extra-index-url between overriding the default index via pypi.url and supplying additional ones via pypi.<name>.url.

@pawamoy
Copy link
Contributor

pawamoy commented Jan 5, 2023

Wow, great work @frostming!

@andreas-vester
Copy link
Author

@frostming This is just awesome! THANK YOU!

It now allows me to use our internal pypi indexes and corresponding credentials while not being forced to put them into pyproject.toml. This will allow me to put it into GitHub and share it with the public.

This feature enables me to make the move to pdm!!

In my view, this is a unique selling point and one of the major differences compared to poetry. You should go and advertise this feature.

pdm deserves more appreciation within the wider Python community!

@rickerp
Copy link

rickerp commented Mar 30, 2023

Hey, read the whole conversation, love the feature. But still didn't understood how to store the username/password/credentials outside of the pyproject.toml.

PS: I Love pdm

@rickerp
Copy link

rickerp commented Mar 30, 2023

Hey, read the whole conversation, love the feature. But still didn't understood how to store the username/password/credentials outside of the pyproject.toml.

PS: I Love pdm

Found the solution I wanted looking in the docs.

This is my solution to the config for future viewers of this issue.
PROJECT/pyproject.toml

[tool.pdm.resolution]
respect-source-order = true

[[tool.pdm.source]]
name = "NAME_1"
url = "URL_1"

[[tool.pdm.source]]
name = "NAME_2"
url = "URL_2"

$PDM_CONFIG/config.toml

[pypi.NAME_1]
username = "USERNAME_1"
password = "PASSWORD_1"

[pypi.NAME_2]
username = "USERNAME_2"
password = "PASSWORD_2"

$PDM_CONFIG for macOS is in ~/Library/Preferences/pdm/config.toml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⭐ enhancement Improvements for existing features
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants