Generate nicer documentation via `scoped_crossrefs`
Marco Ricci

Marco Ricci commited on 2024-09-29 23:27:27
Zeige 8 geänderte Dateien mit 67 Einfügungen und 85 Löschungen.


Now that (some) versions of `mkdocstrings-python` have support for
[scoped crossreferences] [SCOPED], references to documented objects can
be named like they would be in code.  This leads to (Markdown)
docstrings that are much more readable in source form than when having
to type or re-type the fully qualified identifier name.

[SCOPED]: https://github.com/mkdocstrings/python/issues/166
... ...
@@ -78,6 +78,7 @@ plugins:
78 78
           separate_signature: true
79 79
           signature_crossrefs: true
80 80
           unwrap_annotated: true
81
+          scoped_crossrefs: true
81 82
         paths:
82 83
           - src
83 84
   mike:
... ...
@@ -190,11 +190,10 @@ class SSH_AGENTC(enum.Enum):  # noqa: N801
190 190
 
191 191
     Attributes:
192 192
         REQUEST_IDENTITIES:
193
-            List identities.  Expecting [`SSH_AGENT.IDENTITIES_ANSWER`]
194
-            [derivepassphrase._types.SSH_AGENT].
193
+            List identities.  Expecting
194
+            [`SSH_AGENT.IDENTITIES_ANSWER`][].
195 195
         SIGN_REQUEST:
196
-            Sign data.  Expecting [`SSH_AGENT.SIGN_RESPONSE`]
197
-            [derivepassphrase._types.SSH_AGENT].
196
+            Sign data.  Expecting [`SSH_AGENT.SIGN_RESPONSE`][].
198 197
         ADD_IDENTITY:
199 198
             Add an (SSH2) identity.
200 199
         REMOVE_IDENTITY:
... ...
@@ -225,11 +224,9 @@ class SSH_AGENT(enum.Enum):  # noqa: N801
225 224
         SUCCESS:
226 225
             Generic success code.
227 226
         IDENTITIES_ANSWER:
228
-            Successful answer to [`SSH_AGENTC.REQUEST_IDENTITIES`]
229
-            [derivepassphrase._types.SSH_AGENTC].
227
+            Successful answer to [`SSH_AGENTC.REQUEST_IDENTITIES`][].
230 228
         SIGN_RESPONSE:
231
-            Successful answer to [`SSH_AGENTC.SIGN_REQUEST`]
232
-            [derivepassphrase._types.SSH_AGENTC].
229
+            Successful answer to [`SSH_AGENTC.SIGN_REQUEST`][].
233 230
 
234 231
     """
235 232
 
... ...
@@ -387,13 +387,11 @@ def _config_filename(
387 387
 def _load_config() -> _types.VaultConfig:
388 388
     """Load a vault(1)-compatible config from the application directory.
389 389
 
390
-    The filename is obtained via
391
-    [`derivepassphrase.cli._config_filename`][].  This must be an
392
-    unencrypted JSON file.
390
+    The filename is obtained via [`_config_filename`][].  This must be
391
+    an unencrypted JSON file.
393 392
 
394 393
     Returns:
395
-        The vault settings.  See
396
-        [`derivepassphrase._types.VaultConfig`][] for details.
394
+        The vault settings.  See [`_types.VaultConfig`][] for details.
397 395
 
398 396
     Raises:
399 397
         OSError:
... ...
@@ -416,15 +414,14 @@ def _migrate_and_load_old_config() -> (
416 414
 ):
417 415
     """Load and migrate a vault(1)-compatible config.
418 416
 
419
-    The (old) filename is obtained via
420
-    [`derivepassphrase.cli._config_filename`][].  This must be an
421
-    unencrypted JSON file.  After loading, the file is migrated to the new
422
-    standard filename.
417
+    The (old) filename is obtained via [`_config_filename`][].  This
418
+    must be an unencrypted JSON file.  After loading, the file is
419
+    migrated to the new standard filename.
423 420
 
424 421
     Returns:
425 422
         The vault settings, and an optional exception encountered during
426
-        migration.  See [`derivepassphrase._types.VaultConfig`][] for
427
-        details on the former.
423
+        migration.  See [`_types.VaultConfig`][] for details on the
424
+        former.
428 425
 
429 426
     Raises:
430 427
         OSError:
... ...
@@ -451,9 +448,8 @@ def _migrate_and_load_old_config() -> (
451 448
 def _save_config(config: _types.VaultConfig, /) -> None:
452 449
     """Save a vault(1)-compatible config to the application directory.
453 450
 
454
-    The filename is obtained via
455
-    [`derivepassphrase.cli._config_filename`][].  The config will be
456
-    stored as an unencrypted JSON file.
451
+    The filename is obtained via [`_config_filename`][].  The config
452
+    will be stored as an unencrypted JSON file.
457 453
 
458 454
     Args:
459 455
         config:
... ...
@@ -485,7 +481,7 @@ def _get_suitable_ssh_keys(
485 481
     """Yield all SSH keys suitable for passphrase derivation.
486 482
 
487 483
     Suitable SSH keys are queried from the running SSH agent (see
488
-    [`derivepassphrase.ssh_agent.SSHAgentClient.list_keys`][]).
484
+    [`ssh_agent.SSHAgentClient.list_keys`][]).
489 485
 
490 486
     Args:
491 487
         conn:
... ...
@@ -628,9 +624,8 @@ def _select_ssh_key(
628 624
     """Interactively select an SSH key for passphrase derivation.
629 625
 
630 626
     Suitable SSH keys are queried from the running SSH agent (see
631
-    [`derivepassphrase.ssh_agent.SSHAgentClient.list_keys`][]), then the
632
-    user is prompted interactively (see [`click.prompt`][]) for
633
-    a selection.
627
+    [`ssh_agent.SSHAgentClient.list_keys`][]), then the user is prompted
628
+    interactively (see [`click.prompt`][]) for a selection.
634 629
 
635 630
     Args:
636 631
         conn:
... ...
@@ -744,9 +739,8 @@ def _check_for_misleading_passphrase(
744 739
 class OptionGroupOption(click.Option):
745 740
     """A [`click.Option`][] with an associated group name and group epilog.
746 741
 
747
-    Used by [`derivepassphrase.cli.CommandWithHelpGroups`][] to print
748
-    help sections.  Each subclass contains its own group name and
749
-    epilog.
742
+    Used by [`CommandWithHelpGroups`][] to print help sections.  Each
743
+    subclass contains its own group name and epilog.
750 744
 
751 745
     Attributes:
752 746
         option_group_name:
... ...
@@ -759,7 +753,9 @@ class OptionGroupOption(click.Option):
759 753
     """
760 754
 
761 755
     option_group_name: str = ''
756
+    """"""
762 757
     epilog: str = ''
758
+    """"""
763 759
 
764 760
     def __init__(self, *args: Any, **kwargs: Any) -> None:  # noqa: ANN401
765 761
         if self.__class__ == __class__:  # type: ignore[name-defined]
... ...
@@ -789,12 +785,12 @@ class CommandWithHelpGroups(click.Command):
789 785
         of the `formatter`.  We list all options (like the base
790 786
         implementation), but grouped into sections according to the
791 787
         concrete [`click.Option`][] subclass being used.  If the option
792
-        is an instance of some subclass `X` of
793
-        [`derivepassphrase.cli.OptionGroupOption`][], then the section
794
-        heading and the epilog are taken from `X.option_group_name` and
795
-        `X.epilog`; otherwise, the section heading is "Options" (or
796
-        "Other options" if there are other option groups) and the epilog
797
-        is empty.
788
+        is an instance of some subclass of [`OptionGroupOption`][], then
789
+        the section heading and the epilog are taken from the
790
+        [`option_group_name`] [OptionGroupOption.option_group_name] and
791
+        [`epilog`] [OptionGroupOption.epilog] attributes; otherwise, the
792
+        section heading is "Options" (or "Other options" if there are
793
+        other option groups) and the epilog is empty.
798 794
 
799 795
         Args:
800 796
             ctx:
... ...
@@ -12,12 +12,11 @@ table entry is separately encrypted and authenticated.  James Coglan
12 12
 designed this format to avoid concurrent write issues when updating or
13 13
 synchronizing the vault configuration with e.g. a cloud service.
14 14
 
15
-The public interface is the
16
-[`derivepassphrase.exporter.storeroom.export_storeroom_data`][]
17
-function.  Multiple *non-public* functions are additionally documented
18
-here for didactical and educational reasons, but they are not part of
19
-the module API, are subject to change without notice (including
20
-removal), and should *not* be used or relied on.
15
+The public interface is the [`export_storeroom_data`][] function.
16
+Multiple *non-public* functions are additionally documented here for
17
+didactical and educational reasons, but they are not part of the module
18
+API, are subject to change without notice (including removal), and
19
+should *not* be used or relied on.
21 20
 
22 21
 """
23 22
 
... ...
@@ -209,8 +208,7 @@ def decrypt_master_keys_data(data: bytes, keys: KeyPair) -> MasterKeys:
209 208
         keys:
210 209
             The encryption and signing keys for the master keys data.
211 210
             These should have previously been derived via the
212
-            [`derivepassphrase.exporter.storeroom.derive_master_keys_keys`][]
213
-            function.
211
+            [`derive_master_keys_keys`][] function.
214 212
 
215 213
     Returns:
216 214
         The master encryption, signing and hashing keys.
... ...
@@ -303,9 +301,7 @@ def decrypt_session_keys(data: bytes, master_keys: MasterKeys) -> KeyPair:
303 301
             The encrypted bucket item session key data.
304 302
         master_keys:
305 303
             The master keys.  Presumably these have previously been
306
-            obtained via the
307
-            [`derivepassphrase.exporter.storeroom.decrypt_master_keys_data`][]
308
-            function.
304
+            obtained via the [`decrypt_master_keys_data`][] function.
309 305
 
310 306
     Returns:
311 307
         The bucket item's encryption and signing keys.
... ...
@@ -414,8 +410,7 @@ def decrypt_contents(data: bytes, session_keys: KeyPair) -> bytes:
414 410
             The encrypted bucket item payload data.
415 411
         session_keys:
416 412
             The bucket item's session keys.  Presumably these have
417
-            previously been obtained via the
418
-            [`derivepassphrase.exporter.storeroom.decrypt_session_keys`][]
413
+            previously been obtained via the [`decrypt_session_keys`][]
419 414
             function.
420 415
 
421 416
     Returns:
... ...
@@ -494,9 +489,7 @@ def decrypt_bucket_item(bucket_item: bytes, master_keys: MasterKeys) -> bytes:
494 489
             The encrypted bucket item.
495 490
         master_keys:
496 491
             The master keys.  Presumably these have previously been
497
-            obtained via the
498
-            [`derivepassphrase.exporter.storeroom.decrypt_master_keys_data`][]
499
-            function.
492
+            obtained via the [`decrypt_master_keys_data`][] function.
500 493
 
501 494
     Returns:
502 495
         The decrypted bucket item.
... ...
@@ -553,9 +546,7 @@ def decrypt_bucket_file(
553 546
             The bucket file's filename.
554 547
         master_keys:
555 548
             The master keys.  Presumably these have previously been
556
-            obtained via the
557
-            [`derivepassphrase.exporter.storeroom.decrypt_master_keys_data`][]
558
-            function.
549
+            obtained via the [`decrypt_master_keys_data`][] function.
559 550
         root_dir:
560 551
             The root directory of the data store.  The filename is
561 552
             interpreted relatively to this directory.
... ...
@@ -635,13 +626,12 @@ def export_storeroom_data(  # noqa: C901,PLR0912,PLR0914,PLR0915
635 626
     Args:
636 627
         storeroom_path:
637 628
             Path to the storeroom; usually `~/.vault`.  If not given,
638
-            then query [`derivepassphrase.exporter.get_vault_path`][]
639
-            for the value.
629
+            then query [`exporter.get_vault_path`][] for the value.
640 630
         master_keys_key:
641 631
             Encryption key/password for the master keys, usually the
642 632
             username, or passed via the `VAULT_KEY` environment
643 633
             variable.  If not given, then query
644
-            [`derivepassphrase.exporter.get_vault_key`][] for the value.
634
+            [`exporter.get_vault_key`][] for the value.
645 635
 
646 636
     Returns:
647 637
         The full configuration, as stored in the storeroom.
... ...
@@ -13,12 +13,11 @@ cryptographic weaknesses (API misuse of a key derivation function, and
13 13
 a low-entropy method of generating initialization vectors for CBC block
14 14
 encryption mode) and should thus be avoided if possible.
15 15
 
16
-The public interface is the
17
-[`derivepassphrase.exporter.vault_native.export_vault_native_data`][]
18
-function.  Multiple *non-public* classes are additionally documented
19
-here for didactical and educational reasons, but they are not part of
20
-the module API, are subject to change without notice (including
21
-removal), and should *not* be used or relied on.
16
+The public interface is the [`export_vault_native_data`][] function.
17
+Multiple *non-public* classes are additionally documented here for
18
+didactical and educational reasons, but they are not part of the module
19
+API, are subject to change without notice (including removal), and
20
+should *not* be used or relied on.
22 21
 
23 22
 """
24 23
 
... ...
@@ -108,8 +107,7 @@ class VaultNativeConfigParser(abc.ABC):
108 107
             password:
109 108
                 The vault master key/master passphrase the file is
110 109
                 encrypted with.  Must be non-empty.  See
111
-                [`derivepassphrase.exporter.get_vault_key`][] for
112
-                details.
110
+                [`exporter.get_vault_key`][] for details.
113 111
 
114 112
                 If this is a text string, then the UTF-8 encoding of the
115 113
                 string is used as the binary password.
... ...
@@ -403,8 +401,8 @@ def export_vault_native_data(
403 401
         contents:
404 402
             The binary encrypted contents of the vault configuration
405 403
             file.  If not given, then query
406
-            [`derivepassphrase.exporter.get_vault_path`][] for the
407
-            correct filename and read the contents from there.
404
+            [`exporter.get_vault_path`][] for the correct filename and
405
+            read the contents from there.
408 406
 
409 407
             Note: On disk, these are usually stored in base64-encoded
410 408
             form, not in the "raw" form as needed here.
... ...
@@ -412,7 +410,7 @@ def export_vault_native_data(
412 410
             Encryption key/password for the configuration file, usually
413 411
             the username, or passed via the `VAULT_KEY` environment
414 412
             variable.  If not given, then query
415
-            [`derivepassphrase.exporter.get_vault_key`][] for the value.
413
+            [`exporter.get_vault_key`][] for the value.
416 414
         try_formats:
417 415
             A sequence of formats to try out, in order.  Each key must
418 416
             be one of `v0.2` or `v0.3`.
... ...
@@ -14,8 +14,7 @@ deterministic, stateless password manager that recomputes passwords
14 14
 instead of storing them), and this reimplementation is used for
15 15
 a similar purpose.
16 16
 
17
-The main API is the [`Sequin`] [derivepassphrase.sequin.Sequin] class,
18
-which is thoroughly documented.
17
+The main API is the [`Sequin`][] class, which is thoroughly documented.
19 18
 
20 19
 """
21 20
 
... ...
@@ -61,11 +61,13 @@ class SSHAgentClient:
61 61
     The main use case is requesting the agent sign some data, after
62 62
     checking that the necessary key is already loaded.
63 63
 
64
-    The main fleshed out methods are `list_keys` and `sign`, which
65
-    implement the `REQUEST_IDENTITIES` and `SIGN_REQUEST` requests.  If
66
-    you *really* wanted to, there is enough infrastructure in place to
67
-    issue other requests as defined in the protocol---it's merely the
68
-    wrapper functions and the protocol numbers table that are missing.
64
+    The main fleshed out methods are [`list_keys`][] and [`sign`][],
65
+    which implement the [`REQUEST_IDENTITIES`]
66
+    [_types.SSH_AGENTC.REQUEST_IDENTITIES] and [`SIGN_REQUEST`]
67
+    [_types.SSH_AGENTC.SIGN_REQUEST] requests.  If you *really* wanted
68
+    to, there is enough infrastructure in place to issue other requests
69
+    as defined in the protocol---it's merely the wrapper functions and
70
+    the protocol numbers table that are missing.
69 71
 
70 72
     """
71 73
 
... ...
@@ -433,7 +435,7 @@ class SSHAgentClient:
433 435
         Args:
434 436
             key:
435 437
                 The public SSH key to sign the payload with, in the same
436
-                format as returned by, e.g., the `list_keys` method.
438
+                format as returned by, e.g., the [`list_keys`][] method.
437 439
                 The corresponding private key must have previously been
438 440
                 loaded into the agent to successfully issue a signature.
439 441
             payload:
... ...
@@ -445,7 +447,7 @@ class SSHAgentClient:
445 447
                 algorithms when signing with RSA keys.  (No such
446 448
                 real-world usage is currently implemented.)
447 449
             check_if_key_loaded:
448
-                If true, check beforehand (via `list_keys`) if the
450
+                If true, check beforehand (via [`list_keys`][]) if the
449 451
                 corresponding key has been loaded into the agent.
450 452
 
451 453
         Returns:
... ...
@@ -34,10 +34,9 @@ class Vault:
34 34
     detail][ALGORITHM] in his blog post on said topic: A principally
35 35
     infinite bit stream is obtained by running a key-derivation function
36 36
     on the master passphrase and the service name, then this bit stream
37
-    is fed into a [Sequin][derivepassphrase.sequin.Sequin] to generate
38
-    random numbers in the correct range, and finally these random
39
-    numbers select passphrase characters until the desired length is
40
-    reached.
37
+    is fed into a [sequin.Sequin][] to generate random numbers in the
38
+    correct range, and finally these random numbers select passphrase
39
+    characters until the desired length is reached.
41 40
 
42 41
     [vault]: https://www.npmjs.com/package/vault
43 42
     [ALGORITHM]: https://blog.jcoglan.com/2012/07/16/designing-vaults-generator-algorithm/
... ...
@@ -219,10 +218,10 @@ class Vault:
219 218
     ) -> int:
220 219
         """Estimate the sufficient hash length, given the current settings.
221 220
 
222
-        Using the entropy (via `_entropy`) and a safety factor, give an
223
-        initial estimate of the length to use for `create_hash` such
224
-        that using a `Sequin` with this hash will not exhaust it during
225
-        passphrase generation.
221
+        Using the entropy (via [`_entropy`][]) and a safety factor, give
222
+        an initial estimate of the length to use for [`create_hash`][]
223
+        such that using a [`sequin.Sequin`][] with this hash will not
224
+        exhaust it during passphrase generation.
226 225
 
227 226
         Args:
228 227
             safety_factor: The safety factor.  Must be at least 1.
229 228