File: index.rst

package info (click to toggle)
python-pytooling 8.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,564 kB
  • sloc: python: 23,883; makefile: 13
file content (185 lines) | stat: -rw-r--r-- 6,208 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
.. _CLIABS:

Overview
########

:mod:`~pyTooling.CLIAbstraction` offers an abstraction layer for command line programs, so they can be used easily in
Python. There is no need for manually assembling parameter lists or considering the order of parameters. All parameters
like ``-v`` or ``--value=42`` are described as :class:`~pyTooling.CLIAbstraction.Argument.CommandLineArgument` instances
on a :class:`~pyTooling.CLIAbstraction.Program` class. Each argument class like :class:`~pyTooling.CLIAbstraction.Flag.ShortFlag`
or :class:`~pyTooling.CLIAbstraction.Argument.PathArgument` knows about the correct formatting pattern, character
escaping, and if needed about necessary type conversions. A program instance can be converted to an argument list
suitable for :class:`subprocess.Popen`.

While a user-defined command line program abstraction derived from :class:`~pyTooling.CLIAbstraction.Program` only
takes care of maintaining and assembling parameter lists, a more advanced base-class, called :class:`~pyTooling.CLIAbstraction.Executable`,
is offered with embedded :class:`~subprocess.Popen` behavior.


.. _CLIABS/Goals:

Design Goals
************

The main design goals are:

* Offer access to CLI programs as Python classes.
* Abstract CLI arguments (a.k.a. parameter, option, flag, ...) as members on such a Python class.
* Abstract differences in operating systems like argument pattern (POSIX: ``-h`` vs. Windows: ``/h``), path delimiter
  signs (POSIX: ``/`` vs. Windows: ``\``) or executable names.
* Derive program variants from existing programs.
* Assemble parameters as list for handover to :class:`subprocess.Popen` with proper escaping and quoting.
* Launch a program with :class:`~subprocess.Popen` and hide the complexity of Popen.
* Get a generator object for line-by-line output reading to enable postprocessing of outputs.


.. _CLIABS/Example:

Example
*******

The following example implements a portion of the ``git`` program and its ``commit`` sub-command.

1. A new class ``Git`` is derived from :class:`pyTooling.CLIAbstraction.Executable`.
2. A class variable ``_executableNames`` is set, to specify different executable names based on the operating system.
3. Nested classes are used to describe arguments and flags for the Git program.
4. These nested classes are annotated with the ``@CLIArgument`` attribute, which is used to register the nested classes
   in an ordered lookup structure. This declaration order is also used to order arguments when converting to a list for
   :class:`~subprocess.Popen`.

.. grid:: 2

   .. grid-item:: **Usage of** ``Git``
      :columns: 6

      .. code-block:: Python

         # Create a program instance and set common parameters.
         git = Git()
         git[git.FlagVerbose] = True

         # Derive a variant of that pre-configured program.
         commit = git.getCommitTool()
         commit[commit.ValueCommitMessage] = "Bumped dependencies."

         # Launch the program and parse outputs line-by-line.
         commit.StartProcess()
         for line in commit.GetLineReader():
           print(line)

   .. grid-item:: **Declaration of** ``Git``
      :columns: 6

      .. code-block:: Python

         from pyTooling.CLIAbstraction import Executable
         from pyTooling.CLIAbstraction.Command import CommandArgument
         from pyTooling.CLIAbstraction.Flag import LongFlag
         from pyTooling.CLIAbstraction.ValuedTupleFlag import ShortTupleFlag

         class Git(Executable):
           _executableNames: ClassVar[Dict[str, str]] = {
             "Darwin":  "git",
             "FreeBSD": "git",
             "Linux":   "git",
             "Windows": "git.exe"
           }

           @CLIArgument()
           class FlagVerbose(LongFlag, name="verbose"):
             """Print verbose messages."""

           @CLIArgument()
           class CommandCommit(CommandArgument, name="commit"):
             """Command to commit staged files."""

           @CLIArgument()
           class ValueCommitMessage(ShortTupleFlag, name="m"):
             """Specify the commit message."""

           def GetCommitTool(self):
             """Derive a new program from a configured program."""
             tool = self.__class__(executablePath=self._executablePath)
             tool[tool.CommandCommit] = True
             self._CopyParameters(tool)

             return tool


.. _CLIABS/ProgramAPI:

Programm API
************

**Condensed definition of class** :class:`~pyTooling.CLIAbstraction.Program`:

.. code-block:: Python

   class Program(metaclass=ExtendedType, slots=True):
      # Register @CLIArgument marked nested classes in `__cliOptions__
      def __init_subclass__(cls, *args: Tuple[Any, ...], **kwargs: Dict[str, Any]):
        ...

      def __init__(self, executablePath: Path = None, binaryDirectoryPath: Path = None, dryRun: bool = False) -> None:
        ...

      @staticmethod
      def _NeedsParameterInitialization(key):
         ...

      # Implement indexed access operators: prog[...]
      def __getitem__(self, key):
         ...
      def __setitem__(self, key, value):
         ...

      @readonly
      def Path(self) -> Path:
         ...

      def ToArgumentList(self) -> List[str]:
         ...

      def __repr__(self):
         ...

      def __str__(self):
         ...


.. _CLIABS/ExecutableAPI:

Executable API
**************

**Condensed definition of class** :class:`~pyTooling.CLIAbstraction.Executable`:

.. code-block:: Python

   class Executable(Program):
      def __init__( self, executablePath: Path = None, binaryDirectoryPath: Path = None, workingDirectory: Path = None, # environment: Environment = None, dryRun: bool = False):
         ...

      def StartProcess(self):
         ...

      def Send(self, line: str, end: str="\n") -> None:
         ...

      def GetLineReader(self) -> Generator[str, None, None]:
         ...

      @readonly
      def ExitCode(self) -> int:
         ...


.. _CLIABS/Consumers:

Consumers
*********

This abstraction layer is used by:

* ✅ Wrap command line interfaces of EDA tools (Electronic Design Automation) in Python classes. |br|
  `pyEDAA.CLITool <https://github.com/edaa-org/pyEDAA.CLITool>`__