pdfdancer-client-python 0.2.2__py3-none-any.whl → 0.2.4__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.
pdfdancer/pdfdancer_v1.py CHANGED
@@ -6,6 +6,7 @@ Provides session-based PDF manipulation operations with strict validation.
6
6
  """
7
7
 
8
8
  import json
9
+ import os
9
10
  from pathlib import Path
10
11
  from typing import List, Optional, Union, BinaryIO
11
12
 
@@ -116,11 +117,39 @@ class PDFDancer:
116
117
  @classmethod
117
118
  def open(cls,
118
119
  pdf_data: Union[bytes, Path, str, BinaryIO],
119
- token: str,
120
- base_url: str = "http://localhost:8080",
120
+ token: Optional[str] = None,
121
+ base_url: Optional[str] = None,
121
122
  timeout: float = 30.0) -> "PDFDancer":
123
+ """
124
+ Create a client session, falling back to environment variables when needed.
125
+
126
+ Args:
127
+ pdf_data: PDF payload supplied directly or via filesystem handles.
128
+ token: Override for the API token; falls back to `PDFDANCER_TOKEN` environement variable.
129
+ base_url: Override for the API base URL; falls back to `PDFDANCER_BASE_URL`
130
+ or defaults to `https://api.pdfdancer.com`.
131
+ timeout: HTTP read timeout in seconds.
132
+
133
+ Returns:
134
+ A ready-to-use `PDFDancer` client instance.
135
+ """
136
+ resolved_token = token.strip() if token and token.strip() else None
137
+ if resolved_token is None:
138
+ env_token = os.getenv("PDFDANCER_TOKEN")
139
+ resolved_token = env_token.strip() if env_token and env_token.strip() else None
140
+
141
+ if resolved_token is None:
142
+ raise ValidationException(
143
+ "Missing PDFDancer API token. Pass a token via the `token` argument "
144
+ "or set the PDFDANCER_TOKEN environment variable."
145
+ )
146
+
147
+ env_base_url = os.getenv("PDFDANCER_BASE_URL")
148
+ resolved_base_url = base_url or (env_base_url.strip() if env_base_url and env_base_url.strip() else None)
149
+ if resolved_base_url is None:
150
+ resolved_base_url = "https://api.pdfdancer.com"
122
151
 
123
- return PDFDancer(token, pdf_data, base_url, timeout)
152
+ return PDFDancer(resolved_token, pdf_data, resolved_base_url, timeout)
124
153
 
125
154
  def __init__(self, token: str, pdf_data: Union[bytes, Path, str, BinaryIO],
126
155
  base_url: str, read_timeout: float = 0):
@@ -236,6 +265,22 @@ class PDFDancer:
236
265
  # If JSON parsing fails, return response content or status
237
266
  return response.text or f"HTTP {response.status_code}"
238
267
 
268
+ def _handle_authentication_error(self, response: Optional[requests.Response]) -> None:
269
+ """
270
+ Translate authentication failures into a clear, actionable validation error.
271
+ """
272
+ if response is None:
273
+ return
274
+
275
+ if response.status_code in (401, 403):
276
+ details = self._extract_error_message(response)
277
+ raise ValidationException(
278
+ "Authentication with the PDFDancer API failed. "
279
+ "Confirm that your API token is valid, has not expired, and is supplied via "
280
+ "the `token` argument or the PDFDANCER_TOKEN environment variable. "
281
+ f"Server response: {details}"
282
+ )
283
+
239
284
  def _create_session(self) -> str:
240
285
  """
241
286
  Creates a new PDF processing session by uploading the PDF data.
@@ -251,6 +296,7 @@ class PDFDancer:
251
296
  timeout=self._read_timeout if self._read_timeout > 0 else None
252
297
  )
253
298
 
299
+ self._handle_authentication_error(response)
254
300
  response.raise_for_status()
255
301
  session_id = response.text.strip()
256
302
 
@@ -260,6 +306,7 @@ class PDFDancer:
260
306
  return session_id
261
307
 
262
308
  except requests.exceptions.RequestException as e:
309
+ self._handle_authentication_error(getattr(e, 'response', None))
263
310
  error_message = self._extract_error_message(getattr(e, 'response', None))
264
311
  raise HttpClientException(f"Failed to create session: {error_message}",
265
312
  response=getattr(e, 'response', None), cause=e) from None
@@ -293,10 +340,12 @@ class PDFDancer:
293
340
  except (json.JSONDecodeError, KeyError):
294
341
  pass
295
342
 
343
+ self._handle_authentication_error(response)
296
344
  response.raise_for_status()
297
345
  return response
298
346
 
299
347
  except requests.exceptions.RequestException as e:
348
+ self._handle_authentication_error(getattr(e, 'response', None))
300
349
  error_message = self._extract_error_message(getattr(e, 'response', None))
301
350
  raise HttpClientException(f"API request failed: {error_message}", response=getattr(e, 'response', None),
302
351
  cause=e) from None
@@ -811,7 +860,7 @@ class PDFDancer:
811
860
 
812
861
  # Builder Pattern Support
813
862
 
814
- def paragraph_builder(self) -> 'ParagraphBuilder':
863
+ def _paragraph_builder(self) -> 'ParagraphBuilder':
815
864
  """
816
865
  Creates a new ParagraphBuilder for fluent paragraph construction.
817
866
  Returns:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Python client for PDFDancer API
5
5
  Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License: MIT
@@ -63,7 +63,7 @@ client._delete(paragraphs[0])
63
63
  client._move(images[0], Position.at_page_coordinates(0, 100, 200))
64
64
 
65
65
  # Builder pattern (mirrors Java ParagraphBuilder)
66
- paragraph = (client.paragraph_builder()
66
+ paragraph = (client._paragraph_builder()
67
67
  .from_string("Hello World")
68
68
  .with_font(Font("Arial", 12))
69
69
  .with_color(Color(255, 0, 0))
@@ -141,25 +141,26 @@ result = client.modify_text_line(ref, "new text")
141
141
  ```
142
142
 
143
143
  ### Builder Pattern
144
+
144
145
  ```python
145
146
  # Java: client.paragraphBuilder()
146
- builder = client.paragraph_builder()
147
+ builder = client._paragraph_builder()
147
148
 
148
149
  # Fluent interface (mirrors Java ParagraphBuilder)
149
150
  paragraph = (builder
150
- .from_string("Text content") # Java: fromString()
151
- .with_font(Font("Arial", 12)) # Java: withFont()
152
- .with_color(Color(255, 0, 0)) # Java: withColor()
153
- .with_line_spacing(1.5) # Java: withLineSpacing()
154
- .with_position(position) # Java: withPosition()
155
- .build()) # Java: build()
151
+ .from_string("Text content") # Java: fromString()
152
+ .with_font(Font("Arial", 12)) # Java: withFont()
153
+ .with_color(Color(255, 0, 0)) # Java: withColor()
154
+ .with_line_spacing(1.5) # Java: withLineSpacing()
155
+ .with_position(position) # Java: withPosition()
156
+ .build()) # Java: build()
156
157
 
157
158
  # Font file registration (Java: withFont(File, double))
158
159
  paragraph = (builder
159
- .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
160
- .from_string("Custom font text")
161
- .with_position(position)
162
- .build())
160
+ .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
161
+ .from_string("Custom font text")
162
+ .with_position(position)
163
+ .build())
163
164
  ```
164
165
 
165
166
  ### Position API
@@ -3,9 +3,9 @@ pdfdancer/exceptions.py,sha256=Y5zwNVZprsv2hvKX304cXWobJt11nrEhCzLklu2wiO8,1567
3
3
  pdfdancer/image_builder.py,sha256=Omxc2LcieJ1MbvWBXR5_sfia--eAucTUe0KWgr22HYo,842
4
4
  pdfdancer/models.py,sha256=SmkKScr47uVs6FCWUAVIg6rucYrYHvbIxZngyA50XyI,15498
5
5
  pdfdancer/paragraph_builder.py,sha256=bAfwX9U2YT1UGX9EKkPnGYvGK3SQP3X1ocxlgyLE_rU,8872
6
- pdfdancer/pdfdancer_v1.py,sha256=Jsr1ZnqUoeNd3So-9ZhygDdwXSCN-m3G_9HUZ3cU_Cc,33196
6
+ pdfdancer/pdfdancer_v1.py,sha256=Pgv-2L0pYQdOUVK2nUntrGB6hCDKUOMWHYuN8loBM3Q,35540
7
7
  pdfdancer/types.py,sha256=lcYnqCFgnrGpplSPCxKh3X9AZ3-9t-lJqMH5ZLew_I4,8188
8
- pdfdancer_client_python-0.2.2.dist-info/METADATA,sha256=5XM20sBrcRra-BCRaJZL1L4bKgVzvmdOjdM1CmkwSCw,9242
9
- pdfdancer_client_python-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- pdfdancer_client_python-0.2.2.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
11
- pdfdancer_client_python-0.2.2.dist-info/RECORD,,
8
+ pdfdancer_client_python-0.2.4.dist-info/METADATA,sha256=a-sAoRCXslorLigW0Vxbmhqzg5krpQ3b9WpHyi_8FM8,9253
9
+ pdfdancer_client_python-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ pdfdancer_client_python-0.2.4.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
11
+ pdfdancer_client_python-0.2.4.dist-info/RECORD,,