takluyver / pynsist

Build Windows installers for Python applications
https://pynsist.readthedocs.io/
Other
930 stars 123 forks source link

Docs: How to single source version number? #216

Open fohrloop opened 4 years ago

fohrloop commented 4 years ago

Hi,

Thanks for making and maintaining this awesome package! I was about to create something similar, but was happily surprised there is already a package that does the job. I have a question about single sourcing the version number.

Currently, I use a common strategy: __version__.py inside mypackage folder, which has just

__version__="1.1.0"

And then, in the setup.py, I read the version using some regex. In application code, I read the version by just importing by from mypackage.__version__ import __version__. or similar. When using pynsist, I have to define a version number for the application in installer.cfg:Application.version.

What is the recommended way to single source version number in applications using pynsist? I suppose there are many ways to do it, but probably there is one is covers most use cases and/or is better than the others.

takluyver commented 4 years ago

Glad you like it!

At present, this isn't possible with a static configuration file. Larger projects often favour either creating the config file from a script as a build step, or scripting the build with Pynsist's Python API.

I tend to favour using tools like bump2version to update version numbers over having a strictly single source for it. But I am also open to retrieving the version number automatically (e.g. Flit does this for publishing Python packages). I lean towards doing so with static analysis rather than importing the package, as importing it can have side effects, needs dependencies to be installed, etc.

albertogomcas commented 2 years ago

As far as I know, a common pattern is to declare the version numbers in pyproject.toml (keeping it up to date with poetry or other tools) Then in some file (typically mypackage/__init__.py):

from importlib import metadata

try:
    __version__ = metadata.version(__package__)
except metadata.PackageNotFoundError:
    __version__ = "0.0.0"

However, this does not work for pynsist generated apps (it never finds the metadata and thus defaults to 0.0.0), because the mypackage is not really installed (?) so the mypackage-xxx.dist-info folder is not created inside pkgs (unlike for the rest of dependencies).

takluyver commented 2 years ago

Pynsist is roughly designed so that you don't need to make a package of your application code (in the sense that pip etc. know about) before you can build an installer for it. So yes, it copies the application code without 'installing' it in a Python packaging sense (the relevant spec is Recording installed projects). This might be something to change, but it's probably a trickier change than it sounds.

I personally dislike packages calling importlib.metadata.version() on import, because it scans all installed packages, which can be really quite slow.

albertogomcas commented 2 years ago

Thanks for the hint that using importlib like this may be a performance issue, good to know.