Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: ModuleNotFoundError: No module named 'NanoVNASaver._version' when compiling using the uv python package mananger #759

Open
xros opened this issue Jan 19, 2025 · 13 comments
Labels
bug Something isn't working

Comments

@xros
Copy link
Contributor

xros commented Jan 19, 2025

Bug Report

NanoVNA-Saver version:
main branch at the time of writting

Current behavior:

ModuleNotFoundError: No module named 'NanoVNASaver._version'

Before we used pip install -r requirments.txt to install default packages needed for nanovna-saver, but @redrathnure changed to package manager uv https://github.com/astral-sh/uv from this pull-request: #752

I don't use uv to create virtual enviroment, because I need 2 defined virtual enviroment for building binaries for both ARM64 macOS and X86_64 macOS

In a pre-configured python virtual enviroment for macOS ARM64, and another virtual environment for macOS x86_64

uv pip sync pyproject.toml It automatically installed some packages in my defined virtual enviroment, but when I started to test the app using python nanovna-saver.py it had this bug

  File "/Users/someone/joy/X_proj/nanovna-saver-dev/nanovna-saver/src/NanoVNASaver/app_version.py", line 9, in <module>
    from ._version import version
ModuleNotFoundError: No module named 'NanoVNASaver._version'

I read the doc https://github.com/NanoVNA-Saver/nanovna-saver/blob/main/docs/DEVELOPMENT.md, and tried to manually install the packages by switching back to pip by doing this uv pip compile pyproject.toml -o requirements.txt. This will create the file requirements.txt back. Then I tried with pip install -r requirements.txt, then tried to re-run the app python nanovna-saver.py and it had the same error.

The error ModuleNotFoundError: No module named 'NanoVNASaver._version' pointed to this file https://github.com/NanoVNA-Saver/nanovna-saver/blob/main/src/NanoVNASaver/app_version.py#L9. I think it's because I didn't use uv to create a virtual enviroment in .env folder and other auto-machines were stopped.

I used this method I wrote here for building apps https://github.com/xros/nanovna-saver/wiki/Making-apps-for-MacOS for different architectures of macOS including ARM64 and x86_64 on 1 Apple Computer.

By using 2 different pre-defined virtual enviroments, I don't need to re-install all the packages again everytime in the ONLY 1 folder .env (which uv creates) for us, and it's fast when building apps

By using the uv run NanoVNASaver, the file _version.py will be automatically generated as this

# file generated by setuptools_scm
# don't change, don't track in version control
TYPE_CHECKING = False
if TYPE_CHECKING:
    from typing import Tuple, Union
    VERSION_TUPLE = Tuple[Union[int, str], ...]
else:
    VERSION_TUPLE = object

version: str
__version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE

__version__ = version = '0.6.8.post1.dev18+gbce1fa4.d20250119'
__version_tuple__ = version_tuple = (0, 6, 8, 'dev18', 'gbce1fa4.d20250119')

Expected behavior:

python nanovna-saver.py should run without error

Steps to reproduce:

As above

Related code:

file https://github.com/NanoVNA-Saver/nanovna-saver/blob/main/src/NanoVNASaver/app_version.py#L9

import importlib.metadata

APP_VERSION = "unknown"
try:
    # Change here if project is renamed and does not equal the package name
    APP_VERSION = importlib.metadata.version("NanoVNASaver")
except importlib.metadata.PackageNotFoundError:  # pragma: no cover
    # Looks like we neded this case for apps out of pyinstaller packages
    from ._version import version

    APP_VERSION = version

Other information:

Can someone come up with a method which can keep that using pip as package manager is still available for some cases and not using uv for creating the python virtual environment?

Yes I know uv works, but can we still be able to use pip as an option?

Here I address why uv sync should create 2 different virtual enviroments in different folders for building packages for different architectures so that I don't need to manually delete the folder .env everytime I build apps for arm64 macOS and x86_64 macOS.

Image

@xros xros added the bug Something isn't working label Jan 19, 2025
@redrathnure
Copy link
Contributor

redrathnure commented Jan 20, 2025

_version.py is generated by setuptools_scm package and it is not related to venv or package installation. You may run 'python -m setuptools_scm' inside venv or check documentation.

About requerements management, 'uv export' may be used to form requirements.txt file, then you may use "usual" pip/pip3 to prepare venv "manually". 'uv pip instal' may also work and be more faster option, however I am not sure how it manage multiarch configurations.

@xros
Copy link
Contributor Author

xros commented Jan 20, 2025

_version.py is generated by setuptools_scm package and it is not related to venv or package installation. You may run 'python -m setuptools_scm' inside venv or check documentation.

About requerements management, 'uv export' may be used to form requirements.txt file, then you may use "usual" pip/pip3 to prepare venv "manually". 'uv pip instal' may also work and be more faster option, however I am not sure how it manage multiarch configurations.

Exactly. uv pip install works fine if you only build for 1 arch. the file _version.py can be auto-generated. But if I want to build for multiarch on 1 computer, the venv virtual environment should be replaced too. I'm finding a method to alternate the virtual environment for multiarch

@redrathnure
Copy link
Contributor

https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path may be helpful for this case. Seems it posdible to change location or venv using env vars. However I still not sure how uv handles multiplatform builds (e. g. will it choose right wheel pakage to install)

@xros
Copy link
Contributor Author

xros commented Jan 20, 2025

https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path may be helpful for this case. Seems it posdible to change location or venv using env vars. However I still not sure how uv handles multiplatform builds (e. g. will it choose right wheel pakage to install)

Currently I delete the .venv manually for different architectures everytime. https://github.com/xros/nanovna-saver/wiki/Making-apps-for-MacOS#1-manually-remove-the-env-enviroment-everytime-you-change-architecture

But the uv project claimed there's not a way to define multiple persistent environments. from astral-sh/uv#7778

The uv binaries are in different architectures. And if I don't want to pollute my original Python enviroment, I would have to manually firstly create a virtual enviroment A, then use the A to install uv binaries, then with uv create another virtual enviroment under folder .env B which is unpleasant.

@redrathnure
Copy link
Contributor

Question/idea: do you need to use UV to build application package at all?

In theory it should be possible to build python package by uv build, which is cross platform. And then install it into the empty/multi-arch/special venv (just pip install package_file command). This should install nanovna-saver together with all runtime dependencies without need to mess with dev dependencies and all related tools. And then it should be possible to run pyinstaller in the prepared/multi-arch venv. It scans all dependencies anyway, so should not be big difference between raw sources from src dir and installed package (which at the end the same sources but in different location).

@xros could you try it? Would it simplify macos package building process?

@xros
Copy link
Contributor Author

xros commented Jan 20, 2025

@redrathnure

Question/idea: do you need to use UV to build application package at all?

Yes I tried to use uv build for building packages on different platforms including arm64 macOS and x86_64 macOS. And it works but not as what you might think. I had to manually delete the virtual environment .venv everytime when switching to another architecture. Because the "uv" binary you installed via pip install uv on your computer is only working in either arm64 or amd64 mode. If you want to change the architecture, you would need another "uv" binary built for another architecture. "uv" the project itself won't support swtiching architecture and it can NEVER do it.

As for the Python interpreter, I used one Interpreter compiled with 2 different architectures. https://www.python.org/downloads/macos/ , download the universal2 version which has both arm64 and x86_64 .

file `which python3`
/Library/Frameworks/Python.framework/Versions/3.13/bin/python3: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64:Mach-O 64-bit executable arm64]
/Library/Frameworks/Python.framework/Versions/3.13/bin/python3 (for architecture x86_64):	Mach-O 64-bit executable x86_64
/Library/Frameworks/Python.framework/Versions/3.13/bin/python3 (for architecture arm64):	Mach-O 64-bit executable arm64

I need this multi-arch Python3 for building packages for multi-arch macOS in different shell: https://github.com/xros/nanovna-saver/wiki/Making-apps-for-MacOS

Don't use the homebrew version of Python since it's ONLY support 1 architecture. Or you need to install 2 (different architectures) Python interpreter using homebrew which is not wise and waste of resources.

The Python3.9 interpreter macOS 15.2 has is too old for this project which needs Python3.10+.

If you build apps using uv. Firstly you need to install uv in 1 virtual environment A. Then for runnings some tasks using the uv , the uv itself will create another virtual environment B based on A, which is a waste.

If you want to build for different architectures using uv, you need to install uv in 1 virtual environment C, then uv will create another virtual environment D based on C. Now you have 4 virtual environments...

Previously, we used pip, you only need 1 virtual environment for 1 architecture. 2 virtual enviroments for different architectures.

@xros
Copy link
Contributor Author

xros commented Jan 20, 2025

Right now I think the work-around would be that find a method to automatically change the .venv when using uv.

Such as

# build 1 (on arm64)
# do something in shell, change to arm64 shell
uv sync                # this uv is in arm64 binary
uv build xxxxx
# build 2 (on x86_64)
# do something in shell, change to x86_64 shell
mv .env .env_arm64
uv sync                # this uv is in x86_64 binary
uv build xxxxx
# build 3 (on arm64)
# then switch to another architecture, do something in shell, change to arm64 shell
mv .env .env_x86_64
ln -sf .env_arm64 .env
uv sync                # this uv is in arm64 binary
uv build xxxxx

uv is not like pip which is fully written in Pythonic way, so you will have to change to different binaries everytime. You may think pip is slower when building for this project. But it won't affect too much. And if uv still can't come up with a method to deal with packaging for different architectures. It won't be very useful as pip.

Suppose if you want to build Linux binaries for arm64 platform on a x86_64 Linux. For cross-platform compiling, you would need to install different uv in 4 different virtual machines. With pip it only needs 2 virtual machines.

@redrathnure
Copy link
Contributor

I still not sure why do you need to swap dirs manually. E.g. following code should work if you need different venv:

export UV_PROJECT_ENVIRONMENT=.env_arm64
#uv sync && uv build
# or just
uv build

export UV_PROJECT_ENVIRONMENT=.env_x86_64
#uv sync && uv build
# or just
uv build

More details is here

And if uv still can't come up with a method to deal with packaging for different architectures. It won't be very useful as pip.

Just FYI uv is not for building. pip is not for building too. This project use setuptools (*.gz and wheel) to prepare python packages and pyinstall to prepare binary distributions.

Plus keep in mind, the project does not have any platform specific libs or native bindings, and result package has a valid meta information. So you may build source package, move it to any platform and prepare venv with whatever tool do you prefer. This means you do not need any uv' or 'uv pip at all, you do not need to rebuild the package (uv build) per each arch nether. Just prepare right venv, install the package (the rest will be installed as dependency) and run pyinstaller.

@xros
Copy link
Contributor Author

xros commented Jan 21, 2025

I think because you didn't try, that's why you don't know why.

If you have M chip mac computer by side, you can try.

Here's the error that uv returns

(env_313_x86_64_macos) MacBookPro:nanovna-saver $ mv .venv .venv_x86_64
(env_313_x86_64_macos) MacBookPro:nanovna-saver $ export UV_PROJECT_ENVIRONMENT=.venv_x86_64
(env_313_x86_64_macos) MacBookPro:nanovna-saver $ uv run NanoVNASaver -d
warning: `VIRTUAL_ENV=/Users/someone/nanovna-saver-dev/env_313_x86_64_macos` does not match the project environment path `.venv_x86_64` and will be ignored
error: Failed to spawn: `NanoVNASaver`
  Caused by: No such file or directory (os error 2)

I thought that's the error from system parameters. So I changed to the full path
export UV_PROJECT_ENVIRONMENT=`pwd`"/.venv_x86_64" but it still has the same error does not match the project environment path .venv_x86_64 and will be ignored

As I shared with you from here #759 (comment)

https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path may be helpful for this case. Seems it posdible to change location or venv using env vars. However I still not sure how uv handles multiplatform builds (e. g. will it choose right wheel pakage to install)

Currently I delete the .venv manually for different architectures everytime. https://github.com/xros/nanovna-saver/wiki/Making-apps-for-MacOS#1-manually-remove-the-env-enviroment-everytime-you-change-architecture

But the uv project claimed there's not a way to define multiple persistent environments. from astral-sh/uv#7778

The uv binaries are in different architectures. And if I don't want to pollute my original Python enviroment, I would have to manually firstly create a virtual enviroment A, then use the A to install uv binaries, then with uv create another virtual enviroment under folder .env B which is unpleasant.

From uv developers: there's not a way to define multiple persistent environments.

The do-able method I found is to either delete the .venv or move it somewhere and create a softlink to it, like what I did above.


FYI, I know uv is not for building, maybe there's some misunderstandings here. I use pyinstaller for building apps from commands in terminal sometimes or some CI. I saw your previous commit which removed the file requirements.txt entirely which made me feel odd. They can exist together.

Certainly we can get the file requirements.txt back by uv pip compile pyproject.toml -o requirements.txt.

Here I address that if you want to get requirements.txt, then you'll have to install virtual enviroment A, then in A install uv. Then uv will create another virtual enviroment B for development purposes. Of cause we don't need to use uv, but where is the requirements.txt in the repo now?

I think uv is cool but we can still have something traditional as well.

For buildings app for mutiple architectures on different OS, the simpler the better. Especially for small project.

@xros
Copy link
Contributor Author

xros commented Jan 21, 2025

Just as I tried

From uv developers: there's not a way to define multiple persistent environments.
The do-able method I found is to either delete the .venv or move it somewhere and create a softlink to it, like what I did above.

I tried once again. UV_PROJECT_ENVIRONMENT has nothing to do with virtual environment. when you run applications. uv run NanoVNASaver -d, it ONLY looks for folder .venv! And the uv develpers won't care till now.

@redrathnure
Copy link
Contributor

Looks weird, I will try "custom" venv location on M3 laptop, however on win machine it works without any issues ;(

Here I address that if you want to get requirements.txt, then you'll have to install virtual enviroment A, then in A install uv. Then uv will create another virtual enviroment B for development purposes. Of cause we don't need to use uv, but where is the requirements.txt in the repo now?

Perhaps we need to export requirements.txt as part of build process and put it under git control to simplify scenarios like you have.
But I still not sure that you really need it. After uv build you would have python package (./dist/*.gz) which internally has reference to all requirements. Why just do not install the package instead playing with requirements.txt exporting?

@redrathnure
Copy link
Contributor

redrathnure commented Jan 21, 2025

> uname -a
Darwin xxx 24.2.0 Darwin Kernel Version 24.2.0: Fri Dec  6 19:02:41 PST 2024; root:xnu-11215.61.5~2/RELEASE_ARM64_T6030 arm64

> uv --version
uv 0.5.10 (37b11ddb2 2024-12-17)

> uv run whereis python
python: /tmp/nanovna-saver/.venv/bin/python

> export UV_PROJECT_ENVIRONMENT=".test"

> uv run whereis python

...
Creating virtual environment at: .test
Installed 48 packages in 228ms
python: /tmp/nanovna-saver/.test/bin/python

#  Cleaning up
> rm -rf .env
> rm -rf .test

> ls .venv
ls: .venv: No such file or directory

> ls .test
ls: .test: No such file or directory

# Trigger venv preparation
> uv sync

# Nothin in default venv location
> ls .venv
ls: .venv: No such file or directory

# However it respects UV_PROJECT_ENVIRONMENT=".test" settings
> ls .test
CACHEDIR.TAG bin          lib          pyvenv.cfg

# Try to run app
> uv run NanoVNASaver

# Wait till App window open, then close it

# Check venv again

# Nothin in default venv location
> ls .venv
ls: .venv: No such file or directory

# However it respects UV_PROJECT_ENVIRONMENT=".test" settings
> ls .test
CACHEDIR.TAG bin          lib          pyvenv.cfg

Sorry, I have not idea why it does not work in your case.

@redrathnure
Copy link
Contributor

And, just as illustration for "uv and requriements.txt less" approach:

> rm -rf dist

> uv build


> ls dist
NanoVNASaver-0.6.8.post1.dev3+ge3587e2.d20250121-py3-none-any.whl nanovnasaver-0.6.8.post1.dev3+ge3587e2.d20250121.tar.gz

# BTW, I have no idea why wheel package has so weird naming

# Prepare new venv manually.
# In case of multyarch build this step may be more complex 
> mkvirtualenv test_2
....

# No need any dev packages, no need of uv export, just install nanovnasaver package
> pip install dist/nanovnasaver-*.tar.gz
....

> pip list
Package      Version
------------ -----------------------------------
NanoVNASaver 0.6.8.post1.dev3+ge3587e2.d20250121
numpy        2.2.2
pip          24.3.1
PyQt6        6.8.0
PyQt6-Qt6    6.8.1
PyQt6_sip    13.9.1
pyserial     3.5
scipy        1.15.1
setuptools   75.6.0
wheel        0.45.1

# Just install pyinstaller and you're good to go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants