Exercise Answers: Descriptor Evidence Studio Review¶
Page Maps¶
flowchart LR
evidence["Observed evidence"] --> reasoning["Mechanism and owner"]
reasoning --> limit["What remains unproved"]
limit --> transfer["Capstone or design transfer"]
Use these answers to review reasoning, not to compare prose word for word. A defensible answer names timing, owner, public surface, proof, and limitation.
The answer review should connect evidence back to ownership:
flowchart TD
classvalue["Static class attachment"] --> protocol{"Which hooks exist on its type?"}
protocol --> prediction["Lookup prediction"]
state["Instance dictionary"] --> prediction
prediction --> operation["Deliberate read or write"]
operation --> judgment["Owner, result, and limitation"]
Answer 1: Naming is not descriptor status¶
A protocol inventory can be built from the installed objects:
marker = vars(Delivery)["marker"]
field = vars(Delivery)["endpoint"]
marker_protocol = inspect_descriptor_protocol(marker)
field_protocol = inspect_descriptor_protocol(field)
Expected conclusion:
| Object | __set_name__ |
Access hooks | Classification |
|---|---|---|---|
NameAware |
yes | none | not a descriptor |
StoredField |
yes | __get__, __set__ |
data descriptor |
Reasoning:
- both objects receive names during owner-class creation
- only
StoredFieldparticipates in later attribute access __set_name__configures installation; access hooks establish protocol status
Common wrong turn:
NameAwareis a non-data descriptor because it does not define__set__.
Why it fails: a non-data descriptor still needs __get__.
What this proves:
- the hook sets and installation names of these exact objects
What it does not prove:
- that name-aware helpers are useless
- that every descriptor needs
__set_name__
Capstone transfer:
Field.__set_name__ records metadata, while Field.__get__ and Field.__set__ own
access.
Answer 2: Predict lookup before execution¶
A valid fourth class value is:
With no vars(delivery)["fallback"], the prediction is
"non-data-descriptor". The complete table is:
| Name | Category | Public instance entry | Winner |
|---|---|---|---|
enforced |
data | yes | data descriptor |
shadowable |
non-data | yes | instance dictionary |
fallback |
non-data | no | non-data descriptor |
category |
neither | no | class attribute |
Reasoning:
The descriptor category is determined before deciding where a non-data descriptor sits
relative to instance state. Private _enforced storage does not count as a same-named
public entry.
Common wrong turn:
The instance dictionary wins whenever the name exists there.
Why it fails: data descriptors are checked first.
What this proves:
- the lab predictor agrees with ordinary lookup for these cases
What it does not prove:
- custom
__getattribute__behavior __getattr__fallback behavior
Capstone transfer:
Capstone fields are data descriptors, so a public instance entry cannot shadow their normal reads.
Answer 3: Binding without action execution¶
One review route:
plugin = ConsoleNotifier()
static_value = inspect.getattr_static(ConsoleNotifier, "deliver")
bound = plugin.deliver
assert bound.__func__ is static_value
assert bound.__self__ is plugin
assert plugin.action_history() == []
Reasoning:
- the action decorator owns the wrapper function and
ActionSpec - the function type's
__get__owns binding - the plugin instance becomes the method receiver
- retrieving the method does not invoke it
Common wrong turn:
PluginMetabinds the method because it collected the action.
Why it fails: collection happens during class creation; binding happens during instance attribute access.
What this proves:
- function and receiver identities
- absence of action execution at inspection time
What it does not prove:
- successful delivery behavior
- metadata preservation beyond the inspected identities
Capstone transfer:
Use action-wrapper for wrapper ownership and method identity for binding ownership.
They answer different questions about the same public name.
Answer 4: Extend quantity conversion¶
A valid declaration is:
class DeliveryTiming:
timeout = Quantity(
"s",
{"s": 1.0, "ms": 0.001, "min": 60.0},
minimum=0.0,
maximum=300.0,
)
Focused proof:
first = DeliveryTiming()
second = DeliveryTiming()
first.timeout = (2, "min")
second.timeout = 30
assert vars(first) == {"_timeout_canonical": 120.0}
assert vars(second) == {"_timeout_canonical": 30.0}
assert first.timeout.in_unit("min") == 2.0
with self.assertRaisesRegex(ValueError, "must be <= 300.0 s"):
first.timeout = (6, "min")
Reasoning:
Bounds apply after conversion because storage and comparison use canonical seconds.
Common wrong turn:
Compare
6with300before applying the unit.
Why it fails: six minutes is 360 canonical seconds.
What this proves:
- minutes use the declared factor
- range checks use canonical values
- two instances remain independent
What it does not prove:
- decimal-exact conversion
- parsing strings such as
"2 min"
Application connection:
The capstone field family similarly coerces before storing, but it does not model units.
Answer 5: Schema and ownership are complementary¶
make capstone-field answers:
What public configuration contract does
endpointexport?
It reports name, kind, description, required/default status, and minimum length.
make capstone-field-ownership answers:
Which runtime objects implement that contract?
It reports:
{
"declaring_class": "WebhookNotifier",
"descriptor_kind": "data",
"descriptor_type": "StringField",
"hooks": ["__get__", "__set__", "__set_name__"],
"storage_name": "_endpoint",
"storage_owner": "plugin-instance.__dict__",
"constructed": false,
"executed": false
}
Reasoning:
Schema is a public declaration. Ownership evidence explains the mechanism behind it. Neither output substitutes for the field tests that execute coercion and rejection.
Common wrong turn:
Because the metaclass collected the field, the metaclass owns validation.
Why it fails: PluginMeta owns collection and constructor generation; StringField
owns coercion, validation, reads, and writes.
What this proves:
- static descriptor ownership without plugin construction
What it does not prove:
- that every raw value is accepted or rejected correctly
- that direct private storage mutation is impossible
Answer 6: Ownership review¶
| Requirement | Chosen owner | Rejected owner | Deciding pressure |
|---|---|---|---|
| one class computes a display label | property | custom descriptor | no reuse or stored-field contract |
| many plugin classes reuse bounded integers | descriptor | repeated properties | repeated field semantics and metadata |
| all assignments fail after initialization | __setattr__ or a bounded freezing policy |
one descriptor per field | rule spans the whole object |
| duplicate fields fail during class-body execution | prepared namespace or metaclass | descriptor | failure must happen before duplicate dictionary state is lost |
| one method performs delivery I/O | method | descriptor property | effect must remain explicit |
Reasoning:
Each owner matches the time and width of the requirement. The descriptor row is the only one whose rule is both attribute-local and repeated.
Common wrong turn:
Use descriptors for every row because the module is about descriptors.
Why it fails: mechanism practice without ownership judgment creates over-engineered APIs.
What this proves:
- the reviewer can place requirements on the power ladder
What it does not prove:
- that the chosen production implementation handles every inheritance or concurrency concern
Capstone transfer:
The capstone uses all three relevant owners deliberately: descriptors for fields, wrappers for actions, and a metaclass for class-family creation.
Final review standard¶
A complete studio packet should make these statements defensible:
__set_name__and descriptor status occur at different boundaries- data/non-data precedence predicts a concrete conflict
- method binding retains inspectable function and receiver identities
- descriptor configuration is shared while instance values are not
- the quantity example earns reuse through repeated field semantics
- capstone field schema and capstone ownership evidence answer different questions
If your packet states only results, add the owner and timing. If it states only mechanism, add the design judgment and limitation.
Continue through Module 07¶
- Previous: Exercises
- Return: Module 07 Overview
- Terms: Glossary