open-document-lib 1.0.0__tar.gz → 1.0.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 (42) hide show
  1. {open_document_lib-1.0.0/open_document_lib.egg-info → open_document_lib-1.0.2}/PKG-INFO +2 -1
  2. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/README.md +11 -0
  3. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/odf_lib/odf_common.py +1 -1
  4. {open_document_lib-1.0.0 → open_document_lib-1.0.2/open_document_lib.egg-info}/PKG-INFO +2 -1
  5. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/open_document_lib.egg-info/requires.txt +1 -0
  6. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/pyproject.toml +7 -2
  7. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_install.py +69 -1
  8. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/LICENSE +0 -0
  9. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/docs/library-api.md +0 -0
  10. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/odf_lib/__init__.py +0 -0
  11. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/odf_lib/citation_mapping.py +0 -0
  12. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/odf_lib/py.typed +0 -0
  13. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/open_document_lib.egg-info/SOURCES.txt +0 -0
  14. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/open_document_lib.egg-info/dependency_links.txt +0 -0
  15. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/open_document_lib.egg-info/top_level.txt +0 -0
  16. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/setup.cfg +0 -0
  17. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_benchmarks.py +0 -0
  18. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_citations.py +0 -0
  19. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_corpus.py +0 -0
  20. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_cross_refs.py +0 -0
  21. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_dao_template.py +0 -0
  22. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_docs.py +0 -0
  23. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_edge_cases.py +0 -0
  24. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_examples.py +0 -0
  25. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_flat_odf.py +0 -0
  26. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_footnotes.py +0 -0
  27. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_lib_odf_common.py +0 -0
  28. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_libreoffice_integration.py +0 -0
  29. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_math.py +0 -0
  30. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_meta_lifecycle.py +0 -0
  31. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odg_connectors.py +0 -0
  32. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odg_gluepoints.py +0 -0
  33. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odg_groups.py +0 -0
  34. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odp_animations.py +0 -0
  35. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odp_master.py +0 -0
  36. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_odp_transitions.py +0 -0
  37. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_ods_charts.py +0 -0
  38. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_ods_named_ranges.py +0 -0
  39. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_ods_validation.py +0 -0
  40. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_property.py +0 -0
  41. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_schema_validation.py +0 -0
  42. {open_document_lib-1.0.0 → open_document_lib-1.0.2}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-document-lib
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Standard-library toolkit for reading, editing, and writing OpenDocument Format files (ODT, ODP, ODS, ODG)
5
5
  Author: Patrick Leiverkus
6
6
  License: MIT
@@ -36,6 +36,7 @@ Requires-Dist: ruff>=0.9; extra == "dev"
36
36
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
37
37
  Requires-Dist: hypothesis>=6.0; extra == "dev"
38
38
  Requires-Dist: build>=1.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
41
42
  # open-document-lib
@@ -89,6 +89,17 @@ The skills are available through three channels — pick what fits your setup:
89
89
 
90
90
  Browse and install via [smithery.ai](https://smithery.ai). Install one or more skills directly through the Smithery UI: [odt](https://smithery.ai/skills/leiverkus/odt), [odp](https://smithery.ai/skills/leiverkus/odp), [ods](https://smithery.ai/skills/leiverkus/ods), [odg](https://smithery.ai/skills/leiverkus/odg).
91
91
 
92
+ ### Claude Code plugin marketplace
93
+
94
+ The repository is also a Claude Code plugin marketplace. Add it once, then install the plugin — all four skills come bundled:
95
+
96
+ ```text
97
+ /plugin marketplace add leiverkus/open-document-skills
98
+ /plugin install open-document-skills@leiverkus-skills
99
+ ```
100
+
101
+ `/plugin marketplace update` pulls later releases.
102
+
92
103
  ### Open Agent Skills CLI
93
104
 
94
105
  The [vercel-labs/skills](https://github.com/vercel-labs/skills) CLI installs across Claude Code, Codex, Cursor, OpenCode, and 50+ other agents:
@@ -19,7 +19,7 @@ from datetime import datetime, timezone
19
19
  from pathlib import Path
20
20
  from xml.etree import ElementTree as ET
21
21
 
22
- VERSION = "1.0.0" # keep in sync with pyproject.toml (see CONTRIBUTING.md)
22
+ VERSION = "1.0.2" # keep in sync with pyproject.toml (see CONTRIBUTING.md)
23
23
 
24
24
  ODF_NAMESPACES: dict[str, str] = {
25
25
  "office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-document-lib
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Standard-library toolkit for reading, editing, and writing OpenDocument Format files (ODT, ODP, ODS, ODG)
5
5
  Author: Patrick Leiverkus
6
6
  License: MIT
@@ -36,6 +36,7 @@ Requires-Dist: ruff>=0.9; extra == "dev"
36
36
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
37
37
  Requires-Dist: hypothesis>=6.0; extra == "dev"
38
38
  Requires-Dist: build>=1.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
41
42
  # open-document-lib
@@ -5,6 +5,7 @@ ruff>=0.9
5
5
  pytest-cov>=5.0
6
6
  hypothesis>=6.0
7
7
  build>=1.0
8
+ mypy>=1.8
8
9
 
9
10
  [odf]
10
11
  odfpy>=1.4.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "open-document-lib"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
8
  description = "Standard-library toolkit for reading, editing, and writing OpenDocument Format files (ODT, ODP, ODS, ODG)"
9
9
  readme = "docs/library-api.md"
10
10
  requires-python = ">=3.10"
@@ -47,7 +47,7 @@ Issues = "https://github.com/leiverkus/open-document-skills/issues"
47
47
  odf = ["odfpy>=1.4.0"]
48
48
  scholarly = ["bibtexparser>=1.4"]
49
49
  validate = ["lxml>=4.9"]
50
- dev = ["pytest>=8.0", "ruff>=0.9", "pytest-cov>=5.0", "hypothesis>=6.0", "build>=1.0"]
50
+ dev = ["pytest>=8.0", "ruff>=0.9", "pytest-cov>=5.0", "hypothesis>=6.0", "build>=1.0", "mypy>=1.8"]
51
51
 
52
52
  [tool.setuptools]
53
53
  packages = ["odf_lib"]
@@ -65,6 +65,11 @@ source = ["odf_lib"]
65
65
  fail_under = 30
66
66
  show_missing = true
67
67
 
68
+ [tool.mypy]
69
+ python_version = "3.10"
70
+ # Optional deps (lxml, bibtexparser) are lazily imported; don't fail on them.
71
+ ignore_missing_imports = true
72
+
68
73
  [tool.ruff]
69
74
  line-length = 120
70
75
  target-version = "py310"
@@ -8,7 +8,9 @@ import tempfile
8
8
  import unittest
9
9
  from pathlib import Path
10
10
 
11
- from helpers import ROOT
11
+ from helpers import ROOT, run_script
12
+
13
+ MINIMAL_ODT_SPEC = {"title": "Install Test", "blocks": [{"type": "paragraph", "text": "Hi"}]}
12
14
 
13
15
 
14
16
  class InstallTests(unittest.TestCase):
@@ -89,6 +91,72 @@ class InstallTests(unittest.TestCase):
89
91
  self.assertTrue((dest / "skills" / "odt" / "SKILL.md").exists())
90
92
  self.assertTrue((dest / "skills" / "odg" / "scripts").is_dir())
91
93
 
94
+ def test_installed_skill_bundles_odf_lib_and_runs(self) -> None:
95
+ """A standalone-installed skill must carry odf_lib/ and run its scripts.
96
+
97
+ Regression: installs previously omitted the shared odf_lib/ package,
98
+ so every script crashed with ModuleNotFoundError outside the repo
99
+ checkout (surfaced when the plugin was installed in Claude Cowork).
100
+ """
101
+ with tempfile.TemporaryDirectory() as tmp:
102
+ tmp_path = Path(tmp)
103
+ dest = tmp_path / "skills"
104
+ subprocess.run(
105
+ [
106
+ sys.executable,
107
+ str(ROOT / "scripts" / "install_skills.py"),
108
+ "--dest",
109
+ str(dest),
110
+ "--skills",
111
+ "odt",
112
+ ],
113
+ cwd=ROOT,
114
+ check=True,
115
+ stdout=subprocess.PIPE,
116
+ stderr=subprocess.STDOUT,
117
+ text=True,
118
+ )
119
+ # odf_lib/ must be bundled into the installed skill.
120
+ self.assertTrue((dest / "odt" / "odf_lib" / "odf_common.py").is_file())
121
+ # The install must be free of build junk.
122
+ self.assertEqual(list(dest.rglob("__pycache__")), [])
123
+ self.assertEqual(list(dest.rglob("*.pyc")), [])
124
+ # A bundled script must run standalone — repo odf_lib/ is unreachable here.
125
+ spec = tmp_path / "spec.json"
126
+ spec.write_text(json.dumps(MINIMAL_ODT_SPEC), encoding="utf-8")
127
+ out = tmp_path / "out.odt"
128
+ run_script(dest / "odt" / "scripts" / "create_minimal_odt.py", spec, out)
129
+ result = run_script(dest / "odt" / "scripts" / "validate_refs.py", out)
130
+ self.assertEqual(json.loads(result.stdout)["status"], "ok")
131
+
132
+ def test_claude_plugin_bundle_runs_standalone(self) -> None:
133
+ """The Claude plugin bundle must carry odf_lib/ and run its scripts."""
134
+ with tempfile.TemporaryDirectory() as tmp:
135
+ tmp_path = Path(tmp)
136
+ dest = tmp_path / "plugin"
137
+ subprocess.run(
138
+ [
139
+ sys.executable,
140
+ str(ROOT / "scripts" / "install_skills.py"),
141
+ "--target",
142
+ "claude",
143
+ "--dest",
144
+ str(dest),
145
+ ],
146
+ cwd=ROOT,
147
+ check=True,
148
+ stdout=subprocess.PIPE,
149
+ stderr=subprocess.STDOUT,
150
+ text=True,
151
+ )
152
+ self.assertTrue((dest / "odf_lib" / "odf_common.py").is_file())
153
+ spec = tmp_path / "spec.json"
154
+ spec.write_text(json.dumps(MINIMAL_ODT_SPEC), encoding="utf-8")
155
+ out = tmp_path / "out.odt"
156
+ run_script(dest / "skills" / "odt" / "scripts" / "create_minimal_odt.py", spec, out)
157
+ result = run_script(dest / "skills" / "odt" / "scripts" / "validate_refs.py", out)
158
+ self.assertEqual(json.loads(result.stdout)["status"], "ok")
159
+
92
160
  def test_claude_target_requires_destination(self) -> None:
93
161
  result = subprocess.run(
94
162
  [sys.executable, str(ROOT / "scripts" / "install_skills.py"), "--target", "claude"],