valentina-python-client 3.2.0__tar.gz → 3.3.0__tar.gz

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.
Files changed (85) hide show
  1. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/PKG-INFO +3 -3
  2. valentina_python_client-3.3.0/pyproject.toml +242 -0
  3. valentina_python_client-3.2.0/pyproject.toml → valentina_python_client-3.3.0/pyproject.toml.orig +13 -12
  4. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/__init__.py +2 -2
  5. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/campaigns.py +7 -3
  6. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/character_traits.py +3 -2
  7. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/characters.py +7 -3
  8. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/__init__.py +2 -0
  9. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/campaigns.py +4 -1
  10. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/character_trait.py +1 -0
  11. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/characters.py +8 -1
  12. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/dictionary.py +5 -0
  13. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/shared.py +21 -1
  14. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/campaigns.py +9 -2
  15. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/character_traits.py +3 -2
  16. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/characters.py +9 -2
  17. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/LICENSE +0 -0
  18. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/README.md +0 -0
  19. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_codegen.py +0 -0
  20. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/__init__.py +0 -0
  21. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/client.py +0 -0
  22. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/registry.py +0 -0
  23. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/__init__.py +0 -0
  24. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/_audit_params.py +0 -0
  25. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/base.py +0 -0
  26. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
  27. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/campaign_books.py +0 -0
  28. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/character_autogen.py +0 -0
  29. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
  30. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/companies.py +0 -0
  31. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/developers.py +0 -0
  32. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/dicerolls.py +0 -0
  33. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/dictionary.py +0 -0
  34. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/global_admin.py +0 -0
  35. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/identity.py +0 -0
  36. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/options.py +0 -0
  37. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/system.py +0 -0
  38. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/user_lookup.py +0 -0
  39. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/services/users.py +0 -0
  40. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/testing/__init__.py +0 -0
  41. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/_sync/testing/_client.py +0 -0
  42. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/client.py +0 -0
  43. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/config.py +0 -0
  44. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/constants.py +0 -0
  45. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/endpoints.py +0 -0
  46. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/exceptions.py +0 -0
  47. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/audit_logs.py +0 -0
  48. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/books.py +0 -0
  49. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/chapters.py +0 -0
  50. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/character_autogen.py +0 -0
  51. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/character_blueprint.py +0 -0
  52. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/companies.py +0 -0
  53. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/developers.py +0 -0
  54. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/diceroll.py +0 -0
  55. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/full_sheet.py +0 -0
  56. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/global_admin.py +0 -0
  57. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/pagination.py +0 -0
  58. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/system.py +0 -0
  59. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/user_lookup.py +0 -0
  60. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/models/users.py +0 -0
  61. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/py.typed +0 -0
  62. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/registry.py +0 -0
  63. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/__init__.py +0 -0
  64. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/_audit_params.py +0 -0
  65. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/base.py +0 -0
  66. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/campaign_book_chapters.py +0 -0
  67. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/campaign_books.py +0 -0
  68. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/character_autogen.py +0 -0
  69. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/character_blueprint.py +0 -0
  70. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/companies.py +0 -0
  71. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/developers.py +0 -0
  72. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/dicerolls.py +0 -0
  73. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/dictionary.py +0 -0
  74. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/global_admin.py +0 -0
  75. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/identity.py +0 -0
  76. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/options.py +0 -0
  77. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/system.py +0 -0
  78. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/user_lookup.py +0 -0
  79. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/services/users.py +0 -0
  80. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/testing/__init__.py +0 -0
  81. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/testing/_client.py +0 -0
  82. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/testing/_factories.py +0 -0
  83. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/testing/_router.py +0 -0
  84. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/testing/_routes.py +0 -0
  85. {valentina_python_client-3.2.0 → valentina_python_client-3.3.0}/src/vclient/validate_constants.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valentina-python-client
3
- Version: 3.2.0
3
+ Version: 3.3.0
4
4
  Summary: Async Python client library for the Valentina Noir API
5
5
  Author: Nate Landau
6
6
  Author-email: Nate Landau <github@natenate.org>
@@ -15,8 +15,8 @@ License: MIT License
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Programming Language :: Python :: 3.14
18
- Requires-Dist: anyio>=4.14.1,<5.0.0
19
- Requires-Dist: httpx2>=2.5.0
18
+ Requires-Dist: anyio>=4.14.2,<5.0.0
19
+ Requires-Dist: httpx2>=2.9.1
20
20
  Requires-Dist: loguru>=0.7.3
21
21
  Requires-Dist: polyfactory>=3.3.0,<4.0.0
22
22
  Requires-Dist: pydantic[email]>=2.13.4,<3.0.0
@@ -0,0 +1,242 @@
1
+ [project]
2
+ classifiers = [
3
+ "Programming Language :: Python :: 3.13",
4
+ "Programming Language :: Python :: 3.14",
5
+ ]
6
+ dependencies = [
7
+ "anyio>=4.14.2,<5.0.0",
8
+ "httpx2>=2.9.1",
9
+ "loguru>=0.7.3",
10
+ "polyfactory>=3.3.0, <4.0.0",
11
+ "pydantic[email]>=2.13.4, <3.0.0",
12
+ ]
13
+ description = "Async Python client library for the Valentina Noir API"
14
+ name = "valentina-python-client"
15
+ readme = "README.md"
16
+ requires-python = ">=3.13"
17
+ version = "3.3.0"
18
+
19
+ [[project.authors]]
20
+ name = "Nate Landau"
21
+ email = "github@natenate.org"
22
+
23
+ [project.license]
24
+ file = "LICENSE"
25
+
26
+ [project.optional-dependencies]
27
+ testing = ["polyfactory>=3.3.0"]
28
+
29
+ [project.urls]
30
+ Homepage = "https://docs.valentina-noir.com/python-api-client/"
31
+ Repository = "https://github.com/natelandau/valentina-python-client"
32
+
33
+ [build-system]
34
+ build-backend = "uv_build"
35
+ requires = ["uv_build>=0.11.15,<0.13.0"]
36
+
37
+ [tool.uv.build-backend]
38
+ module-name = "vclient"
39
+
40
+ [tool.commitizen]
41
+ bump_message = "bump(release): v$current_version → v$new_version"
42
+ changelog_merge_prerelease = true
43
+ tag_format = "v$version"
44
+ update_changelog_on_bump = true
45
+ version_files = ["src/vclient/__init__.py:__version__"]
46
+ version_provider = "uv"
47
+
48
+ [tool.coverage.report]
49
+ exclude_lines = [
50
+ "def __repr__",
51
+ 'except [\w\s\._]+ as .*:',
52
+ "if TYPE_CHECKING",
53
+ "pragma: no cover",
54
+ "raise Abort",
55
+ "raise Exit",
56
+ ]
57
+ fail_under = 0
58
+ precision = 1
59
+ show_missing = true
60
+ skip_covered = true
61
+ skip_empty = true
62
+
63
+ [tool.coverage.run]
64
+ branch = true
65
+ command_line = "--module pytest"
66
+ data_file = ".cache/coverage"
67
+ omit = ["tests/*"]
68
+ source = ["src"]
69
+
70
+ [tool.coverage.xml]
71
+ output = ".cache/coverage.xml"
72
+
73
+ [tool.pytest.ini_options]
74
+ addopts = "--color=yes --doctest-modules --strict-config --strict-markers -n auto --dist loadfile"
75
+ cache_dir = ".cache/pytest"
76
+ filterwarnings = [
77
+ "error",
78
+ "ignore:.*Pydantic.*:UserWarning",
79
+ "ignore::DeprecationWarning:",
80
+ ]
81
+ markers = ["serial"]
82
+ testpaths = ["tests"]
83
+ xfail_strict = true
84
+
85
+ [tool.ruff]
86
+ exclude = [
87
+ ".cache",
88
+ ".dev",
89
+ ".git",
90
+ ".venv",
91
+ ".worktrees",
92
+ "_build",
93
+ "_site",
94
+ "build",
95
+ "dist",
96
+ ]
97
+ fix = true
98
+ line-length = 100
99
+ output-format = "grouped"
100
+ src = [
101
+ "src",
102
+ "tests",
103
+ ]
104
+ target-version = "py313"
105
+
106
+ [tool.ruff.lint]
107
+ ignore = [
108
+ "ANN002",
109
+ "ANN003",
110
+ "ANN204",
111
+ "ANN401",
112
+ "B006",
113
+ "B008",
114
+ "COM812",
115
+ "CPY001",
116
+ "D107",
117
+ "E501",
118
+ "PLC0415",
119
+ "E711",
120
+ "E712",
121
+ "FIX002",
122
+ "S311",
123
+ "TD001",
124
+ "TD002",
125
+ "TD003",
126
+ ]
127
+ select = ["ALL"]
128
+ unfixable = [
129
+ "ERA001",
130
+ "F401",
131
+ "F841",
132
+ ]
133
+
134
+ [tool.ruff.lint.per-file-ignores]
135
+ "src/vclient/_sync/**" = [
136
+ "C416",
137
+ "C901",
138
+ "D",
139
+ "E501",
140
+ "ERA001",
141
+ "PLR0913",
142
+ "PLR2004",
143
+ "PLW0603",
144
+ "SLF001",
145
+ "TRY300",
146
+ "UP028",
147
+ ]
148
+ "scripts/*.py" = [
149
+ "INP001",
150
+ "T201",
151
+ ]
152
+ "src/vclient/services/*.py" = ["PLR0913"]
153
+ "src/vclient/validate_constants.py" = ["T201"]
154
+ "tests/**/*.py" = [
155
+ "A002",
156
+ "A003",
157
+ "ANN001",
158
+ "ANN002",
159
+ "ANN003",
160
+ "ANN201",
161
+ "ARG001",
162
+ "ARG002",
163
+ "ARG005",
164
+ "D102",
165
+ "ERA001",
166
+ "F403",
167
+ "F405",
168
+ "FBT001",
169
+ "PGH003",
170
+ "PLC0415",
171
+ "PLR0913",
172
+ "PLR0917",
173
+ "PLR2004",
174
+ "PT011",
175
+ "S101",
176
+ "SLF001",
177
+ "W292",
178
+ ]
179
+
180
+ [tool.ruff.lint.mccabe]
181
+ max-complexity = 10
182
+
183
+ [tool.ruff.lint.isort]
184
+ combine-as-imports = true
185
+
186
+ [tool.ruff.lint.pydocstyle]
187
+ convention = "google"
188
+
189
+ [tool.ruff.lint.pylint]
190
+ max-args = 6
191
+
192
+ [tool.ruff.format]
193
+ exclude = ["src/vclient/_sync"]
194
+ indent-style = "space"
195
+ line-ending = "auto"
196
+ quote-style = "double"
197
+ skip-magic-trailing-comma = false
198
+
199
+ [tool.vulture]
200
+ exclude = [
201
+ "duties.py",
202
+ "scripts/",
203
+ "tests/",
204
+ "tmp/",
205
+ ]
206
+ ignore_decorators = []
207
+ ignore_names = []
208
+ make_whitelist = false
209
+ min_confidence = 100
210
+ paths = ["src/"]
211
+ sort_by_size = true
212
+ verbose = false
213
+
214
+ [tool.ty.terminal]
215
+ error-on-warning = true
216
+
217
+ [dependency-groups]
218
+ dev = [
219
+ "commitizen>=4.17.0",
220
+ "coverage>=7.15.3",
221
+ "duty>=1.9.0",
222
+ "httpx>=0.28.1",
223
+ "prek>=0.4.12",
224
+ "pytest-anyio>=0.0.0",
225
+ "pytest-clarity>=1.0.1",
226
+ "pytest-cov>=7.1.0",
227
+ "pytest-devtools>=1.3.0",
228
+ "pytest-httpx2>=1.0.0",
229
+ "pytest-mock>=3.15.1",
230
+ "pytest-repeat>=0.9.4",
231
+ "pytest-sugar>=1.1.1",
232
+ "pytest-xdist>=3.8.0",
233
+ "pytest>=9.1.1",
234
+ "respx>=0.23.1",
235
+ "ruff>=0.16.1",
236
+ "shellcheck-py>=0.11.0.1",
237
+ "ty>=0.0.65",
238
+ "typos>=1.48.0",
239
+ "vulture>=2.16",
240
+ "yamllint>=1.38.0",
241
+ ]
242
+ docs = ["zensical>=0.0.52"]
@@ -5,8 +5,8 @@
5
5
  "Programming Language :: Python :: 3.14",
6
6
  ]
7
7
  dependencies = [
8
- "anyio>=4.14.1,<5.0.0",
9
- "httpx2>=2.5.0",
8
+ "anyio>=4.14.2,<5.0.0",
9
+ "httpx2>=2.9.1",
10
10
  "loguru>=0.7.3",
11
11
  "polyfactory>=3.3.0, <4.0.0",
12
12
  "pydantic[email]>=2.13.4, <3.0.0",
@@ -16,7 +16,7 @@
16
16
  name = "valentina-python-client"
17
17
  readme = "README.md"
18
18
  requires-python = ">=3.13"
19
- version = "3.2.0"
19
+ version = "3.3.0"
20
20
 
21
21
  [project.optional-dependencies]
22
22
  testing = ["polyfactory>=3.3.0"]
@@ -27,22 +27,22 @@
27
27
 
28
28
  [build-system]
29
29
  build-backend = "uv_build"
30
- requires = ["uv_build>=0.11.15,<0.12.0"]
30
+ requires = ["uv_build>=0.11.15,<0.13.0"]
31
31
 
32
32
  [tool.uv.build-backend]
33
33
  module-name = "vclient"
34
34
 
35
35
  [dependency-groups]
36
36
  dev = [
37
- "commitizen>=4.16.4",
38
- "coverage>=7.15.0",
37
+ "commitizen>=4.17.0",
38
+ "coverage>=7.15.3",
39
39
  "duty>=1.9.0",
40
40
  "httpx>=0.28.1",
41
- "prek>=0.4.8",
41
+ "prek>=0.4.12",
42
42
  "pytest-anyio>=0.0.0",
43
43
  "pytest-clarity>=1.0.1",
44
44
  "pytest-cov>=7.1.0",
45
- "pytest-devtools>=1.2.0",
45
+ "pytest-devtools>=1.3.0",
46
46
  "pytest-httpx2>=1.0.0",
47
47
  "pytest-mock>=3.15.1",
48
48
  "pytest-repeat>=0.9.4",
@@ -50,14 +50,14 @@
50
50
  "pytest-xdist>=3.8.0",
51
51
  "pytest>=9.1.1",
52
52
  "respx>=0.23.1",
53
- "ruff>=0.15.20",
53
+ "ruff>=0.16.1",
54
54
  "shellcheck-py>=0.11.0.1",
55
- "ty>=0.0.56",
55
+ "ty>=0.0.65",
56
56
  "typos>=1.48.0",
57
57
  "vulture>=2.16",
58
58
  "yamllint>=1.38.0",
59
59
  ]
60
- docs = ["zensical>=0.0.47"]
60
+ docs = ["zensical>=0.0.52"]
61
61
 
62
62
  [tool.commitizen]
63
63
  bump_message = "bump(release): v$current_version → v$new_version"
@@ -168,7 +168,8 @@
168
168
  "FBT001", # Boolean-typed positional argument in function definition
169
169
  "PGH003", # Use specific rule codes when ignoring type issues
170
170
  "PLC0415", # Imports should be at top of file
171
- "PLR0913",
171
+ "PLR0913", # Too many positional arguments
172
+ "PLR0917", # Too many positional arguments
172
173
  "PLR2004",
173
174
  "PT011", # set the `match` parameter or use a more specific exception
174
175
  "S101",
@@ -31,7 +31,7 @@ _logger.add(
31
31
  # Forward TRACE too (e.g. the "Send request" line) and let the consumer's
32
32
  # stdlib logging level decide what to show; the sink must not pre-filter it.
33
33
  level="TRACE",
34
- filter=lambda record: record["name"].startswith("vclient"),
34
+ filter=lambda record: record["name"].startswith("vclient"), # ty: ignore[unresolved-attribute]
35
35
  )
36
36
 
37
37
  _logger.disable("vclient")
@@ -116,4 +116,4 @@ __all__ = (
116
116
  "users_service",
117
117
  )
118
118
 
119
- __version__ = "3.2.0"
119
+ __version__ = "3.3.0"
@@ -127,6 +127,7 @@ class SyncCampaignsService(SyncBaseService):
127
127
  request: A CampaignCreate model, OR pass fields as keyword arguments.
128
128
  **kwargs: Fields for CampaignCreate if request is not provided.
129
129
  Accepts: name (str, required), description (str | None),
130
+ in_game_date (datetime.date | None, ISO 8601 calendar date),
130
131
  desperation (int, default 0), danger (int, default 0).
131
132
 
132
133
  Returns:
@@ -154,7 +155,8 @@ class SyncCampaignsService(SyncBaseService):
154
155
  request: A CampaignUpdate model, OR pass fields as keyword arguments.
155
156
  **kwargs: Fields for CampaignUpdate if request is not provided.
156
157
  Accepts: name (str | None), description (str | None),
157
- desperation (int | None), danger (int | None).
158
+ in_game_date (datetime.date | None, ISO 8601 calendar date;
159
+ pass None to clear it), desperation (int | None), danger (int | None).
158
160
 
159
161
  Returns:
160
162
  The updated Campaign object.
@@ -166,9 +168,11 @@ class SyncCampaignsService(SyncBaseService):
166
168
  ValidationError: If the request data is invalid.
167
169
  """
168
170
  body = request if request is not None else self._validate_request(CampaignUpdate, **kwargs)
171
+ payload = body.model_dump(exclude_none=True, exclude_unset=True, mode="json")
172
+ if "in_game_date" in body.model_fields_set and body.in_game_date is None:
173
+ payload["in_game_date"] = None
169
174
  response = self._patch(
170
- self._format_endpoint(Endpoints.CAMPAIGN, campaign_id=campaign_id),
171
- json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
175
+ self._format_endpoint(Endpoints.CAMPAIGN, campaign_id=campaign_id), json=payload
172
176
  )
173
177
  return Campaign.model_validate(response.json())
174
178
 
@@ -222,8 +222,9 @@ class SyncCharacterTraitsService(SyncBaseService):
222
222
  **kwargs: Fields for TraitCreate if request is not provided.
223
223
  Required: name (str), category_id (str), currency (TraitModifyCurrency).
224
224
  Optional: description (str | None), max_value (int), min_value (int),
225
- show_when_zero (bool), initial_cost (int | None),
226
- upgrade_cost (int | None).
225
+ show_when_zero (bool), is_rollable (bool, default True),
226
+ initial_cost (int | None), upgrade_cost (int | None),
227
+ count_based_cost_multiplier (int | None).
227
228
  """
228
229
  body = request if request is not None else self._validate_request(TraitCreate, **kwargs)
229
230
  response = self._post(
@@ -241,6 +241,7 @@ class SyncCharactersService(SyncBaseService):
241
241
  Required: campaign_id (str), character_class (CharacterClass),
242
242
  game_version (GameVersion), name_first (str), name_last (str).
243
243
  Optional: type (CharacterType), name_nick (str), age (int),
244
+ date_of_birth (datetime.date | None, ISO 8601 calendar date),
244
245
  biography (str), demeanor (str), nature (str), concept_id (str),
245
246
  user_player_id (str), traits (list), vampire_attributes, etc.
246
247
 
@@ -274,7 +275,8 @@ class SyncCharactersService(SyncBaseService):
274
275
  request: A CharacterUpdate model, OR pass fields as keyword arguments.
275
276
  **kwargs: Fields for CharacterUpdate if request is not provided.
276
277
  All fields are optional: character_class, type, game_version, status,
277
- name_first, name_last, name_nick, age, biography, demeanor, nature,
278
+ name_first, name_last, name_nick, age, date_of_birth (datetime.date | None,
279
+ ISO 8601 calendar date; pass None to clear it), biography, demeanor, nature,
278
280
  concept_id, user_player_id, vampire_attributes, werewolf_attributes, etc.
279
281
 
280
282
  Returns:
@@ -287,9 +289,11 @@ class SyncCharactersService(SyncBaseService):
287
289
  ValidationError: If the request data is invalid.
288
290
  """
289
291
  body = request if request is not None else self._validate_request(CharacterUpdate, **kwargs)
292
+ payload = body.model_dump(exclude_none=True, exclude_unset=True, mode="json")
293
+ if "date_of_birth" in body.model_fields_set and body.date_of_birth is None:
294
+ payload["date_of_birth"] = None
290
295
  response = self._patch(
291
- self._format_endpoint(Endpoints.CHARACTER, character_id=character_id),
292
- json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
296
+ self._format_endpoint(Endpoints.CHARACTER, character_id=character_id), json=payload
293
297
  )
294
298
  return Character.model_validate(response.json())
295
299
 
@@ -122,6 +122,7 @@ from .shared import (
122
122
  ProviderAudiences,
123
123
  RollStatistics,
124
124
  Trait,
125
+ TraitPower,
125
126
  )
126
127
  from .system import SystemHealth
127
128
  from .user_lookup import (
@@ -242,6 +243,7 @@ __all__ = [
242
243
  "Trait",
243
244
  "TraitCategory",
244
245
  "TraitCreate",
246
+ "TraitPower",
245
247
  "TraitSubcategory",
246
248
  "User",
247
249
  "UserApproveDTO",
@@ -1,6 +1,6 @@
1
1
  """Pydantic models for Campaign API responses and requests."""
2
2
 
3
- from datetime import datetime
3
+ from datetime import date, datetime
4
4
  from typing import Annotated
5
5
 
6
6
  from pydantic import BaseModel, Field
@@ -22,6 +22,7 @@ class Campaign(BaseModel):
22
22
  date_modified: datetime
23
23
  name: str
24
24
  description: str | None = None
25
+ in_game_date: date | None = None
25
26
  asset_ids: list[str] = Field(default_factory=list)
26
27
  desperation: int = 0
27
28
  danger: int = 0
@@ -47,6 +48,7 @@ class CampaignCreate(BaseModel):
47
48
 
48
49
  name: str = Field(min_length=3, max_length=50)
49
50
  description: Annotated[str, Field(min_length=3)] | None = None
51
+ in_game_date: date | None = None
50
52
  desperation: int = Field(default=0, ge=0, le=5)
51
53
  danger: int = Field(default=0, ge=0, le=5)
52
54
 
@@ -59,6 +61,7 @@ class CampaignUpdate(BaseModel):
59
61
 
60
62
  name: Annotated[str, Field(min_length=3, max_length=50)] | None = None
61
63
  description: Annotated[str, Field(min_length=3)] | None = None
64
+ in_game_date: date | None = None
62
65
  desperation: Annotated[int, Field(ge=0, le=5)] | None = None
63
66
  danger: Annotated[int, Field(ge=0, le=5)] | None = None
64
67
 
@@ -47,6 +47,7 @@ class TraitCreate(BaseModel):
47
47
  max_value: Annotated[int, Field(ge=0, le=100, default=5)] = 5
48
48
  min_value: Annotated[int, Field(ge=0, le=100, default=0)] = 0
49
49
  show_when_zero: bool | None = True
50
+ is_rollable: bool = True
50
51
  initial_cost: int | None = None
51
52
  upgrade_cost: int | None = None
52
53
  count_based_cost_multiplier: int | None = None
@@ -1,6 +1,6 @@
1
1
  """Pydantic models for Character API responses and requests."""
2
2
 
3
- from datetime import datetime
3
+ from datetime import date, datetime
4
4
  from typing import Annotated
5
5
 
6
6
  from pydantic import BaseModel, Field
@@ -177,6 +177,9 @@ class Character(BaseModel):
177
177
 
178
178
  # Biography
179
179
  age: int | None = Field(default=None, description="Character's age.")
180
+ date_of_birth: date | None = Field(
181
+ default=None, description="Character's date of birth (ISO 8601 calendar date)."
182
+ )
180
183
  biography: Annotated[str, Field(min_length=3)] | None = Field(
181
184
  default=None, description="Character biography."
182
185
  )
@@ -280,6 +283,9 @@ class CharacterCreate(BaseModel):
280
283
  default=None, description="Character's nickname."
281
284
  )
282
285
  age: int | None = Field(default=None, description="Character's age.")
286
+ date_of_birth: date | None = Field(
287
+ default=None, description="Character's date of birth (ISO 8601 calendar date)."
288
+ )
283
289
  biography: Annotated[str, Field(min_length=3)] | None = Field(
284
290
  default=None, description="Character biography."
285
291
  )
@@ -333,6 +339,7 @@ class CharacterUpdate(BaseModel):
333
339
  )
334
340
 
335
341
  age: int | None = None
342
+ date_of_birth: date | None = None
336
343
  biography: Annotated[str, Field(min_length=3)] | None = Field(
337
344
  default=None, description="Character biography."
338
345
  )
@@ -4,6 +4,8 @@ from datetime import datetime
4
4
 
5
5
  from pydantic import BaseModel, Field
6
6
 
7
+ from .shared import TraitPower
8
+
7
9
 
8
10
  class DictionaryTerm(BaseModel):
9
11
  """Response model for a dictionary term."""
@@ -19,6 +21,9 @@ class DictionaryTerm(BaseModel):
19
21
  source_type: str | None = None
20
22
  source_id: str | None = None
21
23
 
24
+ # Populated only for terms with source_type "trait", resolved from that trait's powers.
25
+ powers: list[TraitPower] = Field(default_factory=list)
26
+
22
27
 
23
28
  class DictionaryTermCreate(BaseModel):
24
29
  """Request body for creating a dictionary term."""
@@ -146,6 +146,22 @@ class GiftAttributes(BaseModel):
146
146
  auspice_name: str | None = None
147
147
 
148
148
 
149
+ class TraitPower(BaseModel):
150
+ """A power a trait grants at a specific dot level.
151
+
152
+ Named powers (Disciplines and Thaumaturgy/Necromancy paths) carry a name and usually a
153
+ system. Nameless per-dot descriptors on Attributes and Skills describe what each dot
154
+ rating means and have `name` set to None.
155
+ """
156
+
157
+ id: str
158
+ level: int
159
+ name: str | None = None
160
+ description: str | None = None
161
+ system: str | None = None
162
+ link: str | None = None
163
+
164
+
149
165
  class Trait(BaseModel):
150
166
  """Response model for a trait.
151
167
 
@@ -161,7 +177,6 @@ class Trait(BaseModel):
161
177
  show_when_zero: bool = True
162
178
  max_value: int = 5
163
179
  min_value: int = 0
164
- is_custom: bool = False
165
180
  initial_cost: int = 1
166
181
  upgrade_cost: int = 2
167
182
  count_based_cost_multiplier: int | None = None
@@ -171,6 +186,7 @@ class Trait(BaseModel):
171
186
  sheet_section_id: str
172
187
  category_name: str | None = None
173
188
  category_id: str
189
+ # Non-null marks a custom trait owned by that character, which is deleted along with it.
174
190
  custom_for_character_id: str | None = None
175
191
  subcategory_id: str | None = None
176
192
  subcategory_name: str | None = None
@@ -179,6 +195,9 @@ class Trait(BaseModel):
179
195
  system: str | None = None
180
196
  gift_attributes: GiftAttributes | None = None
181
197
 
198
+ # Ordered by level ascending, then name. A single level may grant several powers.
199
+ powers: list[TraitPower] = Field(default_factory=list)
200
+
182
201
  character_classes: list[CharacterClass] = Field(default_factory=list)
183
202
  game_versions: list[GameVersion] = Field(default_factory=list)
184
203
 
@@ -202,4 +221,5 @@ __all__ = [
202
221
  "ProviderAudiences",
203
222
  "RollStatistics",
204
223
  "Trait",
224
+ "TraitPower",
205
225
  ]
@@ -152,6 +152,7 @@ class CampaignsService(BaseService):
152
152
  request: A CampaignCreate model, OR pass fields as keyword arguments.
153
153
  **kwargs: Fields for CampaignCreate if request is not provided.
154
154
  Accepts: name (str, required), description (str | None),
155
+ in_game_date (datetime.date | None, ISO 8601 calendar date),
155
156
  desperation (int, default 0), danger (int, default 0).
156
157
 
157
158
  Returns:
@@ -184,7 +185,8 @@ class CampaignsService(BaseService):
184
185
  request: A CampaignUpdate model, OR pass fields as keyword arguments.
185
186
  **kwargs: Fields for CampaignUpdate if request is not provided.
186
187
  Accepts: name (str | None), description (str | None),
187
- desperation (int | None), danger (int | None).
188
+ in_game_date (datetime.date | None, ISO 8601 calendar date;
189
+ pass None to clear it), desperation (int | None), danger (int | None).
188
190
 
189
191
  Returns:
190
192
  The updated Campaign object.
@@ -196,9 +198,14 @@ class CampaignsService(BaseService):
196
198
  ValidationError: If the request data is invalid.
197
199
  """
198
200
  body = request if request is not None else self._validate_request(CampaignUpdate, **kwargs)
201
+ payload = body.model_dump(exclude_none=True, exclude_unset=True, mode="json")
202
+ # The API distinguishes an omitted key (leave unchanged) from an explicit null (clear).
203
+ # exclude_none drops None, so re-add in_game_date when the caller set it to None to clear it.
204
+ if "in_game_date" in body.model_fields_set and body.in_game_date is None:
205
+ payload["in_game_date"] = None
199
206
  response = await self._patch(
200
207
  self._format_endpoint(Endpoints.CAMPAIGN, campaign_id=campaign_id),
201
- json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
208
+ json=payload,
202
209
  )
203
210
  return Campaign.model_validate(response.json())
204
211
 
@@ -254,8 +254,9 @@ class CharacterTraitsService(BaseService):
254
254
  **kwargs: Fields for TraitCreate if request is not provided.
255
255
  Required: name (str), category_id (str), currency (TraitModifyCurrency).
256
256
  Optional: description (str | None), max_value (int), min_value (int),
257
- show_when_zero (bool), initial_cost (int | None),
258
- upgrade_cost (int | None).
257
+ show_when_zero (bool), is_rollable (bool, default True),
258
+ initial_cost (int | None), upgrade_cost (int | None),
259
+ count_based_cost_multiplier (int | None).
259
260
  """
260
261
  body = request if request is not None else self._validate_request(TraitCreate, **kwargs)
261
262
  response = await self._post(
@@ -252,6 +252,7 @@ class CharactersService(BaseService):
252
252
  Required: campaign_id (str), character_class (CharacterClass),
253
253
  game_version (GameVersion), name_first (str), name_last (str).
254
254
  Optional: type (CharacterType), name_nick (str), age (int),
255
+ date_of_birth (datetime.date | None, ISO 8601 calendar date),
255
256
  biography (str), demeanor (str), nature (str), concept_id (str),
256
257
  user_player_id (str), traits (list), vampire_attributes, etc.
257
258
 
@@ -288,7 +289,8 @@ class CharactersService(BaseService):
288
289
  request: A CharacterUpdate model, OR pass fields as keyword arguments.
289
290
  **kwargs: Fields for CharacterUpdate if request is not provided.
290
291
  All fields are optional: character_class, type, game_version, status,
291
- name_first, name_last, name_nick, age, biography, demeanor, nature,
292
+ name_first, name_last, name_nick, age, date_of_birth (datetime.date | None,
293
+ ISO 8601 calendar date; pass None to clear it), biography, demeanor, nature,
292
294
  concept_id, user_player_id, vampire_attributes, werewolf_attributes, etc.
293
295
 
294
296
  Returns:
@@ -301,9 +303,14 @@ class CharactersService(BaseService):
301
303
  ValidationError: If the request data is invalid.
302
304
  """
303
305
  body = request if request is not None else self._validate_request(CharacterUpdate, **kwargs)
306
+ payload = body.model_dump(exclude_none=True, exclude_unset=True, mode="json")
307
+ # The API distinguishes an omitted key (leave unchanged) from an explicit null (clear).
308
+ # exclude_none drops None, so re-add date_of_birth when the caller set it to None to clear it.
309
+ if "date_of_birth" in body.model_fields_set and body.date_of_birth is None:
310
+ payload["date_of_birth"] = None
304
311
  response = await self._patch(
305
312
  self._format_endpoint(Endpoints.CHARACTER, character_id=character_id),
306
- json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
313
+ json=payload,
307
314
  )
308
315
  return Character.model_validate(response.json())
309
316