I think the documentation (or lack of documentation) can be very illuminating in the focus and though process of those behind a project. That there is concise "This is how you did it before and this is how it is done now" shows that there seemingly haven't been any though process in building that bridge. And in general it seems like the process of packaging projects that is not going to be published on pypi, but are going to be used internally is a dark spot as well.
Having the mess of different blog posts and documentation sources saying different stuff is far from ideal though. If you can't sum up the process in a clear concise way you are far from done.
I do not understand your comment. What module are you talking about?
What prevents the pip from a normal CPython installation from doing an install?
You can even bootstrap pip via the ensurepip module documented at https://docs.python.org/3/library/ensurepip.html which notes "This module does not access the internet. All of the components needed to bootstrap pip are included as internal parts of the package."
Here is a program which, when run, creates the zipfile "hello-1.0-py3-none-any.whl" containing the wheel for a package named "hello" along with an entry point for the command-line program also named "hello"
# Create a wheel for a 'Hello, world!' Python package.
#
# To install:
# pip install hello-1.0-py3-none-any.whl
#
# To use on the command-line:
# hello
#
# To use from Python:
# >>> import hello
# >>> hello.main()
# Hello, world!
#
# To uninstall:
# pip uninstall hello
import base64, hashlib, zipfile
package_name = "hello"
version = "1.0"
# This will go in hello/__init__.py
payload = """
def main():
print("Hello, world!")
"""
METADATA = f"""\
Metadata-Version: 2.1
Name: {package_name}
Version: {version}
Summary: Example of a hand-built wheel.
Home-page: http://news.ycombinator.com/
Author: eesmith
Author-email: eesmith@example.com
License: Public Domain
Platform: UNKNOWN
UNKNOWN
"""
# This causes the installer to create the command-line 'hello' program.
entry_points = """\
[console_scripts]
hello = hello:main
"""
WHEEL = """\
Wheel-Version: 1.0
Generator: eesmith_wheelgen (0.0.0)
Root-Is-Purelib: true
Tag: py3-none-any
"""
top_level = f"""
{package_name}
"""
def build():
wheel_name = f"{package_name}-{version}-py3-none-any.whl"
with zipfile.ZipFile(wheel_name, "w") as zip:
dist_info = f"{package_name}-{version}.dist-info"
# Add a file and build up information needed for the RECORD .
record_lines = []
def add_file(filename, content):
with zip.open(filename, "w") as f:
byte_content = content.encode("utf8")
f.write(byte_content)
digest = hashlib.sha256(byte_content).digest()
encoded = base64.urlsafe_b64encode(digest).rstrip(b"=")
record_line = f"{filename},sha256={encoded},{len(byte_content)}\n"
record_lines.append(record_line.encode("utf8"))
add_file(f"{package_name}/__init__.py", payload)
add_file(f"{dist_info}/METADATA", METADATA)
add_file(f"{dist_info}/WHEEL", WHEEL)
add_file(f"{dist_info}/entry_points.txt", entry_points)
add_file(f"{dist_info}/top_level.txt", top_level)
with zip.open(f"{dist_info}/RECORD", "w") as f:
f.writelines(record_lines)
if __name__ == "__main__":
build()
Having the mess of different blog posts and documentation sources saying different stuff is far from ideal though. If you can't sum up the process in a clear concise way you are far from done.