Package: check-wheel-contents
GitHub|PyPI|Issues|ChangelogGetting the right files into your wheel is tricky, and sometimes we mess up and
publish a wheel containing__pycache__directories ortests/.  Do we
have to manually check the contents of every wheel we build before uploading it
to PyPI?  How about letting this program check for you?  Just runcheck-wheel-contentson your wheel, and it’ll fail and notify you if any of
several common errors & mistakes are detected.  The errors are described below,
along with common causes and corresponding fixes.Installationcheck-wheel-contentsrequires Python 3.7 or higher.  Just usepipfor Python 3 (You have pip, right?) to installcheck-wheel-contentsand its dependencies:python3 -m pip install check-wheel-contentsUsagecheck-wheel-contents [<options>] <wheel or directory> ...check-wheel-contentstakes zero or more paths as arguments, each pointing
to either a wheel to analyze or a directory that will be traversed for wheels
to analyze.  If a given wheel fails any checks, a message will be printed for
each check along with (if applicable) a list of filepaths in the wheel causing
the check to fail, and the command will exit with a nonzero status.  If a wheel
passes all checks, the program will print{path_to_wheel}: OK.Options-cFILE,--configFILERead configuration from the given file; see below for
more information--no-configDisable reading from the configuration file-h,--helpDisplay a usage message and exit-V,--versionDisplay the program version and exitThe remaining options can be given either on the command line or in the
configuration file; see “Configuration Options” for more information.ConfigurationConfiguration FileIf a configuration file is specified on the command line with the--configoption,check-wheel-contentsreads its configuration from the given file.
Files with a.tomlextension are parsed as TOML files, and the
configuration is read from thetool.check-wheel-contentstable.  All other
files are parsed as INI files, and their configuration is read from the[check-wheel-contents]section (unless the file is namedsetup.cfg, in
which case the section[tool:check-wheel-contents]is used instead).If no configuration file is specified on the command line, the program begins
searching for a file namedpyproject.toml,tox.ini,setup.cfg,check-wheel-contents.cfg, or.check-wheel-contents.cfg, starting in the
current directory and going up.  The files are read using the same rules as for
the--configoption, and the first file in the list that contains the
appropriate section is used.  Searching stops once a directory containing any
of the named files is found, even if none of them contain the relevant section.Configuration OptionsThe following options may be set either on the command line or in the
configuration file.  Settings given on the command line override those in the
configuration file.  Unknown keys in configuration files are ignored.--select<checks>/select = <checks>Select/enable only the given checks.<checks>is a comma-separated list
of check IDs and/or check ID prefixes (to select all checks beginning with
the given prefixes).In a TOML file,<checks>may alternatively be given as a list of
strings.By default, all checks are selected (though some checks are no-ops when
certain other options are/aren’t given).--ignore<checks>/ignore = <checks>Ignore/skip the given checks.<checks>is a comma-separated list of
check IDs and/or check ID prefixes (to ignore all checks beginning with the
given prefixes).In a TOML file,<checks>may alternatively be given as a list of
strings.By default, no checks are ignored.--toplevel<names>/toplevel = <names>Tellcheck-wheel-contentsto check that the toplevel library entries of
the wheel equal the set of names in the comma-separated list<names>;
e.g.,--toplevelfoo.py,bar/checks thatfoo.py,bar, and
nothing else is at the top level of your wheel.  Trailing slashes on
directory names are optional.In a TOML file,<names>may alternatively be given as a list of strings.This option disables check W009 and enables checks W201 and W202.  It is
also used by check W005 to prevent failure on common names that are
intentionally used as toplevel names.--package<path>/package = <paths>Tellcheck-wheel-contentsto check that the wheel’s library sections
contain the file tree rooted at<path>.Paths given on the command line are resolved relative to the current working
directory.  Paths given in a configuration file are resolved relative to the
directory containing the configuration file.On the command line, multiple paths can be specified by supplying--packagemultiple times.  In a configuration file, multiple paths can
be specified by settingpackageto a comma-separated list of paths.  In
a TOML file,<paths>may alternatively be given as a list of strings.This option disables check W009 and enables checks W101 and W102.--src-dir<path>/src_dir = <paths>The same as--package, except that only the contents of<path>(which must be a directory) and not<path>itself are checked against
the wheel’s contents.--package-omit<patterns>/package_omit = <patterns>Ignore files & directories inside--packageor--src-dirarguments
that match any of the glob patterns in the comma-separated list<patterns>.  Ignored files will not be looked for in wheels for check
W101, and if any of them do show up in a wheel, it will cause check W102 to
fail.In a TOML file,<patterns>may alternatively be given as a list of
strings.The default set of ignored patterns is.*, CVS, RCS, *.pyc, *.pyo,*.egg-info.ChecksNote: Unless otherwise stated, the common causes and their fixes listed
here are specific to projects developed using setuptools.  Users of other tools
like flit and poetry will have to consult those projects’ documentation in
order to resolve failed checks.Note: When rebuilding a wheel with setuptools, it is a good idea to delete
thebuild/directory first.  (This can be done in a single command withpython setup.py clean--allbdist_wheel.)  Not doing this can cause various
checks to continue to fail or new ones to start failing.W001 — Wheel contains .pyc/.pyo filesThis check fails if there are any files in the wheel with a.pycor.pyoextension.  Such files are compiled Python bytecode files, and they do
not belong in wheels, because (a) they are platform-specific and thus useless
to many of your users, and (b) pip generates.pycfiles for the.pyfiles in your wheel automatically.Common causes:You haveinclude_package_dataset toTrue, yourMANIFEST.incontainsgraft packagenameorrecursive-includepackagename *, and
the lineglobal-exclude*.py[co]or similar is either missing from theMANIFEST.inor else in the wrong location.Solution: Ensure thatglobal-exclude*.py[co]appears in yourMANIFEST.infileafterallinclude,recursive-include,global-include, andgraftcommands.You have[install]optimize = 1set insetup.cfg(or, equivalently,options={"install":{"optimize":"1"}}set insetup.py).Solution: Remove this setting.  It’s only useful when usingsetup.py installanyway, which is deprecated.W002 — Wheel contains duplicate filesThis check fails if any two files in the wheel have the same contents.  Common
file contents, such as files that are empty or just contain the line “#-*-coding:utf-8-*-”, are excluded from this check.Common causes:(Build tool agnostic)You copied a file or directory when you actually
meant to rename it.Solution: Delete the original copy of the file or directory.You built a wheel, renamed a file or directory, and then built a wheel again
without first deleting thebuild/directory.Solution: Delete thebuild/directory and build the wheel again.W003 — Wheel contains non-module at library toplevelThis check fails if there are any files at the root of the purelib or platlib
section of the wheel that are not Python modules or.pthfiles.
Non-modules belong elsewhere in a wheel:Licenses and similar notices should be stored in the wheel’s*.dist-infodirectory usingwheel’slicense_filesoption.Package data/resource files belong inside a package directory so that they
can be located withpkg_resourcesorimportlib-resources.A project’sREADMEshould already be used as the project’slong_description, in which case the text of theREADMEis already
included in the wheel inside the*.dist-info/METADATAfile.  There should
thus be no need to store theREADMEin the wheel’s library sections.W004 — Module is not located at importable pathThis check fails if there are any Python modules in the purelib or platlib
section of the wheel that cannot be imported due to one or more of their path
components being invalid Python identifiers.Common causes:(Build tool agnostic)You gave a package directory or module a name
containing a hyphen or other character not allowed in Python identifiers.Solution: Rename the offending directory or module to remove the
offending character, most likely by changing it to an underscore.(Build tool agnostic)You gave a package directory or module the name of a
Python keyword.Solution: Rename the offending directory or module.(Build tool agnostic)Your package contains database migration files
generated by alembic or Django, which (may) begin with numbers and thus do
not have valid Python identifiers as names.Solution: Ignore this check.  (Ignoring checks only for specific files is
not yet implemented.)W005 — Wheel contains common toplevel name in libraryThis check fails if there are any files or directories named.eggs,.nox,.tox,.venv,app,build,cli,data,dist,doc,docs,example,examples,lib,scripts,src,test,tests, orvenvlocated at the root of the purelib or platlib
section of the wheel.  These names are conventionally used for directories that
don’t belong in wheels (aside fromsrc, whose contents belong in wheels but
itself does not belong in a wheel).  Projects should only use toplevel names
that resemble the project name; using common names will cause different
projects’ files to overwrite each other on installation.If the--topleveloption is set, the names listed in the option will not
cause this check to fail.Common causes:Forsrc: You failed to set up yoursrc/layout correctly.srcshould not contain an__init__.pyfile,where='src'needs to be
passed tosetuptools.find_packages()insetup.py, andpackage_dir={"":"src"}needs to be passed tosetup()insetup.py.For directories other thansrc: The directory contains an__init__.pyfile, and the directory is not listed in theexcludeargument tosetuptools.find_packages()insetup.py.Solution: Include'DIRNAME'and'DIRNAME.*'in the list passed to
theexcludeargument offind_packages().For directories other thansrc: The directory is listed in theexcludeargument tofind_packages(), but'DIRNAME.*'is not, and
a subdirectory of the directory contains an__init__.pyfile.Solution: Include'DIRNAME.*'in the list passed to theexcludeargument offind_packages().You actually want to include your tests or examples in your wheel.Solution: Move the tests or whatever to inside your main package
directory (e.g., movetests/tosomepackage/tests/) so that they
won’t collide with other projects’ files on installation.You are actually making a package whose name is one of the listed names.Solution: Include the name of your package in the--topleveloption
so thatcheck-wheel-contentsknows it’s meant to be there.W006 —__init__.pyat top level of libraryThis check fails if there is a file named__init__.pyat the root of the
purelib or platlib section of the wheel.__init__.pyfiles only belong
inside package directories, not at the root of an installation.Common causes:You failed to set up yoursrc/layout correctly.srcshould not
contain an__init__.pyfile,where='src'needs to be passed tosetuptools.find_packages()insetup.py, andpackage_dir={"":"src"}needs to be passed tosetup()insetup.py.You created an__init__.pyfile at the root of your project and setpackages='.'insetup.py.Solution: Configure your project’s packages correctly.  For single-file
modules, pass a list of their names (without the.pyextension) to thepy_modulesargument tosetup().  For package modules (directories),
pass a list of their names and the dotted names of their descendant
subpackages (possibly obtained by callingsetuptools.find_packages()) topackages.W007 — Wheel library is emptyThis check fails if the wheel contains no files in either its purelib or
platlib section.Common causes:Your project consists of a single-file.pymodule, but you declared it tosetup()insetup.pyusing thepackageskeyword.Solution: Single-file modules must be declared tosetup()using thepy_moduleskeyword.  Pass it a list of the names of your single-file
modules without the.pyextension.You are usingsetuptools.find_packages()to list your packages forsetup(), but your package does not contain an__init__.pyfile.Solution: Create an__init__.pyfile in your package.  If this is not
an option because you are building a namespace package, usesetuptools.find_namespace_packages()instead offind_packages().  Be
sure to set the arguments appropriately so that the function only finds your
main package;see the documentation for further information.You’re deliberately creating a wheel that only contains scripts, headers, or
other data files.Solution: Ignore this check.W008 — Wheel is emptyThis check fails if the wheel contains no files other than the*.dist-infometadata directory.  It is a stronger check than W007, intended for users who
are creating wheels that only contain scripts, headers, and other data files
and thus need to ignore W007.Common causes:Same causes as for W007You’re deliberately creating an empty wheel whose only function is to cause a
set of dependencies to be installed.Solution: Ignore this check.W009 — Wheel contains multiple toplevel library entriesThis check fails if the wheel’s purelib and platlib sections contain more than
one toplevel entry between them, excluding.pthfiles and files &
directories that begin with an underscore.  This is generally a sign that
something has gone wrong in packaging your project, as very few projects want
to distribute code with multiple top-level modules or packages.This check is disabled if the--toplevel,--package, or--src-diroption is given either on the command line or in the configuration file.Common causes:You built a wheel, renamed a toplevel file or directory, and then built a
wheel again without first deleting thebuild/directory.Solution: Delete thebuild/directory and build the wheel again.You are usingsetuptools.find_packages()in yoursetup.py, your
project contains multiple directories with__init__.pyfiles, and one or
more of these directories (other than your main package) is not listed in theexcludeargument tofind_packages().Solution: Pass a list of all__init__.py-having directories in your
project other than your main package to theexcludeargument offind_packages().  For proper exclusion, each directoryDIRNAMEshould
correspond to two elements of this list,'DIRNAME'and'DIRNAME.*',
in order to ensure that the directory and all of its subdirectories are
excluded.You are deliberately creating a wheel with multiple top-level Python modules
or packages.Solution: Use the--topleveloption to letcheck-wheel-contentsknow what toplevel entries to expect.W010 — Toplevel library directory contains no Python modulesThis check fails if a directory tree rooted at the root of the purelib or
platlib section of the wheel contains no Python modules.*-stubsdirectories are excluded from this check.W101 — Wheel library is missing files in package treeThis check is only enabled if the--packageor--src-diroption is set.
This check fails if a path in a tree rooted at an argument to--packageor
inside an argument to--src-dirdoes not appear in the wheel’s purelib or
platlib section.  Empty directories and local files & directories that match
any of the patterns specified with--package-omitor its default value are
excluded from this check.Note that this check only checks file paths, i.e., names of files &
directories.  File contents are not examined.For example, given the below local tree:/usr/src/project/
├── foo/
│   ├── .gitignore
│   ├── __init__.py
│   └── foo.py
└── src/
    ├── bar/
    │   ├── __init__.py
    │   ├── bar.py
    │   ├── empty/
    │   └── quux/
    │       └── data.dat
    └── bar.egg-info/
        └── PKG-INFOIf the options--package/usr/src/project/fooand--src-dir/usr/src/project/srcare supplied and--package-omitis left at its
default value, thencheck-wheel-contentswill look for the following paths
in the wheel, and the check will fail if any of them do not appear in either
the purelib or platlib section:foo/__init__.py
foo/foo.py
bar/__init__.py
bar/bar.py
bar/quux/data.datNote thatfoo/.gitignoreandsrc/bar.egg-infoare omitted from this
check (and if they do appear in the wheel, it will cause check W102 to fail).
Empty directories are ignored altogether.Common causes:For Python files: You failed to pass all of your project’s packages &
subpackages tosetup()’spackagesargument.  If you are usingsetuptools.find_packages(), all of your packages & subpackages need to
contain__init__.pyfiles.For non-Python files: You failed to declare your project’s package data
appropriately.See the setuptools documentation for information on how to
do this.W102 — Wheel library contains files not in package treeThis check is only enabled if the--packageor--src-diroption is set.
This check fails if the purelib or platlib section of the wheel contains any
files at paths that do not exist in any of the file trees specified with--packageor--src-dir.Note that this check only checks file paths, i.e., names of files &
directories.  File contents are not examined.For example, given the local tree and options shown in the example under W101,
this check will fail if the wheel contains any files in its purelib or platlib
section other than the following:foo/__init__.py
foo/foo.py
bar/__init__.py
bar/bar.py
bar/quux/data.datNote that files & directories that match any of the patterns specified with--package-omitor its default value are ignored in local trees, and so any
entries with those names in the wheel will cause this check to fail.  Empty
directories are ignored altogether.Common causes: See common causes of W009W201 — Wheel library is missing specified toplevel entryThis check is only enabled if the--topleveloption is set.  This check
fails if one or more of the names given in the--topleveloption does not
appear at the root of the purelib or platlib section of the wheel.Common causes: See common causes of W007W202 — Wheel library has undeclared toplevel entryThis check is only enabled if the--topleveloption is set.  This check
fails if there is a file or directory at the root of the purelib or platlib
section of the wheel that is not listed in the--topleveloption.*.pthfiles are ignored for the purposes of this check.Common causes: See common causes of W009