github-backup 0.62.1__tar.gz → 0.63.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.
Files changed (38) hide show
  1. {github_backup-0.62.1 → github_backup-0.63.0}/CHANGES.rst +47 -1
  2. {github_backup-0.62.1/github_backup.egg-info → github_backup-0.63.0}/PKG-INFO +1 -1
  3. github_backup-0.63.0/github_backup/__init__.py +1 -0
  4. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup/github_backup.py +41 -24
  5. {github_backup-0.62.1 → github_backup-0.63.0/github_backup.egg-info}/PKG-INFO +1 -1
  6. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup.egg-info/SOURCES.txt +1 -0
  7. {github_backup-0.62.1 → github_backup-0.63.0}/release-requirements.txt +2 -2
  8. github_backup-0.63.0/tests/test_disabled_issues_pulls.py +105 -0
  9. github_backup-0.62.1/github_backup/__init__.py +0 -1
  10. {github_backup-0.62.1 → github_backup-0.63.0}/LICENSE.txt +0 -0
  11. {github_backup-0.62.1 → github_backup-0.63.0}/MANIFEST.in +0 -0
  12. {github_backup-0.62.1 → github_backup-0.63.0}/README +0 -0
  13. {github_backup-0.62.1 → github_backup-0.63.0}/README.rst +0 -0
  14. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup/__main__.py +0 -0
  15. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup/cli.py +0 -0
  16. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup/graphql_queries.py +0 -0
  17. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup.egg-info/dependency_links.txt +0 -0
  18. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup.egg-info/entry_points.txt +0 -0
  19. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup.egg-info/top_level.txt +0 -0
  20. {github_backup-0.62.1 → github_backup-0.63.0}/github_backup.egg-info/zip-safe +0 -0
  21. {github_backup-0.62.1 → github_backup-0.63.0}/requirements.txt +0 -0
  22. {github_backup-0.62.1 → github_backup-0.63.0}/setup.cfg +0 -0
  23. {github_backup-0.62.1 → github_backup-0.63.0}/setup.py +0 -0
  24. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_all_starred.py +0 -0
  25. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_attachments.py +0 -0
  26. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_auth.py +0 -0
  27. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_case_sensitivity.py +0 -0
  28. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_discussions.py +0 -0
  29. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_http_451.py +0 -0
  30. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_incremental_per_repository.py +0 -0
  31. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_json_dump_if_changed.py +0 -0
  32. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_pagination.py +0 -0
  33. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_pull_incremental_pagination.py +0 -0
  34. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_pull_reviews.py +0 -0
  35. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_releases.py +0 -0
  36. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_retrieve_data.py +0 -0
  37. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_skip_assets_on.py +0 -0
  38. {github_backup-0.62.1 → github_backup-0.63.0}/tests/test_starred_skip_size_over.py +0 -0
@@ -1,9 +1,55 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- 0.62.1 (2026-04-30)
4
+ 0.63.0 (2026-06-17)
5
5
  -------------------
6
6
  ------------------------
7
+ - Skip repositories with pull requests disabled (HTTP 404) instead of
8
+ crashing (#511) [Rodos]
9
+
10
+ Also harden the advisories/hooks 404 checks to match HTTPError.code instead of a substring.
11
+ - Chore(deps): bump pytest in the python-packages group.
12
+ [dependabot[bot]]
13
+
14
+ Bumps the python-packages group with 1 update: [pytest](https://github.com/pytest-dev/pytest).
15
+
16
+
17
+ Updates `pytest` from 9.0.3 to 9.1.0
18
+ - [Release notes](https://github.com/pytest-dev/pytest/releases)
19
+ - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
20
+ - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.0)
21
+
22
+ ---
23
+ updated-dependencies:
24
+ - dependency-name: pytest
25
+ dependency-version: 9.1.0
26
+ dependency-type: direct:production
27
+ update-type: version-update:semver-minor
28
+ dependency-group: python-packages
29
+ ...
30
+ - Chore(deps): bump black in the python-packages group.
31
+ [dependabot[bot]]
32
+
33
+ Bumps the python-packages group with 1 update: [black](https://github.com/psf/black).
34
+
35
+
36
+ Updates `black` from 26.3.1 to 26.5.1
37
+ - [Release notes](https://github.com/psf/black/releases)
38
+ - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
39
+ - [Commits](https://github.com/psf/black/compare/26.3.1...26.5.1)
40
+
41
+ ---
42
+ updated-dependencies:
43
+ - dependency-name: black
44
+ dependency-version: 26.5.1
45
+ dependency-type: direct:production
46
+ update-type: version-update:semver-minor
47
+ dependency-group: python-packages
48
+ ...
49
+
50
+
51
+ 0.62.1 (2026-04-30)
52
+ -------------------
7
53
  - Document that nothing is saved by default. [Changaco]
8
54
  - Eliminate trailing spaces. [Changaco]
9
55
  - Remove pointless and unsafe `export`s in examples. [Changaco]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-backup
3
- Version: 0.62.1
3
+ Version: 0.63.0
4
4
  Summary: backup a github user or organization
5
5
  Home-page: http://github.com/josegonzalez/python-github-backup
6
6
  Author: Jose Diaz-Gonzalez
@@ -0,0 +1 @@
1
+ __version__ = "0.63.0"
@@ -2657,10 +2657,20 @@ def backup_pulls(args, repo_cwd, repository, repos_template):
2657
2657
  def pull_is_due_for_repository_checkpoint(pull):
2658
2658
  return not repository_since or pull["updated_at"] > repository_since
2659
2659
 
2660
- if not args.include_pull_details:
2661
- pull_states = ["open", "closed"]
2662
- for pull_state in pull_states:
2663
- query_args["state"] = pull_state
2660
+ try:
2661
+ if not args.include_pull_details:
2662
+ pull_states = ["open", "closed"]
2663
+ for pull_state in pull_states:
2664
+ query_args["state"] = pull_state
2665
+ for pull in retrieve_data(
2666
+ args, _pulls_template, query_args=query_args, lazy=True
2667
+ ):
2668
+ track_newest_pull_update(pull)
2669
+ if pulls_since and pull["updated_at"] <= pulls_since:
2670
+ break
2671
+ if not pulls_since or pull["updated_at"] > pulls_since:
2672
+ pulls[pull["number"]] = pull
2673
+ else:
2664
2674
  for pull in retrieve_data(
2665
2675
  args, _pulls_template, query_args=query_args, lazy=True
2666
2676
  ):
@@ -2669,22 +2679,29 @@ def backup_pulls(args, repo_cwd, repository, repos_template):
2669
2679
  break
2670
2680
  if not pulls_since or pull["updated_at"] > pulls_since:
2671
2681
  pulls[pull["number"]] = pull
2672
- else:
2673
- for pull in retrieve_data(
2674
- args, _pulls_template, query_args=query_args, lazy=True
2675
- ):
2676
- track_newest_pull_update(pull)
2677
- if pulls_since and pull["updated_at"] <= pulls_since:
2678
- break
2679
- if not pulls_since or pull["updated_at"] > pulls_since:
2680
- if pull_is_due_for_repository_checkpoint(pull):
2681
- pulls[pull["number"]] = retrieve_data(
2682
- args,
2683
- _pulls_template + "/{}".format(pull["number"]),
2684
- paginated=False,
2685
- )[0]
2686
- else:
2687
- pulls[pull["number"]] = pull
2682
+ except HTTPError as e:
2683
+ # Repositories with pull requests disabled return HTTP 404 for the
2684
+ # listing endpoint; treat that as "no pull requests" rather than a
2685
+ # fatal error. retrieve_data is lazy here, so the 404 surfaces during
2686
+ # iteration. Only the listing call is guarded — a 404 on an individual
2687
+ # pull's detail fetch below is a different condition and must propagate.
2688
+ if e.code == 404:
2689
+ logger.info("Pull requests are disabled for this repository, skipping")
2690
+ return
2691
+ raise
2692
+
2693
+ if args.include_pull_details:
2694
+ # Replace the summary payload with the full pull detail. Done outside
2695
+ # the disabled-feature guard above so a 404 on a single pull (e.g. one
2696
+ # deleted between listing and fetch) is not mistaken for the whole
2697
+ # feature being disabled, which would discard every collected pull.
2698
+ for number in list(pulls.keys()):
2699
+ if pull_is_due_for_repository_checkpoint(pulls[number]):
2700
+ pulls[number] = retrieve_data(
2701
+ args,
2702
+ _pulls_template + "/{}".format(number),
2703
+ paginated=False,
2704
+ )[0]
2688
2705
 
2689
2706
  logger.info("Saving {0} pull requests to disk".format(len(list(pulls.keys()))))
2690
2707
  # Comments from pulls API are only _review_ comments
@@ -2813,8 +2830,8 @@ def backup_security_advisories(args, repo_cwd, repository, repos_template):
2813
2830
 
2814
2831
  try:
2815
2832
  _advisories = retrieve_data(args, template)
2816
- except Exception as e:
2817
- if "404" in str(e):
2833
+ except HTTPError as e:
2834
+ if e.code == 404:
2818
2835
  logger.info("Security advisories are not available for this repository, skipping")
2819
2836
  return
2820
2837
  raise
@@ -2861,8 +2878,8 @@ def backup_hooks(args, repo_cwd, repository, repos_template):
2861
2878
  template = "{0}/{1}/hooks".format(repos_template, repository["full_name"])
2862
2879
  try:
2863
2880
  _backup_data(args, "hooks", template, output_file, hook_cwd)
2864
- except Exception as e:
2865
- if "404" in str(e):
2881
+ except HTTPError as e:
2882
+ if e.code == 404:
2866
2883
  logger.info("Unable to read hooks, skipping")
2867
2884
  else:
2868
2885
  raise e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-backup
3
- Version: 0.62.1
3
+ Version: 0.63.0
4
4
  Summary: backup a github user or organization
5
5
  Home-page: http://github.com/josegonzalez/python-github-backup
6
6
  Author: Jose Diaz-Gonzalez
@@ -21,6 +21,7 @@ tests/test_all_starred.py
21
21
  tests/test_attachments.py
22
22
  tests/test_auth.py
23
23
  tests/test_case_sensitivity.py
24
+ tests/test_disabled_issues_pulls.py
24
25
  tests/test_discussions.py
25
26
  tests/test_http_451.py
26
27
  tests/test_incremental_per_repository.py
@@ -1,10 +1,10 @@
1
1
  # Linting & Formatting
2
2
  autopep8==2.3.2
3
- black==26.3.1
3
+ black==26.5.1
4
4
  flake8==7.3.0
5
5
 
6
6
  # Testing
7
- pytest==9.0.3
7
+ pytest==9.1.0
8
8
 
9
9
  # Release & Publishing
10
10
  twine==6.2.0
@@ -0,0 +1,105 @@
1
+ """Tests for repositories with pull requests disabled (GitHub issue #511).
2
+
3
+ When pull requests are disabled on a repository, the ``/pulls`` endpoint
4
+ responds with HTTP 404. The backup should treat that as "no pull requests"
5
+ and continue, rather than aborting the whole run. (The ``/issues`` endpoint
6
+ returns 200 with an empty list when issues are disabled, so it needs no such
7
+ handling.)
8
+ """
9
+
10
+ import logging
11
+ from urllib.error import HTTPError
12
+
13
+ from github_backup import github_backup
14
+
15
+
16
+ def _http_error(template, code, reason):
17
+ return HTTPError(template, code, reason, hdrs=None, fp=None)
18
+
19
+
20
+ def test_backup_pulls_skips_on_404(create_args, tmp_path, monkeypatch, caplog):
21
+ args = create_args(include_pulls=True, since=None)
22
+ repository = {"full_name": "owner/repo"}
23
+
24
+ def fake_retrieve_data(passed_args, template, query_args=None, lazy=False, **kwargs):
25
+ # retrieve_data is called lazily for the pull listing; the 404 must
26
+ # surface while iterating the returned generator.
27
+ def gen():
28
+ raise _http_error(template, 404, "Not Found")
29
+ yield # pragma: no cover - generator marker
30
+
31
+ if lazy:
32
+ return gen()
33
+ raise _http_error(template, 404, "Not Found")
34
+
35
+ monkeypatch.setattr(github_backup, "retrieve_data", fake_retrieve_data)
36
+
37
+ # Should not raise, and should log that it skipped.
38
+ with caplog.at_level(logging.INFO, logger=github_backup.logger.name):
39
+ github_backup.backup_pulls(
40
+ args, tmp_path, repository, "https://api.github.com/repos"
41
+ )
42
+
43
+ assert "Pull requests are disabled for this repository, skipping" in caplog.text
44
+
45
+ pulls_dir = tmp_path / "pulls"
46
+ if pulls_dir.exists():
47
+ assert not [p for p in pulls_dir.iterdir() if p.suffix == ".json"]
48
+
49
+
50
+ def test_backup_pulls_reraises_non_404(create_args, tmp_path, monkeypatch):
51
+ args = create_args(include_pulls=True, since=None)
52
+ repository = {"full_name": "owner/repo"}
53
+
54
+ def fake_retrieve_data(passed_args, template, query_args=None, lazy=False, **kwargs):
55
+ def gen():
56
+ raise _http_error(template, 500, "Server Error")
57
+ yield # pragma: no cover - generator marker
58
+
59
+ if lazy:
60
+ return gen()
61
+ raise _http_error(template, 500, "Server Error")
62
+
63
+ monkeypatch.setattr(github_backup, "retrieve_data", fake_retrieve_data)
64
+
65
+ try:
66
+ github_backup.backup_pulls(
67
+ args, tmp_path, repository, "https://api.github.com/repos"
68
+ )
69
+ except HTTPError as exc:
70
+ assert exc.code == 500
71
+ else:
72
+ raise AssertionError("Expected non-404 error to propagate")
73
+
74
+
75
+ def test_backup_pulls_details_individual_404_propagates(
76
+ create_args, tmp_path, monkeypatch
77
+ ):
78
+ # With --pull-details the listing succeeds (feature is enabled), but an
79
+ # individual pull's detail fetch 404s (e.g. deleted mid-backup). That must
80
+ # propagate, NOT be swallowed as "pull requests are disabled".
81
+ args = create_args(include_pulls=True, include_pull_details=True, since=None)
82
+ repository = {"full_name": "owner/repo"}
83
+
84
+ listing = "https://api.github.com/repos/owner/repo/pulls"
85
+
86
+ def fake_retrieve_data(passed_args, template, query_args=None, lazy=False, **kwargs):
87
+ if template == listing:
88
+ # Listing works fine -> pull requests are clearly not disabled.
89
+ def gen():
90
+ yield {"number": 1, "updated_at": "2026-02-01T00:00:00Z"}
91
+
92
+ return gen()
93
+ # Individual pull detail fetch (.../pulls/1) is missing.
94
+ raise _http_error(template, 404, "Not Found")
95
+
96
+ monkeypatch.setattr(github_backup, "retrieve_data", fake_retrieve_data)
97
+
98
+ try:
99
+ github_backup.backup_pulls(
100
+ args, tmp_path, repository, "https://api.github.com/repos"
101
+ )
102
+ except HTTPError as exc:
103
+ assert exc.code == 404
104
+ else:
105
+ raise AssertionError("Expected individual-pull 404 to propagate")
@@ -1 +0,0 @@
1
- __version__ = "0.62.1"
File without changes
File without changes
File without changes