Skip to main content

Easy object serialization & versioning framework

Project description

This package provides an easy way to define complex nested objects that can be saved/loaded to/from strings or JSON files, and easily migrated from older object versions.

See API documentation

Example– VersionedObject as a configuration file

from versionedobj import VersionedObject

# Nested config object
class DisplayConfig(VersionedObject):
    display_mode = "windowed"
    resolution = "1920x1080"
    volume = 0.66

# Top-level config object with another nested config object
class UserConfig(VersionedObject):
    version = "v1.0.0"
    username = "john smith"
    friend_list = ["user1", "user2", "user3"]
    display_config = DisplayConfig()

# Create an instance of the top-level object
cfg = UserConfig()

# Change some values
cfg.display_config.volume = 1.0
cfg.username = "jane doe"

# Save to JSON file
cfg.to_file('user_config.json', indent=4)

# Load from JSON file
cfg.from_file('user_config.json')

You can also just get the object data as a string:

>>> cfg.to_json(indent=4)

{
    "version": "v1.0.0",
    "username": "jane doe",
    "friend_list": [
            "user1",
            "user2",
            "user3"
    ],
    "display_config": {
        "display_mode": "windowed",
        "resolution": "1920x1080",
        "volume": 1.0
    }
}

Migrations – making use of the version number

Any VersionedObject object can have a version attribute, which can be any object, although it is typically a string (e.g. "v1.2.3"). This version attribute can be used to support migrations for older objects, in the event that you need to change the format of your object.

Let’s take the same config file definition from the previous example:

from versionedobj import VersionedObject

# Nested config object
class DisplayConfig(VersionedObject):
    display_mode = "windowed"
    resolution = "1920x1080"
    volume = 0.66

# Top-level config object with another nested config object
class UserConfig(VersionedObject):
    version = "v1.0.0"
    username = "john smith"
    friend_list = ["user1", "user2", "user3"]
    display_config = DisplayConfig()

Imagine we’ve already released this code out into the world. People are already using it, and they have JSON files generated by the UserConfig class sitting on their computers.

Now, imagine you are making a new release of your software, and some new features require you to make the following changes to the config file structure:

  • remove the the DisplayConfig.resolution field entirely

  • change the name of DisplayConfig.volume to DisplayConfig.volumes

  • change the value of DisplayConfig.volumes from a float to a list

from versionedobj import VersionedObject

# Nested config object
class DisplayConfig(VersionedObject):
    display_mode = "windowed"
    # 'resolution' field is deleted
    volumes = [0.66, 0.1] # 'volume' is now called 'volumes', and is a list

# Top-level config object with another nested config object
class UserConfig(VersionedObject):
    version = "v1.0.0"
    username = "john smith"
    friend_list = ["user1", "user2", "user3"]
    display_config = DisplayConfig()

Right now, if you send this updated UserConfig class to your existing users, it will fail to load their existing JSON files with version v1.0.0, since those files will contain the DisplayConfig.resolution field that we deleted in v1.0.1, and DisplayConfig.volume will similarly be gone, having been replaced with DisplayConfig.volumes. This situation is what migrations are for.

The solution is to:

  1. Change the version number to something new, e.g. v1.0.0 becomes v1.0.1

  2. Write a migration function to transform v1.0.0 object data into v1.0.1 object data

from versionedobj import VersionedObject

# Nested config object
class DisplayConfig(VersionedObject):
    display_mode = "windowed"
    # 'resolution' field is deleted
    volumes = [0.66, 0.1] # 'volume' is now called 'volumes', and is a list

# Top-level config object with another nested config object
class UserConfig(VersionedObject):
    version = "v1.0.1" # Version has been updated to 1.0.1
    username = "john smith"
    friend_list = ["user1", "user2", "user3"]
    display_config = DisplayConfig()

# Create the migration function for v1.0.0 to v1.0.1
def migrate_100_to_101(attrs):
    del attrs['display_config']['resolution']        # Delete resolution field
    del attrs['display_config']['volume']            # Delete volume field
    attrs['display_config']['volumes'] = [0.66, 0.1] # Add defaults for new volume values
    return attrs                                     # Return modified data (important!)

# Add the migration function for v1.0.0 to v1.0.1
UserConfig.add_migration("v1.0.0", "v1.0.1", migrate_100_to_101)

after you add the migration function and update the version to v1.0.1, JSON files loaded with version``v1.0.0`` will be migrated to version v1.0.1.

The downside to this approach, is that you have to manually udpate the version number, and write a new migration function, anytime the structure of your config data changes.

The upside, of course, is that you can relatively easily support migrating any older version of your config file to the current version.

If you don’t need the versioning/migration functionality, just never change your version number, or don’t create a version attribute on your VersionedObject classes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

versionedobj-0.2.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file versionedobj-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: versionedobj-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.7.6

File hashes

Hashes for versionedobj-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6cee6def8e773489622688975884242e60d64de060ec388769a99ae090560095
MD5 09a464154757a2961404a2c6d1c94d92
BLAKE2b-256 d6f411b9374793855a473a902f9669deba60a95d8758e401c5f27c59228fe6df

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page