namecheap-python 1.0.4__tar.gz → 1.0.6__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 (36) hide show
  1. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/CLI.md +3 -1
  2. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/PKG-INFO +20 -2
  3. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/README.md +19 -1
  4. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/pyproject.toml +1 -1
  5. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/__init__.py +1 -1
  6. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/_api/dns.py +14 -14
  7. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/models.py +9 -6
  8. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_cli/__main__.py +7 -2
  9. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/__main__.py +3 -3
  10. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/uv.lock +1 -1
  11. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/.env.example +0 -0
  12. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/.github/cliff.toml +0 -0
  13. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/.github/workflows/release.yml +0 -0
  14. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/.gitignore +0 -0
  15. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/.pre-commit-config.yaml +0 -0
  16. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/LICENSE +0 -0
  17. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/MANIFEST.in +0 -0
  18. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/docs/dev/README.md +0 -0
  19. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/examples/README.md +0 -0
  20. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/examples/quickstart.py +0 -0
  21. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/pending.md +0 -0
  22. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/_api/__init__.py +0 -0
  23. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/_api/base.py +0 -0
  24. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/_api/domains.py +0 -0
  25. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/client.py +0 -0
  26. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/errors.py +0 -0
  27. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap/logging.py +0 -0
  28. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_cli/README.md +0 -0
  29. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_cli/__init__.py +0 -0
  30. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_cli/completion.py +0 -0
  31. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/README.md +0 -0
  32. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/__init__.py +0 -0
  33. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/assets/screenshot1.png +0 -0
  34. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/assets/screenshot2.png +0 -0
  35. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/assets/screenshot3.png +0 -0
  36. {namecheap_python-1.0.4 → namecheap_python-1.0.6}/src/namecheap_dns_tui/assets/screenshot4.png +0 -0
@@ -144,10 +144,12 @@ nc dns add example.com TXT @ "v=spf1 include:_spf.google.com ~all"
144
144
  # Add URL redirect
145
145
  nc dns add example.com URL301 www https://newsite.com
146
146
 
147
- # Custom TTL
147
+ # Custom TTL (default is 1799 = "Automatic" in Namecheap UI)
148
148
  nc dns add example.com A www 192.0.2.1 --ttl 300
149
149
  ```
150
150
 
151
+ **Note:** The default TTL is 1799 seconds, which displays as "Automatic" in the Namecheap web interface.
152
+
151
153
  ### Delete DNS Records
152
154
 
153
155
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: namecheap-python
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: A friendly Python SDK for Namecheap API
5
5
  Project-URL: Homepage, https://github.com/adriangalilea/namecheap-python
6
6
  Project-URL: Repository, https://github.com/adriangalilea/namecheap-python
@@ -300,6 +300,8 @@ nc.dns.set("example.com",
300
300
  )
301
301
  ```
302
302
 
303
+ **Note on TTL:** The default TTL is **1799 seconds**, which displays as **"Automatic"** in the Namecheap web interface. This is an undocumented Namecheap API behavior. You can specify custom TTL values (60-86400 seconds) in any DNS method.
304
+
303
305
  ### Domain Management
304
306
 
305
307
  ```python
@@ -357,12 +359,28 @@ except NamecheapError as e:
357
359
  print(f"💡 Tip: {e.help}")
358
360
  ```
359
361
 
362
+ ## ⚠️ Namecheap API Quirks
363
+
364
+ This section documents undocumented or unusual Namecheap API behaviors we've discovered:
365
+
366
+ ### TTL "Automatic" = 1799 seconds
367
+
368
+ The Namecheap web interface displays TTL as **"Automatic"** when the value is exactly **1799 seconds**, but shows **"30 min"** when it's **1800 seconds**. This behavior is completely undocumented in their official API documentation.
369
+
370
+ Their API docs state TTL defaults to 1800 when omitted, but the UI treats 1799 specially. This SDK defaults to 1799 to match the "Automatic" behavior users see in the web interface.
371
+
372
+ ```python
373
+ # Both are valid, but display differently in Namecheap UI:
374
+ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
375
+ nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
376
+ ```
377
+
360
378
  ## 🚧 Pending Features
361
379
 
362
380
  The following Namecheap API features are planned for future releases:
363
381
 
364
382
  - **SSL API** - Certificate management
365
- - **Domain Transfer API** - Transfer domains between registrars
383
+ - **Domain Transfer API** - Transfer domains between registrars
366
384
  - **Domain NS API** - Custom nameserver management
367
385
  - **Users API** - Account management and balance checking
368
386
  - **Whois API** - WHOIS information lookups
@@ -258,6 +258,8 @@ nc.dns.set("example.com",
258
258
  )
259
259
  ```
260
260
 
261
+ **Note on TTL:** The default TTL is **1799 seconds**, which displays as **"Automatic"** in the Namecheap web interface. This is an undocumented Namecheap API behavior. You can specify custom TTL values (60-86400 seconds) in any DNS method.
262
+
261
263
  ### Domain Management
262
264
 
263
265
  ```python
@@ -315,12 +317,28 @@ except NamecheapError as e:
315
317
  print(f"💡 Tip: {e.help}")
316
318
  ```
317
319
 
320
+ ## ⚠️ Namecheap API Quirks
321
+
322
+ This section documents undocumented or unusual Namecheap API behaviors we've discovered:
323
+
324
+ ### TTL "Automatic" = 1799 seconds
325
+
326
+ The Namecheap web interface displays TTL as **"Automatic"** when the value is exactly **1799 seconds**, but shows **"30 min"** when it's **1800 seconds**. This behavior is completely undocumented in their official API documentation.
327
+
328
+ Their API docs state TTL defaults to 1800 when omitted, but the UI treats 1799 specially. This SDK defaults to 1799 to match the "Automatic" behavior users see in the web interface.
329
+
330
+ ```python
331
+ # Both are valid, but display differently in Namecheap UI:
332
+ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
333
+ nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
334
+ ```
335
+
318
336
  ## 🚧 Pending Features
319
337
 
320
338
  The following Namecheap API features are planned for future releases:
321
339
 
322
340
  - **SSL API** - Certificate management
323
- - **Domain Transfer API** - Transfer domains between registrars
341
+ - **Domain Transfer API** - Transfer domains between registrars
324
342
  - **Domain NS API** - Custom nameserver management
325
343
  - **Users API** - Account management and balance checking
326
344
  - **Whois API** - WHOIS information lookups
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "namecheap-python"
3
- version = "1.0.4"
3
+ version = "1.0.6"
4
4
  description = "A friendly Python SDK for Namecheap API"
5
5
  authors = [{name = "Adrian Galilea Delgado", email = "adriangalilea@gmail.com"}]
6
6
  readme = "README.md"
@@ -14,7 +14,7 @@ from .client import Namecheap
14
14
  from .errors import ConfigurationError, NamecheapError, ValidationError
15
15
  from .models import Contact, DNSRecord, Domain, DomainCheck
16
16
 
17
- __version__ = "1.0.0"
17
+ __version__ = "1.0.5"
18
18
  __all__ = [
19
19
  "ConfigurationError",
20
20
  "Contact",
@@ -22,14 +22,14 @@ class DNSRecordBuilder:
22
22
  """Initialize empty builder."""
23
23
  self._records: list[DNSRecord] = []
24
24
 
25
- def a(self, name: str, ip: str, ttl: int = 1800) -> Self:
25
+ def a(self, name: str, ip: str, ttl: int = 1799) -> Self:
26
26
  """
27
27
  Add an A record.
28
28
 
29
29
  Args:
30
30
  name: Record name (@ for root)
31
31
  ip: IPv4 address
32
- ttl: Time to live in seconds (60-86400, default: 1800)
32
+ ttl: Time to live in seconds (60-86400, default: 1799 = "Automatic")
33
33
 
34
34
  Returns:
35
35
  Self for chaining
@@ -41,14 +41,14 @@ class DNSRecordBuilder:
41
41
  )
42
42
  return self
43
43
 
44
- def aaaa(self, name: str, ipv6: str, ttl: int = 1800) -> Self:
44
+ def aaaa(self, name: str, ipv6: str, ttl: int = 1799) -> Self:
45
45
  """
46
46
  Add an AAAA record.
47
47
 
48
48
  Args:
49
49
  name: Record name (@ for root)
50
50
  ipv6: IPv6 address
51
- ttl: Time to live in seconds
51
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
52
52
 
53
53
  Returns:
54
54
  Self for chaining
@@ -60,14 +60,14 @@ class DNSRecordBuilder:
60
60
  )
61
61
  return self
62
62
 
63
- def cname(self, name: str, target: str, ttl: int = 1800) -> Self:
63
+ def cname(self, name: str, target: str, ttl: int = 1799) -> Self:
64
64
  """
65
65
  Add a CNAME record.
66
66
 
67
67
  Args:
68
68
  name: Record name (cannot be @)
69
69
  target: Target domain
70
- ttl: Time to live in seconds
70
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
71
71
 
72
72
  Returns:
73
73
  Self for chaining
@@ -81,7 +81,7 @@ class DNSRecordBuilder:
81
81
  )
82
82
  return self
83
83
 
84
- def mx(self, name: str, server: str, priority: int = 10, ttl: int = 1800) -> Self:
84
+ def mx(self, name: str, server: str, priority: int = 10, ttl: int = 1799) -> Self:
85
85
  """
86
86
  Add an MX record.
87
87
 
@@ -89,7 +89,7 @@ class DNSRecordBuilder:
89
89
  name: Record name (@ for root)
90
90
  server: Mail server hostname
91
91
  priority: MX priority (lower = higher priority)
92
- ttl: Time to live in seconds
92
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
93
93
 
94
94
  Returns:
95
95
  Self for chaining
@@ -107,14 +107,14 @@ class DNSRecordBuilder:
107
107
  )
108
108
  return self
109
109
 
110
- def txt(self, name: str, value: str, ttl: int = 1800) -> Self:
110
+ def txt(self, name: str, value: str, ttl: int = 1799) -> Self:
111
111
  """
112
112
  Add a TXT record.
113
113
 
114
114
  Args:
115
115
  name: Record name (@ for root)
116
116
  value: Text value
117
- ttl: Time to live in seconds
117
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
118
118
 
119
119
  Returns:
120
120
  Self for chaining
@@ -126,14 +126,14 @@ class DNSRecordBuilder:
126
126
  )
127
127
  return self
128
128
 
129
- def ns(self, name: str, nameserver: str, ttl: int = 1800) -> Self:
129
+ def ns(self, name: str, nameserver: str, ttl: int = 1799) -> Self:
130
130
  """
131
131
  Add an NS record.
132
132
 
133
133
  Args:
134
134
  name: Record name
135
135
  nameserver: Nameserver hostname
136
- ttl: Time to live in seconds
136
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
137
137
 
138
138
  Returns:
139
139
  Self for chaining
@@ -151,7 +151,7 @@ class DNSRecordBuilder:
151
151
  url: str,
152
152
  *,
153
153
  redirect_type: Literal["301", "frame"] = "301",
154
- ttl: int = 1800,
154
+ ttl: int = 1799,
155
155
  ) -> Self:
156
156
  """
157
157
  Add a URL redirect record.
@@ -160,7 +160,7 @@ class DNSRecordBuilder:
160
160
  name: Record name (@ for root)
161
161
  url: Target URL
162
162
  redirect_type: "301" for permanent redirect, "frame" for masked
163
- ttl: Time to live in seconds
163
+ ttl: Time to live in seconds (default: 1799 = "Automatic")
164
164
 
165
165
  Returns:
166
166
  Self for chaining
@@ -188,7 +188,7 @@ class DNSRecord(XMLModel):
188
188
  Field(alias="@Type")
189
189
  )
190
190
  value: str = Field(alias="@Address")
191
- ttl: int = Field(alias="@TTL", default=1800)
191
+ ttl: int = Field(alias="@TTL", default=1799) # 1799 = "Automatic" in Namecheap UI
192
192
  priority: int | None = Field(alias="@MXPref", default=None)
193
193
 
194
194
  @field_validator("ttl", mode="before")
@@ -196,9 +196,9 @@ class DNSRecord(XMLModel):
196
196
  def parse_ttl(cls, v: Any) -> int:
197
197
  """Ensure TTL is within valid range."""
198
198
  try:
199
- ttl = int(v) if v else 1800
199
+ ttl = int(v) if v else 1799
200
200
  except (ValueError, TypeError):
201
- ttl = 1800
201
+ ttl = 1799
202
202
  return max(60, min(86400, ttl))
203
203
 
204
204
  @field_validator("priority", mode="before")
@@ -239,12 +239,15 @@ class Domain(XMLModel):
239
239
  @field_validator("created", "expires", mode="before")
240
240
  @classmethod
241
241
  def parse_datetime(cls, v: Any) -> datetime:
242
- """Parse datetime strings."""
242
+ """Parse datetime from Namecheap API or Pydantic serialization."""
243
243
  if isinstance(v, datetime):
244
244
  return v
245
245
  if isinstance(v, str):
246
- # Namecheap uses MM/DD/YYYY format
247
- return datetime.strptime(v, "%m/%d/%Y")
246
+ # Namecheap API format (MM/DD/YYYY)
247
+ if "/" in v:
248
+ return datetime.strptime(v, "%m/%d/%Y")
249
+ # Pydantic serialization format (ISO 8601)
250
+ return datetime.fromisoformat(v)
248
251
  raise ValueError(f"Cannot parse datetime from {v}")
249
252
 
250
253
 
@@ -512,7 +512,12 @@ def dns_list(config: Config, domain: str, type: str | None, name: str | None) ->
512
512
  )
513
513
  @click.argument("name")
514
514
  @click.argument("value")
515
- @click.option("--ttl", type=int, default=1800, help="TTL in seconds (60-86400)")
515
+ @click.option(
516
+ "--ttl",
517
+ type=int,
518
+ default=1799,
519
+ help="TTL in seconds (60-86400, default: 1799 = Automatic)",
520
+ )
516
521
  @click.option("--priority", type=int, help="Priority (required for MX records)")
517
522
  @pass_config
518
523
  def dns_add(
@@ -849,7 +854,7 @@ def config_init() -> None:
849
854
  "color": True,
850
855
  "auto_renew": True,
851
856
  "whois_privacy": True,
852
- "dns_ttl": 1800,
857
+ "dns_ttl": 1799,
853
858
  },
854
859
  }
855
860
 
@@ -233,9 +233,9 @@ class AddRecordModal(ModalScreen):
233
233
  with Vertical(classes="field-group"):
234
234
  yield Label("TTL (seconds):")
235
235
  initial_ttl = (
236
- str(self.editing_record.ttl) if self.editing_record else "1800"
236
+ str(self.editing_record.ttl) if self.editing_record else "1799"
237
237
  )
238
- yield Input(placeholder="1800", id="record-ttl", value=initial_ttl)
238
+ yield Input(placeholder="1799", id="record-ttl", value=initial_ttl)
239
239
 
240
240
  with Vertical(classes="field-group", id="priority-group"):
241
241
  yield Label("Priority:")
@@ -296,7 +296,7 @@ class AddRecordModal(ModalScreen):
296
296
  record_type = self.query_one("#record-type", Select).value
297
297
  name = self.query_one("#record-name", Input).value or "@"
298
298
  value = self.query_one("#record-value", Input).value
299
- ttl_str = self.query_one("#record-ttl", Input).value or "1800"
299
+ ttl_str = self.query_one("#record-ttl", Input).value or "1799"
300
300
  ttl = max(60, min(86400, int(ttl_str)))
301
301
 
302
302
  priority = None
@@ -200,7 +200,7 @@ wheels = [
200
200
 
201
201
  [[package]]
202
202
  name = "namecheap-python"
203
- version = "1.0.3"
203
+ version = "1.0.5"
204
204
  source = { editable = "." }
205
205
  dependencies = [
206
206
  { name = "httpx" },