barunsaha commited on
Commit
51a1ad7
·
unverified ·
2 Parent(s): 9ba5451 ee7e58b

Merge pull request #172 from barun-saha/maintenance

Browse files

Skip image search when Pexels API key is unavailable and prepare for new release

pyproject.toml CHANGED
@@ -7,7 +7,7 @@ name = "slidedeckai"
7
  authors = [
8
  { name="Barun Saha", email="[email protected]" }
9
  ]
10
- description = "A Python package to generate slide decks using AI."
11
  readme = "README.md"
12
  requires-python = ">=3.10"
13
  classifiers = [
 
7
  authors = [
8
  { name="Barun Saha", email="[email protected]" }
9
  ]
10
+ description = "Co-create PowerPoint slide decks with AI"
11
  readme = "README.md"
12
  requires-python = ">=3.10"
13
  classifiers = [
src/slidedeckai/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '8.0.2'
 
1
+ __version__ = '8.0.3'
src/slidedeckai/helpers/image_search.py CHANGED
@@ -4,6 +4,7 @@ Search photos using Pexels API.
4
  import logging
5
  import os
6
  import random
 
7
  from io import BytesIO
8
  from typing import Union, Literal
9
  from urllib.parse import urlparse, parse_qs
@@ -15,6 +16,19 @@ from dotenv import load_dotenv
15
  load_dotenv()
16
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  REQUEST_TIMEOUT = 12
19
  MAX_PHOTOS = 3
20
 
@@ -50,23 +64,27 @@ def search_pexels(
50
  per_page: No. of results to be displayed per page.
51
 
52
  Returns:
53
- The JSON response from the Pexels API containing search results.
 
54
 
55
  Raises:
56
  requests.exceptions.RequestException: If the request to the Pexels API fails.
57
  """
58
- url = 'https://api.pexels.com/v1/search'
59
- headers = {
60
- 'Authorization': os.getenv('PEXEL_API_KEY'),
61
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
62
- }
63
  params = {
64
  'query': query,
65
  'size': size,
66
  'page': 1,
67
  'per_page': per_page
68
  }
69
- response = requests.get(url, headers=headers, params=params, timeout=REQUEST_TIMEOUT)
 
 
 
 
 
70
  response.raise_for_status() # Ensure the request was successful
71
 
72
  return response.json()
@@ -83,8 +101,12 @@ def get_photo_url_from_api_response(
83
  json_response: The JSON response.
84
 
85
  Returns:
86
- The selected photo URL and page URL or `None`.
 
87
  """
 
 
 
88
  page_url = None
89
  photo_url = None
90
 
@@ -123,11 +145,7 @@ def get_image_from_url(url: str) -> BytesIO:
123
  Raises:
124
  requests.exceptions.RequestException: If the request to the URL fails.
125
  """
126
- headers = {
127
- 'Authorization': os.getenv('PEXEL_API_KEY'),
128
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
129
- }
130
- response = requests.get(url, headers=headers, stream=True, timeout=REQUEST_TIMEOUT)
131
  response.raise_for_status()
132
  image_data = BytesIO(response.content)
133
 
 
4
  import logging
5
  import os
6
  import random
7
+ import warnings
8
  from io import BytesIO
9
  from typing import Union, Literal
10
  from urllib.parse import urlparse, parse_qs
 
16
  load_dotenv()
17
 
18
 
19
+ # If PEXEL_API_KEY env var is unavailable, issue a one-time warning
20
+ if not os.getenv('PEXEL_API_KEY'):
21
+ warnings.warn(
22
+ 'PEXEL_API_KEY environment variable is not set. '
23
+ 'Image search functionality will not work without it.',
24
+ stacklevel=2
25
+ )
26
+
27
+ PEXELS_URL = 'https://api.pexels.com/v1/search'
28
+ REQUEST_HEADER = {
29
+ 'Authorization': os.getenv('PEXEL_API_KEY'),
30
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
31
+ }
32
  REQUEST_TIMEOUT = 12
33
  MAX_PHOTOS = 3
34
 
 
64
  per_page: No. of results to be displayed per page.
65
 
66
  Returns:
67
+ The JSON response from the Pexels API containing search results. Empty dict if API key
68
+ is not set.
69
 
70
  Raises:
71
  requests.exceptions.RequestException: If the request to the Pexels API fails.
72
  """
73
+ if not os.getenv('PEXEL_API_KEY'):
74
+ return {}
75
+
 
 
76
  params = {
77
  'query': query,
78
  'size': size,
79
  'page': 1,
80
  'per_page': per_page
81
  }
82
+ response = requests.get(
83
+ PEXELS_URL,
84
+ headers=REQUEST_HEADER,
85
+ params=params,
86
+ timeout=REQUEST_TIMEOUT
87
+ )
88
  response.raise_for_status() # Ensure the request was successful
89
 
90
  return response.json()
 
101
  json_response: The JSON response.
102
 
103
  Returns:
104
+ The selected photo URL and page URL or `None`. Empty tuple if no photos found or API key
105
+ is not set.
106
  """
107
+ if not os.getenv('PEXEL_API_KEY'):
108
+ return None, None
109
+
110
  page_url = None
111
  photo_url = None
112
 
 
145
  Raises:
146
  requests.exceptions.RequestException: If the request to the URL fails.
147
  """
148
+ response = requests.get(url, headers=REQUEST_HEADER, stream=True, timeout=REQUEST_TIMEOUT)
 
 
 
 
149
  response.raise_for_status()
150
  image_data = BytesIO(response.content)
151
 
tests/unit/test_image_search.py CHANGED
@@ -42,7 +42,6 @@ def _dummy_requests_get_success_search(
42
  timeout: int
43
  ):
44
  """Return a successful mock response for search_pexels."""
45
-
46
  # Validate that the function under test passes expected args
47
  assert 'Authorization' in headers
48
  assert 'User-Agent' in headers
@@ -72,7 +71,6 @@ def _dummy_requests_get_image(
72
  stream: bool, timeout: int
73
  ):
74
  """Return a mock image response for get_image_from_url."""
75
-
76
  assert stream is True
77
  assert 'Authorization' in headers
78
  data = b'\x89PNG\r\n\x1a\n...'
@@ -114,6 +112,10 @@ def test_get_photo_url_from_api_response_selects_large_and_original(monkeypatch)
114
  {'url': 'https://pexels.com/photo/3', 'src': {'large': 'https://images/3_large.jpg'}},
115
  ]
116
 
 
 
 
 
117
  # Force selection of index 1 (second photo) which only has 'original'
118
  monkeypatch.setattr(image_search.random, 'choice', lambda seq: 1)
119
 
@@ -169,3 +171,22 @@ def test_search_pexels_raises_on_request_error(monkeypatch) -> None:
169
 
170
  with pytest.raises(RuntimeError):
171
  image_search.search_pexels(query='x')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  timeout: int
43
  ):
44
  """Return a successful mock response for search_pexels."""
 
45
  # Validate that the function under test passes expected args
46
  assert 'Authorization' in headers
47
  assert 'User-Agent' in headers
 
71
  stream: bool, timeout: int
72
  ):
73
  """Return a mock image response for get_image_from_url."""
 
74
  assert stream is True
75
  assert 'Authorization' in headers
76
  data = b'\x89PNG\r\n\x1a\n...'
 
112
  {'url': 'https://pexels.com/photo/3', 'src': {'large': 'https://images/3_large.jpg'}},
113
  ]
114
 
115
+ # Ensure the Pexels API key is present so the helper will attempt to select
116
+ # and return photo URLs rather than early-returning (None, None).
117
+ monkeypatch.setenv('PEXEL_API_KEY', 'akey')
118
+
119
  # Force selection of index 1 (second photo) which only has 'original'
120
  monkeypatch.setattr(image_search.random, 'choice', lambda seq: 1)
121
 
 
171
 
172
  with pytest.raises(RuntimeError):
173
  image_search.search_pexels(query='x')
174
+
175
+
176
+ def test_search_pexels_returns_empty_when_no_api_key(monkeypatch) -> None:
177
+ """When PEXEL_API_KEY is not set, search_pexels should return an empty dict."""
178
+ monkeypatch.delenv('PEXEL_API_KEY', raising=False)
179
+ result = image_search.search_pexels(query='people')
180
+
181
+ assert result == {}
182
+
183
+
184
+ def test_get_photo_url_from_api_response_returns_none_when_no_api_key(monkeypatch) -> None:
185
+ """When PEXEL_API_KEY is not set, get_photo_url_from_api_response should return (None, None)."""
186
+ photos = [
187
+ {'url': 'https://pexels.com/photo/1', 'src': {'large': 'https://images/1_large.jpg'}}
188
+ ]
189
+ monkeypatch.delenv('PEXEL_API_KEY', raising=False)
190
+ result = image_search.get_photo_url_from_api_response({'photos': photos})
191
+
192
+ assert result == (None, None)