julee 0.1.3__py3-none-any.whl → 0.1.4__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.
@@ -19,17 +19,20 @@ Provides directives:
19
19
 
20
20
  import ast
21
21
  import os
22
- import re
23
22
  from pathlib import Path
23
+
24
24
  from docutils import nodes
25
25
  from docutils.parsers.rst import directives
26
- from sphinx.util.docutils import SphinxDirective
27
26
  from sphinx.util import logging
27
+ from sphinx.util.docutils import SphinxDirective
28
28
 
29
29
  from .config import get_config
30
30
  from .utils import (
31
- normalize_name, slugify, kebab_to_snake, path_to_root,
32
- parse_list_option, parse_integration_options
31
+ kebab_to_snake,
32
+ normalize_name,
33
+ parse_integration_options,
34
+ parse_list_option,
35
+ path_to_root,
33
36
  )
34
37
 
35
38
  logger = logging.getLogger(__name__)
@@ -40,14 +43,14 @@ _code_registry: dict = {}
40
43
 
41
44
  def get_accelerator_registry(env):
42
45
  """Get the accelerator registry from env, creating if needed."""
43
- if not hasattr(env, 'accelerator_registry'):
46
+ if not hasattr(env, "accelerator_registry"):
44
47
  env.accelerator_registry = {}
45
48
  return env.accelerator_registry
46
49
 
47
50
 
48
51
  def get_documented_accelerators(env):
49
52
  """Get the set of documented accelerators from env, creating if needed."""
50
- if not hasattr(env, 'documented_accelerators'):
53
+ if not hasattr(env, "documented_accelerators"):
51
54
  env.documented_accelerators = set()
52
55
  return env.documented_accelerators
53
56
 
@@ -69,16 +72,18 @@ def scan_python_classes(directory: Path) -> list[dict]:
69
72
  for node in ast.walk(tree):
70
73
  if isinstance(node, ast.ClassDef):
71
74
  docstring = ast.get_docstring(node) or ""
72
- first_line = docstring.split('\n')[0].strip() if docstring else ""
73
- classes.append({
74
- 'name': node.name,
75
- 'docstring': first_line,
76
- 'file': py_file.name,
77
- })
75
+ first_line = docstring.split("\n")[0].strip() if docstring else ""
76
+ classes.append(
77
+ {
78
+ "name": node.name,
79
+ "docstring": first_line,
80
+ "file": py_file.name,
81
+ }
82
+ )
78
83
  except Exception as e:
79
84
  logger.warning(f"Could not parse {py_file}: {e}")
80
85
 
81
- return sorted(classes, key=lambda c: c['name'])
86
+ return sorted(classes, key=lambda c: c["name"])
82
87
 
83
88
 
84
89
  def get_module_docstring(module_path: Path) -> tuple[str | None, str | None]:
@@ -91,7 +96,7 @@ def get_module_docstring(module_path: Path) -> tuple[str | None, str | None]:
91
96
  tree = ast.parse(f.read(), filename=str(module_path))
92
97
  docstring = ast.get_docstring(tree)
93
98
  if docstring:
94
- first_line = docstring.split('\n')[0].strip()
99
+ first_line = docstring.split("\n")[0].strip()
95
100
  return first_line, docstring
96
101
  except Exception as e:
97
102
  logger.warning(f"Could not parse {module_path}: {e}")
@@ -103,7 +108,7 @@ def scan_bounded_context(slug: str, project_root: Path) -> dict | None:
103
108
  """Introspect src/{slug}/ for ADR 001-compliant code structure."""
104
109
  snake_slug = kebab_to_snake(slug)
105
110
  config = get_config()
106
- src_dir = config.get_path('bounded_contexts')
111
+ src_dir = config.get_path("bounded_contexts")
107
112
  context_dir = src_dir / snake_slug
108
113
 
109
114
  if not context_dir.exists() and snake_slug != slug:
@@ -117,14 +122,16 @@ def scan_bounded_context(slug: str, project_root: Path) -> dict | None:
117
122
  objective, full_docstring = get_module_docstring(init_file)
118
123
 
119
124
  return {
120
- 'entities': scan_python_classes(context_dir / "domain" / "models"),
121
- 'use_cases': scan_python_classes(context_dir / "use_cases"),
122
- 'repository_protocols': scan_python_classes(context_dir / "domain" / "repositories"),
123
- 'service_protocols': scan_python_classes(context_dir / "domain" / "services"),
124
- 'has_infrastructure': (context_dir / "infrastructure").exists(),
125
- 'code_dir': context_dir.name,
126
- 'objective': objective,
127
- 'docstring': full_docstring,
125
+ "entities": scan_python_classes(context_dir / "domain" / "models"),
126
+ "use_cases": scan_python_classes(context_dir / "use_cases"),
127
+ "repository_protocols": scan_python_classes(
128
+ context_dir / "domain" / "repositories"
129
+ ),
130
+ "service_protocols": scan_python_classes(context_dir / "domain" / "services"),
131
+ "has_infrastructure": (context_dir / "infrastructure").exists(),
132
+ "code_dir": context_dir.name,
133
+ "objective": objective,
134
+ "docstring": full_docstring,
128
135
  }
129
136
 
130
137
 
@@ -134,7 +141,7 @@ def scan_code_structure(app):
134
141
  _code_registry = {}
135
142
 
136
143
  config = get_config()
137
- src_dir = config.get_path('bounded_contexts')
144
+ src_dir = config.get_path("bounded_contexts")
138
145
 
139
146
  if not src_dir.exists():
140
147
  logger.info("src/ directory not found - no code to introspect yet")
@@ -150,19 +157,22 @@ def scan_code_structure(app):
150
157
  code_info = scan_bounded_context(slug, config.project_root)
151
158
  if code_info:
152
159
  _code_registry[slug] = code_info
153
- logger.info(f"Introspected bounded context '{slug}': "
154
- f"{len(code_info['entities'])} entities, "
155
- f"{len(code_info['use_cases'])} use cases")
160
+ logger.info(
161
+ f"Introspected bounded context '{slug}': "
162
+ f"{len(code_info['entities'])} entities, "
163
+ f"{len(code_info['use_cases'])} use cases"
164
+ )
156
165
 
157
166
 
158
167
  def get_apps_for_accelerator(accelerator_slug: str) -> list[str]:
159
168
  """Get apps that expose this accelerator (from app manifests)."""
160
169
  from . import apps
170
+
161
171
  _app_registry = apps.get_app_registry()
162
172
 
163
173
  result = []
164
174
  for app_slug, app_data in _app_registry.items():
165
- if accelerator_slug in app_data.get('accelerators', []):
175
+ if accelerator_slug in app_data.get("accelerators", []):
166
176
  result.append(app_slug)
167
177
  return sorted(result)
168
178
 
@@ -170,13 +180,14 @@ def get_apps_for_accelerator(accelerator_slug: str) -> list[str]:
170
180
  def get_stories_for_accelerator(accelerator_slug: str) -> list[dict]:
171
181
  """Get stories for apps that use this accelerator."""
172
182
  from . import stories
183
+
173
184
  _story_registry = stories.get_story_registry()
174
185
 
175
186
  app_slugs = get_apps_for_accelerator(accelerator_slug)
176
187
  result = []
177
188
 
178
189
  for story in _story_registry:
179
- if story['app'] in app_slugs:
190
+ if story["app"] in app_slugs:
180
191
  result.append(story)
181
192
 
182
193
  return result
@@ -185,16 +196,17 @@ def get_stories_for_accelerator(accelerator_slug: str) -> list[dict]:
185
196
  def get_journeys_for_accelerator(accelerator_slug: str, env) -> list[str]:
186
197
  """Get journeys that include stories from this accelerator's apps."""
187
198
  from . import journeys
199
+
188
200
  journey_registry = journeys.get_journey_registry(env)
189
201
 
190
202
  story_list = get_stories_for_accelerator(accelerator_slug)
191
- story_titles = {normalize_name(s['feature']) for s in story_list}
203
+ story_titles = {normalize_name(s["feature"]) for s in story_list}
192
204
 
193
205
  result = []
194
206
  for slug, journey in journey_registry.items():
195
- for step in journey.get('steps', []):
196
- if step.get('type') == 'story':
197
- if normalize_name(step['ref']) in story_titles:
207
+ for step in journey.get("steps", []):
208
+ if step.get("type") == "story":
209
+ if normalize_name(step["ref"]) in story_titles:
198
210
  result.append(slug)
199
211
  break
200
212
 
@@ -220,13 +232,13 @@ class DefineAcceleratorDirective(SphinxDirective):
220
232
  required_arguments = 1
221
233
  has_content = True
222
234
  option_spec = {
223
- 'status': directives.unchanged,
224
- 'milestone': directives.unchanged,
225
- 'acceptance': directives.unchanged,
226
- 'sources_from': directives.unchanged,
227
- 'feeds_into': directives.unchanged,
228
- 'publishes_to': directives.unchanged,
229
- 'depends_on': directives.unchanged,
235
+ "status": directives.unchanged,
236
+ "milestone": directives.unchanged,
237
+ "acceptance": directives.unchanged,
238
+ "sources_from": directives.unchanged,
239
+ "feeds_into": directives.unchanged,
240
+ "publishes_to": directives.unchanged,
241
+ "depends_on": directives.unchanged,
230
242
  }
231
243
 
232
244
  def run(self):
@@ -234,31 +246,31 @@ class DefineAcceleratorDirective(SphinxDirective):
234
246
 
235
247
  get_documented_accelerators(self.env).add(slug)
236
248
 
237
- status = self.options.get('status', '').strip()
238
- milestone = self.options.get('milestone', '').strip()
239
- acceptance = self.options.get('acceptance', '').strip()
240
- sources_from = parse_integration_options(self.options.get('sources_from', ''))
241
- feeds_into = parse_list_option(self.options.get('feeds_into', ''))
242
- publishes_to = parse_integration_options(self.options.get('publishes_to', ''))
243
- depends_on = parse_list_option(self.options.get('depends_on', ''))
249
+ status = self.options.get("status", "").strip()
250
+ milestone = self.options.get("milestone", "").strip()
251
+ acceptance = self.options.get("acceptance", "").strip()
252
+ sources_from = parse_integration_options(self.options.get("sources_from", ""))
253
+ feeds_into = parse_list_option(self.options.get("feeds_into", ""))
254
+ publishes_to = parse_integration_options(self.options.get("publishes_to", ""))
255
+ depends_on = parse_list_option(self.options.get("depends_on", ""))
244
256
 
245
- objective = '\n'.join(self.content).strip()
257
+ objective = "\n".join(self.content).strip()
246
258
 
247
259
  get_accelerator_registry(self.env)[slug] = {
248
- 'slug': slug,
249
- 'status': status,
250
- 'milestone': milestone,
251
- 'acceptance': acceptance,
252
- 'objective': objective,
253
- 'sources_from': sources_from,
254
- 'feeds_into': feeds_into,
255
- 'publishes_to': publishes_to,
256
- 'depends_on': depends_on,
257
- 'docname': self.env.docname,
260
+ "slug": slug,
261
+ "status": status,
262
+ "milestone": milestone,
263
+ "acceptance": acceptance,
264
+ "objective": objective,
265
+ "sources_from": sources_from,
266
+ "feeds_into": feeds_into,
267
+ "publishes_to": publishes_to,
268
+ "depends_on": depends_on,
269
+ "docname": self.env.docname,
258
270
  }
259
271
 
260
272
  node = DefineAcceleratorPlaceholder()
261
- node['accelerator_slug'] = slug
273
+ node["accelerator_slug"] = slug
262
274
  return [node]
263
275
 
264
276
 
@@ -285,7 +297,7 @@ class AcceleratorStatusDirective(SphinxDirective):
285
297
 
286
298
  def run(self):
287
299
  node = AcceleratorStatusPlaceholder()
288
- node['accelerator_slug'] = self.arguments[0]
300
+ node["accelerator_slug"] = self.arguments[0]
289
301
  return [node]
290
302
 
291
303
 
@@ -300,7 +312,7 @@ class AcceleratorsForAppDirective(SphinxDirective):
300
312
 
301
313
  def run(self):
302
314
  node = AcceleratorsForAppPlaceholder()
303
- node['app_slug'] = self.arguments[0]
315
+ node["app_slug"] = self.arguments[0]
304
316
  return [node]
305
317
 
306
318
 
@@ -312,12 +324,12 @@ class DependentAcceleratorsDirective(SphinxDirective):
312
324
  """List accelerators that depend on or publish to an integration."""
313
325
 
314
326
  option_spec = {
315
- 'relationship': directives.unchanged_required,
327
+ "relationship": directives.unchanged_required,
316
328
  }
317
329
 
318
330
  def run(self):
319
- relationship = self.options.get('relationship', '').strip()
320
- if relationship not in ('sources_from', 'publishes_to'):
331
+ relationship = self.options.get("relationship", "").strip()
332
+ if relationship not in ("sources_from", "publishes_to"):
321
333
  error = self.state_machine.reporter.error(
322
334
  f"Invalid relationship '{relationship}'. "
323
335
  f"Must be 'sources_from' or 'publishes_to'.",
@@ -326,11 +338,11 @@ class DependentAcceleratorsDirective(SphinxDirective):
326
338
  return [error]
327
339
 
328
340
  docname = self.env.docname
329
- integration_slug = docname.split('/')[-1]
341
+ integration_slug = docname.split("/")[-1]
330
342
 
331
343
  node = DependentAcceleratorsPlaceholder()
332
- node['integration_slug'] = integration_slug
333
- node['relationship'] = relationship
344
+ node["integration_slug"] = integration_slug
345
+ node["relationship"] = relationship
334
346
  return [node]
335
347
 
336
348
 
@@ -345,7 +357,7 @@ class AcceleratorDependencyDiagramDirective(SphinxDirective):
345
357
 
346
358
  def run(self):
347
359
  node = AcceleratorDependencyDiagramPlaceholder()
348
- node['accelerator_slug'] = self.arguments[0]
360
+ node["accelerator_slug"] = self.arguments[0]
349
361
  return [node]
350
362
 
351
363
 
@@ -360,7 +372,7 @@ class SrcAcceleratorBacklinksDirective(SphinxDirective):
360
372
 
361
373
  def run(self):
362
374
  node = SrcAcceleratorBacklinksPlaceholder()
363
- node['accelerator_slug'] = self.arguments[0]
375
+ node["accelerator_slug"] = self.arguments[0]
364
376
  return [node]
365
377
 
366
378
 
@@ -375,7 +387,7 @@ class SrcAppBacklinksDirective(SphinxDirective):
375
387
 
376
388
  def run(self):
377
389
  node = SrcAppBacklinksPlaceholder()
378
- node['app_slug'] = self.arguments[0]
390
+ node["app_slug"] = self.arguments[0]
379
391
  return [node]
380
392
 
381
393
 
@@ -395,22 +407,22 @@ def build_accelerator_status(slug: str, env) -> list:
395
407
  accel = accelerator_registry[slug]
396
408
  result_nodes = []
397
409
 
398
- if accel['status'] or accel['milestone']:
410
+ if accel["status"] or accel["milestone"]:
399
411
  status_para = nodes.paragraph()
400
- if accel['status']:
412
+ if accel["status"]:
401
413
  status_para += nodes.strong(text="Status: ")
402
- status_para += nodes.Text(accel['status'].title())
403
- if accel['status'] and accel['milestone']:
414
+ status_para += nodes.Text(accel["status"].title())
415
+ if accel["status"] and accel["milestone"]:
404
416
  status_para += nodes.Text(" | ")
405
- if accel['milestone']:
417
+ if accel["milestone"]:
406
418
  status_para += nodes.strong(text="Milestone: ")
407
- status_para += nodes.Text(accel['milestone'])
419
+ status_para += nodes.Text(accel["milestone"])
408
420
  result_nodes.append(status_para)
409
421
 
410
- if accel['acceptance']:
422
+ if accel["acceptance"]:
411
423
  accept_para = nodes.paragraph()
412
424
  accept_para += nodes.strong(text="Acceptance: ")
413
- accept_para += nodes.Text(accel['acceptance'])
425
+ accept_para += nodes.Text(accel["acceptance"])
414
426
  result_nodes.append(accept_para)
415
427
 
416
428
  return result_nodes
@@ -437,10 +449,10 @@ def build_accelerator_content(slug: str, docname: str, env) -> list:
437
449
  code_info = _code_registry.get(slug) or _code_registry.get(snake_slug)
438
450
 
439
451
  objective = None
440
- if code_info and code_info.get('objective'):
441
- objective = code_info['objective']
442
- elif accel['objective']:
443
- objective = accel['objective']
452
+ if code_info and code_info.get("objective"):
453
+ objective = code_info["objective"]
454
+ elif accel["objective"]:
455
+ objective = accel["objective"]
444
456
 
445
457
  if objective:
446
458
  obj_para = nodes.paragraph()
@@ -450,11 +462,22 @@ def build_accelerator_content(slug: str, docname: str, env) -> list:
450
462
  seealso_items = []
451
463
 
452
464
  if code_info:
453
- code_dir = code_info.get('code_dir', snake_slug)
465
+ code_dir = code_info.get("code_dir", snake_slug)
454
466
  autodoc_path = f"{prefix}source/_autosummary/rba.{code_dir}.html"
455
- seealso_items.append(('Source', [(autodoc_path, f"rba.{code_dir}", True)]))
467
+ seealso_items.append(("Source", [(autodoc_path, f"rba.{code_dir}", True)]))
456
468
  else:
457
- seealso_items.append(('Source', [(None, f"No implementation yet — expecting code at src/{snake_slug}/", False)]))
469
+ seealso_items.append(
470
+ (
471
+ "Source",
472
+ [
473
+ (
474
+ None,
475
+ f"No implementation yet — expecting code at src/{snake_slug}/",
476
+ False,
477
+ )
478
+ ],
479
+ )
480
+ )
458
481
 
459
482
  apps = get_apps_for_accelerator(slug)
460
483
  if apps:
@@ -462,51 +485,71 @@ def build_accelerator_content(slug: str, docname: str, env) -> list:
462
485
  for app_slug in apps:
463
486
  app_path = f"{prefix}{config.get_doc_path('applications')}/{app_slug}.html"
464
487
  app_links.append((app_path, app_slug.replace("-", " ").title(), False))
465
- seealso_items.append(('Exposed By', app_links))
488
+ seealso_items.append(("Exposed By", app_links))
466
489
 
467
490
  journeys = get_journeys_for_accelerator(slug, env)
468
491
  if journeys:
469
492
  journey_links = []
470
493
  for journey_slug in journeys:
471
- journey_path = f"{prefix}{config.get_doc_path('journeys')}/{journey_slug}.html"
472
- journey_links.append((journey_path, journey_slug.replace("-", " ").title(), False))
473
- seealso_items.append(('Journeys', journey_links))
494
+ journey_path = (
495
+ f"{prefix}{config.get_doc_path('journeys')}/{journey_slug}.html"
496
+ )
497
+ journey_links.append(
498
+ (journey_path, journey_slug.replace("-", " ").title(), False)
499
+ )
500
+ seealso_items.append(("Journeys", journey_links))
474
501
 
475
- if accel['depends_on']:
502
+ if accel["depends_on"]:
476
503
  accel_links = []
477
- for dep_slug in accel['depends_on']:
504
+ for dep_slug in accel["depends_on"]:
478
505
  if dep_slug in accelerator_registry:
479
- accel_path = f"{prefix}{config.get_doc_path('accelerators')}/{dep_slug}.html"
480
- accel_links.append((accel_path, dep_slug.replace("-", " ").title(), False))
506
+ accel_path = (
507
+ f"{prefix}{config.get_doc_path('accelerators')}/{dep_slug}.html"
508
+ )
509
+ accel_links.append(
510
+ (accel_path, dep_slug.replace("-", " ").title(), False)
511
+ )
481
512
  else:
482
- accel_links.append((None, f"{dep_slug.replace('-', ' ').title()} [not found]", False))
483
- seealso_items.append(('Depends On', accel_links))
513
+ accel_links.append(
514
+ (None, f"{dep_slug.replace('-', ' ').title()} [not found]", False)
515
+ )
516
+ seealso_items.append(("Depends On", accel_links))
484
517
 
485
- if accel['feeds_into']:
518
+ if accel["feeds_into"]:
486
519
  accel_links = []
487
- for feed_slug in accel['feeds_into']:
520
+ for feed_slug in accel["feeds_into"]:
488
521
  if feed_slug in accelerator_registry:
489
- accel_path = f"{prefix}{config.get_doc_path('accelerators')}/{feed_slug}.html"
490
- accel_links.append((accel_path, feed_slug.replace("-", " ").title(), False))
522
+ accel_path = (
523
+ f"{prefix}{config.get_doc_path('accelerators')}/{feed_slug}.html"
524
+ )
525
+ accel_links.append(
526
+ (accel_path, feed_slug.replace("-", " ").title(), False)
527
+ )
491
528
  else:
492
- accel_links.append((None, f"{feed_slug.replace('-', ' ').title()} [not found]", False))
493
- seealso_items.append(('Feeds Into', accel_links))
529
+ accel_links.append(
530
+ (None, f"{feed_slug.replace('-', ' ').title()} [not found]", False)
531
+ )
532
+ seealso_items.append(("Feeds Into", accel_links))
494
533
 
495
- if accel['sources_from']:
534
+ if accel["sources_from"]:
496
535
  int_links = []
497
- for source in accel['sources_from']:
498
- int_path = f"{prefix}{config.get_doc_path('integrations')}/{source['slug']}.html"
499
- label = source['slug'].replace("-", " ").title()
536
+ for source in accel["sources_from"]:
537
+ int_path = (
538
+ f"{prefix}{config.get_doc_path('integrations')}/{source['slug']}.html"
539
+ )
540
+ label = source["slug"].replace("-", " ").title()
500
541
  int_links.append((int_path, label, False))
501
- seealso_items.append(('Sources From', int_links))
542
+ seealso_items.append(("Sources From", int_links))
502
543
 
503
- if accel['publishes_to']:
544
+ if accel["publishes_to"]:
504
545
  int_links = []
505
- for target in accel['publishes_to']:
506
- int_path = f"{prefix}{config.get_doc_path('integrations')}/{target['slug']}.html"
507
- label = target['slug'].replace("-", " ").title()
546
+ for target in accel["publishes_to"]:
547
+ int_path = (
548
+ f"{prefix}{config.get_doc_path('integrations')}/{target['slug']}.html"
549
+ )
550
+ label = target["slug"].replace("-", " ").title()
508
551
  int_links.append((int_path, label, False))
509
- seealso_items.append(('Publishes To', int_links))
552
+ seealso_items.append(("Publishes To", int_links))
510
553
 
511
554
  if seealso_items:
512
555
  seealso_node = seealso()
@@ -548,21 +591,21 @@ def build_accelerator_index(docname: str, env) -> list:
548
591
  para += nodes.emphasis(text="No accelerators defined")
549
592
  return [para]
550
593
 
551
- by_status = {'alpha': [], 'future': [], 'production': [], 'other': []}
594
+ by_status = {"alpha": [], "future": [], "production": [], "other": []}
552
595
  for slug, accel in accelerator_registry.items():
553
- status = accel.get('status', '').lower()
596
+ status = accel.get("status", "").lower()
554
597
  if status in by_status:
555
598
  by_status[status].append((slug, accel))
556
599
  else:
557
- by_status['other'].append((slug, accel))
600
+ by_status["other"].append((slug, accel))
558
601
 
559
602
  result_nodes = []
560
603
 
561
604
  status_sections = [
562
- ('alpha', 'Alpha Phase'),
563
- ('production', 'Production'),
564
- ('future', 'Future'),
565
- ('other', 'Other'),
605
+ ("alpha", "Alpha Phase"),
606
+ ("production", "Production"),
607
+ ("future", "Future"),
608
+ ("other", "Other"),
566
609
  ]
567
610
 
568
611
  for status_key, status_label in status_sections:
@@ -585,7 +628,7 @@ def build_accelerator_index(docname: str, env) -> list:
585
628
  ref += nodes.Text(slug.replace("-", " ").title())
586
629
  para += ref
587
630
 
588
- if accel.get('milestone'):
631
+ if accel.get("milestone"):
589
632
  para += nodes.Text(f" — {accel['milestone']}")
590
633
 
591
634
  if slug in _code_registry:
@@ -593,9 +636,9 @@ def build_accelerator_index(docname: str, env) -> list:
593
636
 
594
637
  item += para
595
638
 
596
- if accel.get('objective'):
639
+ if accel.get("objective"):
597
640
  obj_para = nodes.paragraph()
598
- obj_text = accel['objective']
641
+ obj_text = accel["objective"]
599
642
  if len(obj_text) > 100:
600
643
  obj_text = obj_text[:100] + "..."
601
644
  obj_para += nodes.Text(obj_text)
@@ -625,7 +668,7 @@ def build_accelerators_for_app(app_slug: str, docname: str, env) -> list:
625
668
  para += nodes.emphasis(text=f"App '{app_slug}' not found")
626
669
  return [para]
627
670
 
628
- accel_slugs = app_data.get('accelerators', [])
671
+ accel_slugs = app_data.get("accelerators", [])
629
672
  if not accel_slugs:
630
673
  para = nodes.paragraph()
631
674
  para += nodes.emphasis(text="No accelerators")
@@ -643,7 +686,7 @@ def build_accelerators_for_app(app_slug: str, docname: str, env) -> list:
643
686
  para += ref
644
687
 
645
688
  if slug in accelerator_registry:
646
- objective = accelerator_registry[slug].get('objective', '')
689
+ objective = accelerator_registry[slug].get("objective", "")
647
690
  if objective:
648
691
  para += nodes.Text(f" — {objective[:60]}...")
649
692
 
@@ -653,7 +696,9 @@ def build_accelerators_for_app(app_slug: str, docname: str, env) -> list:
653
696
  return [bullet_list]
654
697
 
655
698
 
656
- def build_dependent_accelerators(integration_slug: str, relationship: str, docname: str, env) -> list:
699
+ def build_dependent_accelerators(
700
+ integration_slug: str, relationship: str, docname: str, env
701
+ ) -> list:
657
702
  """Build table of accelerators that depend on or publish to an integration."""
658
703
  config = get_config()
659
704
  accelerator_registry = get_accelerator_registry(env)
@@ -664,11 +709,13 @@ def build_dependent_accelerators(integration_slug: str, relationship: str, docna
664
709
  for accel_slug, accel in accelerator_registry.items():
665
710
  rel_list = accel.get(relationship, [])
666
711
  for rel in rel_list:
667
- if rel['slug'] == integration_slug:
668
- matches.append({
669
- 'slug': accel_slug,
670
- 'description': rel.get('description'),
671
- })
712
+ if rel["slug"] == integration_slug:
713
+ matches.append(
714
+ {
715
+ "slug": accel_slug,
716
+ "description": rel.get("description"),
717
+ }
718
+ )
672
719
  break
673
720
 
674
721
  if not matches:
@@ -693,7 +740,7 @@ def build_dependent_accelerators(integration_slug: str, relationship: str, docna
693
740
  header_row += accel_header
694
741
 
695
742
  data_header = nodes.entry()
696
- if relationship == 'sources_from':
743
+ if relationship == "sources_from":
697
744
  data_header += nodes.paragraph(text="What it sources")
698
745
  else:
699
746
  data_header += nodes.paragraph(text="What it publishes")
@@ -702,23 +749,25 @@ def build_dependent_accelerators(integration_slug: str, relationship: str, docna
702
749
  tbody = nodes.tbody()
703
750
  tgroup += tbody
704
751
 
705
- for match in sorted(matches, key=lambda m: m['slug']):
752
+ for match in sorted(matches, key=lambda m: m["slug"]):
706
753
  row = nodes.row()
707
754
  tbody += row
708
755
 
709
756
  accel_cell = nodes.entry()
710
757
  accel_para = nodes.paragraph()
711
- accel_path = f"{prefix}{config.get_doc_path('accelerators')}/{match['slug']}.html"
758
+ accel_path = (
759
+ f"{prefix}{config.get_doc_path('accelerators')}/{match['slug']}.html"
760
+ )
712
761
  ref = nodes.reference("", "", refuri=accel_path)
713
- ref += nodes.strong(text=match['slug'].replace("-", " ").title())
762
+ ref += nodes.strong(text=match["slug"].replace("-", " ").title())
714
763
  accel_para += ref
715
764
  accel_cell += accel_para
716
765
  row += accel_cell
717
766
 
718
767
  desc_cell = nodes.entry()
719
768
  desc_para = nodes.paragraph()
720
- if match['description']:
721
- desc_para += nodes.Text(match['description'])
769
+ if match["description"]:
770
+ desc_para += nodes.Text(match["description"])
722
771
  else:
723
772
  desc_para += nodes.emphasis(text="(not specified)")
724
773
  desc_cell += desc_para
@@ -740,8 +789,8 @@ def build_accelerator_dependency_diagram(slug: str, docname: str, env) -> list:
740
789
 
741
790
  accel = accelerator_registry[slug]
742
791
  apps = get_apps_for_accelerator(slug)
743
- sources_from = accel.get('sources_from', [])
744
- publishes_to = accel.get('publishes_to', [])
792
+ sources_from = accel.get("sources_from", [])
793
+ publishes_to = accel.get("publishes_to", [])
745
794
 
746
795
  lines = [
747
796
  "@startuml",
@@ -779,17 +828,19 @@ def build_accelerator_dependency_diagram(slug: str, docname: str, env) -> list:
779
828
  lines.append("' Integration dependencies")
780
829
 
781
830
  for source in sources_from:
782
- source_slug = source['slug']
831
+ source_slug = source["slug"]
783
832
  source_id = safe_id(source_slug)
784
833
  source_name = source_slug.replace("-", " ").title()
785
834
  lines.append(f'component "{source_name}" as {source_id} <<integration>>')
786
835
 
787
836
  for target in publishes_to:
788
- target_slug = target['slug']
837
+ target_slug = target["slug"]
789
838
  target_id = safe_id(target_slug)
790
- if not any(s['slug'] == target_slug for s in sources_from):
839
+ if not any(s["slug"] == target_slug for s in sources_from):
791
840
  target_name = target_slug.replace("-", " ").title()
792
- lines.append(f'component "{target_name}" as {target_id} <<integration>>')
841
+ lines.append(
842
+ f'component "{target_name}" as {target_id} <<integration>>'
843
+ )
793
844
 
794
845
  lines.append("")
795
846
 
@@ -800,20 +851,20 @@ def build_accelerator_dependency_diagram(slug: str, docname: str, env) -> list:
800
851
  lines.append(f"{app_id} --> {accel_id} : exposes")
801
852
 
802
853
  for source in sources_from:
803
- source_id = safe_id(source['slug'])
854
+ source_id = safe_id(source["slug"])
804
855
  label = "sources from"
805
- if source.get('description'):
806
- desc = source['description']
856
+ if source.get("description"):
857
+ desc = source["description"]
807
858
  if len(desc) > 30:
808
859
  desc = desc[:27] + "..."
809
860
  label = desc
810
861
  lines.append(f'{accel_id} --> {source_id} : "{label}"')
811
862
 
812
863
  for target in publishes_to:
813
- target_id = safe_id(target['slug'])
864
+ target_id = safe_id(target["slug"])
814
865
  label = "publishes to"
815
- if target.get('description'):
816
- desc = target['description']
866
+ if target.get("description"):
867
+ desc = target["description"]
817
868
  if len(desc) > 30:
818
869
  desc = desc[:27] + "..."
819
870
  label = desc
@@ -825,9 +876,9 @@ def build_accelerator_dependency_diagram(slug: str, docname: str, env) -> list:
825
876
  puml_source = "\n".join(lines)
826
877
 
827
878
  node = plantuml(puml_source)
828
- node['uml'] = puml_source
829
- node['incdir'] = os.path.dirname(docname)
830
- node['filename'] = os.path.basename(docname) + ".rst"
879
+ node["uml"] = puml_source
880
+ node["incdir"] = os.path.dirname(docname)
881
+ node["filename"] = os.path.basename(docname) + ".rst"
831
882
 
832
883
  return [node]
833
884
 
@@ -835,6 +886,7 @@ def build_accelerator_dependency_diagram(slug: str, docname: str, env) -> list:
835
886
  def build_accelerator_backlinks(slug: str, docname: str, env) -> nodes.Element:
836
887
  """Build seealso node with backlinks for an accelerator."""
837
888
  from sphinx.addnodes import seealso
889
+
838
890
  from . import apps
839
891
 
840
892
  config = get_config()
@@ -849,26 +901,40 @@ def build_accelerator_backlinks(slug: str, docname: str, env) -> nodes.Element:
849
901
 
850
902
  accel_path = f"{prefix}{config.get_doc_path('accelerators')}/{slug}.html"
851
903
  accel_data = accelerator_registry.get(slug, {})
852
- accel_desc = accel_data.get('objective', '').strip()
904
+ accel_desc = accel_data.get("objective", "").strip()
853
905
  if not accel_desc:
854
906
  accel_desc = f"Business accelerator for {slug.replace('-', ' ')} capabilities"
855
907
  if len(accel_desc) > 120:
856
908
  accel_desc = accel_desc[:117] + "..."
857
- items.append((accel_path, f"{slug.replace('-', ' ').title()} Accelerator", accel_desc))
909
+ items.append(
910
+ (accel_path, f"{slug.replace('-', ' ').title()} Accelerator", accel_desc)
911
+ )
858
912
 
859
913
  app_list = get_apps_for_accelerator(slug)
860
914
  for app_slug in app_list:
861
915
  app_path = f"{prefix}{config.get_doc_path('applications')}/{app_slug}.html"
862
916
  app_data = _app_registry.get(app_slug, {})
863
- app_desc = app_data.get('description', 'Application documentation')
917
+ app_desc = app_data.get("description", "Application documentation")
864
918
  if len(app_desc) > 120:
865
919
  app_desc = app_desc[:117] + "..."
866
- items.append((app_path, app_data.get('name', app_slug.replace("-", " ").title()), app_desc))
920
+ items.append(
921
+ (
922
+ app_path,
923
+ app_data.get("name", app_slug.replace("-", " ").title()),
924
+ app_desc,
925
+ )
926
+ )
867
927
 
868
928
  if app_list:
869
929
  for app_slug in app_list[:2]:
870
930
  story_path = f"{prefix}{config.get_doc_path('stories')}/{app_slug}.html"
871
- items.append((story_path, f"{app_slug.replace('-', ' ').title()} Stories", "User stories"))
931
+ items.append(
932
+ (
933
+ story_path,
934
+ f"{app_slug.replace('-', ' ').title()} Stories",
935
+ "User stories",
936
+ )
937
+ )
872
938
 
873
939
  def_list = nodes.definition_list()
874
940
  for path, title, description in items:
@@ -895,7 +961,8 @@ def build_accelerator_backlinks(slug: str, docname: str, env) -> nodes.Element:
895
961
  def build_app_backlinks(app_slug: str, docname: str, env) -> nodes.Element:
896
962
  """Build seealso node with backlinks for an app."""
897
963
  from sphinx.addnodes import seealso
898
- from . import apps, stories, journeys
964
+
965
+ from . import apps, journeys, stories
899
966
 
900
967
  config = get_config()
901
968
  accelerator_registry = get_accelerator_registry(env)
@@ -912,38 +979,62 @@ def build_app_backlinks(app_slug: str, docname: str, env) -> nodes.Element:
912
979
 
913
980
  if app_data:
914
981
  app_path = f"{prefix}{config.get_doc_path('applications')}/{app_slug}.html"
915
- app_desc = app_data.get('description', 'Application documentation')
982
+ app_desc = app_data.get("description", "Application documentation")
916
983
  if len(app_desc) > 120:
917
984
  app_desc = app_desc[:117] + "..."
918
- items.append((app_path, app_data.get('name', app_slug.replace("-", " ").title()), app_desc))
985
+ items.append(
986
+ (
987
+ app_path,
988
+ app_data.get("name", app_slug.replace("-", " ").title()),
989
+ app_desc,
990
+ )
991
+ )
919
992
 
920
- accelerators = app_data.get('accelerators', [])
993
+ accelerators = app_data.get("accelerators", [])
921
994
  for accel_slug in accelerators[:4]:
922
- accel_path = f"{prefix}{config.get_doc_path('accelerators')}/{accel_slug}.html"
995
+ accel_path = (
996
+ f"{prefix}{config.get_doc_path('accelerators')}/{accel_slug}.html"
997
+ )
923
998
  accel_data = accelerator_registry.get(accel_slug, {})
924
- accel_desc = accel_data.get('objective', '').strip()
999
+ accel_desc = accel_data.get("objective", "").strip()
925
1000
  if not accel_desc:
926
1001
  accel_desc = f"Business accelerator for {accel_slug.replace('-', ' ')} capabilities"
927
1002
  if len(accel_desc) > 120:
928
1003
  accel_desc = accel_desc[:117] + "..."
929
- items.append((accel_path, f"{accel_slug.replace('-', ' ').title()} Accelerator", accel_desc))
1004
+ items.append(
1005
+ (
1006
+ accel_path,
1007
+ f"{accel_slug.replace('-', ' ').title()} Accelerator",
1008
+ accel_desc,
1009
+ )
1010
+ )
930
1011
 
931
1012
  app_normalized = normalize_name(app_slug)
932
1013
  if app_normalized in {normalize_name(a) for a in _apps_with_stories}:
933
1014
  story_path = f"{prefix}{config.get_doc_path('stories')}/{app_slug}.html"
934
- items.append((story_path, f"{app_slug.replace('-', ' ').title()} Stories", "User stories"))
1015
+ items.append(
1016
+ (
1017
+ story_path,
1018
+ f"{app_slug.replace('-', ' ').title()} Stories",
1019
+ "User stories",
1020
+ )
1021
+ )
935
1022
 
936
1023
  def get_journeys_for_app_slug(slug):
937
1024
  journey_registry = journeys.get_journey_registry(env)
938
1025
  _story_registry = stories.get_story_registry()
939
- story_list = [s for s in _story_registry if normalize_name(s['app']) == normalize_name(slug)]
940
- story_titles = {normalize_name(s['feature']) for s in story_list}
1026
+ story_list = [
1027
+ s
1028
+ for s in _story_registry
1029
+ if normalize_name(s["app"]) == normalize_name(slug)
1030
+ ]
1031
+ story_titles = {normalize_name(s["feature"]) for s in story_list}
941
1032
 
942
1033
  result = []
943
1034
  for j_slug, journey in journey_registry.items():
944
- for step in journey.get('steps', []):
945
- if step.get('type') == 'story':
946
- if normalize_name(step['ref']) in story_titles:
1035
+ for step in journey.get("steps", []):
1036
+ if step.get("type") == "story":
1037
+ if normalize_name(step["ref"]) in story_titles:
947
1038
  result.append(j_slug)
948
1039
  break
949
1040
  return sorted(set(result))
@@ -951,7 +1042,9 @@ def build_app_backlinks(app_slug: str, docname: str, env) -> nodes.Element:
951
1042
  journey_list = get_journeys_for_app_slug(app_slug)
952
1043
  for journey_slug in journey_list[:3]:
953
1044
  journey_path = f"{prefix}{config.get_doc_path('journeys')}/{journey_slug}.html"
954
- items.append((journey_path, f"{journey_slug.replace('-', ' ').title()}", "User journey"))
1045
+ items.append(
1046
+ (journey_path, f"{journey_slug.replace('-', ' ').title()}", "User journey")
1047
+ )
955
1048
 
956
1049
  def_list = nodes.definition_list()
957
1050
  for path, title, description in items:
@@ -984,7 +1077,7 @@ def validate_accelerators(app, env):
984
1077
 
985
1078
  referenced_accelerators = set()
986
1079
  for app_data in _app_registry.values():
987
- for accel in app_data.get('accelerators', []):
1080
+ for accel in app_data.get("accelerators", []):
988
1081
  referenced_accelerators.add(accel)
989
1082
 
990
1083
  for accel in referenced_accelerators:
@@ -1008,12 +1101,12 @@ def process_accelerator_placeholders(app, doctree, docname):
1008
1101
  env = app.env
1009
1102
 
1010
1103
  for node in doctree.traverse(DefineAcceleratorPlaceholder):
1011
- slug = node['accelerator_slug']
1104
+ slug = node["accelerator_slug"]
1012
1105
  content = build_accelerator_content(slug, docname, env)
1013
1106
  node.replace_self(content)
1014
1107
 
1015
1108
  for node in doctree.traverse(AcceleratorStatusPlaceholder):
1016
- slug = node['accelerator_slug']
1109
+ slug = node["accelerator_slug"]
1017
1110
  content = build_accelerator_status(slug, env)
1018
1111
  node.replace_self(content)
1019
1112
 
@@ -1022,28 +1115,30 @@ def process_accelerator_placeholders(app, doctree, docname):
1022
1115
  node.replace_self(content)
1023
1116
 
1024
1117
  for node in doctree.traverse(AcceleratorsForAppPlaceholder):
1025
- app_slug = node['app_slug']
1118
+ app_slug = node["app_slug"]
1026
1119
  content = build_accelerators_for_app(app_slug, docname, env)
1027
1120
  node.replace_self(content)
1028
1121
 
1029
1122
  for node in doctree.traverse(DependentAcceleratorsPlaceholder):
1030
- integration_slug = node['integration_slug']
1031
- relationship = node['relationship']
1032
- content = build_dependent_accelerators(integration_slug, relationship, docname, env)
1123
+ integration_slug = node["integration_slug"]
1124
+ relationship = node["relationship"]
1125
+ content = build_dependent_accelerators(
1126
+ integration_slug, relationship, docname, env
1127
+ )
1033
1128
  node.replace_self(content)
1034
1129
 
1035
1130
  for node in doctree.traverse(AcceleratorDependencyDiagramPlaceholder):
1036
- slug = node['accelerator_slug']
1131
+ slug = node["accelerator_slug"]
1037
1132
  content = build_accelerator_dependency_diagram(slug, docname, env)
1038
1133
  node.replace_self(content)
1039
1134
 
1040
1135
  for node in doctree.traverse(SrcAcceleratorBacklinksPlaceholder):
1041
- slug = node['accelerator_slug']
1136
+ slug = node["accelerator_slug"]
1042
1137
  content = build_accelerator_backlinks(slug, docname, env)
1043
1138
  node.replace_self([content])
1044
1139
 
1045
1140
  for node in doctree.traverse(SrcAppBacklinksPlaceholder):
1046
- app_slug = node['app_slug']
1141
+ app_slug = node["app_slug"]
1047
1142
  content = build_app_backlinks(app_slug, docname, env)
1048
1143
  node.replace_self([content])
1049
1144
 
@@ -1058,7 +1153,9 @@ def setup(app):
1058
1153
  app.add_directive("accelerator-status", AcceleratorStatusDirective)
1059
1154
  app.add_directive("accelerators-for-app", AcceleratorsForAppDirective)
1060
1155
  app.add_directive("dependent-accelerators", DependentAcceleratorsDirective)
1061
- app.add_directive("accelerator-dependency-diagram", AcceleratorDependencyDiagramDirective)
1156
+ app.add_directive(
1157
+ "accelerator-dependency-diagram", AcceleratorDependencyDiagramDirective
1158
+ )
1062
1159
  app.add_directive("src-accelerator-backlinks", SrcAcceleratorBacklinksDirective)
1063
1160
  app.add_directive("src-app-backlinks", SrcAppBacklinksDirective)
1064
1161