POOP β Python Object Oriented Programming. A Python 3.14 interpreter that enforces Smalltalk-style message passing by rejecting if/for/print/isinstance and rewriting Python literals (1, "hi", True, [β¦], {β¦}) into POOP types where every operation is a message to a receiver.
POOP is for educational exploration of message-passing semantics inside the Python ecosystem, not for production. Status: stable β breaking changes ship in major releases (1.x β 2.0). POOP is not distributed via PyPI by design; clone and run it locally.
git clone https://github.com/cassiobotaro/poop.git
cd poop
uv sync
uv run poop examples/basics/hello_world.pyContributor / development setup lives in CONTRIBUTING.md.
"Hello, World!".print()Run it:
poop hello.py # run a file
poop # interactive REPL (Ctrl+D to exit)Inside the REPL, colon-prefixed meta-commands help you explore, Smalltalk-browser style:
>>> :methods "abc" # the messages an object understands
>>> :explain if # why a construct is forbidden + the substitute
>>> :help # list the meta-commands
| Python | POOP |
|---|---|
print(x) |
x.print() |
if cond: β¦ / else: |
cond.if_true(lambda: β¦) / cond.if_true_if_false(lambda: β¦, lambda: β¦) |
for x in col: β¦ |
col.do(lambda x: β¦) |
while cond: β¦ |
(lambda: cond).while_true(lambda: β¦) |
len(x) |
x.len() |
x[i] |
x.at(i) |
x[a:b] |
x.slice(a, b) |
not x |
x.not_() |
x and y / x or y |
x.and_(lambda: y) / x.or_(lambda: y) |
-x |
x.negated() |
Full Python β POOP recipe book: MIGRATION.md. Design rationale: INFECTIONS.md.
POOP runs ~65 validators on every program. Grouped by theme:
Control flow β messages on a Boolean, not statements.
if/else/ ternary βcond.if_true(lambda: β¦)/cond.if_true_if_false(lambda: β¦, lambda: β¦)for/while/matchβcol.do(β¦)/(lambda: cond).while_true(β¦)/ polymorphismwith/try/raise/assertβWith(lambda: cm()).do(β¦)/Try(lambda: β¦).except_(ExcType, lambda e: β¦).run()/ValueError.raise_("msg")/obj.assert_("msg")
Free functions β call as a method on the receiver.
print/len/abs/hash/round/pow/divmod/min/max/sum/sorted/reversedβx.print(),x.len(),x.abs(),x.hash(), β¦map/filterβcol.map(lambda x: β¦)/col.filter(lambda x: β¦)ascii/bin/chr/repr/formatβ corresponding methods onInt/Strinput/openβ"prompt".input()/Path("file").read_text()iter(col)βcol.iter()returns an iterator;it.next()advances it
Introspection β call the method on the receiver, or use polymorphism.
isinstance(x, T)/issubclass(C, P)βx.is_instance(T)/C.is_subclass(P)callable(x)/id(x)/dir(x)βx.callable()/x.id()/x.dir()hasattr(x, n)/getattr(x, n)/setattr(x, n, v)βx.has_attr(n)/x.get_attr(n)/x.set_attr(n, v)
Operator sugar β methods on the receiver.
x[i]/x[a:b]βx.at(i)/x.slice(a, b)not x/x and y/x or yβx.not_()/x.and_(lambda: y)/x.or_(lambda: y)-x/+x/~xβx.negated()/ drop the+/x.bit_invert()x is None/x in yβx.is_none()/y.includes(x)(identity viax.is_identical(y))
Syntax shortcuts that hide behaviour.
- comprehensions (
[x for x in β¦],{β¦}, generator exprs) β explicit.map/.filter/.do - top-level
def(free functions) β define as a method inside a class yield/ walrus:=/del/globalβ out of scope
Side-channels.
exec/breakpoint/exitβ interpreter escape hatches forbidden
The full catalog with one row per validator and the substitute recipe lives in INFECTIONS.md.
examples/ ships 45 programs across three subfolders, grouped by what they teach.
Language basics (examples/basics/)
hello_world.pyβ the smallest POOP programgreet.pyβ string input + concatenationfizzbuzz.pyβ control flow viaif_true_if_falseleap_year.pyβand_/or_/not_collatz.pyβ while-style recursiongrades.pyβ collection processinggeometry.pyβ classes with stateslicing.pyβSliceas a reusable value objectbank_account.pyβ encapsulation
Idiomatic POOP (examples/idiomatic/)
pipeline.pyβfilter/filter_false/map/dochainsafe_config.pyβif_none/if_not_nonecascadecommon_interests.pyβ set operationsstatistics.pyβ number aggregationrpn_calculator.pyβ stack as a POOPListroman_numerals.pyβ string mappingasync_greeter.pyβasync def+await asyncio.sleep(since v0.52.0)
Classic OO patterns (Sandi Metz / GoF) (examples/patterns/)
Creational
abstract_factory.pyβ Abstract Factorybuilder.pyβ Builderfactory_method.pyβ Factory Methodprototype.pyβ Prototype (copy.copy)singleton.pyβ Singleton (class-side cached instance)
Structural
adapter.pyβ Adapterbridge.pyβ Bridgetree.pyβ Composite (replacingisinstance)decorators.pyβ Decorator (composition by delegation)facade.pyβ Facadeflyweight.pyβ Flyweight (shared intrinsic state)proxy.pyβ Proxy (lazy-loading virtual proxy)
Behavioral
chain_of_responsibility.pyβ Chain of Responsibilitycommand.pyβ Command (with undo)interpreter.pyβ Interpreteriterator.pyβ Iteratormediator.pyβ Mediatormemento.pyβ Mementoobserver.pyβ Observerdoor.pyβ Statediscounts.pyβ Strategytemplate_method.pyβ Template Methodvisitor.pyβ Visitor
Other OO patterns (Fowler / Beck / Evans / Metz)
null_customer.pyβ Null Objectpayroll.pyβ polymorphism replacingif employee.type == ...specification.pyβ Specification (composable rules replacingand/or/not)money.pyβ Money value objectexecute_around.pyβ Execute Around Methodhouse_jack_built.pyβ recursive composition refactor
Annotations (x: int, def f(x: int) -> str:) are not evaluated at runtime in Python and do not cause errors in POOP programs, but they are misleading: POOP transforms every literal to its own types (Int, Str, β¦), so a variable annotated as int holds an Int at runtime. Avoid annotations in POOP code. type X = int is explicitly banned by the validator pipeline.
MIGRATION.mdβ full Python β POOP recipe bookINFECTIONS.mdβ every validator / transformer / type, with rationaleproposals.mdβ open design backlogCONTRIBUTING.mdβ dev setup, atomic-commit conventions, release flow- Issues β bugs and feature requests
