scitex 2.16.0__py3-none-any.whl → 2.16.1__py3-none-any.whl

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 (64) hide show
  1. scitex/_mcp_tools/audio.py +11 -65
  2. scitex/audio/README.md +40 -12
  3. scitex/audio/__init__.py +27 -235
  4. scitex/audio/_audio_check.py +93 -0
  5. scitex/audio/_mcp/speak_handlers.py +56 -8
  6. scitex/audio/_speak.py +295 -0
  7. scitex/audio/mcp_server.py +98 -73
  8. scitex/social/__init__.py +1 -24
  9. scitex/writer/README.md +25 -409
  10. scitex/writer/__init__.py +98 -13
  11. {scitex-2.16.0.dist-info → scitex-2.16.1.dist-info}/METADATA +6 -1
  12. {scitex-2.16.0.dist-info → scitex-2.16.1.dist-info}/RECORD +15 -62
  13. scitex/writer/Writer.py +0 -487
  14. scitex/writer/_clone_writer_project.py +0 -160
  15. scitex/writer/_compile/__init__.py +0 -41
  16. scitex/writer/_compile/_compile_async.py +0 -130
  17. scitex/writer/_compile/_compile_unified.py +0 -148
  18. scitex/writer/_compile/_parser.py +0 -63
  19. scitex/writer/_compile/_runner.py +0 -457
  20. scitex/writer/_compile/_validator.py +0 -46
  21. scitex/writer/_compile/manuscript.py +0 -110
  22. scitex/writer/_compile/revision.py +0 -82
  23. scitex/writer/_compile/supplementary.py +0 -100
  24. scitex/writer/_dataclasses/__init__.py +0 -44
  25. scitex/writer/_dataclasses/config/_CONSTANTS.py +0 -46
  26. scitex/writer/_dataclasses/config/_WriterConfig.py +0 -175
  27. scitex/writer/_dataclasses/config/__init__.py +0 -9
  28. scitex/writer/_dataclasses/contents/_ManuscriptContents.py +0 -236
  29. scitex/writer/_dataclasses/contents/_RevisionContents.py +0 -136
  30. scitex/writer/_dataclasses/contents/_SupplementaryContents.py +0 -114
  31. scitex/writer/_dataclasses/contents/__init__.py +0 -9
  32. scitex/writer/_dataclasses/core/_Document.py +0 -146
  33. scitex/writer/_dataclasses/core/_DocumentSection.py +0 -546
  34. scitex/writer/_dataclasses/core/__init__.py +0 -7
  35. scitex/writer/_dataclasses/results/_CompilationResult.py +0 -165
  36. scitex/writer/_dataclasses/results/_LaTeXIssue.py +0 -102
  37. scitex/writer/_dataclasses/results/_SaveSectionsResponse.py +0 -118
  38. scitex/writer/_dataclasses/results/_SectionReadResponse.py +0 -131
  39. scitex/writer/_dataclasses/results/__init__.py +0 -11
  40. scitex/writer/_dataclasses/tree/MINIMUM_FILES.md +0 -121
  41. scitex/writer/_dataclasses/tree/_ConfigTree.py +0 -86
  42. scitex/writer/_dataclasses/tree/_ManuscriptTree.py +0 -84
  43. scitex/writer/_dataclasses/tree/_RevisionTree.py +0 -97
  44. scitex/writer/_dataclasses/tree/_ScriptsTree.py +0 -118
  45. scitex/writer/_dataclasses/tree/_SharedTree.py +0 -100
  46. scitex/writer/_dataclasses/tree/_SupplementaryTree.py +0 -101
  47. scitex/writer/_dataclasses/tree/__init__.py +0 -23
  48. scitex/writer/_mcp/__init__.py +0 -4
  49. scitex/writer/_mcp/handlers.py +0 -32
  50. scitex/writer/_mcp/tool_schemas.py +0 -33
  51. scitex/writer/_project/__init__.py +0 -29
  52. scitex/writer/_project/_create.py +0 -89
  53. scitex/writer/_project/_trees.py +0 -63
  54. scitex/writer/_project/_validate.py +0 -61
  55. scitex/writer/utils/.legacy_git_retry.py +0 -164
  56. scitex/writer/utils/__init__.py +0 -24
  57. scitex/writer/utils/_converters.py +0 -635
  58. scitex/writer/utils/_parse_latex_logs.py +0 -138
  59. scitex/writer/utils/_parse_script_args.py +0 -156
  60. scitex/writer/utils/_verify_tree_structure.py +0 -205
  61. scitex/writer/utils/_watch.py +0 -96
  62. {scitex-2.16.0.dist-info → scitex-2.16.1.dist-info}/WHEEL +0 -0
  63. {scitex-2.16.0.dist-info → scitex-2.16.1.dist-info}/entry_points.txt +0 -0
  64. {scitex-2.16.0.dist-info → scitex-2.16.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,138 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-10-28 17:11:22 (ywatanabe)"
4
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/writer/parse_latex.py
5
- # ----------------------------------------
6
- from __future__ import annotations
7
- import os
8
-
9
- __FILE__ = "./src/scitex/writer/parse_latex.py"
10
- __DIR__ = os.path.dirname(__FILE__)
11
- # ----------------------------------------
12
-
13
- """
14
- LaTeX error and warning parsing from compilation output.
15
-
16
- Simple parsing of LaTeX errors and warnings from stdout/stderr.
17
- """
18
-
19
- from pathlib import Path
20
- from typing import List
21
- from typing import Tuple
22
-
23
- from .._dataclasses import LaTeXIssue
24
-
25
-
26
- def parse_compilation_output(
27
- output: str, log_file: Path = None
28
- ) -> Tuple[List[LaTeXIssue], List[LaTeXIssue]]:
29
- """
30
- Parse errors and warnings from compilation output.
31
-
32
- Args:
33
- output: Compilation output (stdout + stderr)
34
- log_file: Optional path to .log file (unused, for compatibility)
35
-
36
- Returns:
37
- Tuple of (error_issues, warning_issues)
38
- """
39
- errors = []
40
- warnings = []
41
-
42
- for line in output.split("\n"):
43
- # LaTeX error pattern: "! Error message"
44
- if line.startswith("!"):
45
- error_text = line[1:].strip()
46
- if error_text:
47
- errors.append(LaTeXIssue(type="error", message=error_text))
48
-
49
- # LaTeX warning pattern
50
- elif "warning" in line.lower():
51
- warnings.append(LaTeXIssue(type="warning", message=line.strip()))
52
-
53
- return errors, warnings
54
-
55
-
56
- def run_session() -> None:
57
- """Initialize scitex framework, run main function, and cleanup."""
58
- global CONFIG, CC, sys, plt, rng
59
- import sys
60
- import matplotlib.pyplot as plt
61
- import scitex as stx
62
-
63
- args = parse_args()
64
-
65
- CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
66
- sys,
67
- plt,
68
- args=args,
69
- file=__FILE__,
70
- sdir_suffix=None,
71
- verbose=False,
72
- agg=True,
73
- )
74
-
75
- exit_status = main(args)
76
-
77
- stx.session.close(
78
- CONFIG,
79
- verbose=False,
80
- notify=False,
81
- message="",
82
- exit_status=exit_status,
83
- )
84
-
85
-
86
- def main(args):
87
- if args.file:
88
- with open(args.file) as f_f:
89
- output = f_f.read()
90
- else:
91
- output = args.text
92
-
93
- errors, warnings = parse_compilation_output(output)
94
-
95
- print(f"Errors: {len(errors)}")
96
- for err in errors:
97
- print(f" - {err}")
98
-
99
- print(f"Warnings: {len(warnings)}")
100
- for warn in warnings:
101
- print(f" - {warn}")
102
-
103
- return 0
104
-
105
-
106
- def parse_args():
107
- import argparse
108
-
109
- parser = argparse.ArgumentParser(
110
- description="Parse LaTeX compilation output for errors and warnings"
111
- )
112
- parser.add_argument(
113
- "--file",
114
- "-f",
115
- type=str,
116
- help="File containing compilation output",
117
- )
118
- parser.add_argument(
119
- "--text",
120
- "-t",
121
- type=str,
122
- help="Compilation output text",
123
- )
124
-
125
- return parser.parse_args()
126
-
127
-
128
- if __name__ == "__main__":
129
- run_session()
130
-
131
-
132
- __all__ = [
133
- "parse_compilation_output",
134
- ]
135
-
136
- # python -m scitex.writer.utils._parse_latex_logs --file compilation.log
137
-
138
- # EOF
@@ -1,156 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-10-28 17:30:00 (ywatanabe)"
4
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/writer/_parse_script_args.py
5
- # ----------------------------------------
6
- from __future__ import annotations
7
- import os
8
-
9
- __FILE__ = "./src/scitex/writer/_parse_script_args.py"
10
- __DIR__ = os.path.dirname(__FILE__)
11
- # ----------------------------------------
12
-
13
- """
14
- Script argument parser - extract available arguments from shell scripts.
15
-
16
- Parses shell scripts to extract documented arguments from usage() functions.
17
- """
18
-
19
- import re
20
- from pathlib import Path
21
- from dataclasses import dataclass
22
- from typing import Optional
23
-
24
-
25
- @dataclass
26
- class ScriptArgument:
27
- """Represents a single script argument."""
28
-
29
- short_flag: Optional[str] # e.g., "-nf"
30
- long_flag: Optional[str] # e.g., "--no_figs"
31
- description: str # e.g., "Exclude figures for quick compilation"
32
- default: Optional[str] # e.g., "false"
33
-
34
- def __str__(self) -> str:
35
- """Format as help text."""
36
- flags = ", ".join(f for f in [self.short_flag, self.long_flag] if f)
37
- default_str = f" (default: {self.default})" if self.default else ""
38
- return f"{flags:20} {self.description}{default_str}"
39
-
40
-
41
- class ScriptArgumentParser:
42
- """Extract arguments from shell script usage() functions."""
43
-
44
- @staticmethod
45
- def parse(script_path: Path) -> list[ScriptArgument]:
46
- """
47
- Parse shell script to extract available arguments.
48
-
49
- Looks for usage() function pattern:
50
- -flag, --long-flag Description (default: value)
51
-
52
- Args:
53
- script_path: Path to shell script
54
-
55
- Returns:
56
- List of ScriptArgument objects
57
- """
58
- if not script_path.exists():
59
- return []
60
-
61
- content = script_path.read_text()
62
-
63
- # Find usage() function
64
- usage_match = re.search(r"usage\s*\(\)\s*{(.*?)exit\s+0", content, re.DOTALL)
65
-
66
- if not usage_match:
67
- return []
68
-
69
- usage_text = usage_match.group(1)
70
-
71
- # Extract arguments from usage text (contains echo statements)
72
- # Find all lines with option flags (start with "echo" and contain flags)
73
- options_text = usage_text
74
- args = []
75
-
76
- # Parse each line in usage (will contain echo statements)
77
- for line in options_text.split("\n"):
78
- # Extract content between quotes in echo statements
79
- quote_match = re.search(r'"([^"]*)"', line)
80
- if not quote_match:
81
- continue
82
-
83
- line_content = quote_match.group(1).strip()
84
- if not line_content or line_content.startswith("#"):
85
- continue
86
-
87
- # Skip lines without flags
88
- if "-" not in line_content:
89
- continue
90
-
91
- arg = ScriptArgumentParser._parse_argument_line(line_content)
92
- if arg:
93
- args.append(arg)
94
-
95
- return args
96
-
97
- @staticmethod
98
- def _parse_argument_line(line: str) -> Optional[ScriptArgument]:
99
- """
100
- Parse single argument line.
101
-
102
- Format: "-nf, --no_figs Description (default: value)"
103
- """
104
- # Extract flags and description
105
- flags_match = re.match(r"(.*?)\s{2,}(.*)", line)
106
- if not flags_match:
107
- return None
108
-
109
- flags_str = flags_match.group(1).strip()
110
- rest = flags_match.group(2).strip()
111
-
112
- # Parse flags
113
- short_flag = None
114
- long_flag = None
115
-
116
- if "," in flags_str:
117
- parts = flags_str.split(",")
118
- short_flag = parts[0].strip() if parts[0].strip() else None
119
- long_flag = (
120
- parts[1].strip() if len(parts) > 1 and parts[1].strip() else None
121
- )
122
- else:
123
- # Single flag (short or long)
124
- if flags_str.startswith("--"):
125
- long_flag = flags_str
126
- elif flags_str.startswith("-"):
127
- short_flag = flags_str
128
- else:
129
- return None
130
-
131
- # Parse description and default
132
- description = rest
133
- default = None
134
-
135
- default_match = re.search(r"\(default:\s*([^)]+)\)", rest)
136
- if default_match:
137
- default = default_match.group(1).strip()
138
- description = rest[: default_match.start()].strip()
139
-
140
- if not description:
141
- return None
142
-
143
- return ScriptArgument(
144
- short_flag=short_flag,
145
- long_flag=long_flag,
146
- description=description,
147
- default=default,
148
- )
149
-
150
-
151
- __all__ = [
152
- "ScriptArgument",
153
- "ScriptArgumentParser",
154
- ]
155
-
156
- # EOF
@@ -1,205 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Timestamp: "2025-10-29 06:13:05 (ywatanabe)"
3
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/writer/_verify_tree_structure.py
4
- # ----------------------------------------
5
- from __future__ import annotations
6
-
7
- import os
8
-
9
- __FILE__ = "./src/scitex/writer/_verify_tree_structure.py"
10
- __DIR__ = os.path.dirname(__FILE__)
11
- # ----------------------------------------
12
-
13
- import argparse
14
-
15
- """Project structure validation for writer module.
16
-
17
- Leverages dataclass verify_structure() methods for validation."""
18
-
19
- from pathlib import Path
20
-
21
- from scitex.logging import getLogger
22
-
23
- from .._dataclasses import (
24
- ConfigTree,
25
- ManuscriptTree,
26
- RevisionTree,
27
- ScriptsTree,
28
- SharedTree,
29
- SupplementaryTree,
30
- )
31
-
32
- logger = getLogger(__name__)
33
-
34
- # Parameters
35
- TREE_VALIDATORS = {
36
- "config": {"dir_name": "config", "tree_class": ConfigTree},
37
- "00_shared": {"dir_name": "00_shared", "tree_class": SharedTree},
38
- "01_manuscript": {
39
- "dir_name": "01_manuscript",
40
- "tree_class": ManuscriptTree,
41
- },
42
- "02_supplementary": {
43
- "dir_name": "02_supplementary",
44
- "tree_class": SupplementaryTree,
45
- },
46
- "03_revision": {"dir_name": "03_revision", "tree_class": RevisionTree},
47
- "scripts": {"dir_name": "scripts", "tree_class": ScriptsTree},
48
- }
49
-
50
-
51
- # Exception classes
52
- class ProjectValidationError(Exception):
53
- """Raised when project structure is invalid."""
54
-
55
- pass
56
-
57
-
58
- # 2. Public validation functions
59
- def verify_tree_structure(
60
- project_dir: Path, func_name="validate_tree_structures"
61
- ) -> None:
62
- """Validates all tree structures in the project directory."""
63
- logger.info(
64
- f"{func_name}: Validating tree structures: {Path(project_dir).absolute()}..."
65
- )
66
- project_dir = Path(project_dir)
67
- for dir_name, (dir_path, tree_class) in TREE_VALIDATORS.items():
68
- validator_func_name = f"_validate_{dir_name}_structure"
69
- eval(validator_func_name)(project_dir)
70
- logger.success(
71
- f"{func_name}: Validated tree structures: {Path(project_dir).absolute()}"
72
- )
73
- return True
74
-
75
-
76
- # 3. Internal validation functions
77
- def _validate_01_manuscript_structure(project_dir: Path) -> bool:
78
- """Validates manuscript structure."""
79
- return _validate_tree_structure_base(
80
- project_dir, **TREE_VALIDATORS["01_manuscript"]
81
- )
82
-
83
-
84
- def _validate_02_supplementary_structure(project_dir: Path) -> bool:
85
- """Validates supplementary structure."""
86
- return _validate_tree_structure_base(
87
- project_dir, **TREE_VALIDATORS["02_supplementary"]
88
- )
89
-
90
-
91
- def _validate_03_revision_structure(project_dir: Path) -> bool:
92
- """Validates revision structure."""
93
- return _validate_tree_structure_base(project_dir, **TREE_VALIDATORS["03_revision"])
94
-
95
-
96
- def _validate_config_structure(project_dir: Path) -> bool:
97
- """Validates config structure."""
98
- return _validate_tree_structure_base(project_dir, **TREE_VALIDATORS["config"])
99
-
100
-
101
- def _validate_scripts_structure(project_dir: Path) -> bool:
102
- """Validates scripts structure."""
103
- return _validate_tree_structure_base(project_dir, **TREE_VALIDATORS["scripts"])
104
-
105
-
106
- def _validate_00_shared_structure(project_dir: Path) -> bool:
107
- """Validates shared structure."""
108
- return _validate_tree_structure_base(project_dir, **TREE_VALIDATORS["00_shared"])
109
-
110
-
111
- # 4. Helper functions
112
- def _validate_tree_structure_base(
113
- project_dir: Path, dir_name: str, tree_class: type = None
114
- ) -> bool:
115
- """Base validation function that checks directory existence and verifies structure using tree class.
116
-
117
- Args:
118
- project_dir: Root project directory
119
- dir_name: Name of directory to validate
120
- tree_class: Tree class with verify_structure method
121
-
122
- Returns
123
- -------
124
- True if structure is valid
125
-
126
- Raises
127
- ------
128
- ProjectValidationError: If directory missing or structure invalid
129
- """
130
- project_dir = Path(project_dir)
131
- target_dir = project_dir / dir_name
132
- if not target_dir.exists():
133
- raise ProjectValidationError(f"Required directory missing: {target_dir}")
134
- if tree_class is not None:
135
- doc = tree_class(target_dir, git_root=project_dir)
136
- is_valid, issues = doc.verify_structure()
137
- if not is_valid:
138
- raise ProjectValidationError(
139
- f"{dir_name} structure invalid:\n"
140
- + "\n".join(f" - {issue}" for issue in issues)
141
- )
142
- logger.debug(f"{dir_name} structure valid: {project_dir}")
143
- return True
144
-
145
-
146
- # 1. Main entry point
147
- def run_session() -> None:
148
- """Initialize scitex framework, run main function, and cleanup."""
149
- global CONFIG, CC, sys, plt, rng
150
- import sys
151
-
152
- import matplotlib.pyplot as plt
153
-
154
- import scitex as stx
155
-
156
- args = parse_args()
157
-
158
- CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
159
- sys,
160
- plt,
161
- args=args,
162
- file=__FILE__,
163
- sdir_suffix=None,
164
- verbose=False,
165
- agg=True,
166
- )
167
-
168
- exit_status = main(args)
169
-
170
- stx.session.close(
171
- CONFIG,
172
- verbose=False,
173
- notify=False,
174
- message="",
175
- exit_status=exit_status,
176
- )
177
-
178
-
179
- def main(args):
180
- project_dir = Path(args.dir) if args.dir else Path.cwd()
181
- verify_tree_structure(project_dir)
182
- return 0
183
-
184
-
185
- def parse_args() -> argparse.Namespace:
186
- parser = argparse.ArgumentParser(
187
- description="Validate scitex writer project structure"
188
- )
189
- parser.add_argument(
190
- "--dir",
191
- "-d",
192
- type=str,
193
- default=None,
194
- help="Project directory to validate (default: current directory)",
195
- )
196
- args = parser.parse_args()
197
- return args
198
-
199
-
200
- if __name__ == "__main__":
201
- run_session()
202
-
203
- # python -m scitex.writer._verify_tree_structure --dir ./my_paper
204
-
205
- # EOF
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/writer/watch.py
4
-
5
- """
6
- Watch mode for auto-recompilation.
7
-
8
- Monitors file changes and triggers automatic recompilation.
9
- """
10
-
11
- import subprocess
12
- from pathlib import Path
13
- from typing import Optional, Callable
14
-
15
- from scitex.logging import getLogger
16
-
17
- logger = getLogger(__name__)
18
-
19
-
20
- def watch_manuscript(
21
- project_dir: Path,
22
- interval: int = 2,
23
- on_compile: Optional[Callable] = None,
24
- timeout: Optional[int] = None,
25
- ) -> None:
26
- """
27
- Watch and auto-recompile manuscript on file changes.
28
-
29
- Args:
30
- project_dir: Path to writer project directory
31
- interval: Check interval in seconds
32
- on_compile: Callback function called after each compilation
33
- timeout: Optional timeout in seconds (None = infinite)
34
-
35
- Examples:
36
- >>> from pathlib import Path
37
- >>> def on_change():
38
- ... print("Recompiled!")
39
- >>> watch_manuscript(Path("/path/to/project"), on_compile=on_change)
40
- """
41
- # Get compile script from project directory
42
- compile_script = project_dir / "compile"
43
-
44
- if not compile_script.exists():
45
- logger.error(f"compile script not found: {compile_script}")
46
- return
47
-
48
- # Build watch command
49
- cmd = [str(compile_script), "-m", "-w"]
50
-
51
- logger.info(f"Starting watch mode for {project_dir}")
52
- logger.info("Press Ctrl+C to stop")
53
-
54
- try:
55
- # Run watch script
56
- process = subprocess.Popen(
57
- cmd,
58
- cwd=project_dir,
59
- stdout=subprocess.PIPE,
60
- stderr=subprocess.STDOUT,
61
- text=True,
62
- bufsize=1, # Line-buffered
63
- )
64
-
65
- # Stream output
66
- for line in iter(process.stdout.readline, ""):
67
- if line:
68
- print(line.rstrip())
69
-
70
- # Call callback on compilation events
71
- if on_compile and "Compilation" in line:
72
- try:
73
- on_compile()
74
- except Exception as e:
75
- logger.error(f"Callback error: {e}")
76
-
77
- process.wait(timeout=timeout)
78
-
79
- except KeyboardInterrupt:
80
- logger.info("\nWatch mode stopped by user")
81
- if process:
82
- process.terminate()
83
- try:
84
- process.wait(timeout=5)
85
- except subprocess.TimeoutExpired:
86
- process.kill()
87
-
88
- except Exception as e:
89
- logger.error(f"Watch mode error: {e}")
90
- if process:
91
- process.terminate()
92
-
93
-
94
- __all__ = ["watch_manuscript"]
95
-
96
- # EOF