Package: demo-package-sample-data-with-code
IntroductionGoals of this projectThis project doesn’t do anything useful. It exists only as a vehicle to demonstrate:how to prepare a Python project that can be uploaded to thePython Package Index (PyPI)as a release, from which it can then be installed on user systems using
thepippackage installer—usingstaticmetadata (setup.cfg) rather than dynamic
metadata (setup.py)how static text files (for example, templates, sample data, etc.) can be packaged and then—usingimportlib.resources—referenced and read from their host package or any other package, even if these files don’t
actually reside on the file system (e.g., if they reside in a .zip archive). This is relevant because:“[T]he PyPA recommends that any data files you wish to be accessible at run time be includedinside the package.”PEP 302added hooks to import from .zip files and Python Eggs.use of asrc/directory intermediate between the project directory and the outermost package directory—with multiple
benefitshow to provide a single source for the version number, in this case by supplying a__version__.pyfile in the import
module that is (a) imported by__init__.pyand (b) referenced bysetup.cfghow to specify third-party dependencies that are available onPyPIso thatpipwill install
them as well, if necessary, whenpipinstalls  this projecthow to tell thebuildmechanism to identify a specific version of Python, e.g.,py39, in the “Python Tag” in
the file name of the resulting “wheel” (.whl) distribution file, so that users will be better informed of the
Python-version requirement before attempting to install the packagehow to use a__main__.pyfile as an entry point, which will execute when the import package is invoked on
the command line with the-mflag (e.g.,python -m my_package)how to define a console-script entry point, e.g.,my-command, so that the user can simply and directly invokemy-commandon the command line, rather thanpython -m my_packagehow to provide for, and process, an optional argument on the command linehow to reference, from outside the package, modules, methods, function, etc. that are within the package, in order to,
for example, to test themhow to install the project in “editable”/“development” mode during development so that you can test the
functionalities that access resources in packages—without having to rebuild and reinstall the package after every change.In addition to reading this README, it will probably be useful to complement this README with inspection of the files
themselves. You can examine the configurations and code directly for a given file by accessing it at  thefile listfor theGitHub repository. You could also download or clone the repository and use it as a template for a new project.All citations/quotations to documentation and other sources were valid as of May 1, 2022.DependenciesNOTE: This package requires Python 3.9+.This package has been tested only on Python 3.9.12, with pip 22.1.1,
on a Mac (macOS 12.3.1).This package also requiresyachalk. If you install this project (demo-package-sample-data-with-code) as explained later,pipshould automatically installyachalkif it is not
already installed on your system.yachalkallows console output to be rendered in specified colors. Here, I use it to
make error messages red. I incorporatedyachalkinto the code largely to provide an excuse to illustrate how to
disclose in the metadata the existence of a third-party dependency that also may need to be installed bypip.TerminologyFor my use of “project” and “package” (including “import package” and “distribution package”) see Jim Ratliff,
“Unpacking ‘package’ terminology in Python,”
GitHub Gist.In particular, I very deliberately choose:to use hyphens to separate the words in theprojectname:demo-package-sample-data-with-codeI also use hyphens in the console-scripts entry-point command:my-commandthat the user can type on the command
line in lieu of therunpysyntax:python -m demo_package_sample_data_with_codeto use underscores to separate the words in the import package name:demo_package_sample_data_with_codeas well as
every other directory below that level.This isn’t a gratuitous attempt to confuse the reader but rather in the spirit of
“Explicit is better than implicit.” In practice, the project name and import
package name are often chosen to be the same. It then is ambiguous in many discussion whether that common name is
invoked in any particular case (a) because it is the project name or (b) because it is the import package name. My
distinguishing the two is meant to help clarify what drives various behaviors. (See also §Considerations regarding the inter-word delimiter character in a multiword project name.)Resources for topics not well covered hereI will not go into a detailed explanation of many aspects of packaging more generally that are well covered
elsewhere, e.g., theLICENSE,README,pyproject.toml, andsetup.cfgfiles (or why I’m
usingsetup.cfgrather thansetup.py). For these and other such topics, see for example:“Python Packaging User Guide,” by thePython Packaging Authority, and in particularthe tutorial
“Packaging Python Projects”§Configuring metadata.
(“Static metadata (setup.cfg) should be preferred. Dynamic metadata (setup.py) should be used only as an escape
hatch when absolutely necessary.”)§ “Including files in source distributions withMANIFEST.in”§ “Configuring setuptools usingsetup.cfgfiles”
of theSetuptools User Guide.Mark Smith’s presentation at EuroPython 2019: “Publishing (Perfect) Python Packages on PyPi”
(YouTube,GitHub)Vytautas Bielinskas,
“Build a Python Module and Share it with Pip Install,” YouTube » Data
Science Garage, April 11, 2021.I’m also not going to address here any issues about testing, as important as they are.Problem considered: Reliably accessing resources included in the packageInstead, I focus here on the particular problem, asexplained by Barry Warsaw at PyCon US 2018:Resources are files that live within Python packages. Think test data files, certificates, templates, translation
catalogs, and other static files you want to access from Python code. Sometimes you put these static files in a package
directory within your source tree, and then locate them by importing the package and using its__file__attribute. But
this doesn’t work for zip files!The previous standard method,pkg_resources, is not a great solution:You could usepkg_resources, an API that comes withsetuptoolsand hides the differences between files on the file
system and files in a zip file. This is great because you don't have to use__file__, but it’s not so great becausepkg_resourcesis a big library and can have potentially severe performance problems, even at import time. …The biggest problem withpkg_resourcesis that it has import-time side effects. Even if you’re never going to access
your sample data, you’re paying the cost of it because as soon as you importpkg_resourcesyou pay this penalty. …pkg_resourcescrawls over every entry in yoursys.pathand it builds up these working sets and does all this runtime
work. … If you have a lot of things on yoursys.path, this can be very, very slow. …The better solution is:Welcome toimportlib.resources, a new module and API in Python 3.7 that is also available as a standalone library
for older versions of Python.importlib.resourcesis built on top of Python’s existing import system, so it is very
efficient.See also §§Accessing Data Files at Runtimeof §Data Files SupportofBuilding and Distributing Packages with Setuptoolsby PyPA:Typically, existing programs manipulate a package’s__file__attribute in order to find the location of data files.
However, this manipulation isn’t compatible with PEP 302-based import hooks, including importing from zip files and
Python Eggs. It is strongly recommended that, if you are using data files, you should useimportlib.resourcesto
access them.The following sources explicitly discuss usingimportlib.resourcesto access resources integrated within a package:Barry Warsaw, “Get your resources faster, withimportlib.resources,”  PyCon US 2018.
(YouTube,slides)James Briggs, “How to Build Python Packages for Pip,” YouTube » James Briggs, April 2, 2021.
(GitHub)Damien Martin, “Making a Python Package,”
GitHub » kiwidamien. See especially
“Making a Python Package VI: including data files.”If the advent ofimportlib.resourcesin Python 3.7 weren’t enough, an additional function, viz.,files(), was added
in Python 3.9. I rely upon thefiles()function in this package, and for that reason this package requires
Python 3.9.Road map for the remainder of this README documentimportlib.resources: Selected basicsFile and directory structure; rationale forsrc/directoryNoncomprehensive comments on selected elements of the project metadata and structureHere I address those aspects of the project’s metadata that are particularly relevant to the particular goals of
this package.Finish development and upload to PyPIHere I walk through—stage by stage, and command by command—the process of:creating a virtual environment,finishing your development in a local “editable” or “development” install,building your project and turning it into a distribution package that can be uploaded to PyPI,uploading it to TestPyPI,testing your distribution by created a new virtual environment and installing your project from TestPyPI.importlib.resources: Selected basicsBackgroundimportlib, a Python built-in library, appeared in Python 3.1 and
provides the Python 3 implementation of theimportstatement.Withinimportlib, the moduleimportlib.resourceswas added in
Python 3.7:This module leverages Python’s import system to provide access to resources within packages. If you can import a
package, you can access resources within that package. Resources can be opened or read, in either binary or text mode.Resources are roughly akin to files inside directories, though it’s important to keep in mind that this is just a
metaphor. Resources and packages do not have to exist as physical files and directories on the file system.(Note that thedocumentation forimportlib.resourcesactually refers readers to thedocumentation forimportlib_resources, which is the
standalone backport ofimportlib.resourcesfor earlier versions of Python, for more information on usingimportlib.resources.)Thefiles()functionThis demo package calls thefunctionimportlib.resources.files(package),
wherepackagecan be either a name or a module object that conforms to thePackagerequirements. The function
returns an instance of abstract base classimportlib.abc.Traversable. This object has available a subset ofpathlib.Pathmethods suitable for traversing directories and opening files:joinpath(child)__truediv__(child)name()is_dir()is_file()iterdir()open(mode='r',*args,**kwargs)read_bytes()read_text(encoding=None)You can specify the location of a resource from (a) the name of the package immediately enclosing the resource and
(b) the name of the resource  by either:my_resource_location_as_string = importlib.resources.files('mypackage').joinpath('resource_name')ormy_resource_location_as_string = importlib.resources.files('mypackage') / 'resource_name'where the latter corresponds to__truediv__(child)method.To read the text into a variable:text_in_file = my_resource_location_as_string.read_text()This projectFile and directory structure; rationale forsrc/directoryThis project has the following initial directory/file structure:├── LICENSE
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── setup.cfg
├── docs
├── src
│   ├── demo_package_and_read_data_files
│   │   ├── sample_data
│   │   │   ├── __init__.py
│   │   │   └── sample_data_e.txt
│   │   └── sample_data_pi.txt
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── __version__.py
│   │   ├── constants.py
│   │   ├── my_module.py
└── tests
    └── test_my_module.pyBy “initial directory/file structure,” I acknowledge that additional directories will be generated as a result of
(a) creating a virtual environment, which adds avenv/directory, (b) installing the project in an
“editable”/“development” mode, which adds asrc/demo_package_sample_data_with_code.egg-infodirectory, and
(c) thebuildprocess, which adds adist/directory.Key user-facing namesNameMeaningdemo-package-sample-data-with-codeTheprojectname, as it appears on PyPI and in apip installpython-demo-package-sample-data-with-codeName ofGitHub repository. Independent of any other namedemo_package_sample_data_with_codeName of the import packagemy_module.pyName of the primary module of Python codeThesrc/directory structureNote the presence of thesrc/directory at the root level of the project directory and which contains the import
packagedemo_package_and_read_data_files. This structure—the presence of thissrc/directory—is certainly not yet a
standard but is gaining mindshare. I won’t attempt to justify it myself here, but instead I’ll point you to the
following resources:§ “A simple project” in
PyPA’s tutorial “Packaging Python Projects”FromBuilding and Distributing Packages with Setuptools,
PyPA§§ “Using asrc/layoutin
§ “Configuring setuptools usingsetup.cfgfiles§§ “src-layout” of
§ “Automatic discovery”This layout is very handy when you wish to use automatic discovery, since you don’t have to worry about other Python
files or folders in your project root being distributed by mistake. In some circumstances it can be also less
error-prone for testing or when using PEP 420-style packages. On the other hand you cannot rely on the implicit
PYTHONPATH=. to fire up the Python REPL and play with your package (you will need an editable install to be able to do
that).§ “The structure” in Ionel Cristian Mărieș,
“Packaging a python library,” ionel’s codelog, September 30,* E.g., the `src/` structure (a) ensures that you test your code from the same working directory that your users
will see when they install your package, (b) allows simpler packaging code and a simpler `MANIFEST.in`, and
(c) results in a much cleaner editable install.Mark Smith’s presentation at EuroPython 2019: “Publishing (Perfect) Python Packages on PyPi”
(YouTubeat26:00,GitHub):“Here’s why we use thesrc/directory. Our root directory is the directory we’ve been working in. If our code
was inthis directory—if we importhelloworldwhile running the tests—it would run the code in our current
directory. But we don’t want it to do that. We want it to test installing the package and using the code from there.
By having thesrc/directory, you’re forcing it to use the version you’ve just installed into the versioned
environment.”Noncomprehensive comments on selected elements of the project metadata and structureThe directory that immediately encloses each resource must be a package and thus must have an__init__.pyfileimportlib.resourcesconsiders a file a resource only if the file is in the root directory of a package. A directory
cannot be a package unless it includes a__init__.pyfile. (It’s fine if this__init__.pyfile is empty. It’s its
filename that counts.)Here the relevant resources are two text files:"sample_data_pi.txt"located at the root of the import package, i.e.,src/demo_package_and_read_data_files/sample_data_pi.txt"sample_data_e.txt"located within a subfolder, "sample_data", of the import package, i.e.,src/demo_package_and_read_data_files/sample_data/sample_data_e.txt(Soley to demonstrate throwing aFileNotFoundErrorexception,my_module.pyalso attempts to openmeaning_of_life.txt, but, unsurprisingly, it does not exist.)In our case the requirement that each data file be in the root directory of a package  means that the following
directories each must have an__init__.pyfile:src/demo_package_and_read_data_filesThis directory would have been a package (and thus would have had an__init__.pyfile) even if we had no data
files to worry about, so no additional__init__.pyfile is created for this directory on account of the data
files.src/demo_package_and_read_data_files/sample_dataThis directory isnota directory of Python files, so it would not normally have an__init__.pyfile. Thus,
in order to read the data file from inside, we need to artificially add an__init__.pyfile inside.Tellingsetuptoolsabout data files that need to be included in the packagesetuptoolswill not by default incorporate arbitrary non-Python text files into the package when it builds it. Thus,
you must tellsetuptoolswhich such files you want it to include.In the methodology I use here, this requires:telling the configuration filesetup.cfgthat youdohave such files to be included, but not there saying which
onescreating aMANIFEST.infile that specifies the data files (including their paths) to be included.(If you’re using a different methodology, like usingsetup.pyrather thansetup.cfg, see the corresponding
discussion at §Data File Supportof theSetuptools User Guide.)setup.cfg:include_package_dataIn the configuration filesetup.cfg, in its[options]section, specify:include_package_data = TrueCreateMANIFEST.inand itemize the data filesSee, generally:
§ “Including files in source distributions withMANIFEST.in”
of “Python Packaging User Guide.”In the present case, theMANIFEST.infile contains the following and only the following:include src/demo_package_sample_data_with_code/sample_data_pi.txt
graft src/demo_package_sample_data_with_code/sample_datawheregraftensures, without itemizing them, that all data files in/sample_dataare included. (See
§MANIFEST.incommandsin the
Python Packaging User Guide.)Thanks to the structure adopted here, where thesrc/directory separates all the project metadata from the project
code/data, theMANIFEST.inperhaps could be made even simpler:graft srcwhich would ensure that all files withinsrc/are included in the distribution.
(SeeIonel Cristian Mărieș: “Withoutsrcwritting aMANIFEST.inis tricky. … It’s much easier with asrcdirectory: just addgraft srcinMANIFEST.in.)Establishing a single source for the version numberThis project defines its version number consistent with a frequently expressed desideratum referred to as
“single-sourcing the package version”.This requires coordination between three files: (a)setup.cfg, (b)__init__.py, and (c)__version__.py, as
described below.For further discussion on this topic, see the answers to “Set__version__of module from a file when configuring
setuptools usingsetup.cfgwithoutsetup.py,” Stack Overflow, May 23, 2022.Specify the version number in the__version__.pyfileThe version number is defined in the__version__.pyfile within the root of import package, i.e., at path:/path/to/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/__version__.pywith the syntax:__version__ = '1.0.0'This must be coupled with both animportin the__init__.pyfile and the appropriate directive in thesetup.cfgfile.The__init__.pyfile must import the__version__attributeThe__init__.pyfile, also within the root of the import package, i.e., at path:/path/to/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/__init__.pymust include the followingimportcommand:from . __version__ import __version__This import statement is discussed in detail athttps://stackoverflow.com/a/72357032/8401379Thesetup.cfgfile’sversiondeclaration  must be properly defined to use theattr:special directiveThesetup.cfgfile, in the root of the project directory, i.e., at the path/path/to/demo-package-sample-data-with-code/setup.cfgmust include the following snippet:[metadata]
  …
version = attr: demo_package_sample_data_with_code.__version__Disclose any third-party dependencies that are available on PyPI so that they will also be installed if neededIn the[options]section ofsetup.cfg, I disclose the dependency onyachalkwith the following snippet:install_requires = 
    yachalkSee “Configuring setuptools using setup.cfg files”
of the SetupTools User Guide for more detail.setup.cfg: Add apython-tagtag to force file name of resulting “wheel” distribution file to reflect partticular minimum version of PythonThis discussion will make more sense after you get to the later section § “The wheel file,” but this
discussion nevertheless logically belongs here.This package requires Python 3.9. Mysetup.cfgfile originally contained the following excerpt:classifiers =
    Programming Language :: Python :: 3
    License :: OSI Approved :: MIT License
    Operating System :: OS Independent

[options]
package_dir =
    = src
packages = find:
python_requires = >=3.9
include_package_data = TrueHowever, notwithstanding thepython_requires = >=3.9, the resulting wheel file’s filename contained a “Python Tag”
that was simplypy3rather thanpy39(which would have indicated a minimum Python version of 3.9).So I next changed thePythontag in theClassifierssection to explicitly state version 3.9. However, that did not
affect the Python Tag in the file name of the resulting wheel file.I finally—inspired bythis answer on Stack Overflow—solved the problem
by adding the following section tosetup.cfg:[bdist_wheel]
python-tag = py39After this change, the file name of the resulting wheel file including the Python Tag stringpy39as I desired.Considerations regarding the inter-word delimiter character in a multiword project nameThenamefield insetup.cfg(orsetup.py) defines the name of yourprojectas it will appear on PyPI. (Examples
will often misleadingly suggestthat this is
thepackagename, in some incompletely specified notion of “package,” but the only effect of this field is to
determine theprojectname on PyPI.)When, for better readability, you want your project name to have multiple words, you need to have a nonspace delimiter
between these words. However, no matter what delimiter you prefer, e.g., underscore (_), hyphen (-), or dot (.),
PyPI will ignore your expressed intention and “normalize” the name
so that each such delimiter (or even runs of delimiters) is replaced with a single hyphen. So, to avoid confusion, I
suggest that, when the project name has two or more words joined by delimiters, you specify hyphens for the delimiter
from the get go, since that’s the form it will ultimately take.Of course, you might want theimport packageof your project to have the same name as your project; that’s common.
But if your project has multiple words separted by delimiters, this won’t be exactly possible. The best you could do is
name your import package the same as the project, but substituting an underscore for each delimiter. E.g.,Project name:my-cool-thingImport package name:my_cool_thing__main__.pyis executed when package invoked from command line with-mflag; allows for a CLIAlthough not appropriate in all cases, including a__main__.pyfile establishes an entry point for the case where
the package name is invoked directly (usingrunpy) from the command line
with the-mflag, e.g.,:python -m demo_package_and_read_data_files(Note that the import package name,demo_package_and_read_data_filesmust use underscores as the inter-word delimiter,
even though the project name uses hyphens for the delimiter.)(In general, the-mflagtells Python to searchsys.pathfor the named module and execute its contents as the__main__module. Since the argument is a module name, you must not give a file extension (.py). What’s
crucial for us here is that package names (including namespace packages) are also permitted. When a package name is
supplied instead of a normal module, the interpreter will execute<pkg>.__main__as the main module.)Thus the user can simply reference the package rather than a particular module within the package.This can also be combined with reading additional arguments entered on the command line.See § “__main__.pyin Python Packages”
in “__main__— Top-level code environment,” docs.python.org.Note thatthe contents of__main__.pytypically aren’t fenced withif __name__ == '__main__'blocks.Loosely,__main__.pyis to a package what amain()function is to a console script. (E.g.,
“main functions are often used to create command-line tools by specifying them as entry points for console scripts.”)Establish a console-script entry-point command for a user to execute the programAdding a console script entry point allows the package to define a user-friendly name for installers of the package to
execute. See § “Entry Points” ofBuilding and Distributing Packages with Setuptools, PyPA.To implement such a user-friendly command name, heremy-command, we need to make modifications to (a)setup.cfgand
(b)__init__.py.Insetup.cfg[options.entry_points]
console_scripts =
    my-command = demo_package_sample_data_with_code.__main__:mainIn__init__.py# The following import was apparently required to get the  console script
# entry point to work. Without this import, I got errors at run time (when
# issuing the command `demo-command` on the command line, but not when using
# `runpy`, i.e., `python -m`):
#    AttributeError: module 'demo_package_sample_data_with_code' has no attribute 'main'
# Thus by importing main() in __init__.py, `main` was in the proper namespace.

from . __main__ import mainAllow for the user to optionally enter an argument on the command lineAs an illustration of the functionality, the code uses the built-inargparsemodule allows the user to (optionally) enter an argument on
the command line on the same line as the command to run the program. (Also see Tshepang Lekhonkhobe’sexcellentargparsetutorial.)You can see how this functionality is implemented in this project in thecheck_CLI_for_user_input()function, which is
called frommain(), both inmy_module.py.Accessing the package’s modules, functions, etc. from outside the packageIn the file and directory structure diagram above, you’ll see in the root of the project directory the moduletests/test_my_module.py. This is outside the scope of the code that will be installed bypip, which is limited to
thesrc/directory. Thetestsdirectory is in the standard location for the “src” directory/file structure that
is adopted here (though there is also a popular train of thought that tests should be distributed within the source
directory). (See, e.g., “Where to Put Tests?”: “In Python packaging, there is no consensus on where you should put your
test suite.”)The moduletest_my_module.pyis included to illustrate how a module in this location outside of the installed
package can reference entities within the installed package. This can be seen in the twoimportstatements intest_my_module.py:from demo_package_sample_data_with_code.my_module import print_value_from_resource
import demo_package_sample_data_with_code.constants as sourceFinish development and upload to PyPIHere I walk through—stage by stage, and command by command—the process of:creating a virtual environment,finishing your development in a local “editable” or “development” install,building your project and turning it into a distribution package that can be uploaded to PyPI,uploading it to TestPyPI,testing your distribution by created a new virtual environment and installing your project from TestPyPI.A full transcript (where only some of the output is condensed) of this process is available at:docs/Transcript_of_installation_and_testing.txt.Create a virtual environmentFrom here on, I’m assuming that you’re using a virtual environment. I usevenv, noting that, per the Python Packaging User Guide
(§ “Installing packages using pip and virtual environments”):
“If you are using Python 3.3 or newer, thevenvmodule is the preferred way to create and manage virtual
environments.”Generally, see:§ 12. Virtual Environments and Packagesof Python Docs.Brett Cannon, “A quick-and-dirty guide on how to install packages for Python,”Tall, Snarky Canadian, January 21, 2020. Though not
signaled by its title, there is substantial discussion of the why and how of using a virtual environment.Install the project in “editable mode”/“development mode” in order to test and further develop itWhen still developing the package, and before ever publishing it to PyPI (or even TestPyPI), we want to install the
package from the local source (and thereforenotfrom PyPI). Moreover, we do so in “editable mode,” which is
essentially “setuptools develop mode.” In this
mode, you continue to work on the code without needing to rebuild and reinstall the project every time you make a
change.(See generally § “Local project installs” in thepipdocumentation, and in particular
§§ “Editable installs.”)To install this package in “editable mode”/“development mode,” we usepip installwith the-eoption:python -m pip install -e path/to/SomeProject(See “‘Editable’ Installs” in thepip» commands »installdocumentation.) (Also, here’san argument by Brett Cannon for usingpython -m pip install -e .instead ofpip install -e ..)As thedocumentationwell explains:Editable installs allow you to install your project without copying any files. Instead, the files in the development
directory are added to Python’s import path. This approach is well suited for development and is also known as a
“development installation”.With an editable install, you only need to perform a re-installation if you change the project metadata (eg: version,
what scripts need to be generated etc). You will still need to run build commands when you need to perform a compilation
for non-Python code in the project (eg: C extensions).For example, to navigate to the project directory, create and activate a virtual environment, and install the project
in editable mode:% cd path/to/demo_package_and_read_data_files
% python3 -m venv venv
% source venv/bin/activate
% python -m pip install --upgrade pip
% python -m pip install -e .(In thepipcommand the “.” inpip install -e .represents the path to the project.)Installing in editable mode allows you to edit the code and immediately see the changes without having to rebuild and
reinstall the package.Build the distribution package to upload to PyPIWhen development matures to the point of having a version of the project you want to distribute via PyPI, the next step
is to generate adistribution packagefor
the project.InstallbuildThe first step is to installbuildinto your virtual
environment (or upgrade if it already exists there) with% python -m pip install --upgrade buildBuild the distributionSee
§ “Generating distribution archivesof “Packaging Python Projects,” Python
Packaging User Guide » Tutorials.% cd path/to/demo_package_and_read_data_filesIf this is your first time building distribution files for this project, continue immediately with the next step.
However, if you’ve already built distribution files for earlier versions of this project, you might already have adistdirectory atpath/to/my-project/dist. In that case, move any of those earlier distribution files out of that
directory. Otherwise, later steps will cause you to re-upload them to TestPyPI/PyPI, which you don’t want.% python -m buildThis generates a fair amount of output and creates adistdirectory at the project root, which now contains the
following two files:demo_package_sample_data_with_code-0.0.1-py39-none-any.whl
demo-package-sample-data-with-code-0.0.1.tar.gz(If you didn’t already, be sure to remove older versions of the distribution files from this directory. Otherwise, later
steps will cause you to re-upload those obsolete files to TestPyPI/PyPI, which can cause an error.)The wheel fileThe first of these, with the.whlextension is a “wheel” file, or
“built distribution.” It contains files
and metadata that need only to be moved to an appropriate location on the target system in order to be installed.To better understand the file name of the wheel file (and in particular theplatform compatibility tagsafter
the version number), see
§ “File name convention” ofPEP 427 – The Wheel Binary Package Format 1.0, supplemented
byPEP 425 – Compatibility Tags for Built Distributions. See also Brett Cannon,
“The challenges in designing a library for PEP 425 (aka wheel tags),”Tall, Snarky Canadian, June 1, 2019.In the present case:py39: (“Python Tag”) Iindicates that the package requires Python 3.9.none: (“ABI Tag,” referring toApplication Binary Interface) Indicates which Python ABI
is required by any included extension modules.none“represent[s] the case of not caring. This is typically seen with py interpreter tags since you shouldn't care about what ABI an interpreter supports if you're targeting just the Python language and not a specific interpreter.”any: (“Platform Tag”)The source archiveThe second of these, with thetar.gzextension, is a
“source archive,” that contains raw source code.You should always upload a source archive and provide built archives for the platforms your project is compatible with.
In this case, our example package is compatible with Python on any platform so only one built distribution is needed.Upload the distribution and then install it from scratch to test itInstalltwineTwine (project on PyPI,docs) is a utility
for publishing Python packages to PyPI and other repositories. Twine is the method adopted here to upload the
built distribution files to both TestPyPI and PyPI. (See alsoUploading the distribution archivesin the Python Packaging User Guide.)To installtwine:% pip install --upgrade twinecheckyourdist/*withtwine% twine check dist/*
Checking dist/demo_package_and_read_data_files-0.0.1-py39-none-any.whl: PASSED
Checking dist/demo_package_and_read_data_files-0.0.1.tar.gz: PASSEDTest your entire distribution work flow with TestPyPITestPyPIis a separate Python package index that allows you to “dry run” your packaging
procedures to make sure everything works before uploading it to the main PyPI. SeeUsing TestPyPIin the Python Packaging User Guide.Upload to test.pypi.orgSee “Using TestPyPI,” of the Python
Packaging User Guide, PyPA.% twine upload --repository testpypi dist/*
Uploading distributions to https://test.pypi.org/legacy/
Enter your username: myusername
Enter your password: ••••••••
Uploading demo_package_and_read_data_files-0.0.1-py39-none-any.whl
Uploading demo_package_and_read_data_files-0.0.1.tar.gz
View at:
https://test.pypi.org/project/demo-package-and-read-data-files/0.0.1/Test install the package locally from TestPyPIWhen visiting the above link, the page displays a command for installing the package:pip install -i https://test.pypi.org/simple/ demo-package-and-read-data-files==0.0.1Create a new directory and corresponding new virtual environment% cd GitHub_repos 
% mkdir test_package
% python3 -m venv venv
% source venv/bin/activate
% python -m pip install --upgrade pipInstall package from TestPyPIWhen there are no third-party dependencies that must be installed from PyPIWhen your project has no third-party dependencies (or all of the third-party are on TestPyPI, an unlikely occurrence
because TestPyPI regularly prunes itself)% pip install -i https://test.pypi.org/simple/ demo-package-and-read-data-files==0.0.1When therearethird-party dependencies that must be installed from PyPIWhen there are third-party dependencies that are not available on TestPyPI but are available on
PyPI, you need to tellpipto also look on PyPI. (See “Using TestPyPI,” of the Python
Packaging User Guide, PyPA.)pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ my-projectNote that here you aren’t disclosing which dependency to get where but rather: ifpipcan’t find a dependency on
TestPyPI,pipshould then look on PyPI.Running the program from the command lineRunning the program with no CLI argumentWhen the user does not supply a CLI argument, a default message is printed:The user declined to share any knowledge. 🙁% python -m demo_package_and_read_data_files
I am here, in __main__.py.

# # # # # # # # # # # # # # #

The user declined to share any knowledge. 🙁

π: 3.14159265358979323846
e: 2.71828182845904523536

Please don’t be concerned when you see the following error message. It’s expected.

Oops! The data file «meaning_of_life.txt» wasn’t found at this location:
»» /Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt.
[Errno 2] No such file or directory: '/Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt'
Meaning of life: I have no clue 🤪

* * * * * * * * * * * * * * *Running the program with CLI argument a sequence of wordsAfter installing, you can run the program from the command line with either of the following two syntaxes:python -m demo_package_sample_data_with_code <OPTIONAL_ARGUMENT>ormy-command <OPTIONAL_ARGUMENT>The user is meant to enter text at the command line. If the user does not quote the string, it will be reported as a
list of words, which will be.join()ed into a single string of space-separated words.python -m demo_package_sample_data_with_code Wu Zetian was the only female emperor in China’s history
I am here, in __main__.py.

# # # # # # # # # # # # # # #

The user chose to share: Wu Zetian was the only female emperor in China’s history

π: 3.14159265358979323846
e: 2.71828182845904523536

Please don’t be concerned when you see the following error message. It’s expected.

Oops! The data file «meaning_of_life.txt» wasn’t found at this location:
»» /Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt.
[Errno 2] No such file or directory: '/Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt'
Meaning of life: I have no clue 🤪

* * * * * * * * * * * * * * *Running the program with CLI argument a quoted string of a sequence of wordspython -m demo_package_sample_data_with_code "Wu Zetian was the only female emperor in China’s history"
I am here, in __main__.py.

# # # # # # # # # # # # # # #

The user chose to share: Wu Zetian was the only female emperor in China’s history

π: 3.14159265358979323846
e: 2.71828182845904523536

Please don’t be concerned when you see the following error message. It’s expected.

Oops! The data file «meaning_of_life.txt» wasn’t found at this location:
»» /Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt.
[Errno 2] No such file or directory: '/Volumes/Avocado/Users/ada/Documents/GitHub_repos/demo-package-sample-data-with-code/src/demo_package_sample_data_with_code/sample_data/meaning_of_life.txt'
Meaning of life: I have no clue 🤪

* * * * * * * * * * * * * * *Running the program with CLI argument a call for help:--helppython -m demo_package_sample_data_with_code --help
I am here, in __main__.py.

# # # # # # # # # # # # # # #

usage: __main__.py [-h] [user_wisdom ...]

positional arguments:
  user_wisdom  Please share some wisdom

optional arguments:
  -h, --help   show this help message and exit