Package Name Prefixes

PEP 752 was accepted at the end of June. It lets an organisation apply to PyPI to reserve a package name prefix, after which new uploads matching that prefix are rejected unless they come from the grant holder. Existing packages are grandfathered. The motivation is impersonation: someone publishing malware as google-cloud-something or apache-something gets the vendor’s name in front of it. The PEP names four example prefixes: google-cloud-, opentelemetry-, apache-airflow-providers-, and types-.

I pulled every package name from PyPI (912,665 of them, via ecosyste.ms ) and counted prefixes. Those four account for 264, 271, 154, and 1,295 packages respectively, and each is published almost entirely by one organisation. Google publishes google-cloud-, the OpenTelemetry project publishes opentelemetry-, the Apache Airflow team publishes apache-airflow-providers-, and the typeshed maintainers publish types-. Prefix reservation formalises what those publishers already do; one of the PEP’s authors, Jarek Potiuk, is on the Airflow PMC.

There are 13,934 prefixes on PyPI shared by five or more packages. The biggest is django- with 17,450, published by thousands of different authors at a rate of about a thousand a year since 2010. flask- has 2,815, pytest- has 2,137, mkdocs- has 764, sphinxcontrib- has 370. collective., the Plone community’s shared prefix using Python’s namespace package machinery, has 1,436 packages and has been in use since at least 2008. Anyone publishes a Django app as django-something, a pytest plugin as pytest-something, a Sphinx extension as sphinxcontrib-something, and the host project’s documentation tells them to.

An earlier draft of PEP 752 had a mode for this, letting a grant holder mark a prefix as open so that anyone could still publish under it while the grant holder’s own packages got a visual indicator. It was removed as having “insufficient motivation and the fact that repositories could technically satisfy such use cases with standard grant semantics.” Multiple organisations can share a grant, which handles a prefix split across a few organisations. Under the accepted design, if the Django Software Foundation reserved django-, the existing 17,450 packages would keep publishing new versions and the roughly one thousand new django-* packages that would otherwise arrive next year would be blocked. I compared namespace designs across registries earlier this year; NuGet, crates.io and npm have each attached something to prefixes already.

Seven registries

Splitting package names on runs of -, _ and. and grouping by the first token:

packages

prefixes with ≥5 packages

packages with a shared prefix

≥100

≥500

pypi.org

912,665

13,934

41%

427

66

crates.io

311,301

9,161

54%

166

19

rubygems.org

209,853

4,214

46%

132

18

hex.pm

22,373

382

32%

12

1

hackage.haskell.org

19,345

521

41%

9

0

npmjs.org (unscoped)

2,678,188

37,460

68%

2,604

468

nuget.org

828,361

22,377

70%

690

72

npm has had @scope/name since 2014 so its row counts only the 63% of packages that are unscoped. Between a third and two-thirds of every registry’s packages share a first token with at least four others. The counts here are first-token only; multi-token prefixes like apache-airflow-providers- are counted under apache-* in the table and separately when named in the text.

Open plugin conventions, where many authors publish extensions named after a host project, are the largest category among the biggest prefixes on every registry: django- (17,450 pypi), react- (86,142 npm), jekyll- (1,658 rubygems), bevy- (1,870 crates), phoenix_ (245 hex), servant- (152 hackage). Some of these are functional, meaning the name has to match the pattern for the host tool to load the plugin: cargo runs cargo-foo as the cargo foo subcommand, ESLint’s config resolver matches eslint-plugin-foo, pytest loads pytest-foo via entry points.

Generated SDK families, where one organisation publishes hundreds of packages from an API spec, are the next largest. aws-sdk- (473 crates, 481 gems, 92 pypi), google-cloud- (258 crates, 510 gems, 264 pypi, 5 hackage), azure-mgmt- (273 crates, 124 gems, 334 pypi). The Odoo Community Association publishes about 20,000 packages on PyPI across seven versioned prefix trees ( odoo8-addon- through odoo14-addon- plus odoo-addon- ), which is the largest coordinated prefix use I found on any registry. On npm the generated families are the ones that adopt scopes: @types/ 11,388, @stdlib/ 5,555, @fontsource/* 2,122.

Language and framework tags account for another large block: python- (6,278), rust- (1,692), ex_ (1,123 hex, the Elixir convention), node- (23,389 npm), is- (3,754 npm, predicate functions), use- (4,317 npm, React hooks). Hackage has a set of these that mirror module paths ( data-, network-, language-* ).

Getting-started guides produce their own prefixes: hola- on rubygems has 764 packages from people following the make your own gem guide, example-package- and example-pkg- on PyPI have 1,031 between them from the packaging.python.org tutorial, and guessing-game- on crates.io has 125 from chapter 2 of the Rust book. topsis-* on PyPI has 1,999 packages with student roll numbers embedded in the names ( TOPSIS is a decision-analysis method that gets set as a programming assignment), from what appears to be a university course run each year since 2020.

Bulk publication accounts for some of the largest recent prefixes: use- on crates.io has 820 packages, 809 of them published in 2026 by one account. iflow-mcp- on PyPI has 2,505 packages of republished MCP servers from one publisher, plus 5,184 more under @iflow-mcp on npm from the same account. free.robux- on NuGet has 3,620. mcp- on PyPI, from many publishers, went from 2 packages in 2023 to 3,022 by mid-2026, reaching that count faster than django-* reached 3,000.

For the top 1,000 packages by downloads, between 69% and 80% on crates.io, rubygems, and pypi either belong to a prefix cluster or are the package a large cluster is named after ( serde, pytest, rack ); hex is 45% and hackage 60%. Of the packages flagged critical on ecosyste.ms, 226 of rubygems’ 974 are aws-sdk- alone, and 74% of NuGet’s are under Microsoft. or System.*.

NuGet

PEP 752 cites NuGet’s ID prefix reservation as prior art. It went live in October 2017, works the same way (apply by email, matching uploads are rejected unless from the reservation holder, existing packages grandfathered), and adds a checkmark to verified packages in the gallery and Visual Studio. NuGet also supports subprefix delegation, where the Microsoft. holder can delegate Microsoft.AspNet. to a different account, and public reservations, where the holder gets the checkmark on their own packages while other publishers can still upload under the prefix, which is what PEP 752’s earlier draft called an open namespace.

Sampling NuGet’s per-package verified flag via the search API across the top 30 non-spam prefixes shows Microsoft., DevExpress., Syncfusion. and JetBrains. fully verified and single-publisher, and Google., Amazon. and Azure.* mostly verified with some grandfathered community packages.

Serilog. is 6% verified across 776 packages, and Serilog is a logging library with a plugin model where sinks and enrichers are published as Serilog.Sinks.Foo and Serilog.Enrichers.Foo by whoever writes them. Sampling 60 of those and checking first-publish dates against verified, the core team’s packages ( Serilog, Serilog.Sinks.File ) are verified and community packages aren’t, including community packages first published in every year through 2025 ( Serilog.Enrichers.Metrics.Memory, September 2025, owner Jandini). That is consistent with Serilog. being a public reservation: community sinks are still being accepted eight years in. Cake.* (607 packages, Cake build addins) shows the same pattern at 7% verified. The checkmark appears per package; the reservation list itself is…

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论