Repository navigation
Open
Description
setuptools version
62.3.2
Python version
3.10
OS
Debian with conda
Additional environment information
No response
Description
pyopencl has OpenCL files and some headers in a subdirectory pyopencl/cl and they are included as package_data so that the python module can find them.
package_data={
"pyopencl": [
"cl/*.cl",
"cl/*.h",
"cl/pyopencl-random123/*.cl",
"cl/pyopencl-random123/*.h",
]
},
With new setuptools, there is a warning saying
############################
# Package would be ignored #
############################
Python recognizes 'pyopencl.cl' as an importable package, however it is
included in the distribution as "data".
This behavior is likely to change in future versions of setuptools (and
therefore is considered deprecated).
Please make sure that 'pyopencl.cl' is included as a package by using
setuptools' `packages` configuration field or the proper discovery methods
(for example by using `find_namespace_packages(...)`/`find_namespace:`
instead of `find_packages(...)`/`find:`).
You can read more about "package discovery" and "data files" on setuptools
documentation page.
cc @inducer
Expected behavior
No warning
How to Reproduce
- clone https://github.com/inducer/pyopencl
- install numpy
- Run
python setup.py install
Output
$ python setup.py install
running install
/home/idf2/miniforge3/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
warnings.warn(
/home/idf2/miniforge3/lib/python3.10/site-packages/setuptools/command/easy_install.py:144: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.
warnings.warn(
running bdist_egg
running egg_info
writing pyopencl.egg-info/PKG-INFO
writing dependency_links to pyopencl.egg-info/dependency_links.txt
writing requirements to pyopencl.egg-info/requires.txt
writing top-level names to pyopencl.egg-info/top_level.txt
reading manifest file 'pyopencl.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
adding license file 'LICENSE'
writing manifest file 'pyopencl.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
/home/idf2/miniforge3/lib/python3.10/site-packages/setuptools/command/build_py.py:153: SetuptoolsDeprecationWarning: Installing 'pyopencl.cl' as data is deprecated, please list it in `packages`.
!!
############################
# Package would be ignored #
############################
Python recognizes 'pyopencl.cl' as an importable package, however it is
included in the distribution as "data".
This behavior is likely to change in future versions of setuptools (and
therefore is considered deprecated).
Please make sure that 'pyopencl.cl' is included as a package by using
setuptools' `packages` configuration field or the proper discovery methods
(for example by using `find_namespace_packages(...)`/`find_namespace:`
instead of `find_packages(...)`/`find:`).
You can read more about "package discovery" and "data files" on setuptools
documentation page.
!!
check.warn(importable)
running build_ext
Reacted by Diane M. Napolitano and Ryan Barrett
Metadata
Metadata
Assignees
Labels
No labels
Activity
abravalheri commentedon May 29, 2022
Hi @isuruf thank you for bringing this subject for discussion.
This change (and the warning) was intentional, so in the future we will be able to solve a related problem with
find_packages(..., exclude=...)andinclude_package_data.Since PEP 420, there is no way for really differentiating a folder from a package in Python... People that install pyopencl will be able to successfully run
import pyopencl.cl, which means that the interpreter does considerproperly.cla package. Nevertheless configuration file seems to want to considercla "data directory", which is not currently a well stablished concept in the ecosystem...Would it possible for you guys to use
find_namespace_packages()as indicated in the warning message?jwodder commentedon Jun 3, 2022
I'm facing the same problem, and I'm not clear on the nature of the change suggested by @abravalheri. Are you saying that directories in the package hierarchy that contain only data files and no Python code should be included in the project's list of packages despite not actually being Python packages?
abravalheri commentedon Jun 4, 2022
Hi @jwodder, thank you very much for the input. Please find my comments bellow:
I think that this assumption1 no longer holds.
According to PEP 420,
packageis a term that "refers to Python packages as defined by Python’s import statement".An evidence that a directory without Python code is treated by the Python import statement as a package is given by the following snippets:
Note in this example that both
mypkg1andmypkg2.subpkgcan be imported, and are treated as regular packages (both are imported asmoduleobjects with the attribute__path__set), despite not containing any Python file.I added a
mypkg.subpkgto the example to demonstrate that it does not matter whether or not the directory is nested inside another "traditional package".Moreover, the ecosystem also considers those folders, as demonstrated by the implementation of
importlib.resources:I understand that not all tools in the ecosystem embrace PEP 420 by default and that sometimes it might be frustrating to see tools evolving some interfaces evolving. That is the reason why I decided to add a deprecation warning first and a transition period, instead of simply changing things in a major version bump.
This change in setuptools was implemented due to a single, very pragmatic, objective: to fix internal inconsistencies (as presented in #3260), which are caused by this division between packages and directories that don't contain Python code (which is not a division recognized by Python's import system).
For the time being, I don't plan on removing the deprecation warning, since the only way I know how to fix the internal inconsistencies requires us to eliminate this arbitrary division. However if anyone in the community finds a different way of solving the problem and are willing to provide a backward-compatible PR, I am more than happy to consider an alternative.
Footnotes
Namely the assumption that a directory with no Python code is not a package. ↩
[-][BUG] package data in subdirectory causes warning[/-][+]package data in subdirectory causes warning[/+]jwodder commentedon Jun 4, 2022
@abravalheri So you are saying that "data-only packages" should be included in the project's list of packages? Is that the only change you're currently recommending to authors of such projects? Does it matter whether the data files are included via
package_dataorMANIFEST.in+include_package_data?abravalheri commentedon Jun 4, 2022
Yes, I recommend adding all the sub-directories to the list of packages, even if they only include data files. On the bright side that can be done with
find_namespace_packages(setup.py) orfind_namespace:(setup.cfg).It should make no difference if you are using
package_dataorMANIFEST.in(although I am not sure if users are going to see the warning if they don't useinclude_package_data=True).abravalheri commentedon Jun 4, 2022
Do you have a suggestion about how to improve the warning message to make it more clear?
jwodder commentedon Jun 4, 2022
@abravalheri I find the first paragraph of the warning to be a bit confusing. I would rewrite it from:
to something more like:
Use find_namespace: instead of find:
Use find_namespace: instead of find:
cdfarrow commentedon Jun 9, 2022
I was following this issue because I was very confused (as a newbie) by the warning that came up. Can I suggest a further edit to the warning message:
Currently {importable!r} is only added to the distribution because it may
contain data files...
-->
Currently {importable!r} has been automatically added to the distribution because it may
contain data files...
This is my understanding of what is actually happening (i.e. automatic inclusion). If this suggested change is wrong, then I'm still not following how this works...
48 remaining items
webknjaz commentedon Dec 30, 2023
UPD: turned out that it was
include_package_data=Truethat confusedsetuptools. There was really no need for it and removing the setting fixed the problem.@abravalheri I think that adding a hint suggesting to delete
include_package_data=Trueand replace it with a fine-grainedpackage-datamapping would be useful.In my case, a more straightforward suggestion would've made it clearer what to do, because I wasn't the one to set up initial packaging in the project and had to attempt figuring out how and why things were set up that way.
Another source of confusion was the fact that the error message was unhappy with
multidict._multilib, suggesting to use helpers likefind_namespace_packages()while it did list the packages explicitly with no discovery needed, via a static list:packages=["multidict"]. This also confused a contributor who attempted adding"multidict._multilib"to that list, not understanding immediately that C-headers aren't to be installed insite-packages/(or distributed in wheels, for that matter).shakfu commentedon Jan 10, 2024
@abravalheri
I appreciate your efforts to solve what is a quite challenging situation due to recent changes (PEP 420). You said very clearly early in this issue: "Since PEP 420, there is no way for really differentiating a folder from a package in Python."
But this problematic new state is not one that developers should learn to live with, they should not be compelled to adopt insecure quick fixes. The packaging distinction between code and data should be preserved. Because to do otherwise, to conflate between the two, and to make data executable by default, is to introduce additional security risks and to basically undermine the concept of having an "api".
The proposed fix which resolves the deprecation is apparently to use
find_namespace_packages. But this is just wrong. Since namespace packages are meant to contain importable executable code.In my current project which wraps a third party library. The directory structure is slightly more complex to accommodate two build variants: a default dynamically-linked build and an optional statically-linked build variant.
The default build's code is in
src/cyfaustand the static build code is insrc/static/cyfaust. The project consists of cython extensions and a significant amount of data which is placed in aresourcesdirectory hierarchy in the project root and softlinked into each build variant's folder so it can be picked up as below:Within the
resourcesfolder structure (which is specified by the library I'm wrapping) there are 775 files, 10.4 MB in total. Most of these are templated source files and all are part of the thirdparty library itself. But all are precisely data, not to be imported in the project package, only to be used for the purposes of code generation.The relevant part of
setup.pycaptures this package code, data directory distinction simply and effectively:Right now, and everything just works as expected. The only problem I have is the deprecation warning and the fact that I'm put off by the proposed solution to use
find_namespace_packagesfor the reasons given above.May I suggest that the deprecation warning be dropped or even abbreviated somewhat until the data / code packaging distinction can be restored and preserved by some new PEP or other. It makes no sense to promote solutions, in the interim, which incorrectly conflate the two.
S
abravalheri commentedon Jan 11, 2024
Hi @shakfu, thank you very much for sharing your thoughts. Please see my comments below:
Please note that the "directory is a package" behaviour introduced in PEP 420 is actually quite old. The PEP has been approved in 19/Apr/2012 and the specified behaviour implemented in Python 3.3, over 10 years ago which is a lot in "software development years".
It is safe to assume that this behaviour is stable and that at some point in their carreers Python developers will indeed learn what a Python package means and how adding a directory nested somewhere under one of the entries of
sys.pathcorresponds effectively to create a Python package that can be regularly imported as any other package via animportstatement.Could you please clarify in what sense the solutions presented in the warning message are insecure?
The error message presents to the user a couple of suggestions (to be chosen accordingly to what fits better their use case) which include to manually add the missing entries to the
packagesconfiguration option, or to use a convenience function provided by setuptools. In both cases, the procedure is very mature and stable.I understand that this is a popular interpretation of what the concept of packages (and/or namespace packages) might mean for Python and I see where it comes from. But I don't think this interpretation is backed by the Python implementation and the way it works...
You can
importdirectories that don't contain.pyfiles, and having packages for holding non-Python files is actually a very useful feature1! I never found official documentation saying that packages/namespace packages are meant to contain importable executable code and cannot be used to contain only non-Python files, I don't think there is an official stance on that.If these non-Python files are not meant to be installed in the end-user's machine, I believe it is a matter of properly configuring
packages/package_data/include_package_data/exclude_package_data/MANIFEST.inso that they are part of thesdistbut not part of thewheel. Otherwise, if they end up nested somewhere under an entry ofsys.path, they will be import packages, effectively.If you really don't like the idea of having these directories as importable packages, then the alternative is to use
data-files, which will translate into a special directory in the wheel file ({name}-{version}.data/data/). In turn,pipwill stall them in a different location that will not be nested somewhere insys.path. It is a lot of effort to align expectation and implementation, for most people it might just be worth to adapt their expectations.I think that simply dropping the warning would be unwise.
The purpose of the warning is for developers to align their configuration to their expectations.
If they want certain directories to be installed somewhere under
sys.path, this effectively mean that they are asking setupotools to include certains packages/subpackages into the wheel. Within setuptools, that desire is captured by thepackagesconfiguration option.We need users to start clarifying their configuration, because the next step is to fix other related bugs (see #3340 (comment)), and it would be bad if suddenly some folders are missing from packages.
I don't know of anyone currently attempting to introduce a data / code distinction (and what that will mean for Python packages and import system) via a new PEP. Personally, I like the status quo and I think it works quite well. Since there is no concrete plans for such change in the ecosystem, there is no foreseeable risk of conflation, and we don't need to treat this situation as "interim". As far as we know this is the stable behaviour that we should be targetting to achieve after 10 years of transition.
There is another approach2 that I have absolute no problems in considering and actually would welcome with open arms: if a member of the community is willing to contribute (i.e. design, discuss, find consensus, implement, document, fix, support ...) a different way of configuring setuptools that is more conceptually self-evident and less prone to ambiguity than
packages/package_data/include_package_data/exclude_package_data/MANIFEST.in3. Extra requirements for such solution are: backward compatibility and easy maintenance.It is a tough challenge which I don't have the resources to tackle myself, but I would be very grateful if someone else can.
Footnotes
They make it really easy to find non-Python files them runtime using
importlib.resources. You can also implement extensions/plugin systems on top of it, and etc... ↩But that is orthogonal to the warning and next steps discussed here. ↩
In some sense, the automatic discovery (when the user does not specify
packages) that was introduced a couple of years ago is meant to be easier and less confusing. But automatic discovery is not a fit for all and edge cases still require playing withpackages/package_data/include_package_data/exclude_package_data. ↩abravalheri commentedon Jan 11, 2024
That is nice idea. Depending on what the developer is trying to achieve that will indeed solve the problem. Currently the message does mention
include-package-data=False:But probably that can be rephrased to be more clear. Would you like to give it a shot?
(P.S.: I consider
include-package-data=Falsea better text for the warning message than omitting, because inpyproject.tomlthe default value of this config isTrue, so it just saves the time to explain the nuances).I understand that, but unfortunately there is no single fix for all scenarios. For some people
include_package_data=Falseis the fix, for othersfind_namespace_packages()will do what they are trying to achieve...And that is the problem with the ambiguous1 configuration,
setuptoolsshould not have to guess what the user wants to do, that is why we are asking the user to tell us.Footnotes
"Ambiguous" while setuptools does not take a final action on the "directories with non-Python files are not packages" controversy... Once the warning manage to compel devs to clarify their config, we can move past that problem and actually allow people that are intentionally omitting entries in
packagesto useinclude-package-data. ↩shakfu commentedon Jan 12, 2024
Hi @abravalheri, appreciate your extended and thoughtful reply to my early post (please see my comments below):
I've been working in Python since a little bit before version 1.5.2, so this is certainly not new to me. Nonetheless, I admittedly did not express myself clearly: I have no issue with the principle of "directory is a package" or that packages are importable. This is fundamental to Python since way back.
I'm also happy with having a
datafolder within a python package such that a module within that package can programmatically always find thisdatafolder by retrieving its path relative to__file__. Indeed this is precisely what I'm doing in the project I described earlier: in thecyfaust.commonmodule I have the following code:After reading your prior reply, I appreciate that the current situation (and your understanding of it) is a lot more nuanced than I previously considered, and that the confusion is probably stemming from a couple of things here:
1. My failure to understand the full implications of PEP 420 Namespace Packages:
This PEP makes everything in a package potentially importable, even when that is not the intent!
For a hypothetical example in my project, a user of my package can (currently) import a python file that is only intended to be used as a template.
But due to PEP 420, this is possible:
Now, this can be prevented by renaming
resourcesto a non-python friendly name such aspkg-resources.So now this is impossible:
I did not realize that this was the default situation now... and it has nothing to do with setuptools. That's my bad.
2. The Documentation + the Deprecation + the Multiplicity of Configuration options + Different ways of doing the same thing are confusing:
In my case, this is how I tried to resolve the deprecation:
Then I saw the deprecation notice, and to figure out how to resolve it I read the relevant datafiles section of the setuptools documentation.
I chose the initial recommended
include_package_datamethod.This led to the following:
And it works, it produces the same outcomes as my prior working case, but... the deprecation notice is still there.
So after some further reading and research on the web, the advice is use
find_namespace_packagesso I choose the "Subdirectory for Data Files" methodI naively just changed
find_packagestofind_namespace_packagessince it is seemingly the simplest solutionand then I get an error:
running build running build_py error: package directory 'static' does not existAt this point the frustration of palpable. All I want to do is specify a subdirectory of my package as a data directory to be included in whole.
The more complex way in this method is to add a
package_dataentry, but filling it out seems laborious given that I have 775 files data files accross many directories with different types etc..At this stage, I decide to just live with the deprecation!
You asked for suggestions on how to improve this api. Here are my two cents:
Ideally it should be as simple as something like this:
or just:
In the above ideal cases, MANIFEST.in or similar is only configured if you need more granular control over what is included or excluded. It is not required in the simplest case where you want everything included.
shakfu commentedon Jan 13, 2024
@abravalheri
As an addendum to my last post I finally found a way to remove the deprecation warning. I could not get it done with
find_namespace_packagesand the only way was to runos.walkin myresourcesfolder and then manually add folders which had python friendly names to thepackageslist as per the below code. Note that this was the only solution I could arrive at to remove the deprecation warning. If there is a better way, please let me know otherwise, mysetup.pyfile looks a little ridiculous. 😟abravalheri commentedon Jan 13, 2024
@shakfu, have you tried something like the following?
The example above requires
MANIFEST.into be configured so that all the files necessary for the build process are present in thesdist. Alternatively if you are OK with all the files in your VCS to also be present in yoursdistyou can use a plugin likesetuptools-scm(which potentially simplifies a lot the configuration).shakfu commentedon Jan 13, 2024
@abravalheri Thanks for the tip, I'll try it out to see if I can improve it as per your suggestions.
Incidentally, because I have two build variant in my setup.py file, I generate the
MANIFEST.infile in each case:shakfu commentedon Jan 14, 2024
@abravalheri
Incidentally, I deleted my prior post as it included an error that should not have been flagged here. In any case, the
setup.pyfile is working, checked-in and the deprecation is gone. Thanks very much for your help!merwok commentedon Jan 14, 2024
It does not seem good if docs have to recommend the definition of namespace packages for projects that are not using namespace packages.
shakfu commentedon Jan 14, 2024
This is the essence of the issue: the deprecation makes it necessary to use a more complex solution (
find_namespace_packages) to a problem (bundling adatafolder in one's package) that was previously relatively simple to resolve.My case above is illustrative.
abravalheri commentedon Jan 14, 2024
@merwok, please note that if a distribution install a directory nested somewhere under
sys.pathand this directory that does not contain an__init__.py, then that is indeed a namespace package, and the project is, effectively, using namespace packages. You can use your REPL to check that the directory is importable and the REPL will show you something like<module '...' (namespace)>if you print the object you imported.The assumption that certain directories that don't contain Python files are exempt of this behaviour is not backed by the implementation, and as far as I know, by the Python docs (the PEP introducing implicit namespaces pretty much says the opposite).
The warning message discussed in this issue has to deal with 2 situations:
include_package_datato do so, but is forgetting to list such directories inpackages(therefore, it is providing an incorrect configuration that fails match file structure and intent).packagesor by usingfind_namespace_packages(..)with theinclude/excludeoptions, or by consciously relying on the fact thatfind_packages(...)skip directories without__init__.py). At the same time, the developer is correctly specifyinginclude_package_datasuch that files in other directories are included.Currently there is a bug in setuptools that will incorrectly handle both situations.
For situation 1, setuptools will include packages that are not listed in
packages, and therefore fail to fulfil the configuration that has been passed.For situation 2, setuptools will not skip certain directories that are intentionally omitted from
packages, and therefore also fail to fulfil the configuration that has been passed.To fix this bug we need the warning message to bring awareness for users that fall into category 1: that their configuration does not match the intent, and for users that fall into category 2: that they will need to use a workaround while the fix has not been implemented yet.
merwok commentedon Jan 15, 2024
I understand that, but my viewpoint is about author intent.
In the examples here the developers have regular packages, source files and data files.
They need a clean way to have their packages packaged up and installed, and the data files also packaged up.
abravalheri commentedon Jan 16, 2024
The clean way is to list any package they are using to host data files in the
packagesdirective. This mental model is aligned with the Python implementation, docs, and work great withimportlib.resources.There is no need for differentiating between types of directories, that is only conceptual overhead that does not really match the way Python works.
BUILD: Resort to namespace packaging for (pseudo-)data directories