pypa / hatch

Modern, extensible Python project management
https://hatch.pypa.io/latest/
MIT License
6k stars 303 forks source link

Running hatch build doesn't produce any executables, only wheel and stdist. #1344

Open Day0Dreamer opened 7 months ago

Day0Dreamer commented 7 months ago

Good day, after following instructions in https://hatch.pypa.io/latest/plugins/builder/app/, which note that one must have Rust installed and having installed Rust, the hatch build command doesn't produce anything binary.

And yet there is no binary! Only classical wheel and source dist.

Now I'm at a loss. I'm running Hatch, version 1.9.4 and from lack of anything else to to, I must ask for help here.

Addendum After writing this, I got inspired to ask for help

P:\Python\test-rust-build>hatch build --help
Usage: hatch build [OPTIONS] [LOCATION]

  Build a project.

Options:
  -t, --target TEXT    The target to build, overriding project defaults. This may be selected multiple times e.g. `-t
                       sdist -t wheel`
  --hooks-only         Whether or not to only execute build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]
  --no-hooks           Whether or not to disable build hooks [env var: `HATCH_BUILD_NO_HOOKS`]
  --ext                Whether or not to only execute build hooks for distributing binary Python packages, such as
                       compiling extensions. Equivalent to `--hooks-only -t wheel`
  -c, --clean          Whether or not existing artifacts should first be removed [env var: `HATCH_BUILD_CLEAN`]
  --clean-hooks-after  Whether or not build hook artifacts should be removed after each build [env var:
                       `HATCH_BUILD_CLEAN_HOOKS_AFTER`]
  -h, --help           Show this message and exit.

then run hatch build -t app and it started chugging and hooching!

Still, having read the documentation on building I have no idea how to tell hatch to assemble the binary using pyproject.toml. Mere adding [tool.hatch.build.app] what the docs suggest does nothing.

~P.S.~ PTSD: I have extreme difficulties understanding the docs, and started comprehending anything but the basics only after listening to a great podcast episode featuring @ofek a while ago.

Day0Dreamer commented 7 months ago

Nevermind. I give up.

Better defaults Hatchling is all about providing the best possible defaults, even at the expense of backward compatibility...

@ofek

Even after running said command and seeing:

─────────────────────────────────────────────────────── app ────────────────────────────────────────────────────────
The `app` build target is deprecated and will be removed in a future release. Use the `binary` build target instead.
    Updating crates.io index
  Installing pyapp v0.15.1
    Updating crates.io index
   Compiling windows_x86_64_msvc v0.52.4
...
   Compiling dirs-sys v0.4.1
   Compiling pyapp v0.15.1
   Compiling fastrand v2.0.2
   Compiling clap v4.5.3
    Finished release [optimized] target(s) in 44.07s
  Installing C:\Users\Day\AppData\Local\Temp\tmpi97dbhu0\bin\pyapp.exe
   Installed package `pyapp v0.15.1` (executable `pyapp.exe`) warning: be sure to add 
`C:\Users\Day\AppData\Local\Temp\tmpi97dbhu0\bin` to your PATH to be able to run the installed binaries

happily launching the .exe and waiting for simplest helloworld to appear, I was greeted with:

>dist\app\test-rust-build-0.0.1.exe
ERROR: Could not find a version that satisfies the requirement test-rust-build==0.0.1 (from versions: none)
ERROR: No matching distribution found for test-rust-build==0.0.1

In the end my papercut runs as follows: It really doesn't feel to me, that the defaults are the best possible. At all.

Please address these issues. Thanks.

ofek commented 7 months ago

Did you read the documentation for PyApp?

Day0Dreamer commented 7 months ago

Partly. Only the Building page, because that was what I intended to do. Just Build. And they refer directly back to Hatch docs.

Given how they say:

For a more streamlined workflow, consider using the built-in app build target of Hatch.

I figured there is no point to venture to another documentation pages, and that is confirmed by following the instruction below. Firstly I tried Local repository and secondly Installation and halting at Enviroment Variable PROJECT_NAME is not set I came to the conclusion the PyApp documentation won't be of any help at all.

In other words, I figured that naked PyApp is looking for configurations that are already in place when ruinning hatch build and the deeper I'd delve in PyApp docs, the more I'll stray from finding a fix for the problem with running hatch build.

Was I supposed to read it after all?

ofek commented 7 months ago

The issue is that your test project is not on PyPI and therefore fails because by default it builds for releases. You would have to manually configure development builds by embedding a wheel or source distribution: https://ofek.dev/pyapp/latest/config/#project-embedding

Day0Dreamer commented 7 months ago

Thanks! This is now again very familiar. I'd need another couple of hours to start reading the PyApp docs from top to bottom to eventually stumble on that section.

Basically it's a strone throw's away from current building procedure with shiv that packages the venv with src into an archive and makes it python-launchable.

Would be tremendously helpful if that link was somewhere I could've found it in the beginning.

p.s. Been struggling with distribution of python-tools for coworkers within confines of a company, which means no PyPI sadly. Hopefully with Hatch I'll figure out automatic updates, and such. So far it has been a rocky ride without a clear vision of normalcy. Perhaps a private PyPI server, but that's more developer oriented, rather than useroriented.

Day0Dreamer commented 7 months ago

All right, this is going somewhere, but still not close to success. Trying to pack an actual project, with dependencies. Yet instead of using shiv, trying to build a binary. While the PyApp documentation tells me to configure everything with env vars. I can't find a combination that works.

Basically, requirements.txt doesn't work, because hatch run pip freeze gives me for my own module a github link. So I build a wheel, which gets fed to a project_path variable.

Now requirements.txt doesn't work even if I have just a oneliner with loguru there. I'm certain it gets read, because simply giving ./requirements.txt returns with It's not a file so I give it an absolute path and it stops giving me that error. Either way, nothing I put in requirements.txt makes any difference to my eye.

Nevermind, let's pack existing libraries from hatch's venv. That works, but the app I write is not present there. That is explained by the fact, that the source code is in the src folder, and not \Lib\site-packages\

So it's either - my code gets included, without any deps, or it's the deps get copied over but not my code.

To keep everything organized, I've written a build.py to chain everything together. Here's a chunk:

    subprocess.run("hatch build -t wheel", shell=True, check=False)
    os.environ["PYAPP_PROJECT_PATH"] = str(wheel_file.resolve())
    os.environ["PYAPP_PROJECT_NAME"] = "al_kassa"
    os.environ["PYAPP_PROJECT_VERSION"] = "0.1.3"
    os.environ["PYAPP_DISTRIBUTION_EMBED"] = "1"
    # os.environ["PYAPP_PROJECT_DEPENDENCY_FILE"] = r"P:\Python\al-kassa\binary_requirements.txt"
    os.environ["PYAPP_PROJECT_FEATURES"] = "loguru,pydantic,requests,pywebview,pydantic,pydantic[dotenv]"
    subprocess.run("hatch build -t binary", shell=True, check=False)

While I agree, to the fact that I don't understand a half of what's going in in the docs I don't see a way out of this mess.

ofek commented 7 months ago

What you have in the project features environment variable looks like dependencies rather than features.

Can you please show me your configuration file and also explain more precisely the behavior you're seeing? Uncommenting that dependency file environment variable should work, for example.

Day0Dreamer commented 7 months ago

Right here I tried to include a wheel and pack the dependencies. pyproject.toml.txt build_exe.py.txt log_try1.log

Day0Dreamer commented 7 months ago

Virtually the same result happens when there is no PYAPP_PROJECT_PATH specified. log_try2.log

Day0Dreamer commented 7 months ago

This one is a longer one. Here I try to feed it requrements.txt, it doesn't work straight away. You need absolute path (this time it worked woth backslashes. Prior to that I think I was using forward slashes and that may have caused errors? Not sure). Nevertheless, with this constellation I ended up with loguru and not the module I have written.

There are several dumps of build_exe.py script to show that the env vars were changed. And which one. log_try3.log

ofek commented 7 months ago

Try this: https://ofek.dev/pyapp/latest/runtime/#restore

Day0Dreamer commented 7 months ago

Gave it a shot. Still not finding the code. log_try3.1.log

Day0Dreamer commented 7 months ago

Not sure if that's a good thing, but I've managed to replicate everything in Ubunti @ WSL2.

requirements.txt let itself being read only if you Path("requirements.txt").resolve() it. No idea why it can't find it any other way.

ofek commented 6 months ago

Please provide a repository so that I may reproduce.

Day0Dreamer commented 6 months ago

Going to take some time, 4 AM here <3

johannesloibl commented 5 months ago

Maybe this Powershell script helps your a bit. I was creating it while learning PyApp before switching to the Hatch binary builder. It expects that you uploaded a wheel to a package index, but it works similar if you have the wheel already available offline.

param (
    [string]$package_spec,
    [string]$extras = ""
 )

# Replace dashes with underscores and strip off the version after ==, <, or >
$dist_name = $package_spec -replace "[<>=]=.*", ""
$project_name = $dist_name -replace "-", "_"
$package_name = $project_name -replace "ifx[-_]", ""
Write-Host "Dist Name: $dist_name"
Write-Host "Project Name: $project_name"
Write-Host "Package Name: $package_name"

$tmpwheel = "wheels"

# Clear the wheel folder if it exists
if (Test-Path -Path $tmpwheel) {
    Remove-Item -Path $tmpwheel -Recurse -Force
}

# Download the wheel
pip download $package_spec -d $tmpwheel --no-deps --only-binary :all:

# Find the downloaded wheel
$wheel = Get-ChildItem -Path $tmpwheel -Filter "$project_name*.whl" | Sort-Object CreationTime -Descending | Select-Object -ExpandProperty FullName -First 1
if (-not $wheel) {
    throw "Wheel not found!"
}
Write-Host "Using wheel: $wheel"

# https://ofek.dev/pyapp/latest/runtime/
# https://ofek.dev/pyapp/latest/config/
$env:PYAPP_PROJECT_NAME = $package_name
$env:PYAPP_PYTHON_VERSION = "3.11"
$env:PYAPP_PROJECT_FEATURES = $extras
$env:PYAPP_PROJECT_PATH = $wheel
$env:PYAPP_DISTRIBUTION_EMBED = "false"  # true -> embed Python distribution, false -> download indygreg distri
$env:PYAPP_FULL_ISOLATION  = "true"  # full isolation -> unpack distri, false -> create venv

# Display all environment variables that start with PYAPP
Get-ChildItem -Path env:PYAPP*

cargo install pyapp --force --root "dist/app"

Move-Item dist/app/bin/pyapp.exe dist/app/bin/$package_name.exe