Good practice for naming projects

Hi,

Using different names for “package name” and “import package” confused me in the past.

I have also heard that it may lead to security issues. For example:

In October 2022 sklearn downloads were about 1/5 of the scikit-learn downloads on PyPI so a lot of people are using it

Because of this, I felt it was best to use the same name for both the “distribution package name” and the “import package name” in the projects I work on. Since hyphens cannot be used in import package names, I started recommending underscores—though I’m not particularly fond of them.

However, some tools automatically modify the project name when I use an underscore:

$ uv init --package a_b
[project]
name = "a-b"
version = "0.1.0"
description = "Add your description here"
$ hatch new a_b
a-b
├── src
│   └── a_b
│       ├── __about__.py
│       └── __init__.py
[project]
name = "a-b"
dynamic = ["version"]
description = ''

Initially, I filed a bug to uv, but now I’m unsure about the best approach..

Is it problematic to use an underscore for both names?

I am not fan of having to run $ pip install a-b then import a_b.

Has the packaging community reached a consensus on this?

Regards

1 Like

The (distribution) package names a-b and a_b are defined by the various packaging standards to be equivalent. So as a project name, it shouldn’t matter which you use. Import names, as you note, have to use underscores, though.

There’s no consensus that everyone has to use a particular form, although there is a defined “canonical” form for tools to use when normalising project names (the one with the underscore). If anything, the consensus is that the (display) name of a project should be up to the user.

No, but nor is it problematic to not do so. It’s a matter of preference. Both uv init and hatch new are simply convenience tools for creating a project - you can change what they do (rename the directory, change the name in pyproject.toml). Putting the project name in the defined canonical form as part of a “set up a project” helper seems like a reasonable thing to do, but you’re not forced to keep the default values if you don’t like them.

You can use pip install a_b even if the project’s display name is a-b. That’s what the equivalence rules ensure.

4 Likes

I don’t know about consensus, but generally I’d assume pip install a-b would lead to import a.b while pip install a_b would lead to import a_b (and yes, I know the names normalise to the same thing, but there are options for how the developer documents it, and I prefer to infer intent from documentation than specifications).

So you see examples like pip install azure-common which provides import azure.common, and also pip install simple_ado which provides import simple_ado. You could swap the hyphen and underscore in the “install” command if you want, but luckily the unnormalised name gives me useful information about how to use them.

I am not fan of having to run $ pip install a-b then import a_b.

Even if you upload the package with the name a-b (or a.b or A-_-B or …) you can still pip install a_b because of the way package name normalization works. See PEP 503 – Simple Repository API | peps.python.org for details.

3 Likes

Personally, since I use namespace packages, I will name my package using foo.bar style, which will also lead to import foo.bar. The distribution package name does get normalized to foo_bar but name displayed on the project’s PyPI page will be preserved to the one you give it, i.e. the project.name value in pyproject.toml. YMMV but that’s the way I like to do it.

5 Likes

I knew there was name normalization on PyPI but I didn’t realize / understand that I coud pip install a_b if my project name was a-b. Thanks to you and @fungi for this clarification.

I did not really dig into namespace package, but I begin to understand how and when they could be used. Thanks