devpi-web 5.0.0__tar.gz → 5.0.2__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 (78) hide show
  1. devpi_web-5.0.2/.flake8 +3 -0
  2. {devpi_web-5.0.0 → devpi_web-5.0.2}/CHANGELOG +29 -18
  3. {devpi_web-5.0.0 → devpi_web-5.0.2}/CHANGELOG.short.rst +29 -32
  4. {devpi_web-5.0.0 → devpi_web-5.0.2}/PKG-INFO +31 -33
  5. devpi_web-5.0.2/devpi_web/__init__.py +1 -0
  6. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/doczip.py +6 -3
  7. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/indexing.py +37 -23
  8. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/main.py +10 -8
  9. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/null_index.py +1 -3
  10. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/views.py +34 -14
  11. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/whoosh_index.py +83 -43
  12. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/PKG-INFO +31 -33
  13. {devpi_web-5.0.0 → devpi_web-5.0.2}/mypy.ini +7 -12
  14. {devpi_web-5.0.0 → devpi_web-5.0.2}/pyproject.toml +22 -18
  15. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_indexing.py +53 -2
  16. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_views.py +1 -6
  17. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_views_misc.py +3 -12
  18. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_views_search.py +18 -11
  19. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_views_toxresults.py +7 -3
  20. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_whoosh_index.py +42 -27
  21. {devpi_web-5.0.0 → devpi_web-5.0.2}/tox.ini +5 -4
  22. devpi_web-5.0.0/.flake8 +0 -2
  23. devpi_web-5.0.0/devpi_web/__init__.py +0 -1
  24. {devpi_web-5.0.0 → devpi_web-5.0.2}/LICENSE +0 -0
  25. {devpi_web-5.0.0 → devpi_web-5.0.2}/MANIFEST.in +0 -0
  26. {devpi_web-5.0.0 → devpi_web-5.0.2}/README.rst +0 -0
  27. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/clear_index.py +0 -0
  28. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/compat.py +0 -0
  29. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/config.py +0 -0
  30. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/description.py +0 -0
  31. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/hookspecs.py +0 -0
  32. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/macroregistry.py +0 -0
  33. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/macros.py +0 -0
  34. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/common.js +0 -0
  35. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/docview.js +0 -0
  36. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/favicon.ico +0 -0
  37. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/jquery-3.6.0.min.js +0 -0
  38. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/search.js +0 -0
  39. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/static/style.css +0 -0
  40. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/doc.pt +0 -0
  41. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/error.pt +0 -0
  42. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/favicon.pt +0 -0
  43. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/footer.pt +0 -0
  44. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/footer_versions.pt +0 -0
  45. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/head.pt +0 -0
  46. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/header.pt +0 -0
  47. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/header_breadcrumbs.pt +0 -0
  48. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/header_search.pt +0 -0
  49. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/header_status.pt +0 -0
  50. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/html_head_css.pt +0 -0
  51. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/html_head_scripts.pt +0 -0
  52. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/index.pt +0 -0
  53. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/logo.pt +0 -0
  54. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/notfound.pt +0 -0
  55. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/project.pt +0 -0
  56. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/query_docs.pt +0 -0
  57. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/root.pt +0 -0
  58. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/root_above_user_index_list.pt +0 -0
  59. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/root_below_user_index_list.pt +0 -0
  60. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/search.pt +0 -0
  61. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/search_help.pt +0 -0
  62. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/status.pt +0 -0
  63. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/status_badge.pt +0 -0
  64. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/toxresult.pt +0 -0
  65. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/toxresults.pt +0 -0
  66. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/user.pt +0 -0
  67. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web/templates/version.pt +0 -0
  68. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/SOURCES.txt +0 -0
  69. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/dependency_links.txt +0 -0
  70. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/entry_points.txt +0 -0
  71. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/requires.txt +0 -0
  72. {devpi_web-5.0.0 → devpi_web-5.0.2}/devpi_web.egg-info/top_level.txt +0 -0
  73. {devpi_web-5.0.0 → devpi_web-5.0.2}/setup.cfg +0 -0
  74. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/conftest.py +0 -0
  75. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_macro_registry.py +0 -0
  76. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_main.py +0 -0
  77. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_theme.py +0 -0
  78. {devpi_web-5.0.0 → devpi_web-5.0.2}/tests/test_views_docs.py +0 -0
@@ -0,0 +1,3 @@
1
+ [flake8]
2
+ # E203 is ignored because we enforce ruff format which contradicts it
3
+ ignore = E203,E501,E704,E741,W503
@@ -2,6 +2,35 @@
2
2
 
3
3
  .. towncrier release notes start
4
4
 
5
+ 5.0.2 (2026-03-17)
6
+ ==================
7
+
8
+ Bug Fixes
9
+ ---------
10
+
11
+ - Prevent double indexing work on first start of freshly initialized/imported instance.
12
+
13
+ - Optimize preparation of mirror project data for indexing.
14
+
15
+ - Consistently use offline mode when indexing. This especially helps performance on startup.
16
+
17
+ - Skip redirect if an installer is detected which is configured without ``+simple`` in the URL.
18
+
19
+ Other Changes
20
+ -------------
21
+
22
+ - Support string timestamps for file history log entries from devpi-server 7.0.0.
23
+
24
+
25
+ 5.0.1 (2025-06-15)
26
+ ==================
27
+
28
+ Bug Fixes
29
+ ---------
30
+
31
+ - Fix traceback during unpacking of documentation in some cases.
32
+
33
+
5
34
  5.0.0 (2025-06-12)
6
35
  ==================
7
36
 
@@ -14,15 +43,11 @@ Deprecations and Removals
14
43
 
15
44
  - Future releases will more often require newer devpi-server than before.
16
45
 
17
-
18
-
19
46
  Bug Fixes
20
47
  ---------
21
48
 
22
49
  - Fix #930: remove remaining uses of unmaintained py library.
23
50
 
24
-
25
-
26
51
  Other Changes
27
52
  -------------
28
53
 
@@ -59,7 +84,6 @@ Other Changes
59
84
  - macros.pt: Move ``versions`` macro to separate ``footer_versions.pt`` template.
60
85
 
61
86
 
62
-
63
87
  4.3.0 (2024-10-16)
64
88
  ==================
65
89
 
@@ -69,7 +93,6 @@ Features
69
93
  - index.pt, project.pt, version.pt: Fix #1062: Added a link to download the documentation as zip-file to the index, project and version view.
70
94
 
71
95
 
72
-
73
96
  4.2.3 (2024-09-19)
74
97
  ==================
75
98
 
@@ -85,7 +108,6 @@ Bug Fixes
85
108
  - Increase threshold for index status from 60 s to 300 s for warnings and from 300 s to 3600 s for fatal.
86
109
 
87
110
 
88
-
89
111
  4.2.2 (2024-04-20)
90
112
  ==================
91
113
 
@@ -99,7 +121,6 @@ Bug Fixes
99
121
  - Fix #970: overwrite fixed html/body heights like ``100%`` in documentation iframe content.
100
122
 
101
123
 
102
-
103
124
  4.2.1 (2023-07-02)
104
125
  ==================
105
126
 
@@ -121,7 +142,6 @@ Features
121
142
 
122
143
  - Add ``--keep-docs-packed`` option.
123
144
 
124
-
125
145
  Bug Fixes
126
146
  ---------
127
147
 
@@ -141,7 +161,6 @@ Bug Fixes
141
161
  4.1.1 (2022-09-28)
142
162
  ==================
143
163
 
144
-
145
164
  Bug Fixes
146
165
  ---------
147
166
 
@@ -160,7 +179,6 @@ Features
160
179
 
161
180
  - project.pt: add navigation links to original project page for mirrors and to "Simple page" of projects.
162
181
 
163
-
164
182
  Bug Fixes
165
183
  ---------
166
184
 
@@ -206,7 +224,6 @@ Bug Fixes
206
224
 
207
225
  - Use a lock file to prevent concurrent unpacking of documentation.
208
226
 
209
-
210
227
  Other Changes
211
228
  -------------
212
229
 
@@ -267,7 +284,6 @@ Deprecations and Removals
267
284
 
268
285
  - Dropped support for Python 2.7.
269
286
 
270
-
271
287
  Features
272
288
  --------
273
289
 
@@ -307,7 +323,6 @@ Features
307
323
 
308
324
  - Indexer backend options can now be specified as a dict in yaml config files.
309
325
 
310
-
311
326
  Bug Fixes
312
327
  ---------
313
328
 
@@ -315,7 +330,6 @@ Bug Fixes
315
330
 
316
331
  - Pass query string on to documentation iframe, this fixes Sphinx search among other things.
317
332
 
318
-
319
333
  Other Changes
320
334
  -------------
321
335
 
@@ -354,7 +368,6 @@ Features
354
368
 
355
369
  - feature #193: adding support for using markdown in package descriptions.
356
370
 
357
-
358
371
  Bug Fixes
359
372
  ---------
360
373
 
@@ -394,7 +407,6 @@ Features
394
407
 
395
408
  - Use ``mirror_web_url_fmt`` in ``get_description`` for mirror indexes.
396
409
 
397
-
398
410
  Bug Fixes
399
411
  ---------
400
412
 
@@ -619,7 +631,6 @@ Bug Fixes
619
631
  - fix notfound-redirect when serving under an outside URL with a sub path
620
632
 
621
633
 
622
-
623
634
  2.2.0
624
635
  =====
625
636
 
@@ -9,6 +9,35 @@ Changelog
9
9
 
10
10
  .. towncrier release notes start
11
11
 
12
+ 5.0.2 (2026-03-17)
13
+ ==================
14
+
15
+ Bug Fixes
16
+ ---------
17
+
18
+ - Prevent double indexing work on first start of freshly initialized/imported instance.
19
+
20
+ - Optimize preparation of mirror project data for indexing.
21
+
22
+ - Consistently use offline mode when indexing. This especially helps performance on startup.
23
+
24
+ - Skip redirect if an installer is detected which is configured without ``+simple`` in the URL.
25
+
26
+ Other Changes
27
+ -------------
28
+
29
+ - Support string timestamps for file history log entries from devpi-server 7.0.0.
30
+
31
+
32
+ 5.0.1 (2025-06-15)
33
+ ==================
34
+
35
+ Bug Fixes
36
+ ---------
37
+
38
+ - Fix traceback during unpacking of documentation in some cases.
39
+
40
+
12
41
  5.0.0 (2025-06-12)
13
42
  ==================
14
43
 
@@ -21,15 +50,11 @@ Deprecations and Removals
21
50
 
22
51
  - Future releases will more often require newer devpi-server than before.
23
52
 
24
-
25
-
26
53
  Bug Fixes
27
54
  ---------
28
55
 
29
56
  - Fix #930: remove remaining uses of unmaintained py library.
30
57
 
31
-
32
-
33
58
  Other Changes
34
59
  -------------
35
60
 
@@ -66,7 +91,6 @@ Other Changes
66
91
  - macros.pt: Move ``versions`` macro to separate ``footer_versions.pt`` template.
67
92
 
68
93
 
69
-
70
94
  4.3.0 (2024-10-16)
71
95
  ==================
72
96
 
@@ -76,7 +100,6 @@ Features
76
100
  - index.pt, project.pt, version.pt: Fix #1062: Added a link to download the documentation as zip-file to the index, project and version view.
77
101
 
78
102
 
79
-
80
103
  4.2.3 (2024-09-19)
81
104
  ==================
82
105
 
@@ -91,29 +114,3 @@ Bug Fixes
91
114
 
92
115
  - Increase threshold for index status from 60 s to 300 s for warnings and from 300 s to 3600 s for fatal.
93
116
 
94
-
95
-
96
- 4.2.2 (2024-04-20)
97
- ==================
98
-
99
- Bug Fixes
100
- ---------
101
-
102
- - style.css: Always let content be full browser height. This also gives more height with some documentation themes when content is short.
103
-
104
- - style.css: set ``scrollbar-gutter: stable`` on ``body`` to prevent jumping content in documentation iframe.
105
-
106
- - Fix #970: overwrite fixed html/body heights like ``100%`` in documentation iframe content.
107
-
108
-
109
-
110
- 4.2.1 (2023-07-02)
111
- ==================
112
-
113
- Bug Fixes
114
- ---------
115
-
116
- - Fix #953: Exception when browsers send ETag for documentation pages.
117
-
118
- - Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
119
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devpi-web
3
- Version: 5.0.0
3
+ Version: 5.0.2
4
4
  Summary: devpi-web: a web view for devpi-server
5
5
  Maintainer-email: Florian Schulze <mail@pyfidelity.com>
6
6
  License-Expression: MIT
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
23
24
  Classifier: Programming Language :: Python :: Implementation :: PyPy
24
25
  Classifier: Topic :: Internet :: WWW/HTTP
25
26
  Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
@@ -87,6 +88,35 @@ Changelog
87
88
 
88
89
  .. towncrier release notes start
89
90
 
91
+ 5.0.2 (2026-03-17)
92
+ ==================
93
+
94
+ Bug Fixes
95
+ ---------
96
+
97
+ - Prevent double indexing work on first start of freshly initialized/imported instance.
98
+
99
+ - Optimize preparation of mirror project data for indexing.
100
+
101
+ - Consistently use offline mode when indexing. This especially helps performance on startup.
102
+
103
+ - Skip redirect if an installer is detected which is configured without ``+simple`` in the URL.
104
+
105
+ Other Changes
106
+ -------------
107
+
108
+ - Support string timestamps for file history log entries from devpi-server 7.0.0.
109
+
110
+
111
+ 5.0.1 (2025-06-15)
112
+ ==================
113
+
114
+ Bug Fixes
115
+ ---------
116
+
117
+ - Fix traceback during unpacking of documentation in some cases.
118
+
119
+
90
120
  5.0.0 (2025-06-12)
91
121
  ==================
92
122
 
@@ -99,15 +129,11 @@ Deprecations and Removals
99
129
 
100
130
  - Future releases will more often require newer devpi-server than before.
101
131
 
102
-
103
-
104
132
  Bug Fixes
105
133
  ---------
106
134
 
107
135
  - Fix #930: remove remaining uses of unmaintained py library.
108
136
 
109
-
110
-
111
137
  Other Changes
112
138
  -------------
113
139
 
@@ -144,7 +170,6 @@ Other Changes
144
170
  - macros.pt: Move ``versions`` macro to separate ``footer_versions.pt`` template.
145
171
 
146
172
 
147
-
148
173
  4.3.0 (2024-10-16)
149
174
  ==================
150
175
 
@@ -154,7 +179,6 @@ Features
154
179
  - index.pt, project.pt, version.pt: Fix #1062: Added a link to download the documentation as zip-file to the index, project and version view.
155
180
 
156
181
 
157
-
158
182
  4.2.3 (2024-09-19)
159
183
  ==================
160
184
 
@@ -169,29 +193,3 @@ Bug Fixes
169
193
 
170
194
  - Increase threshold for index status from 60 s to 300 s for warnings and from 300 s to 3600 s for fatal.
171
195
 
172
-
173
-
174
- 4.2.2 (2024-04-20)
175
- ==================
176
-
177
- Bug Fixes
178
- ---------
179
-
180
- - style.css: Always let content be full browser height. This also gives more height with some documentation themes when content is short.
181
-
182
- - style.css: set ``scrollbar-gutter: stable`` on ``body`` to prevent jumping content in documentation iframe.
183
-
184
- - Fix #970: overwrite fixed html/body heights like ``100%`` in documentation iframe content.
185
-
186
-
187
-
188
- 4.2.1 (2023-07-02)
189
- ==================
190
-
191
- Bug Fixes
192
- ---------
193
-
194
- - Fix #953: Exception when browsers send ETag for documentation pages.
195
-
196
- - Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
197
-
@@ -0,0 +1 @@
1
+ __version__ = "5.0.2"
@@ -33,7 +33,9 @@ def locked_unpack_path(stage, name, version, *, remove_lock_file=False):
33
33
  hash_path = unpack_path.with_suffix(".hash")
34
34
  try:
35
35
  hash_path.parent.mkdir(parents=True, exist_ok=True)
36
- with hash_path.open("a+") as hash_file:
36
+ with suppress(FileExistsError), hash_path.open("x"):
37
+ pass
38
+ with hash_path.open("r+") as hash_file:
37
39
  if fcntl:
38
40
  fcntl.flock(hash_file, fcntl.LOCK_EX)
39
41
  try:
@@ -90,12 +92,13 @@ def unpack_docs(stage, name, version, entry):
90
92
  # we are not losing the original zip file anyway
91
93
  with locked_unpack_path(stage, name, version) as (hash_file, unpack_path):
92
94
  hash_file.seek(0)
93
- if hash_file.read().strip() == entry.best_available_hash_spec:
95
+ hash_spec = hash_file.read().strip()
96
+ if hash_spec == entry.best_available_hash_spec:
94
97
  return unpack_path
95
98
  if unpack_path.exists():
96
99
  with suppress(FileNotFoundError):
97
100
  # there is a rare possibility of a race condition here
98
- unpack_path.unlink()
101
+ shutil.rmtree(unpack_path)
99
102
  if not entry.file_exists():
100
103
  return unpack_path
101
104
  with entry.file_open_read() as f, Archive(f) as archive:
@@ -1,10 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from attrs import define
1
4
  from collections.abc import Mapping
2
- from devpi_common.types import ensure_unicode
3
5
  from devpi_common.metadata import get_sorted_versions
6
+ from devpi_common.types import ensure_unicode
4
7
  from devpi_common.validation import normalize_name
5
8
  from devpi_web.config import get_pluginmanager
6
9
  from devpi_web.doczip import Docs
7
- import attr
10
+ from typing import TYPE_CHECKING
11
+
12
+
13
+ if TYPE_CHECKING:
14
+ from typing import Any
8
15
 
9
16
 
10
17
  def is_project_cached(stage, project):
@@ -14,7 +21,7 @@ def is_project_cached(stage, project):
14
21
  return True
15
22
 
16
23
 
17
- def preprocess_project(project):
24
+ def preprocess_project(project: ProjectIndexingInfo) -> dict | None:
18
25
  stage = project.stage
19
26
  pm = get_pluginmanager(stage.xom.config)
20
27
  name = normalize_name(project.name)
@@ -25,15 +32,19 @@ def preprocess_project(project):
25
32
  user, index = stage.name.split('/')
26
33
  user = ensure_unicode(user)
27
34
  index = ensure_unicode(index)
28
- if not is_project_cached(stage, name):
29
- result = dict(name=project.name, user=user, index=index)
30
- pm.hook.devpiweb_modify_preprocess_project_result(
31
- project=project, result=result)
32
- return result
33
- stage.offline = True
34
- if not stage.has_project_perstage(name):
35
+ result: dict[str, object]
36
+ if stage.ixconfig["type"] == "mirror":
37
+ stage.offline = True
38
+ if not stage.is_project_cached(name):
39
+ # only index basic info for projects with no downloads
40
+ result = dict(name=project.name, user=user, index=index)
41
+ pm.hook.devpiweb_modify_preprocess_project_result(
42
+ project=project, result=result
43
+ )
44
+ return result
45
+ elif not stage.has_project_perstage(name):
35
46
  # project doesn't exist anymore
36
- return
47
+ return None
37
48
  # metadata_keys is only available on private indexes
38
49
  setuptools_metadata = frozenset(getattr(stage, 'metadata_keys', ()))
39
50
  versions = get_sorted_versions(stage.list_versions_perstage(name))
@@ -49,8 +60,7 @@ def preprocess_project(project):
49
60
  result['doc_version'] = version
50
61
  result['+doczip'] = docs
51
62
  break
52
- else:
53
- assert '+doczip' not in result
63
+ assert "+doczip" not in result
54
64
 
55
65
  result[u'user'] = user
56
66
  result[u'index'] = index
@@ -64,17 +74,18 @@ def preprocess_project(project):
64
74
  return result
65
75
 
66
76
 
67
- @attr.s(slots=True)
68
- class ProjectIndexingInfo(object):
69
- stage = attr.ib()
70
- name = attr.ib(type=str)
77
+ @define
78
+ class ProjectIndexingInfo:
79
+ stage: Any
80
+ name: str
81
+ num_names: int
71
82
 
72
83
  @property
73
- def indexname(self):
84
+ def indexname(self) -> str:
74
85
  return self.stage.name
75
86
 
76
87
  @property
77
- def is_from_mirror(self):
88
+ def is_from_mirror(self) -> bool:
78
89
  return self.stage.ixconfig['type'] == 'mirror'
79
90
 
80
91
 
@@ -92,18 +103,21 @@ def iter_indexes(xom):
92
103
  yield from mirrors
93
104
 
94
105
 
95
- def iter_projects(xom):
106
+ def iter_projects(xom, *, offline=True):
96
107
  for username, index in iter_indexes(xom):
97
108
  stage = xom.model.getstage(username, index)
98
109
  if stage is None: # this is async, so the stage may be gone
99
110
  continue
100
111
  names = stage.list_projects_perstage()
112
+ # only go offline after we got the projects list
113
+ if stage.ixconfig["type"] == "mirror":
114
+ stage.offline = offline
101
115
  if isinstance(names, Mapping):
102
116
  # since devpi-server 6.6.0 mirrors return a mapping where
103
117
  # the un-normalized names are in the values
104
118
  names = names.values()
119
+ num_names = len(names)
105
120
  for name in names:
106
- name = ensure_unicode(name)
107
121
  yield ProjectIndexingInfo(
108
- stage=stage,
109
- name=name)
122
+ stage=stage, name=ensure_unicode(name), num_names=num_names
123
+ )
@@ -101,9 +101,8 @@ def status_info(request):
101
101
  msgs = []
102
102
  pm = request.registry['devpiweb-pluginmanager']
103
103
  for result in pm.hook.devpiweb_get_status_info(request=request):
104
- for msg in result:
105
- msgs.append(msg)
106
- states = set(x['status'] for x in msgs)
104
+ msgs.extend(result)
105
+ states = {x["status"] for x in msgs}
107
106
  if 'fatal' in states:
108
107
  status = 'fatal'
109
108
  short_msg = 'fatal'
@@ -284,9 +283,11 @@ def devpiserver_mirror_initialnames(stage, projectnames):
284
283
  # since devpi-server 6.6.0 mirrors return a mapping where
285
284
  # the un-normalized names are in the values
286
285
  projectnames = projectnames.values()
286
+ num_names = len(projectnames)
287
287
  ix.update_projects(
288
- ProjectIndexingInfo(stage=stage, name=name)
289
- for name in projectnames)
288
+ ProjectIndexingInfo(stage=stage, name=name, num_names=num_names)
289
+ for name in projectnames
290
+ )
290
291
  threadlog.info("finished mirror indexing operation")
291
292
 
292
293
 
@@ -315,14 +316,14 @@ def delete_project(stage, name):
315
316
  if stage is None:
316
317
  return
317
318
  ix = get_indexer(stage.xom)
318
- ix.delete_projects([ProjectIndexingInfo(stage=stage, name=name)])
319
+ ix.delete_projects([ProjectIndexingInfo(stage=stage, name=name, num_names=1)])
319
320
 
320
321
 
321
322
  def index_project(stage, name):
322
323
  if stage is None:
323
324
  return
324
325
  ix = get_indexer(stage.xom)
325
- ix.update_projects([ProjectIndexingInfo(stage=stage, name=name)])
326
+ ix.update_projects([ProjectIndexingInfo(stage=stage, name=name, num_names=1)])
326
327
 
327
328
 
328
329
  @devpiserver_hookimpl
@@ -338,9 +339,10 @@ def devpiserver_on_upload(stage, project, version, link):
338
339
  @devpiserver_hookimpl
339
340
  def devpiserver_on_changed_versiondata(stage, project, version, metadata):
340
341
  if stage is None:
341
- # TODO we don't have enough info to delete the project
342
342
  return
343
343
  if not metadata:
344
+ if stage.ixconfig["type"] == "mirror":
345
+ stage.offline = True
344
346
  if is_project_cached(stage, project) and not stage.has_project_perstage(project):
345
347
  delete_project(stage, project)
346
348
  return
@@ -24,9 +24,7 @@ class Index:
24
24
  return dict(items=[])
25
25
 
26
26
  def _matcher(self, searchinfo):
27
- items = list(
28
- re.escape(x)
29
- for x in searchinfo['fields']['name'])
27
+ items = [re.escape(x) for x in searchinfo["fields"]["name"]]
30
28
  return re.compile('|'.join(items)).search
31
29
 
32
30
  def query_packages(self, searchinfo, sro):