certapi 1.1.8__tar.gz → 1.1.9__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 (70) hide show
  1. {certapi-1.1.8/src/certapi.egg-info → certapi-1.1.9}/PKG-INFO +1 -1
  2. {certapi-1.1.8 → certapi-1.1.9}/setup.py +1 -1
  3. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/client/renewal_manager.py +20 -2
  4. {certapi-1.1.8 → certapi-1.1.9/src/certapi.egg-info}/PKG-INFO +1 -1
  5. {certapi-1.1.8 → certapi-1.1.9}/tests/test_renewal_manager.py +23 -4
  6. {certapi-1.1.8 → certapi-1.1.9}/MANIFEST.in +0 -0
  7. {certapi-1.1.8 → certapi-1.1.9}/README.md +0 -0
  8. {certapi-1.1.8 → certapi-1.1.9}/pyproject.toml +0 -0
  9. {certapi-1.1.8 → certapi-1.1.9}/setup.cfg +0 -0
  10. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/__init__.py +0 -0
  11. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/Acme.py +0 -0
  12. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/AcmeError.py +0 -0
  13. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/Challenge.py +0 -0
  14. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/Order.py +0 -0
  15. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/__init__.py +0 -0
  16. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/acme/http.py +0 -0
  17. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/ChallengeSolver.py +0 -0
  18. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/FileSystemChallengeSolver.py +0 -0
  19. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/InmemoryChallengeSolver.py +0 -0
  20. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/__init__.py +0 -0
  21. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/__init__.py +0 -0
  22. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/cloudflare/__init__.py +0 -0
  23. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/cloudflare/cloudflare_challenge_solver.py +0 -0
  24. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/cloudflare/cloudflare_client.py +0 -0
  25. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/digitalocean/__init__.py +0 -0
  26. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/digitalocean/digitalocean_challenge_solver.py +0 -0
  27. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/challenge_solver/dns/digitalocean/digitalocean_client.py +0 -0
  28. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/cli.py +0 -0
  29. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/client/__init__.py +0 -0
  30. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/client/cert_manager_client.py +0 -0
  31. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/crypto/__init__.py +0 -0
  32. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/crypto/crypto.py +0 -0
  33. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/crypto/crypto_classes.py +0 -0
  34. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/domain_batching.py +0 -0
  35. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/errors.py +0 -0
  36. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/http/HttpClientBase.py +0 -0
  37. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/http/__init__.py +0 -0
  38. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/http/types.py +0 -0
  39. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/issuers/AcmeCertIssuer.py +0 -0
  40. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/issuers/SelfCertIssuer.py +0 -0
  41. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/issuers/__init__.py +0 -0
  42. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/issuers/abstract_certissuer.py +0 -0
  43. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/FileSystemKeyStore.py +0 -0
  44. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/KeyStore.py +0 -0
  45. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/PostgresqlKeyStore.py +0 -0
  46. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/RemoteKeyStore.py +0 -0
  47. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/SqliteKeyStore.py +0 -0
  48. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/keystore/__init__.py +0 -0
  49. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/manager/__init__.py +0 -0
  50. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/manager/acme_cert_manager.py +0 -0
  51. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/server/__init__.py +0 -0
  52. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/server/api.py +0 -0
  53. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/server/cert_api.py +0 -0
  54. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/server/key_api.py +0 -0
  55. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/util.py +0 -0
  56. {certapi-1.1.8 → certapi-1.1.9}/src/certapi/utils.py +0 -0
  57. {certapi-1.1.8 → certapi-1.1.9}/src/certapi.egg-info/SOURCES.txt +0 -0
  58. {certapi-1.1.8 → certapi-1.1.9}/src/certapi.egg-info/dependency_links.txt +0 -0
  59. {certapi-1.1.8 → certapi-1.1.9}/src/certapi.egg-info/entry_points.txt +0 -0
  60. {certapi-1.1.8 → certapi-1.1.9}/src/certapi.egg-info/requires.txt +0 -0
  61. {certapi-1.1.8 → certapi-1.1.9}/src/certapi.egg-info/top_level.txt +0 -0
  62. {certapi-1.1.8 → certapi-1.1.9}/tests/test_acme_cert_manager_batching.py +0 -0
  63. {certapi-1.1.8 → certapi-1.1.9}/tests/test_acme_error_handling.py +0 -0
  64. {certapi-1.1.8 → certapi-1.1.9}/tests/test_cert_issuer_generic.py +0 -0
  65. {certapi-1.1.8 → certapi-1.1.9}/tests/test_certs_with_key_types.py +0 -0
  66. {certapi-1.1.8 → certapi-1.1.9}/tests/test_cli.py +0 -0
  67. {certapi-1.1.8 → certapi-1.1.9}/tests/test_domain_batching_boulder_cases.py +0 -0
  68. {certapi-1.1.8 → certapi-1.1.9}/tests/test_http_error_handling.py +0 -0
  69. {certapi-1.1.8 → certapi-1.1.9}/tests/test_keystores.py +0 -0
  70. {certapi-1.1.8 → certapi-1.1.9}/tests/test_obtain_interface.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: certapi
3
- Version: 1.1.8
3
+ Version: 1.1.9
4
4
  Summary: Python Package for managing keys, request SSL certificates from ACME.
5
5
  Home-page: https://github.com/mesudip/certapi
6
6
  Author: Sudip Bhattarai
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="certapi",
5
- version="1.1.8",
5
+ version="1.1.9",
6
6
  packages=find_packages(where="src"),
7
7
  package_dir={"": "src"},
8
8
  install_requires=[
@@ -476,7 +476,8 @@ class RenewalManager:
476
476
  "key_type": self.key_type,
477
477
  "expiry_days": self.expiry_days,
478
478
  "renew_threshold_days": renew_threshold_days,
479
- "skip_failing": False,
479
+ # Renewal should obtain whatever can be issued, then self-sign unresolved domains.
480
+ "skip_failing": True,
480
481
  "batch_domains": True,
481
482
  "self_verify": self.self_verify,
482
483
  "country": self.country,
@@ -492,7 +493,16 @@ class RenewalManager:
492
493
  )
493
494
  if res is None:
494
495
  return
495
- self._update_expiry_cache(res.issued + res.existing)
496
+ certs = res.issued + res.existing
497
+ self._update_expiry_cache(certs)
498
+ covered_domains = self._covered_domains_from_response(certs)
499
+ unresolved_domains = [domain for domain in domains if domain not in covered_domains]
500
+ for domain in unresolved_domains:
501
+ self._add_to_blacklist(domain, now)
502
+ if domain in domains_with_cached_entries or self._has_local_certificate(domain):
503
+ self._schedule_existing_certificate_retry(domain, now)
504
+ else:
505
+ self._register_self_signed(domain)
496
506
  except Exception as e:
497
507
  self._set_error(e)
498
508
  for domain in domains:
@@ -563,6 +573,14 @@ class RenewalManager:
563
573
  self._cache = {d: exp for d, exp in self._cache.items() if d in self._watch_domains}
564
574
  self._lock.notify_all()
565
575
 
576
+ def _covered_domains_from_response(self, certs) -> Set[str]:
577
+ covered: Set[str] = set()
578
+ for cert in certs:
579
+ for domain in cert.domains:
580
+ if domain:
581
+ covered.add(domain)
582
+ return covered
583
+
566
584
  def _add_to_blacklist(self, domain: str, now: datetime):
567
585
  with self._lock:
568
586
  self._blacklist[domain] = now + timedelta(seconds=self.blacklist_duration_seconds)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: certapi
3
- Version: 1.1.8
3
+ Version: 1.1.9
4
4
  Summary: Python Package for managing keys, request SSL certificates from ACME.
5
5
  Home-page: https://github.com/mesudip/certapi
6
6
  Author: Sudip Bhattarai
@@ -425,7 +425,7 @@ def test_renewal_manager_passes_typed_obtain_options_into_renewal_calls():
425
425
  assert len(client.calls) == 1
426
426
  assert client.calls[0]["kwargs"]["batch_domains"] is True
427
427
  assert client.calls[0]["kwargs"]["self_verify"] is False
428
- assert client.calls[0]["kwargs"]["skip_failing"] is False
428
+ assert client.calls[0]["kwargs"]["skip_failing"] is True
429
429
  assert client.calls[0]["kwargs"]["renew_threshold_days"] == 30
430
430
  assert client.calls[0]["kwargs"]["organization"] == "certapi-tests"
431
431
 
@@ -523,6 +523,25 @@ def test_new_domain_failure_selfsigns_and_blacklists():
523
523
  assert len(client.calls) == 2
524
524
 
525
525
 
526
+ def test_partial_renewal_success_selfsigns_uncovered_domains():
527
+ now = datetime(2026, 1, 1, tzinfo=UTC)
528
+ client = DummyClient()
529
+ client.key_store = DummyKeyStore()
530
+ ok_cert = _make_cert_pem("ok.example.com", now, valid_for_days=60)
531
+ client.set_handler(
532
+ "ok.example.com",
533
+ CertificateResponse(issued=[IssuedCert(cert=ok_cert, domains=["ok.example.com"])], existing=[]),
534
+ )
535
+
536
+ mgr = RenewalManager(client, renew_threshold_days=30, clock_fn=lambda: now)
537
+ mgr._renew_domains(["ok.example.com", "missing.example.com"], now)
538
+
539
+ with mgr._lock:
540
+ assert "ok.example.com" in mgr._cache
541
+ assert len(client.key_store.saved_certs) == 1
542
+ assert client.key_store.saved_certs[0][2] == "missing.example.com.selfsigned"
543
+
544
+
526
545
  def test_update_watch_domains_blocks_until_running_worker_processes_domains():
527
546
  now = datetime(2026, 1, 1, tzinfo=UTC)
528
547
  started = threading.Event()
@@ -672,7 +691,7 @@ def test_remote_certapi_polling_prints_waiting_message(capsys):
672
691
  assert len(client.obtain_calls) == 1
673
692
 
674
693
 
675
- def test_remote_renewal_disables_skip_failing_so_unverified_domain_fails():
694
+ def test_remote_renewal_uses_skip_failing_and_selfsigns_unresolved_domain():
676
695
  now = datetime(2026, 1, 1, tzinfo=UTC)
677
696
  client = DummyRemoteSkipFailingClient()
678
697
  client.key_store = DummyKeyStore()
@@ -681,9 +700,9 @@ def test_remote_renewal_disables_skip_failing_so_unverified_domain_fails():
681
700
  mgr.update_watch_domains(["missing.example.com"])
682
701
 
683
702
  assert len(client.obtain_calls) == 1
684
- assert client.obtain_calls[0]["kwargs"]["skip_failing"] is False
703
+ assert client.obtain_calls[0]["kwargs"]["skip_failing"] is True
685
704
  assert "missing.example.com" in mgr.get_state()["blacklisted_domains"]
686
- assert "None of the domains are owned" in mgr.get_state()["last_error_message"]
705
+ assert mgr.get_state()["last_error_message"] is None
687
706
  assert len(client.key_store.saved_certs) == 1
688
707
  assert client.key_store.saved_certs[0][2] == "missing.example.com.selfsigned"
689
708
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes