1"""
2Syside Automator ReqIF import/export.
3
4This module converts between ReqIF (Requirements Interchange Format) files
5and SysMLv2 workspaces. It can be driven from the command line or from
6Python scripts; both surfaces share the same underlying entry points.
7
8Installation
9------------
10
11The public functions and CLI both require the ``[reqif]`` extra which pulls in
12additional third-party dependencies. Install it with:
13
14.. code-block:: shell
15
16 pip install 'syside[reqif]'
17 # or, for uv-managed projects:
18 uv add 'syside[reqif]'
19
20When the extra is missing, the captured ``ImportError`` is exposed as
21``_missing_extra_error`` for callers building their own CLI.
22
23Command-line usage
24------------------
25
26The CLI is reachable both through the top-level ``syside`` console script
27and as a runnable module. The three are equivalent:
28
29.. code-block:: shell
30
31 syside reqif <subcommand> [options]
32 python -m syside reqif <subcommand> [options]
33 python -m syside.reqif <subcommand> [options]
34
35All subcommands operate on the current working directory as the workspace
36root. The available subcommands are:
37
38- ``init`` — write the canonical ``SysideReqIF`` SysMLv2 library into the
39 workspace so subsequent imports have something to reference. Use
40 ``--lib-dir`` to override the default :data:`LIBRARY_DIR` location.
41- ``import <file>`` — convert a ``.reqif`` or ``.reqifz`` file into
42 SysMLv2 packages under the workspace, performing a fresh build or a
43 delta update depending on existing content. Attachments are extracted
44 into ``--attachments-dir`` (default: :data:`ATTACHMENTS_DIR`).
45- ``lock`` — assign stable UUIDs and last-change timestamps to any
46 ReqIF-tagged elements that don't yet have them, so subsequent
47 round-trips preserve identity. Takes no options.
48- ``export`` — convert the SysMLv2 models from the workspace into a
49 ``.reqif`` or ``.reqifz`` file.
50- ``check reqif <file>`` — validate that a ReqIF file satisfies the
51 invariants required for round-trippable conversion (relation-group
52 ownership, no hierarchy cycles, no name shadowing, etc.).
53- ``check sysml`` — validate the workspace SysMLv2 model (currently
54 enforces at most one top-level package per ``.sysml`` document, and
55 that all ReqIF tags are locked).
56
57Each subcommand exits ``0`` on success and raises on failure.
58
59Library usage
60-------------
61
62Every CLI subcommand has a public Python counterpart: an options
63dataclass plus a function named after the subcommand. The functions
64return the CLI exit code and raise ``ValueError`` on validation
65failures, and operate on the current working directory (use
66``os.chdir`` to target a different workspace).
67
68.. code-block:: python
69
70 from pathlib import Path
71
72 from syside.reqif import (
73 ImportOptions,
74 CheckReqifOptions,
75 InitOptions,
76 check_reqif,
77 import_,
78 init,
79 )
80
81 # Initialise the workspace once.
82 init(InitOptions(lib_dir=None))
83
84 # Validate a ReqIF file before importing it.
85 reqif_file = Path("requirements.reqif")
86 check_reqif(CheckReqifOptions(file=reqif_file))
87
88 # Import the file; attachments land in ./attachments_reqif/.
89 import_(ImportOptions(file=reqif_file, attachments_dir=None))
90
91The default-path constants (:data:`LIBRARY_PKG`, :data:`LIBRARY_DIR`,
92:data:`ATTACHMENTS_DIR`) are re-exported here so callers can reference
93the same defaults the CLI uses without reaching into private modules.
94"""
95
96__unstable__ = True
97
98_missing_extra_error: BaseException | None
99try:
100 # Import the chain that transitively loads the [reqif]-extra deps
101 # (reqif, markdown-it-py, markdownify) first, so that a missing extra
102 # bails before any partial bindings can land in this namespace.
103 from syside.reqif._cmd_check import run_reqif as check_reqif
104 from syside.reqif._cmd_check import run_sysml as check_sysml
105 from syside.reqif._cmd_import import run as import_
106 from syside.reqif._cmd_init import run as init
107 from syside.reqif._cmd_lock import run as lock
108 from syside.reqif._cmd_export import run as export
109 from syside.reqif._cli import (
110 CheckReqifOptions,
111 CheckSysmlOptions,
112 ImportOptions,
113 InitOptions,
114 ExportOptions,
115 _OptionsBase as OptionsBase,
116 )
117 from syside.reqif._config import ATTACHMENTS_DIR, LIBRARY_DIR, LIBRARY_PKG
118except ImportError as e:
119 # Anything rooted in ``syside`` is an internal regression (typo, missing
120 # submodule, etc.) and must surface untouched — the friendly install-hint
121 # path is reserved for failures rooted in the third-party deps the
122 # ``[reqif]`` extra pulls in (reqif, markdown-it-py, markdownify, and
123 # their transitive dependencies). This avoids having to maintain a
124 # hand-rolled list of extra deps in lockstep with pyproject.toml.
125 if (e.name or "").split(".", 1)[0] == "syside":
126 raise
127 __all__: list[str] = []
128 _missing_extra_error = e
129else:
130 __all__ = [
131 "ATTACHMENTS_DIR",
132 "CheckReqifOptions",
133 "CheckSysmlOptions",
134 "ImportOptions",
135 "InitOptions",
136 "ExportOptions",
137 "OptionsBase",
138 "LIBRARY_DIR",
139 "LIBRARY_PKG",
140 "check_reqif",
141 "check_sysml",
142 "import_",
143 "init",
144 "lock",
145 "export",
146 ]
147 _missing_extra_error = None