Skip to content

jftuga/py-file-versioning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

py-file-versioning

A flexible file versioning system with compression support, written in Python.

NOTE: The cli tool and API are still under development and will not be stable until v1.0.0 is released

Table of Contents

Features

  • Create versioned backups of files with automatic sequence numbering
  • Multiple compression options: gzip, bzip2, xz, or uncompressed
  • Configurable timestamp formats (UTC or local time)
  • Limit the number of versions kept per file
  • Command-line interface for easy integration
  • Support for Unicode filenames
  • Cross-platform compatibility

Package Name Convention

While the package is installed as py-file-versioning (using hyphens), you should use py_file_versioning (using underscores) in your Python imports following Python naming conventions.

Installation

From PyPI

pip install py-file-versioning

From Source

git clone https://github.com/jftuga/py-file-versioning.git
cd py-file-versioning
pip install .

For development installation:

uv venv
source .venv/bin/activate
uv pip install -e '.[dev]'
uv pip list

Command Line Usage

The package installs a command-line tool called pyfileversioning. Here are some common operations:

Create a version:

pyfileversioning create myfile.txt

Create a compressed version:

pyfileversioning create myfile.txt -c gz  # or --compression gz

List all versions:

pyfileversioning list myfile.txt

Restore a specific version to a new file path:

pyfileversioning restore versions/myfile--20240120.123456_001.txt.gz --target /path/to/restored_file.txt

Remove a version:

pyfileversioning remove versions/myfile--20240120.123456_001.txt.gz

Demo Shell Session

Here's a practical demonstration of using the command-line interface:

# Create a sample config file
$ echo "database_host=localhost" > config.ini
$ cat config.ini
database_host=localhost

# Create first version (uncompressed)
$ pyfileversioning create config.ini -d backups
Created version: backups/config--20250207.044841_001--loc_mod.ini

# Update the file content
$ echo "database_port=5432" >> config.ini

# Create compressed version
$ pyfileversioning create config.ini -d backups -c gz
Created version: backups/config--20250207.044924_001--loc_mod.ini.gz

# List all versions
$ pyfileversioning list config.ini -d backups
Path                                        | Sequence | Size | Timestamp           | TimeZone | TimestampSrc
====                                        | ======== | ==== | =========           | ======== | ============
config--20250207.044924_001--loc_mod.ini.gz |        1 |   95 | 2025-02-07T04:49:24 |    local |  modify time
config--20250207.044841_001--loc_mod.ini    |        1 |   24 | 2025-02-07T04:48:41 |    local |  modify time

# Add more content and create another version with UTC time
$ echo "database_name=myapp" >> config.ini
$ pyfileversioning create config.ini -d backups -c gz -u
Created version: backups/config--20250207.095009_001--utc_mod.ini.gz

# View all versions with size and timestamp (note: mixed timezone versions not recommended)
$ pyfileversioning list config.ini -d backups
Path                                        | Sequence | Size | Timestamp           | TimeZone | TimestampSrc
====                                        | ======== | ==== | =========           | ======== | ============
config--20250207.095009_001--utc_mod.ini.gz |        1 |  107 | 2025-02-07T09:50:09 |      utc |  modify time
config--20250207.044924_001--loc_mod.ini.gz |        1 |   95 | 2025-02-07T04:49:24 |    local |  modify time
config--20250207.044841_001--loc_mod.ini    |        1 |   24 | 2025-02-07T04:48:41 |    local |  modify time

Python API Usage

The library provides a flexible API for file versioning. Here's how to use it:

from py_file_versioning import FileVersioning, FileVersioningConfig

# Create a test file
def create_example_file(filename: str) -> None:
    content = """
    # Database configuration settings
    db_host = db.example.com
    db_port = 5432
    db_name = production_db
    """.strip()
    with open(filename, 'w') as f:
        f.write(content)

# Basic usage
filename = "example.ini"
create_example_file(filename)

versioning = FileVersioning()
version_path, removed, error = versioning.create_version(filename)
if error:
    print(f"Warning: {error}")
print(version_path)

# Advanced configuration
config = FileVersioningConfig(
    versions_path="backups",     # Store versions in 'backups' directory
    compression="gz",            # Use gzip compression
    max_versions=5,             # Keep only last 5 versions
    use_utc=True,              # Use UTC timestamps
    use_modified_time=True,    # Use file's modified time
    delimiter="__"             # Custom delimiter for version files
)
versioning = FileVersioning(config)
version_path, removed, error = versioning.create_version(filename)
if error:
    print(f"Warning: {error}")
print(version_path)

Configuration Options

FileVersioningConfig Parameters

Parameter Type Default Description
delimiter str "--" Separator between filename and version information
use_utc bool False Use UTC timestamps instead of local time
versions_path str "versions" Directory to store versions
compression str "none" Compression type: "none", "gz", "bz2", "xz"
max_versions Optional[int] None Maximum number of versions to keep
use_modified_time bool True Use file's modified time instead of current time

Command Line Options

usage: pyfileversioning [-h] [-V] [-t TARGET] [-d VERSIONS_PATH]
                        [-c {none,gz,bz2,xz}] [-m MAX_VERSIONS] [-s {mod,sto}]
                        [-u] [-D DELIMITER]
                        [{create,restore,list,remove}] files [files ...]

Options:

  • -V, --version: Show version information
  • -t, --target: Target file path for restore operation (must be full path to the restored file)
  • -d, --versions-path: Directory to store versions (default: versions)
  • -c, --compression: Compression type to use (none, gz, bz2, xz)
  • -m, --max-versions: Maximum number of versions to keep
  • -s, --src: Source for timestamps (mod: file modified time, sto: current time)
  • -u, --utc: Use UTC timezone for timestamps (default: local time)
  • --delimiter DELIMITER: The delimiter to use (default: --)

Environment Variables:

  • PFV_VERSIONS_PATH: Override default versions directory
  • PFV_COMPRESSION: Override default compression type
  • PFV_DELIMITER: Override default delimiter

Notes:

  • The tool supports file patterns (e.g., *.txt, config*.ini)
  • Multiple files can be specified for batch operations

Examples

Maintaining Multiple Versions

from py_file_versioning import FileVersioning, FileVersioningConfig

# Create versions with different timezone and timestamp combinations
configs = [
    # Local timezone versions
    {"use_utc": False, "use_modified_time": True, "desc": "local timezone, modified time"},
    {"use_utc": False, "use_modified_time": False, "desc": "local timezone, current time"},

    # UTC timezone versions
    {"use_utc": True, "use_modified_time": True, "desc": "UTC timezone, modified time"},
    {"use_utc": True, "use_modified_time": False, "desc": "UTC timezone, current time"}
]

for cfg in configs:
    config = FileVersioningConfig(
        use_utc=cfg["use_utc"],
        use_modified_time=cfg["use_modified_time"]
    )
    versioning = FileVersioning(config)
    version_path, removed, error = versioning.create_version("example.ini")
    print(version_path)

Using Different Compression Types

from py_file_versioning import FileVersioning, FileVersioningConfig

# Create versions using each compression type
compression_types = ["gz", "bz2", "xz"]

for compression in compression_types:
    config = FileVersioningConfig(
        compression=compression,
        use_utc=True,          # Use UTC time
        use_modified_time=True # Use file's modified time
    )
    versioning = FileVersioning(config)
    version_path, removed, error = versioning.create_version("example.ini")
    print(version_path)

Version File Naming

Version files follow this naming pattern:

{original_name}{delimiter}{timestamp}_{sequence}{delimiter}{version_spec}{extension}[.compression_ext]

Example:

myfile--20240120.123456_001--utc_mod.txt.gz

Where:

  • myfile is the original filename
  • -- is the delimiter (configurable)
  • 20240120.123456 is the timestamp (YYYYMMDD.HHMMSS)
  • 001 is the sequence number
  • utc_mod is the version specification:
    • First part (utc or loc) indicates timezone (UTC or local)
    • Second part (mod or sto) indicates timestamp source (modified time or stored/current time)
  • .txt is the original extension
  • .gz is the compression extension (if compression is used)

Note: All versions of a file must use consistent timezone and timestamp source settings.

Development

Setting Up Development Environment

# Clone the repository
git clone https://github.com/jftuga/py-file-versioning.git
cd py-file-versioning

# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install development dependencies
pip install -e '.[dev]'

Running Tests

pytest

For coverage report:

pytest --cov=file_versioning --cov-report=html

Code Quality Checks

# Format code
black .

# Sort imports
isort .

# Style checking
flake8

# Linting
ruff check

License

This project is licensed under the MIT License - see the LICENSE file for details.