Skip to content

Bugs: value-object core semantics (0.28.1) #33

Open

Description

@otsobide

Defects in the value-object core of 0.28.1 (each one reproduced). The 1.0.0 core (pyvalueobjects/_core.py) is designed to remove them; every item gets a regression test and is ticked when the fix lands.

  • C00 ValueObject.__eq__ raises ValueObjectError when compared with a non-VO: Int(1) == None, Int(2) in [1, Int(2)], Int(1) in {1: 0}, {Int(1), 1} and frozen-dataclass entities with an optional VO all crash. (d8793bf) Fixed in the core (pyvalueobjects/_core.py); the 0.x classes the root still exports keep the old behaviour until their family switches (B3-B9).
  • X00 Equality and hashing ignore the type: UserId(x) == OrderId(x), Int(1) == Float(1.0), Int(1) == PositiveInt(1); unrelated VOs collide as dict/set keys. (d8793bf) Fixed in the core (pyvalueobjects/_core.py); the 0.x classes the root still exports keep the old behaviour until their family switches (B3-B9).
  • C01 The VO stores and returns the caller's mutable object, so a validated ArrayList(Int) can end up holding 'x'.
  • C03 VOs are mutable: _value can be reassigned, attributes added, __init__ re-run in place (corrupts sets/dicts). (833f137, ff48b21, a15af5d) Fixed in the core (pyvalueobjects/_core.py); the 0.x classes the root still exports keep the old behaviour until their family switches (B3-B9).
  • C04 ValueObject.__hash__ assumes a hashable value: hash(ArrayList(Int)([1])) raises TypeError.
  • C06 ValueObjectError does not call super().__init__: keyword construction gives an empty message and it cannot be pickled/copied. (a0c9ce5)
  • X05 from pyvalueobjects.errors import ValueObjectError imports a module, so except ValueObjectError raises TypeError.
  • C07 from pyvalueobjects import * exports the subpackage names (numbers, strings, ...) and shadows the stdlib numbers module.
  • C02/C15 build_nullable makes NullablePositiveInt(None) an instance of PositiveInt holding None (Liskov violation; x.value() > 0 crashes).
  • C05/C17 str_value() of a null VO returns the string 'None', indistinguishable from a real 'None' string.

Activity

  1. otsobide commented on Oct 26, 2026

    @otsobide
    OwnerAuthor

    Status of the core items on v1-redesign:

    • C00, X00 and C03 are fixed in pyvalueobjects/_core.py, with regression tests in tests/test_core.py. The original repros (from pyvalueobjects import Int, PositiveInt) still show the old behaviour, because the package root keeps exporting the 0.x classes until each family switches to the core (B3-B9). Re-running the repros at those switches confirms the fix for the public names.
    • C03 also cites ff48b21 (the scalar guard against re-running __init__) and a15af5d (a base class without __slots__ can no longer give instances a __dict__).
    • C07 already holds on the branch as a side effect of 37dc3e9 and db6f04e: from pyvalueobjects import * gives only the core names, and pyvalueobjects.numbers no longer exists. It stays unticked until the block that finalizes __init__.py and __all__ (B10) adds its regression test and cites these commits.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions