Python Package Version: the __version__ Attribute

5/5 - (1 vote)
import my_package
print(my_package.__version__)
# 1.2.3

Python __version__ Attribute

Python contains many “Magic Methods/Attributes”. One of these is __version__ commonly called “Dunder version” because of the double underscore before and after version. In this article, I will briefly look at what a Dunder Method/Attribute is and talk about __version__.

What Does a Dunder Method/Attribute Do?

Dunder Methods/Attributes, also called “Magic Methods” perform two basic functions. They may convey information to the interpreter (methods), or they can convey information to the programmer (attributes). 

Dunders are used for creation (__init__), comparisons (__eq__), attribute access (__version__), and many more. Some are called by a programmer but some like __add__ and other operators are built-in to Python.

Using the dir() function with the object name, you can view a list of all the methods and properties built into a specific package.

class StarWars:
    __version__ = "19.7.7"

    def __init__(self, name, robot, affiliation):
        self.name = name
        self.robot = robot
        self.affiliation = affiliation

    def bio(self):
        print("{} and {} fight for the {}.".format(self.name, self.robot,      self.affiliation))


legends = StarWars("Luke Skywalker", "R2D2", "Rebel Force")
print(dir(StarWars))

Output:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'affiliation', 'name', 'robot']

Python __version__ Attribute

This brings us to the __version__ “Magic Attribute”. Dunder version is used to determine what version of a package is being used.

In the code above, we can add a print statement to find out what version of the class StarWars is being used.

print(StarWars.__version__)

Output:

19.7.7

You can use this format to find out the version of most packages.

import pandas
print(pandas.__version__)

Output:

1.4.3

Embedding __version__

The examples we have used up to this point have been relatively basic.

If you are building an application that may be dependent on external packages a call for package versions may be embedded in your code. It is important to make sure the proper versions are being imported.

This can be done during build-time or runtime. I think it is safer to determine the versions to be used before runtime. Create a version.py file. This file contains no other code and doesn’t import anything. It only contains the version attribute (__version__ = "1.1.2"). Have the setup.py file read it.

This is beneficial for a couple of reasons. Firstly, it limits the number of places necessary to edit should the version change. Secondly, you can import the version.py file into setup.py and you won’t load dependencies by storing it in the __init__.py file.

Here is an example.

# store version inside sample/_version.py file
__version__ = "1.1.2"

# inside sample/__init__.py:
from.version import__version__

# inside setup.py:
exec(open('sample/version.py').read())
setup(
    ...
    version=__version__,)

I mentioned in the previous section that __version__ will determine the version of most packages. Some third-party packages do not contain a version attribute. It is therefore important to account for that possibility, or your app will fail at runtime. 

Example:

raise RuntimeError("Unable to find version string")