datatrail-cli 0.12.0__tar.gz → 0.13.0__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 (48) hide show
  1. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/workflows/continuous-deployment.yml +3 -6
  2. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/workflows/continuous-integration.yml +28 -16
  3. datatrail_cli-0.13.0/.release-please-manifest.json +3 -0
  4. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/CHANGELOG.md +132 -0
  5. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/PKG-INFO +1 -1
  6. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/cli.py +8 -3
  7. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/scout.py +21 -4
  8. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/src/functions.py +24 -8
  9. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/utilities/cadcclient.py +6 -2
  10. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/utilities/utilities.py +11 -2
  11. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/pyproject.toml +6 -1
  12. datatrail_cli-0.13.0/release-please-config.json +15 -0
  13. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/tests/test_cli.py +28 -0
  14. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/tests/test_functions.py +24 -0
  15. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/uv.lock +1 -1
  16. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  18. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/workflows/docs.yml +0 -0
  20. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.github/workflows/stale.yml +0 -0
  21. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.gitignore +0 -0
  22. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/.pre-commit-config.yaml +0 -0
  23. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/LICENSE +0 -0
  24. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/README.md +0 -0
  25. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/clear.md +0 -0
  26. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/cli.md +0 -0
  27. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/commands.md +0 -0
  28. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/images/Datatrail-logo.png +0 -0
  29. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/index.md +0 -0
  30. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/initialising.md +0 -0
  31. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/install.md +0 -0
  32. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/list.md +0 -0
  33. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/ps.md +0 -0
  34. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/pull.md +0 -0
  35. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/scout.md +0 -0
  36. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/unregistered.md +0 -0
  37. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/docs/user_guide.md +0 -0
  38. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/__init__.py +0 -0
  39. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/clear.py +0 -0
  40. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/config.py +0 -0
  41. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/config.yaml +0 -0
  42. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/ls.py +0 -0
  43. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/ps.py +0 -0
  44. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/pull.py +0 -0
  45. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/dtcli/unregistered.py +0 -0
  46. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/mkdocs.yml +0 -0
  47. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/tests/test_utils.py +0 -0
  48. {datatrail_cli-0.12.0 → datatrail_cli-0.13.0}/tox.ini +0 -0
@@ -9,20 +9,17 @@ jobs:
9
9
  steps:
10
10
  - name: Create release
11
11
  id: release-please
12
- uses: google-github-actions/release-please-action@v3
13
- with:
14
- release-type: python
15
- package-name: datatrail-cli
12
+ uses: googleapis/release-please-action@v5
16
13
 
17
14
  - name: Checkout code
18
15
  if: ${{ steps.release-please.outputs.release_created }}
19
- uses: actions/checkout@v6
16
+ uses: actions/checkout@v7
20
17
  with:
21
18
  fetch-depth: 1
22
19
 
23
20
  - name: Install uv
24
21
  if: ${{ steps.release-please.outputs.release_created }}
25
- uses: astral-sh/setup-uv@v8.3.2
22
+ uses: astral-sh/setup-uv@v10.0.1
26
23
  with:
27
24
  enable-cache: false
28
25
  python-version: "3.13"
@@ -15,10 +15,10 @@ jobs:
15
15
  Pre-commit-checks:
16
16
  runs-on: ubuntu-latest
17
17
  steps:
18
- - uses: actions/checkout@v6
18
+ - uses: actions/checkout@v7
19
19
 
20
20
  - name: Set up Python 3.13
21
- uses: actions/setup-python@v6
21
+ uses: actions/setup-python@v7
22
22
  with:
23
23
  python-version: 3.13
24
24
 
@@ -30,6 +30,8 @@ jobs:
30
30
  Testing:
31
31
  needs: pre-commit-checks
32
32
  runs-on: ubuntu-latest
33
+ env:
34
+ RUN_CADC_TESTS: ${{ github.repository == 'CHIMEFRB/datatrail-cli' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
33
35
  strategy:
34
36
  fail-fast: false
35
37
  matrix:
@@ -41,10 +43,10 @@ jobs:
41
43
  - "3.13"
42
44
  - "3.14"
43
45
  steps:
44
- - uses: actions/checkout@v6
46
+ - uses: actions/checkout@v7
45
47
 
46
48
  - name: Install uv
47
- uses: astral-sh/setup-uv@v8.3.2
49
+ uses: astral-sh/setup-uv@v10.0.1
48
50
  with:
49
51
  enable-cache: true
50
52
  cache-dependency-glob: "uv.lock"
@@ -52,30 +54,40 @@ jobs:
52
54
  python-version: ${{ matrix.python-version }}
53
55
 
54
56
  - name: Installation
55
- run: uv sync
57
+ run: uv sync --locked
56
58
 
57
59
  - name: Create NETRC file
60
+ if: env.RUN_CADC_TESTS == 'true'
61
+ env:
62
+ CANFAR_NETRC: ${{ secrets.CANFAR_NETRC }}
58
63
  run: |
59
- export CANFAR_NETRC_FILE=$RUNNER_TEMP"/CANFAR_NETRC"
60
- echo ${{ secrets.CANFAR_NETRC }} > $CANFAR_NETRC_FILE
61
- echo CANFAR_NETRC_FILE=$CANFAR_NETRC_FILE >> "$GITHUB_ENV"
64
+ umask 077
65
+ test -n "$CANFAR_NETRC"
66
+ printf '%s\n' "$CANFAR_NETRC" > "$RUNNER_TEMP/CANFAR_NETRC"
62
67
 
63
68
  - name: Obtain CADC certificate
69
+ if: env.RUN_CADC_TESTS == 'true'
64
70
  run: |
65
- export CADC_CERT=$HOME"/.ssl/cadcproxy.pem"
66
- uv run --no-sync cadc-get-cert --netrc-file ${{ env.CANFAR_NETRC_FILE }} --days-valid 1 --cert-filename $CADC_CERT
67
- echo CADC_CERT=$CADC_CERT >> "$GITHUB_ENV"
71
+ umask 077
72
+ mkdir -p "$HOME/.ssl"
73
+ uv run --no-sync cadc-get-cert --netrc-file "$RUNNER_TEMP/CANFAR_NETRC" --days-valid 1 --cert-filename "$HOME/.ssl/cadcproxy.pem"
74
+ test -s "$HOME/.ssl/cadcproxy.pem"
75
+ openssl x509 -in "$HOME/.ssl/cadcproxy.pem" -noout -checkend 0
68
76
 
69
- - name: Run Tests
77
+ - name: Run tests
78
+ if: env.RUN_CADC_TESTS == 'true'
70
79
  run: |
71
80
  uv run --no-sync pytest tests/ --cov . --cov-report=lcov --cov-report=xml
72
81
 
82
+ - name: Run tests without CADC credentials
83
+ if: env.RUN_CADC_TESTS != 'true'
84
+ run: |
85
+ uv run --no-sync pytest tests/ -m "not cadc" --cov . --cov-report=lcov --cov-report=xml
86
+
73
87
  - name: Remove NETRC file and CADC certificate
88
+ if: ${{ always() && env.RUN_CADC_TESTS == 'true' }}
74
89
  run: |
75
- echo "Removing file: "$CANFAR_NETRC_FILE
76
- rm $CANFAR_NETRC_FILE
77
- echo "Removing file: "$CADC_CERT
78
- rm $CADC_CERT
90
+ rm -f -- "$RUNNER_TEMP/CANFAR_NETRC" "$HOME/.ssl/cadcproxy.pem"
79
91
 
80
92
  - name: Upload coverage to Coveralls
81
93
  uses: coverallsapp/github-action@v2
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.13.0"
3
+ }
@@ -1,5 +1,137 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.0](https://github.com/CHIMEFRB/datatrail-cli/compare/datatrail-cli-v0.12.0...datatrail-cli-v0.13.0) (2026-08-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * **clear:** command to unstage data locally or at arc ([#16](https://github.com/CHIMEFRB/datatrail-cli/issues/16)) ([737c781](https://github.com/CHIMEFRB/datatrail-cli/commit/737c7811a6112a46e842bc135d94d035a9bf301f))
9
+ * **clear:** remove empty parent dirs ([#20](https://github.com/CHIMEFRB/datatrail-cli/issues/20)) ([1559608](https://github.com/CHIMEFRB/datatrail-cli/commit/1559608de9d18d1b16a069c5b6b8136afb388fab))
10
+ * **cli:** add unregistered command ([d9a97a5](https://github.com/CHIMEFRB/datatrail-cli/commit/d9a97a53b6f87321c50aec34339d6b5959b5e713))
11
+ * **cli:** add unregistered command ([b33a6fa](https://github.com/CHIMEFRB/datatrail-cli/commit/b33a6fae57ff2bdad6c26f0ed5402994fdef7650))
12
+ * **cli:** aliases ([73ede83](https://github.com/CHIMEFRB/datatrail-cli/commit/73ede838133b54ef0ba8f45eda453547d601a180))
13
+ * **cli:** check scope exists ([f14589b](https://github.com/CHIMEFRB/datatrail-cli/commit/f14589bc539ec3448348d1d6bc9b1b32d864d7ea))
14
+ * **config:** added datatrail config module ([fe04ee1](https://github.com/CHIMEFRB/datatrail-cli/commit/fe04ee1af77e3d416c103e9ef73a7d79a4b616d5))
15
+ * **gh-actions:** ci and cd ([ebb7a96](https://github.com/CHIMEFRB/datatrail-cli/commit/ebb7a966836d2a4f57287191b9396d9b72cb3cfe))
16
+ * **ls & ps:** add --json flag for machine readable output ([75dac4d](https://github.com/CHIMEFRB/datatrail-cli/commit/75dac4da4fc172adebe2b518cf0b6136e201d94d))
17
+ * **ls & ps:** add --json flag for machine readable output ([c4742a0](https://github.com/CHIMEFRB/datatrail-cli/commit/c4742a0dfe43823ce8e68f21e26e146b90db958c)), closes [#159](https://github.com/CHIMEFRB/datatrail-cli/issues/159)
18
+ * **ls,-pull:** partially implemented ([6689d11](https://github.com/CHIMEFRB/datatrail-cli/commit/6689d119782d1e251935050c508fb14969ff33d2))
19
+ * **ls,pull:** fully implemented ([2105f5a](https://github.com/CHIMEFRB/datatrail-cli/commit/2105f5a86dc42a0618903d0e315bc6490fd633ed))
20
+ * **ls:** option to write ls datasets to disk ([513d665](https://github.com/CHIMEFRB/datatrail-cli/commit/513d665a843bb4aa1f6f92e54ab020ee4f1477ab))
21
+ * **ls:** show datasets in given parent dataset ([15f7da1](https://github.com/CHIMEFRB/datatrail-cli/commit/15f7da1aa76a2a0c75f9229e445f96886b353a1f))
22
+ * **ls:** show larger datasets ([344e230](https://github.com/CHIMEFRB/datatrail-cli/commit/344e230f9798ad8a27752431bfd70f73370488f8))
23
+ * **project:** boilerplate added ([43085a1](https://github.com/CHIMEFRB/datatrail-cli/commit/43085a15789c2045ea41bca9aa89c26c26182019))
24
+ * **ps:** function implemented ([6feedde](https://github.com/CHIMEFRB/datatrail-cli/commit/6feedde08b0dddc3c9b43aba17999e681a2c6b1e))
25
+ * **ps:** if no files check if dataset is in unregistered bucket and notify user ([40ed903](https://github.com/CHIMEFRB/datatrail-cli/commit/40ed903623a4ec9fd4043b4d1863626303830a54)), closes [#46](https://github.com/CHIMEFRB/datatrail-cli/issues/46)
26
+ * **ps:** show num files and size by default, flag to show all files ([cccbd33](https://github.com/CHIMEFRB/datatrail-cli/commit/cccbd3337289699f80112edff612abf746407f4b))
27
+ * **pull:** allows specific files to be pulled by designating names o… ([49f186a](https://github.com/CHIMEFRB/datatrail-cli/commit/49f186a6bcdd88e9aad0a863279b06e29b645158))
28
+ * **pull:** allows specific files to be pulled by designating names or paths in a local config file ([d3cfb59](https://github.com/CHIMEFRB/datatrail-cli/commit/d3cfb59906fe4cb13c8f549e8c2b6499c32e663e))
29
+ * **pull:** check all files were downloaded ([eea76ef](https://github.com/CHIMEFRB/datatrail-cli/commit/eea76efb91b2a35f2900be59d4484d02721322da))
30
+ * **pull:** check all files were downloaded ([bb1d8b6](https://github.com/CHIMEFRB/datatrail-cli/commit/bb1d8b64378fe80a27944f8eaccf6ce674ce17d0))
31
+ * **pull:** display size of files to be pulled before prompt ([38cffcc](https://github.com/CHIMEFRB/datatrail-cli/commit/38cffccb18dcaca41150ee7af85b4c80f6137284))
32
+ * **pull:** implemented multiprocessor pull ([2e7b332](https://github.com/CHIMEFRB/datatrail-cli/commit/2e7b33205789c54e4f9544223a0555d74edd464d))
33
+ * **scout:** able to heal missing minoc replicas ([bc3b52d](https://github.com/CHIMEFRB/datatrail-cli/commit/bc3b52d44fe7da7335279c96cec03e3deace6f9a))
34
+ * **scout:** add scout command to the cli ([#83](https://github.com/CHIMEFRB/datatrail-cli/issues/83)) ([5cb811f](https://github.com/CHIMEFRB/datatrail-cli/commit/5cb811f416c6fe099cf2405835ad867df125a62d))
35
+ * **scout:** generalised scout functionality to check for discrepanci… ([aabcad1](https://github.com/CHIMEFRB/datatrail-cli/commit/aabcad1937fef1734ef17593726d27bb673a29ad))
36
+ * **scout:** generalised scout functionality to check for discrepancies between all sites, not just minoc ([f3f1005](https://github.com/CHIMEFRB/datatrail-cli/commit/f3f1005722c48d4ac020b65ed13d19f6bc0bbd48))
37
+ * **structure:** added skeleton code ([c3e57f6](https://github.com/CHIMEFRB/datatrail-cli/commit/c3e57f63ea0e54c45f12a4c3682ed01e5d9489ec))
38
+ * **unregistered:** command to search for event in unregistered results bucket ([1bb4ca0](https://github.com/CHIMEFRB/datatrail-cli/commit/1bb4ca0c118e042a907f38205af5d7ed6bf9f16d))
39
+
40
+
41
+ ### Bug Fixes
42
+
43
+ * bound every REST call with a request timeout ([431270d](https://github.com/CHIMEFRB/datatrail-cli/commit/431270d2fb4e59acc8a875b578964d20d2aeebb4))
44
+ * **cadcclient:** check canfar status ([484562c](https://github.com/CHIMEFRB/datatrail-cli/commit/484562c3c8345dd032c00923d3250c1be4a12191))
45
+ * **cadcclient:** improve handling of stdout ([8b35e4f](https://github.com/CHIMEFRB/datatrail-cli/commit/8b35e4f5ccdaf874c706f31a608a89b0612b669d))
46
+ * **cadcclient:** improve handling of stdout ([320d4df](https://github.com/CHIMEFRB/datatrail-cli/commit/320d4dfcdd194aa69f9236c84f21dbfb8690068b))
47
+ * **cadcclient:** more robust switching of stdout ([9c90315](https://github.com/CHIMEFRB/datatrail-cli/commit/9c9031595a9e13d6471e4b5e5edc548a4d1545ad))
48
+ * **ci:** accept request timeout in scopes mock ([1ff9624](https://github.com/CHIMEFRB/datatrail-cli/commit/1ff9624c15045d1acdb30ad471bf6c6a9c298174))
49
+ * **ci:** keep fork main credential-free ([9030bfb](https://github.com/CHIMEFRB/datatrail-cli/commit/9030bfb6d971afdb557772d35b1691f531e5e0b4))
50
+ * **ci:** keep fork main credential-free ([73a4b02](https://github.com/CHIMEFRB/datatrail-cli/commit/73a4b029178db001025ec5442ce9b9a43911615c))
51
+ * **clear:** reorganise file_paths manipulation logic ([fd14f9b](https://github.com/CHIMEFRB/datatrail-cli/commit/fd14f9b714590f1cb57684565cfa23fd7bc8fc52))
52
+ * **clear:** reorganise file_paths manipulation logic ([80f0f2d](https://github.com/CHIMEFRB/datatrail-cli/commit/80f0f2d2bf3d9836eef16f9f6d3bed5f88c8ff05))
53
+ * **cli-test:** new test - marked as fix to trigger release ([aea98d0](https://github.com/CHIMEFRB/datatrail-cli/commit/aea98d07f63ee17f6d5b4acd396ea32e3aefbd11))
54
+ * **cli:** canfar status checks ([7f9df34](https://github.com/CHIMEFRB/datatrail-cli/commit/7f9df349ceeebb1caa7c1c2582cd3d1c54e8ec09))
55
+ * **cli:** catch http 503 errors when validating scope ([b6e75af](https://github.com/CHIMEFRB/datatrail-cli/commit/b6e75affe6db6508882ed5e3da1b22bdec194156))
56
+ * **cli:** check for luskan status ([b229549](https://github.com/CHIMEFRB/datatrail-cli/commit/b2295493c60f0f2a00a95c1c87eb64a0e926b886))
57
+ * **cli:** check for luskan status ([405a3c2](https://github.com/CHIMEFRB/datatrail-cli/commit/405a3c21334b5d8d96ab42436355d8c329880d74))
58
+ * **cli:** check minoc status ([a955ab1](https://github.com/CHIMEFRB/datatrail-cli/commit/a955ab129377dbd62215688598cc2cc434345152))
59
+ * **cli:** check minoc status ([6a0d6df](https://github.com/CHIMEFRB/datatrail-cli/commit/6a0d6dfc4f3490dc407c08d106d7740bb5d892e3))
60
+ * **cli:** print the update-available banner to stderr ([53b9fe4](https://github.com/CHIMEFRB/datatrail-cli/commit/53b9fe4560ff7835a6d7f4e4402538b64f4334aa))
61
+ * **cli:** print the update-available banner to stderr ([8dd6313](https://github.com/CHIMEFRB/datatrail-cli/commit/8dd63139b127cf7219932f9d271482a614dc1261))
62
+ * **cli:** removed pkg resources deprecation warning ([b488b02](https://github.com/CHIMEFRB/datatrail-cli/commit/b488b024c0f6904bc7f6d66ffff8281791cf59c1))
63
+ * **cli:** removed pkg resources deprecation warning ([2ab793d](https://github.com/CHIMEFRB/datatrail-cli/commit/2ab793d753badfd9a2c6aa53ed95010366437c3f))
64
+ * **cli:** updated some docs ([4a61228](https://github.com/CHIMEFRB/datatrail-cli/commit/4a61228300d5082e76081d84520fddf743cc0ebf))
65
+ * **cli:** wrap version check in try except ([2fc9d31](https://github.com/CHIMEFRB/datatrail-cli/commit/2fc9d3169cc28e675c4cfce51aa142b1f22466c4))
66
+ * **config.py:** typo ([#12](https://github.com/CHIMEFRB/datatrail-cli/issues/12)) ([279998b](https://github.com/CHIMEFRB/datatrail-cli/commit/279998bbc5a4c5bd9922db59b159a38fbfdade8b))
67
+ * connection errors raise correct status exit code ([9b4d7bc](https://github.com/CHIMEFRB/datatrail-cli/commit/9b4d7bcc74c6a7c6954d965f8ab421ea6181b016))
68
+ * **functions.py:** added specific handling for canfar site, where the… ([#66](https://github.com/CHIMEFRB/datatrail-cli/issues/66)) ([895a3ca](https://github.com/CHIMEFRB/datatrail-cli/commit/895a3ca7bdb7000847313ef12fe17e50ad7308c8))
69
+ * **functions.py:** changed error catching checks to be for dictionaries with error keys in them as opposed to strings ([#74](https://github.com/CHIMEFRB/datatrail-cli/issues/74)) ([6fdb244](https://github.com/CHIMEFRB/datatrail-cli/commit/6fdb244e46a62253f4654681ec5e5d76431f49f0))
70
+ * **functions.py:** patches deletion logic to check that the path to be deleted is greater than 3 levels deep ([#64](https://github.com/CHIMEFRB/datatrail-cli/issues/64)) ([ad43f35](https://github.com/CHIMEFRB/datatrail-cli/commit/ad43f35ce7b8fbc87210ca93df4433b3c4bbe06d))
71
+ * **functions:** bug ([f4e3788](https://github.com/CHIMEFRB/datatrail-cli/commit/f4e3788944e5a95f868e139369abdfac35c61caa))
72
+ * **functions:** changed return of find_missing_datasets to always ret… ([#76](https://github.com/CHIMEFRB/datatrail-cli/issues/76)) ([a8c0f3d](https://github.com/CHIMEFRB/datatrail-cli/commit/a8c0f3ded414c4e8d4fbca9529bfda7610531841))
73
+ * **init:** set valid choices for datatrail config init ([63f96c3](https://github.com/CHIMEFRB/datatrail-cli/commit/63f96c30c07be1d787381e5457021e805ea0cc6f))
74
+ * **ls:** other section for scopes that don't match list of sites ([0b50d79](https://github.com/CHIMEFRB/datatrail-cli/commit/0b50d790fbfb21fa0edb16eb945722189333a1c2))
75
+ * **ls:** report an unanswered scopes query as an error instead of leaking response text ([6eedaa3](https://github.com/CHIMEFRB/datatrail-cli/commit/6eedaa3720e1f86518fb3ba0292983020affa5af))
76
+ * **ls:** report an unanswered scopes query as an error instead of leaking response text ([8fb77d6](https://github.com/CHIMEFRB/datatrail-cli/commit/8fb77d6d1fbb2d724fbec73f84f4768a4d515181))
77
+ * **ls:** seperate scopes by site ([5bddcc7](https://github.com/CHIMEFRB/datatrail-cli/commit/5bddcc7b368fa0594128f0f7fcb0b2e26959d77e))
78
+ * **ls:** show all larger datasets for scope ([cda63ac](https://github.com/CHIMEFRB/datatrail-cli/commit/cda63ac3053912aa0f4f0bc4781773e3ce46ac0a)), closes [#1](https://github.com/CHIMEFRB/datatrail-cli/issues/1)
79
+ * **ps-and-pull:** check if namespace already prepended ([#9](https://github.com/CHIMEFRB/datatrail-cli/issues/9)) ([eba7532](https://github.com/CHIMEFRB/datatrail-cli/commit/eba7532f3a2c23854a62842f3ce54246916e0d6b))
80
+ * **ps-and-pull:** determination of common path ([a35bbad](https://github.com/CHIMEFRB/datatrail-cli/commit/a35bbad705b9702812281539516e2c7081a9516e)), closes [#19](https://github.com/CHIMEFRB/datatrail-cli/issues/19)
81
+ * **ps.py:** added error handling for returned strings ([#79](https://github.com/CHIMEFRB/datatrail-cli/issues/79)) ([0b3c5b6](https://github.com/CHIMEFRB/datatrail-cli/commit/0b3c5b6f95da361c080534f0e34132172300b956))
82
+ * **ps:** belongs_to cannot be none must be empty string ([#69](https://github.com/CHIMEFRB/datatrail-cli/issues/69)) ([b348dbd](https://github.com/CHIMEFRB/datatrail-cli/commit/b348dbdaa94e106f9c55be006b572566155d2be6))
83
+ * **ps:** bug where common path was not the parent dir of file ([3ba1969](https://github.com/CHIMEFRB/datatrail-cli/commit/3ba1969fec4aa5a57e5913a74d5d6aa10a7d86d7))
84
+ * **ps:** common path bug ([266cc3a](https://github.com/CHIMEFRB/datatrail-cli/commit/266cc3af3d3c397e83c9e18bad39ea5178b191f8))
85
+ * **ps:** controlled error if dataset doesnt exist in datatrail ([09a9d13](https://github.com/CHIMEFRB/datatrail-cli/commit/09a9d13a879971b65902ea19c3b2cda8ce7289de))
86
+ * **ps:** if parent dataset show only policies ([99b4bdd](https://github.com/CHIMEFRB/datatrail-cli/commit/99b4bddfbb788f6617dce408342232e0aa411156))
87
+ * **ps:** invalid scopes, valid scopes are now shown after error message ([5347137](https://github.com/CHIMEFRB/datatrail-cli/commit/534713782fe5105055506d55f1f43c51c110a4b4))
88
+ * **ps:** shows multiple larger datasets in belongs to ([6b32903](https://github.com/CHIMEFRB/datatrail-cli/commit/6b32903007a77f4d8539659870dacbcd94e67f18))
89
+ * **pull:** catch errors when no files at minoc ([fd34637](https://github.com/CHIMEFRB/datatrail-cli/commit/fd346373a3cdab93d0532a82cef5a8a04940aea7))
90
+ * **pull:** default directory ([da6a41d](https://github.com/CHIMEFRB/datatrail-cli/commit/da6a41d9c1dce3613996f735fef65ed62bbcc302))
91
+ * **pull:** FileNotFoundError stopping download ([#15](https://github.com/CHIMEFRB/datatrail-cli/issues/15)) ([cdee724](https://github.com/CHIMEFRB/datatrail-cli/commit/cdee7248d28a94c8bd4cabe3df9c93a337342dd3))
92
+ * **pull:** more processors than files bug ([0536326](https://github.com/CHIMEFRB/datatrail-cli/commit/053632632fd55cb5bba4fe7ce1d398148e0c9cba))
93
+ * **pull:** more processors than files bug ([e44bbe1](https://github.com/CHIMEFRB/datatrail-cli/commit/e44bbe1a14900f034f998b664c1d394fcf31c7a7)), closes [#147](https://github.com/CHIMEFRB/datatrail-cli/issues/147)
94
+ * **pull:** set group permissions if site is canfar ([70236ae](https://github.com/CHIMEFRB/datatrail-cli/commit/70236ae21c0e324d153be89dcead87489b77e5e5))
95
+ * **pull:** wrap download in tenacity retry ([ebdf5b1](https://github.com/CHIMEFRB/datatrail-cli/commit/ebdf5b10489b6a33937576850d57d05b1c1eefcb))
96
+ * **pull:** wrap download in tenacity retry ([8253157](https://github.com/CHIMEFRB/datatrail-cli/commit/8253157e78e1f0abb71fe71fb396b72d1ba51911))
97
+ * **scout:** change order of the args ([d5c33b8](https://github.com/CHIMEFRB/datatrail-cli/commit/d5c33b8ab8a5204fb63f1eb9744d1aa6d5c1f5d1))
98
+ * **scout:** change the order of the arguments ([c7ab294](https://github.com/CHIMEFRB/datatrail-cli/commit/c7ab29459707be153d12f1ff562cf46b984ed519))
99
+ * **scout:** improve error handling ([#87](https://github.com/CHIMEFRB/datatrail-cli/issues/87)) ([1937e9b](https://github.com/CHIMEFRB/datatrail-cli/commit/1937e9bbedfda0ab11919428b0b1b027900f594d))
100
+ * **test:** changed docstring expected in help test ([fed9342](https://github.com/CHIMEFRB/datatrail-cli/commit/fed9342df20b6843171a8cb4b288899ada74e261))
101
+ * **timeout:** bound every REST call with a request timeout ([fb533a2](https://github.com/CHIMEFRB/datatrail-cli/commit/fb533a2487c744fd18da4857b9ab74bea4615459))
102
+ * **unregistered:** improve pattern matching ([22ff11a](https://github.com/CHIMEFRB/datatrail-cli/commit/22ff11aa6d8aa390e465bf0ef62065f288f05bac))
103
+ * **unregistered:** improve way info displayed ([21e19a7](https://github.com/CHIMEFRB/datatrail-cli/commit/21e19a7ea239667ac882b1d8fbff57a145989227))
104
+ * **unregistered:** improve way info displayed ([c5f0fca](https://github.com/CHIMEFRB/datatrail-cli/commit/c5f0fca6f6f53e5004e1f1868517100572d68d1a))
105
+ * Update cadcdata to version 2.5 ([#58](https://github.com/CHIMEFRB/datatrail-cli/issues/58)) ([8ed6ad3](https://github.com/CHIMEFRB/datatrail-cli/commit/8ed6ad3914850483d147f0605809c1dd76363403))
106
+ * **version:** color ([ac2c7be](https://github.com/CHIMEFRB/datatrail-cli/commit/ac2c7bea886f48cc5fa5b84443999fb6e3809461))
107
+
108
+
109
+ ### Documentation
110
+
111
+ * add unregistered command ([d8dd35b](https://github.com/CHIMEFRB/datatrail-cli/commit/d8dd35b22069b69ff7e3e76b6dc935696f8c2840))
112
+ * **all-docs:** revamp and additional content ([697fa6f](https://github.com/CHIMEFRB/datatrail-cli/commit/697fa6f2d2e150414c034c9d45a461808cd87381))
113
+ * **cli:** started docs ([f1838d5](https://github.com/CHIMEFRB/datatrail-cli/commit/f1838d5864cfed16e5221e9b5affaebb39ec108d))
114
+ * **gh-action:** build docs ([0698516](https://github.com/CHIMEFRB/datatrail-cli/commit/0698516e1670fb7a019ac271707d1d40fa4c5904))
115
+ * **gh-action:** install without dev ([0ea4a92](https://github.com/CHIMEFRB/datatrail-cli/commit/0ea4a92395c4eb04e6e6913679c3fd9b1b0cbd56))
116
+ * **gh-actions:** fix python version ([6db8c76](https://github.com/CHIMEFRB/datatrail-cli/commit/6db8c76298da316b7614d9ce794154ce75b83939))
117
+ * **index.md:** allow comments ([c680000](https://github.com/CHIMEFRB/datatrail-cli/commit/c6800007925c5794a5f7c29a63934c904cdaea07))
118
+ * **index:** add clear command ([01fcfb5](https://github.com/CHIMEFRB/datatrail-cli/commit/01fcfb5df89a8c567f743c26cc2342ccad93c632))
119
+ * **index:** badge ([09c637e](https://github.com/CHIMEFRB/datatrail-cli/commit/09c637eea007d0176a0ca0abe7524ca9f5c8a667))
120
+ * **index:** rewording ([6e7b2d1](https://github.com/CHIMEFRB/datatrail-cli/commit/6e7b2d1985233595374dc6d311b7b34389f92344))
121
+ * **index:** small fixes ([6483813](https://github.com/CHIMEFRB/datatrail-cli/commit/6483813033d71281a973dc52d250ae4e37a2df9e))
122
+ * **index:** update commands available ([aec676d](https://github.com/CHIMEFRB/datatrail-cli/commit/aec676d576082e288ee7144c39e34eb289dc8946))
123
+ * **index:** update install instructions ([fd7f51c](https://github.com/CHIMEFRB/datatrail-cli/commit/fd7f51c64d8f4b97f23d2b2b5897b34809a1bee3))
124
+ * **list:** test terminal plug in ([2cb4bc0](https://github.com/CHIMEFRB/datatrail-cli/commit/2cb4bc00ae89bdd1a7321ca2bd887da556225641))
125
+ * **README-and-index:** updated ([97353b3](https://github.com/CHIMEFRB/datatrail-cli/commit/97353b3c7f94a199363853b1622b930064ea085f))
126
+ * **scout:** added for new cli command ([be4f2bf](https://github.com/CHIMEFRB/datatrail-cli/commit/be4f2bfda25000b453b9696b2137e6bf0e8e4dd6))
127
+ * **scout:** reflect changes to cli in docs ([797eeac](https://github.com/CHIMEFRB/datatrail-cli/commit/797eeac4874619298c22f8f8a7623e079ac6542d))
128
+ * **scout:** typos ([7f633b1](https://github.com/CHIMEFRB/datatrail-cli/commit/7f633b17d514303d260990e1ece849d1c489ef91))
129
+ * **style:** use termynal ([e908d47](https://github.com/CHIMEFRB/datatrail-cli/commit/e908d4786e63972bef9f712b6c740088c8ac7906))
130
+ * update mkdocs config + ci/cd ([3a1014e](https://github.com/CHIMEFRB/datatrail-cli/commit/3a1014e75b0639f991cb49baf826bd925455b2f4))
131
+ * update mkdocs config + ci/cd ([0e4bee0](https://github.com/CHIMEFRB/datatrail-cli/commit/0e4bee0efbf9cd28fe4964497b5b0b7492efc3c2))
132
+ * **user-guide:** command highlights ([6813964](https://github.com/CHIMEFRB/datatrail-cli/commit/6813964e361391a8cbd4558b7c9bcda32c42cd7c))
133
+ * **user-guide:** completed ([a25ac76](https://github.com/CHIMEFRB/datatrail-cli/commit/a25ac767f8fee8bb22d100358481090c186f8e1f))
134
+
3
135
  ## [0.12.0](https://github.com/CHIMEFRB/datatrail-cli/compare/v0.11.1...v0.12.0) (2026-08-24)
4
136
 
5
137
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datatrail-cli
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: CHIME/FRB Datatrail CLI
5
5
  Author: CHIME FRB Project Office
6
6
  Author-email: Tarik Zegmott <tarik.zegmott@mcgill.ca>
@@ -11,6 +11,7 @@ from dtcli.utilities import utilities
11
11
 
12
12
  pretty.install()
13
13
  terminal = console.Console()
14
+ terminal_stderr = console.Console(stderr=True)
14
15
 
15
16
 
16
17
  # Main CLI
@@ -50,15 +51,19 @@ cli.add_command(unregistered.unregistered)
50
51
 
51
52
 
52
53
  def check_version() -> None:
53
- """Check if CLI is latest release."""
54
+ """Check if CLI is latest release.
55
+
56
+ The banner goes to stderr so that stdout stays parseable, e.g. for
57
+ `--json` output piped into another tool.
58
+ """
54
59
  if not utilities.cli_is_latest_release():
55
60
  current_version = package_version("datatrail-cli")
56
61
  latest_version = utilities.get_latest_released_version()
57
- terminal.print(
62
+ terminal_stderr.print(
58
63
  f"A new release of datatrail-cli is available: {current_version} → {latest_version}", # noqa: E501
59
64
  style="bold yellow",
60
65
  )
61
- terminal.print()
66
+ terminal_stderr.print()
62
67
 
63
68
 
64
69
  if __name__ == "__main__":
@@ -13,7 +13,12 @@ from rich.table import Table
13
13
  from dtcli.config import procure
14
14
  from dtcli.ls import list as ls
15
15
  from dtcli.utilities import cadcclient
16
- from dtcli.utilities.utilities import check_canfar_status, set_log_level, validate_scope
16
+ from dtcli.utilities.utilities import (
17
+ REQUEST_TIMEOUT,
18
+ check_canfar_status,
19
+ set_log_level,
20
+ validate_scope,
21
+ )
17
22
 
18
23
  logger = logging.getLogger("scout")
19
24
 
@@ -89,7 +94,11 @@ def scout( # noqa: C901
89
94
  )
90
95
  url = server + endpoint
91
96
  logger.debug(f"URL: {url}")
92
- response = requests.get(url)
97
+ try:
98
+ response = requests.get(url, timeout=REQUEST_TIMEOUT)
99
+ except requests.exceptions.Timeout:
100
+ error_console.print("Error: Datatrail server timed out.")
101
+ return None
93
102
  try:
94
103
  data = response.json()
95
104
  logger.debug(f"Data: {data}")
@@ -160,14 +169,22 @@ def scout( # noqa: C901
160
169
  + "/query/datasset/scout/md5sums"
161
170
  + f"?basepath={basepath}&site={se}&filetype={file_type}"
162
171
  )
163
- response = requests.get(md5_url)
172
+ try:
173
+ response = requests.get(md5_url, timeout=REQUEST_TIMEOUT)
174
+ except requests.exceptions.Timeout:
175
+ error_console.print(f"{scope} - Healing timed out; skipping.")
176
+ continue
164
177
  file_md5s = response.json()
165
178
  url = (
166
179
  server
167
180
  + "/commit/dataset/scout/sync"
168
181
  + f"?name={dataset}&scope={scope}&replicate_to={se}"
169
182
  )
170
- response = requests.post(url, json=file_md5s)
183
+ try:
184
+ response = requests.post(url, json=file_md5s, timeout=REQUEST_TIMEOUT)
185
+ except requests.exceptions.Timeout:
186
+ error_console.print(f"{scope} - Healing timed out; skipping.")
187
+ continue
171
188
  if response.status_code == 200:
172
189
  console.print(f"{scope} - Healing successful.")
173
190
  else:
@@ -7,6 +7,7 @@ import shutil
7
7
  import subprocess
8
8
  import time
9
9
  from collections import Counter
10
+ from collections.abc import Sequence
10
11
  from pathlib import Path
11
12
  from typing import Any, Dict, List, Optional, Tuple
12
13
 
@@ -54,10 +55,21 @@ def list( # noqa: C901
54
55
  logger.info("Finding all scopes in Datatrail.")
55
56
  try:
56
57
  url = server + "/query/dataset/scopes"
57
- r = requests.get(url)
58
+ r = requests.get(url, timeout=utilities.REQUEST_TIMEOUT)
58
59
  response = utilities.decode_response(r)
60
+ if isinstance(response, str) or not isinstance(response, Sequence):
61
+ # decode_response passes a non-JSON body (a proxy error page,
62
+ # a 5xx message) through as text; report it, or any other
63
+ # non-list shape, as an error instead of presenting it as
64
+ # the scopes list. NB: the builtin list is shadowed by this
65
+ # module's list(), hence the Sequence check.
66
+ logger.error(f"Scopes query not answered: {response}")
67
+ return {"error": "Datatrail did not answer the scopes query."}
59
68
  return {"scopes": response}
60
- except requests.exceptions.ConnectionError as e:
69
+ except (
70
+ requests.exceptions.ConnectionError,
71
+ requests.exceptions.Timeout,
72
+ ) as e:
61
73
  logger.error(e)
62
74
  return {"error": "Datatrail Server at CHIME is not responding."}
63
75
 
@@ -69,13 +81,16 @@ def list( # noqa: C901
69
81
  logger.info("Finding all larger datasets in Datatrail.")
70
82
  try:
71
83
  url = server + f"/query/dataset/larger?scope={scope}"
72
- r = requests.get(url)
84
+ r = requests.get(url, timeout=utilities.REQUEST_TIMEOUT)
73
85
  response = utilities.decode_response(r)
74
86
  if isinstance(response, dict):
75
87
  return response
76
88
  else:
77
89
  raise requests.exceptions.ConnectionError(response)
78
- except requests.exceptions.ConnectionError as error:
90
+ except (
91
+ requests.exceptions.ConnectionError,
92
+ requests.exceptions.Timeout,
93
+ ) as error:
79
94
  logger.error(error)
80
95
  return {"error": f"{error}"}
81
96
 
@@ -85,7 +100,7 @@ def list( # noqa: C901
85
100
  try:
86
101
  url = server + f"/query/dataset/children/{scope}/{dataset}"
87
102
  logger.debug(f"URL: {url}")
88
- r = requests.get(url)
103
+ r = requests.get(url, timeout=utilities.REQUEST_TIMEOUT)
89
104
  logger.debug(f"Status: {r.status_code}.")
90
105
  response = utilities.decode_response(r)
91
106
  logger.debug(f"Reponse: {response}")
@@ -143,7 +158,7 @@ def ps(
143
158
  logger.info(f"Getting policy for {dataset} in {scope}.")
144
159
  url: str = str(base_url) + f"/query/dataset/{scope}/{dataset}"
145
160
  logger.debug(f"URL: {url}")
146
- r = requests.get(url)
161
+ r = requests.get(url, timeout=utilities.REQUEST_TIMEOUT)
147
162
  logger.debug(f"Status: {r.status_code}.")
148
163
  policy_response = utilities.decode_response(r)
149
164
  utilities.validate_request_response(policy_response, dataset, scope)
@@ -192,7 +207,7 @@ def get_dataset_file_info(
192
207
  logger.debug(f"Payload: {payload}")
193
208
  url = str(base_url) + "/query/dataset/find"
194
209
  logger.debug(f"URL: {url}")
195
- r = requests.post(url, json=payload)
210
+ r = requests.post(url, json=payload, timeout=utilities.REQUEST_TIMEOUT)
196
211
  logger.debug(f"Status: {r.status_code}.")
197
212
  logger.debug("Decoding response.")
198
213
  response = utilities.decode_response(r)
@@ -403,7 +418,7 @@ def find_dataset_common_path(
403
418
  url = server + "/query/dataset/find"
404
419
  logger.debug(f"URL: {url}")
405
420
  try:
406
- r = requests.post(url, json=payload)
421
+ r = requests.post(url, json=payload, timeout=utilities.REQUEST_TIMEOUT)
407
422
  dataset_locations = utilities.decode_response(r) # type: ignore
408
423
  utilities.validate_request_response(dataset_locations, dataset, scope)
409
424
  except ConnectionError:
@@ -455,6 +470,7 @@ def view_results(
455
470
  "projection": projection,
456
471
  "limit": limit,
457
472
  },
473
+ timeout=utilities.REQUEST_TIMEOUT,
458
474
  )
459
475
  return response.json()
460
476
 
@@ -416,15 +416,19 @@ def status(
416
416
  certfile = procure(key="vospace_certfile")
417
417
 
418
418
  def check_url(url: str) -> bool:
419
- response = requests.get(url, cert=certfile, allow_redirects=True)
420
419
  try:
420
+ # Health probe: fail fast, and read a wedged or unreachable
421
+ # endpoint as "down" rather than crashing the caller.
422
+ response = requests.get(
423
+ url, cert=certfile, allow_redirects=True, timeout=(10, 30)
424
+ )
421
425
  response.raise_for_status()
422
426
  authorised = response.headers.get("x-vo-authenticated")
423
427
  if isinstance(authorised, str):
424
428
  return True
425
429
  else:
426
430
  raise TypeError
427
- except HTTPError as error:
431
+ except (HTTPError, requests.exceptions.RequestException) as error:
428
432
  logger.warning(error)
429
433
  logger.warning(f"{url.split('/')[3]} is down.")
430
434
  return False
@@ -13,6 +13,11 @@ try:
13
13
  except ImportError:
14
14
  from pip._vendor.packaging.version import parse
15
15
 
16
+ # Bound every REST call: a wedged connection should fail, not hang forever.
17
+ # (connect, read): fail an unreachable host quickly; keep the read bound
18
+ # generous, because some server-side queries are legitimately slow.
19
+ REQUEST_TIMEOUT: Tuple[float, float] = (10.0, 300.0)
20
+
16
21
 
17
22
  def set_log_level(logger: logging.Logger, verbose: int = 0, quiet: bool = False) -> None:
18
23
  """Set log level."""
@@ -98,7 +103,10 @@ def validate_scope(scope: str) -> bool:
98
103
  Returns:
99
104
  bool: True if scope is valid.
100
105
  """
101
- resp = requests.get("https://frb.chimenet.ca/datatrail/query/dataset/scopes")
106
+ resp = requests.get(
107
+ "https://frb.chimenet.ca/datatrail/query/dataset/scopes",
108
+ timeout=REQUEST_TIMEOUT,
109
+ )
102
110
  scopes = decode_response(resp)
103
111
  return scope in scopes
104
112
 
@@ -116,7 +124,8 @@ def get_latest_released_version(
116
124
  Returns:
117
125
  str: Latest released version.
118
126
  """
119
- req = requests.get(url_pattern.format(package=package))
127
+ # Short timeout: this runs at CLI startup and must not delay it.
128
+ req = requests.get(url_pattern.format(package=package), timeout=5)
120
129
  version = parse("0")
121
130
  if req.status_code == requests.codes.ok:
122
131
  j = json.loads(req.text.encode(req.encoding)) # type: ignore
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datatrail-cli"
3
- version = "0.12.0"
3
+ version = "0.13.0"
4
4
  description = "CHIME/FRB Datatrail CLI"
5
5
  authors = [
6
6
  { name = "CHIME FRB Project Office" },
@@ -57,6 +57,11 @@ ignore = "E203"
57
57
  [tool.isort]
58
58
  profile = "black"
59
59
 
60
+ [tool.pytest.ini_options]
61
+ markers = [
62
+ "cadc: tests that require CADC credentials",
63
+ ]
64
+
60
65
  [build-system]
61
66
  requires = ["hatchling"]
62
67
  build-backend = "hatchling.build"
@@ -0,0 +1,15 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "extra-files": [
5
+ {
6
+ "jsonpath": "$.package[?(@.name.value=='datatrail-cli')].version",
7
+ "path": "uv.lock",
8
+ "type": "toml"
9
+ }
10
+ ],
11
+ "package-name": "datatrail-cli"
12
+ }
13
+ },
14
+ "release-type": "python"
15
+ }
@@ -366,6 +366,7 @@ def test_cli_list_children(runner: CliRunner) -> None:
366
366
  assert "289007650" in result.output
367
367
 
368
368
 
369
+ @pytest.mark.cadc
369
370
  def test_cli_ps(runner: CliRunner) -> None:
370
371
  """Test for CLI ps command.
371
372
 
@@ -376,6 +377,7 @@ def test_cli_ps(runner: CliRunner) -> None:
376
377
  assert result.exit_code == 0
377
378
 
378
379
 
380
+ @pytest.mark.cadc
379
381
  def test_cli_pull_no(runner: CliRunner) -> None:
380
382
  """Test for CLI pull command.
381
383
 
@@ -397,6 +399,7 @@ def test_cli_pull_no(runner: CliRunner) -> None:
397
399
  assert expect in result.output
398
400
 
399
401
 
402
+ @pytest.mark.cadc
400
403
  def test_cli_pull_yes(
401
404
  runner: CliRunner, directory: Path, list_specific_files: Path
402
405
  ) -> None:
@@ -427,6 +430,7 @@ def test_cli_pull_yes(
427
430
  ).exists()
428
431
 
429
432
 
433
+ @pytest.mark.cadc
430
434
  def test_cli_clear_no(runner: CliRunner, directory: Path) -> None:
431
435
  """Test for CLI clear command.
432
436
 
@@ -450,6 +454,7 @@ def test_cli_clear_no(runner: CliRunner, directory: Path) -> None:
450
454
  assert "Roger roger, no files deleted" in result.output
451
455
 
452
456
 
457
+ @pytest.mark.cadc
453
458
  def test_cli_clear_yes(runner: CliRunner, directory: Path) -> None:
454
459
  """Test for CLI clear command.
455
460
 
@@ -479,6 +484,7 @@ def test_cli_clear_yes(runner: CliRunner, directory: Path) -> None:
479
484
  ).exists()
480
485
 
481
486
 
487
+ @pytest.mark.cadc
482
488
  def test_cli_pull_force(
483
489
  runner: CliRunner, directory: Path, list_specific_files: Path
484
490
  ) -> None:
@@ -509,6 +515,7 @@ def test_cli_pull_force(
509
515
  ).exists()
510
516
 
511
517
 
518
+ @pytest.mark.cadc
512
519
  def test_cli_clear_force(runner: CliRunner, directory: Path) -> None:
513
520
  """Test for CLI clear command.
514
521
 
@@ -538,6 +545,7 @@ def test_cli_clear_force(runner: CliRunner, directory: Path) -> None:
538
545
  ).exists()
539
546
 
540
547
 
548
+ @pytest.mark.cadc
541
549
  def test_cli_pull_force_2cores(runner: CliRunner, directory: Path) -> None:
542
550
  """Test for CLI pull command.
543
551
 
@@ -674,6 +682,7 @@ def test_cli_list_children_json(runner: CliRunner) -> None:
674
682
  assert "289007650" in output_data["datasets"]
675
683
 
676
684
 
685
+ @pytest.mark.cadc
677
686
  def test_cli_ps_json(runner: CliRunner) -> None:
678
687
  """Test for CLI ps command with JSON output.
679
688
 
@@ -698,3 +707,22 @@ def test_cli_ps_json(runner: CliRunner) -> None:
698
707
  assert "policies" in output_data
699
708
  assert output_data["dataset"] == "289007650"
700
709
  assert output_data["scope"] == "chime.event.baseband.raw"
710
+
711
+
712
+ def test_check_version_banner_on_stderr(monkeypatch, capsys) -> None:
713
+ """Test the update banner goes to stderr, keeping stdout parseable.
714
+
715
+ Args:
716
+ monkeypatch: Pytest monkeypatch fixture.
717
+ capsys: Pytest capture fixture.
718
+ """
719
+ from dtcli import cli as cli_module
720
+
721
+ monkeypatch.setattr(cli_module.utilities, "cli_is_latest_release", lambda: False)
722
+ monkeypatch.setattr(
723
+ cli_module.utilities, "get_latest_released_version", lambda: "99.0.0"
724
+ )
725
+ cli_module.check_version()
726
+ captured = capsys.readouterr()
727
+ assert "A new release of datatrail-cli is available" in captured.err
728
+ assert "A new release of datatrail-cli is available" not in captured.out
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional
5
5
 
6
6
  import pytest
7
7
 
8
+ from dtcli.src import functions
8
9
  from dtcli.src.functions import (
9
10
  find_unregistered_datasets,
10
11
  get_unregistered_dataset,
@@ -88,3 +89,26 @@ def test_find_unregistered_datasets() -> None:
88
89
  def test_find_unregistered_datasets_no_match() -> None:
89
90
  """Test find_unregistered_datasets with an event that is not unregistered."""
90
91
  assert find_unregistered_datasets("not-an-event") == []
92
+
93
+
94
+ def test_list_scopes_unanswered(monkeypatch) -> None:
95
+ """Test a non-list scopes response becomes an error, not a payload."""
96
+
97
+ class _TextResponse:
98
+ status_code = 502
99
+ text = "Bad Gateway"
100
+
101
+ class _DictResponse:
102
+ status_code = 200
103
+
104
+ @staticmethod
105
+ def json() -> Dict[str, Any]:
106
+ return {"detail": "unexpected shape"}
107
+
108
+ monkeypatch.setattr(functions, "procure", lambda: {"server": "http://testserver"})
109
+ for response in (_TextResponse(), _DictResponse()):
110
+ monkeypatch.setattr(
111
+ functions.requests, "get", lambda url, timeout, _r=response: _r
112
+ )
113
+ results: Dict[str, Any] = functions.list()
114
+ assert results == {"error": "Datatrail did not answer the scopes query."}
@@ -1140,7 +1140,7 @@ wheels = [
1140
1140
 
1141
1141
  [[package]]
1142
1142
  name = "datatrail-cli"
1143
- version = "0.11.0"
1143
+ version = "0.13.0"
1144
1144
  source = { editable = "." }
1145
1145
  dependencies = [
1146
1146
  { name = "cadcdata" },
File without changes
File without changes
File without changes