Skip to main content

Flexible structural typing and runtime validation for Python

Project description

Duckdantic 🦆

PyPI Python Version License Tests Documentation

Duckdantic is a Python library for flexible structural typing and runtime validation. It provides a powerful way to define structural types (traits) and check whether objects satisfy them at runtime, without requiring inheritance or type annotations.

✨ Features

  • 🦆 True Duck Typing: Check object structures at runtime without inheritance
  • 🏗️ Trait-Based Validation: Define reusable structural requirements
  • 🔧 Framework Agnostic: Works with Pydantic, dataclasses, TypedDict, and plain objects
  • 🎯 Flexible Policies: Customize type checking behavior to your needs
  • 🚀 High Performance: Intelligent caching and optimized field normalization
  • 🎨 Intuitive API: Clean, Pythonic interface with excellent IDE support
  • 🔌 ABC Integration: Use traits with isinstance() and issubclass()

📦 Installation

pip install duckdantic

For Pydantic support:

pip install "duckdantic[pydantic]"

🚀 Quick Start

Basic Usage

from duckdantic import TraitSpec, FieldSpec, satisfies

# Define a trait
PersonTrait = TraitSpec(
    name="Person",
    fields=(
        FieldSpec("name", str, required=True),
        FieldSpec("age", int, required=True),
    )
)

# Check if objects satisfy the trait
class Employee:
    def __init__(self, name: str, age: int, employee_id: str):
        self.name = name
        self.age = age
        self.employee_id = employee_id

emp = Employee("Alice", 30, "EMP001")
assert satisfies(emp, PersonTrait)  # ✅ True - has required fields

Duck API (Recommended)

The Duck API provides a more ergonomic interface, especially when working with Pydantic models:

from pydantic import BaseModel
from duckdantic import Duck

class User(BaseModel):
    name: str
    email: str
    age: int

class Person(BaseModel):
    name: str
    age: int

# Create a duck type from a Pydantic model
PersonDuck = Duck(Person)

# Check if instances satisfy the duck type
user = User(name="Bob", email="bob@example.com", age=25)
assert isinstance(user, PersonDuck)  # ✅ True - has required fields

# Convert between compatible types
person = PersonDuck.convert(user)  # Creates Person(name="Bob", age=25)

Method Checking

from duckdantic import MethodSpec, methods_satisfy

# Define method requirements
DrawableMethods = [
    MethodSpec("draw", params=[int, int], returns=None),
    MethodSpec("get_color", params=[], returns=str),
]

class Circle:
    def draw(self, x: int, y: int) -> None:
        pass

    def get_color(self) -> str:
        return "red"

assert methods_satisfy(Circle, DrawableMethods)  # ✅ True

ABC Integration

from duckdantic import TraitSpec, FieldSpec, abc_for

# Define a trait
ConfigTrait = TraitSpec(
    name="Config",
    fields=(
        FieldSpec("host", str),
        FieldSpec("port", int),
    )
)

# Create an ABC from the trait
ConfigABC = abc_for(ConfigTrait)

# Use with isinstance
@ConfigABC.register
class ServerConfig:
    host: str = "localhost"
    port: int = 8080

assert isinstance(ServerConfig(), ConfigABC)  # ✅ True

🎯 Use Cases

  • API Validation: Ensure objects have required fields before processing
  • Plugin Systems: Define interfaces without requiring inheritance
  • Type Bridges: Convert between similar types from different libraries
  • Testing: Create test doubles that satisfy production interfaces
  • Configuration: Validate configuration objects from various sources

📚 Documentation

For comprehensive documentation, visit https://pr1m8.github.io/duckdantic/

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📄 License

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

🙏 Acknowledgments

  • Inspired by structural typing concepts from TypeScript and Go interfaces
  • Built to complement Pydantic's excellent runtime validation
  • Thanks to all contributors and users of the library

Project details


Download files

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

Source Distribution

duckdantic-1.0.1.tar.gz (479.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

duckdantic-1.0.1-py3-none-any.whl (32.2 kB view details)

Uploaded Python 3

File details

Details for the file duckdantic-1.0.1.tar.gz.

File metadata

  • Download URL: duckdantic-1.0.1.tar.gz
  • Upload date:
  • Size: 479.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.8

File hashes

Hashes for duckdantic-1.0.1.tar.gz
Algorithm Hash digest
SHA256 695a310899cf5de8f4d77e0a554db9f26aca1b72d13a9484ab90473135220fbd
MD5 f902de76fbbb3bb7c6fcd12d7c3a85ab
BLAKE2b-256 069004dc69b5624a45852bacd3e826775a928da7e3bc7c25657f3c90e365d811

See more details on using hashes here.

File details

Details for the file duckdantic-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: duckdantic-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 32.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.8

File hashes

Hashes for duckdantic-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a51c4c0603d6aa89b7caf2fecf6ccc5e5728e752012306c577e11a227089c135
MD5 f3fe7e13ed0a2489987cec9f451cc43d
BLAKE2b-256 c77212cf8c86a6c2701e66b2137894983a00079ea98ebdbdd06b5e76b63f4dcb

See more details on using hashes here.

Supported by

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