github-dependents-info 3.2.1__tar.gz → 4.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.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-dependents-info
3
- Version: 3.2.1
3
+ Version: 4.1.0
4
4
  Summary: Collect information about dependencies between a github repo and other repositories. Results available in JSON, markdown and badges.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
7
7
  Keywords: github,dependent,used-by,repository,stats,dependencies,using
8
8
  Author: nvuillam
9
9
  Author-email: nicolas.vuillamy@gmail.com
10
- Requires-Python: >=3.10,<4.0
10
+ Requires-Python: >=3.11,<4.0
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: OS Independent
@@ -15,12 +15,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3.9
17
17
  Requires-Dist: beautifulsoup4 (==4.15.0)
18
- Requires-Dist: click (>=8.4.1,<8.5)
18
+ Requires-Dist: click (>=8.5,<8.6)
19
19
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
20
- Requires-Dist: idna (>=3.14)
21
- Requires-Dist: pandas (>=2.3.3,<3.0)
20
+ Requires-Dist: idna (>=3.20)
21
+ Requires-Dist: pandas (>=3.1,<3.2)
22
22
  Requires-Dist: rich (>=15,<15.1)
23
- Requires-Dist: typer (>=0.25.1,<0.26)
23
+ Requires-Dist: soupsieve (>=2.8.4)
24
+ Requires-Dist: typer (>=0.27.1,<0.28)
24
25
  Project-URL: Homepage, https://github.com/nvuillam/github-dependents-info
25
26
  Project-URL: Repository, https://github.com/nvuillam/github-dependents-info
26
27
  Description-Content-Type: text/markdown
@@ -37,7 +38,7 @@ Description-Content-Type: text/markdown
37
38
  [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/nvuillam/github-dependents-info/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
38
39
 
39
40
  [![GitHub contributors](https://img.shields.io/github/contributors/nvuillam/github-dependents-info.svg)](https://github.com/nvuillam/github-dependents-info/graphs/contributors/)<!-- gh-dependents-info-used-by-start -->
40
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
41
+ [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=54&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
41
42
  [![GitHub Sponsors](https://img.shields.io/github/sponsors/nvuillam)](https://github.com/sponsors/nvuillam)
42
43
  [![MegaLinter](https://github.com/nvuillam/github-dependents-info/actions/workflows/mega-linter.yml/badge.svg)](https://github.com/nvuillam/github-dependents-info/actions/workflows/mega-linter.yml)
43
44
  [![License](https://img.shields.io/github/license/nvuillam/github-dependents-info)](https://github.com/nvuillam/github-dependents-info/blob/master/LICENSE)
@@ -62,8 +63,7 @@ This package uses GitHub HTML to collect dependents information and can:
62
63
  - Generate summary markdown file
63
64
  - Optionally add an AI-generated usage summary (via `litellm`) when an LLM API key is present
64
65
  - Update existing markdown by inserting **Used by** badge within tags
65
- - `<!-- gh-dependents-info-used-by-start -->
66
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`
66
+ - <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>
67
67
  - Handle multiple repositories packages
68
68
  - Filter results using minimum stars or by repository owner
69
69
  - Keep huge ecosystems manageable with pagination controls (`--max-scraped-pages`, `--pagination/--no-pagination`, `--page-size`)
@@ -268,31 +268,30 @@ _________________
268
268
  | Parameter | Type | Description |
269
269
  |-----------|--------|----------------------------------------------|
270
270
  | --repo | String | Repository. Example: `oxsecurity/megalinter` |
271
- | -b<br/> --badgemarkdownfile | String | _(optional)_ Path to markdown file where to insert/update **Used by** badge <br/> (must contain tags `<!-- gh-dependents-info-used-by-start -->
272
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`) |
273
- | -s<br/> --sort | String | _(optional)_ Sort order: name (default) or stars |
274
- | -x<br/> --minstars | String | _(optional)_ If set, filters repositories to keep only those with more than X stars |
275
- | -m<br/> --markdownfile | String | _(optional)_ Output markdown file file |
276
- | -d<br/> --docurl | String | _(optional)_ Hyperlink to use when clicking on badge markdown file badge. (Default: link to markdown file) |
277
- | -p<br/> --mergepackages | String | _(optional)_ In case of multiple packages, merge their stats in a single one in markdown and json output |
278
- | -j<br/> --json | String | _(optional)_ Output in json format |
279
- | -u<br/> --owner | String | _(optional)_ If set, filters repositories to keep only those owned by the specified user/organization |
280
- | -n<br/> --max-scraped-pages | Integer | _(optional)_ Maximum number of GitHub pages to scrape per package (0 uses all available pages) |
281
- | --pagination<br/> --no-pagination | Boolean | _(optional)_ Enable (default) or disable pagination when writing markdown output |
282
- | --page-size | Integer | _(optional)_ Number of repositories per markdown page when pagination is enabled (default: 500) |
283
- | --llm-summary<br/> --no-llm-summary | Boolean | _(optional)_ Generate an AI usage summary in markdown output when an LLM API key is present (default: enabled) |
284
- | --llm-model | String | _(optional)_ LiteLLM model to use for the summary. If not set, a lightweight model is selected based on the detected API key provider |
285
- | --llm-max-repos | Integer | _(optional)_ Max dependent repos included in the summary prompt payload (default: 500) |
286
- | --llm-max-words | Integer | _(optional)_ Max words for the generated summary (default: 300) |
287
- | --llm-timeout | Float | _(optional)_ Timeout (seconds) for the summary LLM call (default: 120) |
288
- | -v<br/> --version | Boolean | _(optional)_ Displays version of github-dependents-info |
289
- | --verbose | Boolean | _(optional)_ Verbose output |
271
+ | -b<br/> --badgemarkdownfile | String | _(optional)_ Path to markdown file where to insert/update **Used by** badge <br/> (must contain tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>) |
272
+ | -s<br/> --sort | String | _(optional)_ Sort order: name (default) or stars |
273
+ | -x<br/> --minstars | String | _(optional)_ If set, filters repositories to keep only those with more than X stars |
274
+ | -m<br/> --markdownfile | String | _(optional)_ Output markdown file file |
275
+ | -d<br/> --docurl | String | _(optional)_ Hyperlink to use when clicking on badge markdown file badge. (Default: link to markdown file) |
276
+ | -p<br/> --mergepackages | String | _(optional)_ In case of multiple packages, merge their stats in a single one in markdown and json output |
277
+ | -j<br/> --json | String | _(optional)_ Output in json format |
278
+ | -u<br/> --owner | String | _(optional)_ If set, filters repositories to keep only those owned by the specified user/organization |
279
+ | -n<br/> --max-scraped-pages | Integer | _(optional)_ Maximum number of GitHub pages to scrape per package (0 uses all available pages) |
280
+ | --pagination<br/> --no-pagination | Boolean | _(optional)_ Enable (default) or disable pagination when writing markdown output |
281
+ | --page-size | Integer | _(optional)_ Number of repositories per markdown page when pagination is enabled (default: 500) |
282
+ | --llm-summary<br/> --no-llm-summary | Boolean | _(optional)_ Generate an AI usage summary in markdown output when an LLM API key is present (default: enabled) |
283
+ | --llm-model | String | _(optional)_ LiteLLM model to use for the summary. If not set, a lightweight model is selected based on the detected API key provider |
284
+ | --llm-max-repos | Integer | _(optional)_ Max dependent repos included in the summary prompt payload (default: 500) |
285
+ | --llm-max-words | Integer | _(optional)_ Max words for the generated summary (default: 300) |
286
+ | --llm-timeout | Float | _(optional)_ Timeout (seconds) for the summary LLM call (default: 120) |
287
+ | -v<br/> --version | Boolean | _(optional)_ Displays version of github-dependents-info |
288
+ | --verbose | Boolean | _(optional)_ Verbose output |
290
289
 
291
290
  Badge tags example (the tool replaces everything between the markers):
292
291
 
293
292
  ```markdown
294
293
  <!-- gh-dependents-info-used-by-start -->
295
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
294
+ [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=54&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
296
295
  ```
297
296
 
298
297
  _________________
@@ -306,8 +305,7 @@ _________________
306
305
 
307
306
  github-dependents-info --repo nvuillam/npm-groovy-lint --json
308
307
 
309
- - Insert/Update **Used by** markdown badge within an existing markdown file containing tags `<!-- gh-dependents-info-used-by-start -->
310
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`
308
+ - Insert/Update **Used by** markdown badge within an existing markdown file containing tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>
311
309
 
312
310
  github-dependents-info --repo nvuillam/npm-groovy-lint --badgemarkdownfile ./README.md
313
311
 
@@ -367,6 +365,7 @@ If an LLM API key is detected in the environment (for example `OPENAI_API_KEY`),
367
365
  - Override model with `--llm-model` (or env var `GITHUB_DEPENDENTS_INFO_LLM_MODEL` / `LITELLM_MODEL`)
368
366
  - Adjust max summary length with `--llm-max-words` (or env var `GITHUB_DEPENDENTS_INFO_LLM_MAX_WORDS`)
369
367
  - The summary is cached in `--csvdirectory` (file `llm_summary_<repo>.json`) and reused on subsequent runs
368
+ - A previously generated summary is never lost: if a new summary can not be generated (no API key anymore, no credit left, rate limit, timeout...), the summary found in the cache or in the existing `--markdownfile` is reused
370
369
 
371
370
 
372
371
  ## Use as GitHub Action
@@ -379,8 +378,7 @@ Create a file **.github/workflows/github-dependents-info.yml** in your repositor
379
378
 
380
379
  If will generate a new Pull Request (or replace the pending one) every time the usage stats will have changed :)
381
380
 
382
- Don't forget to add tags `<!-- gh-dependents-info-used-by-start -->
383
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->` in your **README.md**, at the end of another badge line if you want github-dependents-info to replace its content automatically.
381
+ Don't forget to add tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code> in your **README.md**, at the end of another badge line if you want github-dependents-info to replace its content automatically.
384
382
 
385
383
  ```yaml
386
384
  # GitHub Dependents Info workflow
@@ -431,6 +429,9 @@ jobs:
431
429
  # badgemarkdownfile: README.md
432
430
  # sort: stars
433
431
  # minstars: "0"
432
+ # pagination: "true" # set to "false" to write a single markdown file
433
+ # page-size: "500" # number of repositories per markdown page
434
+ # max-scraped-pages: "0" # max pages to scrape per package (0 = no limit)
434
435
  # llm-summary: "true" # set to "false" to disable AI usage summary
435
436
  # llm-model: "" # optional: override LiteLLM model (example: gpt-4o-mini, gemini-3-flash-preview)
436
437
  # llm-max-repos: "500" # optional: cap repos sent to the summary prompt
@@ -467,6 +468,37 @@ jobs:
467
468
  STEPS_CPR_OUTPUTS_PULL_REQUEST_URL: ${{ steps.cpr.outputs.pull-request-url }}
468
469
  ```
469
470
 
471
+ ### GitHub Action inputs
472
+
473
+ Action inputs are named after the CLI options, without the leading `--`. Boolean CLI flags such as `--no-pagination` become inputs with a `"true"` / `"false"` value.
474
+
475
+ | Input | Description | CLI equivalent | Default |
476
+ |----------------------|--------------------------------------------------------------------------------|--------------------------------------|------------------------------------|
477
+ | `repo` | Owner and name of the repository (required) | `--repo` | _(required)_ |
478
+ | `outputrepo` | Owner and name of the output repository, if different from `repo` | `--outputrepo` | `""` |
479
+ | `markdownfile` | Path of the output markdown file | `--markdownfile` | `"docs/github-dependents-info.md"` |
480
+ | `badgemarkdownfile` | File where badges are replaced (between the `gh-dependents-info-used-by` tags) | `--badgemarkdownfile` | `"README.md"` |
481
+ | `docurl` | Custom URL for the badges hyperlink | `--docurl` | `""` |
482
+ | `markdownbadgecolor` | Color of the generated badges | `--markdownbadgecolor` | `"informational"` |
483
+ | `sort` | Sort criteria: `stars` or `name` | `--sort` | `"stars"` |
484
+ | `minstars` | Minimum number of stars to appear in the results | `--minstars` | `"0"` |
485
+ | `timedelay` | Delay in seconds between GitHub requests | `--timedelay` | `"0.1"` |
486
+ | `csvdirectory` | Directory where CSV progress files are written | `--csvdirectory` | `""` |
487
+ | `mergepackages` | `true` to merge multiple packages into a single result | `--mergepackages` | `"false"` |
488
+ | `json` | `true` to emit JSON output | `--json` | `"false"` |
489
+ | `verbose` | `true` to enable verbose logging | `--verbose` | `"false"` |
490
+ | `overwrite` | `true` to overwrite existing CSV progress files | `--overwrite` | `"false"` |
491
+ | `owner` | Filter dependent repositories by owner | `--owner` | `""` |
492
+ | `max-scraped-pages` | Max pages to scrape per package (`0` = no limit) | `--max-scraped-pages` | `"0"` |
493
+ | `pagination` | `false` to write a single markdown file instead of multiple pages | `--pagination` / `--no-pagination` | `"true"` |
494
+ | `no-pagination` | `true` to disable pagination (same as `pagination: "false"`) | `--no-pagination` | `"false"` |
495
+ | `page-size` | Number of repositories per markdown page when pagination is enabled | `--page-size` | `"500"` |
496
+ | `llm-summary` | `false` to disable the AI usage summary | `--llm-summary` / `--no-llm-summary` | `"true"` |
497
+ | `llm-model` | LiteLLM model for the AI usage summary | `--llm-model` | `""` |
498
+ | `llm-max-repos` | Max dependent repos included in the summary prompt | `--llm-max-repos` | `"500"` |
499
+ | `llm-max-words` | Max words for the generated summary | `--llm-max-words` | `"250"` |
500
+ | `llm-timeout` | Timeout (seconds) for the summary LLM call | `--llm-timeout` | `"120"` |
501
+
470
502
  _________________
471
503
  ## 🛡 License
472
504
 
@@ -10,7 +10,7 @@
10
10
  [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/nvuillam/github-dependents-info/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
11
11
 
12
12
  [![GitHub contributors](https://img.shields.io/github/contributors/nvuillam/github-dependents-info.svg)](https://github.com/nvuillam/github-dependents-info/graphs/contributors/)<!-- gh-dependents-info-used-by-start -->
13
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
13
+ [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=54&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
14
14
  [![GitHub Sponsors](https://img.shields.io/github/sponsors/nvuillam)](https://github.com/sponsors/nvuillam)
15
15
  [![MegaLinter](https://github.com/nvuillam/github-dependents-info/actions/workflows/mega-linter.yml/badge.svg)](https://github.com/nvuillam/github-dependents-info/actions/workflows/mega-linter.yml)
16
16
  [![License](https://img.shields.io/github/license/nvuillam/github-dependents-info)](https://github.com/nvuillam/github-dependents-info/blob/master/LICENSE)
@@ -35,8 +35,7 @@ This package uses GitHub HTML to collect dependents information and can:
35
35
  - Generate summary markdown file
36
36
  - Optionally add an AI-generated usage summary (via `litellm`) when an LLM API key is present
37
37
  - Update existing markdown by inserting **Used by** badge within tags
38
- - `<!-- gh-dependents-info-used-by-start -->
39
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`
38
+ - <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>
40
39
  - Handle multiple repositories packages
41
40
  - Filter results using minimum stars or by repository owner
42
41
  - Keep huge ecosystems manageable with pagination controls (`--max-scraped-pages`, `--pagination/--no-pagination`, `--page-size`)
@@ -241,31 +240,30 @@ _________________
241
240
  | Parameter | Type | Description |
242
241
  |-----------|--------|----------------------------------------------|
243
242
  | --repo | String | Repository. Example: `oxsecurity/megalinter` |
244
- | -b<br/> --badgemarkdownfile | String | _(optional)_ Path to markdown file where to insert/update **Used by** badge <br/> (must contain tags `<!-- gh-dependents-info-used-by-start -->
245
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`) |
246
- | -s<br/> --sort | String | _(optional)_ Sort order: name (default) or stars |
247
- | -x<br/> --minstars | String | _(optional)_ If set, filters repositories to keep only those with more than X stars |
248
- | -m<br/> --markdownfile | String | _(optional)_ Output markdown file file |
249
- | -d<br/> --docurl | String | _(optional)_ Hyperlink to use when clicking on badge markdown file badge. (Default: link to markdown file) |
250
- | -p<br/> --mergepackages | String | _(optional)_ In case of multiple packages, merge their stats in a single one in markdown and json output |
251
- | -j<br/> --json | String | _(optional)_ Output in json format |
252
- | -u<br/> --owner | String | _(optional)_ If set, filters repositories to keep only those owned by the specified user/organization |
253
- | -n<br/> --max-scraped-pages | Integer | _(optional)_ Maximum number of GitHub pages to scrape per package (0 uses all available pages) |
254
- | --pagination<br/> --no-pagination | Boolean | _(optional)_ Enable (default) or disable pagination when writing markdown output |
255
- | --page-size | Integer | _(optional)_ Number of repositories per markdown page when pagination is enabled (default: 500) |
256
- | --llm-summary<br/> --no-llm-summary | Boolean | _(optional)_ Generate an AI usage summary in markdown output when an LLM API key is present (default: enabled) |
257
- | --llm-model | String | _(optional)_ LiteLLM model to use for the summary. If not set, a lightweight model is selected based on the detected API key provider |
258
- | --llm-max-repos | Integer | _(optional)_ Max dependent repos included in the summary prompt payload (default: 500) |
259
- | --llm-max-words | Integer | _(optional)_ Max words for the generated summary (default: 300) |
260
- | --llm-timeout | Float | _(optional)_ Timeout (seconds) for the summary LLM call (default: 120) |
261
- | -v<br/> --version | Boolean | _(optional)_ Displays version of github-dependents-info |
262
- | --verbose | Boolean | _(optional)_ Verbose output |
243
+ | -b<br/> --badgemarkdownfile | String | _(optional)_ Path to markdown file where to insert/update **Used by** badge <br/> (must contain tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>) |
244
+ | -s<br/> --sort | String | _(optional)_ Sort order: name (default) or stars |
245
+ | -x<br/> --minstars | String | _(optional)_ If set, filters repositories to keep only those with more than X stars |
246
+ | -m<br/> --markdownfile | String | _(optional)_ Output markdown file file |
247
+ | -d<br/> --docurl | String | _(optional)_ Hyperlink to use when clicking on badge markdown file badge. (Default: link to markdown file) |
248
+ | -p<br/> --mergepackages | String | _(optional)_ In case of multiple packages, merge their stats in a single one in markdown and json output |
249
+ | -j<br/> --json | String | _(optional)_ Output in json format |
250
+ | -u<br/> --owner | String | _(optional)_ If set, filters repositories to keep only those owned by the specified user/organization |
251
+ | -n<br/> --max-scraped-pages | Integer | _(optional)_ Maximum number of GitHub pages to scrape per package (0 uses all available pages) |
252
+ | --pagination<br/> --no-pagination | Boolean | _(optional)_ Enable (default) or disable pagination when writing markdown output |
253
+ | --page-size | Integer | _(optional)_ Number of repositories per markdown page when pagination is enabled (default: 500) |
254
+ | --llm-summary<br/> --no-llm-summary | Boolean | _(optional)_ Generate an AI usage summary in markdown output when an LLM API key is present (default: enabled) |
255
+ | --llm-model | String | _(optional)_ LiteLLM model to use for the summary. If not set, a lightweight model is selected based on the detected API key provider |
256
+ | --llm-max-repos | Integer | _(optional)_ Max dependent repos included in the summary prompt payload (default: 500) |
257
+ | --llm-max-words | Integer | _(optional)_ Max words for the generated summary (default: 300) |
258
+ | --llm-timeout | Float | _(optional)_ Timeout (seconds) for the summary LLM call (default: 120) |
259
+ | -v<br/> --version | Boolean | _(optional)_ Displays version of github-dependents-info |
260
+ | --verbose | Boolean | _(optional)_ Verbose output |
263
261
 
264
262
  Badge tags example (the tool replaces everything between the markers):
265
263
 
266
264
  ```markdown
267
265
  <!-- gh-dependents-info-used-by-start -->
268
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
266
+ [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=54&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
269
267
  ```
270
268
 
271
269
  _________________
@@ -279,8 +277,7 @@ _________________
279
277
 
280
278
  github-dependents-info --repo nvuillam/npm-groovy-lint --json
281
279
 
282
- - Insert/Update **Used by** markdown badge within an existing markdown file containing tags `<!-- gh-dependents-info-used-by-start -->
283
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->`
280
+ - Insert/Update **Used by** markdown badge within an existing markdown file containing tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code>
284
281
 
285
282
  github-dependents-info --repo nvuillam/npm-groovy-lint --badgemarkdownfile ./README.md
286
283
 
@@ -340,6 +337,7 @@ If an LLM API key is detected in the environment (for example `OPENAI_API_KEY`),
340
337
  - Override model with `--llm-model` (or env var `GITHUB_DEPENDENTS_INFO_LLM_MODEL` / `LITELLM_MODEL`)
341
338
  - Adjust max summary length with `--llm-max-words` (or env var `GITHUB_DEPENDENTS_INFO_LLM_MAX_WORDS`)
342
339
  - The summary is cached in `--csvdirectory` (file `llm_summary_<repo>.json`) and reused on subsequent runs
340
+ - A previously generated summary is never lost: if a new summary can not be generated (no API key anymore, no credit left, rate limit, timeout...), the summary found in the cache or in the existing `--markdownfile` is reused
343
341
 
344
342
 
345
343
  ## Use as GitHub Action
@@ -352,8 +350,7 @@ Create a file **.github/workflows/github-dependents-info.yml** in your repositor
352
350
 
353
351
  If will generate a new Pull Request (or replace the pending one) every time the usage stats will have changed :)
354
352
 
355
- Don't forget to add tags `<!-- gh-dependents-info-used-by-start -->
356
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=51&color=informational&logo=slickpic)](https://github.com/nvuillam/github-dependents-info/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->` in your **README.md**, at the end of another badge line if you want github-dependents-info to replace its content automatically.
353
+ Don't forget to add tags <code>&lt;!-- gh-dependents-info-used-by-start --&gt;</code> and <code>&lt;!-- gh-dependents-info-used-by-end --&gt;</code> in your **README.md**, at the end of another badge line if you want github-dependents-info to replace its content automatically.
357
354
 
358
355
  ```yaml
359
356
  # GitHub Dependents Info workflow
@@ -404,6 +401,9 @@ jobs:
404
401
  # badgemarkdownfile: README.md
405
402
  # sort: stars
406
403
  # minstars: "0"
404
+ # pagination: "true" # set to "false" to write a single markdown file
405
+ # page-size: "500" # number of repositories per markdown page
406
+ # max-scraped-pages: "0" # max pages to scrape per package (0 = no limit)
407
407
  # llm-summary: "true" # set to "false" to disable AI usage summary
408
408
  # llm-model: "" # optional: override LiteLLM model (example: gpt-4o-mini, gemini-3-flash-preview)
409
409
  # llm-max-repos: "500" # optional: cap repos sent to the summary prompt
@@ -440,6 +440,37 @@ jobs:
440
440
  STEPS_CPR_OUTPUTS_PULL_REQUEST_URL: ${{ steps.cpr.outputs.pull-request-url }}
441
441
  ```
442
442
 
443
+ ### GitHub Action inputs
444
+
445
+ Action inputs are named after the CLI options, without the leading `--`. Boolean CLI flags such as `--no-pagination` become inputs with a `"true"` / `"false"` value.
446
+
447
+ | Input | Description | CLI equivalent | Default |
448
+ |----------------------|--------------------------------------------------------------------------------|--------------------------------------|------------------------------------|
449
+ | `repo` | Owner and name of the repository (required) | `--repo` | _(required)_ |
450
+ | `outputrepo` | Owner and name of the output repository, if different from `repo` | `--outputrepo` | `""` |
451
+ | `markdownfile` | Path of the output markdown file | `--markdownfile` | `"docs/github-dependents-info.md"` |
452
+ | `badgemarkdownfile` | File where badges are replaced (between the `gh-dependents-info-used-by` tags) | `--badgemarkdownfile` | `"README.md"` |
453
+ | `docurl` | Custom URL for the badges hyperlink | `--docurl` | `""` |
454
+ | `markdownbadgecolor` | Color of the generated badges | `--markdownbadgecolor` | `"informational"` |
455
+ | `sort` | Sort criteria: `stars` or `name` | `--sort` | `"stars"` |
456
+ | `minstars` | Minimum number of stars to appear in the results | `--minstars` | `"0"` |
457
+ | `timedelay` | Delay in seconds between GitHub requests | `--timedelay` | `"0.1"` |
458
+ | `csvdirectory` | Directory where CSV progress files are written | `--csvdirectory` | `""` |
459
+ | `mergepackages` | `true` to merge multiple packages into a single result | `--mergepackages` | `"false"` |
460
+ | `json` | `true` to emit JSON output | `--json` | `"false"` |
461
+ | `verbose` | `true` to enable verbose logging | `--verbose` | `"false"` |
462
+ | `overwrite` | `true` to overwrite existing CSV progress files | `--overwrite` | `"false"` |
463
+ | `owner` | Filter dependent repositories by owner | `--owner` | `""` |
464
+ | `max-scraped-pages` | Max pages to scrape per package (`0` = no limit) | `--max-scraped-pages` | `"0"` |
465
+ | `pagination` | `false` to write a single markdown file instead of multiple pages | `--pagination` / `--no-pagination` | `"true"` |
466
+ | `no-pagination` | `true` to disable pagination (same as `pagination: "false"`) | `--no-pagination` | `"false"` |
467
+ | `page-size` | Number of repositories per markdown page when pagination is enabled | `--page-size` | `"500"` |
468
+ | `llm-summary` | `false` to disable the AI usage summary | `--llm-summary` / `--no-llm-summary` | `"true"` |
469
+ | `llm-model` | LiteLLM model for the AI usage summary | `--llm-model` | `""` |
470
+ | `llm-max-repos` | Max dependent repos included in the summary prompt | `--llm-max-repos` | `"500"` |
471
+ | `llm-max-words` | Max words for the generated summary | `--llm-max-words` | `"250"` |
472
+ | `llm-timeout` | Timeout (seconds) for the summary LLM call | `--llm-timeout` | `"120"` |
473
+
443
474
  _________________
444
475
  ## 🛡 License
445
476
 
@@ -1,10 +1,12 @@
1
- import logging
2
1
  from typing import Annotated
3
2
 
3
+ import logging
4
+
4
5
  import typer
6
+ from rich.console import Console
7
+
5
8
  from github_dependents_info import version
6
9
  from github_dependents_info.gh_dependents_info import GithubDependentsInfo
7
- from rich.console import Console
8
10
 
9
11
  app = typer.Typer(
10
12
  name="github-dependents-info",
@@ -13,35 +13,40 @@ import numpy as np
13
13
  import pandas as pd
14
14
  from bs4 import BeautifulSoup
15
15
 
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # Markers surrounding the AI summary in generated markdown, so a previous summary
19
+ # can be found again and reused when a new one can not be generated.
20
+ LLM_SUMMARY_START_MARKER = "<!-- gh-dependents-info-summary-start -->"
21
+ LLM_SUMMARY_END_MARKER = "<!-- gh-dependents-info-summary-end -->"
22
+
16
23
 
17
24
  class GithubDependentsInfo:
18
25
  def __init__(self, repo, **options) -> None:
19
26
  self.repo = repo
20
- self.outputrepo = self.repo if "outputrepo" not in options else options["outputrepo"]
27
+ self.outputrepo = options.get("outputrepo", self.repo)
21
28
  if self.outputrepo is None or self.outputrepo == "" or len(self.outputrepo) < 4:
22
29
  self.outputrepo = self.repo
23
30
  self.url_init = f"https://github.com/{self.repo}/network/dependents"
24
31
  self.url_starts_with = f"/{self.repo}/network/dependents" + "?package_id="
25
- self.sort_key = "name" if "sort_key" not in options else options["sort_key"]
26
- self.min_stars = None if "min_stars" not in options else options["min_stars"]
27
- self.json_output = True if "json_output" in options and options["json_output"] is True else False
28
- self.merge_packages = True if "merge_packages" in options and options["merge_packages"] is True else False
29
- self.doc_url = options["doc_url"] if "doc_url" in options else None
30
- self.markdown_file = options["markdown_file"] if "markdown_file" in options else None
31
- self.badge_color = options["badge_color"] if "badge_color" in options else "informational"
32
- self.debug = True if "debug" in options and options["debug"] is True else False
33
- self.overwrite_progress = (
34
- True if "overwrite_progress" in options and options["overwrite_progress"] is True else False
35
- )
32
+ self.sort_key = options.get("sort_key", "name")
33
+ self.min_stars = options.get("min_stars", None)
34
+ self.json_output = options.get("json_output") is True
35
+ self.merge_packages = options.get("merge_packages") is True
36
+ self.doc_url = options.get("doc_url", None)
37
+ self.markdown_file = options.get("markdown_file", None)
38
+ self.badge_color = options.get("badge_color", "informational")
39
+ self.debug = options.get("debug") is True
40
+ self.overwrite_progress = options.get("overwrite_progress") is True
36
41
  self.csv_directory = (
37
42
  Path(options["csv_directory"])
38
43
  if ("csv_directory" in options and options["csv_directory"] is not None)
39
44
  else None
40
45
  )
41
- self.owner = options["owner"] if "owner" in options else None
42
- self.max_scraped_pages = options["max_scraped_pages"] if "max_scraped_pages" in options else 0
46
+ self.owner = options.get("owner", None)
47
+ self.max_scraped_pages = options.get("max_scraped_pages", 0)
43
48
  self.max_concurrent_requests = options.get("max_concurrent_requests", 10)
44
- self.pagination = True if "pagination" not in options else options["pagination"]
49
+ self.pagination = options.get("pagination", True)
45
50
  self.page_size = options.get("page_size", 500)
46
51
  self.total_sum = 0
47
52
  self.total_public_sum = 0
@@ -51,7 +56,7 @@ class GithubDependentsInfo:
51
56
  self.all_public_dependent_repos = []
52
57
  self.badges = {}
53
58
  self.result = {}
54
- self.time_delay = options["time_delay"] if "time_delay" in options else 0.1
59
+ self.time_delay = options.get("time_delay", 0.1)
55
60
  self.http_retry_attempts = options.get("http_retry_attempts", 5)
56
61
  self.http_retry_initial_delay = options.get("http_retry_initial_delay", max(self.time_delay, 1.0))
57
62
  self.http_retry_backoff = options.get("http_retry_backoff", 2.0)
@@ -66,12 +71,13 @@ class GithubDependentsInfo:
66
71
  self.llm_model = (
67
72
  options.get("llm_model") or os.getenv("GITHUB_DEPENDENTS_INFO_LLM_MODEL") or os.getenv("LITELLM_MODEL")
68
73
  )
69
- self.llm_max_repos = int(options.get("llm_max_repos", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_MAX_REPOS", 500)))
70
- self.llm_max_words = int(options.get("llm_max_words", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_MAX_WORDS", 300)))
71
- self.llm_timeout = float(options.get("llm_timeout", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_TIMEOUT", 120)))
74
+ self.llm_max_repos = int(options.get("llm_max_repos", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_MAX_REPOS", "500")))
75
+ self.llm_max_words = int(options.get("llm_max_words", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_MAX_WORDS", "300")))
76
+ self.llm_timeout = float(options.get("llm_timeout", os.getenv("GITHUB_DEPENDENTS_INFO_LLM_TIMEOUT", "120")))
72
77
  self.llm_model_used: str | None = None
73
78
  self.llm_summary: str | None = None
74
79
  self.llm_summary_error: str | None = None
80
+ self.llm_summary_reused: bool = False
75
81
 
76
82
  def collect(self):
77
83
  """Main entry point - synchronous wrapper for async collection."""
@@ -95,13 +101,13 @@ class GithubDependentsInfo:
95
101
  if self.owner:
96
102
  url += "&owner=" + self.owner
97
103
  if self.debug is True:
98
- logging.info("Package " + package["name"] + ": browsing " + url + " ...")
104
+ logger.info("Package " + package["name"] + ": browsing " + url + " ...")
99
105
  else:
100
106
  url = self.url_init
101
107
  if self.owner:
102
108
  url += "?owner=" + self.owner
103
109
  if self.debug is True:
104
- logging.info("Package " + self.repo + ": browsing " + url + " ...")
110
+ logger.info("Package " + self.repo + ": browsing " + url + " ...")
105
111
  package["url"] = url
106
112
  package["public_dependent_stars"] = 0
107
113
 
@@ -115,7 +121,7 @@ class GithubDependentsInfo:
115
121
  result = sorted(result, key=lambda d: d[self.sort_key])
116
122
  if self.debug is True:
117
123
  for r in result:
118
- logging.info(r)
124
+ logger.info(r)
119
125
 
120
126
  # Build package stats
121
127
  total_public_dependents = len(result)
@@ -151,8 +157,8 @@ class GithubDependentsInfo:
151
157
 
152
158
  # Output
153
159
  if self.debug is True:
154
- logging.info("Total for package: " + str(total_public_dependents))
155
- logging.info("")
160
+ logger.info("Total for package: " + str(total_public_dependents))
161
+ logger.info("")
156
162
  # Save crawl progress
157
163
  self.save_progress(package) # only saves if csv_directory is provided
158
164
 
@@ -225,10 +231,73 @@ class GithubDependentsInfo:
225
231
  if summary:
226
232
  self.llm_summary = summary
227
233
  return True
228
- except Exception as exc:
229
- logging.warning("Failed to load cached LLM summary: %s", exc)
234
+ except Exception as exc: # noqa: BLE001 - a corrupted cache must never break the run
235
+ logger.warning("Failed to load cached LLM summary: %s", exc)
230
236
  return False
231
237
 
238
+ def _extract_summary_from_markdown_content(self, content: str) -> str | None:
239
+ """Extract a summary previously written by github-dependents-info in a markdown content."""
240
+ marked = re.search(
241
+ re.escape(LLM_SUMMARY_START_MARKER) + "(.*?)" + re.escape(LLM_SUMMARY_END_MARKER),
242
+ content,
243
+ re.DOTALL,
244
+ )
245
+ if marked is not None:
246
+ block = marked.group(1)
247
+ else:
248
+ # Markdown generated before summary markers existed: fallback on the "## Summary" section
249
+ heading = re.search(r"^##\s+Summary\s*$", content, re.MULTILINE)
250
+ if heading is None:
251
+ return None
252
+ block = content[heading.end() :]
253
+ block = re.sub(r"^\s*##\s+Summary\s*$", "", block, count=1, flags=re.MULTILINE)
254
+ # Keep only the summary text: stop at the next heading, badge, table or footer
255
+ summary_lines: list[str] = []
256
+ for line in block.splitlines():
257
+ stripped = line.strip()
258
+ if stripped.startswith(("#", "[![", "|", "_Generated using [")):
259
+ break
260
+ summary_lines.append(line)
261
+ return "\n".join(summary_lines).strip() or None
262
+
263
+ def _load_previous_markdown_summary(self) -> str | None:
264
+ """Read the summary generated by a previous run in the output markdown file."""
265
+ if not self.markdown_file:
266
+ return None
267
+ try:
268
+ if not os.path.isfile(self.markdown_file):
269
+ return None
270
+ with open(self.markdown_file, encoding="utf-8") as f:
271
+ content = f.read()
272
+ except (OSError, UnicodeDecodeError) as exc:
273
+ logger.warning("Failed to read previous summary from %s: %s", self.markdown_file, exc)
274
+ return None
275
+ return self._extract_summary_from_markdown_content(content)
276
+
277
+ def reuse_previous_llm_summary(self, reason: str) -> bool:
278
+ """Reuse the summary of a previous run when a new one can not be generated.
279
+
280
+ This avoids losing an existing summary when the LLM call is impossible
281
+ (no API key anymore, no credit left, rate limit, timeout...).
282
+ """
283
+ if self.llm_summary:
284
+ return True
285
+ if self.load_llm_summary():
286
+ self.llm_summary_reused = True
287
+ else:
288
+ previous_summary = self._load_previous_markdown_summary()
289
+ if not previous_summary:
290
+ return False
291
+ self.llm_summary = previous_summary
292
+ self.llm_summary_reused = True
293
+ # Keep it in the progress directory so next runs can find it faster
294
+ self.save_llm_summary()
295
+ logger.warning(
296
+ "Unable to generate a new AI summary (%s): reusing the summary generated by a previous run",
297
+ reason,
298
+ )
299
+ return True
300
+
232
301
  def save_llm_summary(self) -> None:
233
302
  """Persist LLM summary into progress directory if enabled."""
234
303
  cache_path = self._llm_summary_cache_path()
@@ -245,9 +314,9 @@ class GithubDependentsInfo:
245
314
  }
246
315
  with open(cache_path, "w", encoding="utf-8") as f:
247
316
  json.dump(payload, f, indent=2)
248
- except Exception as exc:
317
+ except Exception as exc: # noqa: BLE001 - caching is best effort
249
318
  if self.debug:
250
- logging.warning("Failed to save cached LLM summary: %s", exc)
319
+ logger.warning("Failed to save cached LLM summary: %s", exc)
251
320
 
252
321
  def _prepare_llm_summary_payload(self) -> dict:
253
322
  """Prepare a compact data payload for the LLM prompt."""
@@ -295,6 +364,7 @@ class GithubDependentsInfo:
295
364
 
296
365
  provider_info = self._detect_llm_provider()
297
366
  if provider_info is None:
367
+ self.reuse_previous_llm_summary("no LLM API key found in environment variables")
298
368
  return
299
369
 
300
370
  # Default model if none was provided
@@ -346,9 +416,13 @@ class GithubDependentsInfo:
346
416
  if content:
347
417
  self.llm_summary = str(content).strip()
348
418
  self.save_llm_summary()
349
- except Exception as exc:
419
+ else:
420
+ self.llm_summary_error = "LLM returned an empty summary"
421
+ self.reuse_previous_llm_summary(self.llm_summary_error)
422
+ except Exception as exc: # noqa: BLE001 - any provider error must not break the run
350
423
  self.llm_summary_error = str(exc)
351
- logging.warning("Failed to generate LLM summary: %s", exc)
424
+ logger.warning("Failed to generate LLM summary: %s", exc)
425
+ self.reuse_previous_llm_summary(str(exc))
352
426
  finally:
353
427
  # LiteLLM can keep async clients around. Ensure they're closed before
354
428
  # asyncio.run() tears down the event loop to avoid:
@@ -375,9 +449,9 @@ class GithubDependentsInfo:
375
449
  result = close_fn()
376
450
  if inspect.isawaitable(result):
377
451
  await result
378
- except Exception as exc:
452
+ except Exception as exc: # noqa: BLE001 - cleanup is best effort
379
453
  if self.debug:
380
- logging.debug("LiteLLM async client cleanup skipped: %s", exc)
454
+ logger.debug("LiteLLM async client cleanup skipped: %s", exc)
381
455
 
382
456
  def _extract_owner_repo(self, dependent_row):
383
457
  repo_anchor = dependent_row.find("a", {"data-hovercard-type": "repository"})
@@ -426,7 +500,7 @@ class GithubDependentsInfo:
426
500
  if "{{" in package_name:
427
501
  continue
428
502
  if self.debug is True:
429
- logging.info(package_name)
503
+ logger.info(package_name)
430
504
  self.packages += [{"id": package_id, "name": package_name}]
431
505
  if len(self.packages) == 0:
432
506
  self.packages = [{"id": None, "name": self.repo}]
@@ -452,7 +526,10 @@ class GithubDependentsInfo:
452
526
  sources_all_df.set_index("name", inplace=True)
453
527
  source_df = pd.json_normalize(source_info).set_index("name", drop=True)
454
528
  for column in source_df.columns:
455
- if source_df[column].dtype == object and column in sources_all_df.columns:
529
+ # pandas >= 3 infers text columns as StringDtype rather than object, so a
530
+ # `dtype == object` test misses them and writing text into a numeric column
531
+ # raises instead of upcasting. Widen the target column for any non-numeric value.
532
+ if column in sources_all_df.columns and not pd.api.types.is_numeric_dtype(source_df[column]):
456
533
  sources_all_df[column] = sources_all_df[column].astype("object")
457
534
  sources_all_df.loc[source_df.index, column] = source_df[column]
458
535
  sources_all_df.reset_index(inplace=True, drop=False)
@@ -567,9 +644,26 @@ class GithubDependentsInfo:
567
644
  """Build the standard footer for markdown files."""
568
645
  return [
569
646
  "",
570
- "_Generated using [github-dependents-info]"
571
- "(https://github.com/nvuillam/github-dependents-info), "
572
- "by [Nicolas Vuillamy](https://github.com/nvuillam)_",
647
+ (
648
+ "_Generated using [github-dependents-info]"
649
+ "(https://github.com/nvuillam/github-dependents-info), "
650
+ "by [Nicolas Vuillamy](https://github.com/nvuillam)_"
651
+ ),
652
+ ]
653
+
654
+ def _append_llm_summary(self, md_lines: list) -> None:
655
+ """Append the AI summary section, surrounded by markers so it can be reused by later runs."""
656
+ if not self.llm_summary:
657
+ return
658
+ md_lines += [
659
+ LLM_SUMMARY_START_MARKER,
660
+ "",
661
+ "## Summary",
662
+ "",
663
+ self.llm_summary.strip(),
664
+ "",
665
+ LLM_SUMMARY_END_MARKER,
666
+ "",
573
667
  ]
574
668
 
575
669
  def _append_summary_table(self, md_lines: list) -> None:
@@ -606,8 +700,7 @@ class GithubDependentsInfo:
606
700
  self._append_summary_table(md_lines)
607
701
 
608
702
  # Optional LLM summary
609
- if self.llm_summary:
610
- md_lines += ["## Summary", "", self.llm_summary.strip(), ""]
703
+ self._append_llm_summary(md_lines)
611
704
 
612
705
  # Single dependents list
613
706
  if self.merge_packages is True:
@@ -702,8 +795,7 @@ class GithubDependentsInfo:
702
795
  self._append_summary_table(md_lines)
703
796
 
704
797
  # Optional LLM summary (only on first page)
705
- if self.llm_summary:
706
- md_lines += ["## Summary", "", self.llm_summary.strip(), ""]
798
+ self._append_llm_summary(md_lines)
707
799
 
708
800
  # Calculate start and end indices for this page
709
801
  start_idx = (page_num - 1) * self.page_size
@@ -840,12 +932,9 @@ class GithubDependentsInfo:
840
932
  md_lines += [f"|{image_md} &nbsp; [{repo_label}](https://github.com/{repo_label}) | {repo_stars} |"]
841
933
 
842
934
  def build_badge(self, label, nb, **options):
843
- if "url" in options:
844
- url = options["url"]
845
- else:
846
- url = f"https://github.com/{self.repo}/network/dependents"
935
+ url = options.get("url", f"https://github.com/{self.repo}/network/dependents")
847
936
  return (
848
- f"[![Generated by github-dependents-info](https://img.shields.io/static/v1?label={label}&message={str(nb)}"
937
+ f"[![Generated by github-dependents-info](https://img.shields.io/static/v1?label={label}&message={nb!s}"
849
938
  + f"&color={self.badge_color}&logo=slickpic)]({url})"
850
939
  )
851
940
 
@@ -890,7 +979,7 @@ class GithubDependentsInfo:
890
979
  raise
891
980
  delay = self._compute_retry_delay(attempt, response=exc.response)
892
981
  if self.debug:
893
- logging.warning(
982
+ logger.warning(
894
983
  "HTTP %s while fetching %s (attempt %s/%s). Retrying in %.1fs",
895
984
  status_code,
896
985
  url,
@@ -905,7 +994,7 @@ class GithubDependentsInfo:
905
994
  raise
906
995
  delay = self._compute_retry_delay(attempt)
907
996
  if self.debug:
908
- logging.warning(
997
+ logger.warning(
909
998
  "Request error while fetching %s (attempt %s/%s): %s. Retrying in %.1fs",
910
999
  url,
911
1000
  attempt,
@@ -952,12 +1041,12 @@ class GithubDependentsInfo:
952
1041
  # Check if we've reached the max pages limit
953
1042
  if self.max_scraped_pages > 0 and page_number >= self.max_scraped_pages:
954
1043
  if self.debug is True:
955
- logging.info(f" - reached max scraped pages limit ({self.max_scraped_pages})")
1044
+ logger.info(f" - reached max scraped pages limit ({self.max_scraped_pages})")
956
1045
  break
957
1046
  next_link = u["href"]
958
1047
  page_number += 1
959
1048
  if self.debug is True:
960
- logging.info(f" - discovered page {page_number}")
1049
+ logger.info(f" - discovered page {page_number}")
961
1050
  break
962
1051
 
963
1052
  if next_link is None:
@@ -970,13 +1059,13 @@ class GithubDependentsInfo:
970
1059
  current_soup = BeautifulSoup(current_content, "html.parser")
971
1060
  except (httpx.RequestError, httpx.TimeoutException) as e:
972
1061
  if self.debug:
973
- logging.warning(f"Failed to fetch page during discovery: {e}")
1062
+ logger.warning(f"Failed to fetch page during discovery: {e}")
974
1063
  break
975
1064
 
976
1065
  # Fetch additional pages in parallel if any
977
1066
  if urls_to_fetch:
978
1067
  if self.debug:
979
- logging.info(f" - fetching {len(urls_to_fetch)} additional pages in parallel...")
1068
+ logger.info(f" - fetching {len(urls_to_fetch)} additional pages in parallel...")
980
1069
  tasks = [self.fetch_page(client, page_url, semaphore) for page_url in urls_to_fetch]
981
1070
  additional_pages = await asyncio.gather(*tasks, return_exceptions=True)
982
1071
  pages_content += additional_pages
@@ -987,7 +1076,7 @@ class GithubDependentsInfo:
987
1076
  for page_content in pages_content:
988
1077
  if isinstance(page_content, Exception):
989
1078
  if self.debug:
990
- logging.warning(f"Failed to fetch page: {page_content}")
1079
+ logger.warning(f"Failed to fetch page: {page_content}")
991
1080
  continue
992
1081
 
993
1082
  soup = BeautifulSoup(page_content, "html.parser")
@@ -997,7 +1086,7 @@ class GithubDependentsInfo:
997
1086
  owner_repo = self._extract_owner_repo(t)
998
1087
  if owner_repo is None:
999
1088
  if self.debug:
1000
- logging.warning("Skipping dependent row without repository link")
1089
+ logger.warning("Skipping dependent row without repository link")
1001
1090
  continue
1002
1091
  owner_name, repo_name = owner_repo
1003
1092
  star_svg = t.find("svg", {"class": "octicon-star"})
@@ -1068,6 +1157,6 @@ class GithubDependentsInfo:
1068
1157
  number_as_string = number_as_string.replace(",", "").replace(" ", "")
1069
1158
  try:
1070
1159
  return int(number_as_string)
1071
- except Exception:
1072
- logging.warning(f'WARNING: Unable to get integer from "{number_as_string}"')
1160
+ except (ValueError, TypeError):
1161
+ logger.warning(f'WARNING: Unable to get integer from "{number_as_string}"')
1073
1162
  return 0
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
5
5
 
6
6
  [project]
7
7
  name = "github-dependents-info"
8
- version = "3.2.1"
8
+ version = "4.1.0"
9
9
  description = "Collect information about dependencies between a github repo and other repositories. Results available in JSON, markdown and badges."
10
10
  readme = "README.md"
11
11
  license = "MIT"
@@ -13,7 +13,7 @@ license-files = ["LICENSE"]
13
13
  authors = [
14
14
  {name = "nvuillam", email = "nicolas.vuillamy@gmail.com"},
15
15
  ]
16
- requires-python = ">=3.10,<4.0"
16
+ requires-python = ">=3.11,<4.0"
17
17
 
18
18
  # Keywords description https://python-poetry.org/docs/pyproject/#keywords
19
19
  keywords = ["github", "dependent", "used-by", "repository", "stats", "dependencies", "using"]
@@ -37,41 +37,42 @@ Repository = "https://github.com/nvuillam/github-dependents-info"
37
37
  "github-dependents-info" = "github_dependents_info.__main__:app"
38
38
 
39
39
  [tool.poetry.dependencies]
40
- python = ">=3.10,<4.0"
40
+ python = ">=3.11,<4.0"
41
41
 
42
- click = ">=8.4.1,<8.5"
43
- typer = ">=0.25.1,<0.26"
42
+ click = ">=8.5,<8.6"
43
+ typer = ">=0.27.1,<0.28"
44
44
  rich = ">=15,<15.1"
45
45
  beautifulsoup4 = "4.15.0"
46
- pandas = ">=2.3.3,<3.0"
46
+ pandas = ">=3.1,<3.2"
47
47
  httpx = "^0.28.1"
48
- idna = ">=3.14"
48
+ idna = ">=3.20"
49
+ soupsieve = ">=2.8.4" # security floor (CVE-2026-49476, CVE-2026-49477), pulled by beautifulsoup4
49
50
 
50
51
  [tool.poetry.group.dev.dependencies]
51
52
  bandit = "^1.7.5"
52
53
  black = ">=26.3.1"
53
54
  darglint = "^1.8.1"
54
- isort = {extras = ["colors"], version = "^8.0.0"}
55
+ isort = {extras = ["colors"], version = "^9.0.0"}
55
56
  mypy = "^2.0"
56
57
  mypy-extensions = "^1.0.0"
57
58
  pre-commit = "^4.0.0"
58
59
  pydocstyle = "^6.3.0"
59
60
  pylint = "^4.0.0"
60
- pytest = "^9.0.0"
61
+ pytest = "^9.1.1"
61
62
  pyupgrade = "^3.4.0"
62
- safety = "^3.8.1"
63
- coverage = "^7.3.4"
64
- coverage-badge = "^1.1.0"
65
- cryptography = ">=48.0.0"
63
+ coverage = "^7.16.1"
64
+ anybadge = "^1.16.0" # replaces coverage-badge, which imports pkg_resources (removed in setuptools 82)
65
+ cryptography = ">=50.0.0"
66
66
  pytest-html = "^4.1.1"
67
67
  pytest-cov = "^7.0.0"
68
- marshmallow = ">=4.3,<4.4"
69
- authlib = ">=1.7.0"
68
+ marshmallow = ">=4.3.1,<4.4"
69
+ authlib = ">=1.8.0"
70
70
  requests = ">=2.34.2"
71
- urllib3 = ">=2.6.3"
72
- filelock = ">=3.20.4"
73
- virtualenv = ">=21.3.0"
74
- nltk = ">=3.9.4"
71
+ urllib3 = ">=2.8.0"
72
+ filelock = ">=4.0.1"
73
+ virtualenv = ">=21.9.0"
74
+ nltk = ">=3.10.0"
75
+ setuptools = ">=83.0.0"
75
76
 
76
77
 
77
78
  [[tool.poetry.source]]