snap-python 0.0.4__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
snap_python/client.py CHANGED
@@ -27,6 +27,7 @@ class SnapClient(AbstractSnapsClient):
27
27
  tcp_location: str = None,
28
28
  store_base_url: str = "https://api.snapcraft.io",
29
29
  store_headers: dict[str, str] = None,
30
+ prompt_for_authentication: bool = False,
30
31
  ):
31
32
  if tcp_location and snapd_socket_location:
32
33
  raise ValueError(
@@ -40,14 +41,19 @@ class SnapClient(AbstractSnapsClient):
40
41
  self._transport = httpx.AsyncHTTPTransport(
41
42
  uds=snapd_socket_location or SNAPD_SOCKET
42
43
  )
43
-
44
44
  if store_headers is None:
45
45
  store_headers = {"Snap-Device-Series": "16", "X-Ubuntu-Series": "16"}
46
46
 
47
+ self.snapd_headers = {}
48
+ if prompt_for_authentication:
49
+ self.snapd_headers = {"X-Allow-Interaction": "true"}
50
+
47
51
  self.version = version
48
52
  self.store_base_url = store_base_url
49
53
  self.store_headers = store_headers
50
- self.snapd_client = httpx.AsyncClient(transport=self._transport)
54
+ self.snapd_client = httpx.AsyncClient(
55
+ transport=self._transport, headers=self.snapd_headers
56
+ )
51
57
  self.snaps = SnapsEndpoints(self)
52
58
  self.store = StoreEndpoints(
53
59
  base_url=self.store_base_url,
@@ -55,6 +61,7 @@ class SnapClient(AbstractSnapsClient):
55
61
  headers=self.store_headers,
56
62
  )
57
63
  self.config = ConfigEndpoints(self)
64
+ self.snapd_client = httpx.AsyncClient(transport=self._transport)
58
65
 
59
66
  @retry(httpx.HTTPError, tries=3, delay=1, backoff=1)
60
67
  async def request(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
@@ -105,30 +112,54 @@ class SnapClient(AbstractSnapsClient):
105
112
 
106
113
  return response
107
114
 
115
+ @retry(httpx.HTTPError, tries=3, delay=0.5)
108
116
  async def get_changes_by_id(self, change_id: str) -> ChangesResponse:
117
+ """
118
+ Asynchronously retrieves changes by their ID.
119
+ Args:
120
+ change_id (str): The ID of the change to retrieve.
121
+ Returns:
122
+ ChangesResponse: The response object containing the changes.
123
+ Raises:
124
+ httpx.HTTPStatusError: If the request results in an HTTP error.
125
+ """
126
+
109
127
  try:
110
128
  response = await self.request("GET", f"changes/{change_id}")
111
129
  except httpx.HTTPStatusError as e:
112
130
  logger.warning(
113
- f"Bad response status code for get_changes_by_id: {e.response.status_code}"
131
+ "Bad response status code for get_changes_by_id: %s",
132
+ e.response.status_code,
114
133
  )
115
134
  response = e.response
116
- pass
117
135
 
118
136
  return ChangesResponse.model_validate_json(response.content)
119
137
 
120
138
  async def get_changes_by_id_generator(
121
139
  self, change_id: str
122
140
  ) -> AsyncGenerator[ChangesResponse, None]:
141
+ """
142
+ Asynchronous generator to fetch changes by change ID.
143
+ This generator continuously requests changes from the server using the provided
144
+ change ID and yields the response as a ChangesResponse object. It includes error
145
+ handling for HTTP status errors and adds a small delay between requests to avoid
146
+ overwhelming the server.
147
+ Args:
148
+ change_id (str): The ID of the change to fetch.
149
+ Yields:
150
+ ChangesResponse: The response object containing the changes.
151
+ Raises:
152
+ httpx.HTTPStatusError: If the request fails with an HTTP status error.
153
+ """
123
154
  while True:
124
155
  try:
125
156
  response = await self.request("GET", f"changes/{change_id}")
126
157
  except httpx.HTTPStatusError as e:
127
158
  logger.warning(
128
- f"Bad response status code for get_changes_by_id: {e.response.status_code}"
159
+ "Bad response status code for get_changes_by_id: %s",
160
+ e.response.status_code,
129
161
  )
130
162
  response = e.response
131
- pass
132
163
 
133
164
  change_response = ChangesResponse.model_validate_json(response.content)
134
165
  yield change_response
@@ -10,7 +10,7 @@ from snap_python.schemas.snaps import (
10
10
  InstalledSnapListResponse,
11
11
  SingleInstalledSnapResponse,
12
12
  )
13
- from snap_python.utils import AbstractSnapsClient, going_to_reload_daemon
13
+ from snap_python.utils import AbstractSnapsClient, SnapdAPIError, going_to_reload_daemon
14
14
 
15
15
  logger = logging.getLogger("snap_python.components.snaps")
16
16
 
@@ -60,8 +60,8 @@ class SnapsEndpoints:
60
60
  snap: str,
61
61
  channel: str = "stable",
62
62
  classic: bool = False,
63
+ dangerous: bool = False,
63
64
  devmode: bool = False,
64
- ignore_validation: bool = False,
65
65
  jailmode: bool = False,
66
66
  revision: int = None,
67
67
  filename: str = None,
@@ -76,15 +76,15 @@ class SnapsEndpoints:
76
76
  snap (str): name of the snap to install
77
77
  channel (str, optional): Channel to install. Defaults to "stable".
78
78
  classic (bool, optional): Install with classic confinement. Defaults to False.
79
+ dangerous (bool, optional): install the given snap files even if there are no pre-acknowledged signatures for them, meaning they are not verified and could be dangerous if true (optional, implied by devmode). Defaults to False.
79
80
  devmode (bool, optional): Install with devmode. Defaults to False.
80
- ignore_validation (bool, optional): _description_. Defaults to False.
81
81
  jailmode (bool, optional): Install snap with jailmode. Defaults to False.
82
82
  revision (int, optional): install a specific revision of the snap. Defaults to None.
83
83
  filename (str, optional): Path to snap to sideload. Defaults to None.
84
84
  wait (bool, optional): Whether to wait for snap to install. If not waiting, will return async response with change id. Defaults to False.
85
85
 
86
86
  Raises:
87
- Exception: If error occurs during snap install
87
+ SnapdAPIError: If error occurs during snap install
88
88
 
89
89
  Returns:
90
90
  AsyncResponse | ChangesResponse: If wait is True, will return ChangesResponse. Otherwise, will return AsyncResponse
@@ -93,8 +93,8 @@ class SnapsEndpoints:
93
93
  "action": "install",
94
94
  "channel": channel,
95
95
  "classic": classic,
96
+ "dangerous": dangerous,
96
97
  "devmode": devmode,
97
- "ignore_validation": ignore_validation,
98
98
  "jailmode": jailmode,
99
99
  }
100
100
  if revision:
@@ -103,7 +103,10 @@ class SnapsEndpoints:
103
103
  # sideload
104
104
  if not Path(filename).exists():
105
105
  raise FileNotFoundError(f"File {filename} does not exist")
106
- request_data["dangerous"] = True
106
+ if request_data.get("dangerous") is not True:
107
+ raise ValueError(
108
+ "Cannot sideload snap without dangerous flag set to True"
109
+ )
107
110
  raw_response: httpx.Response = await self._client.request(
108
111
  "POST",
109
112
  f"{self.common_endpoint}",
@@ -122,17 +125,16 @@ class SnapsEndpoints:
122
125
  while True:
123
126
  try:
124
127
  changes = await self._client.get_changes_by_id(changes_id)
125
- logger.debug(f"Progress: {changes.result.overall_progress}")
126
- except httpx.HTTPError as e:
128
+ logger.debug("Progress: %s", changes.result.overall_progress)
129
+ except httpx.HTTPError:
127
130
  if going_to_reload_daemon(previous_changes):
128
131
  logger.debug("Waiting for daemon to reload")
129
- await asyncio.sleep(0.1)
130
- continue
132
+ changes = previous_changes
131
133
 
132
134
  if changes.ready:
133
135
  break
134
136
  if changes.result.err:
135
- raise Exception(f"Error in snap install: {changes.result.err}")
137
+ raise SnapdAPIError(f"Error in snap install: {changes.result.err}")
136
138
  await asyncio.sleep(0.1)
137
139
  previous_changes = changes
138
140
  return changes
@@ -145,6 +147,18 @@ class SnapsEndpoints:
145
147
  terminate: bool = False,
146
148
  wait: bool = False,
147
149
  ) -> AsyncResponse | ChangesResponse:
150
+ """
151
+ Asynchronously removes a snap package.
152
+ Args:
153
+ snap (str): The name of the snap package to remove.
154
+ purge (bool, optional): If True, purges the snap package. Defaults to False.
155
+ terminate (bool, optional): If True, terminates the snap package. Defaults to False.
156
+ wait (bool, optional): If True, waits for the removal process to complete. Defaults to False.
157
+ Returns:
158
+ AsyncResponse | ChangesResponse: The response from the snapd API, either an asynchronous response or a changes response if waiting for completion.
159
+ Raises:
160
+ SnapdAPIError: If there is an error in the snap removal process.
161
+ """
148
162
  request_data = {
149
163
  "action": "remove",
150
164
  "purge": purge,
@@ -162,7 +176,7 @@ class SnapsEndpoints:
162
176
  while True:
163
177
  try:
164
178
  changes = await self._client.get_changes_by_id(changes_id)
165
- except httpx.HTTPError as e:
179
+ except httpx.HTTPError:
166
180
  if going_to_reload_daemon(previous_changes):
167
181
  logger.debug("Waiting for daemon to reload")
168
182
  await asyncio.sleep(0.1)
@@ -170,9 +184,95 @@ class SnapsEndpoints:
170
184
  if changes.ready:
171
185
  break
172
186
  if changes.result.err:
173
- raise Exception(f"Error in snap remove: {changes.result.err}")
187
+ raise SnapdAPIError(f"Error in snap remove: {changes.result.err}")
174
188
  await asyncio.sleep(0.1)
175
189
  previous_changes = changes
176
190
  return changes
177
191
 
178
192
  return response
193
+
194
+ async def refresh_snap(
195
+ self,
196
+ snap: str,
197
+ channel: str = "stable",
198
+ classic: bool = False,
199
+ dangerous: bool = False,
200
+ devmode: bool = False,
201
+ ignore_validation: bool = False,
202
+ jailmode: bool = False,
203
+ revision: int = None,
204
+ filename: str = None,
205
+ wait: bool = False,
206
+ ) -> AsyncResponse | ChangesResponse:
207
+ """
208
+ Refreshes a snap package.
209
+ Args:
210
+ snap (str): The name of the snap package to refresh.
211
+ channel (str, optional): The channel to refresh the snap from. Defaults to "stable".
212
+ classic (bool, optional): Whether to use classic confinement. Defaults to False.
213
+ dangerous (bool, optional): Whether to allow installation of unasserted snaps. Defaults to False.
214
+ devmode (bool, optional): Whether to use development mode. Defaults to False.
215
+ ignore_validation (bool, optional): Whether to ignore validation. Defaults to False.
216
+ jailmode (bool, optional): Whether to use jail mode. Defaults to False.
217
+ revision (int, optional): The specific revision to refresh to. Defaults to None.
218
+ filename (str, optional): The path to the snap file for sideloading. Defaults to None.
219
+ wait (bool, optional): Whether to wait for the refresh operation to complete. Defaults to False.
220
+ Returns:
221
+ AsyncResponse | ChangesResponse: The response from the refresh operation.
222
+ Raises:
223
+ FileNotFoundError: If the specified snap file does not exist.
224
+ ValueError: If attempting to sideload without the dangerous flag set to True.
225
+ SnapdAPIError: If there is an error during the snap refresh.
226
+ """
227
+ request_data = {
228
+ "action": "refresh",
229
+ "channel": channel,
230
+ "classic": classic,
231
+ "dangerous": dangerous,
232
+ "devmode": devmode,
233
+ "ignore_validation": ignore_validation,
234
+ "jailmode": jailmode,
235
+ }
236
+ if revision:
237
+ request_data["revision"] = revision
238
+ if filename:
239
+ # sideload
240
+ if not Path(filename).exists():
241
+ raise FileNotFoundError(f"File {filename} does not exist")
242
+ if request_data.get("dangerous") is not True:
243
+ raise ValueError(
244
+ "Cannot sideload snap without dangerous flag set to True"
245
+ )
246
+ raw_response: httpx.Response = await self._client.request(
247
+ "POST",
248
+ f"{self.common_endpoint}",
249
+ data=request_data,
250
+ files={"snap": open(filename, "rb")},
251
+ )
252
+ else:
253
+ # install from default snap store
254
+ raw_response: httpx.Response = await self._client.request(
255
+ "POST", f"{self.common_endpoint}/{snap}", json=request_data
256
+ )
257
+ response = AsyncResponse.model_validate_json(raw_response.content)
258
+ if wait:
259
+ changes_id = response.change
260
+ previous_changes = None
261
+ while True:
262
+ try:
263
+ changes = await self._client.get_changes_by_id(changes_id)
264
+ logger.debug("Progress: %s", changes.result.overall_progress)
265
+ except httpx.HTTPError:
266
+ if going_to_reload_daemon(previous_changes):
267
+ logger.debug("Waiting for daemon to reload")
268
+ await asyncio.sleep(0.1)
269
+ continue
270
+
271
+ if changes.ready:
272
+ break
273
+ if changes.result.err:
274
+ raise SnapdAPIError(f"Error in snap remove: {changes.result.err}")
275
+ await asyncio.sleep(0.1)
276
+ previous_changes = changes
277
+ return changes
278
+ return response
@@ -11,6 +11,7 @@ from snap_python.schemas.store.categories import (
11
11
  from snap_python.schemas.store.info import VALID_SNAP_INFO_FIELDS, InfoResponse
12
12
  from snap_python.schemas.store.search import (
13
13
  VALID_SEARCH_CATEGORY_FIELDS,
14
+ ArchSearchResponse,
14
15
  SearchResponse,
15
16
  )
16
17
 
@@ -165,3 +166,30 @@ class StoreEndpoints:
165
166
  return await self.find(
166
167
  category=category, fields=["title", "store-url", "summary"]
167
168
  )
169
+
170
+ async def get_all_snaps_for_arch(self, arch: str) -> ArchSearchResponse:
171
+ # use the old "/api/v1/snaps/names" to get all snaps for a given architecture
172
+
173
+ # ensure valid arch
174
+ if arch not in [
175
+ "amd64",
176
+ "arm64",
177
+ "armhf",
178
+ "i386",
179
+ "ppc64el",
180
+ "s390x",
181
+ "riscv64",
182
+ ]:
183
+ raise ValueError(f"Invalid architecture: {arch}")
184
+
185
+ route = "/api/v1/snaps/names"
186
+ extra_headers = {"X-Ubuntu-Architecture": arch}
187
+
188
+ response = await self.store_client.get(
189
+ f"{self._raw_base_url}{route}", headers=extra_headers, timeout=60
190
+ )
191
+ response.raise_for_status()
192
+
193
+ response_json = response.json()
194
+ response_json["arch"] = arch
195
+ return ArchSearchResponse.model_validate(response_json)
@@ -13,10 +13,17 @@ from snap_python.schemas.common import (
13
13
  from snap_python.schemas.store.categories import Category
14
14
 
15
15
 
16
+ class SnapHealth(BaseModel):
17
+ status: str
18
+ revision: str
19
+ timestamp: AwareDatetime
20
+
21
+
16
22
  class InstalledSnapFields(Revision):
17
23
  apps: list[SnapApp] = Field(default_factory=list)
18
24
  developer: Optional[str] = None
19
25
  devmode: Optional[bool] = None
26
+ health: Optional[SnapHealth] = None
20
27
  icon: Optional[str] = None
21
28
  id: Optional[str] = Field(
22
29
  None, validation_alias=AliasChoices("id", "snap-id"), serialization_alias="id"
@@ -1,9 +1,16 @@
1
1
  from typing import Any, Dict, List, Optional
2
2
 
3
- from pydantic import AliasChoices, AwareDatetime, BaseModel, ConfigDict, Field
3
+ from pydantic import (
4
+ AliasChoices,
5
+ AliasPath,
6
+ AwareDatetime,
7
+ BaseModel,
8
+ ConfigDict,
9
+ Field,
10
+ )
4
11
 
5
12
  from snap_python.schemas.common import Revision
6
- from snap_python.schemas.snaps import InstalledSnap, StoreSnap, StoreSnapFields
13
+ from snap_python.schemas.snaps import InstalledSnap, Snap, StoreSnap, StoreSnapFields
7
14
 
8
15
  VALID_SEARCH_CATEGORY_FIELDS = [
9
16
  "base",
@@ -99,15 +106,17 @@ class SearchResult(BaseModel):
99
106
  revision_info[field] = snap_info.pop(field)
100
107
 
101
108
  snap_id = snap_info.pop("id")
102
- snap_name = snap_info.pop("name")
109
+ snap_name = snap_info.get("name")
103
110
 
104
111
  # get set of acceptable fields for StoreSnap
105
- store_snap_fields = StoreSnapFields.model_fields.keys()
106
- snap_info = {
112
+ store_snap_fields = set(StoreSnapFields.model_fields.keys()).union(
113
+ Snap.model_fields.keys()
114
+ )
115
+ store_snap_info = {
107
116
  key: value for key, value in snap_info.items() if key in store_snap_fields
108
117
  }
109
118
 
110
- snap_info = {"snap": snap_info, "snap-id": snap_id, "name": snap_name}
119
+ snap_info = {"snap": store_snap_info, "snap-id": snap_id, "name": snap_name}
111
120
  snap_info["revision"] = revision_info
112
121
 
113
122
  return cls.model_validate(snap_info)
@@ -118,3 +127,21 @@ class SearchResponse(BaseModel):
118
127
 
119
128
  error_list: Optional[List[ErrorListItem]] = Field(None, alias="error-list")
120
129
  results: List[SearchResult]
130
+
131
+
132
+ class ArchSearchItem(BaseModel):
133
+ model_config = ConfigDict(extra="ignore")
134
+
135
+ aliases: Optional[list[dict[str, str]]]
136
+ apps: list[str]
137
+ package_name: str
138
+ summary: str
139
+ title: str
140
+ version: str
141
+
142
+
143
+ class ArchSearchResponse(BaseModel):
144
+ results: list[ArchSearchItem] = Field(
145
+ alias=AliasPath("_embedded", "clickindex:package")
146
+ )
147
+ arch: str
snap_python/utils.py CHANGED
@@ -5,13 +5,17 @@ import httpx
5
5
  from snap_python.schemas.changes import ChangesResponse
6
6
 
7
7
 
8
+ class SnapdAPIError(Exception):
9
+ pass
10
+
11
+
8
12
  class AbstractSnapsClient(ABC): # pragma: no cover
9
13
  @abstractmethod
10
- async def request(self) -> httpx.Response:
14
+ async def request(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
11
15
  pass
12
16
 
13
17
  @abstractmethod
14
- async def request_raw(self) -> httpx.Response:
18
+ async def request_raw(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
15
19
  pass
16
20
 
17
21
  @abstractmethod
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: snap-python
3
- Version: 0.0.4
3
+ Version: 0.1.0
4
4
  Summary: An api to interact with snapd and snap stores
5
- Home-page: https://github.com/alexlukens/snap-python
6
5
  License: MIT
7
6
  Author: Alexander Lukens
8
7
  Author-email: alexdlukens@gmail.com
@@ -12,6 +11,7 @@ Classifier: Programming Language :: Python :: 3
12
11
  Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
15
  Requires-Dist: httpx (>=0.27.2,<0.28.0)
16
16
  Requires-Dist: pydantic (>=2.9.2,<3.0.0)
17
17
  Requires-Dist: retry (>=0.9.2,<0.10.0)
@@ -38,6 +38,15 @@ Here is a simple example of how to use snap_python:
38
38
  # TODO
39
39
  ```
40
40
 
41
+ ## Create a Release
42
+
43
+ ```bash
44
+ git tag -a v0.x.x -m "Release v0.x.x"
45
+ gh release create v0.x.x --title "Release v0.x.x"
46
+ ```
47
+
48
+ The automation will build a wheel and push it to pypi
49
+
41
50
  ## License
42
51
 
43
52
  This project is licensed under the MIT License. See the LICENSE file for more details.
@@ -1,13 +1,13 @@
1
1
  snap_python/__init__.py,sha256=gV43Nempk7HFKD1UCvxUhmP9x6dBmPjotcITREr81Vg,222
2
- snap_python/client.py,sha256=PsmO9EIszUsYc6zYOOijFjFr1jdHQR2dxl3by2bWUMo,5150
2
+ snap_python/client.py,sha256=iiKryiJSqyHj_zPHyQHB0RgydwVql6IwgJ2Nny9HPNY,6492
3
3
  snap_python/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  snap_python/components/config.py,sha256=IO_gCrumphEZioINFmXxb8I5OVsKC9cgSa28sQbZ700,1775
5
- snap_python/components/snaps.py,sha256=RkCxxHfArWATx7bsNst4v0yvJPqVQtaKJkLm_bO70qE,6698
6
- snap_python/components/store.py,sha256=_o_2OYMvGbq_bLEQXw9zHjBrCipQ92qZaSkwdAfngAc,6451
5
+ snap_python/components/snaps.py,sha256=etqnlFcfaBPiOeIb-r13kSY-KnPdvv4WE1LDv15u-YU,11551
6
+ snap_python/components/store.py,sha256=MWs2gh-ZdsuLiGsN9LJDHsPf-a0B206QzzLEbzKbwh0,7326
7
7
  snap_python/schemas/changes.py,sha256=7SYUFXAQq9arl7hAA3YQ0YubMurPJtknTOVxP8ryqew,1919
8
8
  snap_python/schemas/common.py,sha256=GLtC9aBo3O8vLKX9PNvzE-_DKN02OGpwsS7Ik2WK9-8,2608
9
9
  snap_python/schemas/config.py,sha256=xXTGhk6TpwI8RlR6_qnfWlBHrBnTwr3hRCO5qCnN-6U,107
10
- snap_python/schemas/snaps.py,sha256=y1v-FjZ4ZpcqmLxXbULP6Wa85RMPjXQpbKGf0CEHVIg,2846
10
+ snap_python/schemas/snaps.py,sha256=cO0UwghFZwfIlJtAjYDdNgfLKwU7953B9bgolpXcUL0,2980
11
11
  snap_python/schemas/store/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  snap_python/schemas/store/categories.py,sha256=PewotRqoPs7cMKtlZKsHVNfnJoItmNKMekS1Be-46l0,1082
13
13
  snap_python/schemas/store/info.py,sha256=TuqG5wZc89e_Epai1xkcpA1nXNMKBOKng0B40xm_PfU,3333
@@ -15,9 +15,9 @@ snap_python/schemas/store/json/category_by_name_schema.json,sha256=3-95m6V2WXaPC
15
15
  snap_python/schemas/store/json/category_schema.json,sha256=c4h_eyyBbChOiNby3hYfjAh9i_1MDbPJnHHU4rGOGss,1898
16
16
  snap_python/schemas/store/json/snaps_find_schema.json,sha256=TfKpnievs5QOc7ah97ATMCQTOEfKspONTZnwSwq_0fI,8866
17
17
  snap_python/schemas/store/json/snaps_info_schema.json,sha256=udoOovb6Ea7EnE_R9RuZ1CpY1wOJ3-K8xP4-8zKHgrQ,15888
18
- snap_python/schemas/store/search.py,sha256=GFfK8qfYnCD1UziU5kWmmjac03qNYWWkmIGcKZoSb90,3223
19
- snap_python/utils.py,sha256=pE-mVtoq5KMZH_Xg1O9lJlAmktLyuIdAVBEomuLZjWI,752
20
- snap_python-0.0.4.dist-info/LICENSE,sha256=lYXNZszxC_l2rlBvrHYs-esDegxWh8MKtjrdxA24uOk,1072
21
- snap_python-0.0.4.dist-info/METADATA,sha256=CsfubdQSuEypyBzgGd42EHi1EyNmjwO78b88DEsh1RI,1096
22
- snap_python-0.0.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
23
- snap_python-0.0.4.dist-info/RECORD,,
18
+ snap_python/schemas/store/search.py,sha256=u92aFLLuvDwpfMNHlZo7O2-k-kj7cgzAOtvxxsvi6mU,3718
19
+ snap_python/utils.py,sha256=RzNnbcA-ca-lU-3-2uW-2dCYkMt4O0uxme-XMgv9Wrs,871
20
+ snap_python-0.1.0.dist-info/LICENSE,sha256=lYXNZszxC_l2rlBvrHYs-esDegxWh8MKtjrdxA24uOk,1072
21
+ snap_python-0.1.0.dist-info/METADATA,sha256=oghLA_J2xPWkVR7En0-ctujzxAEP1FvGaDZj6sE2dfI,1271
22
+ snap_python-0.1.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
23
+ snap_python-0.1.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.0.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any