npe2 0.7.9__py3-none-any.whl → 0.8.0rc0__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.
- npe2/_command_registry.py +6 -5
- npe2/_dynamic_plugin.py +25 -27
- npe2/_inspection/_compile.py +9 -8
- npe2/_inspection/_fetch.py +18 -30
- npe2/_inspection/_from_npe1.py +26 -32
- npe2/_inspection/_setuputils.py +14 -14
- npe2/_inspection/_visitors.py +26 -21
- npe2/_plugin_manager.py +45 -57
- npe2/_pydantic_util.py +53 -0
- npe2/_pytest_plugin.py +3 -4
- npe2/_setuptools_plugin.py +9 -9
- npe2/cli.py +25 -21
- npe2/implements.py +13 -10
- npe2/implements.pyi +3 -2
- npe2/io_utils.py +40 -44
- npe2/manifest/_bases.py +15 -14
- npe2/manifest/_npe1_adapter.py +3 -3
- npe2/manifest/_package_metadata.py +40 -47
- npe2/manifest/contributions/_commands.py +16 -14
- npe2/manifest/contributions/_configuration.py +22 -20
- npe2/manifest/contributions/_contributions.py +13 -14
- npe2/manifest/contributions/_icon.py +3 -5
- npe2/manifest/contributions/_json_schema.py +86 -89
- npe2/manifest/contributions/_keybindings.py +5 -6
- npe2/manifest/contributions/_menus.py +11 -9
- npe2/manifest/contributions/_readers.py +10 -8
- npe2/manifest/contributions/_sample_data.py +16 -15
- npe2/manifest/contributions/_submenu.py +2 -4
- npe2/manifest/contributions/_themes.py +18 -22
- npe2/manifest/contributions/_widgets.py +6 -5
- npe2/manifest/contributions/_writers.py +22 -18
- npe2/manifest/schema.py +82 -70
- npe2/manifest/utils.py +24 -28
- npe2/plugin_manager.py +17 -14
- npe2/types.py +16 -19
- {npe2-0.7.9.dist-info → npe2-0.8.0rc0.dist-info}/METADATA +13 -7
- npe2-0.8.0rc0.dist-info/RECORD +49 -0
- {npe2-0.7.9.dist-info → npe2-0.8.0rc0.dist-info}/WHEEL +1 -1
- npe2/_pydantic_compat.py +0 -54
- npe2-0.7.9.dist-info/RECORD +0 -49
- {npe2-0.7.9.dist-info → npe2-0.8.0rc0.dist-info}/entry_points.txt +0 -0
- {npe2-0.7.9.dist-info → npe2-0.8.0rc0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,38 +1,32 @@
|
|
|
1
1
|
from importlib import metadata
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Literal
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Extra,
|
|
8
|
-
Field,
|
|
9
|
-
constr,
|
|
10
|
-
root_validator,
|
|
11
|
-
)
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, constr, model_validator
|
|
5
|
+
|
|
6
|
+
from npe2._pydantic_util import is_list_type
|
|
12
7
|
|
|
13
8
|
# https://packaging.python.org/specifications/core-metadata/
|
|
14
9
|
|
|
15
10
|
MetadataVersion = Literal["1.0", "1.1", "1.2", "2.0", "2.1", "2.2", "2.3"]
|
|
16
11
|
_alphanum = "[a-zA-Z0-9]"
|
|
17
|
-
PackageName = constr(
|
|
12
|
+
PackageName = constr(pattern=f"^{_alphanum}[a-zA-Z0-9._-]*{_alphanum}$")
|
|
18
13
|
|
|
19
14
|
|
|
20
15
|
class PackageMetadata(BaseModel):
|
|
21
16
|
"""Pydantic model for standard python package metadata.
|
|
22
17
|
|
|
23
18
|
https://www.python.org/dev/peps/pep-0566/
|
|
24
|
-
https://packaging.python.org/
|
|
19
|
+
https://packaging.python.org/pattern/core-metadata/
|
|
25
20
|
|
|
26
21
|
The `importlib.metadata` provides the `metadata()` function,
|
|
27
22
|
but it returns a somewhat awkward `email.message.Message` object.
|
|
28
23
|
"""
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
extra = Extra.ignore
|
|
25
|
+
model_config = ConfigDict(extra="ignore")
|
|
32
26
|
|
|
33
27
|
# allow str as a fallback in case the metata-version specification has been
|
|
34
28
|
# updated and we haven't updated the code yet
|
|
35
|
-
metadata_version:
|
|
29
|
+
metadata_version: MetadataVersion | str = Field(
|
|
36
30
|
"1.0", description="Version of the file format"
|
|
37
31
|
)
|
|
38
32
|
name: PackageName = Field( # type: ignore
|
|
@@ -48,34 +42,34 @@ class PackageMetadata(BaseModel):
|
|
|
48
42
|
description="A string containing the distribution's version number. "
|
|
49
43
|
"This field must be in the format specified in PEP 440.",
|
|
50
44
|
)
|
|
51
|
-
dynamic:
|
|
45
|
+
dynamic: list[str] | None = Field(
|
|
52
46
|
None,
|
|
53
47
|
description="A string containing the name of another core metadata "
|
|
54
48
|
"field. The field names Name and Version may not be specified in this field.",
|
|
55
49
|
min_ver="2.2",
|
|
56
50
|
)
|
|
57
|
-
platform:
|
|
51
|
+
platform: list[str] | None = Field(
|
|
58
52
|
None,
|
|
59
53
|
description="A Platform specification describing an operating system "
|
|
60
54
|
"supported by the distribution which is not listed in the “Operating System” "
|
|
61
55
|
"Trove classifiers. See “Classifier” below.",
|
|
62
56
|
)
|
|
63
|
-
supported_platform:
|
|
57
|
+
supported_platform: list[str] | None = Field(
|
|
64
58
|
None,
|
|
65
59
|
description="Binary distributions containing a PKG-INFO file will use the "
|
|
66
60
|
"Supported-Platform field in their metadata to specify the OS and CPU for "
|
|
67
61
|
"which the binary distribution was compiled",
|
|
68
62
|
min_ver="1.1",
|
|
69
63
|
)
|
|
70
|
-
summary:
|
|
64
|
+
summary: str | None = Field(
|
|
71
65
|
None, description="A one-line summary of what the distribution does."
|
|
72
66
|
)
|
|
73
|
-
description:
|
|
67
|
+
description: str | None = Field(
|
|
74
68
|
None,
|
|
75
69
|
description="A longer description of the distribution that can "
|
|
76
70
|
"run to several paragraphs.",
|
|
77
71
|
)
|
|
78
|
-
description_content_type:
|
|
72
|
+
description_content_type: str | None = Field(
|
|
79
73
|
None,
|
|
80
74
|
description="A string stating the markup syntax (if any) used in the "
|
|
81
75
|
"distribution's description, so that tools can intelligently render the "
|
|
@@ -83,45 +77,45 @@ class PackageMetadata(BaseModel):
|
|
|
83
77
|
"text/plain, text/x-rst, text/markdown",
|
|
84
78
|
min_ver="2.1",
|
|
85
79
|
)
|
|
86
|
-
keywords:
|
|
80
|
+
keywords: str | None = Field(
|
|
87
81
|
None,
|
|
88
82
|
description="A list of additional keywords, separated by commas, to be used "
|
|
89
83
|
"to assist searching for the distribution in a larger catalog.",
|
|
90
84
|
)
|
|
91
|
-
home_page:
|
|
85
|
+
home_page: str | None = Field(
|
|
92
86
|
None,
|
|
93
87
|
description="A string containing the URL for the distribution's home page.",
|
|
94
88
|
)
|
|
95
|
-
download_url:
|
|
89
|
+
download_url: str | None = Field(
|
|
96
90
|
None,
|
|
97
91
|
description="A string containing the URL from which THIS version of the "
|
|
98
92
|
"distribution can be downloaded.",
|
|
99
93
|
min_ver="1.1",
|
|
100
94
|
)
|
|
101
|
-
author:
|
|
95
|
+
author: str | None = Field(
|
|
102
96
|
None,
|
|
103
97
|
description="A string containing the author's name at a minimum; "
|
|
104
98
|
"additional contact information may be provided.",
|
|
105
99
|
)
|
|
106
|
-
author_email:
|
|
100
|
+
author_email: str | None = Field(
|
|
107
101
|
None,
|
|
108
102
|
description="A string containing the author's e-mail address. It can contain "
|
|
109
103
|
"a name and e-mail address in the legal forms for a RFC-822 From: header.",
|
|
110
104
|
)
|
|
111
|
-
maintainer:
|
|
105
|
+
maintainer: str | None = Field(
|
|
112
106
|
None,
|
|
113
107
|
description="A string containing the maintainer's name at a minimum; "
|
|
114
108
|
"additional contact information may be provided.",
|
|
115
109
|
min_ver="1.2",
|
|
116
110
|
)
|
|
117
|
-
maintainer_email:
|
|
111
|
+
maintainer_email: str | None = Field(
|
|
118
112
|
None,
|
|
119
113
|
description="A string containing the maintainer's e-mail address. It can "
|
|
120
114
|
"contain a name and e-mail address in the legal forms for a "
|
|
121
115
|
"RFC-822 From: header.",
|
|
122
116
|
min_ver="1.2",
|
|
123
117
|
)
|
|
124
|
-
license:
|
|
118
|
+
license: str | None = Field(
|
|
125
119
|
None,
|
|
126
120
|
description="Text indicating the license covering the distribution where the "
|
|
127
121
|
"license is not a selection from the “License” Trove classifiers. See "
|
|
@@ -129,21 +123,21 @@ class PackageMetadata(BaseModel):
|
|
|
129
123
|
"version of a license which is named via the Classifier field, or to "
|
|
130
124
|
"indicate a variation or exception to such a license.",
|
|
131
125
|
)
|
|
132
|
-
classifier:
|
|
126
|
+
classifier: list[str] | None = Field(
|
|
133
127
|
None,
|
|
134
128
|
description="Each entry is a string giving a single classification value for "
|
|
135
129
|
"the distribution. Classifiers are described in PEP 301, and the Python "
|
|
136
130
|
"Package Index publishes a dynamic list of currently defined classifiers.",
|
|
137
131
|
min_ver="1.1",
|
|
138
132
|
)
|
|
139
|
-
requires_dist:
|
|
133
|
+
requires_dist: list[str] | None = Field(
|
|
140
134
|
None,
|
|
141
135
|
description="The field format specification was relaxed to accept the syntax "
|
|
142
136
|
"used by popular publishing tools. Each entry contains a string naming some "
|
|
143
137
|
"other distutils project required by this distribution.",
|
|
144
138
|
min_ver="1.2",
|
|
145
139
|
)
|
|
146
|
-
requires_python:
|
|
140
|
+
requires_python: str | None = Field(
|
|
147
141
|
None,
|
|
148
142
|
description="This field specifies the Python version(s) that the distribution "
|
|
149
143
|
"is guaranteed to be compatible with. Installation tools may look at this "
|
|
@@ -151,7 +145,7 @@ class PackageMetadata(BaseModel):
|
|
|
151
145
|
"The value must be in the format specified in Version specifiers (PEP 440).",
|
|
152
146
|
min_ver="1.2",
|
|
153
147
|
)
|
|
154
|
-
requires_external:
|
|
148
|
+
requires_external: list[str] | None = Field(
|
|
155
149
|
None,
|
|
156
150
|
description="The field format specification was relaxed to accept the syntax "
|
|
157
151
|
"used by popular publishing tools. Each entry contains a string describing "
|
|
@@ -160,13 +154,13 @@ class PackageMetadata(BaseModel):
|
|
|
160
154
|
"has no semantics which are meaningful to the distutils distribution.",
|
|
161
155
|
min_ver="1.2",
|
|
162
156
|
)
|
|
163
|
-
project_url:
|
|
157
|
+
project_url: list[str] | None = Field(
|
|
164
158
|
None,
|
|
165
159
|
description="A string containing a browsable URL for the project and a label "
|
|
166
160
|
"for it, separated by a comma.",
|
|
167
161
|
min_ver="1.2",
|
|
168
162
|
)
|
|
169
|
-
provides_extra:
|
|
163
|
+
provides_extra: list[str] | None = Field(
|
|
170
164
|
None,
|
|
171
165
|
description="A string containing the name of an optional feature. Must be a "
|
|
172
166
|
"valid Python identifier. May be used to make a dependency conditional on "
|
|
@@ -175,19 +169,18 @@ class PackageMetadata(BaseModel):
|
|
|
175
169
|
)
|
|
176
170
|
|
|
177
171
|
# rarely_used
|
|
178
|
-
provides_dist:
|
|
179
|
-
obsoletes_dist:
|
|
172
|
+
provides_dist: list[str] | None = Field(None, min_ver="1.2")
|
|
173
|
+
obsoletes_dist: list[str] | None = Field(None, min_ver="1.2")
|
|
180
174
|
|
|
181
|
-
@
|
|
175
|
+
@model_validator(mode="before")
|
|
176
|
+
@classmethod
|
|
182
177
|
def _validate_root(cls, values):
|
|
183
178
|
if "metadata_version" not in values:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
values["metadata_version"] = str(max(float(x) for x in mins))
|
|
179
|
+
min_vers = {"1.0"}
|
|
180
|
+
for n, info in cls.model_fields.items():
|
|
181
|
+
if n in values and info.json_schema_extra is not None:
|
|
182
|
+
min_vers.add(info.json_schema_extra.get("min_ver", "1.0"))
|
|
183
|
+
values["metadata_version"] = str(max(float(x) for x in min_vers))
|
|
191
184
|
return values
|
|
192
185
|
|
|
193
186
|
@classmethod
|
|
@@ -200,8 +193,8 @@ class PackageMetadata(BaseModel):
|
|
|
200
193
|
@classmethod
|
|
201
194
|
def from_dist_metadata(cls, meta: "metadata.PackageMetadata") -> "PackageMetadata":
|
|
202
195
|
"""Generate PackageMetadata from importlib.metadata.PackageMetdata."""
|
|
203
|
-
manys = [
|
|
204
|
-
d:
|
|
196
|
+
manys = [n for n, f in cls.model_fields.items() if is_list_type(f.annotation)]
|
|
197
|
+
d: dict[str, str | list[str]] = {}
|
|
205
198
|
# looks like py3.10 changed the public protocol of metadata.PackageMetadata
|
|
206
199
|
# and they don't want you to rely on the Mapping interface... however, the
|
|
207
200
|
# __iter__ method doesn't iterate key value pairs, just keys, and I can't figure
|
|
@@ -213,7 +206,7 @@ class PackageMetadata(BaseModel):
|
|
|
213
206
|
d.setdefault(key, []).append(value) # type: ignore
|
|
214
207
|
else:
|
|
215
208
|
d[key] = value
|
|
216
|
-
return cls.
|
|
209
|
+
return cls.model_validate(d)
|
|
217
210
|
|
|
218
211
|
def __hash__(self) -> int:
|
|
219
212
|
return id(self)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Annotated, Any
|
|
4
|
+
|
|
5
|
+
from pydantic import AfterValidator, BaseModel, ConfigDict, Field
|
|
2
6
|
|
|
3
|
-
from npe2._pydantic_compat import BaseModel, Extra, Field, validator
|
|
4
7
|
from npe2.manifest import _validators
|
|
5
8
|
from npe2.types import PythonName
|
|
6
9
|
|
|
@@ -33,7 +36,7 @@ class CommandContribution(BaseModel):
|
|
|
33
36
|
```
|
|
34
37
|
"""
|
|
35
38
|
|
|
36
|
-
id: str = Field(
|
|
39
|
+
id: Annotated[str, AfterValidator(_validators.command_id)] = Field(
|
|
37
40
|
...,
|
|
38
41
|
description="A unique identifier used to reference this command. While this may"
|
|
39
42
|
" look like a python fully qualified name this does *not* refer to a python "
|
|
@@ -41,7 +44,6 @@ class CommandContribution(BaseModel):
|
|
|
41
44
|
"the name of the package, and include only alphanumeric characters, plus "
|
|
42
45
|
"dashes and underscores.",
|
|
43
46
|
)
|
|
44
|
-
_valid_id = validator("id", allow_reuse=True)(_validators.command_id)
|
|
45
47
|
|
|
46
48
|
title: str = Field(
|
|
47
49
|
...,
|
|
@@ -49,26 +51,27 @@ class CommandContribution(BaseModel):
|
|
|
49
51
|
"for example, when searching in a command palette. Examples: 'Generate lily "
|
|
50
52
|
"sample', 'Read tiff image', 'Open gaussian blur widget'. ",
|
|
51
53
|
)
|
|
52
|
-
python_name:
|
|
54
|
+
python_name: (
|
|
55
|
+
Annotated[PythonName, AfterValidator(_validators.python_name)] | None
|
|
56
|
+
) = Field(
|
|
53
57
|
None,
|
|
54
58
|
description="Fully qualified name to a callable python object "
|
|
55
59
|
"implementing this command. This usually takes the form of "
|
|
56
60
|
"`{obj.__module__}:{obj.__qualname__}` "
|
|
57
61
|
"(e.g. `my_package.a_module:some_function`)",
|
|
58
62
|
)
|
|
59
|
-
_valid_pyname = validator("python_name", allow_reuse=True)(_validators.python_name)
|
|
60
63
|
|
|
61
|
-
short_title:
|
|
64
|
+
short_title: str | None = Field(
|
|
62
65
|
None,
|
|
63
66
|
description="Short title by which the command is represented in "
|
|
64
67
|
"the UI. Menus pick either `title` or `short_title` depending on the context "
|
|
65
68
|
"in which they show commands.",
|
|
66
69
|
)
|
|
67
|
-
category:
|
|
70
|
+
category: str | None = Field(
|
|
68
71
|
None,
|
|
69
72
|
description="Category string by which the command may be grouped in the UI.",
|
|
70
73
|
)
|
|
71
|
-
icon:
|
|
74
|
+
icon: str | Icon | None = Field(
|
|
72
75
|
None,
|
|
73
76
|
description="Icon used to represent this command in the UI, on "
|
|
74
77
|
"buttons or in menus. These may be [superqt](https://github.com/napari/superqt)"
|
|
@@ -76,7 +79,7 @@ class CommandContribution(BaseModel):
|
|
|
76
79
|
"expected to depend on any fonticon libraries they use, e.g "
|
|
77
80
|
"[fonticon-fontawesome6](https://github.com/tlambert03/fonticon-fontawesome6).",
|
|
78
81
|
)
|
|
79
|
-
enablement:
|
|
82
|
+
enablement: str | None = Field(
|
|
80
83
|
None,
|
|
81
84
|
description=(
|
|
82
85
|
"Expression which must evaluate as true to enable the command in the UI "
|
|
@@ -85,14 +88,13 @@ class CommandContribution(BaseModel):
|
|
|
85
88
|
),
|
|
86
89
|
)
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
extra = Extra.forbid
|
|
91
|
+
model_config = ConfigDict(extra="forbid", validate_assignment=True)
|
|
90
92
|
|
|
91
93
|
def exec(
|
|
92
94
|
self,
|
|
93
95
|
args: tuple = (),
|
|
94
|
-
kwargs:
|
|
95
|
-
_registry:
|
|
96
|
+
kwargs: dict | None = None,
|
|
97
|
+
_registry: CommandRegistry | None = None,
|
|
96
98
|
) -> Any:
|
|
97
99
|
if kwargs is None:
|
|
98
100
|
kwargs = {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Annotated, Any, Literal
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pydantic import BaseModel, BeforeValidator, Field, conlist, model_validator
|
|
4
4
|
|
|
5
5
|
from ._json_schema import (
|
|
6
6
|
Draft07JsonSchema,
|
|
@@ -18,21 +18,23 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
18
18
|
https://json-schema.org/understanding-json-schema/reference/index.html
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
type:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
type: Annotated[JsonType | JsonTypeArray, BeforeValidator(_coerce_type_name)] = (
|
|
22
|
+
Field(
|
|
23
|
+
None,
|
|
24
|
+
description="The type of this variable. Either JSON Schema type names "
|
|
25
|
+
"('array', 'boolean', 'object', ...) or python type names ('list', 'bool', "
|
|
26
|
+
"'dict', ...) may be used, but they will be coerced to JSON Schema types. "
|
|
27
|
+
"Numbers, strings, and booleans will be editable in the UI, other types "
|
|
28
|
+
"(lists, dicts) *may* be editable in the UI depending on their content, "
|
|
29
|
+
"but maby will only be editable as text in the napari settings file. For "
|
|
30
|
+
"boolean entries, the description (or markdownDescription) will be used as"
|
|
31
|
+
" the label for the checkbox.",
|
|
32
|
+
)
|
|
30
33
|
)
|
|
31
|
-
_coerce_type_name = validator("type", pre=True, allow_reuse=True)(_coerce_type_name)
|
|
32
34
|
|
|
33
35
|
default: Any = Field(None, description="The default value for this property.")
|
|
34
36
|
|
|
35
|
-
description:
|
|
37
|
+
description: str | None = Field(
|
|
36
38
|
None,
|
|
37
39
|
description="Your `description` appears after the title and before the input "
|
|
38
40
|
"field, except for booleans, where the description is used as the label for "
|
|
@@ -45,12 +47,12 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
45
47
|
"plain text, set this value to `plain`.",
|
|
46
48
|
)
|
|
47
49
|
|
|
48
|
-
enum:
|
|
50
|
+
enum: conlist(Any, min_length=1) | None = Field( # type: ignore
|
|
49
51
|
None,
|
|
50
52
|
description="A list of valid options for this field. If you provide this field,"
|
|
51
53
|
"the settings UI will render a dropdown menu.",
|
|
52
54
|
)
|
|
53
|
-
enum_descriptions:
|
|
55
|
+
enum_descriptions: list[str] = Field(
|
|
54
56
|
default_factory=list,
|
|
55
57
|
description="If you provide a list of items under the `enum` field, you may "
|
|
56
58
|
"provide `enum_descriptions` to add descriptive text for each enum.",
|
|
@@ -62,7 +64,7 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
62
64
|
"plain text, set this value to `plain`.",
|
|
63
65
|
)
|
|
64
66
|
|
|
65
|
-
deprecation_message:
|
|
67
|
+
deprecation_message: str | None = Field(
|
|
66
68
|
None,
|
|
67
69
|
description="If you set deprecationMessage, the setting will get a warning "
|
|
68
70
|
"underline with your specified message. It won't show up in the settings "
|
|
@@ -80,7 +82,7 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
80
82
|
description="By default, string settings will be rendered with a single-line "
|
|
81
83
|
"editor. To render with a multi-line editor, set this value to `multiline`.",
|
|
82
84
|
)
|
|
83
|
-
order:
|
|
85
|
+
order: int | None = Field(
|
|
84
86
|
None,
|
|
85
87
|
description="When specified, gives the order of this setting relative to other "
|
|
86
88
|
"settings within the same category. Settings with an order property will be "
|
|
@@ -88,14 +90,14 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
88
90
|
" will be placed in alphabetical order.",
|
|
89
91
|
)
|
|
90
92
|
|
|
91
|
-
pattern_error_message:
|
|
93
|
+
pattern_error_message: str | None = Field(
|
|
92
94
|
None,
|
|
93
95
|
description="When restricting string types to a given regular expression with "
|
|
94
96
|
"the `pattern` field, this field may be used to provide a custom error when "
|
|
95
97
|
"the pattern does not match.",
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
@
|
|
100
|
+
@model_validator(mode="before")
|
|
99
101
|
def _validate_root(cls, values):
|
|
100
102
|
values = super()._validate_root(values)
|
|
101
103
|
|
|
@@ -135,7 +137,7 @@ class ConfigurationContribution(BaseModel):
|
|
|
135
137
|
'"Plugin", "Configuration", and "Settings" are redundant and should not be'
|
|
136
138
|
"used in your title.",
|
|
137
139
|
)
|
|
138
|
-
properties:
|
|
140
|
+
properties: dict[str, ConfigurationProperty] = Field(
|
|
139
141
|
...,
|
|
140
142
|
description="Configuration properties. In the settings UI, your configuration "
|
|
141
143
|
"key will be used to namespace and construct a title. Though a plugin can "
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from npe2._pydantic_compat import BaseModel, Field, validator
|
|
1
|
+
from pydantic import BaseModel, Field, field_validator
|
|
4
2
|
|
|
5
3
|
from ._commands import CommandContribution
|
|
6
4
|
from ._configuration import ConfigurationContribution
|
|
@@ -30,13 +28,13 @@ __all__ = [
|
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
class ContributionPoints(BaseModel):
|
|
33
|
-
commands:
|
|
34
|
-
readers:
|
|
35
|
-
writers:
|
|
36
|
-
widgets:
|
|
37
|
-
sample_data:
|
|
38
|
-
themes:
|
|
39
|
-
menus:
|
|
31
|
+
commands: list[CommandContribution] | None = None
|
|
32
|
+
readers: list[ReaderContribution] | None = None
|
|
33
|
+
writers: list[WriterContribution] | None = None
|
|
34
|
+
widgets: list[WidgetContribution] | None = None
|
|
35
|
+
sample_data: list[SampleDataContribution] | None = None
|
|
36
|
+
themes: list[ThemeContribution] | None = None
|
|
37
|
+
menus: dict[str, list[MenuItem]] = Field(
|
|
40
38
|
default_factory=dict,
|
|
41
39
|
description="Add menu items to existing napari menus."
|
|
42
40
|
"A menu item can be a command, such as open a widget, or a submenu."
|
|
@@ -44,10 +42,10 @@ class ContributionPoints(BaseModel):
|
|
|
44
42
|
"This allows you to organize your plugin's contributions within"
|
|
45
43
|
"napari's menu structure.",
|
|
46
44
|
)
|
|
47
|
-
submenus:
|
|
48
|
-
keybindings:
|
|
45
|
+
submenus: list[SubmenuContribution] | None = None
|
|
46
|
+
keybindings: list[KeyBindingContribution] | None = Field(None, hide_docs=True)
|
|
49
47
|
|
|
50
|
-
configuration:
|
|
48
|
+
configuration: list[ConfigurationContribution] = Field(
|
|
51
49
|
default_factory=list,
|
|
52
50
|
hide_docs=True,
|
|
53
51
|
description="Configuration options for this plugin."
|
|
@@ -58,6 +56,7 @@ class ContributionPoints(BaseModel):
|
|
|
58
56
|
"keys will be used for the submenu entry names.",
|
|
59
57
|
)
|
|
60
58
|
|
|
61
|
-
@
|
|
59
|
+
@field_validator("configuration", mode="before")
|
|
60
|
+
@classmethod
|
|
62
61
|
def _to_list(cls, v):
|
|
63
62
|
return v if isinstance(v, list) else [v]
|