Document preserves.schema

This commit is contained in:
Tony Garnock-Jones 2023-03-17 15:24:53 +01:00
parent 108119a351
commit 316c2dd407
10 changed files with 1844 additions and 275 deletions

View File

@ -0,0 +1,16 @@
A Preserves schema connects Preserves `Value`s to host-language data
structures. Each definition within a schema can be processed by a
compiler to produce
- a simple host-language *type definition*;
- a partial *parsing* function from `Value`s to instances of the
produced type; and
- a total *serialization* function from instances of the type to
`Value`s.
Every parsed `Value` retains enough information to always be able to
be serialized again, and every instance of a host-language data
structure contains, by construction, enough information to be
successfully serialized.

View File

@ -1,3 +1,7 @@
# Preserves Schema
{% include "what-is-preserves-schema.md" %}
## Schema support in Python
::: preserves.schema

View File

@ -0,0 +1,40 @@
´³bundle·µ³tcp„´³schema·³version³ definitions·³TcpLocal´³rec´³lit³ tcp-local„´³tupleµ´³named³host´³atom³String„„´³named³port´³atom³ SignedInteger„„„„„³ TcpRemote´³rec´³lit³
tcp-remote„´³tupleµ´³named³host´³atom³String„„´³named³port´³atom³ SignedInteger„„„„„³ TcpPeerInfo´³rec´³lit³tcp-peer„´³tupleµ´³named³handle´³embedded³any„„´³named³local´³refµ„³TcpLocal„„´³named³remote´³refµ„³ TcpRemote„„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³http„´³schema·³version³ definitions·³Chunk´³orµµ±string´³atom³String„„µ±bytes´³atom³
ByteString„„„„³Headers´³dictof´³atom³Symbol„´³atom³String„„³MimeType´³atom³Symbol„³
QueryValue´³orµµ±string´³atom³String„„µ±file´³rec´³lit³file„´³tupleµ´³named³filename´³atom³String„„´³named³headers´³refµ„³Headers„„´³named³body´³atom³
ByteString„„„„„„„„³ HostPattern´³orµµ±host´³atom³String„„µ±any´³lit€„„„„³ HttpBinding´³rec´³lit³ http-bind„´³tupleµ´³named³host´³refµ„³ HostPattern„„´³named³port´³atom³ SignedInteger„„´³named³method´³refµ„³ MethodPattern„„´³named³path´³refµ„³ PathPattern„„´³named³handler´³embedded´³refµ„³ HttpRequest„„„„„„³ HttpContext´³rec´³lit³request„´³tupleµ´³named³req´³refµ„³ HttpRequest„„´³named³res´³embedded´³refµ„³ HttpResponse„„„„„„³ HttpRequest´³rec´³lit³ http-request„´³tupleµ´³named³sequenceNumber´³atom³ SignedInteger„„´³named³host´³atom³String„„´³named³port´³atom³ SignedInteger„„´³named³method´³atom³Symbol„„´³named³path´³seqof´³atom³String„„„´³named³headers´³refµ„³Headers„„´³named³query´³dictof´³atom³Symbol„´³seqof´³refµ„³
QueryValue„„„„´³named³body´³refµ„³ RequestBody„„„„„³ HttpService´³rec´³lit³ http-service„´³tupleµ´³named³host´³refµ„³ HostPattern„„´³named³port´³atom³ SignedInteger„„´³named³method´³refµ„³ MethodPattern„„´³named³path´³refµ„³ PathPattern„„„„„³ PathPattern´³seqof´³refµ„³PathPatternElement„„³ RequestBody´³orµµ±present´³atom³
ByteString„„µ±absent´³lit€„„„„³ HttpListener´³rec´³lit³ http-listener„´³tupleµ´³named³port´³atom³ SignedInteger„„„„„³ HttpResponse´³orµµ±status´³rec´³lit³status„´³tupleµ´³named³code´³atom³ SignedInteger„„´³named³message´³atom³String„„„„„„µ±header´³rec´³lit³header„´³tupleµ´³named³name´³atom³Symbol„„´³named³value´³atom³String„„„„„„µ±chunk´³rec´³lit³chunk„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„µ±done´³rec´³lit³done„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„„„³ MethodPattern´³orµµ±any´³lit€„„µ±specific´³atom³Symbol„„„„³PathPatternElement´³orµµ±label´³atom³String„„µ±wildcard´³lit³_„„µ±rest´³lit³...„„„„„³ embeddedType€„„µ³noise„´³schema·³version³ definitions·³Packet´³orµµ±complete´³atom³
ByteString„„µ±
fragmented´³seqof´³atom³
ByteString„„„„„³ NoiseSpec´³andµ´³dict·³key´³named³key´³atom³
ByteString„„³service´³named³service´³refµ„³ServiceSelector„„„„´³named³protocol´³refµ„³ NoiseProtocol„„´³named³ preSharedKeys´³refµ„³NoisePreSharedKeys„„„„³ NoiseProtocol´³orµµ±present´³dict·³protocol´³named³protocol´³atom³String„„„„„µ±invalid´³dict·³protocol´³named³protocol³any„„„„µ±absent´³dict·„„„„„³ NoiseStepType´³lit³noise„³SecretKeyField´³orµµ±present´³dict·³ secretKey´³named³ secretKey´³atom³
ByteString„„„„„µ±invalid´³dict·³ secretKey´³named³ secretKey³any„„„„µ±absent´³dict·„„„„„³DefaultProtocol´³lit±!Noise_NK_25519_ChaChaPoly_BLAKE2s„³NoiseStepDetail´³refµ„³ServiceSelector„³ServiceSelector³any³NoiseServiceSpec´³andµ´³named³base´³refµ„³ NoiseSpec„„´³named³ secretKey´³refµ„³SecretKeyField„„„„³NoisePreSharedKeys´³orµµ±present´³dict·³ preSharedKeys´³named³ preSharedKeys´³seqof´³atom³
ByteString„„„„„„µ±invalid´³dict·³ preSharedKeys´³named³ preSharedKeys³any„„„„µ±absent´³dict·„„„„„³NoisePathStepDetail´³refµ„³ NoiseSpec„³NoiseDescriptionDetail´³refµ„³NoiseServiceSpec„„³ embeddedType€„„µ³timer„´³schema·³version³ definitions·³SetTimer´³rec´³lit³ set-timer„´³tupleµ´³named³label³any„´³named³seconds´³atom³Double„„´³named³kind´³refµ„³ TimerKind„„„„„³ LaterThan´³rec´³lit³
later-than„´³tupleµ´³named³seconds´³atom³Double„„„„„³ TimerKind´³orµµ±relative´³lit³relative„„µ±absolute´³lit³absolute„„µ±clear´³lit³clear„„„„³ TimerExpired´³rec´³lit³ timer-expired„´³tupleµ´³named³label³any„´³named³seconds´³atom³Double„„„„„„³ embeddedType€„„µ³trace„´³schema·³version³ definitions·³Oid³any³Name´³orµµ± anonymous´³rec´³lit³ anonymous„´³tupleµ„„„„µ±named´³rec´³lit³named„´³tupleµ´³named³name³any„„„„„„„³Target´³rec´³lit³entity„´³tupleµ´³named³actor´³refµ„³ActorId„„´³named³facet´³refµ„³FacetId„„´³named³oid´³refµ„³Oid„„„„„³TaskId³any³TurnId³any³ActorId³any³FacetId³any³ TurnCause´³orµµ±turn´³rec´³lit³ caused-by„´³tupleµ´³named³id´³refµ„³TurnId„„„„„„µ±cleanup´³rec´³lit³cleanup„´³tupleµ„„„„µ±linkedTaskRelease´³rec´³lit³linked-task-release„´³tupleµ´³named³id´³refµ„³TaskId„„´³named³reason´³refµ„³LinkedTaskReleaseReason„„„„„„µ±periodicActivation´³rec´³lit³periodic-activation„´³tupleµ´³named³period´³atom³Double„„„„„„µ±delay´³rec´³lit³delay„´³tupleµ´³named³ causingTurn´³refµ„³TurnId„„´³named³amount´³atom³Double„„„„„„µ±external´³rec´³lit³external„´³tupleµ´³named³ description³any„„„„„„„³ TurnEvent´³orµµ±assert´³rec´³lit³assert„´³tupleµ´³named³ assertion´³refµ„³AssertionDescription„„´³named³handle´³refµ³protocol„³Handle„„„„„„µ±retract´³rec´³lit³retract„´³tupleµ´³named³handle´³refµ³protocol„³Handle„„„„„„µ±message´³rec´³lit³message„´³tupleµ´³named³body´³refµ„³AssertionDescription„„„„„„µ±sync´³rec´³lit³sync„´³tupleµ´³named³peer´³refµ„³Target„„„„„„µ± breakLink´³rec´³lit³
break-link„´³tupleµ´³named³source´³refµ„³ActorId„„´³named³handle´³refµ³protocol„³Handle„„„„„„„„³
ExitStatus´³orµµ±ok´³lit³ok„„µ±Error´³refµ³protocol„³Error„„„„³
TraceEntry´³rec´³lit³trace„´³tupleµ´³named³ timestamp´³atom³Double„„´³named³actor´³refµ„³ActorId„„´³named³item´³refµ„³ActorActivation„„„„„³ActorActivation´³orµµ±start´³rec´³lit³start„´³tupleµ´³named³ actorName´³refµ„³Name„„„„„„µ±turn´³refµ„³TurnDescription„„µ±stop´³rec´³lit³stop„´³tupleµ´³named³status´³refµ„³
ExitStatus„„„„„„„„³FacetStopReason´³orµµ±explicitAction´³lit³explicit-action„„µ±inert´³lit³inert„„µ±parentStopping´³lit³parent-stopping„„µ± actorStopping´³lit³actor-stopping„„„„³TurnDescription´³rec´³lit³turn„´³tupleµ´³named³id´³refµ„³TurnId„„´³named³cause´³refµ„³ TurnCause„„´³named³actions´³seqof´³refµ„³ActionDescription„„„„„„³ActionDescription´³orµµ±dequeue´³rec´³lit³dequeue„´³tupleµ´³named³event´³refµ„³TargetedTurnEvent„„„„„„µ±enqueue´³rec´³lit³enqueue„´³tupleµ´³named³event´³refµ„³TargetedTurnEvent„„„„„„µ±dequeueInternal´³rec´³lit³dequeue-internal„´³tupleµ´³named³event´³refµ„³TargetedTurnEvent„„„„„„µ±enqueueInternal´³rec´³lit³enqueue-internal„´³tupleµ´³named³event´³refµ„³TargetedTurnEvent„„„„„„µ±spawn´³rec´³lit³spawn„´³tupleµ´³named³link´³atom³Boolean„„´³named³id´³refµ„³ActorId„„„„„„µ±link´³rec´³lit³link„´³tupleµ´³named³ parentActor´³refµ„³ActorId„„´³named³ childToParent´³refµ³protocol„³Handle„„´³named³
childActor´³refµ„³ActorId„„´³named³ parentToChild´³refµ³protocol„³Handle„„„„„„µ±
facetStart´³rec´³lit³ facet-start„´³tupleµ´³named³path´³seqof´³refµ„³FacetId„„„„„„„µ± facetStop´³rec´³lit³
facet-stop„´³tupleµ´³named³path´³seqof´³refµ„³FacetId„„„´³named³reason´³refµ„³FacetStopReason„„„„„„µ±linkedTaskStart´³rec´³lit³linked-task-start„´³tupleµ´³named³taskName´³refµ„³Name„„´³named³id´³refµ„³TaskId„„„„„„„„³TargetedTurnEvent´³rec´³lit³event„´³tupleµ´³named³target´³refµ„³Target„„´³named³detail´³refµ„³ TurnEvent„„„„„³AssertionDescription´³orµµ±value´³rec´³lit³value„´³tupleµ´³named³value³any„„„„„µ±opaque´³rec´³lit³opaque„´³tupleµ´³named³ description³any„„„„„„„³LinkedTaskReleaseReason´³orµµ± cancelled´³lit³ cancelled„„µ±normal´³lit³normal„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³stream„´³schema·³version³ definitions·³Mode´³orµµ±bytes´³lit³bytes„„µ±lines´³refµ„³LineMode„„µ±packet´³rec´³lit³packet„´³tupleµ´³named³size´³atom³ SignedInteger„„„„„„µ±object´³rec´³lit³object„´³tupleµ´³named³ description³any„„„„„„„³Sink´³orµµ±source´³rec´³lit³source„´³tupleµ´³named³
controller´³embedded´³refµ„³Source„„„„„„„µ± StreamError´³refµ„³ StreamError„„µ±data´³rec´³lit³data„´³tupleµ´³named³payload³any„´³named³mode´³refµ„³Mode„„„„„„µ±eof´³rec´³lit³eof„´³tupleµ„„„„„„³Source´³orµµ±sink´³rec´³lit³sink„´³tupleµ´³named³
controller´³embedded´³refµ„³Sink„„„„„„„µ± StreamError´³refµ„³ StreamError„„µ±credit´³rec´³lit³credit„´³tupleµ´³named³amount´³refµ„³ CreditAmount„„´³named³mode´³refµ„³Mode„„„„„„„„³LineMode´³orµµ±lf´³lit³lf„„µ±crlf´³lit³crlf„„„„³ StreamError´³rec´³lit³error„´³tupleµ´³named³message´³atom³String„„„„„³ CreditAmount´³orµµ±count´³atom³ SignedInteger„„µ± unbounded´³lit³ unbounded„„„„³StreamConnection´³rec´³lit³stream-connection„´³tupleµ´³named³source´³embedded´³refµ„³Source„„„´³named³sink´³embedded´³refµ„³Sink„„„´³named³spec³any„„„„³StreamListenerError´³rec´³lit³stream-listener-error„´³tupleµ´³named³spec³any„´³named³message´³atom³String„„„„„³StreamListenerReady´³rec´³lit³stream-listener-ready„´³tupleµ´³named³spec³any„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³sturdy„´³schema·³version³ definitions·³Lit´³rec´³lit³lit„´³tupleµ´³named³value³any„„„„³Oid´³atom³ SignedInteger„³Alts´³rec´³lit³or„´³tupleµ´³named³ alternatives´³seqof´³refµ„³Rewrite„„„„„„³PAnd´³rec´³lit³and„´³tupleµ´³named³patterns´³seqof´³refµ„³Pattern„„„„„„³PNot´³rec´³lit³not„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³TRef´³rec´³lit³ref„´³tupleµ´³named³binding´³atom³ SignedInteger„„„„„³PAtom´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ±
ByteString´³lit³
ByteString„„µ±Symbol´³lit³Symbol„„„„³PBind´³rec´³lit³bind„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³Caveat´³orµµ±Rewrite´³refµ„³Rewrite„„µ±Alts´³refµ„³Alts„„µ±Reject´³refµ„³Reject„„µ±unknown³any„„„³Reject´³rec´³lit³reject„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³Pattern´³orµµ±PDiscard´³refµ„³PDiscard„„µ±PAtom´³refµ„³PAtom„„µ± PEmbedded´³refµ„³ PEmbedded„„µ±PBind´³refµ„³PBind„„µ±PAnd´³refµ„³PAnd„„µ±PNot´³refµ„³PNot„„µ±Lit´³refµ„³Lit„„µ± PCompound´³refµ„³ PCompound„„„„³Rewrite´³rec´³lit³rewrite„´³tupleµ´³named³pattern´³refµ„³Pattern„„´³named³template´³refµ„³Template„„„„„³WireRef´³orµµ±mine´³tupleµ´³lit<69>´³named³oid´³refµ„³Oid„„„„„µ±yours´³ tuplePrefixµ´³lit´³named³oid´³refµ„³Oid„„„´³named³ attenuation´³seqof´³refµ„³Caveat„„„„„„„³PDiscard´³rec´³lit³_„´³tupleµ„„„³Template´³orµµ±
TAttenuate´³refµ„³
TAttenuate„„µ±TRef´³refµ„³TRef„„µ±Lit´³refµ„³Lit„„µ± TCompound´³refµ„³ TCompound„„„„³ PCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Pattern„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Pattern„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Pattern„„„„„„„„„³ PEmbedded´³lit³Embedded„³ SturdyRef´³rec´³lit³ref„´³tupleµ´³named³
parameters´³refµ„³
Parameters„„„„„³ TCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Template„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Template„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Template„„„„„„„„„³
Parameters´³andµ´³dict·³oid´³named³oid³any„³sig´³named³sig´³atom³
ByteString„„„„´³named³caveats´³refµ„³ CaveatsField„„„„³
TAttenuate´³rec´³lit³ attenuate„´³tupleµ´³named³template´³refµ„³Template„„´³named³ attenuation´³seqof´³refµ„³Caveat„„„„„„³ CaveatsField´³orµµ±present´³dict·³caveats´³named³caveats´³seqof´³refµ„³Caveat„„„„„„µ±invalid´³dict·³caveats´³named³caveats³any„„„„µ±absent´³dict·„„„„„³SturdyStepType´³lit³ref„³SturdyStepDetail´³refµ„³
Parameters„³SturdyPathStepDetail´³refµ„³
Parameters„³SturdyDescriptionDetail´³dict·³key´³named³key´³atom³
ByteString„„³oid´³named³oid³any„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³worker„´³schema·³version³ definitions·³Instance´³rec´³lit³Instance„´³tupleµ´³named³name´³atom³String„„´³named³argument³any„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³service„´³schema·³version³ definitions·³State´³orµµ±started´³lit³started„„µ±ready´³lit³ready„„µ±failed´³lit³failed„„µ±complete´³lit³complete„„µ± userDefined³any„„„³
RunService´³rec´³lit³ run-service„´³tupleµ´³named³ serviceName³any„„„„³ ServiceState´³rec´³lit³ service-state„´³tupleµ´³named³ serviceName³any„´³named³state´³refµ„³State„„„„„³ ServiceObject´³rec´³lit³service-object„´³tupleµ´³named³ serviceName³any„´³named³object³any„„„„³RequireService´³rec´³lit³require-service„´³tupleµ´³named³ serviceName³any„„„„³RestartService´³rec´³lit³restart-service„´³tupleµ´³named³ serviceName³any„„„„³ServiceDependency´³rec´³lit³
depends-on„´³tupleµ´³named³depender³any„´³named³dependee´³refµ„³ ServiceState„„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³protocol„´³schema·³version³ definitions·³Oid´³atom³ SignedInteger„³Sync´³rec´³lit³sync„´³tupleµ´³named³peer´³embedded´³lit<69>„„„„„„³Turn´³seqof´³refµ„³ TurnEvent„„³Error´³rec´³lit³error„´³tupleµ´³named³message´³atom³String„„´³named³detail³any„„„„³Event´³orµµ±Assert´³refµ„³Assert„„µ±Retract´³refµ„³Retract„„µ±Message´³refµ„³Message„„µ±Sync´³refµ„³Sync„„„„³Assert´³rec´³lit³assert„´³tupleµ´³named³ assertion´³refµ„³ Assertion„„´³named³handle´³refµ„³Handle„„„„„³Handle´³atom³ SignedInteger„³Packet´³orµµ±Turn´³refµ„³Turn„„µ±Error´³refµ„³Error„„µ± Extension´³refµ„³ Extension„„„„³Message´³rec´³lit³message„´³tupleµ´³named³body´³refµ„³ Assertion„„„„„³Retract´³rec´³lit³retract„´³tupleµ´³named³handle´³refµ„³Handle„„„„„³ Assertion³any³ Extension´³rec´³named³label³any„´³named³fields´³seqof³any„„„³ TurnEvent´³tupleµ´³named³oid´³refµ„³Oid„„´³named³event´³refµ„³Event„„„„„³ embeddedType€„„µ³ dataspace„´³schema·³version³ definitions·³Observe´³rec´³lit³Observe„´³tupleµ´³named³pattern´³refµ³dataspacePatterns„³Pattern„„´³named³observer´³embedded³any„„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³
gatekeeper„´³schema·³version³ definitions·³Bind´³rec´³lit³bind„´³tupleµ´³named³ description´³refµ„³ Description„„´³named³target´³embedded³any„„´³named³observer´³refµ„³ BindObserver„„„„„³Step´³rec´³named³stepType´³atom³Symbol„„´³tupleµ´³named³detail³any„„„„³Bound´³orµµ±bound´³rec´³lit³bound„´³tupleµ´³named³pathStep´³refµ„³PathStep„„„„„„µ±Rejected´³refµ„³Rejected„„„„³Route´³rec´³lit³route„´³ tuplePrefixµ´³named³
transports´³seqof³any„„„´³named³ pathSteps´³seqof´³refµ„³PathStep„„„„„³Resolve´³rec´³lit³resolve„´³tupleµ´³named³step´³refµ„³Step„„´³named³observer´³embedded´³refµ„³Resolved„„„„„„³PathStep´³rec´³named³stepType´³atom³Symbol„„´³tupleµ´³named³detail³any„„„„³Rejected´³rec´³lit³rejected„´³tupleµ´³named³detail³any„„„„³Resolved´³orµµ±accepted´³rec´³lit³accepted„´³tupleµ´³named³responderSession´³embedded³any„„„„„„µ±Rejected´³refµ„³Rejected„„„„³ Description´³rec´³named³stepType´³atom³Symbol„„´³tupleµ´³named³detail³any„„„„³ ResolvePath´³rec´³lit³ resolve-path„´³tupleµ´³named³route´³refµ„³Route„„´³named³addr³any„´³named³control´³embedded´³refµ„³TransportControl„„„´³named³resolved´³refµ„³Resolved„„„„„³ BindObserver´³orµµ±present´³embedded´³refµ„³Bound„„„µ±absent´³lit€„„„„³ForceDisconnect´³rec´³lit³force-disconnect„´³tupleµ„„„³ResolvedPathStep´³rec´³lit³ path-step„´³tupleµ´³named³origin´³embedded´³refµ„³Resolve„„„´³named³pathStep´³refµ„³PathStep„„´³named³resolved´³refµ„³Resolved„„„„„³TransportControl´³refµ„³ForceDisconnect„³TransportConnection´³rec´³lit³connect-transport„´³tupleµ´³named³addr³any„´³named³control´³embedded´³refµ„³TransportControl„„„´³named³resolved´³refµ„³Resolved„„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„µ³transportAddress„´³schema·³version³ definitions·³Tcp´³rec´³lit³tcp„´³tupleµ´³named³host´³atom³String„„´³named³port´³atom³ SignedInteger„„„„„³Unix´³rec´³lit³unix„´³tupleµ´³named³path´³atom³String„„„„„³Stdio´³rec´³lit³stdio„´³tupleµ„„„³ WebSocket´³rec´³lit³ws„´³tupleµ´³named³url´³atom³String„„„„„„³ embeddedType€„„µ³dataspacePatterns„´³schema·³version³ definitions·³DLit´³rec´³lit³lit„´³tupleµ´³named³value´³refµ„³AnyAtom„„„„„³DBind´³rec´³lit³bind„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³AnyAtom´³orµµ±bool´³atom³Boolean„„µ±float´³atom³Float„„µ±double´³atom³Double„„µ±int´³atom³ SignedInteger„„µ±string´³atom³String„„µ±bytes´³atom³
ByteString„„µ±symbol´³atom³Symbol„„µ±embedded´³embedded³any„„„„³Pattern´³orµµ±DDiscard´³refµ„³DDiscard„„µ±DBind´³refµ„³DBind„„µ±DLit´³refµ„³DLit„„µ± DCompound´³refµ„³ DCompound„„„„³DDiscard´³rec´³lit³_„´³tupleµ„„„³ DCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Pattern„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Pattern„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Pattern„„„„„„„„„„³ embeddedType´³refµ³ EntityRef„³Cap„„„„„

View File

@ -1,7 +1,266 @@
"""This is an implementation of [Preserves Schema](https://preserves.dev/preserves-schema.html)
for Python 3.
"""The [preserves.schema][] module implements [Preserves
Schema](https://preserves.dev/preserves-schema.html) for Python.
A Schema source file (like [this one](https://preserves.dev/schema/schema.prs)) is first
compiled using [`preserves-schemac`](https://preserves.dev/doc/preserves-schemac.html) to
produce a binary-syntax *schema bundle* containing schema module definitons (like [this
one](https://preserves.dev/preserves-schema.html#appendix-metaschema-instance)). Python code
then loads the bundle, exposing its contents as [Namespace][preserves.schema.Namespace]s
ultimately containing [SchemaObject][preserves.schema.SchemaObject]s.
## Examples
### Setup: Loading a schema bundle
For our running example, we will use schemas associated with the [Syndicated Actor
Model](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols). (The schema bundle
is a copy of [this
file](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/branch/main/schema-bundle.bin)
from the `syndicate-protocols` repository.)
To load a schema bundle, use [load_schema_file][preserves.schema.load_schema_file] (or,
alternatively, use [Compiler][preserves.schema.Compiler] directly):
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> type(bundle)
<class 'preserves.schema.Namespace'>
```
The top-level entries in the loaded bundle are schema modules. Let's examine the `stream`
schema module, whose [source
code](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/commit/d8a139b23a40bad6698f9f4240f9e8426b4a123f/schemas/stream.prs)
indicates that it should contain definitions for `Mode`, `Source`, `Sink`, etc.:
```python
>>> bundle.stream # doctest: +ELLIPSIS
{'Mode': <class 'stream.Mode'>, 'Sink': <class 'stream.Sink'>, ...}
```
### Example 1: stream.StreamListenerError, a product type
Drilling down further, let's consider the [definition of
StreamListenerError](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/commit/d8a139b23a40bad6698f9f4240f9e8426b4a123f/schemas/stream.prs#L9), which appears in the source as
```
StreamListenerError = <stream-listener-error @spec any @message string> .
```
This reads, in the [Preserves Schema
language](https://preserves.dev/preserves-schema.html#the-preserves-schema-language), as the
definition of a simple product type (record, class, object) with two named fields `spec` and
`message`. Parsing a value into a `StreamListenerError` will only succeed if it's a record, if
the label matches, the second field (`message`) is a string, and it has exactly two fields.
```python
>>> bundle.stream.StreamListenerError
<class 'stream.StreamListenerError'>
```
The `StreamListenerError` class includes a [decode][preserves.schema.SchemaObject.decode]
method that analyzes an input value:
```python
>>> bundle.stream.StreamListenerError.decode(
... parse('<stream-listener-error <xyz> "an error">'))
StreamListenerError {'spec': #xyz(), 'message': 'an error'}
```
If invalid input is supplied, [decode][preserves.schema.SchemaObject.decode] will raise
[SchemaDecodeFailed][preserves.schema.SchemaDecodeFailed], which includes helpful information
for diagnosing the problem (as we will see below, this is especially useful for parsers for sum
types):
```python
>>> bundle.stream.StreamListenerError.decode(
... parse('<i-am-invalid>'))
Traceback (most recent call last):
...
preserves.schema.SchemaDecodeFailed: Could not decode i-am-invalid using <class 'stream.StreamListenerError'>
Most likely reason: in stream.StreamListenerError: <lit stream-listener-error> didn't match i-am-invalid
Full explanation:
in stream.StreamListenerError: <lit stream-listener-error> didn't match i-am-invalid
```
Alternatively, the [try_decode][preserves.schema.SchemaObject.try_decode] method catches
[SchemaDecodeFailed][preserves.schema.SchemaDecodeFailed], transforming it into `None`:
```python
>>> bundle.stream.StreamListenerError.try_decode(
... parse('<stream-listener-error <xyz> "an error">'))
StreamListenerError {'spec': #xyz(), 'message': 'an error'}
>>> bundle.stream.StreamListenerError.try_decode(
... parse('<i-am-invalid>'))
```
The class can also be instantiated directly:
```python
>>> err = bundle.stream.StreamListenerError(Record(Symbol('xyz'), []), 'an error')
>>> err
StreamListenerError {'spec': #xyz(), 'message': 'an error'}
```
The fields and contents of instances can be queried:
```python
>>> err.spec
#xyz()
>>> err.message
'an error'
```
And finally, instances can of course be serialized and encoded:
```python
>>> print(stringify(err))
<stream-listener-error <xyz> "an error">
>>> canonicalize(err)
b'\\xb4\\xb3\\x15stream-listener-error\\xb4\\xb3\\x03xyz\\x84\\xb1\\x08an error\\x84'
```
### Example 2: stream.Mode, a sum type
Now let's consider the [definition of
Mode](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/commit/d8a139b23a40bad6698f9f4240f9e8426b4a123f/schemas/stream.prs#L37),
which appears in the source as
```
Mode = =bytes / @lines LineMode / <packet @size int> / <object @description any> .
```
This reads, in the [Preserves Schema
language](https://preserves.dev/preserves-schema.html#the-preserves-schema-language), as an
alternation (disjoint union, variant, sum type) of four possible kinds of value: the symbol
`bytes`; a `LineMode` value; a record with `packet` as its label and an integer as its only
field; or a record with `object` as its label and any kind of value as its only field. In
Python, this becomes:
```python
>>> bundle.stream.Mode.bytes
<class 'stream.Mode.bytes'>
>>> bundle.stream.Mode.lines
<class 'stream.Mode.lines'>
>>> bundle.stream.Mode.packet
<class 'stream.Mode.packet'>
>>> bundle.stream.Mode.object
<class 'stream.Mode.object'>
```
As before, `Mode` includes a [decode][preserves.schema.SchemaObject.decode] method that analyzes
an input value:
```python
>>> bundle.stream.Mode.decode(parse('bytes'))
Mode.bytes()
>>> bundle.stream.Mode.decode(parse('lf'))
Mode.lines(LineMode.lf())
>>> bundle.stream.Mode.decode(parse('<packet 123>'))
Mode.packet {'size': 123}
>>> bundle.stream.Mode.decode(parse('<object "?">'))
Mode.object {'description': '?'}
```
Invalid input causes [SchemaDecodeFailed][preserves.schema.SchemaDecodeFailed] to be raised:
```python
>>> bundle.stream.Mode.decode(parse('<i-am-not-a-valid-mode>'))
Traceback (most recent call last):
...
preserves.schema.SchemaDecodeFailed: Could not decode <i-am-not-a-valid-mode> using <class 'stream.Mode'>
Most likely reason: in stream.LineMode.crlf: <lit crlf> didn't match <i-am-not-a-valid-mode>
Full explanation:
in stream.Mode: matching <i-am-not-a-valid-mode>
in stream.Mode.bytes: <lit bytes> didn't match <i-am-not-a-valid-mode>
in stream.Mode.lines: <ref [] LineMode> didn't match <i-am-not-a-valid-mode>
in stream.LineMode: matching <i-am-not-a-valid-mode>
in stream.LineMode.lf: <lit lf> didn't match <i-am-not-a-valid-mode>
in stream.LineMode.crlf: <lit crlf> didn't match <i-am-not-a-valid-mode>
in stream.Mode.packet: <lit packet> didn't match i-am-not-a-valid-mode
in stream.Mode.object: <lit object> didn't match i-am-not-a-valid-mode
```
The "full explanation" includes details on which parses were attempted, and why they failed.
Again, the [try_decode][preserves.schema.SchemaObject.try_decode] method catches
[SchemaDecodeFailed][preserves.schema.SchemaDecodeFailed], transforming it into `None`:
```python
>>> bundle.stream.Mode.try_decode(parse('bytes'))
Mode.bytes()
>>> bundle.stream.Mode.try_decode(parse('<i-am-not-a-valid-mode>'))
```
Direct instantiation is done with the variant classes, not with `Mode` itself:
```python
>>> bundle.stream.Mode.bytes()
Mode.bytes()
>>> bundle.stream.Mode.lines(bundle.stream.LineMode.lf())
Mode.lines(LineMode.lf())
>>> bundle.stream.Mode.packet(123)
Mode.packet {'size': 123}
>>> bundle.stream.Mode.object('?')
Mode.object {'description': '?'}
```
Fields and contents can be queried as usual:
```python
>>> bundle.stream.Mode.lines(bundle.stream.LineMode.lf()).value
LineMode.lf()
>>> bundle.stream.Mode.packet(123).size
123
>>> bundle.stream.Mode.object('?').description
'?'
```
And serialization and encoding are also as expected:
```python
>>> print(stringify(bundle.stream.Mode.bytes()))
bytes
>>> print(stringify(bundle.stream.Mode.lines(bundle.stream.LineMode.lf())))
lf
>>> print(stringify(bundle.stream.Mode.packet(123)))
<packet 123>
>>> print(stringify(bundle.stream.Mode.object('?')))
<object "?">
>>> canonicalize(bundle.stream.Mode.object('?'))
b'\\xb4\\xb3\\x06object\\xb1\\x01?\\x84'
```
Finally, the [VARIANT][preserves.schema.SchemaObject.VARIANT] attribute of instances
allows code to dispatch on what kind of data it is handling at a given moment:
```python
>>> bundle.stream.Mode.bytes().VARIANT
#bytes
>>> bundle.stream.Mode.lines(bundle.stream.LineMode.lf()).VARIANT
#lines
>>> bundle.stream.Mode.packet(123).VARIANT
#packet
>>> bundle.stream.Mode.object('?').VARIANT
#object
```
TODO
"""
from . import *
@ -40,7 +299,15 @@ def sequenceish(x):
return isinstance(x, tuple) or isinstance(x, list)
class SchemaDecodeFailed(ValueError):
"""TODO"""
"""Raised when [decode][preserves.schema.SchemaObject.decode] cannot find a way to parse a
given input.
Attributes:
cls (class): the SchemaObject subclass attempting the parse
pattern (Value): the failing pattern, a `Value` conforming to schema `meta.Pattern`
value (Value): the unparseable value
failures (list[SchemaDecodeFailed]): descriptions of failed paths attempted during the match this failure describes
"""
def __init__(self, cls, p, v, failures=None):
super().__init__()
self.cls = cls
@ -84,22 +351,108 @@ class ExplanationBuilder:
return '\n' + ' ' * self.indentLevel + self._node(failure) + ''.join(nested)
class SchemaObject:
"""TODO"""
"""Base class for classes representing grammatical productions in a schema: instances of
[SchemaObject][preserves.schema.SchemaObject] represent schema *definitions*. This is an
abstract class, as are its subclasses [Enumeration][preserves.schema.Enumeration] and
[Definition][preserves.schema.Definition]. It is subclasses of *those* subclasses,
automatically produced during schema loading, that are actually instantiated.
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> bundle.stream.Mode.mro()[1:-1]
[<class 'preserves.schema.Enumeration'>, <class 'preserves.schema.SchemaObject'>]
>>> bundle.stream.Mode.packet.mro()[1:-1]
[<class 'stream.Mode._ALL'>, <class 'preserves.schema.Definition'>, <class 'preserves.schema.SchemaObject'>]
>>> bundle.stream.StreamListenerError.mro()[1:-1]
[<class 'preserves.schema.Definition'>, <class 'preserves.schema.SchemaObject'>]
```
Illustrating the class attributes on [SchemaObject][preserves.schema.SchemaObject]
subclasses:
```python
>>> bundle.stream.Mode.ROOTNS is bundle
True
>>> print(stringify(bundle.stream.Mode.SCHEMA, indent=2))
<or [
[
"bytes"
<lit bytes>
]
[
"lines"
<ref [] LineMode>
]
[
"packet"
<rec <lit packet> <tuple [<named size <atom SignedInteger>>]>>
]
[
"object"
<rec <lit object> <tuple [<named description any>]>>
]
]>
>>> bundle.stream.Mode.MODULE_PATH
(#stream,)
>>> bundle.stream.Mode.NAME
#Mode
>>> bundle.stream.Mode.VARIANT is None
True
>>> bundle.stream.Mode.packet.VARIANT
#packet
```
"""
ROOTNS = None
"""A [Namespace][preserves.schema.Namespace] that is the top-level environment for all
bundles included in the [Compiler][preserves.schema.Compiler] run that produced this
[SchemaObject][preserves.schema.SchemaObject].
"""
SCHEMA = None
"""A `Value` conforming to schema `meta.Definition` (and thus often to `meta.Pattern`
etc.), interpreted by the [SchemaObject][preserves.schema.SchemaObject] machinery to drive
parsing, unparsing and so forth."""
MODULE_PATH = None
"""A sequence (tuple) of [Symbol][preserves.values.Symbol]s naming the path from the root
to the schema module containing this definition."""
NAME = None
"""A [Symbol][preserves.values.Symbol] naming this definition within its module."""
VARIANT = None
"""`None` for [Definition][preserves.schema.Definition]s (such as
`bundle.stream.StreamListenerError` above) and for overall
[Enumeration][preserves.schema.Enumeration]s (such as `bundle.stream.Mode`), or a
[Symbol][preserves.values.Symbol] for variant definitions *contained within* an enumeration
(such as `bundle.stream.Mode.packet`).
"""
@classmethod
def decode(cls, v):
"""TODO"""
"""Parses `v` using the [SCHEMA][preserves.schema.SchemaObject.SCHEMA], returning a
(sub)instance of [SchemaObject][preserves.schema.SchemaObject] or raising
[SchemaDecodeFailed][preserves.schema.SchemaDecodeFailed]."""
raise NotImplementedError('Subclass responsibility')
@classmethod
def try_decode(cls, v):
"""TODO"""
"""Parses `v` using the [SCHEMA][preserves.schema.SchemaObject.SCHEMA], returning a
(sub)instance of [SchemaObject][preserves.schema.SchemaObject] or `None` if parsing
failed."""
try:
return cls.decode(v)
except SchemaDecodeFailed:
@ -183,7 +536,8 @@ class SchemaObject:
raise ValueError(f'Bad schema {p}')
def __preserve__(self):
"""TODO"""
"""Called by [preserves.values.preserve][]: *unparses* the information represented by
this instance, using its schema definition, to produce a Preserves `Value`."""
raise NotImplementedError('Subclass responsibility')
def __repr__(self):
@ -200,9 +554,29 @@ class SchemaObject:
raise NotImplementedError('Subclass responsibility')
class Enumeration(SchemaObject):
"""TODO"""
"""Subclasses of [Enumeration][preserves.schema.Enumeration] represent a group of variant
options within a sum type.
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> import pprint
>>> pprint.pprint(bundle.stream.Mode.VARIANTS)
[(#bytes, <class 'stream.Mode.bytes'>),
(#lines, <class 'stream.Mode.lines'>),
(#packet, <class 'stream.Mode.packet'>),
(#object, <class 'stream.Mode.object'>)]
>>> bundle.stream.Mode.VARIANTS[0][1] is bundle.stream.Mode.bytes
True
```
"""
VARIANTS = None
"""List of `(Symbol, SchemaObject class)` tuples representing the possible options within
this sum type."""
def __init__(self):
raise TypeError('Cannot create instance of Enumeration')
@ -237,6 +611,7 @@ class Enumeration(SchemaObject):
raise TypeError('Cannot encode instance of Enumeration')
def safeattrname(k):
"""Escapes Python keywords by prepending `_`; passes all other strings through."""
return k + '_' if keyword.iskeyword(k) else k
def safesetattr(o, k, v):
@ -249,13 +624,61 @@ def safehasattr(o, k):
return hasattr(o, safeattrname(k))
class Definition(SchemaObject):
"""TODO"""
"""Subclasses of [Definition][preserves.schema.Definition] are used to represent both
standalone non-alternation definitions as well as alternatives within an
[Enumeration][preserves.schema.Enumeration].
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> bundle.stream.StreamListenerError.FIELD_NAMES
['spec', 'message']
>>> bundle.stream.StreamListenerError.SAFE_FIELD_NAMES
['spec', 'message']
>>> bundle.stream.StreamListenerError.ENUMERATION is None
True
>>> bundle.stream.Mode.object.FIELD_NAMES
['description']
>>> bundle.stream.Mode.object.SAFE_FIELD_NAMES
['description']
>>> bundle.stream.Mode.object.ENUMERATION is bundle.stream.Mode
True
>>> bundle.stream.CreditAmount.count.FIELD_NAMES
[]
>>> bundle.stream.CreditAmount.count.SAFE_FIELD_NAMES
[]
>>> bundle.stream.CreditAmount.count.ENUMERATION is bundle.stream.CreditAmount
True
>>> bundle.stream.CreditAmount.decode(parse('123'))
CreditAmount.count(123)
>>> bundle.stream.CreditAmount.count(123)
CreditAmount.count(123)
>>> bundle.stream.CreditAmount.count(123).value
123
```
"""
EMPTY = False
SIMPLE = False
FIELD_NAMES = []
"""List of strings: names of the fields contained within this definition, if it has named
fields at all; otherwise, an empty list, and the definition is a simple wrapper for another
value, in which case that value is accessed via the `value` attribute."""
SAFE_FIELD_NAMES = []
"""The list produced by mapping [safeattrname][preserves.schema.safeattrname] over
[FIELD_NAMES][preserves.schema.Definition.FIELD_NAMES]."""
ENUMERATION = None
"""`None` for standalone top-level definitions with a module; otherwise, an
[Enumeration][preserves.schema.Enumeration] subclass representing a top-level alternation
definition."""
def _constructor_name(self):
if self.VARIANT is None:
@ -357,7 +780,6 @@ class escape:
return self.escaped
def encode(p, v):
"""TODO"""
if hasattr(v, '__escape_schema__'):
return preserve(v.__escape_schema__())
if p == ANY:
@ -444,7 +866,13 @@ def definition_not_found(module_path, name):
raise KeyError('Definition not found: ' + module_path_str(module_path + (name,)))
class Namespace:
"""TODO"""
"""A [Namespace][preserves.schema.Namespace] is a dictionary-like object representing a
schema module that knows its location in a schema module hierarchy and whose attributes
correspond to definitions and submodules within the schema module.
Attributes:
_prefix (tuple[Symbol]): path to this module/Namespace from the root Namespace
"""
def __init__(self, prefix):
self._prefix = prefix
@ -467,22 +895,50 @@ class Namespace:
return repr(self._items())
class Compiler:
"""TODO"""
"""Instances of [Compiler][preserves.schema.Compiler] populate an initially-empty
[Namespace][preserves.schema.Namespace] by loading and compiling schema bundle files.
```python
>>> c = Compiler()
>>> c.load('docs/syndicate-protocols-schema-bundle.bin')
>>> type(c.root)
<class 'preserves.schema.Namespace'>
```
Attributes:
root (Namespace): the root namespace into which top-level schema modules are installed.
"""
def __init__(self):
self.root = Namespace(())
def load_filelike(self, module_path, f):
def load_filelike(self, f, module_name=None):
"""Reads a `meta.Bundle` or `meta.Schema` from the filelike object `f`, compiling and
installing it in `self.root`. If `f` contains a bundle, `module_name` is not used,
since the schema modules in the bundle know their own names; if `f` contains a plain
schema module, however, `module_name` is used directly if it is a string, and if it is
`None`, a suitable module name is computed from the `name` attribute of `f`, if it is
present. If `name` is absent in that case, `ValueError` is raised.
"""
x = Decoder(f.read()).next()
if x.key == SCHEMA:
self.load_schema((Symbol(module_path),), x)
if module_name is None:
if hasattr(f, 'name'):
module_name = pathlib.Path(f.name).stem
else:
raise ValueError('Cannot load schema module from filelike object without a module_name')
self.load_schema((Symbol(module_name),), x)
elif x.key == BUNDLE:
for (p, s) in x[0].items():
self.load_schema(p, s)
def load(self, filename):
"""Opens the file at `filename`, passing the resulting file object to
[load_filelike][preserves.schema.Compiler.load_filelike]."""
filename = pathlib.Path(filename)
with open(filename, 'rb') as f:
self.load_filelike(filename.stem, f)
self.load_filelike(f, filename.stem)
def load_schema(self, module_path, schema):
if schema[0][VERSION] != 1:
@ -502,14 +958,50 @@ class Compiler:
ns[n] = c
def load_schema_file(filename):
"""TODO"""
"""Simple entry point to the compiler: creates a [Compiler][preserves.schema.Compiler],
calls [load][preserves.schema.Compiler.load] on it, and returns its `root`
[Namespace][preserves.schema.Namespace].
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> type(bundle)
<class 'preserves.schema.Namespace'>
```
"""
c = Compiler()
c.load(filename)
return c.root
# a decorator
def extend(cls):
"""TODO"""
"""A decorator for function definitions. Useful for adding *behaviour* to the classes
resulting from loading a schema module:
```python
>>> bundle = load_schema_file('docs/syndicate-protocols-schema-bundle.bin')
>>> @extend(bundle.stream.LineMode.lf)
... def what_am_i(self):
... return 'I am a LINEFEED linemode'
>>> @extend(bundle.stream.LineMode.crlf)
... def what_am_i(self):
... return 'I am a CARRIAGE-RETURN-PLUS-LINEFEED linemode'
>>> bundle.stream.LineMode.lf()
LineMode.lf()
>>> bundle.stream.LineMode.lf().what_am_i()
'I am a LINEFEED linemode'
>>> bundle.stream.LineMode.crlf()
LineMode.crlf()
>>> bundle.stream.LineMode.crlf().what_am_i()
'I am a CARRIAGE-RETURN-PLUS-LINEFEED linemode'
```
"""
@wraps(cls)
def extender(f):
setattr(cls, f.__name__, f)
@ -518,7 +1010,8 @@ def extend(cls):
__metaschema_filename = pathlib.Path(__file__).parent / 'schema.prb'
meta = load_schema_file(__metaschema_filename).schema
"""TODO"""
"""Schema module [Namespace][preserves.schema.Namespace] corresponding to [Preserves Schema's
metaschema](https://preserves.dev/preserves-schema.html#appendix-metaschema)."""
if __name__ == '__main__':
with open(__metaschema_filename, 'rb') as f:

View File

@ -195,7 +195,9 @@ class Symbol(object):
```
Attributes:
name (str): the symbol's text label
name (str | Symbol):
The symbol's text label. If an existing [Symbol][preserves.values.Symbol] is passed
in, the existing Symbol's `name` is used as the `name` for the new Symbol.
"""
def __init__(self, name):
self.name = name.name if isinstance(name, Symbol) else name

View File

@ -13,22 +13,7 @@ This document proposes a Schema language for the
## Introduction
A Preserves schema connects Preserves `Value`s to host-language data
structures. Each definition within a schema can be processed by a
compiler to produce
- a simple host-language *type definition*;
- a partial *parsing* function from `Value`s to instances of the
produced type; and
- a total *serialization* function from instances of the type to
`Value`s.
Every parsed `Value` retains enough information to always be able to
be serialized again, and every instance of a host-language data
structure contains, by construction, enough information to be
successfully serialized.
{% include what-is-preserves-schema.md %}
**Portability.** Preserves Schema is broadly portable. Any host-language
type system that can represent [algebraic

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -904,9 +904,9 @@ about annotations</a>.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">573</span>
<span class="normal">574</span>
<span class="normal">575</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">575</span>
<span class="normal">576</span>
<span class="normal">577</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">annotations</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">item</span> <span class="o">=</span> <span class="n">item</span>
</code></pre></div></td></tr></table></div>
@ -940,9 +940,9 @@ about annotations</a>.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">596</span>
<span class="normal">597</span>
<span class="normal">598</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">peel</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">598</span>
<span class="normal">599</span>
<span class="normal">600</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">peel</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Calls [strip_annotations][preserves.values.strip_annotations] on `self` with `depth=1`.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">strip_annotations</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></td></tr></table></div>
@ -967,9 +967,9 @@ about annotations</a>.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">592</span>
<span class="normal">593</span>
<span class="normal">594</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">strip</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="n">inf</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">594</span>
<span class="normal">595</span>
<span class="normal">596</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">strip</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="n">inf</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Calls [strip_annotations][preserves.values.strip_annotations] on `self` and `depth`.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">strip_annotations</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">depth</span><span class="p">)</span>
</code></pre></div></td></tr></table></div>
@ -1040,8 +1040,8 @@ Preserves <code>Value</code>, could be <code>None</code>, could be anything!</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">718</span>
<span class="normal">719</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">embeddedValue</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">720</span>
<span class="normal">721</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">embeddedValue</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">embeddedValue</span> <span class="o">=</span> <span class="n">embeddedValue</span>
</code></pre></div></td></tr></table></div>
</details>
@ -1397,10 +1397,10 @@ other dictionaries.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">459</span>
<span class="normal">460</span>
<span class="normal">461</span>
<span class="normal">462</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">461</span>
<span class="normal">462</span>
<span class="normal">463</span>
<span class="normal">464</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s1">&#39;__hash&#39;</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s1">&#39;Immutable&#39;</span><span class="p">)</span>
<span class="nb">super</span><span class="p">(</span><span class="n">ImmutableDict</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__hash</span> <span class="o">=</span> <span class="kc">None</span>
@ -1449,9 +1449,7 @@ key-value pairs.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">480</span>
<span class="normal">481</span>
<span class="normal">482</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">482</span>
<span class="normal">483</span>
<span class="normal">484</span>
<span class="normal">485</span>
@ -1481,7 +1479,9 @@ key-value pairs.</p>
<span class="normal">509</span>
<span class="normal">510</span>
<span class="normal">511</span>
<span class="normal">512</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="normal">512</span>
<span class="normal">513</span>
<span class="normal">514</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">from_kvs</span><span class="p">(</span><span class="n">kvs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Constructs an [ImmutableDict][preserves.values.ImmutableDict] from a sequence of</span>
<span class="sd"> alternating keys and values; compare to the</span>
@ -1622,10 +1622,10 @@ key-value pairs.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">271</span>
<span class="normal">272</span>
<span class="normal">273</span>
<span class="normal">274</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">fields</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">273</span>
<span class="normal">274</span>
<span class="normal">275</span>
<span class="normal">276</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">fields</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fields</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__hash</span> <span class="o">=</span> <span class="kc">None</span>
@ -1739,9 +1739,7 @@ argument.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">319</span>
<span class="normal">320</span>
<span class="normal">321</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">321</span>
<span class="normal">322</span>
<span class="normal">323</span>
<span class="normal">324</span>
@ -1818,7 +1816,9 @@ argument.</p>
<span class="normal">395</span>
<span class="normal">396</span>
<span class="normal">397</span>
<span class="normal">398</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="normal">398</span>
<span class="normal">399</span>
<span class="normal">400</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">makeBasicConstructor</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">fieldNames</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Constructs and returns a &quot;constructor&quot; for `Record`s having a certain `label` and</span>
<span class="sd"> number of fields.</span>
@ -1928,15 +1928,15 @@ argument.</p>
</details>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">309</span>
<span class="normal">310</span>
<span class="normal">311</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">311</span>
<span class="normal">312</span>
<span class="normal">313</span>
<span class="normal">314</span>
<span class="normal">315</span>
<span class="normal">316</span>
<span class="normal">317</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="normal">317</span>
<span class="normal">318</span>
<span class="normal">319</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">makeConstructor</span><span class="p">(</span><span class="n">labelSymbolText</span><span class="p">,</span> <span class="n">fieldNames</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Equivalent to `Record.makeBasicConstructor(Symbol(labelSymbolText), fieldNames)`.</span>
@ -2010,9 +2010,9 @@ count).</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">414</span>
<span class="normal">415</span>
<span class="normal">416</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">arity</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">416</span>
<span class="normal">417</span>
<span class="normal">418</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">arity</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
<span class="bp">self</span><span class="o">.</span><span class="n">arity</span> <span class="o">=</span> <span class="n">arity</span>
</code></pre></div></td></tr></table></div>
@ -2082,9 +2082,10 @@ count).</p>
<tr>
<td><code>name</code></td>
<td>
<code>str</code>
<code>str | <a class="autorefs autorefs-internal" title="preserves.values.Symbol" href="#preserves.values.Symbol">Symbol</a></code>
</td>
<td><p>the symbol's text label</p></td>
<td><p>The symbol's text label. If an existing <a class="autorefs autorefs-internal" href="#preserves.values.Symbol">Symbol</a> is passed
in, the existing Symbol's <code>name</code> is used as the <code>name</code> for the new Symbol.</p></td>
</tr>
</tbody>
</table>
@ -2092,8 +2093,8 @@ count).</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">200</span>
<span class="normal">201</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">202</span>
<span class="normal">203</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">.</span><span class="n">name</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">Symbol</span><span class="p">)</span> <span class="k">else</span> <span class="n">name</span>
</code></pre></div></td></tr></table></div>
</details>
@ -2142,9 +2143,7 @@ wrapped, and appends each of the <code>anns</code> to the <a class="autorefs aut
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">662</span>
<span class="normal">663</span>
<span class="normal">664</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">664</span>
<span class="normal">665</span>
<span class="normal">666</span>
<span class="normal">667</span>
@ -2159,7 +2158,9 @@ wrapped, and appends each of the <code>anns</code> to the <a class="autorefs aut
<span class="normal">676</span>
<span class="normal">677</span>
<span class="normal">678</span>
<span class="normal">679</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">annotate</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="o">*</span><span class="n">anns</span><span class="p">):</span>
<span class="normal">679</span>
<span class="normal">680</span>
<span class="normal">681</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">annotate</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="o">*</span><span class="n">anns</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Wraps `v` in an [Annotated][preserves.values.Annotated] object, if it isn&#39;t already</span>
<span class="sd"> wrapped, and appends each of the `anns` to the [Annotated][preserves.values.Annotated]&#39;s</span>
<span class="sd"> `annotations` sequence. NOTE: Does not recursively ensure that any parts of the argument</span>
@ -2245,9 +2246,7 @@ sense the inverse of <a class="autorefs autorefs-internal" href="#preserves.valu
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">514</span>
<span class="normal">515</span>
<span class="normal">516</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">516</span>
<span class="normal">517</span>
<span class="normal">518</span>
<span class="normal">519</span>
@ -2257,7 +2256,9 @@ sense the inverse of <a class="autorefs autorefs-internal" href="#preserves.valu
<span class="normal">523</span>
<span class="normal">524</span>
<span class="normal">525</span>
<span class="normal">526</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">dict_kvs</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="normal">526</span>
<span class="normal">527</span>
<span class="normal">528</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">dict_kvs</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Generator function yielding a sequence of alternating keys and values from `d`. In some</span>
<span class="sd"> sense the inverse of [ImmutableDict.from_kvs][preserves.values.ImmutableDict.from_kvs].</span>
@ -2292,9 +2293,9 @@ sense the inverse of <a class="autorefs autorefs-internal" href="#preserves.valu
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">612</span>
<span class="normal">613</span>
<span class="normal">614</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">is_annotated</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">614</span>
<span class="normal">615</span>
<span class="normal">616</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">is_annotated</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;`True` iff `v` is an instance of [Annotated][preserves.values.Annotated].&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">Annotated</span><span class="p">)</span>
</code></pre></div></td></tr></table></div>
@ -2386,9 +2387,7 @@ into the structure of <code>v.item</code>.</p>
<details class="quote">
<summary>Source code in <code>preserves/values.py</code></summary>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">616</span>
<span class="normal">617</span>
<span class="normal">618</span>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">618</span>
<span class="normal">619</span>
<span class="normal">620</span>
<span class="normal">621</span>
@ -2430,7 +2429,9 @@ into the structure of <code>v.item</code>.</p>
<span class="normal">657</span>
<span class="normal">658</span>
<span class="normal">659</span>
<span class="normal">660</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">strip_annotations</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="n">inf</span><span class="p">):</span>
<span class="normal">660</span>
<span class="normal">661</span>
<span class="normal">662</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span> <span class="nf">strip_annotations</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="n">inf</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Exposes `depth` layers of raw structure of</span>
<span class="sd"> potentially-[Annotated][preserves.values.Annotated] `Value`s. If `depth==0` or `v` is not</span>
<span class="sd"> [Annotated][preserves.values.Annotated], just returns `v`. Otherwise, descends recursively</span>