You may find this to be overkill, but you can in fact build a wheel which can then be used in all the normal ways (uploaded to PyPI, published as a release on GitHub etc.) You can do it all with built-in tools (except the uploads) following standards, and you can get a nice custom wrapper executable (on Windows, it will be an actual compiled .exe
file, although it still depends on the Python for which the wheel was installed).
Suppose we have a directory structure
camelot/
pyproject.toml
src/
camelot.py
with pyproject.toml
something like
[project]
name = "camelot"
version = "1.0.0"
scripts = { "holy-grail-camelot-scene" = "camelot:camelot" }
description = "It's a very silly place."
license = { text = "You must bring us a shrubbery!" }
[[project.authors]]
name = "Arthur, King of the Britons"
email = "king_arthur@royalty.co.uk"
(Disclaimer: please use a real license. Normally you would put license = { file = "LICENSE.txt" }
or similar, and include that file with the license.)
and src/camelot.py
def camelot():
print("It's only a model...")
if __name__ == '__main__':
camelot()
Now we can run (output redacted):
$ pip wheel .
Processing /path/to/camelot
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: camelot
Building wheel for camelot (pyproject.toml) ... done
Created wheel for camelot: filename=camelot-1.0.0-py3-none-any.whl size=1524 sha256=...
Stored in directory: /tmp/...
Successfully built camelot
Which we can test by making a local venv, activating it, and installing that wheel file into it:
$ python -m venv .venv
$ source .venv/bin/activate
(.venv) $ pip install camelot-1.0.0-py3-none-any.whl
Processing ./camelot-1.0.0-py3-none-any.whl
Installing collected packages: camelot
Successfully installed camelot-1.0.0
and try the wrapper:
(.venv) $ holy-grail-camelot-scene
It's only a model...
(The wrapper import
s the code, so that __name__
will be 'camelot'
, and not '__main__'
. However, it then explicitly calls the camelot
function itself - that’s what the :camelot
part is for in pyproject.toml
. Use .
s normally for the package/module “path”, and :
optionally to mark something inside the module to call. No arguments are passed; you’ll have to access and parse sys.args
yourself if you care about command-line arguments.)
This of course also allows library use:
(.venv) $ python
Python 3.8.10 (default, May 26 2023, 14:05:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import camelot
>>> camelot.camelot()
It's only a model...
and running as a module:
(.venv) $ python -m camelot
It's only a model...
and running as a script (if you can find it):
(.venv) $ python .venv/lib/python3.8/site-packages/camelot.py
It's only a model...
(Is this worth putting up as a guide somewhere?)