beeware / briefcase

Tools to support converting a Python project into a standalone native application.
https://briefcase.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
2.48k stars 353 forks source link

`briefcase dev` does not always install required dependencies #1733

Closed mwchase closed 2 months ago

mwchase commented 2 months ago

Describe the bug

I managed to get briefcase into a state where my app is missing dependencies like toga, but briefcase dev does not reinstall, and simply fails.

Steps to reproduce

  1. Follow the tutorial at https://docs.beeware.org/en/latest/tutorial/tutorial-1.html, but use pipx to install briefcase instead of explicitly managing a virtual environment
  2. Before running briefcase dev, see what's in the virtual environment currently (via pipx runpip briefcase freeze)
  3. Run briefcase dev (and rerun pipx runpip briefcase freeze)
  4. Uninstall briefcase
  5. Reinstall briefcase
  6. Run briefcase dev

Expected behavior

The first briefcase dev run installs dependencies and runs successfully. The second such run should do the same.

Screenshots

No response

Environment

Logs

First freeze output:

arrow==1.3.0
binaryornot==0.4.4
briefcase==0.3.17
build==1.2.1
certifi==2024.2.2
chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
cookiecutter==2.6.0
gitdb==4.0.11
GitPython==3.1.43
idna==3.7
Jinja2==3.1.3
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
packaging==24.0
platformdirs==4.2.0
psutil==5.9.8
Pygments==2.17.2
pyproject_hooks==1.0.0
python-dateutil==2.9.0.post0
python-slugify==8.0.4
PyYAML==6.0.1
requests==2.31.0
rich==13.7.1
setuptools==69.0.3
six==1.16.0
smmap==5.0.1
text-unidecode==1.3
tomli_w==1.0.0
types-python-dateutil==2.9.0.20240316
urllib3==2.2.1
wheel==0.42.0

Second freeze output:

arrow==1.3.0
binaryornot==0.4.4
briefcase==0.3.17
build==1.2.1
certifi==2024.2.2
chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
cookiecutter==2.6.0
gbulb==0.6.4
gitdb==4.0.11
GitPython==3.1.43
idna==3.7
iniconfig==2.0.0
Jinja2==3.1.3
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
packaging==24.0
platformdirs==4.2.0
pluggy==1.4.0
psutil==5.9.8
pycairo==1.26.0
Pygments==2.17.2
PyGObject==3.48.2
pyproject_hooks==1.0.0
pytest==8.1.1
python-dateutil==2.9.0.post0
python-slugify==8.0.4
PyYAML==6.0.1
requests==2.31.0
rich==13.7.1
setuptools==69.0.3
six==1.16.0
smmap==5.0.1
text-unidecode==1.3
toga-core==0.4.2
toga-gtk==0.4.2
tomli_w==1.0.0
travertino==0.3.0
types-python-dateutil==2.9.0.20240316
urllib3==2.2.1
wheel==0.42.0

Final dev output:

[helloworld] Starting in dev mode...
===========================================================================
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/home/maxchase/beeware-tutorial/helloworld/src/helloworld/__main__.py", line 1, in <module>
    from helloworld.app import main
  File "/home/maxchase/beeware-tutorial/helloworld/src/helloworld/app.py", line 4, in <module>
    import toga
ModuleNotFoundError: No module named 'toga'

Problem running app helloworld.

Log saved to /home/maxchase/beeware-tutorial/helloworld/logs/briefcase.2024_04_15-18_26_06.dev.log

Additional context

In a sense, a lot of this is self-inflicted, but it's weird that I was able to self-inflict it. I ended up going down this path because I wanted to see what would happen if I avoided making a virtual environment explicitly, and simply relied on other tools to manage it. But when I saw that it was installing packages in its virtual environment, I didn't want just one briefcase install.

I feel like what briefcase is doing here is at odds with the direction the Python ecosystem is going. Lots of tools and projects are focused around the idea of lockfiles or explicit requirements, and briefcase dev just kind of YOLOs its own virtual environment. Are there any tools beyond just virtualenv/venv that briefcase dev is meant to work with?

(Also, how is it getting into a state where it won't try to install the dependencies? I "fixed" it by blowing away the project directory and creating it from scratch...)

freakboy3742 commented 2 months ago

Thanks for the report.

Briefcase only automatically installs dependencies on first run, which is identified based on whether the app's dist-info has been written. By recreating the virtual environment, you've removed all the dependencies, but you haven't removed the file that Briefcase is using to identify whether the project has been executed previously.

The documented behavior/workaround for this is to run briefcase dev -r, which will force a re-install of all declared dependencies.

Briefcase isn't YOLOing anything, and it definitely isn't creating its own virtual environment (at least, not for the dev case). It expects you to provide the virtual environment that the dev environment will use; and it provides a mechanism (which it uses automatically on first run) to install everything that you've declared as a requirement. It doesn't do this on every run because you should usually be running the project a lot more often than you update dependencies for your project.

The underlying issue to be addressed here is part of #881, which is currently being worked on as part of #1714.

I'm going to close this ticket on the basis that it's halfway between "documented behavior" and "edge case we're aware of".

mwchase commented 2 months ago

Just to clarify, by "its own" environment, I meant the one that pipx set up for it. I was thinking "oh, can I have briefcase as a globally available command?" and right now, I think the answer is "not with pipx, at least", because the environment that pipx sets up is going to end up just having the requirements of every project installed in it.

And if I set it up with Poetry or PDM, I'm skeptical it would work out much better; I haven't tried, but it looks like it would go:

freakboy3742 commented 2 months ago

Just to clarify, by "its own" environment, I meant the one that pipx set up for it. I was thinking "oh, can I have briefcase as a globally available command?" and right now, I think the answer is "not with pipx, at least", because the environment that pipx sets up is going to end up just having the requirements of every project installed in it.

Unless I'm missing something, that's completely backwards. If you have a global pipx virtual environment, and you create a new project, the first time you run briefcase dev you'll get all the requirements for that new project installed into your global pipx environment.

The issue you've experienced is different - you created a new virtual environment on an existing project, and that is what has confused Briefcase's auto-install tooling. But, as I mentioned, that's essentially documented behaviour with a workaround of briefcase dev -r.

  • The installed libraries are now out of sync with the lockfile

The use of lockfiles is completely different issue (see #476). There are at least 2 problems here:

Firstly, there isn't a standard lockfile format across the Python ecosystem. This means Briefcase would either need to support every competing lockfile format, or pick a winner and recommend the use of that one tool. Neither of those are especially appealing options to me as a project maintainer (especially as I'm not a user of PDM or poetry myself).

Secondly, to some extent, PDM, Poetry et al are competitors to Briefcase, because they're trying to manage project requirements; but to the best of my knowledge, none of the existing toolchains or lockfile formats are able to handle Briefcase's use case. The key missing feature is support for cross-platform builds - i.e., building on machine X for deployment on machine Y, recognising that the wheels/hashes for machine Y will be different to the ones on machine X.

I'm sympathetic to the dependency locking use case - that's why #476 is open as a feature request - but it's not a use case we currently support.

mwchase commented 2 months ago

So, I'd like to focus on the pipx side of things... The reason I ended up in this situation is that I saw briefcase installing project dependencies in ~/.local/pipx/venvs/briefcase, and thought "This doesn't seem right, what if I have briefcase projects with incompatible dependencies?" and then messed around a bunch.

freakboy3742 commented 2 months ago

I'm not sure how to respond to this. You've installed Briefcase using a global virtual environment, and then you're concerned that briefcase is... using a global environment? This seems a little like a "Doctor, it hurts when I do this"... "well don't do that" conversation. 😄

Virtual environments literally exist to provide per-project isolation. The BeeWare tutorial explicitly directs you to create a virtual environment, and tells you why you should do this. If you're actively choosing to not follow this advice, then, frankly, you reap what you sow.

And then you "messed around a bunch", and are surprised that Briefcase isn't responding in an optimal (to you) way?

briefcase dev is a developer shortcut. It's designed to make it easy to do a quick check of your code, in an environment that will broadly resemble what will happen in the full packaged form. To do that, it makes some compromises. One of those compromises is to use the currently active environment to run the code.

FWIW: We have, at various points in the past, contemplated modifying briefcase dev to use an isolated virtual environment that only includes the declared dependencies. However, those conversations haven't really progressed, because (a) it introduces a whole new breed of "but I've installed X using pip/pdm/poetry, why can't my app import it" questions, and (b) briefcase dev is, as I've said, a developer shortcut, not an attempt to build a robust replica of the packaged environment. We might revisit this in the future, but at least for now, the behaviour of briefcase dev is documented and entirely predictable.

So - what specifically are you arguing for/against?

mwchase commented 2 months ago

My hope was that there'd be some kind of isolation, like how various PEP 517 builders have isolation for build-time requirements. Of course, this isn't a build situation, so... I'll get back to you if I can think of anything else to say that I find satisfying.