rhapsody-cli 0.1.0__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.
Files changed (92) hide show
  1. rhapsody_cli/__init__.py +21 -0
  2. rhapsody_cli/actions/__init__.py +17 -0
  3. rhapsody_cli/actions/abstract_action.py +259 -0
  4. rhapsody_cli/actions/attribute_action.py +595 -0
  5. rhapsody_cli/actions/class_action.py +708 -0
  6. rhapsody_cli/actions/operation_action.py +606 -0
  7. rhapsody_cli/actions/package_action.py +592 -0
  8. rhapsody_cli/actions/port_action.py +565 -0
  9. rhapsody_cli/actions/project_action.py +119 -0
  10. rhapsody_cli/application.py +390 -0
  11. rhapsody_cli/cli/__init__.py +5 -0
  12. rhapsody_cli/cli/cli.py +102 -0
  13. rhapsody_cli/cli/formatters.py +47 -0
  14. rhapsody_cli/cli/logging_config.py +39 -0
  15. rhapsody_cli/cli/main.py +6 -0
  16. rhapsody_cli/cli/path_resolver.py +117 -0
  17. rhapsody_cli/com_utils.py +54 -0
  18. rhapsody_cli/commands/__init__.py +9 -0
  19. rhapsody_cli/commands/abstract_command.py +92 -0
  20. rhapsody_cli/commands/attribute_command.py +36 -0
  21. rhapsody_cli/commands/class_command.py +38 -0
  22. rhapsody_cli/commands/operation_command.py +36 -0
  23. rhapsody_cli/commands/package_command.py +36 -0
  24. rhapsody_cli/commands/port_command.py +36 -0
  25. rhapsody_cli/commands/project_command.py +34 -0
  26. rhapsody_cli/exceptions/__init__.py +9 -0
  27. rhapsody_cli/exceptions/core.py +42 -0
  28. rhapsody_cli/models/__init__.py +16 -0
  29. rhapsody_cli/models/core.py +2263 -0
  30. rhapsody_cli/models/elements/__init__.py +15 -0
  31. rhapsody_cli/models/elements/activity/__init__.py +33 -0
  32. rhapsody_cli/models/elements/activity/model_actions.py +358 -0
  33. rhapsody_cli/models/elements/activity/model_activity.py +744 -0
  34. rhapsody_cli/models/elements/classifiers/__init__.py +27 -0
  35. rhapsody_cli/models/elements/classifiers/model_actor.py +76 -0
  36. rhapsody_cli/models/elements/classifiers/model_association_class.py +21 -0
  37. rhapsody_cli/models/elements/classifiers/model_class.py +461 -0
  38. rhapsody_cli/models/elements/classifiers/model_classifier.py +870 -0
  39. rhapsody_cli/models/elements/classifiers/model_interface_item.py +138 -0
  40. rhapsody_cli/models/elements/classifiers/model_operation.py +182 -0
  41. rhapsody_cli/models/elements/classifiers/model_statechart.py +108 -0
  42. rhapsody_cli/models/elements/classifiers/model_stereotype.py +23 -0
  43. rhapsody_cli/models/elements/classifiers/model_usecase.py +86 -0
  44. rhapsody_cli/models/elements/common/__init__.py +21 -0
  45. rhapsody_cli/models/elements/common/model_misc.py +45 -0
  46. rhapsody_cli/models/elements/common/model_other_model.py +822 -0
  47. rhapsody_cli/models/elements/containment/__init__.py +29 -0
  48. rhapsody_cli/models/elements/containment/model_collaboration.py +55 -0
  49. rhapsody_cli/models/elements/containment/model_component.py +59 -0
  50. rhapsody_cli/models/elements/containment/model_component_instance.py +20 -0
  51. rhapsody_cli/models/elements/containment/model_configuration.py +62 -0
  52. rhapsody_cli/models/elements/containment/model_module.py +17 -0
  53. rhapsody_cli/models/elements/containment/model_node.py +22 -0
  54. rhapsody_cli/models/elements/containment/model_package.py +285 -0
  55. rhapsody_cli/models/elements/containment/model_profile.py +17 -0
  56. rhapsody_cli/models/elements/containment/model_project.py +248 -0
  57. rhapsody_cli/models/elements/diagrams/__init__.py +31 -0
  58. rhapsody_cli/models/elements/diagrams/model_diagram_types.py +285 -0
  59. rhapsody_cli/models/elements/diagrams/model_diagrams.py +114 -0
  60. rhapsody_cli/models/elements/graphics/__init__.py +35 -0
  61. rhapsody_cli/models/elements/graphics/model_graphics.py +3053 -0
  62. rhapsody_cli/models/elements/interactions/__init__.py +29 -0
  63. rhapsody_cli/models/elements/interactions/model_interactions.py +1336 -0
  64. rhapsody_cli/models/elements/relations/__init__.py +23 -0
  65. rhapsody_cli/models/elements/relations/model_association_role.py +20 -0
  66. rhapsody_cli/models/elements/relations/model_dependency.py +23 -0
  67. rhapsody_cli/models/elements/relations/model_generalization.py +26 -0
  68. rhapsody_cli/models/elements/relations/model_hyperlink.py +25 -0
  69. rhapsody_cli/models/elements/relations/model_instance.py +224 -0
  70. rhapsody_cli/models/elements/relations/model_port.py +227 -0
  71. rhapsody_cli/models/elements/relations/model_relation.py +474 -0
  72. rhapsody_cli/models/elements/requirements/__init__.py +11 -0
  73. rhapsody_cli/models/elements/requirements/model_requirements.py +176 -0
  74. rhapsody_cli/models/elements/statemachine/__init__.py +11 -0
  75. rhapsody_cli/models/elements/statemachine/model_statemachine.py +793 -0
  76. rhapsody_cli/models/elements/templates/__init__.py +13 -0
  77. rhapsody_cli/models/elements/templates/model_templates.py +172 -0
  78. rhapsody_cli/models/elements/values/__init__.py +17 -0
  79. rhapsody_cli/models/elements/values/model_values.py +282 -0
  80. rhapsody_cli/models/elements/variables/__init__.py +15 -0
  81. rhapsody_cli/models/elements/variables/model_variables.py +301 -0
  82. rhapsody_cli/models/support/__init__.py +5 -0
  83. rhapsody_cli/models/support/model_codegen.py +1749 -0
  84. rhapsody_cli/models/support/model_files.py +438 -0
  85. rhapsody_cli/models/support/model_ide.py +782 -0
  86. rhapsody_cli/py.typed +0 -0
  87. rhapsody_cli-0.1.0.dist-info/METADATA +395 -0
  88. rhapsody_cli-0.1.0.dist-info/RECORD +92 -0
  89. rhapsody_cli-0.1.0.dist-info/WHEEL +5 -0
  90. rhapsody_cli-0.1.0.dist-info/entry_points.txt +2 -0
  91. rhapsody_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  92. rhapsody_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ """rhapsody_cli: Pythonic wrapper around the IBM Rhapsody COM API.
2
+
3
+ Method names on wrapped elements mirror the Rhapsody Java API
4
+ (`com.telelogic.rhapsody.core`) exactly, so existing Rhapsody Java API
5
+ knowledge transfers directly. Importing this package registers all core
6
+ element wrappers with the internal ``AbstractRPModelElement.wrap()`` dispatch factory.
7
+ """
8
+
9
+ from rhapsody_cli import models # noqa: F401
10
+ from rhapsody_cli.application import RhapsodyApplication
11
+ from rhapsody_cli.exceptions import RhapsodyConnectionError, RhapsodyRuntimeException
12
+ from rhapsody_cli.models import RPCollection, RPModelElement, RPUnit
13
+
14
+ __all__ = [
15
+ "RPCollection",
16
+ "RPModelElement",
17
+ "RPUnit",
18
+ "RhapsodyApplication",
19
+ "RhapsodyConnectionError",
20
+ "RhapsodyRuntimeException",
21
+ ]
@@ -0,0 +1,17 @@
1
+ """Action classes for CLI subcommands (PanGu-style architecture)."""
2
+
3
+ from rhapsody_cli.actions.abstract_action import AbstractAction
4
+ from rhapsody_cli.actions.project_action import (
5
+ ProjectCloseAction,
6
+ ProjectListAction,
7
+ ProjectNewAction,
8
+ ProjectOpenAction,
9
+ )
10
+
11
+ __all__ = [
12
+ "AbstractAction",
13
+ "ProjectCloseAction",
14
+ "ProjectListAction",
15
+ "ProjectNewAction",
16
+ "ProjectOpenAction",
17
+ ]
@@ -0,0 +1,259 @@
1
+ """Abstract base class for CLI actions (subcommands)."""
2
+
3
+ import argparse
4
+ import logging
5
+ from typing import Any, List, NoReturn, Optional
6
+
7
+ from rhapsody_cli.application import RhapsodyApplication
8
+ from rhapsody_cli.cli.formatters import OutputFormatter
9
+ from rhapsody_cli.cli.path_resolver import PathResolver, PathResolverError
10
+ from rhapsody_cli.exceptions import CliExecutionError, RhapsodyConnectionError
11
+ from rhapsody_cli.models.elements.containment import RPProject
12
+
13
+
14
+ class AbstractAction:
15
+ """Base class for a single subcommand action.
16
+
17
+ Each action owns:
18
+ - command_id: The subcommand identifier (e.g., "add", "open", "import")
19
+ - logger: Instance-specific logger for this action
20
+ - its own argument registration (init_arguments)
21
+ - its own execution logic (execute)
22
+
23
+ Instance Attributes:
24
+ command_id (str): Subcommand identifier used for CLI dispatch (e.g., "add" for "element add")
25
+ logger (logging.Logger): Instance-specific logger using the class name
26
+ """
27
+
28
+ def __init__(self, command_id: str = "") -> None:
29
+ """Initialize the action with a command identifier.
30
+
31
+ Args:
32
+ command_id: The subcommand identifier (e.g., "add", "open", "import")
33
+ """
34
+ self.command_id = command_id
35
+ self.logger = logging.getLogger(self.__class__.__name__)
36
+
37
+ def init_arguments(self, sub_parser: "argparse._SubParsersAction[argparse.ArgumentParser]") -> None:
38
+ """Register this action's subparser and arguments.
39
+
40
+ Args:
41
+ sub_parser: The subparsers object returned by
42
+ ArgumentParser.add_subparsers(), used to add this action's
43
+ own subparser via sub_parser.add_parser(...).
44
+ """
45
+ raise NotImplementedError("Subclasses must implement the init_arguments method.")
46
+
47
+ def execute(self, args: argparse.Namespace) -> None:
48
+ """Execute the action using the parsed arguments.
49
+
50
+ Args:
51
+ args: The parsed argparse.Namespace for the command group.
52
+ """
53
+ raise NotImplementedError("Subclasses must implement the execute method.")
54
+
55
+ @staticmethod
56
+ def add_verbose_argument(parser: argparse.ArgumentParser) -> None:
57
+ """Add the shared --verbose/-v flag to a subcommand parser.
58
+
59
+ Args:
60
+ parser: The argument parser to add the verbose flag to.
61
+ """
62
+ parser.add_argument(
63
+ "--verbose",
64
+ "-v",
65
+ action="store_true",
66
+ help="Enable DEBUG-level logging",
67
+ )
68
+
69
+
70
+ class RhapsodyContextAction(AbstractAction):
71
+ """Base class for actions that require a live Rhapsody connection.
72
+
73
+ Provides shared error handling and lazily-cached application/project
74
+ state, plus multi-format output rendering.
75
+ """
76
+
77
+ _NO_ACTIVE_INSTANCE_MESSAGE = "No running Rhapsody instance found. Please open Rhapsody and a project first."
78
+
79
+ def __init__(self, command_id: str = "") -> None:
80
+ """Initialize the action with a command identifier and empty connection state.
81
+
82
+ Args:
83
+ command_id: The subcommand identifier (e.g., "add", "open", "import")
84
+ """
85
+ super().__init__(command_id)
86
+ self._app: Optional[RhapsodyApplication] = None
87
+ self._project: Optional[RPProject] = None
88
+ self.output_format: str = "table"
89
+
90
+ def _connect_app(self) -> RhapsodyApplication:
91
+ """Lazily connect to Rhapsody and cache the connection for this action.
92
+
93
+ Returns:
94
+ The cached RhapsodyApplication, connecting on first access.
95
+ """
96
+ if self._app is None:
97
+ self._app = RhapsodyApplication.connect()
98
+ return self._app
99
+
100
+ def _print_formatted_output(
101
+ self,
102
+ data: Any,
103
+ headers: List[str],
104
+ table_rows: List[List[Any]],
105
+ *,
106
+ force_table: bool = False,
107
+ ) -> None:
108
+ """Format `data` per this action's output_format and print to stdout.
109
+
110
+ Result data goes to stdout (not the logger) so it stays safe for
111
+ piping/redirection (e.g. `> out.json`).
112
+
113
+ Args:
114
+ data: Payload to emit when the output format is JSON.
115
+ headers: Column headers for the table form.
116
+ table_rows: Rows (each a list of cells) for the table form.
117
+ force_table: When True, always render the table form even if
118
+ output_format is JSON. Use this to preserve an existing
119
+ table-only contract while sharing the helper.
120
+ """
121
+ if self.output_format == "json" and not force_table:
122
+ output = OutputFormatter.json_format(data)
123
+ else:
124
+ output = OutputFormatter.table(headers, table_rows)
125
+ print(output)
126
+
127
+ def _handle_connection_error(self, error: RhapsodyConnectionError, context_msg: str = "") -> NoReturn:
128
+ """Log a connection error and raise CliExecutionError for the user.
129
+
130
+ Args:
131
+ error: The RhapsodyConnectionError to handle.
132
+ context_msg: Optional context message to log.
133
+
134
+ Raises:
135
+ CliExecutionError: Always, with the user-facing "no active
136
+ instance" message.
137
+ """
138
+ msg = f"Failed to attach to Rhapsody: {error}"
139
+ if context_msg:
140
+ msg = f"{context_msg}: {msg}"
141
+ self.logger.error(msg)
142
+ raise CliExecutionError(self._NO_ACTIVE_INSTANCE_MESSAGE) from error
143
+
144
+ def _handle_execution_error(self, error: Exception, operation: str = "Operation") -> NoReturn:
145
+ """Log an execution error and raise CliExecutionError for the user.
146
+
147
+ Args:
148
+ error: The exception to handle.
149
+ operation: Description of what operation failed.
150
+
151
+ Raises:
152
+ CliExecutionError: Always, wrapping `error`'s message.
153
+ """
154
+ self.logger.error("%s failed: %s", operation, error)
155
+ raise CliExecutionError(f"Error: {error}") from error
156
+
157
+
158
+ class ElementManagementAction(RhapsodyContextAction):
159
+ """Base class for element management actions (add, delete, query, view).
160
+
161
+ Provides shared element-specific utilities.
162
+ """
163
+
164
+ _PATH_ARGUMENT_HELP = "Container path using '/' or '\\' separators (default: project root)"
165
+
166
+ _RECURSIVE_QUERY_HELP = "Include elements nested at any depth below the container"
167
+ _RECURSIVE_DELETE_HELP = "Delete the element and all elements nested within it"
168
+
169
+ @staticmethod
170
+ def add_path_argument(
171
+ parser: argparse.ArgumentParser,
172
+ *,
173
+ required: bool = False,
174
+ help_text: str = _PATH_ARGUMENT_HELP,
175
+ ) -> None:
176
+ """Add the shared --path argument to a subcommand parser.
177
+
178
+ Args:
179
+ parser: The argument parser to add the path argument to.
180
+ required: Whether the argument is required (defaults to False).
181
+ help_text: Help text shown in --help (defaults to the standard
182
+ container-path description).
183
+ """
184
+ parser.add_argument("--path", default=None, required=required, help=help_text)
185
+
186
+ @staticmethod
187
+ def add_recursive_argument(parser: argparse.ArgumentParser, *, help_text: str) -> None:
188
+ """Add the shared --recursive flag to a subcommand parser.
189
+
190
+ Args:
191
+ parser: The argument parser to add the recursive flag to.
192
+ help_text: Help text shown in --help (caller-supplied because
193
+ query vs. delete have different descriptions).
194
+ """
195
+ parser.add_argument("--recursive", action="store_true", help=help_text)
196
+
197
+ def _get_active_project(self) -> RPProject:
198
+ """Get the active project, handling errors appropriately.
199
+
200
+ Returns:
201
+ The active project object.
202
+
203
+ Raises:
204
+ CliExecutionError: If no active project or connection error occurs.
205
+ """
206
+ try:
207
+ app = self._connect_app()
208
+ self._project = app.active_project()
209
+ return self._project
210
+ except RhapsodyConnectionError as e:
211
+ self._handle_connection_error(e)
212
+
213
+ def _get_active_root(self) -> Any:
214
+ """Return the root element of the active project.
215
+
216
+ Returns:
217
+ The root model element of the active project.
218
+ """
219
+ return self._get_active_project().get_root()
220
+
221
+ def _resolve_container_or_element(
222
+ self,
223
+ root: Any,
224
+ path: Optional[str],
225
+ *,
226
+ resolve_element: bool,
227
+ operation: str = "resolve path",
228
+ ) -> Any:
229
+ """Resolve `path` to a container or element, mapping errors to CliExecutionError.
230
+
231
+ Args:
232
+ root: The root element to navigate from (typically the active
233
+ project's root).
234
+ path: A "/" or "\\"-separated path, or None/"" to mean the root
235
+ itself (only valid when `resolve_element` is False).
236
+ resolve_element: True to use PathResolver.resolve_element (strict,
237
+ path required); False to use PathResolver.resolve_container
238
+ (lenient, empty path returns root).
239
+ operation: Short description of the calling operation, used in
240
+ the error message if an unexpected exception occurs.
241
+
242
+ Returns:
243
+ The element/container resolved at the end of the path.
244
+
245
+ Raises:
246
+ CliExecutionError: If the path cannot be parsed or navigated, or
247
+ if PathResolver raises any other exception.
248
+ """
249
+ try:
250
+ if resolve_element:
251
+ if path is None:
252
+ raise CliExecutionError(f"A path is required to {operation}")
253
+ return PathResolver.resolve_element(root, path)
254
+ return PathResolver.resolve_container(root, path)
255
+ except PathResolverError as e:
256
+ self.logger.error("%s", e)
257
+ raise CliExecutionError(str(e)) from e
258
+ except Exception as e:
259
+ self._handle_execution_error(e, f"Failed to {operation}")