CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-mergedeep

A deep merge function for Python dictionaries and other mapping objects with configurable merge strategies.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

mergedeep

A deep merge function for Python dictionaries and other mapping objects with configurable merge strategies. This package provides comprehensive deep merging functionality with fine-grained control over how conflicts are resolved, supporting various Python collection types and type safety options.

Package Information

  • Package Name: mergedeep
  • Language: Python
  • Installation: pip install mergedeep
  • Minimum Python Version: 3.6+

Core Imports

from mergedeep import merge, Strategy

Package-level import:

import mergedeep
# Access via mergedeep.merge, mergedeep.Strategy

Basic Usage

from mergedeep import merge, Strategy

# Basic non-mutating merge
a = {"keyA": 1}
b = {"keyB": {"sub1": 10}}
c = {"keyB": {"sub2": 20}}

merged = merge({}, a, b, c)
print(merged)
# {"keyA": 1, "keyB": {"sub1": 10, "sub2": 20}}

# Mutating merge into existing dict
result = {"initial": "data"}
merge(result, a, b, c)
print(result)
# {"initial": "data", "keyA": 1, "keyB": {"sub1": 10, "sub2": 20}}

# Using different merge strategies
dst = {"key": [1, 2]}
src = {"key": [3, 4]}

# Replace strategy (default)
merge(dst, src, strategy=Strategy.REPLACE)
print(dst)  # {"key": [3, 4]}

# Additive strategy
dst = {"key": [1, 2]}
merge(dst, src, strategy=Strategy.ADDITIVE)
print(dst)  # {"key": [1, 2, 3, 4]}

Architecture

The mergedeep package implements a flexible merge system built around the Strategy pattern and recursive processing:

Core Components

  • Strategy Enumeration: Defines merge behavior policies (REPLACE, ADDITIVE, TYPESAFE variants)
  • Handler Dispatch System: Maps each strategy to specialized handler functions that implement the merge logic
  • Recursive Merge Engine: The _deepmerge function that traverses nested mapping structures
  • Type-Aware Processing: Specialized handling for different Python collection types (list, set, tuple, Counter)

Merge Process Flow

  1. Strategy Selection: User specifies merge strategy (defaults to REPLACE)
  2. Source Iteration: Multiple source mappings are processed sequentially using functools.reduce
  3. Key-by-Key Processing: For each key in source mappings:
    • If key exists in destination: Apply strategy-specific merge logic
    • If key is new: Deep copy value from source to destination
  4. Recursive Descent: When both destination and source values are mappings (but not Counter objects), recurse with _deepmerge
  5. Collection Handling: Strategy handlers apply type-specific merge operations for lists, sets, tuples, and Counter objects

Design Patterns

  • Strategy Pattern: Encapsulates merge algorithms in the Strategy enum and handler functions
  • Immutability Support: Uses copy.deepcopy to prevent unintended mutations between source and destination
  • Type Safety: Optional type checking prevents accidental type mismatches during merge operations
  • Functional Composition: Uses functools.reduce and functools.partial for clean handler composition

This architecture enables mergedeep to handle complex nested data structures while providing fine-grained control over merge behavior through configurable strategies.

Capabilities

Deep Merge Function

The core merge function that performs deep merging of multiple source mappings into a destination mapping with configurable strategies.

def merge(destination: MutableMapping, *sources: Mapping, strategy: Strategy = Strategy.REPLACE) -> MutableMapping:
    """
    A deep merge function for Python dictionaries and mapping objects.

    Parameters:
    - destination (MutableMapping): The target mapping to merge into
    - *sources (Mapping): Variable number of source mappings to merge from
    - strategy (Strategy, optional): The merge strategy to use (defaults to Strategy.REPLACE)

    Returns:
    MutableMapping: The merged destination mapping (same object as input destination)
    """

Usage Examples

from mergedeep import merge, Strategy
from collections import Counter

# Merging nested dictionaries
config = {"database": {"host": "localhost", "port": 5432}}
overrides = {"database": {"port": 3306, "ssl": True}}
merge(config, overrides)
# {"database": {"host": "localhost", "port": 3306, "ssl": True}}

# Working with different collection types
data = {
    "lists": [1, 2],
    "sets": {1, 2},
    "tuples": (1, 2),
    "counters": Counter({"a": 1, "b": 1})
}
updates = {
    "lists": [3, 4],
    "sets": {3, 4},
    "tuples": (3, 4),
    "counters": Counter({"a": 1, "c": 1})
}

# Additive merge combines collections
merge(data, updates, strategy=Strategy.ADDITIVE)
# {
#   "lists": [1, 2, 3, 4],
#   "sets": {1, 2, 3, 4},
#   "tuples": (1, 2, 3, 4),
#   "counters": Counter({"a": 2, "b": 1, "c": 1})
# }

# Type-safe merge with error checking
try:
    dst = {"key": [1, 2]}
    src = {"key": {3, 4}}  # Different type (set vs list)
    merge(dst, src, strategy=Strategy.TYPESAFE_REPLACE)
except TypeError as e:
    print(e)  # destination type: <class 'list'> differs from source type: <class 'set'> for key: "key"

Merge Strategies

Enumeration defining different strategies for handling conflicting keys during merge operations.

class Strategy(Enum):
    """
    Enumeration of merge strategies for handling conflicts.
    
    Values:
    - REPLACE (0): Replace destination value with source value (default)
    - ADDITIVE (1): Combine collections by extending/updating
    - TYPESAFE (2): Alias for TYPESAFE_REPLACE
    - TYPESAFE_REPLACE (3): Raise TypeError if types differ, otherwise REPLACE
    - TYPESAFE_ADDITIVE (4): Raise TypeError if types differ, otherwise ADDITIVE
    """
    REPLACE = 0
    ADDITIVE = 1
    TYPESAFE = 2
    TYPESAFE_REPLACE = 3
    TYPESAFE_ADDITIVE = 4

Strategy Behaviors

REPLACE (default)

  • When keys exist in both destination and source, replace destination value with source value
  • With multiple sources, the rightmost (last) source value appears in the merged result
  • Nested dictionaries are recursively merged

ADDITIVE

  • Combines collections of the same type:
    • list: extends destination with source items
    • set: updates destination with source items
    • tuple: concatenates tuples
    • Counter: updates counter values
  • Falls back to REPLACE behavior for non-collection types or type mismatches

TYPESAFE_REPLACE (TYPESAFE)

  • Raises TypeError when destination and source types differ
  • Otherwise performs REPLACE merge
  • Ensures type consistency during merge operations

TYPESAFE_ADDITIVE

  • Raises TypeError when destination and source types differ
  • Otherwise performs ADDITIVE merge
  • Provides type-safe collection merging

Strategy Usage Examples

from mergedeep import merge, Strategy
from collections import Counter

# Replace strategy examples
dst = {"key": [1, 2], "nested": {"a": 1}}
src = {"key": [3, 4], "nested": {"b": 2}}
merge(dst, src, strategy=Strategy.REPLACE)
# {"key": [3, 4], "nested": {"a": 1, "b": 2}}  # Lists replaced, dicts merged

# Additive strategy examples
dst = {"numbers": [1, 2], "count": Counter({"x": 1})}
src = {"numbers": [3, 4], "count": Counter({"x": 1, "y": 1})}
merge(dst, src, strategy=Strategy.ADDITIVE)
# {"numbers": [1, 2, 3, 4], "count": Counter({"x": 2, "y": 1})}

# Type-safe examples
dst = {"data": [1, 2]}
src = {"data": "string"}  # Different type
try:
    merge(dst, src, strategy=Strategy.TYPESAFE)
except TypeError as e:
    print(f"Type safety error: {e}")

Error Handling

The package raises TypeError when using type-safe strategies and type mismatches occur:

try:
    dst = {"key": [1, 2]}
    src = {"key": {3, 4}}  # set instead of list
    merge(dst, src, strategy=Strategy.TYPESAFE_REPLACE)
except TypeError as e:
    # Error format: 'destination type: <class 'list'> differs from source type: <class 'set'> for key: "key"'
    print(f"Merge failed: {e}")

Supported Collection Types

The merge function supports these Python collection types:

  • dict and other Mapping types: Recursive deep merge
  • list: Extend destination with source items (ADDITIVE mode)
  • tuple: Concatenate tuples (ADDITIVE mode)
  • set: Update destination with source items (ADDITIVE mode)
  • Counter: Update counter values, with special handling for nested Counter objects

Package Constants

Version Information

__version__: str
# Package version string, currently "1.3.4"
# Usage: from mergedeep import __version__

Types

from typing import MutableMapping, Mapping
from enum import Enum
from collections import Counter

# Core function signature uses these types
MutableMapping  # For destination parameter
Mapping         # For source parameters
Enum           # Base class for Strategy
Counter        # Supported collection type

Advanced Usage Patterns

Configuration Management

from mergedeep import merge

# Base configuration
base_config = {
    "database": {"host": "localhost", "port": 5432, "ssl": False},
    "cache": {"ttl": 300, "size": 1000},
    "features": ["logging", "monitoring"]
}

# Environment-specific overrides
production_config = {
    "database": {"host": "prod.db.com", "ssl": True},
    "cache": {"size": 10000},
    "features": ["logging", "monitoring", "analytics"]
}

# Merge configurations
final_config = merge({}, base_config, production_config)

Data Processing Pipelines

from mergedeep import merge, Strategy
from collections import Counter

# Aggregating results from multiple sources
results = [
    {"metrics": Counter({"success": 10, "error": 2}), "data": [1, 2, 3]},
    {"metrics": Counter({"success": 15, "error": 1}), "data": [4, 5, 6]},
    {"metrics": Counter({"success": 8, "error": 3}), "data": [7, 8, 9]}
]

# Combine all results
aggregated = {}
for result in results:
    merge(aggregated, result, strategy=Strategy.ADDITIVE)

# aggregated = {
#   "metrics": Counter({"success": 33, "error": 6}),
#   "data": [1, 2, 3, 4, 5, 6, 7, 8, 9]
# }

API Response Merging

from mergedeep import merge

# Merging paginated API responses
page1 = {"users": [{"id": 1, "name": "Alice"}], "pagination": {"page": 1, "total": 100}}
page2 = {"users": [{"id": 2, "name": "Bob"}], "pagination": {"page": 2, "total": 100}}

# Combine user data while preserving pagination info
combined = merge({}, page1)
merge(combined["users"], {"users": page2["users"]}, strategy=Strategy.ADDITIVE)
merge(combined, {"pagination": page2["pagination"]})

docs

index.md

tile.json