delphi-code 0.2.0__py3-none-any.whl → 0.2.2__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.
delphi_code/hosts.py CHANGED
@@ -8,7 +8,7 @@ import subprocess
8
8
  import sys
9
9
  from typing import Any, NamedTuple
10
10
  from urllib.error import HTTPError, URLError
11
- from urllib.parse import quote, urlencode
11
+ from urllib.parse import quote, urlencode, urlsplit
12
12
  from urllib.request import Request, urlopen
13
13
 
14
14
  if __package__:
@@ -24,6 +24,11 @@ class Owner(NamedTuple):
24
24
  name: str
25
25
 
26
26
 
27
+ class ApiCredentials(NamedTuple):
28
+ authorization: str
29
+ source: str
30
+
31
+
27
32
  class Repository(NamedTuple):
28
33
  host: str
29
34
  owner: str
@@ -43,6 +48,7 @@ class RepositoryHost:
43
48
  default_api_base: str
44
49
  credentials_hint: str
45
50
  rejected_credentials_hint = ""
51
+ missing_permission_hint = ""
46
52
 
47
53
  def __init__(self, api_authorization=None):
48
54
  self._given_api_authorization = api_authorization
@@ -57,11 +63,14 @@ class RepositoryHost:
57
63
  def permalink_template(self, owner, repository, commit):
58
64
  raise NotImplementedError
59
65
 
66
+ def suggested_owner(self):
67
+ return None
68
+
60
69
  def clone_credentials(self):
61
70
  raise NotImplementedError
62
71
 
63
72
  def has_api_credentials(self):
64
- return bool(self._available_api_authorization)
73
+ return self._available_api_credentials is not None
65
74
 
66
75
  def owners(self):
67
76
  return [Owner(**item) for item in self._list_in_child_process("owners")]
@@ -69,7 +78,7 @@ class RepositoryHost:
69
78
  def repositories(self, owner):
70
79
  return [Repository(**item) for item in self._list_in_child_process("repositories", owner)]
71
80
 
72
- def _find_api_authorization(self):
81
+ def _find_api_credentials(self):
73
82
  raise NotImplementedError
74
83
 
75
84
  def _list_owners(self):
@@ -93,19 +102,21 @@ class RepositoryHost:
93
102
  return os.environ.get(f"{self._variable_prefix}_API_BASE", self.default_api_base).rstrip("/")
94
103
 
95
104
  @cached_property
96
- def _available_api_authorization(self):
97
- return self._given_api_authorization or self._find_api_authorization()
105
+ def _available_api_credentials(self):
106
+ if self._given_api_authorization:
107
+ return ApiCredentials(self._given_api_authorization, "given credentials")
108
+ return self._find_api_credentials()
98
109
 
99
110
  @property
100
- def _api_authorization(self):
101
- authorization = self._available_api_authorization
102
- if not authorization:
111
+ def _api_credentials(self):
112
+ credentials = self._available_api_credentials
113
+ if credentials is None:
103
114
  raise Failure(
104
115
  "remote_auth_missing",
105
116
  f"Listing {self.name} repositories needs credentials: {self.credentials_hint}",
106
117
  ExitCode.RUNTIME_ASSETS,
107
118
  )
108
- return authorization
119
+ return credentials
109
120
 
110
121
  def _repository(self, owner, slug, description, private):
111
122
  return Repository(self.domain, owner, slug, description or "", bool(private))
@@ -129,7 +140,8 @@ class RepositoryHost:
129
140
  return json.load(response), (response.headers.get("Link") if response.headers else None) or ""
130
141
 
131
142
  def _list_in_child_process(self, listing, *arguments) -> list[dict]:
132
- environment = dict(os.environ, DELPHI_CODE_API_AUTHORIZATION=self._api_authorization)
143
+ credentials = self._api_credentials
144
+ environment = dict(os.environ, DELPHI_CODE_API_AUTHORIZATION=credentials.authorization)
133
145
  try:
134
146
  result = subprocess.run(
135
147
  [sys.executable, __file__, self.domain, listing, *arguments],
@@ -152,10 +164,18 @@ class RepositoryHost:
152
164
  if result.returncode == 0:
153
165
  return payload
154
166
  error = payload.get("error", {})
155
- if error.get("status") in (401, 403):
167
+ if error.get("status") == 401:
156
168
  raise Failure(
157
169
  "remote_auth_failed",
158
- f"{self.name} refused the credentials: {error['message']}. {self.rejected_credentials_hint or self.credentials_hint}",
170
+ f"{self.name} rejected the {credentials.source}: {error['message']}. "
171
+ f"{self.rejected_credentials_hint or self.credentials_hint}",
172
+ ExitCode.OPERATION,
173
+ )
174
+ if error.get("status") == 403:
175
+ raise Failure(
176
+ "remote_permission_denied",
177
+ f"{self.name} accepted the {credentials.source} but refused {error.get('path')}: {error['message']}. "
178
+ f"{self.missing_permission_hint}",
159
179
  ExitCode.OPERATION,
160
180
  )
161
181
  raise Failure(
@@ -170,39 +190,43 @@ class Bitbucket(RepositoryHost):
170
190
  default_api_base = "https://api.bitbucket.org/2.0"
171
191
  credentials_hint = "set BITBUCKET_USERNAME and BITBUCKET_APP_PASSWORD, or store git credentials for bitbucket.org"
172
192
  rejected_credentials_hint = "With an API token, set BITBUCKET_EMAIL to your Atlassian account email"
193
+ missing_permission_hint = (
194
+ "Listing needs an API token with the scopes read:user:bitbucket, read:workspace:bitbucket and "
195
+ "read:repository:bitbucket, or an app password with Account, Workspace membership and Repositories read access"
196
+ )
173
197
  _repository_fields = "next,values.slug,values.description,values.is_private"
174
198
 
175
199
  def permalink_template(self, owner, repository, commit):
176
200
  return f"{self.web_url(owner, repository)}/src/{commit}/{{path}}#lines-{{start}}:{{end}}"
177
201
 
202
+ def suggested_owner(self):
203
+ return os.environ.get("BITBUCKET_WORKSPACE")
204
+
178
205
  def clone_credentials(self):
179
206
  username, password = os.environ.get("BITBUCKET_USERNAME"), os.environ.get("BITBUCKET_APP_PASSWORD")
180
207
  return (username, password) if username and password else None
181
208
 
182
- def _find_api_authorization(self):
183
- username = os.environ.get("BITBUCKET_EMAIL") or os.environ.get("BITBUCKET_USERNAME")
184
- password = os.environ.get("BITBUCKET_APP_PASSWORD")
185
- credentials = (username, password) if username and password else _git_stored_credentials(self.domain)
186
- return "Basic " + base64.b64encode(":".join(credentials).encode()).decode() if credentials else None
209
+ def _find_api_credentials(self):
210
+ username_variable = "BITBUCKET_EMAIL" if os.environ.get("BITBUCKET_EMAIL") else "BITBUCKET_USERNAME"
211
+ username, password = os.environ.get(username_variable), os.environ.get("BITBUCKET_APP_PASSWORD")
212
+ if username and password:
213
+ source = f"{username_variable} and BITBUCKET_APP_PASSWORD from the environment"
214
+ else:
215
+ stored = _git_stored_credentials(self.domain)
216
+ if stored is None:
217
+ return None
218
+ (username, password), source = stored, f"git credentials stored for {self.domain}"
219
+ return ApiCredentials("Basic " + base64.b64encode(f"{username}:{password}".encode()).decode(), source)
187
220
 
188
221
  def _workspaces_url(self):
189
222
  return f"{self._api_base}/user/workspaces?pagelen={PAGE_LENGTH}"
190
223
 
191
- def _legacy_workspaces_url(self):
192
- return f"{self._api_base}/user/permissions/workspaces?pagelen={PAGE_LENGTH}"
193
-
194
224
  def _repositories_url(self, workspace):
195
225
  query = urlencode({"role": "member", "pagelen": PAGE_LENGTH, "sort": "slug", "fields": self._repository_fields})
196
226
  return f"{self._api_base}/repositories/{quote(workspace)}?{query}"
197
227
 
198
228
  def _list_owners(self):
199
- try:
200
- memberships = self._fetch_pages(self._workspaces_url())
201
- except HTTPError as exc:
202
- exc.close()
203
- if exc.code not in (404, 410):
204
- raise
205
- memberships = self._fetch_pages(self._legacy_workspaces_url())
229
+ memberships = self._fetch_pages(self._workspaces_url())
206
230
  names_by_slug = {}
207
231
  for membership in memberships:
208
232
  workspace = membership.get("workspace", membership)
@@ -228,6 +252,9 @@ class GitHub(RepositoryHost):
228
252
  owner_noun = "account"
229
253
  default_api_base = "https://api.github.com"
230
254
  credentials_hint = "set GITHUB_TOKEN, sign in with gh auth login, or store git credentials for github.com"
255
+ missing_permission_hint = (
256
+ "Listing needs a classic token with the repo scope, or a fine-grained token with Metadata read access"
257
+ )
231
258
  _next_link = re.compile(r'<([^>]+)>;\s*rel="next"')
232
259
 
233
260
  def permalink_template(self, owner, repository, commit):
@@ -237,12 +264,16 @@ class GitHub(RepositoryHost):
237
264
  token = _github_environment_token()
238
265
  return ("x-access-token", token) if token else None
239
266
 
240
- def _find_api_authorization(self):
241
- token = _github_environment_token() or _gh_token(self.domain)
242
- if not token:
243
- stored = _git_stored_credentials(self.domain)
244
- token = stored[1] if stored else None
245
- return f"Bearer {token}" if token else None
267
+ def _find_api_credentials(self):
268
+ for source, find_token in (
269
+ ("GITHUB_TOKEN or GH_TOKEN from the environment", _github_environment_token),
270
+ ("token from gh auth token", lambda: _gh_token(self.domain)),
271
+ (f"token stored in git credentials for {self.domain}", lambda: _git_stored_token(self.domain)),
272
+ ):
273
+ token = find_token()
274
+ if token:
275
+ return ApiCredentials(f"Bearer {token}", source)
276
+ return None
246
277
 
247
278
  def owners(self):
248
279
  owners = sorted({repository.owner for repository in self._accessible_repositories}, key=str.lower)
@@ -304,6 +335,11 @@ def _gh_token(domain):
304
335
  return result.stdout.strip() if result.returncode == 0 and result.stdout.strip() else None
305
336
 
306
337
 
338
+ def _git_stored_token(domain):
339
+ stored = _git_stored_credentials(domain)
340
+ return stored[1] if stored else None
341
+
342
+
307
343
  def _git_stored_credentials(domain):
308
344
  environment = {name: value for name, value in os.environ.items() if name not in {"GIT_ASKPASS", "SSH_ASKPASS"}}
309
345
  environment["GIT_TERMINAL_PROMPT"] = "0"
@@ -324,14 +360,44 @@ def _git_stored_credentials(domain):
324
360
  return fields["username"], fields["password"]
325
361
 
326
362
 
363
+ def _error_body(exc):
364
+ try:
365
+ return exc.read()
366
+ except (OSError, ValueError):
367
+ return b""
368
+ finally:
369
+ exc.close()
370
+
371
+
372
+ def _service_error_message(body):
373
+ try:
374
+ payload = json.loads(body)
375
+ except ValueError:
376
+ return None
377
+ if not isinstance(payload, dict):
378
+ return None
379
+ bitbucket_error = payload.get("error")
380
+ if not isinstance(bitbucket_error, dict):
381
+ return payload.get("message")
382
+ message, detail = bitbucket_error.get("message"), bitbucket_error.get("detail")
383
+ if isinstance(detail, dict):
384
+ detail = "; ".join(f"{name}: {_listed(value)}" for name, value in detail.items())
385
+ return f"{message} ({detail})" if message and detail else message or detail
386
+
387
+
388
+ def _listed(value):
389
+ return ", ".join(map(str, value)) if isinstance(value, list) else str(value)
390
+
391
+
327
392
  def main(arguments):
328
393
  domain, listing, *listing_arguments = arguments
329
394
  host = HOSTS_BY_DOMAIN[domain](os.environ["DELPHI_CODE_API_AUTHORIZATION"])
330
395
  try:
331
396
  items = host._list_owners() if listing == "owners" else host._list_repositories(*listing_arguments)
332
397
  except HTTPError as exc:
333
- exc.close()
334
- print(json.dumps({"error": {"status": exc.code, "message": f"HTTP {exc.code} {exc.reason}"}}))
398
+ reason = _service_error_message(_error_body(exc)) or exc.reason
399
+ path = urlsplit(exc.url).path if exc.url else None
400
+ print(json.dumps({"error": {"status": exc.code, "path": path, "message": f"HTTP {exc.code} {reason}"}}))
335
401
  return 1
336
402
  except (URLError, OSError, ValueError, KeyError, TypeError) as exc:
337
403
  print(json.dumps({"error": {"status": None, "message": str(exc)}}))
delphi_code/picker.py CHANGED
@@ -78,7 +78,12 @@ class RepositoryPicker:
78
78
  def _choose_owner(self, host):
79
79
  import questionary
80
80
 
81
- owners = host.owners()
81
+ try:
82
+ owners = host.owners()
83
+ except Failure as failure:
84
+ if failure.code != "remote_permission_denied":
85
+ raise
86
+ return self._type_owner(host)
82
87
  if not owners:
83
88
  raise Failure("remote_empty", f"Your {host.name} account has no repositories", ExitCode.USAGE)
84
89
  if len(owners) == 1:
@@ -91,6 +96,16 @@ class RepositoryPicker:
91
96
  **self._terminal,
92
97
  ).ask()
93
98
 
99
+ def _type_owner(self, host):
100
+ import questionary
101
+
102
+ answer = questionary.text(
103
+ f"These credentials cannot list {host.name} {host.owner_noun}s. {host.name} {host.owner_noun} to index:",
104
+ default=host.suggested_owner() or "",
105
+ **self._terminal,
106
+ ).ask()
107
+ return (answer or "").strip() or None
108
+
94
109
  def _choose_repositories(self, owner, repositories, tracked_keys):
95
110
  import questionary
96
111
 
delphi_code/sources.py CHANGED
@@ -110,7 +110,8 @@ class GitRemoteSource:
110
110
  return Revision(branch, commit)
111
111
 
112
112
  def _named_revision(self, ref):
113
- commits = dict((name, value) for value, name in self._remote_refs(f"refs/heads/{ref}", f"refs/tags/{ref}"))
113
+ advertised = self._remote_refs(f"refs/heads/{ref}", f"refs/tags/{ref}", f"refs/tags/{ref}^{{}}")
114
+ commits = dict((name, value) for value, name in advertised)
114
115
  commit = (
115
116
  commits.get(f"refs/heads/{ref}") or commits.get(f"refs/tags/{ref}^{{}}") or commits.get(f"refs/tags/{ref}")
116
117
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: delphi-code
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Offline local code search for agents
5
5
  Author: Mark Dorofeev
6
6
  License-Expression: Apache-2.0
@@ -13,6 +13,8 @@ Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: MacOS
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
16
18
  Classifier: Topic :: Software Development
17
19
  Classifier: Topic :: Text Processing :: Indexing
18
20
  Requires-Python: >=3.12
@@ -35,7 +37,7 @@ Offline semantic code search for agents. Delphi Code is a small Python CLI that
35
37
  ## Quick start
36
38
 
37
39
  ```sh
38
- uv tool install --python 3.12 --managed-python delphi-code
40
+ uv tool install --managed-python delphi-code
39
41
  delphi-code setup
40
42
  delphi-code index -p /absolute/path/to/project
41
43
  delphi-code search 'where are user passwords checked?'
@@ -46,7 +48,7 @@ delphi-code search 'where are user passwords checked?'
46
48
  ## Requirements
47
49
 
48
50
  - macOS 14 or later on arm64 (the tested target). Linux has not yet been verified; Windows is not supported by the current file locking implementation.
49
- - CPython 3.12 with SQLite loadable extensions. Some python.org macOS builds disable that feature; `--managed-python` makes uv use its own Python build, which supports it. `doctor` reports this explicitly.
51
+ - CPython 3.12 or later (tested on 3.12, 3.13 and 3.14) with SQLite loadable extensions. Some python.org macOS builds disable that feature; `--managed-python` makes uv use its own Python build, which supports it. `doctor` reports this explicitly.
50
52
  - CPU inference; no GPU is needed.
51
53
 
52
54
  ## Installation
@@ -54,21 +56,21 @@ delphi-code search 'where are user passwords checked?'
54
56
  From PyPI, as in the quick start:
55
57
 
56
58
  ```sh
57
- uv tool install --python 3.12 --managed-python delphi-code
59
+ uv tool install --managed-python delphi-code
58
60
  delphi-code setup
59
61
  ```
60
62
 
61
63
  From a checkout, with uv:
62
64
 
63
65
  ```sh
64
- uv tool install --python 3.12 --managed-python /absolute/path/to/delphi-code
66
+ uv tool install --managed-python /absolute/path/to/delphi-code
65
67
  delphi-code setup
66
68
  ```
67
69
 
68
- From a checkout, with the pinned dependency versions and a Python 3.12 build that supports SQLite extensions:
70
+ From a checkout, with the pinned dependency versions and a Python 3.12+ build that supports SQLite extensions:
69
71
 
70
72
  ```sh
71
- python3.12 -m venv .venv
73
+ python3 -m venv .venv
72
74
  .venv/bin/python -m pip install -r requirements-lock.txt
73
75
  .venv/bin/python -m pip install --no-deps --no-build-isolation .
74
76
  .venv/bin/delphi-code setup
@@ -168,7 +170,7 @@ delphi-code add github.com/octo/tools
168
170
  delphi-code add
169
171
  ```
170
172
 
171
- Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbucket workspace or GitHub account (skipped when there is only one). Then check repositories in a list you can filter by typing. Repositories you already track start checked, so unchecking one stops tracking it and deletes its index. After you confirm, the new repositories are indexed. Filter options and `--ref` apply to the repositories you add. The prompts are drawn on stderr, and stdout still receives one JSON object with the added repositories under `repos` and the untracked ones under `removed`. Cancelling changes nothing and reports `"cancelled": true`. Without a terminal, `add` requires sources.
173
+ Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbucket workspace or GitHub account (skipped when there is only one). If your Bitbucket credentials may not list workspaces, you type the workspace name instead; `BITBUCKET_WORKSPACE` pre-fills it. Then check repositories in a list you can filter by typing. Repositories you already track start checked, so unchecking one stops tracking it and deletes its index. After you confirm, the new repositories are indexed. Filter options and `--ref` apply to the repositories you add. The prompts are drawn on stderr, and stdout still receives one JSON object with the added repositories under `repos` and the untracked ones under `removed`. Cancelling changes nothing and reports `"cancelled": true`. Without a terminal, `add` requires sources.
172
174
 
173
175
  **Credentials.** Git never prompts. Credentials from the environment reach git through `GIT_ASKPASS`, never in URLs or arguments, and take precedence over configured git credentials. Listing repositories for the picker uses the services' REST APIs. The API requests run in a separate process, like the model download in `setup`, so the main process keeps its network guard.
174
176
 
@@ -177,7 +179,7 @@ Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbu
177
179
  | Cloning | `BITBUCKET_USERNAME` and `BITBUCKET_APP_PASSWORD`, otherwise git's own credentials | `GITHUB_TOKEN` or `GH_TOKEN`, otherwise git's own credentials (`gh auth setup-git` configures them) |
178
180
  | Listing for the picker | The same variables (with `BITBUCKET_EMAIL` in place of the username when set), otherwise the credentials git has stored for bitbucket.org | `GITHUB_TOKEN` or `GH_TOKEN`, otherwise `gh auth token`, otherwise the token git has stored for github.com |
179
181
 
180
- Bitbucket has replaced app passwords with API tokens. An API token also works in `BITBUCKET_APP_PASSWORD`. Git over HTTPS takes the username that Bitbucket shows for it, and the REST API takes your Atlassian account email, which you set in `BITBUCKET_EMAIL`.
182
+ Bitbucket has replaced app passwords with API tokens. An API token also works in `BITBUCKET_APP_PASSWORD`. Git over HTTPS takes the username that Bitbucket shows for it, and the REST API takes your Atlassian account email, which you set in `BITBUCKET_EMAIL`. Cloning and listing a workspace's repositories need the `read:repository:bitbucket` scope. Listing your workspaces also needs `read:workspace:bitbucket`; without it, the picker asks for the workspace name. An error names the credentials that were used and the scopes Bitbucket requires.
181
183
 
182
184
  `DELPHI_CODE_BITBUCKET_GIT_BASE` and `DELPHI_CODE_GITHUB_GIT_BASE` send clones to another base URL, such as a mirror, and `DELPHI_CODE_BITBUCKET_API_BASE` and `DELPHI_CODE_GITHUB_API_BASE` do the same for the APIs. Result links still point to bitbucket.org and github.com.
183
185
 
@@ -5,7 +5,7 @@ delphi_code/doctor.py,sha256=smuiZANoEGCRKDuiL2ls8wH9Uxl-yVsXf5EcNUHYYlA,1442
5
5
  delphi_code/download.py,sha256=Mx50naH8omV38ZZ1U1J0ELopD7ccUNVW8H1gfOg6zLE,1048
6
6
  delphi_code/errors.py,sha256=GXInITgDqBbqbBNdYXufy20fuH5es46rM-_iw2J6vCY,794
7
7
  delphi_code/files.py,sha256=3CIhEBT1B6MjSaLVo7mxXQfIPKk5DpVXtaW8VeSuW-I,3321
8
- delphi_code/hosts.py,sha256=p9JByx8pHSuP6hvNXbBuWgkHU1egR9o9Er3-c_Bh2Mg,12513
8
+ delphi_code/hosts.py,sha256=U_XpAnBM0R2rOziaoMaBCHbimp9HTwXn0oZ_4UI19C4,14918
9
9
  delphi_code/indexing.py,sha256=VwVoL-r2khtEyVr4xtSiP6hzQiuwMYsh-qV-mX3BWFQ,3799
10
10
  delphi_code/keys.py,sha256=s4G-iGu4JEJGku3_GdwNU8cMcIoA3Y8ClxrChXcqflE,2638
11
11
  delphi_code/manifest.py,sha256=Gy5CHiW4tTDEO7lcT5S2w5-SSowulpXej2Q0e6PXvJQ,3989
@@ -13,17 +13,17 @@ delphi_code/model.py,sha256=8YVkgFKUvmhPjiEvWOjpckbapeonmYbKes4cSsEDIS4,4684
13
13
  delphi_code/model_provenance.json,sha256=9fPASR9vJZXuKIdFWmc6Rc0A_I2OQnyOm6Ihytn9ab8,1370
14
14
  delphi_code/offline.py,sha256=I7Axon7IS9vuVa6x8c0bjCeq9yf969GMkqgLv67OSJA,1119
15
15
  delphi_code/paths.py,sha256=RoM1FDEpR-3u-mLfeFgLigPhqtzk-tPxzL_cQI8K1Ok,645
16
- delphi_code/picker.py,sha256=WwYaHFuUFafd5xSolqZEMO82f-WC-YJQssur9bV6bEI,4874
16
+ delphi_code/picker.py,sha256=D25tnsMxnBNDnQYJm6rKhgnd93NY5EtZpVIXlC2i-8k,5406
17
17
  delphi_code/projects.py,sha256=M1cSVCxzZ0YedVDAhmVzF5Wv1BP_VaPlMLzFOWbp0Rc,12440
18
18
  delphi_code/registry.py,sha256=tT4Ohu7L9ix_2NPJLcgOsebOafxJ45EnBiOtsLrePp0,4678
19
19
  delphi_code/selection.py,sha256=PI8QvzX7KHcHFNIHHwzwZbOWUsvB9aJhiiWJr32h-qk,614
20
20
  delphi_code/setup.py,sha256=eeQnc1K6MkCiPc5XLF5byAdC8QGubFETrzbSuGQM7_4,4596
21
- delphi_code/sources.py,sha256=X-DvBTUategTwYkn_BTiTaTLMwr2HpNx2TscAzQq5Wo,7082
21
+ delphi_code/sources.py,sha256=lgF4Ml9G2ovj48glcpIdVJUk26jUroUNkG_j3p4Q_GY,7139
22
22
  delphi_code/store.py,sha256=2MVm2qRjWmuzeevqcgvkAPQ_JfoJ2WkSLviGNQVGEv4,9696
23
- delphi_code-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
- delphi_code-0.2.0.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=7UBHSW7RAHFxx3BG1GtsQ7a_bb1Si9TG2JL4HFvz774,2302
25
- delphi_code-0.2.0.dist-info/METADATA,sha256=HDJxu5bzg385VGNFeWXWAjUvOSfzuyGVk-c19owQfig,18522
26
- delphi_code-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
27
- delphi_code-0.2.0.dist-info/entry_points.txt,sha256=2UhlG66g8QmTYZko1fdwaZ5ziVY75sBPZeeSLk0fdJo,53
28
- delphi_code-0.2.0.dist-info/top_level.txt,sha256=duf6zGKXk5n7vYW2b6IKVKfvZwLbXwp3yTiuTt62l2M,12
29
- delphi_code-0.2.0.dist-info/RECORD,,
23
+ delphi_code-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
+ delphi_code-0.2.2.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=7UBHSW7RAHFxx3BG1GtsQ7a_bb1Si9TG2JL4HFvz774,2302
25
+ delphi_code-0.2.2.dist-info/METADATA,sha256=GTUE_q6gv72F6125jyquaeYi3d7WSFWbGLiFIIIX5y8,19036
26
+ delphi_code-0.2.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
27
+ delphi_code-0.2.2.dist-info/entry_points.txt,sha256=2UhlG66g8QmTYZko1fdwaZ5ziVY75sBPZeeSLk0fdJo,53
28
+ delphi_code-0.2.2.dist-info/top_level.txt,sha256=duf6zGKXk5n7vYW2b6IKVKfvZwLbXwp3yTiuTt62l2M,12
29
+ delphi_code-0.2.2.dist-info/RECORD,,