article-cli 1.3.0__tar.gz → 1.3.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 (21) hide show
  1. {article_cli-1.3.0 → article_cli-1.3.1}/PKG-INFO +1 -1
  2. {article_cli-1.3.0 → article_cli-1.3.1}/pyproject.toml +1 -1
  3. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/__init__.py +1 -1
  4. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/fonts.py +48 -29
  5. {article_cli-1.3.0 → article_cli-1.3.1}/tests/test_fonts.py +8 -9
  6. {article_cli-1.3.0 → article_cli-1.3.1}/.gitignore +0 -0
  7. {article_cli-1.3.0 → article_cli-1.3.1}/LICENSE +0 -0
  8. {article_cli-1.3.0 → article_cli-1.3.1}/README.md +0 -0
  9. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/__main__.py +0 -0
  10. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/cli.py +0 -0
  11. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/config.py +0 -0
  12. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/git_manager.py +0 -0
  13. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/latex_compiler.py +0 -0
  14. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/repository_setup.py +0 -0
  15. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/themes.py +0 -0
  16. {article_cli-1.3.0 → article_cli-1.3.1}/src/article_cli/zotero.py +0 -0
  17. {article_cli-1.3.0 → article_cli-1.3.1}/tests/__init__.py +0 -0
  18. {article_cli-1.3.0 → article_cli-1.3.1}/tests/test_config.py +0 -0
  19. {article_cli-1.3.0 → article_cli-1.3.1}/tests/test_latex_compiler.py +0 -0
  20. {article_cli-1.3.0 → article_cli-1.3.1}/tests/test_repository_setup.py +0 -0
  21. {article_cli-1.3.0 → article_cli-1.3.1}/tests/test_themes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: article-cli
3
- Version: 1.3.0
3
+ Version: 1.3.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.3.0"
7
+ version = "1.3.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.3.0"
11
+ __version__ = "1.3.1"
12
12
  __author__ = "Christophe Prud'homme"
13
13
  __email__ = "prudhomm@cemosis.fr"
14
14
 
@@ -9,26 +9,38 @@ import zipfile
9
9
  import tempfile
10
10
  from pathlib import Path
11
11
  from typing import List, Dict, Optional, Any
12
- from urllib.request import urlopen, Request
13
- from urllib.error import URLError, HTTPError
12
+
13
+ import requests
14
14
 
15
15
  from .zotero import print_error, print_info, print_success, print_warning
16
16
 
17
17
 
18
18
  # Default font sources for common themes
19
+ # Note: Marianne font from French government requires manual download due to Cloudflare protection.
20
+ # Configure custom URL in pyproject.toml if you have a mirror or local copy.
19
21
  DEFAULT_FONT_SOURCES = [
20
22
  {
21
- "name": "Marianne",
22
- "url": "https://www.systeme-de-design.gouv.fr/uploads/Marianne_fd0ba9c190.zip",
23
- "description": "French government official font (Système de Design de l'État)",
23
+ "name": "Roboto",
24
+ "url": "https://github.com/googlefonts/roboto/releases/download/v2.138/roboto-unhinted.zip",
25
+ "description": "Google's sans-serif font family",
24
26
  },
25
27
  {
26
28
  "name": "Roboto Mono",
27
- "url": "https://fonts.google.com/download?family=Roboto+Mono",
29
+ "url": "https://github.com/googlefonts/RobotoMono/archive/refs/heads/main.zip",
28
30
  "description": "Google's monospace font, good for code",
29
31
  },
30
32
  ]
31
33
 
34
+ # Additional font sources that may require special handling
35
+ OPTIONAL_FONT_SOURCES = {
36
+ "Marianne": {
37
+ "name": "Marianne",
38
+ "url": "https://www.info.gouv.fr/upload/media/content/0001/14/e9dd2398914853b3c21c402245866bc74cd3d3c5.zip",
39
+ "description": "French government official font - TTF version (Système de Design de l'État)",
40
+ "note": "May require manual download due to Cloudflare protection",
41
+ },
42
+ }
43
+
32
44
 
33
45
  class FontInstaller:
34
46
  """Handles downloading and installing fonts for LaTeX projects"""
@@ -163,25 +175,30 @@ class FontInstaller:
163
175
  url: URL to download
164
176
  dest: Destination path
165
177
  """
166
- # Create request with user agent to avoid blocks
167
- headers = {"User-Agent": "Mozilla/5.0 (compatible; article-cli font installer)"}
168
- request = Request(url, headers=headers)
178
+ # Use requests library for better redirect and header handling
179
+ headers = {
180
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
181
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
182
+ "Accept-Language": "en-US,en;q=0.5",
183
+ }
169
184
 
170
185
  try:
171
- with urlopen(request, timeout=60) as response:
172
- # Get file size if available
173
- content_length = response.headers.get("Content-Length")
174
- total_size = int(content_length) if content_length else None
175
-
176
- # Download in chunks
177
- chunk_size = 8192
178
- downloaded = 0
179
-
180
- with open(dest, "wb") as f:
181
- while True:
182
- chunk = response.read(chunk_size)
183
- if not chunk:
184
- break
186
+ response = requests.get(
187
+ url, headers=headers, allow_redirects=True, stream=True, timeout=60
188
+ )
189
+ response.raise_for_status()
190
+
191
+ # Get file size if available
192
+ content_length = response.headers.get("Content-Length")
193
+ total_size = int(content_length) if content_length else None
194
+
195
+ # Download in chunks
196
+ chunk_size = 8192
197
+ downloaded = 0
198
+
199
+ with open(dest, "wb") as f:
200
+ for chunk in response.iter_content(chunk_size=chunk_size):
201
+ if chunk:
185
202
  f.write(chunk)
186
203
  downloaded += len(chunk)
187
204
 
@@ -200,14 +217,16 @@ class FontInstaller:
200
217
  flush=True,
201
218
  )
202
219
 
203
- print() # New line after progress
220
+ print() # New line after progress
204
221
 
205
- except HTTPError as e:
206
- raise RuntimeError(f"HTTP error {e.code}: {e.reason}")
207
- except URLError as e:
208
- raise RuntimeError(f"URL error: {e.reason}")
209
- except TimeoutError:
222
+ except requests.exceptions.HTTPError as e:
223
+ raise RuntimeError(f"HTTP error: {e}")
224
+ except requests.exceptions.ConnectionError as e:
225
+ raise RuntimeError(f"Connection error: {e}")
226
+ except requests.exceptions.Timeout:
210
227
  raise RuntimeError("Download timed out")
228
+ except requests.exceptions.RequestException as e:
229
+ raise RuntimeError(f"Download failed: {e}")
211
230
 
212
231
  def _extract_zip(self, zip_path: Path, target_dir: Path) -> None:
213
232
  """
@@ -107,16 +107,15 @@ class TestFontInstaller:
107
107
  assert len(files) == 1
108
108
  assert files[0].name == "font1.ttf"
109
109
 
110
- @patch("article_cli.fonts.urlopen")
111
- def test_download_file_success(self, mock_urlopen, tmp_path):
110
+ @patch("article_cli.fonts.requests.get")
111
+ def test_download_file_success(self, mock_get, tmp_path):
112
112
  """Test successful file download"""
113
113
  # Create mock response
114
114
  mock_response = MagicMock()
115
115
  mock_response.headers.get.return_value = "1000"
116
- mock_response.read.side_effect = [b"test content", b""]
117
- mock_response.__enter__ = MagicMock(return_value=mock_response)
118
- mock_response.__exit__ = MagicMock(return_value=False)
119
- mock_urlopen.return_value = mock_response
116
+ mock_response.iter_content.return_value = [b"test content"]
117
+ mock_response.raise_for_status = MagicMock()
118
+ mock_get.return_value = mock_response
120
119
 
121
120
  installer = FontInstaller(fonts_dir=tmp_path / "fonts")
122
121
  dest = tmp_path / "test.zip"
@@ -228,10 +227,10 @@ class TestDefaultFontSources:
228
227
  assert "url" in source
229
228
  assert source["url"].startswith("http")
230
229
 
231
- def test_default_sources_include_marianne(self):
232
- """Test that Marianne font is included"""
230
+ def test_default_sources_include_roboto(self):
231
+ """Test that Roboto font is included"""
233
232
  names = [s["name"] for s in DEFAULT_FONT_SOURCES]
234
- assert "Marianne" in names
233
+ assert "Roboto" in names
235
234
 
236
235
  def test_default_sources_include_roboto_mono(self):
237
236
  """Test that Roboto Mono font is included"""
File without changes
File without changes
File without changes