fleetmesh-node 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
- fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
- fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
- fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
- fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
- fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
- node/RELEASE +1 -0
- node/__init__.py +35 -0
- node/app/__init__.py +49 -0
- node/app/commands.py +265 -0
- node/app/config.py +131 -0
- node/app/control.py +670 -0
- node/app/core.py +867 -0
- node/app/plugins.py +76 -0
- node/app/router.py +83 -0
- node/app/socket.py +431 -0
- node/app/systemd.py +36 -0
- node/app/views.py +204 -0
- node/channels/__init__.py +378 -0
- node/channels/bus.py +331 -0
- node/channels/journal.py +46 -0
- node/commands/__init__.py +140 -0
- node/commands/adapter.py +210 -0
- node/commands/base.py +278 -0
- node/commands/cluster_cmds.py +183 -0
- node/commands/compute_cmds.py +401 -0
- node/commands/core.py +474 -0
- node/commands/gossip_cmds.py +106 -0
- node/commands/nsite_cmds.py +141 -0
- node/commands/peering.py +402 -0
- node/commands/plugin_cmds.py +135 -0
- node/commands/sidecar_cmds.py +120 -0
- node/commands/sync.py +388 -0
- node/commands/transport_cmds.py +451 -0
- node/commands/video_cmds.py +99 -0
- node/compartment/__init__.py +124 -0
- node/compartment/base.py +868 -0
- node/compartment/command.py +458 -0
- node/compartment/context.py +163 -0
- node/compartment/every.py +108 -0
- node/compartment/handler.py +131 -0
- node/compartment/lifecycle.py +460 -0
- node/compartment/lint.py +388 -0
- node/compartment/need.py +158 -0
- node/compartment/problem.py +521 -0
- node/compartment/refusals.py +120 -0
- node/compartment/registry.py +790 -0
- node/compartment/schema.py +290 -0
- node/compartment/sidecar.py +499 -0
- node/compartment/stores.py +662 -0
- node/compartment/wire.py +225 -0
- node/compartment/worker.py +61 -0
- node/config/__init__.py +43 -0
- node/config/base.py +116 -0
- node/config/change.py +441 -0
- node/config/changestore.py +201 -0
- node/config/compartments.py +1144 -0
- node/config/node.py +318 -0
- node/config/parameter.py +563 -0
- node/config/protocols.py +274 -0
- node/config/records.py +171 -0
- node/examples/__init__.py +1 -0
- node/examples/demo.py +226 -0
- node/examples/follower.py +46 -0
- node/interfaces.py +235 -0
- node/keystore/__init__.py +27 -0
- node/keystore/core.py +103 -0
- node/keystore/enclave.py +688 -0
- node/meshnode.py +111 -0
- node/metrics/__init__.py +396 -0
- node/metrics/adapters.py +102 -0
- node/metrics/face.py +110 -0
- node/planes/__init__.py +21 -0
- node/planes/identity/__init__.py +20 -0
- node/planes/identity/announce/__init__.py +9 -0
- node/planes/identity/announce/compartment.py +447 -0
- node/planes/identity/announce/types.py +26 -0
- node/planes/identity/discovery/__init__.py +9 -0
- node/planes/identity/discovery/compartment.py +522 -0
- node/planes/identity/discovery/types.py +13 -0
- node/planes/identity/linked/__init__.py +1 -0
- node/planes/identity/linked/compartment.py +270 -0
- node/planes/identity/linked/types.py +25 -0
- node/planes/identity/presence/__init__.py +7 -0
- node/planes/identity/presence/compartment.py +409 -0
- node/planes/identity/presence/types.py +14 -0
- node/planes/operations/__init__.py +13 -0
- node/planes/operations/scheduler/__init__.py +9 -0
- node/planes/operations/scheduler/compartment.py +353 -0
- node/planes/operations/scheduler/types.py +21 -0
- node/planes/operations/updates/__init__.py +17 -0
- node/planes/operations/updates/core.py +310 -0
- node/planes/operations/updates/store.py +79 -0
- node/planes/operations/updates/types.py +20 -0
- node/planes/peering/__init__.py +28 -0
- node/planes/peering/budget/__init__.py +43 -0
- node/planes/peering/budget/chain.py +43 -0
- node/planes/peering/budget/core.py +537 -0
- node/planes/peering/budget/evaluation.py +80 -0
- node/planes/peering/budget/meter.py +100 -0
- node/planes/peering/budget/receipts.py +439 -0
- node/planes/peering/budget/shape.py +101 -0
- node/planes/peering/budget/tick.py +110 -0
- node/planes/peering/budget/types.py +67 -0
- node/planes/peering/budget/windows.py +346 -0
- node/planes/peering/capacity/__init__.py +7 -0
- node/planes/peering/capacity/compartment.py +342 -0
- node/planes/peering/capacity/types.py +24 -0
- node/planes/peering/peering/__init__.py +25 -0
- node/planes/peering/peering/core.py +464 -0
- node/planes/peering/peering/envelope.py +142 -0
- node/planes/peering/peering/handshake.py +198 -0
- node/planes/peering/peering/shape.py +80 -0
- node/planes/peering/peering/types.py +39 -0
- node/planes/peering/reporter/__init__.py +42 -0
- node/planes/peering/reporter/constants.py +29 -0
- node/planes/peering/reporter/core.py +299 -0
- node/planes/peering/reporter/finding.py +74 -0
- node/planes/peering/reporter/findings.py +171 -0
- node/planes/peering/reporter/publish.py +285 -0
- node/planes/peering/reporter/reading.py +193 -0
- node/planes/peering/reporter/shape.py +86 -0
- node/planes/peering/reporter/types.py +50 -0
- node/planes/peering/strain/__init__.py +9 -0
- node/planes/peering/strain/compartment.py +204 -0
- node/planes/peering/strain/types.py +21 -0
- node/planes/peering/trust/__init__.py +9 -0
- node/planes/peering/trust/compartment.py +239 -0
- node/planes/peering/trust/types.py +30 -0
- node/planes/services/__init__.py +33 -0
- node/planes/services/compute/__init__.py +160 -0
- node/planes/services/compute/common.py +134 -0
- node/planes/services/compute/compartment.py +681 -0
- node/planes/services/compute/executors/__init__.py +121 -0
- node/planes/services/compute/executors/agent.py +840 -0
- node/planes/services/compute/executors/bacalhau.py +605 -0
- node/planes/services/compute/executors/base.py +127 -0
- node/planes/services/compute/executors/harness.py +439 -0
- node/planes/services/compute/executors/mcp.py +454 -0
- node/planes/services/compute/executors/media.py +898 -0
- node/planes/services/compute/executors/nondet.py +283 -0
- node/planes/services/compute/executors/providers.py +499 -0
- node/planes/services/compute/executors/registry.py +140 -0
- node/planes/services/compute/executors/wasm.py +244 -0
- node/planes/services/compute/executors/zk.py +604 -0
- node/planes/services/compute/jobs.py +227 -0
- node/planes/services/compute/load.py +180 -0
- node/planes/services/compute/market.py +433 -0
- node/planes/services/compute/sandbox.py +282 -0
- node/planes/services/compute/types.py +84 -0
- node/planes/services/compute_wire/__init__.py +58 -0
- node/planes/services/compute_wire/compartment.py +256 -0
- node/planes/services/compute_wire/messages.py +320 -0
- node/planes/services/compute_wire/sealing.py +147 -0
- node/planes/services/compute_wire/session.py +572 -0
- node/planes/services/compute_wire/types.py +49 -0
- node/planes/services/compute_wire/verify.py +331 -0
- node/planes/services/dvm/__init__.py +24 -0
- node/planes/services/dvm/gateway.py +526 -0
- node/planes/services/dvm/protocol.py +35 -0
- node/planes/services/dvm/translate.py +15 -0
- node/planes/services/dvm/types.py +25 -0
- node/planes/services/gateway/__init__.py +40 -0
- node/planes/services/gateway/settle.py +279 -0
- node/planes/services/gateway/translate.py +644 -0
- node/planes/services/gossip/__init__.py +22 -0
- node/planes/services/gossip/core.py +929 -0
- node/planes/services/gossip/envelope.py +154 -0
- node/planes/services/gossip/mesh.py +127 -0
- node/planes/services/gossip/types.py +50 -0
- node/planes/services/nsite/__init__.py +7 -0
- node/planes/services/nsite/client.py +62 -0
- node/planes/services/nsite/compartment.py +752 -0
- node/planes/services/nsite/types.py +44 -0
- node/planes/services/paidfetch/__init__.py +7 -0
- node/planes/services/paidfetch/compartment.py +862 -0
- node/planes/services/paidfetch/types.py +99 -0
- node/planes/services/settle/__init__.py +27 -0
- node/planes/services/settle/compartment.py +384 -0
- node/planes/services/settle/engine.py +456 -0
- node/planes/services/settle/gateways.py +541 -0
- node/planes/services/settle/payment.py +512 -0
- node/planes/services/settle/store.py +74 -0
- node/planes/services/settle/types.py +58 -0
- node/planes/services/sidecars/__init__.py +8 -0
- node/planes/services/sidecars/compartment.py +461 -0
- node/planes/services/sidecars/manifest.py +304 -0
- node/planes/services/sidecars/manifests/10-grasp.json +37 -0
- node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
- node/planes/services/sidecars/manifests/30-magpie.json +28 -0
- node/planes/services/sidecars/types.py +43 -0
- node/planes/services/video/__init__.py +106 -0
- node/planes/services/video/barter.py +407 -0
- node/planes/services/video/cli.py +158 -0
- node/planes/services/video/compartment.py +932 -0
- node/planes/services/video/ingest.py +524 -0
- node/planes/services/video/ledger.py +705 -0
- node/planes/services/video/packager.py +847 -0
- node/planes/services/video/player.py +456 -0
- node/planes/services/video/timed_segments.py +124 -0
- node/planes/services/video/transit.py +478 -0
- node/planes/store/__init__.py +24 -0
- node/planes/store/blob_replication/__init__.py +7 -0
- node/planes/store/blob_replication/compartment.py +496 -0
- node/planes/store/blob_replication/types.py +35 -0
- node/planes/store/blob_wire/__init__.py +48 -0
- node/planes/store/blob_wire/client.py +57 -0
- node/planes/store/blob_wire/core.py +359 -0
- node/planes/store/blob_wire/intake.py +74 -0
- node/planes/store/blob_wire/messages.py +85 -0
- node/planes/store/projected/__init__.py +7 -0
- node/planes/store/projected/compartment.py +258 -0
- node/planes/store/projected/types.py +29 -0
- node/planes/store/retention/__init__.py +7 -0
- node/planes/store/retention/compartment.py +214 -0
- node/planes/store/retention/types.py +12 -0
- node/planes/store/sync/__init__.py +35 -0
- node/planes/store/sync/core.py +311 -0
- node/planes/store/sync/envelope.py +106 -0
- node/planes/store/sync/handover.py +206 -0
- node/planes/store/sync/initiator.py +128 -0
- node/planes/store/sync/responder.py +114 -0
- node/planes/store/sync/rules.py +179 -0
- node/planes/store/sync/shape.py +80 -0
- node/planes/store/sync/types.py +51 -0
- node/planes/transport/__init__.py +23 -0
- node/planes/transport/bridge/__init__.py +13 -0
- node/planes/transport/bridge/compartment.py +785 -0
- node/planes/transport/bridge/ratelimit.py +44 -0
- node/planes/transport/bridge/types.py +43 -0
- node/planes/transport/lan/__init__.py +9 -0
- node/planes/transport/lan/compartment.py +446 -0
- node/planes/transport/lan/types.py +25 -0
- node/planes/transport/link/__init__.py +7 -0
- node/planes/transport/link/compartment.py +257 -0
- node/planes/transport/link/types.py +27 -0
- node/planes/transport/mediation/__init__.py +45 -0
- node/planes/transport/mediation/compartment.py +238 -0
- node/planes/transport/mediation/leaf.py +107 -0
- node/planes/transport/mediation/messages.py +302 -0
- node/planes/transport/mediation/service.py +233 -0
- node/planes/transport/mediation/store.py +37 -0
- node/planes/transport/mediation/types.py +20 -0
- node/planes/transport/p2p/__init__.py +7 -0
- node/planes/transport/p2p/compartment.py +349 -0
- node/planes/transport/p2p/types.py +22 -0
- node/protocols/__init__.py +277 -0
- node/protocols/activitypub/__init__.py +56 -0
- node/protocols/activitypub/actor.py +230 -0
- node/protocols/activitypub/federation.py +490 -0
- node/protocols/activitypub/media.py +145 -0
- node/protocols/activitypub/models.py +286 -0
- node/protocols/activitypub/package.py +271 -0
- node/protocols/activitypub/store.py +564 -0
- node/protocols/arcade/__init__.py +26 -0
- node/protocols/atproto/__init__.py +13 -0
- node/protocols/atproto/firehose.py +126 -0
- node/protocols/atproto/metering.py +165 -0
- node/protocols/atproto/package.py +155 -0
- node/protocols/atproto/repo.py +250 -0
- node/protocols/atproto/xrpc.py +285 -0
- node/protocols/base.py +109 -0
- node/protocols/circles/__init__.py +29 -0
- node/protocols/dm/__init__.py +50 -0
- node/protocols/grasp/__init__.py +22 -0
- node/protocols/isotime.py +15 -0
- node/protocols/nostr/__init__.py +51 -0
- node/protocols/nostr/blobstore.py +383 -0
- node/protocols/nostr/blossom.py +627 -0
- node/protocols/nostr/client.py +377 -0
- node/protocols/nostr/descriptor.py +211 -0
- node/protocols/nostr/eventstore.py +304 -0
- node/protocols/nostr/package.py +182 -0
- node/protocols/nostr/peers.py +109 -0
- node/protocols/nostr/relayserver.py +656 -0
- node/protocols/p2panda/__init__.py +22 -0
- node/protocols/p2panda/identity.py +19 -0
- node/protocols/p2panda/package.py +236 -0
- node/protocols/p2panda/rung.py +152 -0
- node/protocols/p2panda/store.py +273 -0
- node/protocols/rhio/__init__.py +33 -0
- node/protocols/rhio/package.py +275 -0
- node/protocols/rhio/rung.py +136 -0
- node/protocols/rhio/store.py +1423 -0
- node/protocols/ssb/muxrpc.py +256 -0
- node/protocols/ssb/package.py +267 -0
- node/protocols/textile/__init__.py +111 -0
- node/protocols/textile/transducers.py +259 -0
- node/release.py +119 -0
- node/storage/__init__.py +88 -0
- node/storage/accounts.py +305 -0
- node/storage/adapter.py +161 -0
- node/storage/base.py +385 -0
- node/storage/dialect.py +209 -0
- node/storage/grants.py +317 -0
- node/storage/locator.py +150 -0
- node/storage/postgres.py +294 -0
- node/storage/projected.py +953 -0
- node/storage/sharding.py +448 -0
- node/storage/standing.py +568 -0
- node/testnet/__init__.py +9 -0
- node/testnet/cluster.py +731 -0
- node/testnet/fips_daemon.py +230 -0
- node/transport/__init__.py +94 -0
- node/transport/const.py +110 -0
- node/transport/didcomm_inbound.py +251 -0
- node/transport/dnstxt.py +169 -0
- node/transport/fips_rung/__init__.py +67 -0
- node/transport/fips_rung/core.py +666 -0
- node/transport/fips_rung/listener.py +96 -0
- node/transport/fips_rung/types.py +12 -0
- node/transport/host.py +334 -0
- node/transport/iroh_rung/__init__.py +29 -0
- node/transport/iroh_rung/core.py +477 -0
- node/transport/iroh_rung/quic.py +942 -0
- node/transport/iroh_rung/types.py +22 -0
- node/transport/ladder.py +145 -0
- node/transport/lineproto.py +49 -0
- node/transport/mdns.py +303 -0
- node/transport/netfetch.py +283 -0
- node/transport/relay_rung/__init__.py +23 -0
- node/transport/relay_rung/cooldown.py +87 -0
- node/transport/relay_rung/core.py +138 -0
- node/transport/relay_rung/inbound.py +161 -0
- node/transport/relay_rung/ledger.py +104 -0
- node/transport/relay_rung/listen.py +121 -0
- node/transport/relay_rung/outbound.py +181 -0
- node/transport/relay_rung/shape.py +71 -0
- node/transport/relay_rung/types.py +58 -0
- node/transport/relay_rung/wire.py +63 -0
- node/transport/router.py +761 -0
- node/transport/rung.py +276 -0
- node/transport/ssb_rung.py +166 -0
- node/transport/ws.py +502 -0
- sidecar/fleetmesh_announce.py +338 -0
- sidecar/fleetmesh_p2p.py +373 -0
- sidecar/fleetmesh_rhio.py +352 -0
- sidecar/fleetmesh_ssb.py +603 -0
node/config/parameter.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"""One declaration per setting, and one rule for which layer wins.
|
|
2
|
+
|
|
3
|
+
A `Block` states a compartment's config as a frozen dataclass. That works, but
|
|
4
|
+
each setting's knowledge ends up in four places: the docstring says what it
|
|
5
|
+
means, the field says its type and default, `check` says what is wrong with a
|
|
6
|
+
bad value, and `from_dict` repeats the default while coercing. `UpdatesConfig`
|
|
7
|
+
wrote `DEFAULT_CHECK_EVERY_SECONDS` twice for exactly that reason. Four places
|
|
8
|
+
agreeing by hand is how a control comes to report something the node does not
|
|
9
|
+
do, which is a defect this tree has now met several times.
|
|
10
|
+
|
|
11
|
+
A `Parameter` is that knowledge said once. `ParamBlock` derives the other
|
|
12
|
+
three from it, so a compartment declares and does not re-implement.
|
|
13
|
+
|
|
14
|
+
It also buys what no amount of dataclass fields could: the node can be *asked*
|
|
15
|
+
what it is configurable by. `every_parameter()` enumerates the lot with types,
|
|
16
|
+
defaults, tags and whether a reload applies them, which is what makes uniform
|
|
17
|
+
defaults something you can check rather than hope for.
|
|
18
|
+
|
|
19
|
+
## Escalation
|
|
20
|
+
|
|
21
|
+
Highest wins, and the order is stated here rather than per call site - before
|
|
22
|
+
this, exactly one setting in the node had a documented precedence
|
|
23
|
+
(`app/config.py:_blob_storage_cap`, env then file then manifest), so an
|
|
24
|
+
operator had nothing general to rely on. That chain is preserved: FILE beats
|
|
25
|
+
MANIFEST because a manifest is compiled *into* the file by
|
|
26
|
+
`config_from_manifests`, so the file is the later word.
|
|
27
|
+
|
|
28
|
+
OVERRIDE set live over the control socket; only for `live` parameters
|
|
29
|
+
ARG a command line argument
|
|
30
|
+
ENV an environment variable, when the parameter names one
|
|
31
|
+
FILE config.json, which is the operator's own file
|
|
32
|
+
MANIFEST compiled from a meshctl manifest
|
|
33
|
+
DEFAULT the parameter's own default
|
|
34
|
+
|
|
35
|
+
## Field names
|
|
36
|
+
|
|
37
|
+
The design was given in a schema; these are its Python spellings.
|
|
38
|
+
|
|
39
|
+
key key
|
|
40
|
+
name name
|
|
41
|
+
description description
|
|
42
|
+
tags tags
|
|
43
|
+
type type (plus `item` for what a list or mapping holds)
|
|
44
|
+
valueRequired required
|
|
45
|
+
defaultValue default
|
|
46
|
+
optionalValues choices - the permitted set, named for what it is
|
|
47
|
+
verifyRegex verify_regex
|
|
48
|
+
liveToggle live
|
|
49
|
+
|
|
50
|
+
Two things were added. `minimum` and `maximum`, because a bound is part of
|
|
51
|
+
what most of this tree's integers are - `non_negative_int` and `positive_int`
|
|
52
|
+
are among base.py's most used predicates, and a `must` clause promising
|
|
53
|
+
"non-negative" with nothing enforcing it is a sentence an operator never gets
|
|
54
|
+
to read. And `must` itself, which is not a duplicate of `description`:
|
|
55
|
+
the description is what the setting means, `must` is what a wrong value is
|
|
56
|
+
told. "seconds between checks; 0 = only on command" against "be a
|
|
57
|
+
non-negative integer of seconds". Deriving the second from the type would
|
|
58
|
+
produce worse English than the sentences operators already read.
|
|
59
|
+
"""
|
|
60
|
+
from __future__ import annotations
|
|
61
|
+
|
|
62
|
+
import dataclasses
|
|
63
|
+
import re
|
|
64
|
+
from typing import Any, Callable, Generic, Mapping, Optional, Sequence, Type, TypedDict, TypeVar, Union
|
|
65
|
+
|
|
66
|
+
from .base import Block, Problems
|
|
67
|
+
|
|
68
|
+
T = TypeVar('T')
|
|
69
|
+
|
|
70
|
+
# Precedence, highest first. The index is the precedence, so a layer added
|
|
71
|
+
# later must be inserted where it belongs rather than appended.
|
|
72
|
+
OVERRIDE, ARG, ENV, FILE, MANIFEST, DEFAULT = 'override', 'arg', 'env', 'file', 'manifest', 'default'
|
|
73
|
+
SOURCES: tuple[str, ...] = (OVERRIDE, ARG, ENV, FILE, MANIFEST, DEFAULT)
|
|
74
|
+
|
|
75
|
+
# Which half of a parameter's access statement the node acts on today.
|
|
76
|
+
#
|
|
77
|
+
# `sources` is enforced: `resolve` skips a layer the parameter does not
|
|
78
|
+
# permit and `check` refuses such a value by name.
|
|
79
|
+
#
|
|
80
|
+
# `requires` is DECLARED ONLY. Nothing authenticates a principal yet - the
|
|
81
|
+
# control socket is chmod 0o600 and all or nothing, with no peer credential
|
|
82
|
+
# read and no token - so a parameter naming an ability is stating an intent,
|
|
83
|
+
# not defending itself. Shipping it as though it were a defence would be a
|
|
84
|
+
# control reporting a protection that does not exist, which is worse than no
|
|
85
|
+
# field at all because it reads as security. Same footing, and for the same
|
|
86
|
+
# reason, as `BaseStore.DURABILITY`. `every_parameter` reports which is which
|
|
87
|
+
# so the two cannot be confused, and enforcement arrives with an
|
|
88
|
+
# authenticated principal on the socket.
|
|
89
|
+
ACCESS_ENFORCED: tuple[str, ...] = ('sources',)
|
|
90
|
+
ACCESS_DECLARED_ONLY: tuple[str, ...] = ('requires',)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class _Stated(TypedDict):
|
|
94
|
+
"""The part of a parameter that is always reported."""
|
|
95
|
+
key: str
|
|
96
|
+
name: str
|
|
97
|
+
description: str
|
|
98
|
+
type: str
|
|
99
|
+
default: Any
|
|
100
|
+
required: bool
|
|
101
|
+
live: bool
|
|
102
|
+
tags: list[str]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ParameterRecord(_Stated, total=False):
|
|
106
|
+
"""A parameter as `meshnode config` and the docs read it.
|
|
107
|
+
|
|
108
|
+
Named records rather than loose mappings, for the reason check-dicts.py
|
|
109
|
+
enforces it: a row an operator reads by field name is the case that rule
|
|
110
|
+
exists for. The optional half is present only when the parameter states
|
|
111
|
+
it, so a report does not show an empty pattern or a bound that is not
|
|
112
|
+
there.
|
|
113
|
+
"""
|
|
114
|
+
choices: list[Any]
|
|
115
|
+
verify_regex: str
|
|
116
|
+
minimum: Union[int, float]
|
|
117
|
+
maximum: Union[int, float]
|
|
118
|
+
env: str
|
|
119
|
+
secret: bool
|
|
120
|
+
requires: str
|
|
121
|
+
requires_enforced: bool
|
|
122
|
+
sources: list[str]
|
|
123
|
+
resource: str
|
|
124
|
+
short: str
|
|
125
|
+
cli_name: str
|
|
126
|
+
positional: bool
|
|
127
|
+
nargs: str
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ParameterRow(ParameterRecord, total=False):
|
|
131
|
+
"""A `ParameterRecord` with the block it belongs to, for the flat list."""
|
|
132
|
+
block: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class BlockRecord(TypedDict):
|
|
136
|
+
"""One block, and every parameter it declares."""
|
|
137
|
+
key: str
|
|
138
|
+
label: str
|
|
139
|
+
parameters: list[ParameterRecord]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclasses.dataclass(frozen=True)
|
|
143
|
+
class Parameter(Generic[T]):
|
|
144
|
+
"""Everything true about one setting, in one place.
|
|
145
|
+
|
|
146
|
+
`validate` is the escape hatch, and it is deliberate. A regex and a set of
|
|
147
|
+
choices describe a scalar or a list of scalars completely; they cannot
|
|
148
|
+
describe `updates.policy`, which is a mapping whose *values* are an enum
|
|
149
|
+
and which has a special sentence for a policy that does not exist yet. A
|
|
150
|
+
parameter that cannot state its own rule says so by carrying the callable
|
|
151
|
+
that can, and stays the single declaration either way - which is the point,
|
|
152
|
+
because a setting this file does not know about is a setting
|
|
153
|
+
`every_parameter()` cannot show an operator.
|
|
154
|
+
"""
|
|
155
|
+
key: str
|
|
156
|
+
name: str
|
|
157
|
+
description: str
|
|
158
|
+
# `typing.Type` rather than the builtin: the field below is named `type`,
|
|
159
|
+
# which shadows the builtin everywhere else in this class body, so an
|
|
160
|
+
# annotation written `type[...]` would resolve to the field. This keeps
|
|
161
|
+
# the schema's own name for it.
|
|
162
|
+
type: Type[Any]
|
|
163
|
+
# A value, or a factory for one. A mutable default needs the factory, and
|
|
164
|
+
# `release_key` derives its default from the running release. `value()`
|
|
165
|
+
# resolves either, so nothing else has to know which it is.
|
|
166
|
+
default: Union[T, Callable[[], T]]
|
|
167
|
+
must: str = ''
|
|
168
|
+
tags: tuple[str, ...] = ()
|
|
169
|
+
required: bool = False
|
|
170
|
+
choices: tuple[Any, ...] = ()
|
|
171
|
+
verify_regex: str = ''
|
|
172
|
+
live: bool = False
|
|
173
|
+
item: Optional[Type[Any]] = None
|
|
174
|
+
# Access. `requires` is the UCAN ability a delegation must carry to set
|
|
175
|
+
# this - UCAN's `can`, a verb and not a role, so a role registry no mesh
|
|
176
|
+
# can agree on is never needed. Empty means operator only and is the
|
|
177
|
+
# default on purpose: remote settability is opted into per parameter and
|
|
178
|
+
# is never inherited, so a parameter added without thinking about access
|
|
179
|
+
# is closed rather than open.
|
|
180
|
+
# The dataclass field this lands on, when it is not the file's own key.
|
|
181
|
+
# The MeshNode manifest writes `maxBytes` and `allowPrivateMirror`; the
|
|
182
|
+
# block holds `max_bytes` and `allow_private_mirror`. The file's spelling
|
|
183
|
+
# is the key, because that is what an operator types.
|
|
184
|
+
field: str = ''
|
|
185
|
+
# A credential, so a report must not print it. `settlement.nwc_uri` is a
|
|
186
|
+
# wallet connection string. The lease refusal in node/storage/base.py put
|
|
187
|
+
# a database password in an operator's log earlier today for want of
|
|
188
|
+
# exactly this distinction, so it is stated rather than remembered.
|
|
189
|
+
secret: bool = False
|
|
190
|
+
requires: str = ''
|
|
191
|
+
# Which escalation layers may set it. Empty means all of them. This is
|
|
192
|
+
# the half an ability string cannot state: the six layers are six
|
|
193
|
+
# different principals, so "host only, never over the wire" is said here
|
|
194
|
+
# and nowhere else.
|
|
195
|
+
sources: tuple[str, ...] = ()
|
|
196
|
+
minimum: Optional[Union[int, float]] = None
|
|
197
|
+
maximum: Optional[Union[int, float]] = None
|
|
198
|
+
env: str = ''
|
|
199
|
+
validate: Optional[Callable[[str, Any, Problems], None]] = None
|
|
200
|
+
coerce: Optional[Callable[[Any], Any]] = None
|
|
201
|
+
short: str = ''
|
|
202
|
+
cli_name: str = ''
|
|
203
|
+
positional: bool = False
|
|
204
|
+
nargs: str = ''
|
|
205
|
+
cli: bool = True
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def attr(self) -> str:
|
|
209
|
+
"""The dataclass field name: the key unless an alias was given."""
|
|
210
|
+
return self.field or self.key
|
|
211
|
+
|
|
212
|
+
def value(self) -> Any:
|
|
213
|
+
"""The default, with a factory called. One place knows which it is."""
|
|
214
|
+
d: Any = self.default
|
|
215
|
+
return d() if callable(d) else d
|
|
216
|
+
|
|
217
|
+
def resource(self, label: str, node_did: str = '') -> str:
|
|
218
|
+
"""The UCAN resource this parameter is, as `with` would name it.
|
|
219
|
+
|
|
220
|
+
Derived rather than declared: 151 hand-written resource strings would
|
|
221
|
+
be 151 chances to name the wrong one, and the name is already implied
|
|
222
|
+
by the block and the key.
|
|
223
|
+
"""
|
|
224
|
+
path = 'config/%s.%s' % (label, self.key)
|
|
225
|
+
return '%s#%s' % (node_did, path) if node_did else path
|
|
226
|
+
|
|
227
|
+
def settable_by(self, source: str) -> bool:
|
|
228
|
+
"""Whether `source` is a layer allowed to set this parameter.
|
|
229
|
+
|
|
230
|
+
A data rule, decided by what the parameter declares about layers -
|
|
231
|
+
not by who is asking, which is a question this class must never
|
|
232
|
+
answer. An empty `sources` permits every layer, and DEFAULT is always
|
|
233
|
+
permitted because it is not a layer anyone sets.
|
|
234
|
+
"""
|
|
235
|
+
if source == DEFAULT or not self.sources:
|
|
236
|
+
return True
|
|
237
|
+
return source in self.sources
|
|
238
|
+
|
|
239
|
+
def sentence(self, label: str) -> str:
|
|
240
|
+
"""What an operator is told when the value is wrong."""
|
|
241
|
+
return '%s.%s must %s' % (label, self.key, self.must)
|
|
242
|
+
|
|
243
|
+
def ok(self, value: Any) -> bool:
|
|
244
|
+
"""Whether `value` satisfies what this parameter states about itself.
|
|
245
|
+
|
|
246
|
+
Only the stated things: type, the element type, the permitted set and
|
|
247
|
+
the regex. A parameter carrying `validate` is not judged here.
|
|
248
|
+
"""
|
|
249
|
+
if self.type is bool:
|
|
250
|
+
# bool before int, because bool is a subclass of int and `true`
|
|
251
|
+
# is not an acceptable integer anywhere in this file.
|
|
252
|
+
if not isinstance(value, bool):
|
|
253
|
+
return False
|
|
254
|
+
elif self.type is int:
|
|
255
|
+
if not isinstance(value, int) or isinstance(value, bool):
|
|
256
|
+
return False
|
|
257
|
+
# The bound is part of the type for most of this tree's integers:
|
|
258
|
+
# `non_negative_int` and `positive_int` are two of base.py's most
|
|
259
|
+
# used predicates, and a `must` promising "non-negative" that
|
|
260
|
+
# nothing enforces is a sentence an operator never sees.
|
|
261
|
+
if self.minimum is not None and value < self.minimum:
|
|
262
|
+
return False
|
|
263
|
+
if self.maximum is not None and value > self.maximum:
|
|
264
|
+
return False
|
|
265
|
+
elif self.type in (tuple, list):
|
|
266
|
+
if not isinstance(value, (list, tuple)):
|
|
267
|
+
return False
|
|
268
|
+
if self.item is not None and not all(_is(v, self.item) for v in value):
|
|
269
|
+
return False
|
|
270
|
+
if self.choices and not all(v in self.choices for v in value):
|
|
271
|
+
return False
|
|
272
|
+
if self.verify_regex and not all(
|
|
273
|
+
isinstance(v, str) and re.fullmatch(self.verify_regex, v) for v in value):
|
|
274
|
+
return False
|
|
275
|
+
return True
|
|
276
|
+
elif self.type is dict:
|
|
277
|
+
if not isinstance(value, Mapping):
|
|
278
|
+
return False
|
|
279
|
+
elif not isinstance(value, self.type):
|
|
280
|
+
return False
|
|
281
|
+
if self.choices and value not in self.choices:
|
|
282
|
+
return False
|
|
283
|
+
if self.verify_regex:
|
|
284
|
+
if not isinstance(value, str) or not re.fullmatch(self.verify_regex, value):
|
|
285
|
+
return False
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
def read(self, raw: Mapping[str, Any]) -> Any:
|
|
289
|
+
"""This parameter's value out of a raw config object.
|
|
290
|
+
|
|
291
|
+
An absent key and an explicit `null` both mean "not said", so both
|
|
292
|
+
take the default. `coerce` is what turns what the file is allowed to
|
|
293
|
+
say into what the compartment reads - a pubkey lowercased, a list
|
|
294
|
+
frozen to a tuple.
|
|
295
|
+
"""
|
|
296
|
+
v = raw.get(self.key) if isinstance(raw, Mapping) else None
|
|
297
|
+
if v is None:
|
|
298
|
+
v = self.value()
|
|
299
|
+
elif self.coerce is not None:
|
|
300
|
+
v = self.coerce(v)
|
|
301
|
+
elif self.type is tuple and isinstance(v, (list, tuple)):
|
|
302
|
+
v = tuple(v)
|
|
303
|
+
return v
|
|
304
|
+
|
|
305
|
+
def describe(self) -> ParameterRecord:
|
|
306
|
+
"""The parameter as data, for `meshnode config` and for the docs.
|
|
307
|
+
|
|
308
|
+
`default` goes through the factory when it is one, so what is shown is
|
|
309
|
+
a value an operator could paste rather than the word 'function'.
|
|
310
|
+
"""
|
|
311
|
+
# A secret's default is not shown. The point of a report is that an
|
|
312
|
+
# operator can read it, and a credential printed into a terminal, a
|
|
313
|
+
# log or a paste is a credential disclosed.
|
|
314
|
+
shown: Any = '(secret)' if self.secret and self.value() else _jsonable(self.value())
|
|
315
|
+
out: ParameterRecord = {
|
|
316
|
+
'key': self.key, 'name': self.name, 'description': self.description,
|
|
317
|
+
'type': _type_name(self.type, self.item), 'default': shown,
|
|
318
|
+
'required': self.required, 'live': self.live, 'tags': list(self.tags),
|
|
319
|
+
}
|
|
320
|
+
if self.secret:
|
|
321
|
+
out['secret'] = True
|
|
322
|
+
if self.choices:
|
|
323
|
+
out['choices'] = [_jsonable(c) for c in self.choices]
|
|
324
|
+
if self.verify_regex:
|
|
325
|
+
out['verify_regex'] = self.verify_regex
|
|
326
|
+
if self.minimum is not None:
|
|
327
|
+
out['minimum'] = self.minimum
|
|
328
|
+
if self.maximum is not None:
|
|
329
|
+
out['maximum'] = self.maximum
|
|
330
|
+
if self.env:
|
|
331
|
+
out['env'] = self.env
|
|
332
|
+
if self.requires:
|
|
333
|
+
out['requires'] = self.requires
|
|
334
|
+
# Stated on the row, not only in a footer, because a row is what
|
|
335
|
+
# an operator reads and a declared ability must not look enforced.
|
|
336
|
+
out['requires_enforced'] = False
|
|
337
|
+
if self.sources:
|
|
338
|
+
out['sources'] = list(self.sources)
|
|
339
|
+
if self.short:
|
|
340
|
+
out['short'] = self.short
|
|
341
|
+
if self.cli_name:
|
|
342
|
+
out['cli_name'] = self.cli_name
|
|
343
|
+
if self.positional:
|
|
344
|
+
out['positional'] = True
|
|
345
|
+
if self.nargs:
|
|
346
|
+
out['nargs'] = self.nargs
|
|
347
|
+
return out
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _is(v: Any, t: Type[Any]) -> bool:
|
|
351
|
+
if t is int:
|
|
352
|
+
return isinstance(v, int) and not isinstance(v, bool)
|
|
353
|
+
return isinstance(v, t)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _type_name(t: Type[Any], item: Optional[Type[Any]]) -> str:
|
|
357
|
+
base = {int: 'int', str: 'str', bool: 'bool', dict: 'object',
|
|
358
|
+
tuple: 'list', list: 'list'}.get(t, getattr(t, '__name__', str(t)))
|
|
359
|
+
if item is not None and base == 'list':
|
|
360
|
+
return 'list[%s]' % _type_name(item, None)
|
|
361
|
+
return base
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _jsonable(v: Any) -> Any:
|
|
365
|
+
if isinstance(v, tuple):
|
|
366
|
+
return [_jsonable(x) for x in v]
|
|
367
|
+
if isinstance(v, Mapping):
|
|
368
|
+
return {k: _jsonable(x) for k, x in v.items()}
|
|
369
|
+
return v
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@dataclasses.dataclass(frozen=True)
|
|
373
|
+
class Arg(Generic[T]):
|
|
374
|
+
"""A resolved value, and where it came from.
|
|
375
|
+
|
|
376
|
+
`source` is not in the original schema and is the one thing added to it.
|
|
377
|
+
The purpose of this system is that an operator can see how the node is
|
|
378
|
+
configured, and "900" does not answer "why 900" - "900, from config.json"
|
|
379
|
+
does, and distinguishes a deliberate setting from a default nobody chose.
|
|
380
|
+
"""
|
|
381
|
+
key: str
|
|
382
|
+
type: Type[Any]
|
|
383
|
+
value: T
|
|
384
|
+
source: str = DEFAULT
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def is_default(self) -> bool:
|
|
388
|
+
return self.source == DEFAULT
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def resolve(param: Parameter[T], layers: Mapping[str, Any]) -> Arg[T]:
|
|
392
|
+
"""The winning value for `param`, by the order at the top of this file.
|
|
393
|
+
|
|
394
|
+
A layer that does not mention the parameter is skipped, and so is an
|
|
395
|
+
explicit `null` - a file saying `null` is declining to set it, not setting
|
|
396
|
+
it to nothing. OVERRIDE is ignored for a parameter that is not `live`,
|
|
397
|
+
because applying one would be a change the node cannot honour until it
|
|
398
|
+
restarts, reported as though it had taken effect.
|
|
399
|
+
|
|
400
|
+
A layer the parameter does not permit is skipped too. That is enforcement,
|
|
401
|
+
and it is enforceable here because it reads only what the parameter
|
|
402
|
+
declares: no identity is involved, so no security decision is being taken
|
|
403
|
+
inside a class whose job is to describe data. `ParamBlock.check` refuses
|
|
404
|
+
such a value by name as well, because a setting silently doing nothing is
|
|
405
|
+
the failure this whole mechanism exists to remove.
|
|
406
|
+
"""
|
|
407
|
+
for source in SOURCES:
|
|
408
|
+
if source == DEFAULT:
|
|
409
|
+
break
|
|
410
|
+
if source == OVERRIDE and not param.live:
|
|
411
|
+
continue
|
|
412
|
+
if not param.settable_by(source):
|
|
413
|
+
continue
|
|
414
|
+
layer = layers.get(source)
|
|
415
|
+
if not isinstance(layer, Mapping) or layer.get(param.key) is None:
|
|
416
|
+
continue
|
|
417
|
+
v = layer[param.key]
|
|
418
|
+
return Arg(param.key, param.type, param.coerce(v) if param.coerce else v, source)
|
|
419
|
+
return Arg(param.key, param.type, param.value(), DEFAULT)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class ParamBlock(Block):
|
|
423
|
+
"""A `Block` whose `check` and `from_dict` come from its `PARAMS`.
|
|
424
|
+
|
|
425
|
+
A subclass declares `PARAMS` and the dataclass fields that hold them, and
|
|
426
|
+
writes neither method. Sentences come out in `PARAMS` order, which is why
|
|
427
|
+
the order is the order an operator reads in the file.
|
|
428
|
+
|
|
429
|
+
`SWITCHABLE` keeps the shorthand an optional compartment already has: the
|
|
430
|
+
block may be plain `true`, meaning on with every default.
|
|
431
|
+
"""
|
|
432
|
+
PARAMS: tuple[Parameter[Any], ...] = ()
|
|
433
|
+
SWITCHABLE: bool = False
|
|
434
|
+
# The parameter that is true because the block is present at all.
|
|
435
|
+
#
|
|
436
|
+
# `p2p` and `lan` both declare `enabled: bool = False` and mean opposite
|
|
437
|
+
# things by it: p2p is on as soon as its key appears, lan is not. That
|
|
438
|
+
# distinction lived only in the body of `from_dict` - two blocks that
|
|
439
|
+
# look identical behaving differently - and a conversion that trusted the
|
|
440
|
+
# dataclass default would have silently turned the libp2p transport off
|
|
441
|
+
# on every node configuring it as an object. Said here instead.
|
|
442
|
+
PRESENCE_ENABLES: str = ''
|
|
443
|
+
# False when PARAMS are a declaration for the census and the methods stay
|
|
444
|
+
# hand-written, because the raw form is not an object of named settings:
|
|
445
|
+
# `capacity` IS the mapping, and carries no field names in the file at
|
|
446
|
+
# all. Stated rather than inferred, so a block that merely has not been
|
|
447
|
+
# converted cannot pass for a deliberate exception.
|
|
448
|
+
DERIVED: bool = True
|
|
449
|
+
|
|
450
|
+
@classmethod
|
|
451
|
+
def check(cls, raw: Any, p: Problems) -> None:
|
|
452
|
+
label = cls.label or cls.key
|
|
453
|
+
if cls.SWITCHABLE:
|
|
454
|
+
if isinstance(raw, bool):
|
|
455
|
+
return
|
|
456
|
+
if not isinstance(raw, Mapping):
|
|
457
|
+
p.add('%s must be true, false or an object' % label)
|
|
458
|
+
return
|
|
459
|
+
elif not isinstance(raw, Mapping):
|
|
460
|
+
p.add('%s must be an object' % label)
|
|
461
|
+
return
|
|
462
|
+
for param in cls.PARAMS:
|
|
463
|
+
# config.json is the FILE layer. A parameter that does not permit
|
|
464
|
+
# it is refused by name rather than ignored: an operator who
|
|
465
|
+
# writes a setting that silently does nothing is exactly the
|
|
466
|
+
# failure this mechanism removes.
|
|
467
|
+
if (param.key in raw and raw[param.key] is not None
|
|
468
|
+
and not param.settable_by(FILE)):
|
|
469
|
+
p.add('%s.%s cannot be set in config.json; it is settable by %s'
|
|
470
|
+
% (label, param.key, ', '.join(param.sources)))
|
|
471
|
+
continue
|
|
472
|
+
if param.validate is not None:
|
|
473
|
+
param.validate(label, raw, p)
|
|
474
|
+
continue
|
|
475
|
+
if param.key not in raw or raw[param.key] is None:
|
|
476
|
+
if param.required:
|
|
477
|
+
p.add('%s.%s is required' % (label, param.key))
|
|
478
|
+
continue
|
|
479
|
+
if not param.ok(raw[param.key]):
|
|
480
|
+
p.add(param.sentence(label))
|
|
481
|
+
|
|
482
|
+
@classmethod
|
|
483
|
+
def from_dict(cls, raw: Any) -> Any:
|
|
484
|
+
if cls.SWITCHABLE and isinstance(raw, bool):
|
|
485
|
+
# `true` is on with every default. `false` is the block's own
|
|
486
|
+
# default, which for a presence-enabled block is off.
|
|
487
|
+
if raw and cls.PRESENCE_ENABLES:
|
|
488
|
+
return cls(**{cls.PRESENCE_ENABLES: True})
|
|
489
|
+
return cls()
|
|
490
|
+
src: Mapping[str, Any] = raw if isinstance(raw, Mapping) else {}
|
|
491
|
+
vals = {p.attr: p.read(src) for p in cls.PARAMS}
|
|
492
|
+
if cls.PRESENCE_ENABLES and src.get(cls.PRESENCE_ENABLES) is None:
|
|
493
|
+
# Reached only because the key is in the file, so the block is on
|
|
494
|
+
# unless the operator wrote it off explicitly.
|
|
495
|
+
vals[cls.PRESENCE_ENABLES] = True
|
|
496
|
+
return cls(**vals)
|
|
497
|
+
|
|
498
|
+
@classmethod
|
|
499
|
+
def parameters(cls) -> tuple[Parameter[Any], ...]:
|
|
500
|
+
return cls.PARAMS
|
|
501
|
+
|
|
502
|
+
@classmethod
|
|
503
|
+
def describe(cls) -> BlockRecord:
|
|
504
|
+
"""This block as data: its key and every parameter it declares."""
|
|
505
|
+
return {'key': cls.key, 'label': cls.label or cls.key,
|
|
506
|
+
'parameters': [p.describe() for p in cls.PARAMS]}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def declared(block_cls: type) -> tuple[Parameter[Any], ...]:
|
|
510
|
+
"""The parameters a block declares, or () for one not yet converted.
|
|
511
|
+
|
|
512
|
+
Returns () rather than raising so the two kinds of block can coexist while
|
|
513
|
+
the tree is converted one compartment at a time, and so a report can say
|
|
514
|
+
honestly which blocks it cannot describe yet.
|
|
515
|
+
"""
|
|
516
|
+
return tuple(getattr(block_cls, 'PARAMS', ()) or ())
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def every_parameter(blocks: Sequence[type]) -> list[ParameterRow]:
|
|
520
|
+
"""Every declared parameter across `blocks`, as flat rows.
|
|
521
|
+
|
|
522
|
+
This is the visibility the system exists for: one list, with the block it
|
|
523
|
+
belongs to, its type, its default and whether a reload applies it.
|
|
524
|
+
"""
|
|
525
|
+
out: list[ParameterRow] = []
|
|
526
|
+
for b in blocks:
|
|
527
|
+
for param in declared(b):
|
|
528
|
+
row: ParameterRow = {**param.describe(),
|
|
529
|
+
'block': getattr(b, 'key', '') or getattr(b, '__name__', '')}
|
|
530
|
+
out.append(row)
|
|
531
|
+
return out
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def converted(block_cls: type) -> bool:
|
|
535
|
+
"""Whether this block states its settings as parameters.
|
|
536
|
+
|
|
537
|
+
Asked of the class, not of its PARAMS: `mediation` and `transport` are
|
|
538
|
+
reserved and have no settings at all, so an empty PARAMS is their correct
|
|
539
|
+
and final answer. Counting them as outstanding would never reach zero.
|
|
540
|
+
"""
|
|
541
|
+
return isinstance(block_cls, type) and issubclass(block_cls, ParamBlock)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def undeclared(blocks: Sequence[type]) -> list[str]:
|
|
545
|
+
"""Blocks not yet stating their settings as parameters."""
|
|
546
|
+
return [getattr(b, 'key', '') or b.__name__ for b in blocks if not converted(b)]
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def hand_written(blocks: Sequence[type]) -> list[str]:
|
|
550
|
+
"""Blocks that declare parameters but keep their own check and from_dict.
|
|
551
|
+
|
|
552
|
+
Separate from `undeclared` because they mean different things: this list
|
|
553
|
+
is complete for the census and deliberately not derived, that one is work
|
|
554
|
+
outstanding.
|
|
555
|
+
"""
|
|
556
|
+
return [getattr(b, 'key', '') or b.__name__ for b in blocks
|
|
557
|
+
if declared(b) and not getattr(b, 'DERIVED', True)]
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
__all__ = ['Parameter', 'ParameterRecord', 'ParameterRow', 'BlockRecord', 'Arg', 'ParamBlock',
|
|
561
|
+
'resolve', 'declared', 'every_parameter', 'undeclared', 'SOURCES', 'OVERRIDE', 'ARG',
|
|
562
|
+
'ENV', 'FILE', 'MANIFEST', 'DEFAULT', 'ACCESS_ENFORCED', 'ACCESS_DECLARED_ONLY',
|
|
563
|
+
'hand_written', 'converted']
|