diff --git a/source/discussions/choosing-a-package-module-name.rst b/source/discussions/choosing-a-package-module-name.rst new file mode 100644 index 000000000..6d5eee522 --- /dev/null +++ b/source/discussions/choosing-a-package-module-name.rst @@ -0,0 +1,77 @@ +Choosing a Package/Module Name +============================== + +This discussion is a complement to :doc:`/tutorials/packaging-projects`. + +Make sure to choose a valid :ref:`Python identifier ` for the names of all your :term:`import packages ` and :term:`modules `. +The name of the :term:`project` and the name of the :term:`distribution package` are one and the same. +It is the name you will see on PyPI, for example. +The name of the project and the name of the top-level :term:`import package` (or :term:`module`) can be different. + +Moreover, one PyPI project/dist may ship more than one module or importable package — it is only possible that one matches the name, others can't. +It is recommended to have only one importable package, with a name as similar as possible as the ``dist-info`` file in the installation folder. + +Project names (usually found in :file:`pyproject.toml`) and import package names follow different rules and conventions. +The normalized form (and thus the preferred form) for project names +is the so-called "kebab case" (see :doc:`/specifications/name-normalization`), for example ``abcd-1234``. +But import packages and modules should have a valid Python identifier as a name. + +With an import package name ``abcd-1234``, the following would not work: + +.. code-block:: pycon + + >>> import abcd-1234 + >>> from abcd-1234 import something + +Since ``abcd-1234`` is not a valid Python identifier. +(Importing such a module would be cumbersome, completely unnatural and against the long-established conventions in the Python community; +see, for example, :pep:`8#package-and-module-names`. +There is a way to import it anyway, see :doc:`importlib ` and this question_.) + +:ref:`Python identifiers ` follow the so-called "snake case". +The preferred form for an import package name is ``abcd_1234`` which is a valid Python identifier. +Note the underscore ``_`` as separation character instead of of the dash ``-`` seen in the project name. + +Having a directory structure with ``src/abcd_1234/`` instead of ``src/abcd-1234/`` has 2 consequences: + +- The following works: + + .. code-block:: pycon + + >>> import abcd_1234 + >>> from abcd_1234 import something + +- All four build backends covered in the tutorial :doc:`/tutorials/packaging-projects` will work: + + - Flit will not crash with an error; + - Hatch will recognize that the module corresponding to the package is ``abcd_1234`` instead of defaulting to ``src`` and building a not working wheel. + +More information about :doc:`Python imports ` and its :doc:`grammar `. + +Another aspect of choosing a package/module name is the naming consistency +of the files generated by the build backend. +With the four build backends covered in the tutorial +(:ref:`Flit `, :ref:`Hatchling `, :ref:`PDM `, and :ref:`setuptools`), +when the correct import package is found, its name is always leaved as is. + +But, starting from the project name, the build backend also generates: + +- an archive file like ``abcd_1234-1.0.0.tar.gz`` with Flit, Hatchling, and PDM, + or ``abcd-1234-1.0.0.tar.gz`` with setup-tools; + here we can see that some backends but not all + apply a normalization on the project name: lowercase, underscore ("snake case") instead of hyphen; + note that we do not talk about the same normalization referenced at the beginning of this document + (:doc:`/specifications/name-normalization`); +- a wheel like ``abcd_1234-1.0.0-py3-none-any.whl`` with all build backends; +- a dist-info like ``abcd_1234-1.0.0.dist-info`` with all build backends; +- in the case of setuptools, an egg-info like ``abcd_1234-1.0.0.egg-info``. + +Thus, except for the archive with setuptools, all prefixes of filenames +use a normalization of project name. +This normalization will match an import package name chosen consistently +by normalization of the project name. +You may see an interest in installing a wheel named ``abcd_1234-1.0.0-py3-none-any.whl``, +and having as a result ``abcd_1234-1.0.0.dist-info`` and ``abcd_1234/`` (or ``abcd_1234.py``) in your installation directory :) +(instead of a project ``aBcD-1234``, a wheel ``abcd_1234-1.0.0-py3-none-any.whl``, installing ``abcd_1234-1.0.0.dist-info`` and ``4-3-2-1cDbA/`` ;) ). + +.. _question: https://stackoverflow.com/questions/8350853/how-to-import-module-when-module-name-has-a-dash-or-hyphen-in-it diff --git a/source/discussions/index.rst b/source/discussions/index.rst index e5411ece3..a52f53de3 100644 --- a/source/discussions/index.rst +++ b/source/discussions/index.rst @@ -14,3 +14,4 @@ specific topic. If you're just trying to get stuff done, see wheel-vs-egg src-layout-vs-flat-layout setup-py-deprecated + choosing-a-package-module-name diff --git a/source/tutorials/packaging-projects.rst b/source/tutorials/packaging-projects.rst index 761b2748f..d60fc7c38 100644 --- a/source/tutorials/packaging-projects.rst +++ b/source/tutorials/packaging-projects.rst @@ -539,6 +539,7 @@ some things you can do: * Consider packaging tools that provide a single command-line interface for project management and packaging, such as :ref:`hatch`, :ref:`flit`, :ref:`pdm`, and :ref:`poetry`. +* Read about :doc:`/discussions/choosing-a-package-module-name`. ----