libcontext 0.7.1__tar.gz → 0.7.2__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.7.1 → libcontext-0.7.2}/PKG-INFO +1 -1
  2. {libcontext-0.7.1 → libcontext-0.7.2}/pyproject.toml +1 -1
  3. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/_envsetup.py +54 -28
  4. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_envsetup.py +14 -8
  5. {libcontext-0.7.1 → libcontext-0.7.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  6. {libcontext-0.7.1 → libcontext-0.7.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  7. {libcontext-0.7.1 → libcontext-0.7.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  8. {libcontext-0.7.1 → libcontext-0.7.2}/.github/workflows/ci.yml +0 -0
  9. {libcontext-0.7.1 → libcontext-0.7.2}/.github/workflows/release.yml +0 -0
  10. {libcontext-0.7.1 → libcontext-0.7.2}/.gitignore +0 -0
  11. {libcontext-0.7.1 → libcontext-0.7.2}/CHANGELOG.md +0 -0
  12. {libcontext-0.7.1 → libcontext-0.7.2}/CONTRIBUTING.md +0 -0
  13. {libcontext-0.7.1 → libcontext-0.7.2}/DEPENDENCIES.md +0 -0
  14. {libcontext-0.7.1 → libcontext-0.7.2}/LICENSE +0 -0
  15. {libcontext-0.7.1 → libcontext-0.7.2}/README.md +0 -0
  16. {libcontext-0.7.1 → libcontext-0.7.2}/SECURITY.md +0 -0
  17. {libcontext-0.7.1 → libcontext-0.7.2}/docs/adr/001-progressive-disclosure-over-always-on-context.md +0 -0
  18. {libcontext-0.7.1 → libcontext-0.7.2}/docs/adr/002-skill-plus-cli-as-primary-integration.md +0 -0
  19. {libcontext-0.7.1 → libcontext-0.7.2}/docs/adr/004-ast-only-inspection.md +0 -0
  20. {libcontext-0.7.1 → libcontext-0.7.2}/docs/adr/README.md +0 -0
  21. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/__init__.py +0 -0
  22. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/_security.py +0 -0
  23. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/cache.py +0 -0
  24. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/cli.py +0 -0
  25. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/collector.py +0 -0
  26. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/config.py +0 -0
  27. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/diff.py +0 -0
  28. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/exceptions.py +0 -0
  29. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/inspector.py +0 -0
  30. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/mcp_server.py +0 -0
  31. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/models.py +0 -0
  32. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/py.typed +0 -0
  33. {libcontext-0.7.1 → libcontext-0.7.2}/src/libcontext/renderer.py +0 -0
  34. {libcontext-0.7.1 → libcontext-0.7.2}/tests/__init__.py +0 -0
  35. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_cache.py +0 -0
  36. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_cli.py +0 -0
  37. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_cli_mcp_parity.py +0 -0
  38. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_collector.py +0 -0
  39. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_config.py +0 -0
  40. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_diff.py +0 -0
  41. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_inspector.py +0 -0
  42. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_mcp_server.py +0 -0
  43. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_models.py +0 -0
  44. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_renderer.py +0 -0
  45. {libcontext-0.7.1 → libcontext-0.7.2}/tests/test_security.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libcontext
3
- Version: 0.7.1
3
+ Version: 0.7.2
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.7.1"
7
+ version = "0.7.2"
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"
@@ -46,6 +46,50 @@ _INTERPRETER_CANDIDATES = (
46
46
  Path("bin") / "python3", # Unix alternative
47
47
  )
48
48
 
49
+ # Script executed in the *target* interpreter to collect site-packages
50
+ # directories. Kept as a module constant so it can be tested directly.
51
+ # Uses only stdlib modules guaranteed present in Python 3.9+.
52
+ _SITE_PACKAGES_SCRIPT = """\
53
+ import json, site, sys, os
54
+
55
+ paths = []
56
+
57
+ # site-packages directories (includes system site-packages when the
58
+ # venv was created with --system-site-packages)
59
+ try:
60
+ paths.extend(site.getsitepackages())
61
+ except AttributeError:
62
+ pass
63
+
64
+ # User site-packages (e.g. ~/.local/lib/python3.x/site-packages),
65
+ # honoured only when ENABLE_USER_SITE is not explicitly disabled.
66
+ if site.ENABLE_USER_SITE:
67
+ try:
68
+ user = site.getusersitepackages()
69
+ if isinstance(user, str):
70
+ paths.append(user)
71
+ except AttributeError:
72
+ pass
73
+
74
+ # .pth files in site-packages can inject arbitrary paths into sys.path
75
+ # (editable installs, namespace packages, etc.). Collect them by
76
+ # diffing sys.path against site-packages — any path that is not a
77
+ # site-packages dir and not under sys.base_prefix is .pth-injected.
78
+ site_set = set(os.path.realpath(p) for p in paths)
79
+ base = os.path.realpath(sys.base_prefix)
80
+ for p in sys.path:
81
+ if not p:
82
+ continue
83
+ rp = os.path.realpath(p)
84
+ if rp in site_set:
85
+ continue
86
+ if rp == base or rp.startswith(base + os.sep):
87
+ continue
88
+ paths.append(p)
89
+
90
+ print(json.dumps(paths))
91
+ """
92
+
49
93
 
50
94
  def _has_python_interpreter(venv_dir: Path) -> bool:
51
95
  """Check whether a directory contains a recognisable Python interpreter."""
@@ -262,44 +306,26 @@ def resolve_python_executable(python_arg: str) -> Path:
262
306
 
263
307
 
264
308
  def get_target_sys_path(python_exe: Path) -> list[str]:
265
- """Query a Python interpreter for its non-stdlib ``sys.path`` entries.
309
+ """Query a target interpreter for its package-discovery paths.
266
310
 
267
- Runs the interpreter in a subprocess with a short timeout to
268
- extract site-packages and user-added paths. Stdlib paths are
269
- excluded to prevent cross-version import contamination when the
270
- tool interpreter differs from the target (e.g. tool on 3.13,
271
- target venv on 3.11).
311
+ Collects site-packages directories (including system site-packages
312
+ for ``--system-site-packages`` venvs), user site-packages, and
313
+ any paths injected by ``.pth`` files (editable installs, namespace
314
+ packages). Stdlib paths are **excluded** to prevent cross-version
315
+ import contamination when tool and target run different Python
316
+ versions.
272
317
 
273
318
  Args:
274
319
  python_exe: Absolute path to the target Python executable.
275
320
 
276
321
  Returns:
277
- List of non-stdlib path strings from the target interpreter.
322
+ List of path strings suitable for prepending to ``sys.path``
323
+ in the tool process.
278
324
 
279
325
  Raises:
280
326
  EnvironmentSetupError: If the subprocess fails or times out.
281
327
  """
282
- # Filter out stdlib paths from the target interpreter's sys.path.
283
- # When the target runs a different Python version from the tool,
284
- # injecting stdlib paths causes C-extension mismatches (e.g.
285
- # Python 3.13 loading csv.py from 3.11 stdlib → _csv ImportError).
286
- script = (
287
- "import json, os, sys\n"
288
- "base = os.path.realpath(sys.base_prefix)\n"
289
- "prefix = os.path.realpath(sys.prefix)\n"
290
- "in_venv = prefix != base\n"
291
- "def _under(p, root):\n"
292
- " return p == root or p.startswith(root + os.sep)\n"
293
- "paths = [\n"
294
- " p for p in sys.path\n"
295
- " if p and not (\n"
296
- " in_venv\n"
297
- " and _under(os.path.realpath(p), base)\n"
298
- " and not _under(os.path.realpath(p), prefix)\n"
299
- " )\n"
300
- "]\n"
301
- "print(json.dumps(paths))\n"
302
- )
328
+ script = _SITE_PACKAGES_SCRIPT
303
329
  try:
304
330
  result = subprocess.run(
305
331
  [str(python_exe), "-c", script],
@@ -82,8 +82,8 @@ def test_get_target_sys_path_bad_executable(tmp_path):
82
82
  get_target_sys_path(fake)
83
83
 
84
84
 
85
- def test_get_target_sys_path_excludes_base_stdlib_in_venv():
86
- """Stdlib paths from the base interpreter are excluded when in a venv."""
85
+ def test_get_target_sys_path_returns_site_packages_not_stdlib():
86
+ """Returns site-packages paths and excludes base-interpreter stdlib."""
87
87
  paths = get_target_sys_path(Path(sys.executable))
88
88
 
89
89
  base = os.path.realpath(sys.base_prefix)
@@ -91,14 +91,20 @@ def test_get_target_sys_path_excludes_base_stdlib_in_venv():
91
91
  if prefix == base:
92
92
  pytest.skip("not running inside a venv")
93
93
 
94
- # Paths under the base Python installation (stdlib, lib-dynload, etc.)
95
- # must be excluded to prevent cross-version contamination.
96
- # Paths under the venv prefix itself (e.g. site-packages) are kept.
94
+ # At least one site-packages directory must be present.
95
+ assert any("site-packages" in p for p in paths), (
96
+ f"no site-packages found in returned paths: {paths}"
97
+ )
98
+
99
+ # No path should resolve under the base Python installation
100
+ # (stdlib, lib-dynload, zip archives) unless it is also under
101
+ # the venv prefix.
102
+ def _under(child: str, root: str) -> bool:
103
+ return child == root or child.startswith(root + os.sep)
104
+
97
105
  for p in paths:
98
106
  rp = os.path.realpath(p)
99
- is_under_base = rp.startswith(base + os.sep) or rp == base
100
- is_under_venv = rp.startswith(prefix + os.sep) or rp == prefix
101
- if is_under_base and not is_under_venv:
107
+ if _under(rp, base) and not _under(rp, prefix):
102
108
  pytest.fail(f"base-interpreter stdlib path leaked: {p}")
103
109
 
104
110
 
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