pyado 0.3.2__tar.gz → 0.3.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyado
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Typed, Pydantic-backed wrappers and Pythonic convenience methods for the Azure DevOps REST API
5
5
  Keywords: azure,devops,ado,api,pydantic,azure-devops
6
6
  Author: Fred Stober
@@ -44,7 +44,7 @@ license = "MIT"
44
44
  name = "pyado"
45
45
  readme = "README.md"
46
46
  requires-python = ">=3.11"
47
- version = "0.3.2"
47
+ version = "0.3.3"
48
48
 
49
49
  [project.urls]
50
50
  "Bug Tracker" = "https://github.com/fredstober/pyado/issues"
@@ -142,6 +142,8 @@ __all__ = [
142
142
  "WorkItemRef",
143
143
  "WorkItemRelation",
144
144
  "WorkItemRelationType",
145
+ "WorkItemState",
146
+ "WorkItemType",
145
147
  "WorkItemsBatchRequest",
146
148
  "abandon_pr",
147
149
  "add_file",
@@ -198,13 +200,13 @@ __all__ = [
198
200
  "get_work_item",
199
201
  "get_work_item_api_call",
200
202
  "get_work_item_tags",
203
+ "iter_active_prs",
201
204
  "iter_approvals",
202
205
  "iter_build_artifacts",
203
206
  "iter_build_tags",
204
207
  "iter_build_work_item_ids",
205
208
  "iter_builds",
206
209
  "iter_commit_diff",
207
- "iter_open_prs",
208
210
  "iter_pending_approvals",
209
211
  "iter_pipeline_definitions",
210
212
  "iter_pipeline_runs",
@@ -348,13 +350,13 @@ from pyado.high import (
348
350
  get_work_item_tags as get_work_item_tags,
349
351
  )
350
352
  from pyado.high import (
351
- iter_build_work_item_ids as iter_build_work_item_ids,
353
+ iter_active_prs as iter_active_prs,
352
354
  )
353
355
  from pyado.high import (
354
- iter_commit_diff as iter_commit_diff,
356
+ iter_build_work_item_ids as iter_build_work_item_ids,
355
357
  )
356
358
  from pyado.high import (
357
- iter_open_prs as iter_open_prs,
359
+ iter_commit_diff as iter_commit_diff,
358
360
  )
359
361
  from pyado.high import (
360
362
  iter_pending_approvals as iter_pending_approvals,
@@ -821,6 +823,12 @@ from pyado.raw import (
821
823
  from pyado.raw import (
822
824
  WorkItemsBatchRequest as WorkItemsBatchRequest,
823
825
  )
826
+ from pyado.raw import (
827
+ WorkItemState as WorkItemState,
828
+ )
829
+ from pyado.raw import (
830
+ WorkItemType as WorkItemType,
831
+ )
824
832
  from pyado.raw import (
825
833
  add_team_iteration as add_team_iteration,
826
834
  )
@@ -55,9 +55,9 @@ __all__ = [
55
55
  "get_last_commit_touching_file",
56
56
  "get_pr_labels",
57
57
  "get_work_item_tags",
58
+ "iter_active_prs",
58
59
  "iter_build_work_item_ids",
59
60
  "iter_commit_diff",
60
- "iter_open_prs",
61
61
  "iter_pending_approvals",
62
62
  "iter_pr_work_item_ids",
63
63
  "iter_work_item_details",
@@ -163,7 +163,7 @@ from pyado.high.pull_request import (
163
163
  get_pr_labels as get_pr_labels,
164
164
  )
165
165
  from pyado.high.pull_request import (
166
- iter_open_prs as iter_open_prs,
166
+ iter_active_prs as iter_active_prs,
167
167
  )
168
168
  from pyado.high.pull_request import (
169
169
  iter_pr_work_item_ids as iter_pr_work_item_ids,
@@ -53,7 +53,7 @@ __all__ = [
53
53
  "create_pr",
54
54
  "create_pr_thread",
55
55
  "get_pr_labels",
56
- "iter_open_prs",
56
+ "iter_active_prs",
57
57
  "iter_pr_work_item_ids",
58
58
  "link_pr_work_item",
59
59
  "reply_to_pr_thread",
@@ -62,7 +62,7 @@ __all__ = [
62
62
  ]
63
63
 
64
64
 
65
- def iter_open_prs(
65
+ def iter_active_prs(
66
66
  project_api_call: ApiCall,
67
67
  *,
68
68
  expand: str | None = None,
@@ -120,15 +120,15 @@ class WorkItemLink:
120
120
 
121
121
  Artifact links (builds, commits, pull requests) use the ArtifactLink
122
122
  relation type with a ``vstfs://`` URL constructed from the supplied IDs.
123
- Work item links (parent, child, related, etc.) require the target work
124
- item's ``url`` field (available as ``WorkItemInfo.url``).
123
+ Work item links (parent, child, related, etc.) accept the target work
124
+ item's ID and project-level API call; the URL is constructed internally.
125
125
 
126
126
  Examples::
127
127
 
128
128
  # At creation time
129
129
  create_work_item(api, fields, relations=[
130
130
  WorkItemLink.build(build_id),
131
- WorkItemLink.parent(str(parent_wi.url)),
131
+ WorkItemLink.parent(project_api, parent_wi_id),
132
132
  ])
133
133
 
134
134
  # On an existing work item
@@ -156,9 +156,13 @@ class WorkItemLink:
156
156
  @staticmethod
157
157
  def _wi_link(
158
158
  rel_type: WorkItemRelationType,
159
- work_item_url: str,
159
+ project_api_call: ApiCall,
160
+ work_item_id: WorkItemId,
160
161
  comment: str | None,
161
162
  ) -> WorkItemRelation:
163
+ work_item_url = get_work_item_api_call(
164
+ project_api_call, work_item_id
165
+ ).url.unicode_string()
162
166
  attributes: dict[str, Any] | None = (
163
167
  {"comment": comment} if comment is not None else None
164
168
  )
@@ -237,108 +241,165 @@ class WorkItemLink:
237
241
  # --- Work item links ---
238
242
 
239
243
  @staticmethod
240
- def related(work_item_url: str, *, comment: str | None = None) -> WorkItemRelation:
244
+ def related(
245
+ project_api_call: ApiCall,
246
+ work_item_id: WorkItemId,
247
+ *,
248
+ comment: str | None = None,
249
+ ) -> WorkItemRelation:
241
250
  """Return a Related link to another work item."""
242
251
  return WorkItemLink._wi_link(
243
- WorkItemRelationType.RELATED, work_item_url, comment
252
+ WorkItemRelationType.RELATED, project_api_call, work_item_id, comment
244
253
  )
245
254
 
246
255
  @staticmethod
247
- def parent(work_item_url: str, *, comment: str | None = None) -> WorkItemRelation:
256
+ def parent(
257
+ project_api_call: ApiCall,
258
+ work_item_id: WorkItemId,
259
+ *,
260
+ comment: str | None = None,
261
+ ) -> WorkItemRelation:
248
262
  """Return a Parent (Hierarchy-Reverse) link to another work item."""
249
263
  return WorkItemLink._wi_link(
250
- WorkItemRelationType.PARENT, work_item_url, comment
264
+ WorkItemRelationType.PARENT, project_api_call, work_item_id, comment
251
265
  )
252
266
 
253
267
  @staticmethod
254
- def child(work_item_url: str, *, comment: str | None = None) -> WorkItemRelation:
268
+ def child(
269
+ project_api_call: ApiCall,
270
+ work_item_id: WorkItemId,
271
+ *,
272
+ comment: str | None = None,
273
+ ) -> WorkItemRelation:
255
274
  """Return a Child (Hierarchy-Forward) link to another work item."""
256
- return WorkItemLink._wi_link(WorkItemRelationType.CHILD, work_item_url, comment)
275
+ return WorkItemLink._wi_link(
276
+ WorkItemRelationType.CHILD, project_api_call, work_item_id, comment
277
+ )
257
278
 
258
279
  @staticmethod
259
280
  def duplicate(
260
- work_item_url: str, *, comment: str | None = None
281
+ project_api_call: ApiCall,
282
+ work_item_id: WorkItemId,
283
+ *,
284
+ comment: str | None = None,
261
285
  ) -> WorkItemRelation:
262
286
  """Return a Duplicate-Forward link to another work item."""
263
287
  return WorkItemLink._wi_link(
264
- WorkItemRelationType.DUPLICATE, work_item_url, comment
288
+ WorkItemRelationType.DUPLICATE, project_api_call, work_item_id, comment
265
289
  )
266
290
 
267
291
  @staticmethod
268
292
  def duplicate_of(
269
- work_item_url: str, *, comment: str | None = None
293
+ project_api_call: ApiCall,
294
+ work_item_id: WorkItemId,
295
+ *,
296
+ comment: str | None = None,
270
297
  ) -> WorkItemRelation:
271
298
  """Return a Duplicate-Reverse link to another work item."""
272
299
  return WorkItemLink._wi_link(
273
- WorkItemRelationType.DUPLICATE_OF, work_item_url, comment
300
+ WorkItemRelationType.DUPLICATE_OF,
301
+ project_api_call,
302
+ work_item_id,
303
+ comment,
274
304
  )
275
305
 
276
306
  @staticmethod
277
307
  def successor(
278
- work_item_url: str, *, comment: str | None = None
308
+ project_api_call: ApiCall,
309
+ work_item_id: WorkItemId,
310
+ *,
311
+ comment: str | None = None,
279
312
  ) -> WorkItemRelation:
280
313
  """Return a Dependency-Forward (successor) link to another work item."""
281
314
  return WorkItemLink._wi_link(
282
- WorkItemRelationType.SUCCESSOR, work_item_url, comment
315
+ WorkItemRelationType.SUCCESSOR, project_api_call, work_item_id, comment
283
316
  )
284
317
 
285
318
  @staticmethod
286
319
  def predecessor(
287
- work_item_url: str, *, comment: str | None = None
320
+ project_api_call: ApiCall,
321
+ work_item_id: WorkItemId,
322
+ *,
323
+ comment: str | None = None,
288
324
  ) -> WorkItemRelation:
289
325
  """Return a Dependency-Reverse (predecessor) link to another work item."""
290
326
  return WorkItemLink._wi_link(
291
- WorkItemRelationType.PREDECESSOR, work_item_url, comment
327
+ WorkItemRelationType.PREDECESSOR, project_api_call, work_item_id, comment
292
328
  )
293
329
 
294
330
  @staticmethod
295
331
  def tested_by(
296
- work_item_url: str, *, comment: str | None = None
332
+ project_api_call: ApiCall,
333
+ work_item_id: WorkItemId,
334
+ *,
335
+ comment: str | None = None,
297
336
  ) -> WorkItemRelation:
298
337
  """Return a TestedBy-Forward link to another work item."""
299
338
  return WorkItemLink._wi_link(
300
- WorkItemRelationType.TESTED_BY, work_item_url, comment
339
+ WorkItemRelationType.TESTED_BY, project_api_call, work_item_id, comment
301
340
  )
302
341
 
303
342
  @staticmethod
304
- def tests(work_item_url: str, *, comment: str | None = None) -> WorkItemRelation:
343
+ def tests(
344
+ project_api_call: ApiCall,
345
+ work_item_id: WorkItemId,
346
+ *,
347
+ comment: str | None = None,
348
+ ) -> WorkItemRelation:
305
349
  """Return a TestedBy-Reverse (tests) link to another work item."""
306
- return WorkItemLink._wi_link(WorkItemRelationType.TESTS, work_item_url, comment)
350
+ return WorkItemLink._wi_link(
351
+ WorkItemRelationType.TESTS, project_api_call, work_item_id, comment
352
+ )
307
353
 
308
354
  @staticmethod
309
355
  def test_case(
310
- work_item_url: str, *, comment: str | None = None
356
+ project_api_call: ApiCall,
357
+ work_item_id: WorkItemId,
358
+ *,
359
+ comment: str | None = None,
311
360
  ) -> WorkItemRelation:
312
- """Return a SharedParameterReferencedBy-Forward link."""
361
+ """Return a TestCase-Forward link to another work item."""
313
362
  return WorkItemLink._wi_link(
314
- WorkItemRelationType.TEST_CASE, work_item_url, comment
363
+ WorkItemRelationType.TEST_CASE, project_api_call, work_item_id, comment
315
364
  )
316
365
 
317
366
  @staticmethod
318
367
  def shared_parameter_referenced_by(
319
- work_item_url: str, *, comment: str | None = None
368
+ project_api_call: ApiCall,
369
+ work_item_id: WorkItemId,
370
+ *,
371
+ comment: str | None = None,
320
372
  ) -> WorkItemRelation:
321
373
  """Return a SharedParameterReferencedBy-Reverse link."""
322
374
  return WorkItemLink._wi_link(
323
375
  WorkItemRelationType.SHARED_PARAMETER_REFERENCED_BY,
324
- work_item_url,
376
+ project_api_call,
377
+ work_item_id,
325
378
  comment,
326
379
  )
327
380
 
328
381
  @staticmethod
329
- def affects(work_item_url: str, *, comment: str | None = None) -> WorkItemRelation:
382
+ def affects(
383
+ project_api_call: ApiCall,
384
+ work_item_id: WorkItemId,
385
+ *,
386
+ comment: str | None = None,
387
+ ) -> WorkItemRelation:
330
388
  """Return an Affects-Forward link to another work item."""
331
389
  return WorkItemLink._wi_link(
332
- WorkItemRelationType.AFFECTS, work_item_url, comment
390
+ WorkItemRelationType.AFFECTS, project_api_call, work_item_id, comment
333
391
  )
334
392
 
335
393
  @staticmethod
336
394
  def affected_by(
337
- work_item_url: str, *, comment: str | None = None
395
+ project_api_call: ApiCall,
396
+ work_item_id: WorkItemId,
397
+ *,
398
+ comment: str | None = None,
338
399
  ) -> WorkItemRelation:
339
400
  """Return an Affects-Reverse (affected by) link to another work item."""
340
401
  return WorkItemLink._wi_link(
341
- WorkItemRelationType.AFFECTED_BY, work_item_url, comment
402
+ WorkItemRelationType.AFFECTED_BY, project_api_call, work_item_id, comment
342
403
  )
343
404
 
344
405
  # --- Other links ---
@@ -139,6 +139,8 @@ __all__ = [
139
139
  "WorkItemRef",
140
140
  "WorkItemRelation",
141
141
  "WorkItemRelationType",
142
+ "WorkItemState",
143
+ "WorkItemType",
142
144
  "WorkItemsBatchRequest",
143
145
  "add_team_iteration",
144
146
  "create_classification_node",
@@ -836,6 +838,12 @@ from pyado.raw.work_item import (
836
838
  from pyado.raw.work_item import (
837
839
  WorkItemsBatchRequest as WorkItemsBatchRequest,
838
840
  )
841
+ from pyado.raw.work_item import (
842
+ WorkItemState as WorkItemState,
843
+ )
844
+ from pyado.raw.work_item import (
845
+ WorkItemType as WorkItemType,
846
+ )
839
847
  from pyado.raw.work_item import (
840
848
  add_team_iteration as add_team_iteration,
841
849
  )
@@ -33,6 +33,8 @@ __all__ = [
33
33
  "WorkItemRef",
34
34
  "WorkItemRelation",
35
35
  "WorkItemRelationType",
36
+ "WorkItemState",
37
+ "WorkItemType",
36
38
  "WorkItemsBatchRequest",
37
39
  "add_team_iteration",
38
40
  "create_classification_node",
@@ -93,6 +95,9 @@ class WorkItemFieldName(StrEnum):
93
95
  HYPERLINK_COUNT = "System.HyperLinkCount"
94
96
  EXTERNAL_LINK_COUNT = "System.ExternalLinkCount"
95
97
  RELATED_LINK_COUNT = "System.RelatedLinkCount"
98
+ # RemoteLinkCount tracks links to external work items (GitHub, etc.);
99
+ # available on Azure DevOps Services only.
100
+ REMOTE_LINK_COUNT = "System.RemoteLinkCount"
96
101
  TAGS = "System.Tags"
97
102
  BOARD_COLUMN = "System.BoardColumn"
98
103
  BOARD_COLUMN_DONE = "System.BoardColumnDone"
@@ -105,28 +110,137 @@ class WorkItemFieldName(StrEnum):
105
110
  BUSINESS_VALUE = "Microsoft.VSTS.Common.BusinessValue"
106
111
  TIME_CRITICALITY = "Microsoft.VSTS.Common.TimeCriticality"
107
112
  RISK = "Microsoft.VSTS.Common.Risk"
108
- EFFORT = "Microsoft.VSTS.Common.Effort"
109
113
  ACTIVITY = "Microsoft.VSTS.Common.Activity"
114
+ # Discipline is the CMMI equivalent of Activity (Agile/Scrum).
115
+ DISCIPLINE = "Microsoft.VSTS.Common.Discipline"
110
116
  STACK_RANK = "Microsoft.VSTS.Common.StackRank"
111
117
  BACKLOG_PRIORITY = "Microsoft.VSTS.Common.BacklogPriority"
112
118
  CLOSED_DATE = "Microsoft.VSTS.Common.ClosedDate"
113
119
  CLOSED_BY = "Microsoft.VSTS.Common.ClosedBy"
120
+ ACTIVATED_BY = "Microsoft.VSTS.Common.ActivatedBy"
121
+ ACTIVATED_DATE = "Microsoft.VSTS.Common.ActivatedDate"
114
122
  RESOLVED_DATE = "Microsoft.VSTS.Common.ResolvedDate"
115
123
  RESOLVED_BY = "Microsoft.VSTS.Common.ResolvedBy"
116
124
  RESOLVED_REASON = "Microsoft.VSTS.Common.ResolvedReason"
117
125
  ACCEPTANCE_CRITERIA = "Microsoft.VSTS.Common.AcceptanceCriteria"
118
126
  STATE_CHANGE_DATE = "Microsoft.VSTS.Common.StateChangeDate"
127
+ # Triage is CMMI-only (Pending / More Info / Info Received / Triaged).
128
+ TRIAGE = "Microsoft.VSTS.Common.Triage"
129
+ # Resolution describes how a Scrum Impediment was resolved.
130
+ RESOLUTION = "Microsoft.VSTS.Common.Resolution"
119
131
  # --- Microsoft.VSTS.Scheduling fields ---
120
132
  REMAINING_WORK = "Microsoft.VSTS.Scheduling.RemainingWork"
121
133
  COMPLETED_WORK = "Microsoft.VSTS.Scheduling.CompletedWork"
122
134
  ORIGINAL_ESTIMATE = "Microsoft.VSTS.Scheduling.OriginalEstimate"
135
+ # Effort, StoryPoints, and Size are process-specific names for the same
136
+ # planning concept (all map to "Effort" in ProcessConfiguration).
137
+ # Scrum uses EFFORT; Agile uses STORY_POINTS; CMMI uses SIZE.
138
+ EFFORT = "Microsoft.VSTS.Scheduling.Effort"
123
139
  STORY_POINTS = "Microsoft.VSTS.Scheduling.StoryPoints"
140
+ SIZE = "Microsoft.VSTS.Scheduling.Size"
124
141
  START_DATE = "Microsoft.VSTS.Scheduling.StartDate"
125
142
  FINISH_DATE = "Microsoft.VSTS.Scheduling.FinishDate"
126
143
  TARGET_DATE = "Microsoft.VSTS.Scheduling.TargetDate"
127
144
  # --- Microsoft.VSTS.Build fields ---
128
145
  INTEGRATION_BUILD = "Microsoft.VSTS.Build.IntegrationBuild"
129
146
  FOUND_IN = "Microsoft.VSTS.Build.FoundIn"
147
+ # --- Microsoft.VSTS.TCM fields ---
148
+ REPRO_STEPS = "Microsoft.VSTS.TCM.ReproSteps"
149
+ # SystemInfo captures the OS/browser environment recorded at repro time
150
+ # (Bug work items, all process templates).
151
+ SYSTEM_INFO = "Microsoft.VSTS.TCM.SystemInfo"
152
+ # AutomationStatus tracks whether a test case is automated
153
+ # (Automated / Not Automated / Planned).
154
+ AUTOMATION_STATUS = "Microsoft.VSTS.TCM.AutomationStatus"
155
+ # --- Microsoft.VSTS.CMMI fields ---
156
+ # Note: despite the CMMI namespace, BLOCKED is also used by Scrum Tasks
157
+ # (same reference name across both process templates).
158
+ BLOCKED = "Microsoft.VSTS.CMMI.Blocked"
159
+ # Committed and Escalate are CMMI-only.
160
+ COMMITTED = "Microsoft.VSTS.CMMI.Committed"
161
+ ESCALATE = "Microsoft.VSTS.CMMI.Escalate"
162
+
163
+
164
+ class WorkItemState(StrEnum):
165
+ """Well-known work item state values across the four built-in ADO processes.
166
+
167
+ States are process-template-specific and can be customised per project.
168
+ This enum covers all states shipped with the Agile, Scrum, CMMI, and Basic
169
+ templates. For projects with custom states, pass the state name as a plain
170
+ string — the field accepts any ``str`` value regardless.
171
+
172
+ To define a project-specific state set that reuses standard values alongside
173
+ custom ones, declare your own ``StrEnum`` and assign members from this
174
+ class::
175
+
176
+ from enum import StrEnum
177
+ from pyado import WorkItemState
178
+
179
+ class AcmeState(StrEnum):
180
+ NEW = WorkItemState.NEW # "New"
181
+ ACTIVE = WorkItemState.ACTIVE # "Active"
182
+ IN_SPRINT = "In Sprint" # custom
183
+ CLOSED = WorkItemState.CLOSED # "Closed"
184
+ """
185
+
186
+ # --- Agile (Epic, Feature, User Story, Bug, Task, Issue) ---
187
+ NEW = "New" # Agile, Scrum
188
+ ACTIVE = "Active" # Agile, CMMI
189
+ RESOLVED = "Resolved" # Agile, CMMI
190
+ CLOSED = "Closed" # Agile, CMMI
191
+ REMOVED = "Removed" # Agile, Scrum
192
+
193
+ # --- Scrum (Product Backlog Item, Bug) ---
194
+ APPROVED = "Approved"
195
+ COMMITTED = "Committed"
196
+
197
+ # --- Scrum / Basic (Task, Epic, Feature) ---
198
+ TO_DO = "To Do"
199
+ IN_PROGRESS = "In Progress"
200
+ DONE = "Done"
201
+
202
+ # --- Basic only ---
203
+ DOING = "Doing"
204
+
205
+ # --- CMMI (Requirement, Change Request, Bug, Task, Issue, Risk, Review) ---
206
+ PROPOSED = "Proposed"
207
+
208
+
209
+ class WorkItemType(StrEnum):
210
+ """Common ADO work item type names for use with ``System.WorkItemType``.
211
+
212
+ These are the standard types shipped with the default Azure DevOps process
213
+ templates (Agile, Scrum, CMMI). Custom process templates may define
214
+ additional types not listed here; pass the type name as a plain string in
215
+ those cases.
216
+
217
+ Example::
218
+
219
+ create_work_item(api, {
220
+ WorkItemFieldName.WORK_ITEM_TYPE: WorkItemType.BUG,
221
+ WorkItemFieldName.TITLE: "Something is broken",
222
+ })
223
+ """
224
+
225
+ # --- Agile / Scrum / CMMI shared ---
226
+ EPIC = "Epic"
227
+ FEATURE = "Feature"
228
+ BUG = "Bug"
229
+ TASK = "Task"
230
+ TEST_CASE = "Test Case"
231
+ TEST_PLAN = "Test Plan"
232
+ TEST_SUITE = "Test Suite"
233
+ # --- Agile ---
234
+ USER_STORY = "User Story"
235
+ ISSUE = "Issue"
236
+ # --- Scrum ---
237
+ PRODUCT_BACKLOG_ITEM = "Product Backlog Item"
238
+ IMPEDIMENT = "Impediment"
239
+ # --- CMMI ---
240
+ REQUIREMENT = "Requirement"
241
+ CHANGE_REQUEST = "Change Request"
242
+ REVIEW = "Review"
243
+ RISK = "Risk"
130
244
 
131
245
 
132
246
  class WorkItemRelationType(StrEnum):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes