Python is Actually Portable
ahgamut.github.io
ahgamut.github.io
Python 3.6.14+ (Actually Portable Python) [GCC 9.2.0] on cosmo
Type "help", "copyright", "credits" or "license" for more information.
>>: print("hello world") hello world
>>: import asyncio Traceback (most recent call last): File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named 'asyncio'
>>: import socket
>>: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>: s.connect(("www.google.com", 80))
>>: s
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.21.19', 63468), raddr=('142.250.70.196', 80)>
This work is going to be of interest to a lot of Python devs!
Edit: here's the link to the file: https://justine.lol/ftrace/python.com
Download it and see if it will run on your OS. I'm getting the standard prompt on every OS so far. On Windows you just type python.com. On Mac OS X I had to chmod +x, allow it in security settings, and use bash -c './hello.com' to execute it. Simple chmod +x python.com; ./python.com on Linux.
arch -x86_64 ./python.comOn my compiled python.com I try to open an app with a socket already in use:
./o/third_party/python/python.com run.py --strace
SYS 15995 443'293'703 read(3, [u"egister(self, selectors.EVENT_READ)◙◙ "...], 8'192) → 8'192
SYS 15995 443'440'589 close(3) → 0
self.socket.bind(self.server_address)
SYS 15995 443'471'588 write(2, u" self.socket.bind(self.server_address"..., 42) → 42
OSError: [Errno 98] EADDRINUSE/98/Address already in use
SYS 15995 443'523'517 write(2, u"OSError: [Errno 98] EADDRINUSE/98 /Addres"..., 57) → 57
SYS 15995 443'533'391 getenv("PYTHONINSPECT") → NULLI think some explanations might be helpful. I'm familiar with python, but not cosmopolitan libc.
The flask demo is unnaturally slow because my old computer is slow (running python tests in the background). plus I was using `_dummy_thread` Python3.6's pure-python threads implementation.
https://news.ycombinator.com/user?id=jart
Her comment history is full of good info about the project.
Cosmopolitan is a libc for that bundle [1], so that you can write your C code just the once, and then you have just the one output executable. No download-per-platform, or that's the goal.
Of course, I am sure some screen readers will simply take a custom text preprocessor that will turn l337sp34k and similar spellings like this one into regular text, but I am certain none of them have it as the default.
I use a magic string somewhere, which has control codes, three ASCII digits for versioning, and an RTL UTF-8 character, and is exactly eight bytes wide.
Anything munges my file, I'll know. Meanwhile I can just mask it.
I would not call it a shibboleth, but vague, generic terms are known to be for most purposes unsearchable, and having a really unique string identifier for your thing could make it more valuable.
It reminds me of this other HN link:
What other term would people suggest then? Surely it's a real concern for projects to be easily accessible by people looking for them.
DOS .COM programs were pure maching language programs with no OS-specifics like headers or syscalls (though running in real mode, they did expect BIOS interrupts), so that makes sense to me: they are trivially portable.
The most famous one was COMMAND.COM.
But to each their own.
(I think the most unfortunate thing is what content is present on the python.com domain, as a sibling comment highlighted)
And then, it's bad because a user trying to search for it using the universal search/URL bar in their browser can't just type that in and hit enter to search for it, they have to manually select that they want to search for it, or else they go to the site about starting your own camsite.
AND THEN... once they have googled for it... none of the results on the first page have anything to do with this program as far as I can tell.
So yeah, weird, obscure, confusing branding that makes the program hard to search for.
EDIT: I could be missing something? Does this program have a different name and python.com is solely the file name of the executable or whatever? It still seems like it could be more descriptively named.
This is a non issue.
But ouch, I haven't looked where python.com might be pointing to today, so that's indeed unfortunate.
As for search results, I think the most obvious candidate for bad naming is... the Go language. ;-)
Btw, this is part of the Cosmopolitan (referenced multiplatform libc implementation) monorepo, and only lives in the `third_party/python` directory. I am not sure if it's customary to name Cosmopolitan-linked binaries with a ".com" extension (perhaps it also alludes to the name "Cosmopolitan", but I guess then it'd be "cosm" or "cosmo"), but it definitely does not seem to be a public name you'd search for.
Searching for "python.com" (in quotes) on Kagi or Google returns a bunch of Python-related .com domains, whereas searching for "Actually Portable Python" returns the above site as the first hit — that's the name one should be looking under for this. Though I am sure it'd be nice if a more generic "multi-platform Python binary" also matched some of this (though platforms sometimes mean architectures as well).
Reading these comments about why it's named python.COM, yes that's pretty clever but it didn't even occur to me when I first read the post and I suspect the majority of people on hackernews had the same impression.
Go is definitely also bad naming, but the domain is golang.org and the canonical search term for the Go programming language is "golang".
In this day and age, we should probably be optimizing for clarity and searchability instead of "clever use of an outdated tech naming" which half of the engineers nowadays wouldn't even know about.
I am not disagreeing on the clarity and naming, but in this case, I don't think it's a big deal at all.
And how is headerless raw assembly that expects the bios interrupts to be usable "trivially portable"? If it was, it would run on modern amd64 OSs without emulation, but it doesn't.
The fact that system interfaces like BIOS interrupts may or may not work is insignificant when you start thinking of it as only a playful name.
And let me repeat a word of warning: this is only my guess, I am unrelated to the project.
Thing is, an APE is a COM file, not an EXE. Different headers, which contemporary Windows no longer cares about; but why lie about it? python.com is in COM format, as far as Windows is concerned.
Added for clarity: 'no longer cares about' meaning, Windows loads executables by checking the header for COM or EXE format and behaving accordingly, it's which of the two valid extensions gets used which it no longer cares about.
this is literally the problem
End user applications, sure… but the raw python interpreter? If you want it, you have to install it. If you want to install it, you go to python.org and install it.
This solves… a headache for the maintainers, having to build multiple packages?
Not having it installed is definitely not solved by this.
Bundling python packages into a single binary is definitely not solved by this.
If you want just the raw python interpreter, you're not the end user?
> Not having it installed is definitely not solved by this.
But not having to install it is the point?
> Bundling python packages into a single binary is definitely not solved by this.
It is? There's even a working example in the article?
mkdir -p Lib/site-packages
# specify python version to pip if necessary
/usr/bin/python2 -m pip download flask -t .
# start "build"
cp ./python.com ./my-release.com
printf '%s\n' '-m' >./.args
printf '%s\n' 'my_module_name' >>./.args
# wheels are just ZIPs, use unzip if pip complains
./python.com -m pip install flask*.whl -t ./Lib/site-packages
./zip.com -qr ./my-release.com ./Lib/site-packages/
./zip.com ./my-release.com ./.args
# optional cleanup
rm -rf ./*.whl ./Lib/site-packages/
./my-release.comThis makes what is already a pain in the ass (packing a python app into a single native distribution) harder to do, for the 'cute' ability to have a single cross platform binary.
...and it is cute. Sure, I get it. ...but yeah, you just zip up all the python dependencies and it's fine? So good. Compile all the native parts or whatever, I'm sure it's easy.
Look, I tell you what, you go and prove that all the smart people who've been trying to solve this for the last 10 years that there's a new good way of doing it, and they (and I) will all be very happy.
Until then, this is a proof of concept for a trivial use case that makes packaging a trivial pure python application somewhat harder to do than it was already.
> It is?
It is not.
Bundling python applications into a native binary is already a badly-solved problem with a bunch of irritating problems and caveats; this doesn't add anything meaningful to those existing efforts or fix any of the issues they have.
Seriously; go and look at the efforts those projects go to, to make what they do, work.
Do you not consider the end-user not having to install anything useful?
The entire point of APE is the portability, hence the name.
But the availability of other options with tradeoffs doesn't answer my question as to why this method is just a bar trick.
A Go binary or similar is an option, but it doesn't make other options mute.
Why does the interpreter need to be OS-specific at runtime?
If you're "bundled with the application itself", and your application is OS-specific anyways, then you should of course bundle a optimized OS-specific interpreter.
But, for an actually portable application, that runs on several OSes, you probably want an actually portable interpreter?
> And it doesn’t have to be “installed”, nix solves this problem by nix-shell -p package “installing” the package temporarily, making it available in the shell only.)
`nix-shell -p package` is a very bad example of your point.
* It represents a "not bundled"/"not embedded" use case;
* You "just" need to install Nix, then;
* You "just" need to invoke Nix using the correct derivation, then;
* Nix "just" need to temporary install the native interpreter and all other dependencies specified in the derivation, then;
* Nix "just" need to temporary install your packages and all other dependencies specified in the derivation in a temporary location.
all in order to not need to have it to not be installed.
Even as someone that likes Nix, this doesn't make a lot of sense for the sake of argument.
However to wish for that to happen on the same level as wishing that Linux was the dominant OS for personal computers, which would render APE mostly moot except for a few specialized cases.
But agreed graphical use cases are limited.
Its a lot more then that. Look at the code. https://github.com/jart/cosmopolitan You compile an executable with flags --nostdlib and --nostdinc because it includes cross platform standard libraries mapped to the OS specific magic numbers for system calls.
Think about a desktop computer with a graphics card. You can install either windows or linux on it. In both cases stuff gets rendered to the screen using the graphics cards. That is done through the driver, which maps higher level commands in libraries to instructions that graphics cards understands. While library format, is OS specific, the driver is essentially doing either ioctls or memory mapped io, both of which are the same x64 instructions.
So compiling everything with APE, means that library entry points are platform agnostic, and everything up the chain all the way to the game engine.
And, yes cosmopolitan only works on very basic things, as would be expected by the small number of people developing it, thats why I said IF it was adopted widely and developed further it would be possible.
Which is a very highly privileged thing to do, that you can’t just circumvent. No mainstream OS does userspace drivers.
And honestly, now you just introduce two levels of abstraction - one at the APE level, and one at Python’s level, so I don’t get that.
Also, that’s why we have cross-platform libraries. The executable-header is hardly the “bottleneck” in cross-platform tooling - that’s why I think it is hardly more than a very cool and genius hack.
once this gets threads, TLS and a recent Python version, I can see it becoming a non-toy fast. well done!
I think it's pretty much a unique feature of APE, because APE binaries modify their own code after the first run to nativize themselves. (Right? Or do the new versions work different?)
I can't tell if my disdain for this is real (Since I really like having my exes read-only and having a consistent hash) or just sublimated envy (Since most desktops will in practice have every file marked as read-write, and if it works it's not stupid)
Any problems still haunting pyinstaller and co. at this point will also show up for APEs, and solving them when you only have a lowest common denominator toolset at disposal will be harder.
I like APEs btw.
Like after I wrote this blog post, I wrote a small Python app using Flask/SQLite/D3.js, put everything in a single file, just copied it onto computers with Debian/Fedora/FreeBSD/Win10, and now I just access the info I need on those systems via the browser. Providing a browser GUI makes it easier for my colleagues as well, some of whom don't use terminals at all :)
For C extensions (where I've stubbed my toes quite a bit with PyInstaller), I think a nice goal with APE Python would be to have a website like Christoph Gohlke's (https://www.lfd.uci.edu/~gohlke/pythonlibs/), where you just select the packages you would like, and you download them compiled as a single-file executable you can use anywhere. This is an interesting problem, and there's been some work towards solving it, let's see if we can have an elegant solution.
Have you done a study?
This reminds me of when I've seen people say things like, "X is a showstopper for me when it comes to C++ [or whatever]", and someone responds, "That's not a problem in practice. People who write C++ don't care about X". And it's like, "Well, yeah, that's the nature of 'showstoppers'—all the people who do care about X noped out a long time ago." It's an intellectually dishonest way to justify choosing not to confront the X problem.
TLS will probably happen first -- Cosmopolitan already has mbedTLS for redbean, so a modified `_ssl.c` should get us most of the way there.
Recent Python versions ... what version is Python on right now? 3.10? (3.11 apparently) I picked 3.6 at the time because Cosmopolitan Libc did not have threads then (it does now, pthreads API is being filled as of this writing), and also because it wouldn't have too much churn. Perhaps after 3.11 gets stable I'll port it to use Cosmopolitan Libc.
I like the idea of combining this with pyScript to create portable “GUI” apps. Use this for a local server, open the users browser and point it at it. Websockets/pickle to bridge to a UI written in pyScript.
Does cosmopolitan have an API to open the users default browser? I know Python itself does…
I think it's only dependencies that contain C code that would need further changes. I think you would have to recompile then with cosmopolitan libc.
Correct me if I'm wrong
APE files (usually using a `.com` extension) are single executable files that can be run natively on many major operating systems. Kind of a "polyglot" executable file that most major operating systems just understand natively.
The implication is that you can now package Python code as a single file that can be directly run regardless of the operating system the user is on. No installers, no dependencies on what must already be installed, no separate downloads for different operating systems. Just one single file, click and run.
systemctl stop systemd-binfmt" on Manjaro
I just followed the directions. build/bootstrap/make.com -j$(nproc) o//third_party/python/python.com
I tested it on Win10, MacOS Monterey, and Linux Manjaro (built on MacOS and Linux), all worked on all platforms.Edit: thank you @dang! That was super fast!
> Cosmopolitan Libc does come with some tradeoffs as well > (static compilation, C codebases, no multithreading > (Update 2022-07-27:, no multithreading yet, > the pthreads API takes a while to fill
Toy.
> Regarding python packages with C extensions, the build > process for adding them to the APE is rather unwieldy > (2022-07-27 adding C extensions to the APE is much more > elegant now because of the Cosmopolitan monorepo
Horrific malware. Unchecked C (even libsafe) is bad.
Edit: Nevermind that each app using this software will need to be individually updated every time a new security patch for Python comes out.
Toys are still fun to play with :)
What we need instead is better support in Linux for legacy Linux and Windows applications so that Linux could run any proprietary software written for it or for Windows (because there are no sources for it and it cannot be recompiled). Today it is not so: for example, very old Firefox builds don't run or crash on modern Linux.
Can you see how the problem that you described in the subsequent paragraph could easily be solved if the applications were compiled in such a format?
APE format doesn't solve the problem with shared libraries changing over time, it dosn't solve the problem that different plaforms provide different APIs (for example, windowing APIs, audio APIs etc).
APE doesn't use shared libraries at all and it's intended to be statically linked?
> APE (...) dosn't solve the problem that different plaforms provide different APIs
APE provides the most important POSIX APIs (or shims) for every platform it supports.
> What we need instead is better support in Linux for legacy Linux and Windows applications (...) Today it is not so: for example, very old Firefox builds don't run or crash on modern Linux.
But that is the exact reason why "legacy Linux and Windows" doesn't work, and why APE works: you should target the "specific architectrure" directly instead of hopefully trusting that the entire underlying system is a completly static target.
That or using containers/shim layers, those of course bringing such entire underlying system with them.
I think WASM is a much more promising avenue for true cross platform executables. You don't have the architecture issue and aren't using weird old formats.
It has some way to go though.