Skip to content

Repository files navigation

POOP logo

POOP πŸ’©

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. POOP is not distributed via PyPI by design, and there are no tagged releases; clone and run it locally.

Install

git clone https://github.com/cassiobotaro/poop.git
cd poop
uv sync
uv run poop examples/basics/hello_world.py

Contributor / development setup lives in CONTRIBUTING.md.

Hello, POOP

"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

The ten essentials

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.

What's banned (and what to use instead)

POOP runs 71 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(…) / polymorphism
  • with / 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 on Int / Str
  • input β†’ "prompt".input() (open is banned outright β€” POOP has no file I/O)
  • iter(col) β†’ col.iter() returns an iterator; it.next() advances it and it.has_next() asks whether it can, so (lambda: it.has_next()).while_true(…) walks it without running off the end

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) / dir(x) β†’ x.callable() / x.dir(); id(x) β†’ a.is_identical(b) (POOP answers the identity question, never an address)
  • hasattr(x, n) / getattr(x, n) / setattr(x, n, v) β†’ x.has_attr(n) / x.get_attr(n) / x.set_attr(n, v)
  • type(x) / x.__class__ / xs.__len__() β†’ x.class_() / x.class_name() / xs.len() β€” dunder attribute access is banned outright

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 via x.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
  • @decorator β†’ send the message; only @staticmethod / @classmethod / @property are allowed, being class-definition machinery rather than runtime operations

Side-channels.

  • exec / breakpoint / exit β€” interpreter escape hatches forbidden
  • __builtins__ / __loader__ / __spec__ / __name__ β€” dunder globals are raw Python natives (and __builtins__ is mutable), so bare dunder names are banned alongside dunder attributes
  • obj._value / obj._items β€” a leading underscore is another object's own state β†’ obj.get_attr(name), which refuses it by saying so. self, cls and the enclosing class by name stay legal: reaching your own state is not reaching into anything

No library. POOP is the language, not the library β€” it mirrors no stdlib module.

  • import β€” if Python needs an import to reach something, POOP does not offer it. There is no math, no json, no os.
  • The only names injected are Try and With β€” the two constructs replacing try/except and with, which are Python keywords rather than modules.
  • Python's builtins are not there either: user code runs against an allow-list (super, classmethod, staticmethod, property, plus what the class statement needs), so OSError, copyright and the rest answer NameError rather than handing back a live Python object.
  • async def / await follow from this: asyncio was the only way to drive a coroutine, so async has no substitute and is banned outright. So is open β€” there is no file I/O; a program talks through "prompt".input() and obj.print().

The full catalog with one row per validator and the substitute recipe lives in INFECTIONS.md.

Learn by example

examples/ ships 44 programs across three subfolders, grouped by what they teach.

Language basics (examples/basics/)

Idiomatic POOP (examples/idiomatic/)

Classic OO patterns (Sandi Metz / GoF) (examples/patterns/)

Creational

Structural

Behavioral

Other OO patterns (Fowler / Beck / Evans / Metz)

Type annotations

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.

Where to next

About

πŸ’© POOPβ€” a Python interpreter infected by Smalltalk

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages