Search code examples
pythonversionglobal-variables

Python: Best practice for including a version number in an app?


I have a PyQt application that reads and writes data files. I am including a 'version number' in each file written. This is a simple number similar to: 1.2 or something (major and minor versions).

I am doing this so that I can change the format of these data files in future versions and then still correctly parse them simply by checking to see what the version is inside the file.

My question is what is the best practice for keeping this number stored inside the app itself. I.e. do I just hard-code the app version number into the class that is responsible for reading and writing files? Or should I have some sort of object/variable stored at the top-level of the app and somehow access it from the class responsible for reading and writing these files. If the latter, how do I store it and how do I access it?

Thanks.


Solution

  • First, version the data format separately from the app, if not already doing so. Second, have separate classes for newer versions of the format. If the format is completely backwards compatible, you can drop the classes for older versions. As for the file structure, you could have something like:

    • DF
      • __init__.py
      • dfbase.py
      • v1_1
        • __init__.py
        • format.py
      • v2_0
        • __init__.py
        • format.py

    where "df" stands for the data format name. The package initialization files import the appropriate packages and define a structure or function to make them accessible. The top-level __init__.py should also define a factory function to create data format objects, so you don't have to.

    DF/__init__.py:

    from df.dfbase import DFBase
    from v1_1 import DF as DF1
    from v2_0 import DF as DF2
    versions = {
      '1.0': DF1, # let's say minor versions are fully backwards compatible
      '1.1': DF1, 
      '2.0': DF2
    }
    def create(file):
        """Factory function. Loads data from 'file', returns a DF of the appropriate 
        version.
        """
        ...
    

    DF/v1_1/__init__.py:

    __version__ = '1.1'
    # previous versions that this one is backwards compatible with
    previous = ['1.0']
    from format import DF
    

    DF/v1_1/format.py:

    from df import DFBase
    
    class DF(DFBase):
        ...
    

    DF/v2_0/__init__.py:

    __version__ = '2.0'
    from format import DF
    

    With a little more coding in DF/__init__.py, you could automate the import and registration of format versions.

    import glob, sys
    from ndf.ndfbase import NDFBase
    
    formats={}
    
    for ver in glob.iglob('v*_*'):
        pkg = '{0}.{1}'.format(__package__, ver)
        __import__(pkg)
        formats[sys.modules[pkg].__version__] = sys.modules[pkg]
        if hasattr(sys.modules[pkg], 'previous'):
            for prev in sys.modules[pkg].previous:
                formats[prev] = sys.modules[pkg]
    
    def create(file):
        """Factory function. Loads data from 'file', returns a DF of the appropriate 
        version.
        """
        ...