()
| 132 | |
| 133 | |
| 134 | def iter_plugins() -> Iterator[PluginInfo]: |
| 135 | session = get_session() |
| 136 | name_2_serial = pytest_plugin_projects_from_pypi(session) |
| 137 | |
| 138 | for name, last_serial in tqdm(name_2_serial.items(), smoothing=0): |
| 139 | response = project_response_with_refresh(session, name, last_serial) |
| 140 | if response.status_code == 404: |
| 141 | # Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple |
| 142 | # but return 404 on the JSON API. Skip. |
| 143 | continue |
| 144 | response.raise_for_status() |
| 145 | info = response.json()["info"] |
| 146 | if "Development Status :: 7 - Inactive" in info["classifiers"]: |
| 147 | continue |
| 148 | for classifier in DEVELOPMENT_STATUS_CLASSIFIERS: |
| 149 | if classifier in info["classifiers"]: |
| 150 | status = classifier[22:] |
| 151 | break |
| 152 | else: |
| 153 | status = "N/A" |
| 154 | requires = "N/A" |
| 155 | if info["requires_dist"]: |
| 156 | for requirement in info["requires_dist"]: |
| 157 | if re.match(r"pytest(?![-.\w])", requirement): |
| 158 | requires = requirement |
| 159 | break |
| 160 | |
| 161 | def version_sort_key(version_string: str) -> Any: |
| 162 | """ |
| 163 | Return the sort key for the given version string |
| 164 | returned by the API. |
| 165 | """ |
| 166 | try: |
| 167 | return packaging.version.parse(version_string) |
| 168 | except packaging.version.InvalidVersion: |
| 169 | # Use a hard-coded pre-release version. |
| 170 | return packaging.version.Version("0.0.0alpha") |
| 171 | |
| 172 | releases = response.json()["releases"] |
| 173 | for release in sorted(releases, key=version_sort_key, reverse=True): |
| 174 | if releases[release]: |
| 175 | release_date = datetime.date.fromisoformat( |
| 176 | releases[release][-1]["upload_time_iso_8601"].split("T")[0] |
| 177 | ) |
| 178 | last_release = release_date.strftime("%b %d, %Y") |
| 179 | break |
| 180 | name = f":pypi:`{info['name']}`" |
| 181 | summary = "" |
| 182 | if info["summary"]: |
| 183 | summary = escape_rst(info["summary"].replace("\n", "")) |
| 184 | yield { |
| 185 | "name": name, |
| 186 | "summary": summary.strip(), |
| 187 | "last_release": last_release, |
| 188 | "status": status, |
| 189 | "requires": requires, |
| 190 | } |
| 191 |
nothing calls this directly
no test coverage detected