Skip to content

Commit

Permalink
Add API design document (#3170)
Browse files Browse the repository at this point in the history
Adds a document establishing API design principles for Toga.
  • Loading branch information
mhsmith authored Feb 13, 2025
1 parent 3323845 commit ad05e89
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 3 deletions.
1 change: 1 addition & 0 deletions changes/3170.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a page summarizing Toga's API design principles.
2 changes: 1 addition & 1 deletion core/src/toga/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __lt__(self, other: Widget) -> bool:

@property
def id(self) -> str:
"""A unique identifier for the widget."""
"""A unique identifier for the widget (read-only)."""
return self._id

@property
Expand Down
2 changes: 1 addition & 1 deletion core/src/toga/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def closable(self) -> bool:

@property
def id(self) -> str:
"""A unique identifier for the window."""
"""A unique identifier for the window (read-only)."""
return self._id

@property
Expand Down
5 changes: 5 additions & 0 deletions docs/background/project/philosophy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ button is a button, no matter what platform you're on. But other widgets may
not be exposed so literally. What the Toga API aims to expose is a set of
mechanisms for achieving UI goals, not a literal widget set.

We follow the 80% rule for abstractions - the goal is to provide an API that makes 80%
of use cases possible, with enough internals exposed so that advanced users can get to
the other 20% if they need to. We don't consider 100% coverage of all possible features
to be desirable (or even achievable in many cases).

Python native
-------------

Expand Down
69 changes: 69 additions & 0 deletions docs/how-to/topics/api-design.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
==========
API design
==========

Toga's API is structured around the following principles:

Coding style
============

The public API follows a "Pythonic" style, using Python language idioms (e.g., context
managers and iterators) and naming conventions (e.g., ``snake_case``, not
``CamelCase``), even if the underlying platforms don't lean that way.

Names are spelled according to US English.

Properties
==========

Wherever possible, Toga exposes an object's state using property notation (e.g.
``widget.property``) rather than getter or setter methods.

Properties follow `Postel's Law <https://en.wikipedia.org/wiki/Robustness_principle>`__
– for example, a widget's ``text`` property will accept any object when set, but will
always return a string when when retrieved.

Constructors
============

Any of a class's writable properties can be initialized in its constructor by passing
keyword arguments with the same names. The constructor may also accept read-only
properties such as :any:`Widget.id`, which cannot be changed later.

If a constructor has a single required argument, such as the text of a :any:`Label`, it
may be passed as a positional argument.

Events
======

Events are used to notify your app of user actions. To make your app handle an event,
you can assign either a regular or async callable to an event handler property. These
can be identified by their names, which always begin with ``on_``.

Events are named for the general purpose of the interaction, not the specific mechanism.
For example, a :any:`Button`'s event is called ``on_press``, not ``on_click``, because
"click" implies a mouse is used.

When the event occurs, your handler will be passed the widget as a positional argument,
and other event-specific information as keyword arguments. For forward compatibility
with arguments added in the future, handlers should always declare a ``**kwargs``
argument.

If an event is triggered by a change in a property:

* The new value of the property will be visible within the event handler.
* Setting the property programmatically will also generate an event, unless the property
is set to its existing value, in which case whether it generates an event is
undefined.

Common names
============

When a widget allows the user to control a simple value (e.g. the ``str`` of a
:any:`TextInput`, or the ``bool`` of a :any:`Switch`), then its property is called
``value``, and the corresponding event is called ``on_change``.

When a widget has a non-editable caption, (e.g. a :any:`Button` or :any:`Switch`), then
its property is called ``text``.

Ranges of numbers are expressed as separate ``min`` and ``max`` properties.
1 change: 1 addition & 0 deletions docs/how-to/topics/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ Topic guides
.. toctree::
:maxdepth: 1

api-design
layout
data-sources
4 changes: 3 additions & 1 deletion docs/spelling_wordlist
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ amongst
AndroidX
App
apps
Async
async
awaitable
backend
backends
Expand All @@ -28,6 +28,7 @@ Flexbox
GBulb
GeoClue
geolocation
getter
GMail
GObject
Gradle
Expand Down Expand Up @@ -58,6 +59,7 @@ px
Pygame
PyScript
pytest
pythonic
Quickstart
radiusx
radiusy
Expand Down

0 comments on commit ad05e89

Please sign in to comment.