Skip to main content

No project description provided

Project description

Classno

pypi downloads downloads versions license Ask DeepWiki

Classno is a lightweight and extensible Python library for data modeling, schema definition, and validation. It provides a clean and intuitive way to define data classes with various features like type validation, immutability, private fields, and automatic type casting.

Key Features

  • Type hints validation - Runtime type checking for all fields
  • Immutable objects - Create frozen, hashable instances
  • Private fields - Write-once, read-only field access patterns
  • Automatic type casting - Lossy autocast between compatible types
  • Customizable comparison - Control equality, hashing, and ordering behavior
  • Default values and factories - Safe handling of mutable defaults
  • Nested object support - Deep validation and casting of complex structures
  • Slots optimization - Reduced memory footprint
  • Rich comparison methods - Full ordering support
  • Union and Optional types - First-class support for Optional[T] and Union[T1, T2]
  • Extended collection types - Support for frozenset, deque, defaultdict, and more

Installation

pip install classno

Basic Usage

Simple Data Class

from classno import Classno, field

class User(Classno):
    name: str
    age: int = 0
    email: str = field(default="")

# Create an instance
user = User(name="John", age=30)

Features Configuration

Features can be enabled by setting the __features__ class attribute:

from classno import Classno, Features

class Config(Classno):
    __features__ = Features.VALIDATION | Features.FROZEN

    host: str
    port: int = 8080

Available features:

  • Features.EQ - Enable equality comparison
  • Features.ORDER - Enable ordering operations
  • Features.HASH - Make instances hashable
  • Features.SLOTS - Use slots for memory optimization
  • Features.FROZEN - Make instances immutable
  • Features.PRIVATE - Enable private field access
  • Features.VALIDATION - Enable type validation
  • Features.LOSSY_AUTOCAST - Enable automatic type casting

Field Configuration

Fields can be configured using the field() function:

from classno import Classno, field
from datetime import datetime

class Post(Classno):
    title: str
    content: str = ""
    created_at: datetime = field(default_factory=datetime.now)
    metadata: dict = field(default_factory=dict, metadata={"indexed": True})

Important: Always use default_factory for mutable defaults (list, dict, set) to avoid shared state issues:

# ❌ WRONG - This will raise an error
class Wrong(Classno):
    tags: list = []

# ✅ CORRECT - Use default_factory
class Correct(Classno):
    tags: list = field(default_factory=list)

Type Validation

class ValidatedModel(Classno):
    __features__ = Features.VALIDATION

    numbers: list[int]
    mapping: dict[str, float]

# This will raise TypeError if types don't match
model = ValidatedModel(
    numbers=[1, 2, 3],
    mapping={"a": 1.0, "b": 2.0}
)

Optional and Union Types

Full support for Optional and Union types with proper validation:

from typing import Optional, Union

class FlexibleModel(Classno):
    __features__ = Features.VALIDATION

    # Optional field - can be None
    optional_name: Optional[str] = None

    # Union type - can be str or int
    identifier: Union[str, int]

    # Complex unions
    data: Union[list[str], dict[str, int], None] = None

model = FlexibleModel(optional_name=None, identifier=42)

Extended Collection Types

Support for Python's extended collection types:

from collections import deque, defaultdict, Counter
from typing import Optional

class AdvancedCollections(Classno):
    __features__ = Features.VALIDATION | Features.LOSSY_AUTOCAST

    # Immutable set
    unique_ids: frozenset[int] = field(default_factory=frozenset)

    # Double-ended queue
    history: deque[str] = field(default_factory=deque)

    # Dict with default values
    counters: defaultdict[str, int] = field(
        default_factory=lambda: defaultdict(int)
    )

    # Counter for frequency tracking
    word_counts: Counter[str] = field(default_factory=Counter)

Immutable Objects

class ImmutableConfig(Classno):
    __features__ = Features.IMMUTABLE  # Combines FROZEN, SLOTS, and HASH

    host: str
    port: int = 8080

config = ImmutableConfig(host="localhost")
# Attempting to modify will raise an exception
config.port = 9000  # Raises Exception

Private Fields

class PrivateFields(Classno):
    __features__ = Features.PRIVATE

    name: str
    secret: str  # Can only be accessed with _secret prefix for rw, secret for ro

obj = PrivateFields(name="public")
obj._secret = "hidden"  # OK
obj.secret  # OK
obj.secret = "hidden"  # Raises Exception

Nested Objects

class Address(Classno):
    street: str
    city: str

class Person(Classno):
    name: str
    address: Address

# Create nested structure
person = Person(
    name="John",
    address=Address(street="123 Main St", city="Boston")
)

Automatic Type Casting

Enable automatic type conversion with LOSSY_AUTOCAST:

class AutoCastModel(Classno):
    __features__ = Features.LOSSY_AUTOCAST

    count: int
    price: float
    active: bool

# Automatic casting from compatible types
model = AutoCastModel(
    count="42",      # str → int
    price="19.99",   # str → float
    active="true"    # str → bool
)
# model.count == 42 (int)
# model.price == 19.99 (float)
# model.active == True (bool)

Combining Features

Features can be combined for powerful data modeling:

class RobustModel(Classno):
    # First cast, then validate
    __features__ = Features.LOSSY_AUTOCAST | Features.VALIDATION | Features.FROZEN

    user_id: int
    tags: list[str] = field(default_factory=list)

# This works: "42" is cast to 42, validated, then frozen
model = RobustModel(user_id="42", tags=["python", "classno"])

Feature execution order:

  1. LOSSY_AUTOCAST - Values are automatically cast to target types
  2. VALIDATION - Type validation is performed on cast values
  3. FROZEN - Object becomes immutable after validation

Customization

Custom Comparison Keys

Control which fields participate in equality, hashing, and ordering:

class CustomCompare(Classno):
    __features__ = Features.EQ | Features.HASH | Features.ORDER

    # Use tuples for ordered keys (order matters for comparison)
    __hash_keys__ = ("id",)              # Only id used for hashing
    __eq_keys__ = ("id", "name")         # id and name for equality
    __order_keys__ = ("priority", "name")  # Sort by priority, then name

    id: int
    name: str
    priority: int = 0
    description: str = ""

# Two objects are equal if id and name match
obj1 = CustomCompare(id=1, name="A", description="First")
obj2 = CustomCompare(id=1, name="A", description="Second")
assert obj1 == obj2  # True - description is ignored

# Ordering uses priority first, then name
items = [
    CustomCompare(id=1, name="B", priority=2),
    CustomCompare(id=2, name="A", priority=1),
]
sorted_items = sorted(items)  # Sorted by priority, then name

Best Practices

  1. Use type hints for all fields - Enables validation and better IDE support
  2. Use default_factory for mutable defaults - Always use field(default_factory=list) instead of field(default=[])
  3. Enable appropriate features - Only enable features you need (e.g., VALIDATION in development, FROZEN for config)
  4. Use Features.SLOTS - Reduces memory usage by ~40% for classes with many instances
  5. Combine LOSSY_AUTOCAST with VALIDATION - Cast first, then validate for robust data handling
  6. Use tuples for comparison keys - __eq_keys__ = ("id", "name") not ["id", "name"]
  7. Leverage Optional and Union - Use proper type hints for flexible APIs

Error Handling

Classno provides clear, actionable error messages:

# Validation error
class Model(Classno):
    __features__ = Features.VALIDATION
    age: int

try:
    Model(age="not a number")
except ValidationError as e:
    # "Validation error for field 'age': expected int, got 'not a number' of type str"
    pass

# Mutable default error
try:
    class Wrong(Classno):
        tags: list = []  # ❌ Error at class definition time
except ValueError as e:
    # "Mutable default values are not allowed. Found list [].
    #  Use default_factory=lambda: [] instead to avoid shared state issues."
    pass

# Frozen modification error
class Frozen(Classno):
    __features__ = Features.FROZEN
    value: int

obj = Frozen(value=42)
try:
    obj.value = 100
except AttributeError as e:
    # "Cannot modify attributes of frozen class Frozen"
    pass

The library raises appropriate exceptions for:

  • ValidationError - Type validation failures
  • TypeError - Type mismatch during casting or validation
  • ValueError - Invalid field configurations (e.g., mutable defaults)
  • AttributeError - Immutability violations (FROZEN), invalid field access (PRIVATE)

Advanced Usage

Inheritance

Features and fields are properly inherited:

class BaseModel(Classno):
    __features__ = Features.VALIDATION
    id: int

class UserModel(BaseModel):
    # Inherits VALIDATION feature and id field
    name: str
    email: str

user = UserModel(id=1, name="John", email="john@example.com")

Serialization

Convert objects to dictionaries for JSON serialization:

class User(Classno):
    name: str
    age: int

user = User(name="John", age=30)
data = user.as_dict()  # {"name": "John", "age": 30}

Copying

Objects support both shallow and deep copying:

import copy

original = User(name="John", age=30)
shallow = copy.copy(original)
deep = copy.deepcopy(original)

Testing

Run the comprehensive test suite:

# Install development dependencies
poetry install

# Run all tests
poetry run poe tests

# Run fast tests (skip slow tests)
poetry run poe test-fast

# Run with coverage
poetry run poe test-all

# Run specific test categories
poetry run poe test-unit          # Unit tests
poetry run poe test-integration   # Integration tests
poetry run poe test-edge-cases    # Edge case tests

Authors

  • Dmitriy Kudryavtsev - author - kuderr

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

classno-0.1.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

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

classno-0.1.0-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file classno-0.1.0.tar.gz.

File metadata

  • Download URL: classno-0.1.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.3 Darwin/22.6.0

File hashes

Hashes for classno-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d40279eb91235caa6b6196152b88af605fcd4ad64fe255b029d7a2705c76feaf
MD5 63fd8ff1b90923a36da87195b3c118e5
BLAKE2b-256 efb6b18828929de0f99bcf2a43e7bad5e4eff1473acc71087204853143f3a751

See more details on using hashes here.

File details

Details for the file classno-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: classno-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.3 Darwin/22.6.0

File hashes

Hashes for classno-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f76e6441a805d1d8451c7d6f8a20e5e8fa7d9bd6c7fe61f0ca05765f42522e7b
MD5 81177782de2de91f0da4390b9f6112bd
BLAKE2b-256 f420510ad4893a7b089c4157baeb16cabde11724711cbcdec13794adc5dd0a80

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