sfapi-client 0.0.8__tar.gz → 0.1.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 (93) hide show
  1. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/workflows/black.yml +1 -1
  2. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/workflows/mkdocs.yml +3 -3
  3. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/workflows/pypi.yml +4 -4
  4. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/workflows/pytest.yml +2 -2
  5. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/workflows/ruff.yml +1 -1
  6. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/PKG-INFO +2 -2
  7. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/requirements-dev.txt +1 -0
  8. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/client.py +7 -0
  9. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/paths.py +4 -1
  10. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/users.py +17 -1
  11. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_models/__init__.py +34 -7
  12. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/client.py +6 -0
  13. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/users.py +17 -1
  14. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/conftest.py +43 -0
  15. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_api.py +4 -5
  16. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_api_async.py +4 -6
  17. sfapi_client-0.1.0/tests/test_client.py +17 -0
  18. sfapi_client-0.1.0/tests/test_client_async.py +19 -0
  19. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_compute.py +18 -19
  20. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_compute_async.py +24 -23
  21. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_groups_async.py +2 -5
  22. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_jobs.py +10 -10
  23. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_jobs_async.py +16 -16
  24. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_paths.py +22 -23
  25. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_paths_async.py +28 -28
  26. sfapi_client-0.1.0/tests/test_projects.py +12 -0
  27. sfapi_client-0.1.0/tests/test_projects_async.py +17 -0
  28. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_resources.py +19 -19
  29. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_resources_async.py +21 -19
  30. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_users.py +28 -17
  31. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_users_async.py +29 -13
  32. sfapi_client-0.0.8/tests/test_client.py +0 -17
  33. sfapi_client-0.0.8/tests/test_client_async.py +0 -19
  34. sfapi_client-0.0.8/tests/test_projects.py +0 -15
  35. sfapi_client-0.0.8/tests/test_projects_async.py +0 -19
  36. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.github/dependabot.yml +0 -0
  37. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.gitignore +0 -0
  38. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/.pre-commit-config.yaml +0 -0
  39. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/LEGAL +0 -0
  40. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/LICENSE +0 -0
  41. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/README.md +0 -0
  42. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/community/contributing.md +0 -0
  43. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/examples/check_current_and_past_jobs.ipynb +0 -0
  44. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/examples/getting_bearer_token.ipynb +0 -0
  45. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/examples/run_job_and_check_status.ipynb +0 -0
  46. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/gen_examples.py +0 -0
  47. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/gen_sync.py +0 -0
  48. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/index.md +0 -0
  49. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/javascript/apiselector.js +0 -0
  50. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/quickstart/index.md +0 -0
  51. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/SUMMARY.txt +0 -0
  52. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/client/index.md +0 -0
  53. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/compute/index.md +0 -0
  54. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/exceptions/index.md +0 -0
  55. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/groups/index.md +0 -0
  56. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/jobs/index.md +0 -0
  57. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/paths/index.md +0 -0
  58. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/projects/index.md +0 -0
  59. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/resources/index.md +0 -0
  60. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/docs/reference/async/users/index.md +0 -0
  61. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/mkdocs.yml +0 -0
  62. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/pyproject.toml +0 -0
  63. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/pytest.ini +0 -0
  64. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/scripts/run.py +0 -0
  65. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/__init__.py +0 -0
  66. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/__init__.py +0 -0
  67. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/compute.py +0 -0
  68. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/groups.py +0 -0
  69. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/jobs.py +0 -0
  70. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_async/projects.py +0 -0
  71. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_compute.py +0 -0
  72. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_jobs.py +0 -0
  73. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_models/job_status_response_sacct.py +0 -0
  74. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_models/job_status_response_squeue.py +0 -0
  75. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_models/resources.py +0 -0
  76. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_monitor.py +0 -0
  77. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/__init__.py +0 -0
  78. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/compute.py +0 -0
  79. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/groups.py +0 -0
  80. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/jobs.py +0 -0
  81. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/paths.py +0 -0
  82. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_sync/projects.py +0 -0
  83. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/_utils.py +0 -0
  84. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/client.py +0 -0
  85. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/compute.py +0 -0
  86. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/exceptions.py +0 -0
  87. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/groups.py +0 -0
  88. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/jobs.py +0 -0
  89. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/paths.py +0 -0
  90. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/projects.py +0 -0
  91. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/resources.py +0 -0
  92. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/src/sfapi_client/users.py +0 -0
  93. {sfapi_client-0.0.8 → sfapi_client-0.1.0}/tests/test_groups.py +0 -0
@@ -10,7 +10,7 @@ jobs:
10
10
  black:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v3
13
+ - uses: actions/checkout@v4
14
14
  - uses: psf/black@23.9.1
15
15
  with:
16
16
  options: "--check --verbose"
@@ -12,11 +12,11 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
14
14
  steps:
15
- - uses: actions/checkout@v3
16
- - uses: actions/setup-python@v4
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
17
  with:
18
18
  python-version: 3.x
19
- - uses: actions/cache@v3
19
+ - uses: actions/cache@v4
20
20
  with:
21
21
  key: mkdocs-${{ github.ref }}
22
22
  path: .cache
@@ -13,9 +13,9 @@ jobs:
13
13
  name: Build wheel
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@v3
16
+ - uses: actions/checkout@v4
17
17
  - name: Set up Python
18
- uses: actions/setup-python@v4
18
+ uses: actions/setup-python@v5
19
19
  with:
20
20
  python-version: "3.x"
21
21
  - name: Install pypa/build
@@ -33,7 +33,7 @@ jobs:
33
33
  --wheel
34
34
  --outdir dist/
35
35
 
36
- - uses: actions/upload-artifact@v3
36
+ - uses: actions/upload-artifact@v4
37
37
  with:
38
38
  name: wheel
39
39
  path: ./dist/
@@ -49,7 +49,7 @@ jobs:
49
49
  id-token: write
50
50
  if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
51
51
  steps:
52
- - uses: actions/download-artifact@v3
52
+ - uses: actions/download-artifact@v4
53
53
  with:
54
54
  name: wheel
55
55
  path: dist
@@ -16,9 +16,9 @@ jobs:
16
16
  python-version: ["3.8", "3.9", "3.10", "3.11"]
17
17
 
18
18
  steps:
19
- - uses: actions/checkout@v3
19
+ - uses: actions/checkout@v4
20
20
  - name: Set up Python ${{ matrix.python-version }}
21
- uses: actions/setup-python@v3
21
+ uses: actions/setup-python@v5
22
22
  with:
23
23
  python-version: ${{ matrix.python-version }}
24
24
  - name: Install dependencies
@@ -10,5 +10,5 @@ jobs:
10
10
  ruff:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v3
13
+ - uses: actions/checkout@v4
14
14
  - uses: chartboost/ruff-action@v1
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: sfapi_client
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: Python client for NERSC SF API
5
5
  Author-email: Chris Harris <cjh@lbl.gov>, Nicholas Tyler <tylern@lbl.gov>
6
6
  License-File: LICENSE
@@ -3,5 +3,6 @@ unasync
3
3
  typer
4
4
  pytest
5
5
  pytest-asyncio
6
+ pytest-mock
6
7
  mkdocs-jupyter
7
8
  datamodel-code-generator[http]
@@ -512,3 +512,10 @@ AsyncProject.model_rebuild()
512
512
  AsyncRemotePath.model_rebuild()
513
513
  AsyncRole.model_rebuild()
514
514
  AsyncGroupMember.model_rebuild()
515
+
516
+ # Ensure that the job models are built, we need to import here to
517
+ # avoid circular imports
518
+ from .jobs import AsyncJobSacct, AsyncJobSqueue # noqa: E402
519
+
520
+ AsyncJobSqueue.model_rebuild()
521
+ AsyncJobSacct.model_rebuild()
@@ -165,7 +165,10 @@ class AsyncRemotePath(PathBase):
165
165
 
166
166
  @staticmethod
167
167
  async def _ls(
168
- compute: "Compute", path, directory=False, filter_dots=True # noqa: F821
168
+ compute: "AsyncCompute", # noqa: F821
169
+ path,
170
+ directory=False,
171
+ filter_dots=True, # noqa: F821
169
172
  ) -> List["RemotePath"]: # noqa: F821
170
173
  r = await compute.client.get(f"utilities/ls/{compute.name}/{path}")
171
174
 
@@ -6,6 +6,7 @@ from pydantic import ConfigDict
6
6
  from .._models import (
7
7
  UserInfo as UserBase,
8
8
  GroupList as GroupsResponse,
9
+ Client as APIClient,
9
10
  )
10
11
  from .projects import AsyncProject, AsyncRole
11
12
  from ..exceptions import SfApiError
@@ -31,7 +32,8 @@ class AsyncUser(UserBase):
31
32
  @staticmethod
32
33
  @check_auth
33
34
  async def _fetch_user(
34
- client: "AsyncClient", username: Optional[str] = None # noqa: F821
35
+ client: "AsyncClient", # noqa: F821
36
+ username: Optional[str] = None, # noqa: F821
35
37
  ): # noqa: F821
36
38
  url = "account/"
37
39
  if username is not None:
@@ -106,3 +108,17 @@ class AsyncUser(UserBase):
106
108
  ]
107
109
 
108
110
  return roles
111
+
112
+ async def clients(self) -> List[APIClient]:
113
+ """
114
+ Get information about the authenticated user's SFAPI clients.
115
+
116
+ :return: the api clients
117
+ """
118
+ r = await self.client.get("account/clients")
119
+
120
+ json_response = r.json()
121
+
122
+ clients = [APIClient.model_validate(c) for c in json_response]
123
+
124
+ return clients
@@ -1,12 +1,12 @@
1
1
  # generated by datamodel-codegen:
2
- # filename: https://api.nersc.gov/api/v1.2/openapi.json
3
- # timestamp: 2023-10-20T20:57:45+00:00
2
+ # filename: https://api-dev.nersc.gov/api/v1.2/openapi.json
3
+ # timestamp: 2024-07-18T15:58:27+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from datetime import date as date_, datetime
8
8
  from enum import Enum
9
- from typing import Dict, List, Optional
9
+ from typing import Any, Dict, List, Optional, Union
10
10
 
11
11
  from pydantic import BaseModel, Field
12
12
 
@@ -37,6 +37,9 @@ class BodySubmitJobComputeJobsMachinePost(BaseModel):
37
37
  isPath: bool = Field(..., title="Ispath")
38
38
  job: str = Field(..., title="Job")
39
39
  args: Optional[List[str]] = Field([], title="Args")
40
+ callbackTimeout: Optional[int] = Field(None, title="Callbacktimeout")
41
+ callbackEmail: Optional[str] = Field(None, title="Callbackemail")
42
+ callbackUrl: Optional[str] = Field(None, title="Callbackurl")
40
43
 
41
44
 
42
45
  class BodyUpdateGroupMembershipAccountGroupsGroupPut(BaseModel):
@@ -53,6 +56,13 @@ class Changelog(BaseModel):
53
56
  change: Optional[str] = Field(None, title="Change")
54
57
 
55
58
 
59
+ class Client(BaseModel):
60
+ name: str = Field(..., title="Name")
61
+ clientId: str = Field(..., title="Clientid")
62
+ expiresAt: str = Field(..., title="Expiresat")
63
+ comments: Optional[str] = Field(None, title="Comments")
64
+
65
+
56
66
  class Config(BaseModel):
57
67
  key: str = Field(..., title="Key")
58
68
  value: Optional[str] = Field(None, title="Value")
@@ -88,6 +98,7 @@ class Outage(BaseModel):
88
98
 
89
99
  class PublicHost(str, Enum):
90
100
  dtn01 = "dtn01"
101
+ dtns = "dtns"
91
102
  perlmutter = "perlmutter"
92
103
 
93
104
 
@@ -117,8 +128,8 @@ class StorageStats(BaseModel):
117
128
 
118
129
  class Task(BaseModel):
119
130
  id: str = Field(..., title="Id")
120
- status: Optional[str] = Field(None, title="Status")
121
- result: Optional[str] = Field(None, title="Result")
131
+ status: Optional[str] = Field(..., title="Status")
132
+ result: Optional[str] = Field(..., title="Result")
122
133
 
123
134
 
124
135
  class Tasks(BaseModel):
@@ -142,7 +153,7 @@ class UserStats(BaseModel):
142
153
 
143
154
 
144
155
  class ValidationError(BaseModel):
145
- loc: List[str] = Field(..., title="Location")
156
+ loc: List[Union[str, int]] = Field(..., title="Location")
146
157
  msg: str = Field(..., title="Message")
147
158
  type: str = Field(..., title="Error Type")
148
159
 
@@ -212,6 +223,22 @@ class ProjectStats(BaseModel):
212
223
  hours_used: Optional[float] = Field(None, title="Hours Used")
213
224
  project_hours_given: Optional[float] = Field(None, title="Project Hours Given")
214
225
  project_hours_used: Optional[float] = Field(None, title="Project Hours Used")
226
+ cpu_hours_given: Optional[float] = Field(None, title="Cpu Hours Given")
227
+ cpu_hours_used: Optional[float] = Field(None, title="Cpu Hours Used")
228
+ cpu_project_hours_given: Optional[float] = Field(
229
+ None, title="Cpu Project Hours Given"
230
+ )
231
+ cpu_project_hours_used: Optional[float] = Field(
232
+ None, title="Cpu Project Hours Used"
233
+ )
234
+ gpu_hours_given: Optional[float] = Field(None, title="Gpu Hours Given")
235
+ gpu_hours_used: Optional[float] = Field(None, title="Gpu Hours Used")
236
+ gpu_project_hours_given: Optional[float] = Field(
237
+ None, title="Gpu Project Hours Given"
238
+ )
239
+ gpu_project_hours_used: Optional[float] = Field(
240
+ None, title="Gpu Project Hours Used"
241
+ )
215
242
  projdir_usage: Optional[List[StorageStats]] = Field(None, title="Projdir Usage")
216
243
  project_projdir_usage: Optional[StorageStats] = None
217
244
  hpss_usage: Optional[List[StorageStats]] = Field(None, title="Hpss Usage")
@@ -226,7 +253,7 @@ class QueueOutput(BaseModel):
226
253
  class TransferResult(BaseModel):
227
254
  task_id: str = Field(..., title="Task Id")
228
255
  status: AppRoutersStorageModelsStatus
229
- reason: Optional[str] = Field(None, title="Reason")
256
+ reason: Optional[str] = Field(..., title="Reason")
230
257
 
231
258
 
232
259
  class UploadResult(BaseModel):
@@ -512,3 +512,9 @@ Project.model_rebuild()
512
512
  RemotePath.model_rebuild()
513
513
  Role.model_rebuild()
514
514
  GroupMember.model_rebuild()
515
+
516
+ # Ensure that the job models are built, we need to import here to
517
+ # avoid circular imports
518
+ from .jobs import JobSacct, JobSqueue
519
+ JobSqueue.model_rebuild()
520
+ JobSacct.model_rebuild()
@@ -6,6 +6,7 @@ from pydantic import ConfigDict
6
6
  from .._models import (
7
7
  UserInfo as UserBase,
8
8
  GroupList as GroupsResponse,
9
+ Client as APIClient,
9
10
  )
10
11
  from .projects import Project, Role
11
12
  from ..exceptions import SfApiError
@@ -31,7 +32,8 @@ class User(UserBase):
31
32
  @staticmethod
32
33
  @check_auth
33
34
  def _fetch_user(
34
- client: "Client", username: Optional[str] = None # noqa: F821
35
+ client: "Client", # noqa: F821
36
+ username: Optional[str] = None, # noqa: F821
35
37
  ): # noqa: F821
36
38
  url = "account/"
37
39
  if username is not None:
@@ -106,3 +108,17 @@ class User(UserBase):
106
108
  ]
107
109
 
108
110
  return roles
111
+
112
+ def clients(self) -> List[APIClient]:
113
+ """
114
+ Get information about the authenticated user's SFAPI clients.
115
+
116
+ :return: the api clients
117
+ """
118
+ r = self.client.get("account/clients")
119
+
120
+ json_response = r.json()
121
+
122
+ clients = [APIClient.model_validate(c) for c in json_response]
123
+
124
+ return clients
@@ -8,12 +8,15 @@ from authlib.jose import JsonWebKey
8
8
 
9
9
  from sfapi_client.compute import Machine
10
10
  from sfapi_client import Resource
11
+ from sfapi_client import Client, AsyncClient
11
12
 
12
13
  from pydantic import ConfigDict
13
14
  from pydantic_settings import BaseSettings
14
15
 
15
16
 
16
17
  class Settings(BaseSettings):
18
+ API_BASE_URL: str = "https://api.nersc.gov/api/v1.2"
19
+ TOKEN_URL: str = "https://oidc.nersc.gov/c2id/token"
17
20
  SFAPI_CLIENT_ID: Optional[str] = None
18
21
  SFAPI_CLIENT_SECRET: Optional[Union[str, Dict]] = None
19
22
  SFAPI_DEV_CLIENT_ID: Optional[str] = None
@@ -125,3 +128,43 @@ def dev_api_url():
125
128
  @pytest.fixture
126
129
  def dev_token_url():
127
130
  return settings.DEV_TOKEN_URL
131
+
132
+
133
+ @pytest.fixture
134
+ def api_base_url():
135
+ return settings.API_BASE_URL
136
+
137
+
138
+ @pytest.fixture
139
+ def token_url():
140
+ return settings.TOKEN_URL
141
+
142
+
143
+ @pytest.fixture
144
+ def unauthenticated_client(api_base_url):
145
+ return Client(api_base_url=api_base_url)
146
+
147
+
148
+ @pytest.fixture
149
+ def async_unauthenticated_client(api_base_url):
150
+ return AsyncClient(api_base_url=api_base_url)
151
+
152
+
153
+ @pytest.fixture
154
+ def authenticated_client(api_base_url, token_url, client_id, client_secret):
155
+ return Client(
156
+ api_base_url=api_base_url,
157
+ token_url=token_url,
158
+ client_id=client_id,
159
+ secret=client_secret,
160
+ )
161
+
162
+
163
+ @pytest.fixture
164
+ def async_authenticated_client(api_base_url, token_url, client_id, client_secret):
165
+ return AsyncClient(
166
+ api_base_url=api_base_url,
167
+ token_url=token_url,
168
+ client_id=client_id,
169
+ secret=client_secret,
170
+ )
@@ -1,16 +1,15 @@
1
1
  import pytest
2
- from sfapi_client import Client
3
2
 
4
3
 
5
4
  @pytest.mark.public
6
- def test_changelog():
7
- with Client() as client:
5
+ def test_changelog(unauthenticated_client):
6
+ with unauthenticated_client as client:
8
7
  changelog = client.api.changelog()
9
8
  assert len(changelog) > 0
10
9
 
11
10
 
12
11
  @pytest.mark.public
13
- def test_config():
14
- with Client() as client:
12
+ def test_config(unauthenticated_client):
13
+ with unauthenticated_client as client:
15
14
  config = client.api.config()
16
15
  assert "compute.targets" in config
@@ -1,19 +1,17 @@
1
1
  import pytest
2
2
 
3
- from sfapi_client import AsyncClient
4
-
5
3
 
6
4
  @pytest.mark.public
7
5
  @pytest.mark.asyncio
8
- async def test_changelog():
9
- async with AsyncClient() as client:
6
+ async def test_changelog(async_unauthenticated_client):
7
+ async with async_unauthenticated_client as client:
10
8
  changelog = await client.api.changelog()
11
9
  assert len(changelog) > 0
12
10
 
13
11
 
14
12
  @pytest.mark.public
15
13
  @pytest.mark.asyncio
16
- async def test_config():
17
- async with AsyncClient() as client:
14
+ async def test_config(async_unauthenticated_client):
15
+ async with async_unauthenticated_client as client:
18
16
  config = await client.api.config()
19
17
  assert "compute.targets" in config
@@ -0,0 +1,17 @@
1
+ import pytest
2
+
3
+ from sfapi_client import SfApiError
4
+
5
+
6
+ @pytest.mark.public
7
+ def test_no_creds(unauthenticated_client):
8
+ with unauthenticated_client as client:
9
+ assert client is not None
10
+
11
+
12
+ @pytest.mark.public
13
+ def test_no_creds_auth_required(unauthenticated_client, test_machine):
14
+ with unauthenticated_client as client:
15
+ machine = client.compute(test_machine)
16
+ with pytest.raises(SfApiError):
17
+ machine.jobs()
@@ -0,0 +1,19 @@
1
+ import pytest
2
+
3
+ from sfapi_client import SfApiError
4
+
5
+
6
+ @pytest.mark.public
7
+ @pytest.mark.asyncio
8
+ async def test_no_creds(async_unauthenticated_client):
9
+ async with async_unauthenticated_client as client:
10
+ assert client is not None
11
+
12
+
13
+ @pytest.mark.public
14
+ @pytest.mark.asyncio
15
+ async def test_no_creds_auth_required(async_unauthenticated_client, test_machine):
16
+ async with async_unauthenticated_client as client:
17
+ machine = await client.compute(test_machine)
18
+ with pytest.raises(SfApiError):
19
+ await machine.jobs()
@@ -1,18 +1,17 @@
1
1
  from pathlib import Path
2
2
 
3
- from sfapi_client import Client
4
3
  from sfapi_client.jobs import JobState
5
4
 
6
5
 
7
- def test_compute(client_id, client_secret, test_machine):
8
- with Client(client_id, client_secret) as client:
6
+ def test_compute(authenticated_client, test_machine):
7
+ with authenticated_client as client:
9
8
  machine = client.compute(test_machine)
10
9
  assert machine.status in ["active", "unavailable", "degraded", "other"]
11
10
  assert machine.name == test_machine.value
12
11
 
13
12
 
14
- def test_job(client_id, client_secret, test_job_path, test_machine):
15
- with Client(client_id, client_secret) as client:
13
+ def test_job(authenticated_client, test_job_path, test_machine):
14
+ with authenticated_client as client:
16
15
  machine = client.compute(test_machine)
17
16
 
18
17
  job = machine.submit_job(test_job_path)
@@ -24,14 +23,14 @@ def test_job(client_id, client_secret, test_job_path, test_machine):
24
23
  assert job.jobid == job_looked_up.jobid
25
24
 
26
25
 
27
- def test_fetch_jobs(client_id, client_secret, test_machine, test_username):
28
- with Client(client_id, client_secret) as client:
26
+ def test_fetch_jobs(authenticated_client, test_machine, test_username):
27
+ with authenticated_client as client:
29
28
  machine = client.compute(test_machine)
30
29
  machine.jobs(user=test_username)
31
30
 
32
31
 
33
- def test_list_dir_contents(client_id, client_secret, test_machine, test_job_path):
34
- with Client(client_id, client_secret) as client:
32
+ def test_list_dir_contents(authenticated_client, test_machine, test_job_path):
33
+ with authenticated_client as client:
35
34
  machine = client.compute(test_machine)
36
35
  test_job = Path(test_job_path)
37
36
  test_path = test_job.parent
@@ -48,8 +47,8 @@ def test_list_dir_contents(client_id, client_secret, test_machine, test_job_path
48
47
  assert found
49
48
 
50
49
 
51
- def test_list_dir(client_id, client_secret, test_machine, test_job_path):
52
- with Client(client_id, client_secret) as client:
50
+ def test_list_dir(authenticated_client, test_machine, test_job_path):
51
+ with authenticated_client as client:
53
52
  machine = client.compute(test_machine)
54
53
  test_job = Path(test_job_path)
55
54
  test_path = test_job.parent
@@ -59,8 +58,8 @@ def test_list_dir(client_id, client_secret, test_machine, test_job_path):
59
58
  assert paths[0].name == test_path.name
60
59
 
61
60
 
62
- def test_list_file(client_id, client_secret, test_machine, test_job_path):
63
- with Client(client_id, client_secret) as client:
61
+ def test_list_file(authenticated_client, test_machine, test_job_path):
62
+ with authenticated_client as client:
64
63
  machine = client.compute(test_machine)
65
64
  test_job_filename = Path(test_job_path).name
66
65
 
@@ -75,24 +74,24 @@ def test_list_file(client_id, client_secret, test_machine, test_job_path):
75
74
  assert found
76
75
 
77
76
 
78
- def test_run_arg_list(client_id, client_secret, test_machine, test_job_path):
79
- with Client(client_id, client_secret) as client:
77
+ def test_run_arg_list(authenticated_client, test_machine, test_job_path):
78
+ with authenticated_client as client:
80
79
  machine = client.compute(test_machine)
81
80
  output = machine.run(["ls", test_job_path])
82
81
 
83
82
  assert test_job_path in output
84
83
 
85
84
 
86
- def test_run_arg_str(client_id, client_secret, test_machine, test_job_path):
87
- with Client(client_id, client_secret) as client:
85
+ def test_run_arg_str(authenticated_client, test_machine, test_job_path):
86
+ with authenticated_client as client:
88
87
  machine = client.compute(test_machine)
89
88
  output = machine.run(f"ls {test_job_path}")
90
89
 
91
90
  assert test_job_path in output
92
91
 
93
92
 
94
- def test_run_arg_path(client_id, client_secret, test_machine):
95
- with Client(client_id, client_secret) as client:
93
+ def test_run_arg_path(authenticated_client, test_machine):
94
+ with authenticated_client as client:
96
95
  machine = client.compute(test_machine)
97
96
  [remote_path] = machine.ls("/usr/bin/hostname")
98
97
  output = machine.run(remote_path)
@@ -1,21 +1,20 @@
1
1
  import pytest
2
2
  from pathlib import Path
3
3
 
4
- from sfapi_client import AsyncClient
5
4
  from sfapi_client.jobs import JobState
6
5
 
7
6
 
8
7
  @pytest.mark.asyncio
9
- async def test_compute(client_id, client_secret, test_machine):
10
- async with AsyncClient(client_id, client_secret) as client:
8
+ async def test_compute(async_authenticated_client, test_machine):
9
+ async with async_authenticated_client as client:
11
10
  machine = await client.compute(test_machine)
12
11
  assert machine.status in ["active", "unavailable", "degraded", "other"]
13
12
  assert machine.name == test_machine.value
14
13
 
15
14
 
16
15
  @pytest.mark.asyncio
17
- async def test_job(client_id, client_secret, test_job_path, test_machine):
18
- async with AsyncClient(client_id, client_secret) as client:
16
+ async def test_job(async_authenticated_client, test_job_path, test_machine):
17
+ async with async_authenticated_client as client:
19
18
  machine = await client.compute(test_machine)
20
19
 
21
20
  job = await machine.submit_job(test_job_path)
@@ -28,15 +27,17 @@ async def test_job(client_id, client_secret, test_job_path, test_machine):
28
27
 
29
28
 
30
29
  @pytest.mark.asyncio
31
- async def test_fetch_jobs(client_id, client_secret, test_machine, test_username):
32
- async with AsyncClient(client_id, client_secret) as client:
30
+ async def test_fetch_jobs(async_authenticated_client, test_machine, test_username):
31
+ async with async_authenticated_client as client:
33
32
  machine = await client.compute(test_machine)
34
33
  await machine.jobs(user=test_username)
35
34
 
36
35
 
37
36
  @pytest.mark.asyncio
38
- async def test_list_dir_contents(client_id, client_secret, test_machine, test_job_path):
39
- async with AsyncClient(client_id, client_secret) as client:
37
+ async def test_list_dir_contents(
38
+ async_authenticated_client, test_machine, test_job_path
39
+ ):
40
+ async with async_authenticated_client as client:
40
41
  machine = await client.compute(test_machine)
41
42
  test_job = Path(test_job_path)
42
43
  test_path = test_job.parent
@@ -54,8 +55,8 @@ async def test_list_dir_contents(client_id, client_secret, test_machine, test_jo
54
55
 
55
56
 
56
57
  @pytest.mark.asyncio
57
- async def test_list_dir(client_id, client_secret, test_machine, test_job_path):
58
- async with AsyncClient(client_id, client_secret) as client:
58
+ async def test_list_dir(async_authenticated_client, test_machine, test_job_path):
59
+ async with async_authenticated_client as client:
59
60
  machine = await client.compute(test_machine)
60
61
  test_job = Path(test_job_path)
61
62
  test_path = test_job.parent
@@ -66,8 +67,8 @@ async def test_list_dir(client_id, client_secret, test_machine, test_job_path):
66
67
 
67
68
 
68
69
  @pytest.mark.asyncio
69
- async def test_list_file(client_id, client_secret, test_machine, test_job_path):
70
- async with AsyncClient(client_id, client_secret) as client:
70
+ async def test_list_file(async_authenticated_client, test_machine, test_job_path):
71
+ async with async_authenticated_client as client:
71
72
  machine = await client.compute(test_machine)
72
73
  test_job_filename = Path(test_job_path).name
73
74
  test_job_dir = Path(test_job_path).parent
@@ -84,8 +85,8 @@ async def test_list_file(client_id, client_secret, test_machine, test_job_path):
84
85
 
85
86
 
86
87
  @pytest.mark.asyncio
87
- async def test_run_arg_list(client_id, client_secret, test_machine, test_job_path):
88
- async with AsyncClient(client_id, client_secret) as client:
88
+ async def test_run_arg_list(async_authenticated_client, test_machine, test_job_path):
89
+ async with async_authenticated_client as client:
89
90
  machine = await client.compute(test_machine)
90
91
  output = await machine.run(["ls", test_job_path])
91
92
 
@@ -93,8 +94,8 @@ async def test_run_arg_list(client_id, client_secret, test_machine, test_job_pat
93
94
 
94
95
 
95
96
  @pytest.mark.asyncio
96
- async def test_run_arg_str(client_id, client_secret, test_machine, test_job_path):
97
- async with AsyncClient(client_id, client_secret) as client:
97
+ async def test_run_arg_str(async_authenticated_client, test_machine, test_job_path):
98
+ async with async_authenticated_client as client:
98
99
  machine = await client.compute(test_machine)
99
100
  output = await machine.run(f"ls {test_job_path}")
100
101
 
@@ -102,8 +103,8 @@ async def test_run_arg_str(client_id, client_secret, test_machine, test_job_path
102
103
 
103
104
 
104
105
  @pytest.mark.asyncio
105
- async def test_run_arg_path(client_id, client_secret, test_machine):
106
- async with AsyncClient(client_id, client_secret) as client:
106
+ async def test_run_arg_path(async_authenticated_client, test_machine):
107
+ async with async_authenticated_client as client:
107
108
  machine = await client.compute(test_machine)
108
109
  [remote_path] = await machine.ls("/usr/bin/hostname")
109
110
  output = await machine.run(remote_path)
@@ -112,8 +113,8 @@ async def test_run_arg_path(client_id, client_secret, test_machine):
112
113
 
113
114
 
114
115
  @pytest.mark.asyncio
115
- async def test_outages(client_id, client_secret, test_machine):
116
- async with AsyncClient(client_id, client_secret) as client:
116
+ async def test_outages(async_authenticated_client, test_machine):
117
+ async with async_authenticated_client as client:
117
118
  machine = await client.compute(test_machine)
118
119
  outages = await machine.outages()
119
120
 
@@ -121,8 +122,8 @@ async def test_outages(client_id, client_secret, test_machine):
121
122
 
122
123
 
123
124
  @pytest.mark.asyncio
124
- async def test_planned_outages(client_id, client_secret, test_machine):
125
- async with AsyncClient(client_id, client_secret) as client:
125
+ async def test_planned_outages(async_authenticated_client, test_machine):
126
+ async with async_authenticated_client as client:
126
127
  machine = await client.compute(test_machine)
127
128
  outages = await machine.planned_outages()
128
129