#extensionlib
1 messages ยท Page 1 of 1 (latest)
I'm happy to -- or, not to invite myself to the party, would like to -- get involved however I can. My lab's project, ChimeraX, allows users to write Python plugins and I wanted to move our plugin builder off XML and to TOML so bad that I made up an extension spec table when I extended it to read TOML
So it will be awesome when I can deprecate that code and move things to a Python standard
is it public?
hey @void vale, can we coordinate regarding PEP 739 ?
right now I am working on a real-world implementation by adding support for it in Meson, but it should probably be easier enough to implement it in extensionlib too
hey! I haven't read that thoroughly, how do you suppose they would interop?
that PEP would be great to have for package managers, no more subprocessing Python to get information on paths etc
I think the most impactful use-case for the Python packaging environment is cross-compilation, but everything else that currently needs to introspect Python installations might benefit from this
to be clear, PEP 739 does not actually cover any information regarding environment paths, just information regarding the Python installation
that said, I want to followup with a PEP that provides the environment-specific installation
yeah, but that is still something we need to query pythons for
yes
One cross-platform annoyance PEP 739 could help with in the general case is finding the Python executable to run. It has a .exe extension solely on Windows, and whether it's in the base directory (the one containing lib), a bin subdirectory, or a Scripts subdirectory can also vary.
The PEP would be nicer if paths could be relative to the JSON file instead of always being absolute, though (it's annoying enough that pyvenv.cfg and script shebang lines have to use absolute paths, we don't want to add more mechanisms that hinder Python installation and environment portability across machines)
PEP 739 currently does not specify the interpreter, but I am thinking it may be worth as the "base" interpreter, I am still looking for use-cases where that's needed though (assuming that we will have a followup PEP for the environment details).
The current iteration of the PEP does allow paths to be relative to the JSON file! Currently, we have a base_prefix prefix entry, which can either be absolute or relative to the JSON file location. All other paths can either be absolute or relative to base_prefix.
Expanding a bit more on the Python installation/environment discoverability, the current plan is for PEP 739 to specify all data needed to build Python extensions and all other use-cases where we need to introspect the build configuration of the Python installation, then I will push another PEP introducing a JSON file containing the data needed to introspect a environment (this will include paths to the interpreter, site-packages, stdlib, etc. and a path to the PEP 739 build details file), after that, I don't have a specif plan yet, I would like to standardize a way do discover Python installations (this could be a set of paths where you can place a environment description file, or some kind of pointer to one).
the reason for the build and environment separation is that environments (eg. virtual environments) often outlive Python installations/builds
with this approach, you can create a virtual environment, update the Python installation, and still have the tools be able to rely on the introspection JSON data
if the build and environment data were together in the same file, you could never rely on the build details being correct, as the Python installation might have been updated
Is the idea that libraries that do the actual compilation register themselves with extensionlib, and I (as a backend) can ask extensionlib to run that builder without knowing anything about the builder itself?
yes precisely
@void vale looks interesting, i presume this will superseed/power hatchs mypyc/cython/extension modules integrations
yes
This would be great. Are you looking for collaborators?
I think yes. At least testers and contributors.
Hi @void vale thank you very much for working on this.
I went through the documentation and it looks very good.
If the idea is to use this library as a proof of concept for a future PEP targeting interoperability between backends, I would have the following (unsolicited ๐, sorry for that, please feel free to simply ignore) suggestions:
- How about making the
ExtensionModulesinterface a protocol that implementations must adhere to instead of using inheritance?
I think this is more in-line with the spirit of a PEP and also would free backends from having to add another dependency (which is always complicated given the limitations of PEP 517 in terms of cycles). - How about having 2 separated methods (or properties), one for the "input files" and one for the "output files"?
This way a build backend may decide which files should be added to thesdist(input files) and which files should be added to thewheel(output files).
Additionally, when talking about a possible follow up PEP, I would assume the following:
- The
ExtensionModulesinterface is the only thing specified by the PEP. TheBuildRunnerinterface is an implementation detail. - To be "library-independent" spec, the PEP could replace
[[tool.extensionlib.*]]with a similar[[project.extension-modules.*]]table (or any other name really that is not related to a single implementation).- If that is the case, maybe it would be required to add a
configure(self, options: dict)method to theExtensionModulesinterface, so the build-backend can pass information used to derive the values ofinput_filesandoutput_files. Or simply make a spec for__init__(self, options: dict). - The
optionsargument, being an JSON/TOML-serializable dict, whose keys are a combination of arbitrarily defined by each implementation and that are written to the[[project.extension-modules.*]]table.
- If that is the case, maybe it would be required to add a
@lethal magnet thanks!
How about making the
ExtensionModulesinterface a protocol that implementations must adhere to instead of using inheritance?
I can write that in the PEP, but I think the recommendation should be this lib. What isn't shown is that it will have some utilities that pretty much every implementation would end up needing (like sysconfig stuff). @final dune will help with that, it's not my expertise.
How about having 2 separated methods (or properties), one for [...] the
sdist(input files) and [...] thewheel(output files).
I immediately love this idea! Though, do you have an example of something currently generated that is intended to be shipped in thesdist?
The
ExtensionModulesinterface is the only thing specified by the PEP. TheBuildRunnerinterface is an implementation detail.
Yup!
To be "library-independent" spec, the PEP could replace
[[tool.extensionlib.*]]with a similar[[project.extension-modules.*]]table (or any other name really that is not related to a single implementation).
Yup, that's my exact plan! This is just temporary until there's at least one PoC builder.
If that is the case, maybe it would be required to add a
configure(self, options: dict)method to theExtensionModulesinterface, so the build-backend can pass information used to derive the values ofinput_filesandoutput_files. Or simply make a spec for__init__(self, options: dict).
That's part of the build method https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.build
The
optionsargument, being an JSON/TOML-serializable dict, whose keys are a combination of arbitrarily defined by each implementation and that are written to the[[project.extension-modules.*]]table.
That's https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.config
@void vale wrt generated in sdist - cythonize comes to mind (when wanting to avoid cython as dep on the consumer)
ah interesting
borgbackup does that for example, the sdist for distros gets cythonized sdists that only need a c compiler
(its setup.py is horrendously painfull due to these details)
Yes, Cython is a good example. But I believe that other regular builds also have this property right?
For example, if someone writes an extension in C or C++, they probably want the C/C++ files to be in the sdist but not in the wheel.
If the backend can query for input_files, it will be able to exclude them from the wheel.
That's part of the build method https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.build
So is the idea here that you can only call thefilesmethod after you runbuild?
That's https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.config
My original phrase was more in the other direction. I imagined that it would be good for the backend to feed the information into the class (instead of retrieving it). But I understand if it is required to call thebuild()method first before accessing the other ones.
So is the idea here that you can only call the files method after you run build?
no anytime
Perhaps this should also live under pypa? ๐ค especially if hatchling will use it
@sonic scroll https://github.com/ofek/extensionlib/issues/6 ๐
Oh, I'd have accepted it from day 1, but does need a vote ๐คฃ
Yeah, I think after some initial implementation/testing phase it could be transferred
I think is fine to develop projects under pypa if we voted to accept it ๐
But seems Paul seems to disagree
OK, I maybe I am not understanding how this library is supposed to be used...
I was thinking that each extension module would be feed with the information of the TOML file, and then it would be able to produce a list of files. But if the information is provided only by the build method, then I don't know how it would be possible to list the files beforehand...
@lethal magnet it does as you thought, data of https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.build is for backends, https://ofek.dev/extensionlib/interface/#extension._interface.ExtensionModules.config is user config minus runner options https://ofek.dev/extensionlib/runners/#options
btw even though I'm ok with
The
BuildRunnerinterface is an implementation detail.
the runner options, imo, are required
In the docs you mentions as an example [[tool.extensionlib.spam]] and then says that the table is named after the plugin, which is the extension_modules entry-point, right? Then in your example of entry point you define an ExtensionModule as plugin.
So does it make sense to call these options runner options? Aren't they the options that the runner will pass through the ExtensionModule class anyway? In my mind on top of the enable-by-default and the force-rebuild options, these tables would also contain other parameters, like flags that can be passed to the compilers and (why not) files that will actually be compiled. which brings me to the following question
So if the options are not passed prior to calling build, does it mean that the implementation will either have to a) auto-discover which files have to be compiled or c) use a completely different file (e.g. Makefile) to find it out?
I see! Thank you very much @void vale. Sorry for the questions, I was trying to understand how the library is supposed to be used.
So the __init__ method is also part of the API for ExtensionModules, and the config gets passed through it. Thank you!
Thanks @void vale, I was just thinking that (if there is no requirement for inheriting from extensionlib), it might be possible for setuptools to export such an entry-point based on the existing code for Extension and build_ext... I have not investigated this deeply though.
I'll be catching up on this next week (PyCON till tomorrow afternoon was one solid mess of talks, meetings, and deadlines), but I'm strongly in favor of a Protocol over ABCs. If you look at UHI, that's a Protocol plus some helpers. Most users like the helpers and use UHI, but you don't have to, and I think extensionlib should be the same way.
pushed an update: big refactor + 100% test coverage
The toolkit for building Python extension modules
Hi, I just found out about this project, and the idea seems really interesting! Is there any way I can get involved and contribute? Thanks!
hey! right now it's still in the design phase but you could:
- ensure everything is type hinted https://github.com/ofek/extensionlib/blob/f807a759b8e560eba49e67b4d66eb28d25f94688/pyproject.toml#L58
- find out why Mypyc fails on macOS https://github.com/ofek/extensionlib/blob/f807a759b8e560eba49e67b4d66eb28d25f94688/.github/workflows/build.yml#L36
Do you think you'll infer whether an extension is a library based on its name or should there be [[project.libraries.foo]]?
what do you mean by library?
If you want to build a dynamic library instead of a normal extension you can import Library from setuptools.extension and use that in ext_modules instead of extension.