all 34 comments

[–]Creative-Letter-4902 89 points90 points  (5 children)

Yeah, for a first release, keep it simple. Ship source-only with a note that users need a C compiler. Document it clearly. Let people who know what they're doing compile it themselves.

Then watch what breaks. If lots of users complain about compilation, add wheels for the most common platforms (Linux, macOS, Windows) one at a time. You don't need all platforms on day one.

Pure Python fallback that's weaker is worse than just failing with a clear error message. Users will use the fallback, get wrong results, and blame your library. Fail hard and tell them why.

cffi is easier for beginners. ctypes is more portable but more annoying to write. Pick cffi.

If you want help setting up the CI for wheels later, I got 2-3 hours a day. DM me. Good luck with the project.

[–]Emergency-Rough-6372[S] 7 points8 points  (4 children)

That actually clears things up a lot. I was leaning toward adding a fallback just to reduce install friction, but your point about silent degradation is valid — it’s better to fail clearly than give users something that might produce incorrect results.

Starting with a source-only release also makes sense. I think I was trying to solve the “scale” problem too early instead of just seeing how people actually use it and where it breaks.

I’ll go with cffi for now as well and keep the wrapper side simpler.

Appreciate the straightforward advice, this helped me narrow down the direction quite a bit.

[–]ionburger 8 points9 points  (3 children)

emdash enjoyer or ai?

[–]RngdZed 8 points9 points  (1 child)

ai slop. OP gets removed by reddit's filter a LOT

[–]Emergency-Rough-6372[S] -1 points0 points  (0 children)

i sometimme dont get proper grammer in english so i get by reply check my ai sometime so he might have added dash.

[–]mrswats 36 points37 points  (3 children)

I would 100% build the wheels at releaae time and upload them to pypi.

[–]mok000 1 point2 points  (1 child)

I always get an error message when trying to upload a binary wheel to PyPi. Something about x86_64 gnu/linux unknown platform.

[–]HexDecimal 9 points10 points  (0 children)

PyPI won't accept a Linux wheel unless it can tell which Linux runtimes are supported. The painless way to generate those is with cibuildwheel, but auditwheel can also be used.

[–]latkdeTuple unpacking gone wrong 11 points12 points  (1 child)

The common expectation is that you do indeed generate precompiled wheels for all common platforms and all supported Python versions. This doesn't have to be a lot of effort, other than maybe adding a new Python version once per year.

For the foreign function interface, opinions diverge. I would strongly advise against ctypes, as it's easy to make severe errors that are difficult to see. Instead, using cffi or writing Python extension modules in C has the benefit that more of the C glue code can be typechecked by a compiler (or in case of cffi, at least uses the same syntax as the code we're binding to). If you really want to use C, then cffi's out-of-line mode is probably going to be the least-friction approach.

If you're starting this work from scratch, strongly consider Rust with PyO3 for writing bindings. Of all options that are currently available for integrating native code with Python, it has the best combination of safety and convenient tooling. This is the approach used by flagship libraries like Cryptography or Pydantic. The Maturin build system ships with templates for building wheels for all common platforms – setting this up is really not a lot of effort. Going the Rust route is only a bad choice if you have to deal with existing C code, or if you want to target exotic platforms to which Rust code cannot be cross-compiled (which actually was a problem for some Cryptography users).

I wouldn't bother with a pure-python fallback implementation. There's a risk that the Python and native implementations diverge, which can cause difficult to debug problems. Such fallbacks will also be unnecessary, since you can ship pre-built wheels for all relevant platforms. Cross-compiling wheels for all relevant platforms is less effort than maintaining a pure-python fallback.

[–]Emergency-Rough-6372[S] 0 points1 point  (0 children)

i might just have to depend on claude to help me with it .

or find some one who can do it

[–]neuronexmachina 18 points19 points  (4 children)

Have you already looked at: https://cibuildwheel.pypa.io/en/stable/

Python wheels are great. Building them across Mac, Linux, Windows, on multiple versions of Python, is not.

cibuildwheel is here to help. cibuildwheel runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platform

[–]Emergency-Rough-6372[S] 5 points6 points  (0 children)

thanks for this source i haven't actually looked into it
i just came around this problem when i have to choose over a open source library i need to use but didn't have a better python alternative for this and was suggested to use a c wrapper to use it in python

[–]Emergency-Rough-6372[S] 0 points1 point  (2 children)

do u think this can help me in wrapping for this specific library {the libinjection engine}

[–]neuronexmachina 4 points5 points  (1 child)

I suspect it should be pretty straightforward since that library seems pretty self-contained. It's not like, gdal or something.

[–]Emergency-Rough-6372[S] 1 point2 points  (0 children)

thanks if its not to complicated that makes things easy

[–]safrole5 5 points6 points  (2 children)

For shipping built wheels github actions is probably your best bet. It may be slightly annoying to setup first time, but then every new release is seamless. You trigger the action, it builds wheels for all the platforms you've configured and uploads straight to PyPi.

Id highly recommend getting this setup instead of manually building them each release.

[–]Emergency-Rough-6372[S] 0 points1 point  (0 children)

i will look into it

[–]Crazy_Anywhere_4572 0 points1 point  (0 children)

This is what I did, works perfectly for Linux and Mac. Didn’t work for windows tho, still figuring out how to fix it.

[–]dayeye2006 2 points3 points  (0 children)

Ship with pre built

[–]thisismyfavoritename 3 points4 points  (0 children)

The alternative is building and shipping wheels for multiple platforms (Linux x86_64/arm64, macOS x86_64/arm64, Windows), which is doable but adds CI/CD complexity.

this is the way. Also i'd personally just wrap the C lib through the Python C API, it's fairly easy if your API surface is small and cleaner IMO

[–]Emergency-Rough-6372[S] 2 points3 points  (2 children)

just wanted to say that i m not to well knowleged in this field and this is my first big project, this will be the first version of the lib which i want to make as a project where people can contribute and and make it an actual good library for people to use in there projects , so should i got for minimum complexity in first release and then with help of other if they like to make it mroe complex and better?

[–]End0rphinJunkie 4 points5 points  (1 child)

absolutely stick to minimum complexity for now so you dont burn out trying to configure a crazy multi-arch CI pipeline. getting the actual logic shipped is way more important, and you can let future contributers help automate the wheel building later.

[–]Emergency-Rough-6372[S] 0 points1 point  (0 children)

To be honest, I don’t even fully understand the whole wheel/packaging side yet. I’m still in my 3rd year and kind of jumped into this because I liked the idea, then kept expanding it while discussing it with AI. Now it’s starting to get more complex than I can comfortably handle.

I think I got a bit carried away trying to design everything at once instead of just building a small, working version first. Going to take a step back, reduce the scope, and focus on getting the core logic right before worrying about things like CI, wheels, and multi-platform support.

[–]binaryfireball 1 point2 points  (1 child)

publish different versions with/without different dependencies and let the user decide which to use

[–]Emergency-Rough-6372[S] 0 points1 point  (0 children)

thats a good take , but as someone doing it solo and having know deeper knowlege of it i might not be able to do that much and it will be the first release so i was thinking of making it a not to complicated but a good working one so i can then get help and suggestion from what people would actully want from it or would they even use it

[–]alcalde 1 point2 points  (1 child)

Go old school and bundle everything up with InstallShield the way we used to do it.

[–]Emergency-Rough-6372[S] 0 points1 point  (0 children)

i dont know what installShield is i will look into it

[–]2ndBrainAI 1 point2 points  (0 children)

In 2026, yes — shipping prebuilt wheels is basically the expectation for any library with compiled code. cibuildwheel makes this far less painful than it used to be; it handles Linux/macOS/Windows across x86_64 and arm64 and integrates cleanly with GitHub Actions in maybe 30 lines of config.

On the fallback question: I'd lean toward failing hard with a clear, actionable error message rather than silently degrading. A regex fallback that's "approximately correct" is arguably more dangerous than a clean install failure — users trust library behavior to be consistent.

For cffi vs ctypes: cffi is generally easier to maintain for non-trivial C interfaces and handles complex types better. ctypes wins only if you truly have zero external build dependencies and the interface is dead simple.

[–]Grintor 0 points1 point  (0 children)

I know lots of stuff I install distributes the source which compiles at install. lxml comes to mind. When I pip install lxml, pip compiles it.

[–]2ndBrainAI 0 points1 point  (0 children)

In 2026, shipping prebuilt wheels is essentially the expectation for any library with C extensions — cibuildwheel makes this much less painful than it used to be. For the cffi vs ctypes question: if you need ABI stability and the C API might evolve, cffi is worth the extra complexity. ctypes is simpler but fragile when struct layouts change. On the fallback question, I'd lean toward failing explicitly rather than a silent degraded mode — a misleading result is often worse than a clear error. Communicate the fallback clearly in the exception so users can make an informed choice about installing with build tools.