1====================== 2The Sphinx QAPI Domain 3====================== 4 5An extension to the `rST syntax 6<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_ 7in Sphinx is provided by the QAPI Domain, located in 8``docs/sphinx/qapi_domain.py``. This extension is analogous to the 9`Python Domain 10<https://www.sphinx-doc.org/en/master/usage/domains/python.html>`_ 11included with Sphinx, but provides special directives and roles 12for annotating and documenting QAPI definitions 13specifically. 14 15A `Domain 16<https://www.sphinx-doc.org/en/master/usage/domains/index.html>`_ 17provides a set of special rST directives and cross-referencing roles to 18Sphinx for understanding rST markup written to document a specific 19language. By itself, this QAPI extension is only sufficient to parse rST 20markup written by hand; the `autodoc 21<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html>`_ 22functionality is provided elsewhere, in ``docs/sphinx/qapidoc.py``, by 23the "Transmogrifier". 24 25It is not expected that any developer nor documentation writer would 26never need to write *nor* read these special rST forms. However, in the 27event that something needs to be debugged, knowing the syntax of the 28domain is quite handy. This reference may also be useful as a guide for 29understanding the QAPI Domain extension code itself. Although most of 30these forms will not be needed for documentation writing purposes, 31understanding the cross-referencing syntax *will* be helpful when 32writing rST documentation elsewhere, or for enriching the body of 33QAPIDoc blocks themselves. 34 35 36Concepts 37======== 38 39The QAPI Domain itself provides no mechanisms for reading the QAPI 40Schema or generating documentation from code that exists. It is merely 41the rST syntax used to describe things. For instance, the Sphinx Python 42domain adds syntax like ``:py:func:`` for describing Python functions in 43documentation, but it's the autodoc module that is responsible for 44reading Python code and generating such syntax. QAPI is analogous here: 45qapidoc.py is responsible for reading the QAPI Schema and generating rST 46syntax, and qapi_domain.py is responsible for translating that special 47syntax and providing APIs for Sphinx internals. 48 49In other words: 50 51qapi_domain.py adds syntax like ``.. qapi:command::`` to Sphinx, and 52qapidoc.py transforms the documentation in ``qapi/*.json`` into rST 53using directives defined by the domain. 54 55Or even shorter: 56 57``:py:`` is to ``:qapi:`` as *autodoc* is to *qapidoc*. 58 59 60Info Field Lists 61================ 62 63`Field lists 64<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#field-lists>`_ 65are a standard syntax in reStructuredText. Sphinx `extends that syntax 66<https://www.sphinx-doc.org/en/master/usage/domains/python.html#info-field-lists>`_ 67to give certain field list entries special meaning and parsing to, for 68example, add cross-references. The QAPI Domain takes advantage of this 69field list extension to document things like Arguments, Members, Values, 70and so on. 71 72The special parsing and handling of info field lists in Sphinx is provided by 73three main classes; Field, GroupedField, and TypedField. The behavior 74and formatting for each configured field list entry in the domain 75changes depending on which class is used. 76 77Field: 78 * Creates an ungrouped field: i.e., each entry will create its own 79 section and they will not be combined. 80 * May *optionally* support an argument. 81 * May apply cross-reference roles to *either* the argument *or* the 82 content body, both, or neither. 83 84This is used primarily for entries which are not expected to be 85repeated, i.e., items that may only show up at most once. The QAPI 86domain uses this class for "Errors" section. 87 88GroupedField: 89 * Creates a grouped field: i.e. multiple adjacent entries will be 90 merged into one section, and the content will form a bulleted list. 91 * *Must* take an argument. 92 * May optionally apply a cross-reference role to the argument, but not 93 the body. 94 * Can be configured to remove the bulleted list if there is only a 95 single entry. 96 * All items will be generated with the form: "argument -- body" 97 98This is used for entries which are expected to be repeated, but aren't 99expected to have two arguments, i.e. types without names, or names 100without types. The QAPI domain uses this class for features, returns, 101and enum values. 102 103TypedField: 104 * Creates a grouped, typed field. Multiple adjacent entries will be 105 merged into one section, and the content will form a bulleted list. 106 * *Must* take at least one argument, but supports up to two - 107 nominally, a name and a type. 108 * May optionally apply a cross-reference role to the type or the name 109 argument, but not the body. 110 * Can be configured to remove the bulleted list if there is only a 111 single entry. 112 * All items will be generated with the form "name (type) -- body" 113 114This is used for entries that are expected to be repeated and will have 115a name, a type, and a description. The QAPI domain uses this class for 116arguments, alternatives, and members. Wherever type names are referenced 117below, They must be a valid, documented type that will be 118cross-referenced in the HTML output; or one of the built-in JSON types 119(string, number, int, boolean, null, value, q_empty). 120 121 122``:feat:`` 123---------- 124 125Document a feature attached to a QAPI definition. 126 127:availability: This field list is available in the body of Command, 128 Event, Enum, Object and Alternate directives. 129:syntax: ``:feat name: Lorem ipsum, dolor sit amet...`` 130:type: `sphinx.util.docfields.GroupedField 131 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_ 132 133Example:: 134 135 .. qapi:object:: BlockdevOptionsVirtioBlkVhostVdpa 136 :since: 7.2 137 :ifcond: CONFIG_BLKIO 138 139 Driver specific block device options for the virtio-blk-vhost-vdpa 140 backend. 141 142 :memb string path: path to the vhost-vdpa character device. 143 :feat fdset: Member ``path`` supports the special "/dev/fdset/N" path 144 (since 8.1) 145 146 147``:arg:`` 148--------- 149 150Document an argument to a QAPI command. 151 152:availability: This field list is only available in the body of the 153 Command directive. 154:syntax: ``:arg type name: description`` 155:type: `sphinx.util.docfields.TypedField 156 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_ 157 158 159Example:: 160 161 .. qapi:command:: job-pause 162 :since: 3.0 163 164 Pause an active job. 165 166 This command returns immediately after marking the active job for 167 pausing. Pausing an already paused job is an error. 168 169 The job will pause as soon as possible, which means transitioning 170 into the PAUSED state if it was RUNNING, or into STANDBY if it was 171 READY. The corresponding JOB_STATUS_CHANGE event will be emitted. 172 173 Cancelling a paused job automatically resumes it. 174 175 :arg string id: The job identifier. 176 177 178``:error:`` 179----------- 180 181Document the error condition(s) of a QAPI command. 182 183:availability: This field list is only available in the body of the 184 Command directive. 185:syntax: ``:error: Lorem ipsum dolor sit amet ...`` 186:type: `sphinx.util.docfields.Field 187 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.Field.html?private=1>`_ 188 189The format of the :errors: field list description is free-form rST. The 190alternative spelling ":errors:" is also permitted, but strictly 191analogous. 192 193Example:: 194 195 .. qapi:command:: block-job-set-speed 196 :since: 1.1 197 198 Set maximum speed for a background block operation. 199 200 This command can only be issued when there is an active block job. 201 202 Throttling can be disabled by setting the speed to 0. 203 204 :arg string device: The job identifier. This used to be a device 205 name (hence the name of the parameter), but since QEMU 2.7 it 206 can have other values. 207 :arg int speed: the maximum speed, in bytes per second, or 0 for 208 unlimited. Defaults to 0. 209 :error: 210 - If no background operation is active on this device, 211 DeviceNotActive 212 213 214``:return:`` 215------------- 216 217Document the return type(s) and value(s) of a QAPI command. 218 219:availability: This field list is only available in the body of the 220 Command directive. 221:syntax: ``:return type: Lorem ipsum dolor sit amet ...`` 222:type: `sphinx.util.docfields.GroupedField 223 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_ 224 225 226Example:: 227 228 .. qapi:command:: query-replay 229 :since: 5.2 230 231 Retrieve the record/replay information. It includes current 232 instruction count which may be used for ``replay-break`` and 233 ``replay-seek`` commands. 234 235 :return ReplayInfo: record/replay information. 236 237 .. qmp-example:: 238 239 -> { "execute": "query-replay" } 240 <- { "return": { 241 "mode": "play", "filename": "log.rr", "icount": 220414 } 242 } 243 244 245``:return-nodesc:`` 246------------------- 247 248Document the return type of a QAPI command, without an accompanying 249description. 250 251:availability: This field list is only available in the body of the 252 Command directive. 253:syntax: ``:return-nodesc: type`` 254:type: `sphinx.util.docfields.Field 255 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.Field.html?private=1>`_ 256 257 258Example:: 259 260 .. qapi:command:: query-replay 261 :since: 5.2 262 263 Retrieve the record/replay information. It includes current 264 instruction count which may be used for ``replay-break`` and 265 ``replay-seek`` commands. 266 267 :return-nodesc: ReplayInfo 268 269 .. qmp-example:: 270 271 -> { "execute": "query-replay" } 272 <- { "return": { 273 "mode": "play", "filename": "log.rr", "icount": 220414 } 274 } 275 276``:value:`` 277----------- 278 279Document a possible value for a QAPI enum. 280 281:availability: This field list is only available in the body of the Enum 282 directive. 283:syntax: ``:value name: Lorem ipsum, dolor sit amet ...`` 284:type: `sphinx.util.docfields.GroupedField 285 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_ 286 287Example:: 288 289 .. qapi:enum:: QapiErrorClass 290 :since: 1.2 291 292 QEMU error classes 293 294 :value GenericError: this is used for errors that don't require a specific 295 error class. This should be the default case for most errors 296 :value CommandNotFound: the requested command has not been found 297 :value DeviceNotActive: a device has failed to be become active 298 :value DeviceNotFound: the requested device has not been found 299 :value KVMMissingCap: the requested operation can't be fulfilled because a 300 required KVM capability is missing 301 302 303``:alt:`` 304------------ 305 306Document a possible branch for a QAPI alternate. 307 308:availability: This field list is only available in the body of the 309 Alternate directive. 310:syntax: ``:alt type name: Lorem ipsum, dolor sit amet ...`` 311:type: `sphinx.util.docfields.TypedField 312 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_ 313 314As a limitation of Sphinx, we must document the "name" of the branch in 315addition to the type, even though this information is not visible on the 316wire in the QMP protocol format. This limitation *may* be lifted at a 317future date. 318 319Example:: 320 321 .. qapi:alternate:: StrOrNull 322 :since: 2.10 323 324 This is a string value or the explicit lack of a string (null 325 pointer in C). Intended for cases when 'optional absent' already 326 has a different meaning. 327 328 :alt string s: the string value 329 :alt null n: no string value 330 331 332``:memb:`` 333---------- 334 335Document a member of an Event or Object. 336 337:availability: This field list is available in the body of Event or 338 Object directives. 339:syntax: ``:memb type name: Lorem ipsum, dolor sit amet ...`` 340:type: `sphinx.util.docfields.TypedField 341 <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_ 342 343This is fundamentally the same as ``:arg:`` and ``:alt:``, but uses the 344"Members" phrasing for Events and Objects (Structs and Unions). 345 346Example:: 347 348 .. qapi:event:: JOB_STATUS_CHANGE 349 :since: 3.0 350 351 Emitted when a job transitions to a different status. 352 353 :memb string id: The job identifier 354 :memb JobStatus status: The new job status 355 356 357Arbitrary field lists 358--------------------- 359 360Other field list names, while valid rST syntax, are prohibited inside of 361QAPI directives to help prevent accidental misspellings of info field 362list names. If you want to add a new arbitrary "non-value-added" field 363list to QAPI documentation, you must add the field name to the allow 364list in ``docs/conf.py`` 365 366For example:: 367 368 qapi_allowed_fields = { 369 "see also", 370 } 371 372Will allow you to add arbitrary field lists in QAPI directives:: 373 374 .. qapi:command:: x-fake-command 375 376 :see also: Lorem ipsum, dolor sit amet ... 377 378 379Cross-references 380================ 381 382Cross-reference `roles 383<https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html>`_ 384in the QAPI domain are modeled closely after the `Python 385cross-referencing syntax 386<https://www.sphinx-doc.org/en/master/usage/domains/python.html#cross-referencing-python-objects>`_. 387 388QAPI definitions can be referenced using the standard `any 389<https://www.sphinx-doc.org/en/master/usage/referencing.html#role-any>`_ 390role cross-reference syntax, such as with ```query-blockstats```. In 391the event that disambiguation is needed, cross-references can also be 392written using a number of explicit cross-reference roles: 393 394* ``:qapi:mod:`block-core``` -- Reference a QAPI module. The link will 395 take you to the beginning of that section in the documentation. 396* ``:qapi:cmd:`query-block``` -- Reference a QAPI command. 397* ``:qapi:event:`JOB_STATUS_CHANGE``` -- Reference a QAPI event. 398* ``:qapi:enum:`QapiErrorClass``` -- Reference a QAPI enum. 399* ``:qapi:obj:`BlockdevOptionsVirtioBlkVhostVdpa`` -- Reference a QAPI 400 object (struct or union) 401* ``:qapi:alt:`StrOrNull``` -- Reference a QAPI alternate. 402* ``:qapi:type:`BlockDirtyInfo``` -- Reference *any* QAPI type; this 403 excludes modules, commands, and events. 404* ``:qapi:any:`block-job-set-speed``` -- Reference absolutely any QAPI entity. 405 406Type arguments in info field lists are converted into references as if 407you had used the ``:qapi:type:`` role. All of the special syntax below 408applies to both info field lists and standalone explicit 409cross-references. 410 411 412Type decorations 413---------------- 414 415Type names in references can be surrounded by brackets, like 416``[typename]``, to indicate an array of that type. The cross-reference 417will apply only to the type name between the brackets. For example; 418``:qapi:type:`[Qcow2BitmapInfoFlags]``` renders to: 419:qapi:type:`[QMP:Qcow2BitmapInfoFlags]` 420 421To indicate an optional argument/member in a field list, the type name 422can be suffixed with ``?``. The cross-reference will be transformed to 423"type, Optional" with the link applying only to the type name. For 424example; ``:qapi:type:`BitmapSyncMode?``` renders to: 425:qapi:type:`QMP:BitmapSyncMode?` 426 427 428Namespaces 429---------- 430 431Mimicking the `Python domain target specification syntax 432<https://www.sphinx-doc.org/en/master/usage/domains/python.html#target-specification>`_, 433QAPI allows you to specify the fully qualified path for a data 434type. 435 436* A namespace can be explicitly provided; 437 e.g. ``:qapi:type:`QMP:BitmapSyncMode`` 438* A module can be explicitly provided; 439 ``:qapi:type:`QMP:block-core.BitmapSyncMode``` will render to: 440 :qapi:type:`QMP:block-core.BitmapSyncMode` 441* If you don't want to display the "fully qualified" name, it can be 442 prefixed with a tilde; ``:qapi:type:`~QMP:block-core.BitmapSyncMode``` 443 will render to: :qapi:type:`~QMP:block-core.BitmapSyncMode` 444 445 446Target resolution 447----------------- 448 449Any cross-reference to a QAPI type, whether using the ```any``` style of 450reference or the more explicit ```:qapi:any:`target``` syntax, allows 451for the presence or absence of either the namespace or module 452information. 453 454When absent, their value will be inferred from context by the presence 455of any ``qapi:namespace`` or ``qapi:module`` directives preceding the 456cross-reference. 457 458If no results are found when using the inferred values, other 459namespaces/modules will be searched as a last resort; but any explicitly 460provided values must always match in order to succeed. 461 462This allows for efficient cross-referencing with a minimum of syntax in 463the large majority of cases, but additional context or namespace markup 464may be required outside of the QAPI reference documents when linking to 465items that share a name across multiple documented QAPI schema. 466 467 468Custom link text 469---------------- 470 471The name of a cross-reference link can be explicitly overridden like 472`most stock Sphinx references 473<https://www.sphinx-doc.org/en/master/usage/referencing.html#syntax>`_ 474using the ``custom text <target>`` syntax. 475 476For example, ``:qapi:cmd:`Merge dirty bitmaps 477<block-dirty-bitmap-merge>``` will render as: :qapi:cmd:`Merge dirty 478bitmaps <QMP:block-dirty-bitmap-merge>` 479 480 481Directives 482========== 483 484The QAPI domain adds a number of custom directives for documenting 485various QAPI/QMP entities. The syntax is plain rST, and follows this 486general format:: 487 488 .. qapi:directive:: argument 489 :option: 490 :another-option: with an argument 491 492 Content body, arbitrary rST is allowed here. 493 494 495Sphinx standard options 496----------------------- 497 498All QAPI directives inherit a number of `standard options 499<https://www.sphinx-doc.org/en/master/usage/domains/index.html#basic-markup>`_ 500from Sphinx's ObjectDescription class. 501 502The dashed spellings of the below options were added in Sphinx 7.2, the 503undashed spellings are currently retained as aliases, but will be 504removed in a future version. 505 506* ``:no-index:`` and ``:noindex:`` -- Do not add this item into the 507 Index, and do not make it available for cross-referencing. 508* ``no-index-entry:`` and ``:noindexentry:`` -- Do not add this item 509 into the Index, but allow it to be cross-referenced. 510* ``no-contents-entry`` and ``:nocontentsentry:`` -- Exclude this item 511 from the Table of Contents. 512* ``no-typesetting`` -- Create TOC, Index and cross-referencing 513 entities, but don't actually display the content. 514 515 516QAPI standard options 517--------------------- 518 519All QAPI directives -- *except* for namespace and module -- support 520these common options. 521 522* ``:namespace: name`` -- This option allows you to override the 523 namespace association of a given definition. 524* ``:module: modname`` -- Borrowed from the Python domain, this option allows 525 you to override the module association of a given definition. 526* ``:since: x.y`` -- Allows the documenting of "Since" information, which is 527 displayed in the signature bar. 528* ``:ifcond: CONDITION`` -- Allows the documenting of conditional availability 529 information, which is displayed in an eyecatch just below the 530 signature bar. 531* ``:deprecated:`` -- Adds an eyecatch just below the signature bar that 532 advertises that this definition is deprecated and should be avoided. 533* ``:unstable:`` -- Adds an eyecatch just below the signature bar that 534 advertises that this definition is unstable and should not be used in 535 production code. 536 537 538qapi:namespace 539-------------- 540 541The ``qapi:namespace`` directive marks the start of a QAPI namespace. It 542does not take a content body, nor any options. All subsequent QAPI 543directives are associated with the most recent namespace. This affects 544the definition's "fully qualified name", allowing two different 545namespaces to create an otherwise identically named definition. 546 547This directive also influences how reference resolution works for any 548references that do not explicitly specify a namespace, so this directive 549can be used to nudge references into preferring targets from within that 550namespace. 551 552Example:: 553 554 .. qapi:namespace:: QMP 555 556 557This directive has no visible effect. 558 559 560qapi:module 561----------- 562 563The ``qapi:module`` directive marks the start of a QAPI module. It may have 564a content body, but it can be omitted. All subsequent QAPI directives 565are associated with the most recent module; this effects their "fully 566qualified" name, but has no other effect. 567 568Example:: 569 570 .. qapi:module:: block-core 571 572 Welcome to the block-core module! 573 574Will be rendered as: 575 576.. qapi:module:: block-core 577 :noindex: 578 579 Welcome to the block-core module! 580 581 582qapi:command 583------------ 584 585This directive documents a QMP command. It may use any of the standard 586Sphinx or QAPI options, and the documentation body may contain 587``:arg:``, ``:feat:``, ``:error:``, or ``:return:`` info field list 588entries. 589 590Example:: 591 592 .. qapi:command:: x-fake-command 593 :since: 42.0 594 :unstable: 595 596 This command is fake, so it can't hurt you! 597 598 :arg int foo: Your favorite number. 599 :arg string? bar: Your favorite season. 600 :return [string]: A lovely computer-written poem for you. 601 602 603Will be rendered as: 604 605 .. qapi:command:: x-fake-command 606 :noindex: 607 :since: 42.0 608 :unstable: 609 610 This command is fake, so it can't hurt you! 611 612 :arg int foo: Your favorite number. 613 :arg string? bar: Your favorite season. 614 :return [string]: A lovely computer-written poem for you. 615 616 617qapi:event 618---------- 619 620This directive documents a QMP event. It may use any of the standard 621Sphinx or QAPI options, and the documentation body may contain 622``:memb:`` or ``:feat:`` info field list entries. 623 624Example:: 625 626 .. qapi:event:: COMPUTER_IS_RUINED 627 :since: 0.1 628 :deprecated: 629 630 This event is emitted when your computer is *extremely* ruined. 631 632 :memb string reason: Diagnostics as to what caused your computer to 633 be ruined. 634 :feat sadness: When present, the diagnostic message will also 635 explain how sad the computer is as a result of your wrongdoings. 636 637Will be rendered as: 638 639.. qapi:event:: COMPUTER_IS_RUINED 640 :noindex: 641 :since: 0.1 642 :deprecated: 643 644 This event is emitted when your computer is *extremely* ruined. 645 646 :memb string reason: Diagnostics as to what caused your computer to 647 be ruined. 648 :feat sadness: When present, the diagnostic message will also explain 649 how sad the computer is as a result of your wrongdoings. 650 651 652qapi:enum 653--------- 654 655This directive documents a QAPI enum. It may use any of the standard 656Sphinx or QAPI options, and the documentation body may contain 657``:value:`` or ``:feat:`` info field list entries. 658 659Example:: 660 661 .. qapi:enum:: Mood 662 :ifcond: LIB_PERSONALITY 663 664 This enum represents your virtual machine's current mood! 665 666 :value Happy: Your VM is content and well-fed. 667 :value Hungry: Your VM needs food. 668 :value Melancholic: Your VM is experiencing existential angst. 669 :value Petulant: Your VM is throwing a temper tantrum. 670 671Will be rendered as: 672 673.. qapi:enum:: Mood 674 :noindex: 675 :ifcond: LIB_PERSONALITY 676 677 This enum represents your virtual machine's current mood! 678 679 :value Happy: Your VM is content and well-fed. 680 :value Hungry: Your VM needs food. 681 :value Melancholic: Your VM is experiencing existential angst. 682 :value Petulant: Your VM is throwing a temper tantrum. 683 684 685qapi:object 686----------- 687 688This directive documents a QAPI structure or union and represents a QMP 689object. It may use any of the standard Sphinx or QAPI options, and the 690documentation body may contain ``:memb:`` or ``:feat:`` info field list 691entries. 692 693Example:: 694 695 .. qapi:object:: BigBlobOfStuff 696 697 This object has a bunch of disparate and unrelated things in it. 698 699 :memb int Birthday: Your birthday, represented in seconds since the 700 UNIX epoch. 701 :memb [string] Fav-Foods: A list of your favorite foods. 702 :memb boolean? Bizarre-Docs: True if the documentation reference 703 should be strange. 704 705Will be rendered as: 706 707.. qapi:object:: BigBlobOfStuff 708 :noindex: 709 710 This object has a bunch of disparate and unrelated things in it. 711 712 :memb int Birthday: Your birthday, represented in seconds since the 713 UNIX epoch. 714 :memb [string] Fav-Foods: A list of your favorite foods. 715 :memb boolean? Bizarre-Docs: True if the documentation reference 716 should be strange. 717 718 719qapi:alternate 720-------------- 721 722This directive documents a QAPI alternate. It may use any of the 723standard Sphinx or QAPI options, and the documentation body may contain 724``:alt:`` or ``:feat:`` info field list entries. 725 726Example:: 727 728 .. qapi:alternate:: ErrorCode 729 730 This alternate represents an Error Code from the VM. 731 732 :alt int ec: An error code, like the type you're used to. 733 :alt string em: An expletive-laced error message, if your 734 computer is feeling particularly cranky and tired of your 735 antics. 736 737Will be rendered as: 738 739.. qapi:alternate:: ErrorCode 740 :noindex: 741 742 This alternate represents an Error Code from the VM. 743 744 :alt int ec: An error code, like the type you're used to. 745 :alt string em: An expletive-laced error message, if your 746 computer is feeling particularly cranky and tired of your 747 antics. 748