Skip to main content

🗺️ PyMapMe

Python 3.13+ Pydantic 2.0+ License: GPL v3 Tests

Transform Pydantic models from one structure to another with declarative field mapping.

✨ Reshape data between APIs

🎯 Flatten nested structures

🔄 Aggregate complex models

All while maintaining Pydantic's validation and type safety.


📋 Table of Contents


🚀 Quick Start

Map data from a source Pydantic model to a target model with a different structure, using simple field declarations.

Common Use Case: Third-Party API Integration

Convert camelCase third-party API responses with nested structures to snake_case Python models:

from pydantic import BaseModel, Field
from pymapme.models.mapping import MappingModel

# Third-party API response models (camelCase with nesting)
class ThirdPartyAddress(BaseModel):
    streetName: str
    cityName: str
    zipCode: str

class ThirdPartyUserProfile(BaseModel):
    firstName: str
    lastName: str
    userEmail: str
    homeAddress: ThirdPartyAddress
    isActive: bool

# Your application's Python model (snake_case, flattened)
class User(MappingModel):
    first_name: str = Field(json_schema_extra={"source": "firstName"})
    last_name: str = Field(json_schema_extra={"source": "lastName"})
    email: str = Field(json_schema_extra={"source": "userEmail"})
    street: str = Field(json_schema_extra={"source": "homeAddress.streetName"})
    city: str = Field(json_schema_extra={"source": "homeAddress.cityName"})
    zip_code: str = Field(json_schema_extra={"source": "homeAddress.zipCode"})
    is_active: bool = Field(json_schema_extra={"source": "isActive"})

# Transform third-party API response to your application model
third_party_data = ThirdPartyUserProfile(
    firstName="John", 
    lastName="Doe", 
    userEmail="john@example.com",
    homeAddress=ThirdPartyAddress(
        streetName="123 Main St",
        cityName="New York", 
        zipCode="10001"
    ),
    isActive=True
)
user = User.build_from_model(third_party_data)
# User(first_name="John", last_name="Doe", email="john@example.com", 
#      street="123 Main St", city="New York", zip_code="10001", is_active=True)

Basic Structure Mapping

from pydantic import BaseModel, Field
from pymapme.models.mapping import MappingModel

# Source models
class PersonalInfo(BaseModel):
    first_name: str
    last_name: str

class JobInfo(BaseModel):
    title: str
    company: str

class UserProfile(BaseModel):
    personal: PersonalInfo
    job: JobInfo

# Target model with flattened structure
class UserSummary(MappingModel):
    name: str = Field(json_schema_extra={"source": "personal.first_name"})
    title: str = Field(json_schema_extra={"source": "job.title"})

# Transform
profile = UserProfile(
    personal=PersonalInfo(first_name="John", last_name="Smith"),
    job=JobInfo(title="Developer", company="Acme")
)
summary = UserSummary.build_from_model(profile)
# UserSummary(name="John", title="Developer")

✨ Features

🎯 Nested Field Mapping

Map deeply nested fields using dot notation:

from pydantic import Field
from pymapme.models.mapping import MappingModel

class OrderSummary(MappingModel):
    customer_name: str = Field(json_schema_extra={"source": "customer.profile.name"})
    payment_total: float = Field(json_schema_extra={"source": "payment.amount"})
    shipping_city: str = Field(json_schema_extra={"source": "shipping.address.city"})

🔧 Custom Transformation Functions

Transform data using custom functions with access to the source model:

from pydantic import Field
from pymapme.models.mapping import MappingModel

class UserDisplay(MappingModel):
    full_name: str = Field(json_schema_extra={"source_func": "_build_full_name"})
    
    @staticmethod
    def _build_full_name(source_model, default):
        return f"{source_model.first_name} {source_model.last_name}".strip()

📊 Context Data Injection

Inject additional data during transformation:

from pydantic import BaseModel, Field
from pymapme.models.mapping import MappingModel

class User(BaseModel):
    name: str

class EnrichedUser(MappingModel):
    name: str = Field(json_schema_extra={"source": "name"})
    is_premium: bool = Field(json_schema_extra={"source_func": "_check_premium"})
    
    @staticmethod
    def _check_premium(source_model, default, user_tier: str = "basic"):
        return user_tier == "premium"

# Usage with context
user = User(name="John")
enriched = EnrichedUser.build_from_model(user, context={"user_tier": "premium"})
# EnrichedUser(name="John", is_premium=True)

⚡ Automatic Field Mapping

Fields without explicit mapping use the same field name from source:

from pydantic import Field
from pymapme.models.mapping import MappingModel

class SimpleMapping(MappingModel):
    # These map automatically by name
    name: str
    email: str
    # This uses explicit mapping
    user_id: int = Field(json_schema_extra={"source": "id"})

📦 Installation

# Using pip
pip install pymapme

# Using Poetry
poetry add pymapme

🔧 Requirements

  • Python 3.13+
  • Pydantic 2.0+

🛠️ Development

Setup

# Clone the repository
git clone https://github.com/funnydman/pymapme.git
cd pymapme

# Install dependencies
poetry install

Commands

# Run tests with coverage
make run-unit-tests

# Run static analysis (Ruff + mypy)
make run-static-analysis

# Auto-format code
make format

# Build package
make build-package

🤝 Contributing

We welcome contributions! Please follow these steps:

1. Fork and Clone

git clone https://github.com/funnydman/pymapme.git
cd pymapme

2. Create Feature Branch

git checkout -b feature/your-feature-name

3. Make Changes

  • Write tests for new functionality
  • Follow existing code patterns
  • Update documentation if needed

4. Verify Quality

# Run full test suite
make run-unit-tests

# Check code quality
make run-static-analysis

# Format code
make format

5. Submit Pull Request

  • Ensure all tests pass
  • Include clear description of changes
  • Reference any related issues

Development Guidelines

  • Tests: Write tests for all new features and bug fixes
  • Type hints: Use modern Python type annotations
  • Documentation: Update examples and docstrings as needed
  • Commit messages: Use clear, descriptive commit messages

📄 License

This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

Release files for pymapme 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymapme 1.0.0
File Size Uploaded
pymapme-1.0.0.tar.gz 16.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pymapme 1.0.0
File Interpreter ABI Platform
pymapme-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.7 kB

Release files / pymapme-1.0.0.tar.gz

Download URL pymapme-1.0.0.tar.gz
Size 16.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a5cf46c031994df53a01029fdf7f7ba85eee880b12fe4c19b901671bd2ad4a16
BLAKE2b-256 checksum
How to use checksums
40e69d313db1bb97f4f1c2eddcdd5bd6f3766ae25e2edc5f485713c0e30e2337
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.0.1 CPython/3.13.3 Linux/6.12.33-1-lts

Release files / pymapme-1.0.0-py3-none-any.whl

Download URL pymapme-1.0.0-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
98ea959d6a406d86bf927daafdd48b2ab246c17419d45acd739dcb6fd4db6550
BLAKE2b-256 checksum
How to use checksums
6ba194999a88bc679529403c7b78babcaffda5f28e6841207d2be34ac1cbc7e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.0.1 CPython/3.13.3 Linux/6.12.33-1-lts

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page