article-cli 1.1.0__tar.gz → 1.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: article-cli
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: CLI tool for managing LaTeX articles with git integration and Zotero bibliography
5
5
  Project-URL: Homepage, https://github.com/feelpp/article.cli
6
6
  Project-URL: Documentation, https://github.com/feelpp/article.cli#readme
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "article-cli"
7
- version = "1.1.0"
7
+ version = "1.1.1"
8
8
  authors = [
9
9
  {name = "Christophe Prud'homme", email = "prudhomm@cemosis.fr"},
10
10
  ]
@@ -8,7 +8,7 @@ This package provides tools for:
8
8
  - Git hooks setup
9
9
  """
10
10
 
11
- __version__ = "1.1.0"
11
+ __version__ = "1.1.1"
12
12
  __author__ = "Christophe Prud'homme"
13
13
  __email__ = "prudhomm@cemosis.fr"
14
14
 
@@ -19,4 +19,11 @@ from .git_manager import GitManager
19
19
  from .repository_setup import RepositorySetup
20
20
  from .latex_compiler import LaTeXCompiler
21
21
 
22
- __all__ = ["main", "Config", "ZoteroBibTexUpdater", "GitManager", "RepositorySetup", "LaTeXCompiler"]
22
+ __all__ = [
23
+ "main",
24
+ "Config",
25
+ "ZoteroBibTexUpdater",
26
+ "GitManager",
27
+ "RepositorySetup",
28
+ "LaTeXCompiler",
29
+ ]
@@ -56,9 +56,7 @@ Environment variables:
56
56
  init_parser = subparsers.add_parser(
57
57
  "init", help="Initialize repository with workflows and configuration"
58
58
  )
59
- init_parser.add_argument(
60
- "--title", required=True, help="Article title"
61
- )
59
+ init_parser.add_argument("--title", required=True, help="Article title")
62
60
  init_parser.add_argument(
63
61
  "--authors",
64
62
  required=True,
@@ -86,41 +84,40 @@ Environment variables:
86
84
  subparsers.add_parser("clean", help="Clean LaTeX build files")
87
85
 
88
86
  # Compile command
89
- compile_parser = subparsers.add_parser("compile", help="Compile LaTeX document using latexmk")
87
+ compile_parser = subparsers.add_parser(
88
+ "compile", help="Compile LaTeX document using latexmk"
89
+ )
90
90
  compile_parser.add_argument(
91
- "tex_file",
92
- nargs="?",
93
- help="LaTeX file to compile (auto-detected if not specified)"
91
+ "tex_file",
92
+ nargs="?",
93
+ help="LaTeX file to compile (auto-detected if not specified)",
94
94
  )
95
95
  compile_parser.add_argument(
96
- "--engine",
97
- choices=["latexmk", "pdflatex"],
96
+ "--engine",
97
+ choices=["latexmk", "pdflatex"],
98
98
  default="latexmk",
99
- help="LaTeX engine to use (default: latexmk)"
99
+ help="LaTeX engine to use (default: latexmk)",
100
100
  )
101
101
  compile_parser.add_argument(
102
- "--shell-escape",
103
- action="store_true",
104
- help="Enable shell escape (for code highlighting, etc.)"
102
+ "--shell-escape",
103
+ action="store_true",
104
+ help="Enable shell escape (for code highlighting, etc.)",
105
105
  )
106
106
  compile_parser.add_argument(
107
- "--clean-first",
108
- action="store_true",
109
- help="Clean build files before compilation"
107
+ "--clean-first",
108
+ action="store_true",
109
+ help="Clean build files before compilation",
110
110
  )
111
111
  compile_parser.add_argument(
112
- "--clean-after",
113
- action="store_true",
114
- help="Clean build files after compilation"
112
+ "--clean-after", action="store_true", help="Clean build files after compilation"
115
113
  )
116
114
  compile_parser.add_argument(
117
- "--watch",
118
- action="store_true",
119
- help="Watch for changes and recompile automatically"
115
+ "--watch",
116
+ action="store_true",
117
+ help="Watch for changes and recompile automatically",
120
118
  )
121
119
  compile_parser.add_argument(
122
- "--output-dir",
123
- help="Output directory for compiled files"
120
+ "--output-dir", help="Output directory for compiled files"
124
121
  )
125
122
 
126
123
  # Create command
@@ -223,48 +220,50 @@ def handle_compile_command(args: argparse.Namespace, config: Config) -> int:
223
220
  """Handle the compile command"""
224
221
  try:
225
222
  from .latex_compiler import LaTeXCompiler
226
-
223
+
227
224
  # Auto-detect tex file if not provided
228
225
  tex_file = args.tex_file
229
226
  if not tex_file:
230
227
  tex_file = _auto_detect_tex_file()
231
228
  if not tex_file:
232
- print_error("No .tex file specified and none found in current directory")
229
+ print_error(
230
+ "No .tex file specified and none found in current directory"
231
+ )
233
232
  return 1
234
-
233
+
235
234
  # Validate tex file exists
236
235
  tex_path = Path(tex_file)
237
236
  if not tex_path.exists():
238
237
  print_error(f"LaTeX file not found: {tex_file}")
239
238
  return 1
240
-
239
+
241
240
  compiler = LaTeXCompiler(config)
242
-
241
+
243
242
  # Clean before compilation if requested
244
243
  if args.clean_first:
245
244
  print_info("Cleaning build files before compilation...")
246
245
  git_manager = GitManager()
247
246
  latex_config = config.get_latex_config()
248
247
  git_manager.clean_latex_files(latex_config["clean_extensions"])
249
-
248
+
250
249
  # Compile the document
251
250
  success = compiler.compile(
252
251
  tex_file=tex_file,
253
252
  engine=args.engine,
254
253
  shell_escape=args.shell_escape,
255
254
  output_dir=args.output_dir,
256
- watch=args.watch
255
+ watch=args.watch,
257
256
  )
258
-
257
+
259
258
  # Clean after compilation if requested
260
259
  if args.clean_after and success:
261
260
  print_info("Cleaning build files after compilation...")
262
261
  git_manager = GitManager()
263
262
  latex_config = config.get_latex_config()
264
263
  git_manager.clean_latex_files(latex_config["clean_extensions"])
265
-
264
+
266
265
  return 0 if success else 1
267
-
266
+
268
267
  except Exception as e:
269
268
  print_error(f"Compilation failed: {e}")
270
269
  return 1
@@ -274,18 +273,18 @@ def _auto_detect_tex_file() -> Optional[str]:
274
273
  """Auto-detect main .tex file in current directory"""
275
274
  current_dir = Path.cwd()
276
275
  tex_files = list(current_dir.glob("*.tex"))
277
-
276
+
278
277
  if not tex_files:
279
278
  return None
280
-
279
+
281
280
  if len(tex_files) == 1:
282
281
  return tex_files[0].name
283
-
282
+
284
283
  # Multiple .tex files - prefer common patterns
285
284
  for pattern in ["main.tex", "article.tex", f"{current_dir.name}.tex"]:
286
285
  if (current_dir / pattern).exists():
287
286
  return pattern
288
-
287
+
289
288
  # Return first .tex file found
290
289
  print_info(f"Multiple .tex files found, using: {tex_files[0].name}")
291
290
  return tex_files[0].name
@@ -5,9 +5,7 @@ Provides compilation functionality that mimics LaTeX Workshop configuration
5
5
  with support for latexmk and pdflatex engines.
6
6
  """
7
7
 
8
- import os
9
8
  import subprocess
10
- import sys
11
9
  import time
12
10
  from pathlib import Path
13
11
  from typing import Dict, List, Optional
@@ -103,6 +101,8 @@ class LaTeXCompiler:
103
101
  # Stream output in real time
104
102
  try:
105
103
  while True:
104
+ if process.stdout is None:
105
+ break
106
106
  output = process.stdout.readline()
107
107
  if output == "" and process.poll() is not None:
108
108
  break
@@ -178,7 +178,7 @@ class LaTeXCompiler:
178
178
  try:
179
179
  # Run multiple passes for cross-references, bibliography, etc.
180
180
  passes = ["First pass", "Second pass", "Third pass"]
181
-
181
+
182
182
  for i, pass_name in enumerate(passes):
183
183
  print_info(f"{pass_name}...")
184
184
  result = subprocess.run(
@@ -237,12 +237,14 @@ class LaTeXCompiler:
237
237
  # Core options (from LaTeX Workshop)
238
238
  if shell_escape:
239
239
  cmd.append("--shell-escape")
240
-
241
- cmd.extend([
242
- "-pdf",
243
- "-interaction=nonstopmode",
244
- "-synctex=1",
245
- ])
240
+
241
+ cmd.extend(
242
+ [
243
+ "-pdf",
244
+ "-interaction=nonstopmode",
245
+ "-synctex=1",
246
+ ]
247
+ )
246
248
 
247
249
  if output_dir:
248
250
  cmd.extend(["-outdir", output_dir])
@@ -264,11 +266,13 @@ class LaTeXCompiler:
264
266
  if shell_escape:
265
267
  cmd.append("--shell-escape")
266
268
 
267
- cmd.extend([
268
- "-synctex=1",
269
- "-interaction=nonstopmode",
270
- "-file-line-error",
271
- ])
269
+ cmd.extend(
270
+ [
271
+ "-synctex=1",
272
+ "-interaction=nonstopmode",
273
+ "-file-line-error",
274
+ ]
275
+ )
272
276
 
273
277
  if output_dir:
274
278
  cmd.extend(["-output-directory", output_dir])
@@ -282,7 +286,10 @@ class LaTeXCompiler:
282
286
  base_name = tex_path.stem
283
287
 
284
288
  # Check if bibliography is needed
285
- if "citation undefined" in latex_output.lower() or "rerun" in latex_output.lower():
289
+ if (
290
+ "citation undefined" in latex_output.lower()
291
+ or "rerun" in latex_output.lower()
292
+ ):
286
293
  # Check for .aux file to determine if we need bibtex
287
294
  aux_file = tex_path.with_suffix(".aux")
288
295
  if aux_file.exists():
@@ -306,12 +313,12 @@ class LaTeXCompiler:
306
313
  size = pdf_path.stat().st_size
307
314
  size_mb = size / (1024 * 1024)
308
315
  print_info(f"PDF size: {size_mb:.2f} MB")
309
-
316
+
310
317
  # Show modification time
311
318
  mtime = pdf_path.stat().st_mtime
312
319
  mtime_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime))
313
320
  print_info(f"Generated: {mtime_str}")
314
-
321
+
315
322
  except Exception:
316
323
  pass # Silently ignore errors getting file info
317
324
 
@@ -346,14 +353,16 @@ class LaTeXCompiler:
346
353
  def print_dependency_status(self) -> None:
347
354
  """Print status of LaTeX dependencies"""
348
355
  print_info("Checking LaTeX dependencies...")
349
-
356
+
350
357
  deps = self.check_dependencies()
351
-
358
+
352
359
  for tool, available in deps.items():
353
360
  status = "✅ Available" if available else "❌ Not found"
354
361
  print(f" {tool}: {status}")
355
-
362
+
356
363
  if not all(deps.values()):
357
- print_info("\nSome LaTeX tools are missing. Install a LaTeX distribution like TeX Live.")
364
+ print_info(
365
+ "\nSome LaTeX tools are missing. Install a LaTeX distribution like TeX Live."
366
+ )
358
367
  print_info("On macOS: brew install --cask mactex")
359
- print_info("On Ubuntu: sudo apt-get install texlive-full")
368
+ print_info("On Ubuntu: sudo apt-get install texlive-full")
@@ -8,10 +8,8 @@ Provides functionality to initialize LaTeX article repositories with:
8
8
  - Git hooks and configuration
9
9
  """
10
10
 
11
- import os
12
- import shutil
13
11
  from pathlib import Path
14
- from typing import Dict, List, Optional
12
+ from typing import List, Optional
15
13
 
16
14
  from .zotero import print_error, print_info, print_success
17
15
 
@@ -163,9 +161,7 @@ class RepositorySetup:
163
161
  directory.mkdir(parents=True, exist_ok=True)
164
162
  print_info(f"Created directory: {directory.relative_to(self.repo_path)}")
165
163
 
166
- def _create_workflow(
167
- self, project_name: str, tex_file: str, force: bool
168
- ) -> bool:
164
+ def _create_workflow(self, project_name: str, tex_file: str, force: bool) -> bool:
169
165
  """
170
166
  Create GitHub Actions workflow file
171
167
 
@@ -180,7 +176,9 @@ class RepositorySetup:
180
176
  workflow_path = self.repo_path / ".github" / "workflows" / "latex.yml"
181
177
 
182
178
  if workflow_path.exists() and not force:
183
- print_info(f"Workflow already exists: {workflow_path.name} (use --force to overwrite)")
179
+ print_info(
180
+ f"Workflow already exists: {workflow_path.name} (use --force to overwrite)"
181
+ )
184
182
  return True
185
183
 
186
184
  # Extract base name (without .tex extension)
@@ -203,7 +201,7 @@ class RepositorySetup:
203
201
  Returns:
204
202
  Workflow YAML content
205
203
  """
206
- return f'''name: Compile LaTeX and Release PDF
204
+ return f"""name: Compile LaTeX and Release PDF
207
205
 
208
206
  # This workflow uses article-cli for:
209
207
  # - Git hooks setup (article-cli setup)
@@ -639,7 +637,7 @@ jobs:
639
637
  echo "" >> $GITHUB_STEP_SUMMARY
640
638
  echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY
641
639
  fi
642
- '''
640
+ """
643
641
 
644
642
  def _create_pyproject(
645
643
  self,
@@ -665,15 +663,13 @@ jobs:
665
663
  pyproject_path = self.repo_path / "pyproject.toml"
666
664
 
667
665
  if pyproject_path.exists() and not force:
668
- print_info(
669
- f"pyproject.toml already exists (use --force to overwrite)"
670
- )
666
+ print_info(f"pyproject.toml already exists (use --force to overwrite)")
671
667
  return True
672
668
 
673
669
  # Format authors for TOML
674
670
  authors_toml = ",\n ".join([f'{{name = "{author}"}}' for author in authors])
675
671
 
676
- pyproject_content = f'''# Article Repository Dependency Management
672
+ pyproject_content = f"""# Article Repository Dependency Management
677
673
  # This file manages dependencies for the LaTeX article project
678
674
 
679
675
  [project]
@@ -709,7 +705,7 @@ clean_extensions = [
709
705
  ".fls", ".synctex.gz", ".toc", ".fdb_latexmk",
710
706
  ".idx", ".ilg", ".ind", ".lof", ".lot"
711
707
  ]
712
- '''
708
+ """
713
709
 
714
710
  pyproject_path.write_text(pyproject_content)
715
711
  print_success(f"Created: {pyproject_path.relative_to(self.repo_path)}")
@@ -744,7 +740,7 @@ clean_extensions = [
744
740
 
745
741
  authors_list = "\n".join([f"- {author}" for author in authors])
746
742
 
747
- readme_content = f'''# {title}
743
+ readme_content = f"""# {title}
748
744
 
749
745
  ## Authors
750
746
 
@@ -867,7 +863,7 @@ article-cli config show
867
863
  url = {{https://github.com/feelpp/{project_name}}}
868
864
  }}
869
865
  ```
870
- '''
866
+ """
871
867
 
872
868
  readme_path.write_text(readme_content)
873
869
  print_success(f"Created: {readme_path.relative_to(self.repo_path)}")
@@ -958,10 +954,12 @@ references.bib.backup
958
954
  vscode_settings_path = self.repo_path / ".vscode" / "settings.json"
959
955
 
960
956
  if vscode_settings_path.exists() and not force:
961
- print_info(".vscode/settings.json already exists (use --force to overwrite)")
957
+ print_info(
958
+ ".vscode/settings.json already exists (use --force to overwrite)"
959
+ )
962
960
  return True
963
961
 
964
- settings_content = '''{
962
+ settings_content = """{
965
963
  "latex-workshop.latex.recipes": [
966
964
  {
967
965
  "name": "latexmk-pdf",
@@ -1023,7 +1021,7 @@ references.bib.backup
1023
1021
  "ltex.enabled": true,
1024
1022
  "ltex.language": "en-US"
1025
1023
  }
1026
- '''
1024
+ """
1027
1025
 
1028
1026
  vscode_settings_path.write_text(settings_content)
1029
1027
  print_success(f"Created: {vscode_settings_path.relative_to(self.repo_path)}")
@@ -1059,7 +1057,9 @@ parametrical
1059
1057
  print_info(f"Created: {dictionary_path.relative_to(self.repo_path)}")
1060
1058
 
1061
1059
  # Hidden false positives file (empty initially)
1062
- false_positives_path = self.repo_path / ".vscode" / "ltex.hiddenFalsePositives.en-US.txt"
1060
+ false_positives_path = (
1061
+ self.repo_path / ".vscode" / "ltex.hiddenFalsePositives.en-US.txt"
1062
+ )
1063
1063
  if not false_positives_path.exists() or force:
1064
1064
  false_positives_path.write_text("")
1065
1065
  print_info(f"Created: {false_positives_path.relative_to(self.repo_path)}")
@@ -1083,7 +1083,7 @@ parametrical
1083
1083
  return True
1084
1084
 
1085
1085
  # gitinfo2 post-commit hook
1086
- post_commit_content = '''#!/bin/sh
1086
+ post_commit_content = """#!/bin/sh
1087
1087
  # Copyright 2015 Brent Longborough
1088
1088
  # Part of gitinfo2 package Version 2
1089
1089
  # Release 2.0.7 2015-11-22
@@ -1114,15 +1114,18 @@ git --no-pager log -1 --date=short --decorate=short \\
1114
1114
  firsttagdescribe={$FIRSTTAG},
1115
1115
  reltag={$RELTAG}
1116
1116
  ]{gitexinfo}" HEAD > .git/gitHeadInfo.gin
1117
- '''
1117
+ """
1118
1118
 
1119
1119
  post_commit_path.write_text(post_commit_content)
1120
-
1120
+
1121
1121
  # Make the hook executable
1122
1122
  import stat
1123
- post_commit_path.chmod(post_commit_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
1124
-
1123
+
1124
+ post_commit_path.chmod(
1125
+ post_commit_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1126
+ )
1127
+
1125
1128
  print_success(f"Created: {post_commit_path.relative_to(self.repo_path)}")
1126
1129
  print_info("Made post-commit hook executable")
1127
-
1130
+
1128
1131
  return True
File without changes
File without changes
File without changes