basebandflow 0.0.1__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.
Files changed (46) hide show
  1. basebandflow-0.0.1/.github/workflows/publish.yaml +48 -0
  2. basebandflow-0.0.1/.github/workflows/update-docs.yaml +50 -0
  3. basebandflow-0.0.1/.gitignore +3 -0
  4. basebandflow-0.0.1/CLI_REFERENCE.md +421 -0
  5. basebandflow-0.0.1/PKG-INFO +15 -0
  6. basebandflow-0.0.1/README.md +76 -0
  7. basebandflow-0.0.1/basebandflow/api/__init__.py +0 -0
  8. basebandflow-0.0.1/basebandflow/api/auth/__init__.py +58 -0
  9. basebandflow-0.0.1/basebandflow/api/auth/canfar.py +24 -0
  10. basebandflow-0.0.1/basebandflow/api/auth/tokens.py +102 -0
  11. basebandflow-0.0.1/basebandflow/api/config.py +59 -0
  12. basebandflow-0.0.1/basebandflow/api/containers.py +199 -0
  13. basebandflow-0.0.1/basebandflow/api/errors.py +50 -0
  14. basebandflow-0.0.1/basebandflow/api/history.py +73 -0
  15. basebandflow-0.0.1/basebandflow/api/launch.py +154 -0
  16. basebandflow-0.0.1/basebandflow/api/settings.py +60 -0
  17. basebandflow-0.0.1/basebandflow/api/status.py +19 -0
  18. basebandflow-0.0.1/basebandflow/api/validate.py +23 -0
  19. basebandflow-0.0.1/basebandflow/api/workflow.py +126 -0
  20. basebandflow-0.0.1/basebandflow/cli/config.py +128 -0
  21. basebandflow-0.0.1/basebandflow/cli/containers.py +159 -0
  22. basebandflow-0.0.1/basebandflow/cli/documentation.py +47 -0
  23. basebandflow-0.0.1/basebandflow/cli/history.py +97 -0
  24. basebandflow-0.0.1/basebandflow/cli/launch.py +83 -0
  25. basebandflow-0.0.1/basebandflow/cli/login.py +27 -0
  26. basebandflow-0.0.1/basebandflow/cli/main.py +65 -0
  27. basebandflow-0.0.1/basebandflow/cli/tokens.py +41 -0
  28. basebandflow-0.0.1/basebandflow/cli/ui.py +196 -0
  29. basebandflow-0.0.1/basebandflow/cli/workflow.py +46 -0
  30. basebandflow-0.0.1/basebandflow/models.py +97 -0
  31. basebandflow-0.0.1/basebandflow/templates/pipeline_config.yaml.j2 +251 -0
  32. basebandflow-0.0.1/basebandflow/tools.py +31 -0
  33. basebandflow-0.0.1/basebandflow.egg-info/PKG-INFO +15 -0
  34. basebandflow-0.0.1/basebandflow.egg-info/SOURCES.txt +44 -0
  35. basebandflow-0.0.1/basebandflow.egg-info/dependency_links.txt +1 -0
  36. basebandflow-0.0.1/basebandflow.egg-info/entry_points.txt +2 -0
  37. basebandflow-0.0.1/basebandflow.egg-info/requires.txt +11 -0
  38. basebandflow-0.0.1/basebandflow.egg-info/scm_file_list.json +40 -0
  39. basebandflow-0.0.1/basebandflow.egg-info/scm_version.json +8 -0
  40. basebandflow-0.0.1/basebandflow.egg-info/top_level.txt +1 -0
  41. basebandflow-0.0.1/check.ipynb +95 -0
  42. basebandflow-0.0.1/config.yaml +251 -0
  43. basebandflow-0.0.1/launch_containers.ipynb +290 -0
  44. basebandflow-0.0.1/pyproject.toml +22 -0
  45. basebandflow-0.0.1/setup.cfg +4 -0
  46. basebandflow-0.0.1/todo.md +10 -0
@@ -0,0 +1,48 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*' # Triggers when you run: git push origin v1.0
7
+
8
+ jobs:
9
+ release-and-publish:
10
+ name: Build, Release, and Publish
11
+ runs-on: ubuntu-latest
12
+
13
+ permissions:
14
+ id-token: write # Required for PyPI Trusted Publishing
15
+ contents: write # Required to create a GitHub Release
16
+
17
+ steps:
18
+ - name: Checkout code
19
+ uses: actions/checkout@v4
20
+ with:
21
+ fetch-depth: 0 # IMPORTANT: Fetches all git tags and full history for setuptools-scm
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+
28
+ - name: Install build dependencies
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ python -m pip install build
32
+
33
+ - name: Build distributions
34
+ run: python -m build
35
+
36
+ - name: Create GitHub Release
37
+ uses: softprops/action-gh-release@v2
38
+ with:
39
+ tag_name: ${{ github.ref_name }}
40
+ name: Release ${{ github.ref_name }}
41
+ draft: false
42
+ prerelease: false
43
+ files: dist/*
44
+ env:
45
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+
47
+ - name: Publish to PyPI
48
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,50 @@
1
+ name: Auto-Update CLI Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ # Only run if python CLI files or the readme itself change
8
+ paths:
9
+ - 'basebandflow/cli/**'
10
+ - 'README.md'
11
+ - 'pyproject.toml'
12
+
13
+ jobs:
14
+ update-readme:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: write
18
+
19
+ steps:
20
+ - name: Check out repository
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: '3.12'
27
+
28
+ - name: Install Dependencies & Typer CLI
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install "typer[cli]"
32
+ pip install -e . # Installs basebandflow to resolve local imports
33
+
34
+ - name: Generate CLI Reference File
35
+ run: |
36
+ typer basebandflow/cli/main.py utils docs --name bbflow --output CLI_REFERENCE.md
37
+
38
+ - name: Commit and Push Changes
39
+ run: |
40
+ git config --global user.name "github-actions[bot]"
41
+ git config --global user.email "github-actions[bot]@://github.com"
42
+
43
+ # Only stage and commit if CLI_REFERENCE.md actually changed
44
+ git add CLI_REFERENCE.md
45
+ if ! git diff --cached --quiet; then
46
+ git commit -m "docs: auto-update CLI reference documentation [skip ci]"
47
+ git push
48
+ else
49
+ echo "No documentation changes detected."
50
+ fi
@@ -0,0 +1,3 @@
1
+ *egg-info
2
+ .vscode/
3
+ __pycache__
@@ -0,0 +1,421 @@
1
+ # `bbflow`
2
+
3
+ Run the baseband pipeline on CANFAR
4
+
5
+ **Usage**:
6
+
7
+ ```console
8
+ $ bbflow [OPTIONS] COMMAND [ARGS]...
9
+ ```
10
+
11
+ **Options**:
12
+
13
+ * `--install-completion`: Install completion for the current shell.
14
+ * `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
15
+ * `--help`: Show this message and exit.
16
+
17
+ **Commands**:
18
+
19
+ * `version`: Show the bbflow version
20
+ * `config`: Configuration of basebandflow (bbflow)
21
+ * `containers`: Commands to manage CANFAR headless...
22
+ * `history`: List recent launches, newest first
23
+ * `launch`: Launch the baseband pipeline (containers +...
24
+ * `login`: Log in to your CANFAR account
25
+ * `tokens`: Manage CHIME/FRB API tokens
26
+ * `workflow`: Workflow related commands
27
+
28
+ ## `bbflow version`
29
+
30
+ Show the bbflow version
31
+
32
+ **Usage**:
33
+
34
+ ```console
35
+ $ bbflow version [OPTIONS]
36
+ ```
37
+
38
+ **Options**:
39
+
40
+ * `--help`: Show this message and exit.
41
+
42
+ ## `bbflow config`
43
+
44
+ Configuration of basebandflow (bbflow)
45
+
46
+ **Usage**:
47
+
48
+ ```console
49
+ $ bbflow config [OPTIONS] COMMAND [ARGS]...
50
+ ```
51
+
52
+ **Options**:
53
+
54
+ * `--help`: Show this message and exit.
55
+
56
+ **Commands**:
57
+
58
+ * `init`: Initialize the configuration
59
+ * `set`: Change a configuration parameter
60
+ * `show`: Show the current configuration
61
+ * `path`: Show where the configuration is saved
62
+
63
+ ### `bbflow config init`
64
+
65
+ Initialize the configuration
66
+
67
+ **Usage**:
68
+
69
+ ```console
70
+ $ bbflow config init [OPTIONS]
71
+ ```
72
+
73
+ **Options**:
74
+
75
+ * `--help`: Show this message and exit.
76
+
77
+ ### `bbflow config set`
78
+
79
+ Change a configuration parameter
80
+
81
+ **Usage**:
82
+
83
+ ```console
84
+ $ bbflow config set [OPTIONS] {parameter}
85
+ ```
86
+
87
+ **Arguments**:
88
+
89
+ * `parameter`: Choose between username, baseband_path, refresh_timer to update the configuration [required]
90
+
91
+ **Options**:
92
+
93
+ * `--help`: Show this message and exit.
94
+
95
+ ### `bbflow config show`
96
+
97
+ Show the current configuration
98
+
99
+ **Usage**:
100
+
101
+ ```console
102
+ $ bbflow config show [OPTIONS]
103
+ ```
104
+
105
+ **Options**:
106
+
107
+ * `--help`: Show this message and exit.
108
+
109
+ ### `bbflow config path`
110
+
111
+ Show where the configuration is saved
112
+
113
+ **Usage**:
114
+
115
+ ```console
116
+ $ bbflow config path [OPTIONS]
117
+ ```
118
+
119
+ **Options**:
120
+
121
+ * `--help`: Show this message and exit.
122
+
123
+ ## `bbflow containers`
124
+
125
+ Commands to manage CANFAR headless containers/sessions
126
+
127
+ **Usage**:
128
+
129
+ ```console
130
+ $ bbflow containers [OPTIONS] COMMAND [ARGS]...
131
+ ```
132
+
133
+ **Options**:
134
+
135
+ * `--help`: Show this message and exit.
136
+
137
+ **Commands**:
138
+
139
+ * `create`: Create a CANFAR container
140
+ * `list`: List CANFAR headless containers
141
+ * `delete`: Delete one CANFAR container
142
+ * `prune`: Prune multiple CANFAR containers
143
+
144
+ ### `bbflow containers create`
145
+
146
+ Create a CANFAR container
147
+
148
+ **Usage**:
149
+
150
+ ```console
151
+ $ bbflow containers create [OPTIONS] [session_type]
152
+ ```
153
+
154
+ **Arguments**:
155
+
156
+ * `session_type`: Type of session: datatrail, analysis, refinement, all
157
+
158
+ **Options**:
159
+
160
+ * `--help`: Show this message and exit.
161
+
162
+ ### `bbflow containers list`
163
+
164
+ List CANFAR headless containers
165
+
166
+ **Usage**:
167
+
168
+ ```console
169
+ $ bbflow containers list [OPTIONS]
170
+ ```
171
+
172
+ **Options**:
173
+
174
+ * `-f, --filter <str>`: Type of session: datatrail, analysis, refinement, all
175
+ * `--help`: Show this message and exit.
176
+
177
+ ### `bbflow containers delete`
178
+
179
+ Delete one CANFAR container
180
+
181
+ **Usage**:
182
+
183
+ ```console
184
+ $ bbflow containers delete [OPTIONS] {session_id}
185
+ ```
186
+
187
+ **Arguments**:
188
+
189
+ * `session_id`: CANFAR session ID [required]
190
+
191
+ **Options**:
192
+
193
+ * `--help`: Show this message and exit.
194
+
195
+ ### `bbflow containers prune`
196
+
197
+ Prune multiple CANFAR containers
198
+
199
+ **Usage**:
200
+
201
+ ```console
202
+ $ bbflow containers prune [OPTIONS] [session_type]
203
+ ```
204
+
205
+ **Arguments**:
206
+
207
+ * `session_type`: Type of session: datatrail, analysis, refinement, all
208
+
209
+ **Options**:
210
+
211
+ * `--help`: Show this message and exit.
212
+
213
+ ## `bbflow history`
214
+
215
+ List recent launches, newest first
216
+
217
+ **Usage**:
218
+
219
+ ```console
220
+ $ bbflow history [OPTIONS] COMMAND [ARGS]...
221
+ ```
222
+
223
+ **Options**:
224
+
225
+ * `-e, --event <str>`: Only this event ID
226
+ * `-n, --limit <int range>`: How many runs to show [default: 10; x&gt;=1]
227
+ * `-a, --all`: Include dry runs
228
+ * `--help`: Show this message and exit.
229
+
230
+ **Commands**:
231
+
232
+ * `show`: Show most recent launch for an event
233
+
234
+ ### `bbflow history show`
235
+
236
+ Show most recent launch for an event
237
+
238
+ **Usage**:
239
+
240
+ ```console
241
+ $ bbflow history show [OPTIONS] {event_id}
242
+ ```
243
+
244
+ **Arguments**:
245
+
246
+ * `event_id`: Event ID used for the baseband pipeline [required]
247
+
248
+ **Options**:
249
+
250
+ * `--help`: Show this message and exit.
251
+
252
+ ## `bbflow launch`
253
+
254
+ Launch the baseband pipeline (containers + deployment)
255
+
256
+ **Usage**:
257
+
258
+ ```console
259
+ $ bbflow launch [OPTIONS] {event_id}
260
+ ```
261
+
262
+ **Arguments**:
263
+
264
+ * `event_id`: [required]
265
+
266
+ **Options**:
267
+
268
+ * `--dry-run`
269
+ * `--help`: Show this message and exit.
270
+
271
+ ## `bbflow login`
272
+
273
+ Log in to your CANFAR account
274
+
275
+ **Usage**:
276
+
277
+ ```console
278
+ $ bbflow login [OPTIONS]
279
+ ```
280
+
281
+ **Options**:
282
+
283
+ * `--check`: Report status only
284
+ * `--help`: Show this message and exit.
285
+
286
+ ## `bbflow tokens`
287
+
288
+ Manage CHIME/FRB API tokens
289
+
290
+ **Usage**:
291
+
292
+ ```console
293
+ $ bbflow tokens [OPTIONS] COMMAND [ARGS]...
294
+ ```
295
+
296
+ **Options**:
297
+
298
+ * `--help`: Show this message and exit.
299
+
300
+ **Commands**:
301
+
302
+ * `path`: Show where CHIME/FRB tokens are saved
303
+ * `status`: Report the status and lifetime of JWT tokens
304
+ * `refresh`: Refresh CHIME/FRB tokens (requires...
305
+
306
+ ### `bbflow tokens path`
307
+
308
+ Show where CHIME/FRB tokens are saved
309
+
310
+ **Usage**:
311
+
312
+ ```console
313
+ $ bbflow tokens path [OPTIONS]
314
+ ```
315
+
316
+ **Options**:
317
+
318
+ * `--help`: Show this message and exit.
319
+
320
+ ### `bbflow tokens status`
321
+
322
+ Report the status and lifetime of JWT tokens
323
+
324
+ **Usage**:
325
+
326
+ ```console
327
+ $ bbflow tokens status [OPTIONS]
328
+ ```
329
+
330
+ **Options**:
331
+
332
+ * `--help`: Show this message and exit.
333
+
334
+ ### `bbflow tokens refresh`
335
+
336
+ Refresh CHIME/FRB tokens (requires password input)
337
+
338
+ **Usage**:
339
+
340
+ ```console
341
+ $ bbflow tokens refresh [OPTIONS]
342
+ ```
343
+
344
+ **Options**:
345
+
346
+ * `--help`: Show this message and exit.
347
+
348
+ ## `bbflow workflow`
349
+
350
+ Workflow related commands
351
+
352
+ **Usage**:
353
+
354
+ ```console
355
+ $ bbflow workflow [OPTIONS] COMMAND [ARGS]...
356
+ ```
357
+
358
+ **Options**:
359
+
360
+ * `--help`: Show this message and exit.
361
+
362
+ **Commands**:
363
+
364
+ * `abort`: Abort the Workflow deployment
365
+ * `status`: Loads the status of the Workflow deployment
366
+ * `open`: Opens the Workflow page for the configuration
367
+
368
+ ### `bbflow workflow abort`
369
+
370
+ Abort the Workflow deployment
371
+
372
+ **Usage**:
373
+
374
+ ```console
375
+ $ bbflow workflow abort [OPTIONS] [config_id]
376
+ ```
377
+
378
+ **Arguments**:
379
+
380
+ * `config_id`: Configuration ID from Workflow
381
+
382
+ **Options**:
383
+
384
+ * `--help`: Show this message and exit.
385
+
386
+ ### `bbflow workflow status`
387
+
388
+ Loads the status of the Workflow deployment
389
+
390
+ **Usage**:
391
+
392
+ ```console
393
+ $ bbflow workflow status [OPTIONS] [event_id]
394
+ ```
395
+
396
+ **Arguments**:
397
+
398
+ * `event_id`: Event ID used for the baseband pipeline
399
+
400
+ **Options**:
401
+
402
+ * `--oneline / --no-oneline`: Oneline status [default: no-oneline]
403
+ * `--help`: Show this message and exit.
404
+
405
+ ### `bbflow workflow open`
406
+
407
+ Opens the Workflow page for the configuration
408
+
409
+ **Usage**:
410
+
411
+ ```console
412
+ $ bbflow workflow open [OPTIONS] [event_id]
413
+ ```
414
+
415
+ **Arguments**:
416
+
417
+ * `event_id`: Event ID used for the baseband pipeline
418
+
419
+ **Options**:
420
+
421
+ * `--help`: Show this message and exit.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: basebandflow
3
+ Version: 0.0.1
4
+ Requires-Python: >=3.12
5
+ Requires-Dist: typer
6
+ Requires-Dist: rich
7
+ Requires-Dist: jinja2
8
+ Requires-Dist: pyyaml
9
+ Requires-Dist: pydantic
10
+ Requires-Dist: platformdirs
11
+ Requires-Dist: canfar
12
+ Requires-Dist: pyjwt
13
+ Requires-Dist: pandas
14
+ Requires-Dist: chime-frb-api
15
+ Requires-Dist: nest_asyncio
@@ -0,0 +1,76 @@
1
+ # 🌌 basebandflow
2
+
3
+ A command-line interface to run the CHIME/FRB baseband pipeline on **CANFAR**.
4
+
5
+ ## 📦 Installation
6
+
7
+ This project uses `pyproject.toml` to manage dependencies and expose the command-line executable.
8
+
9
+ ```bash
10
+ # Clone the repository
11
+ git clone https://github.com
12
+ cd basebandflow
13
+
14
+ # Install the package locally in editable mode (remove -e for normal install)
15
+ pip install -e .
16
+ ```
17
+
18
+ ## 🛠️ Usage
19
+
20
+ Once installed, you can launch the CLI using the executable defined in your `pyproject.toml` (typically `bbflow` or `basebandflow`—replace `bbflow` below with your actual entry point name):
21
+
22
+ ```bash
23
+ bbflow [OPTIONS] COMMAND [ARGS]...
24
+ ```
25
+
26
+ If you run the base command without any subcommands, it will automatically check your CANFAR login status and display an interactive banner menu:
27
+ ```bash
28
+ bbflow
29
+ ```
30
+
31
+ ### Debug Mode
32
+ If you encounter errors and need a full Python stack trace instead of a clean error message, set the debug environment variable:
33
+ ```bash
34
+ export BBFLOW_DEBUG=1
35
+ bbflow <command>
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 📖 CLI Reference
41
+ For a full list of all available commands, options, and flags, see the auto-generated [CLI Reference Guide](CLI_REFERENCE.md).
42
+
43
+
44
+ <!-- ## 📖 CLI Reference
45
+
46
+ The interface is broken down into several modular subcommands:
47
+
48
+ ### Global Options
49
+ * `-h`, `--help`: Show the help message and exit.
50
+
51
+ ### Command Groups
52
+
53
+ | Command Group | Description |
54
+ | :--- | :--- |
55
+ | **`config`** | Manage local CLI configurations and settings. |
56
+ | **`containers`** | List, pull, or manage CANFAR pipeline containers. |
57
+ | **`history`** | View historical logs and past pipeline runs. |
58
+ | **`launch`** | Spin up and initiate a new baseband pipeline job. |
59
+ | **`login`** | Authenticate with CANFAR services. |
60
+ | **`tokens`** | Manage CHIME/FRB and CANFAR authentication tokens. |
61
+ | **`workflow`** | Define, inspect, and track active workflow states. |
62
+
63
+ > 💡 *To view detailed arguments and options for any group, run `bbflow <group> --help`.*
64
+
65
+ > ⚠️ **More information about the commands in [CLI Reference](CLI_REFERENCE.md)** -->
66
+
67
+ <!-- --- -->
68
+
69
+ <!-- ## 🧪 Development
70
+
71
+ To run the test suite or contribute to `basebandflow`:
72
+
73
+ ```bash
74
+ pip install -e ".[test]"
75
+ pytest
76
+ ``` -->
File without changes
@@ -0,0 +1,58 @@
1
+ from datetime import timedelta
2
+
3
+ from basebandflow.api.auth.canfar import is_logged_in
4
+ from basebandflow.api.auth.tokens import (
5
+ load_tokens,
6
+ refresh_tokens,
7
+ token_time_remaining,
8
+ tokens_need_refresh,
9
+ )
10
+ from basebandflow.api.errors import InvalidTokens, NotLoggedIn, TokenRefreshFailed
11
+ from basebandflow.api.settings import require_settings
12
+ from basebandflow.models import TokenStatus
13
+ from basebandflow.tools import require_tool
14
+
15
+
16
+ def tokens_valid() -> bool:
17
+ settings = require_settings()
18
+ margin = timedelta(minutes=settings.token_refresh_timer)
19
+ access, _ = load_tokens()
20
+ return not tokens_need_refresh(access, margin)
21
+
22
+
23
+ def refresh_and_verify() -> None:
24
+ """Refresh (prompts for a password), then confirm the result. Call from the CLI, not from launch()."""
25
+ refresh_tokens()
26
+ if not tokens_valid():
27
+ raise TokenRefreshFailed("Tokens are still expired after refresh.")
28
+
29
+
30
+ def ensure_ready_for_launch() -> None:
31
+ require_tool("workflow")
32
+ logged_in = is_logged_in()
33
+ if logged_in is None:
34
+ raise NotLoggedIn(
35
+ "Couldn't reach CANFAR to verify your login. Check your connection and retry."
36
+ )
37
+ if not logged_in:
38
+ raise NotLoggedIn("Not logged in to CANFAR. Run `bbflow login`.")
39
+ if not tokens_valid():
40
+ raise InvalidTokens(
41
+ "CHIME/FRB tokens are expired or about to expire."
42
+ ) # no refresh here
43
+
44
+
45
+ def _token_status(token: str | None, margin: timedelta) -> TokenStatus:
46
+ return TokenStatus(
47
+ present=bool(token),
48
+ remaining=token_time_remaining(token),
49
+ needs_refresh=tokens_need_refresh(token, margin),
50
+ )
51
+
52
+
53
+ def check_tokens() -> tuple[TokenStatus, TokenStatus]:
54
+ settings = require_settings()
55
+ access, refresh = load_tokens()
56
+ return _token_status(
57
+ access, timedelta(minutes=settings.token_refresh_timer)
58
+ ), _token_status(refresh, timedelta(minutes=settings.token_refresh_timer))
@@ -0,0 +1,24 @@
1
+ from basebandflow import tools
2
+ from basebandflow.api.errors import ToolNotFound
3
+
4
+
5
+ def is_logged_in() -> bool | None:
6
+ """True/False, or None if it couldn't be verified (timeout)."""
7
+ try:
8
+ r = tools.canfar(
9
+ "ps",
10
+ stdin=tools.subprocess.DEVNULL,
11
+ capture_output=True,
12
+ text=True,
13
+ timeout=30,
14
+ )
15
+ except ToolNotFound:
16
+ return False
17
+ except tools.subprocess.TimeoutExpired:
18
+ return None
19
+ return r.returncode == 0
20
+
21
+
22
+ def login_interactive() -> int: # inherits terminal, returns exit code
23
+ r = tools.canfar("login", "--force")
24
+ return r.returncode