libcontext 0.6.0__tar.gz → 0.6.1__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 (45) hide show
  1. {libcontext-0.6.0 → libcontext-0.6.1}/CHANGELOG.md +10 -1
  2. {libcontext-0.6.0 → libcontext-0.6.1}/PKG-INFO +1 -1
  3. {libcontext-0.6.0 → libcontext-0.6.1}/pyproject.toml +1 -1
  4. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/collector.py +14 -2
  5. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_collector.py +50 -10
  6. {libcontext-0.6.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  7. {libcontext-0.6.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  8. {libcontext-0.6.0 → libcontext-0.6.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  9. {libcontext-0.6.0 → libcontext-0.6.1}/.github/workflows/ci.yml +0 -0
  10. {libcontext-0.6.0 → libcontext-0.6.1}/.github/workflows/release.yml +0 -0
  11. {libcontext-0.6.0 → libcontext-0.6.1}/.gitignore +0 -0
  12. {libcontext-0.6.0 → libcontext-0.6.1}/CONTRIBUTING.md +0 -0
  13. {libcontext-0.6.0 → libcontext-0.6.1}/DEPENDENCIES.md +0 -0
  14. {libcontext-0.6.0 → libcontext-0.6.1}/LICENSE +0 -0
  15. {libcontext-0.6.0 → libcontext-0.6.1}/README.md +0 -0
  16. {libcontext-0.6.0 → libcontext-0.6.1}/SECURITY.md +0 -0
  17. {libcontext-0.6.0 → libcontext-0.6.1}/docs/adr/001-progressive-disclosure-over-always-on-context.md +0 -0
  18. {libcontext-0.6.0 → libcontext-0.6.1}/docs/adr/002-skill-plus-cli-as-primary-integration.md +0 -0
  19. {libcontext-0.6.0 → libcontext-0.6.1}/docs/adr/004-ast-only-inspection.md +0 -0
  20. {libcontext-0.6.0 → libcontext-0.6.1}/docs/adr/README.md +0 -0
  21. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/__init__.py +0 -0
  22. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/_envsetup.py +0 -0
  23. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/_security.py +0 -0
  24. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/cache.py +0 -0
  25. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/cli.py +0 -0
  26. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/config.py +0 -0
  27. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/diff.py +0 -0
  28. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/exceptions.py +0 -0
  29. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/inspector.py +0 -0
  30. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/mcp_server.py +0 -0
  31. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/models.py +0 -0
  32. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/py.typed +0 -0
  33. {libcontext-0.6.0 → libcontext-0.6.1}/src/libcontext/renderer.py +0 -0
  34. {libcontext-0.6.0 → libcontext-0.6.1}/tests/__init__.py +0 -0
  35. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_cache.py +0 -0
  36. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_cli.py +0 -0
  37. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_cli_mcp_parity.py +0 -0
  38. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_config.py +0 -0
  39. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_diff.py +0 -0
  40. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_envsetup.py +0 -0
  41. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_inspector.py +0 -0
  42. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_mcp_server.py +0 -0
  43. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_models.py +0 -0
  44. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_renderer.py +0 -0
  45. {libcontext-0.6.0 → libcontext-0.6.1}/tests/test_security.py +0 -0
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.1] - 2026-03-25
11
+
12
+ ### Fixed
13
+
14
+ - **ImportError crash with `packages_distributions()`**: wrapped call in try/except to handle broken metadata or mixed Python installations (e.g. `_csv` ImportError on Python 3.13).
15
+ - **Python 3.9 compatibility**: fixed parenthesized context managers and `create=True` for `mock.patch`.
16
+
10
17
  ## [0.5.0] - 2026-03-23
11
18
 
12
19
  ### Added
@@ -101,7 +108,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
101
108
  - Free-form `extra_context` field for library authors.
102
109
  - Python API for programmatic usage (`collect_package`, `render_package`).
103
110
 
104
- [Unreleased]: https://github.com/Syclaw/libcontext/compare/v0.5.0...HEAD
111
+ [Unreleased]: https://github.com/Syclaw/libcontext/compare/v0.6.1...HEAD
112
+ [0.6.1]: https://github.com/Syclaw/libcontext/compare/v0.6.0...v0.6.1
113
+ [0.6.0]: https://github.com/Syclaw/libcontext/compare/v0.5.0...v0.6.0
105
114
  [0.5.0]: https://github.com/Syclaw/libcontext/compare/v0.4.0...v0.5.0
106
115
  [0.4.0]: https://github.com/Syclaw/libcontext/compare/v0.3.0...v0.4.0
107
116
  [0.3.0]: https://github.com/Syclaw/libcontext/compare/v0.2.0...v0.3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libcontext
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Generate optimized LLM context from Python library APIs — CLI, skill, and MCP server
5
5
  Project-URL: Homepage, https://github.com/Syclaw/libcontext
6
6
  Project-URL: Repository, https://github.com/Syclaw/libcontext
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "libcontext"
7
- version = "0.6.0"
7
+ version = "0.6.1"
8
8
  description = "Generate optimized LLM context from Python library APIs — CLI, skill, and MCP server"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -47,8 +47,20 @@ def _get_installed_package_names() -> list[str]:
47
47
  names: set[str] = set()
48
48
 
49
49
  if sys.version_info >= (3, 11):
50
- for import_name in importlib.metadata.packages_distributions():
51
- names.add(import_name)
50
+ try:
51
+ for import_name in importlib.metadata.packages_distributions():
52
+ names.add(import_name)
53
+ except (ImportError, Exception):
54
+ # packages_distributions() may fail when the environment
55
+ # contains distributions with broken metadata or when a
56
+ # mixed Python installation causes an ImportError inside
57
+ # stdlib modules (e.g. csv). Fall through to the
58
+ # distributions()-based collection below which is more
59
+ # resilient.
60
+ logger.debug(
61
+ "packages_distributions() failed; falling back to distributions() only",
62
+ exc_info=True,
63
+ )
52
64
 
53
65
  seen_distributions: set[str] = set()
54
66
  for dist in importlib.metadata.distributions():
@@ -676,26 +676,66 @@ def test_suggest_similar_packages_uses_top_level_names() -> None:
676
676
  assert "PIL" in suggestions
677
677
 
678
678
 
679
+ _PKGS_DISTS_PATH = "libcontext.collector.importlib.metadata.packages_distributions"
680
+
681
+
682
+ def test_get_installed_package_names_handles_import_error() -> None:
683
+ """packages_distributions() raising ImportError is caught gracefully."""
684
+ dists = [
685
+ _make_mock_dist("requests"),
686
+ ]
687
+ err = ImportError("cannot import name '__version__' from '_csv'")
688
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
689
+ with patch(_DISTS_PATH, return_value=dists):
690
+ names = _get_installed_package_names()
691
+
692
+ # Should still collect names from distributions() fallback
693
+ assert "requests" in names
694
+
695
+
696
+ def test_get_installed_package_names_handles_generic_exception() -> None:
697
+ """packages_distributions() raising any Exception is caught gracefully."""
698
+ dists = [
699
+ _make_mock_dist("flask"),
700
+ ]
701
+ err = RuntimeError("broken metadata")
702
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
703
+ with patch(_DISTS_PATH, return_value=dists):
704
+ names = _get_installed_package_names()
705
+
706
+ assert "flask" in names
707
+
708
+
709
+ def test_suggest_similar_packages_resilient_to_import_error() -> None:
710
+ """suggest_similar_packages works even if packages_distributions() fails."""
711
+ dists = [
712
+ _make_mock_dist("requests"),
713
+ _make_mock_dist("flask"),
714
+ ]
715
+ err = ImportError("csv import broken")
716
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
717
+ with patch(_DISTS_PATH, return_value=dists):
718
+ suggestions = suggest_similar_packages("reqeusts")
719
+
720
+ assert "requests" in suggestions
721
+
722
+
679
723
  def test_collect_package_error_includes_suggestions() -> None:
680
724
  """PackageNotFoundError from collect_package includes suggestions."""
681
725
  dists = [
682
726
  _make_mock_dist("click"),
683
727
  _make_mock_dist("flask"),
684
728
  ]
685
- with (
686
- patch(_DISTS_PATH, return_value=dists),
687
- pytest.raises(PackageNotFoundError, match="Did you mean"),
688
- ):
689
- collect_package("clck")
729
+ with patch(_DISTS_PATH, return_value=dists): # noqa: SIM117
730
+ with pytest.raises(PackageNotFoundError, match="Did you mean"):
731
+ collect_package("clck")
690
732
 
691
733
 
692
734
  def test_collect_package_error_no_suggestions() -> None:
693
735
  """PackageNotFoundError without suggestions shows install hint."""
694
- with (
695
- patch(_DISTS_PATH, return_value=[]),
696
- pytest.raises(PackageNotFoundError, match="Make sure it is installed"),
697
- ):
698
- collect_package("totally_nonexistent_pkg_xyz_999")
736
+ with patch(_DISTS_PATH, return_value=[]): # noqa: SIM117
737
+ with pytest.raises(PackageNotFoundError, match="Make sure it is installed"):
738
+ collect_package("totally_nonexistent_pkg_xyz_999")
699
739
 
700
740
 
701
741
  # ---------------------------------------------------------------------------
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes