1818__all__ = ("IOCRecordServer" ,)
1919
2020
21+ def _with_order (item : Any , order : int | None ) -> Any :
22+ # p4p.server.Server's providers= entries are either a bare provider or a
23+ # (provider, order) tuple -- reattach whichever `order` this entry came
24+ # in with (or none) to each provider it expands to.
25+ return (item , order ) if order is not None else item
26+
27+
2128def _dynamic_fields_provider (provider : Any ) -> _DynamicProvider | None :
2229 # Builds the *additional* DynamicRecordFields-backed provider for a
2330 # plain-dict entry's "<name>.<FIELD>" sub-PVs (see the class docstring
@@ -66,13 +73,22 @@ class IOCRecordServer(_Server):
6673 `StaticRecordProvider`'s `~p4p.server.StaticProvider` -- maintains no
6774 enumerable list of the names it can serve.
6875
76+ An `IOCRecordProvider` instance can also be passed directly, same as a
77+ plain dict or a `StaticRecordProvider` -- unlike `~p4p.server.Server`
78+ itself, which has no concept of a single `providers=` entry backed by two
79+ providers underneath (see `IOCRecordProvider`'s docstring for why it
80+ isn't itself a `~p4p.server.StaticProvider`/`~p4p.server.DynamicProvider`),
81+ `IOCRecordServer` unpacks it into its `.providers` pair automatically, so
82+ ``providers=[base, pvs]`` works the same as
83+ ``providers=[*base.providers, pvs]``.
84+
6985 For DESC updates after add(), per-PV overrides, matching sub-PV flavor to
7086 an asyncio base PV, or `pvlist` visibility, build a `StaticRecordProvider`
7187 explicitly (see its own docstring) and pass that instead of a dict;
72- entries that aren't a plain dict (a provider name string, or an
73- already-constructed provider instance, including a `StaticRecordProvider`)
74- are passed through to `~p4p.server.Server` unchanged, with no extra
75- "<name>.<FIELD>" handling added.
88+ entries that aren't a plain dict or an `IOCRecordProvider` (a provider
89+ name string, or an already-constructed provider instance, including a
90+ `StaticRecordProvider`) are passed through to `~p4p.server.Server`
91+ unchanged, with no extra "<name>.<FIELD>" handling added.
7692 """
7793
7894 def __init__ (self , providers : list [Any ], isolate : bool = False , ** kws : Any ) -> None :
@@ -86,9 +102,17 @@ def __init__(self, providers: list[Any], isolate: bool = False, **kws: Any) -> N
86102 wrapped : list [Any ] = []
87103 for entry in providers :
88104 provider , order = entry if isinstance (entry , tuple ) else (entry , None )
89- wrapped .append ((provider , order ) if order is not None else provider )
105+ sub_providers = getattr (provider , "providers" , None )
106+ if isinstance (sub_providers , tuple ):
107+ # Already backed by its own provider pair (e.g.
108+ # IOCRecordProvider's static + DynamicRecordFields pair) --
109+ # unpack rather than treating it as a single provider (it
110+ # isn't one) or a dict (it has no .items()).
111+ wrapped .extend (_with_order (sub_provider , order ) for sub_provider in sub_providers )
112+ continue
113+ wrapped .append (_with_order (provider , order ))
90114 fields_provider = _dynamic_fields_provider (provider )
91115 if fields_provider is not None :
92116 self ._field_providers .append (fields_provider )
93- wrapped .append ((fields_provider , order ) if order is not None else fields_provider )
117+ wrapped .append (_with_order (fields_provider , order ))
94118 super ().__init__ (wrapped , isolate = isolate , ** kws )
0 commit comments