1

I want to keep __version__ of my package in a single source file that would be accessible to __init__ and also to be able to import it in setup without being dependent on the package's dependencies.

Assume I have a package with a single module module_a with version 1.0.0 and have a single dependency - module_b.

module-a/src/module_a/__init__.py:

__version__ = `1.0.0`

from module_a.inner.foo import bar

module-a/src/module_a/inner/foo.py:

from module_b import baz
...
def bar():
   baz.do_something()

module-a/setup.py:

from setuptools import setup
from module_a import __version__

setup(
    version=__version__,
    ...,
)

Now when I want to update module_a to version 1.0.1 I only need to update __init__.py and not setup.py.

The problem is now that setup depends on module-b being installed, which is not good.

My current workaround: module-a/src/module_a.__init__.py:

__version__ = `1.0.0`

try:
    from module_a.foo import bar
except ImportError:
    # this should happen only when import __version__, __name__ from setup.py
    pass

Now setup does not depend on module-b :) But the problem is that if for some reason from module_a.inner.foo import bar fails (for instance in case - module-b was not installed) the import will fail silently and not raise a proper exception and module_a.foo won't be accessible from module_a.

Is there a proper way to fix this ?

Idan Miara
  • 21
  • 4
  • 2
    Some argue that you shouldn't have a `__version__` at all since it's outdated are pretty much replaced by standardized distribution metadata. But if you really want to include it, just set it from the distribution metadata: `__version__ = importlib.metadata.version("my-package")` – Brian61354270 Aug 06 '23 at 13:54
  • 1
    Do note that `setup.py` is outdated. You might want to consider upgrading to a more modern build system, or at least use `setuptools` with a `pyproject.toml` file. – Brian61354270 Aug 06 '23 at 13:56
  • As mentioned already there are better ways to solve this. But first, you don't need `__version__`, instead code should call `importlib.metadata.version()`. -- On the other hand if you really want a `__version__` attrbute: if you use `pyproject.toml` or `setup.cfg` instead of `setup.py`, then there are straightforward ways to get the bersion string from this `__version__` attribute. Example here: https://stackoverflow.com/a/65625285 – sinoroc Aug 06 '23 at 15:00

1 Answers1

-1

I use a technique similar to what @Farhood suggests in some published packages:

There is a separate version.py which can be independently imported.

The package root level __init__ simply does

from version import __version__

setup.py uses import machinery to load version from the same place. The important bits look like this:

from importlib.util import spec_from_file_location, module_from_spec

spec = spec_from_file_location('version', join(dirname(__file__) or '.', 'src', 'haggis', 'version.py'))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)
# use mod.__version__
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264