namecheap-python 1.1.0__tar.gz → 1.3.0__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 (39) hide show
  1. namecheap_python-1.3.0/CLAUDE.md +2 -0
  2. namecheap_python-1.3.0/CONTRIBUTING.md +113 -0
  3. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/PKG-INFO +96 -13
  4. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/README.md +95 -12
  5. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/pyproject.toml +1 -1
  6. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/__init__.py +16 -2
  7. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/dns.py +73 -1
  8. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/domains.py +93 -1
  9. namecheap_python-1.3.0/src/namecheap/_api/users.py +32 -0
  10. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/client.py +8 -0
  11. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/models.py +71 -0
  12. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/__main__.py +183 -50
  13. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/uv.lock +1 -1
  14. namecheap_python-1.1.0/docs/dev/README.md +0 -220
  15. namecheap_python-1.1.0/pending.md +0 -94
  16. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.env.example +0 -0
  17. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.github/cliff.toml +0 -0
  18. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.github/workflows/release.yml +0 -0
  19. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.gitignore +0 -0
  20. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.pre-commit-config.yaml +0 -0
  21. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/CLI.md +0 -0
  22. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/LICENSE +0 -0
  23. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/MANIFEST.in +0 -0
  24. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/examples/README.md +0 -0
  25. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/examples/quickstart.py +0 -0
  26. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/__init__.py +0 -0
  27. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/base.py +0 -0
  28. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/errors.py +0 -0
  29. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/logging.py +0 -0
  30. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/README.md +0 -0
  31. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/__init__.py +0 -0
  32. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/completion.py +0 -0
  33. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/README.md +0 -0
  34. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/__init__.py +0 -0
  35. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/__main__.py +0 -0
  36. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot1.png +0 -0
  37. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot2.png +0 -0
  38. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot3.png +0 -0
  39. {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot4.png +0 -0
@@ -0,0 +1,2 @@
1
+ @README.md
2
+ @CONTRIBUTING.md
@@ -0,0 +1,113 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/adriangalilea/namecheap-python.git
7
+ cd namecheap-python
8
+ uv sync --all-extras
9
+ pre-commit install
10
+ ```
11
+
12
+ ## Code Quality
13
+
14
+ ```bash
15
+ uv run ruff check # lint
16
+ uv run ruff check --fix # auto-fix
17
+ uv run ruff format # format
18
+
19
+ pre-commit run --all-files # run all hooks
20
+ ```
21
+
22
+ ## Building
23
+
24
+ ```bash
25
+ uv build
26
+ ```
27
+
28
+ ## Release
29
+
30
+ Automated via GitHub Actions on push to `main`. If the version in `pyproject.toml` doesn't exist on PyPI, CI publishes automatically, creates a git tag, and generates a GitHub release with changelog via [git-cliff](https://git-cliff.org/).
31
+
32
+ Bump version in both `pyproject.toml` and `src/namecheap/__init__.py`.
33
+
34
+ ## Debugging
35
+
36
+ ```python
37
+ import logging
38
+ logging.basicConfig(level=logging.DEBUG)
39
+ ```
40
+
41
+ Or set `NAMECHEAP_DEBUG=true`.
42
+
43
+ ## Architecture
44
+
45
+ ### Project Structure
46
+
47
+ ```
48
+ src/
49
+ ├── namecheap/ # Core SDK
50
+ │ ├── client.py # Main client — wires up API classes
51
+ │ ├── models.py # Pydantic models (all API data types live here)
52
+ │ ├── errors.py # Exceptions with helpful messages
53
+ │ ├── logging.py # Colored logging and error display
54
+ │ └── _api/ # API implementations
55
+ │ ├── base.py # BaseAPI with _request() — all API calls go through here
56
+ │ ├── domains.py # namecheap.domains.* endpoints
57
+ │ └── dns.py # namecheap.domains.dns.* endpoints + builder
58
+ ├── namecheap_cli/ # CLI (click)
59
+ │ ├── __main__.py # All commands in one file
60
+ │ └── completion.py # Shell completions
61
+ └── namecheap_dns_tui/ # TUI (textual)
62
+ └── __main__.py
63
+ ```
64
+
65
+ ### Key Patterns
66
+
67
+ **SDK layer** (`src/namecheap/_api/`):
68
+ - Every API class extends `BaseAPI` which provides `_request(command, params, model=, path=)`
69
+ - `_request` handles auth params, XML parsing, error handling, and Pydantic deserialization
70
+ - Pass `model=SomeModel` to auto-parse, or omit for raw dict access
71
+ - Pass `path="DotSeparated.Path"` to navigate the XML response
72
+ - Domain parsing uses `tldextract` to split into SLD/TLD — see any method in `dns.py`
73
+ - Return Pydantic models, not dicts — see `Nameservers`, `DNSRecord`, `Domain`, etc.
74
+ - Use `assert` for assumptions, `ValueError` for bad user input
75
+
76
+ **Models** (`src/namecheap/models.py`):
77
+ - `XMLModel` base handles XML attribute aliases (`@Name` → `name`) and boolean parsing
78
+ - All new data types go here and get exported from `__init__.py`
79
+
80
+ **CLI layer** (`src/namecheap_cli/__main__.py`):
81
+ - Commands use `@pass_config` decorator for client initialization
82
+ - Interactive operations use `rich.progress.Progress` with `SpinnerColumn` for loading
83
+ - Destructive operations use `rich.prompt.Confirm.ask` with `--yes/-y` to skip
84
+ - Table output via `rich.table.Table`, with `output_formatter()` for JSON/YAML alternatives
85
+ - Error handling: catch `NamecheapError`, print with `[red]`, `sys.exit(1)`
86
+
87
+ **DNS Builder** (`src/namecheap/_api/dns.py`):
88
+ - Fluent interface: `builder.a(...).mx(...).txt(...)` returns `Self` for chaining
89
+ - Default TTL is 1799 (displays as "Automatic" in Namecheap UI)
90
+
91
+ ### Adding a New API Endpoint
92
+
93
+ 1. Add Pydantic model in `models.py`, export from `__init__.py`
94
+ 2. Add method in the appropriate `_api/*.py` file using `self._request()`
95
+ 3. Add CLI command in `__main__.py` following existing patterns
96
+ 4. Update the API Coverage table in `README.md`
97
+
98
+ Best reference: `dns.py:get_nameservers()` for SDK, `__main__.py:dns_nameservers()` for CLI.
99
+
100
+ ## PR Checklist
101
+
102
+ - [ ] `uv run ruff check` passes
103
+ - [ ] `uv run ruff format --check` passes
104
+ - [ ] Examples work correctly
105
+ - [ ] Documentation updated if needed
106
+
107
+ ## Links
108
+
109
+ - [Namecheap API Documentation](https://www.namecheap.com/support/api/methods/)
110
+ - [Pydantic Documentation](https://docs.pydantic.dev/)
111
+ - [Click Documentation](https://click.palletsprojects.com/)
112
+ - [Textual Documentation](https://textual.textualize.io/)
113
+ - [Ruff Documentation](https://docs.astral.sh/ruff/)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: namecheap-python
3
- Version: 1.1.0
3
+ Version: 1.3.0
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
@@ -42,6 +42,12 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  # Namecheap Python SDK
44
44
 
45
+ [![PyPI version](https://badge.fury.io/py/namecheap-python.svg)](https://pypi.org/project/namecheap-python/)
46
+ [![Downloads](https://pepy.tech/badge/namecheap-python)](https://pepy.tech/project/namecheap-python)
47
+ [![Downloads/month](https://pepy.tech/badge/namecheap-python/month)](https://pepy.tech/project/namecheap-python)
48
+ [![Python](https://img.shields.io/pypi/pyversions/namecheap-python)](https://pypi.org/project/namecheap-python/)
49
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
50
+
45
51
  A modern, friendly Python SDK for the Namecheap API with comprehensive CLI and TUI tools.
46
52
 
47
53
  ## 🚀 Features
@@ -206,6 +212,38 @@ Adding CNAME record to tdo.garden...
206
212
  ```
207
213
 
208
214
 
215
+ Check account balance:
216
+
217
+ ```bash
218
+ ❯ namecheap-cli account balance
219
+ Account Balance
220
+ ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
221
+ ┃ Field ┃ Amount ┃
222
+ ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
223
+ │ Available Balance │ 0.00 USD │
224
+ │ Account Balance │ 0.00 USD │
225
+ │ Earned Amount │ 0.00 USD │
226
+ │ Withdrawable │ 0.00 USD │
227
+ │ Auto-Renew Required │ 20.16 USD │
228
+ └─────────────────────┴───────────┘
229
+ ```
230
+
231
+ Get detailed domain info:
232
+
233
+ ```bash
234
+ ❯ namecheap-cli domain info self.fm
235
+
236
+ Domain Information: self.fm
237
+
238
+ Status: Ok
239
+ Owner: adriangalilea
240
+ Created: 07/15/2023
241
+ Expires: 07/15/2026
242
+ Premium: No
243
+ WHOIS Guard: ✓ Enabled
244
+ DNS Provider: CUSTOM
245
+ ```
246
+
209
247
  You can also export DNS records:
210
248
 
211
249
  ```bash
@@ -320,6 +358,48 @@ nc.dns.set_custom_nameservers("example.com", [
320
358
  nc.dns.set_default_nameservers("example.com")
321
359
  ```
322
360
 
361
+ ### Domain Info
362
+
363
+ ```python
364
+ info = nc.domains.get_info("example.com")
365
+ print(info.status) # 'Ok'
366
+ print(info.whoisguard_enabled) # True
367
+ print(info.dns_provider) # 'CUSTOM'
368
+ print(info.created) # '07/15/2023'
369
+ print(info.expires) # '07/15/2026'
370
+ ```
371
+
372
+ ### Account Balance
373
+
374
+ ```python
375
+ bal = nc.users.get_balances()
376
+ print(f"{bal.available_balance} {bal.currency}") # '4932.96 USD'
377
+ print(bal.funds_required_for_auto_renew) # Decimal('20.16')
378
+ ```
379
+
380
+ ### Email Forwarding
381
+
382
+ ```python
383
+ # Read
384
+ rules = nc.dns.get_email_forwarding("example.com")
385
+ for r in rules:
386
+ print(f"{r.mailbox} -> {r.forward_to}")
387
+
388
+ # Write (replaces all existing rules)
389
+ nc.dns.set_email_forwarding("example.com", [
390
+ EmailForward(mailbox="info", forward_to="me@gmail.com"),
391
+ EmailForward(mailbox="support", forward_to="help@gmail.com"),
392
+ ])
393
+ ```
394
+
395
+ ### Domain Contacts
396
+
397
+ ```python
398
+ contacts = nc.domains.get_contacts("example.com")
399
+ print(f"{contacts.registrant.first_name} {contacts.registrant.last_name}")
400
+ print(contacts.registrant.email)
401
+ ```
402
+
323
403
  ### Domain Management
324
404
 
325
405
  ```python
@@ -393,22 +473,23 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
393
473
  nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
394
474
  ```
395
475
 
396
- ## 🚧 Pending Features
397
-
398
- The following Namecheap API features are planned for future releases:
399
-
400
- - **SSL API** - Certificate management
401
- - **Domain Transfer API** - Transfer domains between registrars
402
- - **Domain NS API** - Glue record management (child nameservers)
403
- - **Users API** - Account management and balance checking
404
- - **Whois API** - WHOIS information lookups
405
- - **Email Forwarding** - Email forwarding configuration
476
+ ## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
406
477
 
407
- See [pending.md](pending.md) for full details.
478
+ | API | Status | Methods |
479
+ |-----|--------|---------|
480
+ | `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `getContacts`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
481
+ | `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding`, `setEmailForwarding` |
482
+ | `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
483
+ | `namecheap.domains.*` | 🚧 Planned | `getTldList`, `reactivate` |
484
+ | `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
485
+ | `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
486
+ | `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
487
+ | `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
488
+ | `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
408
489
 
409
490
  ## 🛠️ Development
410
491
 
411
- See [Development Guide](docs/dev/README.md) for detailed development instructions.
492
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
412
493
 
413
494
  ## 📝 License
414
495
 
@@ -420,4 +501,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. See the [D
420
501
 
421
502
  ### Contributors
422
503
 
504
+ - [@huntertur](https://github.com/huntertur) — Rich dependency fix
505
+ - [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
423
506
  - [@cosmin](https://github.com/cosmin) — Nameserver management
@@ -1,5 +1,11 @@
1
1
  # Namecheap Python SDK
2
2
 
3
+ [![PyPI version](https://badge.fury.io/py/namecheap-python.svg)](https://pypi.org/project/namecheap-python/)
4
+ [![Downloads](https://pepy.tech/badge/namecheap-python)](https://pepy.tech/project/namecheap-python)
5
+ [![Downloads/month](https://pepy.tech/badge/namecheap-python/month)](https://pepy.tech/project/namecheap-python)
6
+ [![Python](https://img.shields.io/pypi/pyversions/namecheap-python)](https://pypi.org/project/namecheap-python/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
3
9
  A modern, friendly Python SDK for the Namecheap API with comprehensive CLI and TUI tools.
4
10
 
5
11
  ## 🚀 Features
@@ -164,6 +170,38 @@ Adding CNAME record to tdo.garden...
164
170
  ```
165
171
 
166
172
 
173
+ Check account balance:
174
+
175
+ ```bash
176
+ ❯ namecheap-cli account balance
177
+ Account Balance
178
+ ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
179
+ ┃ Field ┃ Amount ┃
180
+ ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
181
+ │ Available Balance │ 0.00 USD │
182
+ │ Account Balance │ 0.00 USD │
183
+ │ Earned Amount │ 0.00 USD │
184
+ │ Withdrawable │ 0.00 USD │
185
+ │ Auto-Renew Required │ 20.16 USD │
186
+ └─────────────────────┴───────────┘
187
+ ```
188
+
189
+ Get detailed domain info:
190
+
191
+ ```bash
192
+ ❯ namecheap-cli domain info self.fm
193
+
194
+ Domain Information: self.fm
195
+
196
+ Status: Ok
197
+ Owner: adriangalilea
198
+ Created: 07/15/2023
199
+ Expires: 07/15/2026
200
+ Premium: No
201
+ WHOIS Guard: ✓ Enabled
202
+ DNS Provider: CUSTOM
203
+ ```
204
+
167
205
  You can also export DNS records:
168
206
 
169
207
  ```bash
@@ -278,6 +316,48 @@ nc.dns.set_custom_nameservers("example.com", [
278
316
  nc.dns.set_default_nameservers("example.com")
279
317
  ```
280
318
 
319
+ ### Domain Info
320
+
321
+ ```python
322
+ info = nc.domains.get_info("example.com")
323
+ print(info.status) # 'Ok'
324
+ print(info.whoisguard_enabled) # True
325
+ print(info.dns_provider) # 'CUSTOM'
326
+ print(info.created) # '07/15/2023'
327
+ print(info.expires) # '07/15/2026'
328
+ ```
329
+
330
+ ### Account Balance
331
+
332
+ ```python
333
+ bal = nc.users.get_balances()
334
+ print(f"{bal.available_balance} {bal.currency}") # '4932.96 USD'
335
+ print(bal.funds_required_for_auto_renew) # Decimal('20.16')
336
+ ```
337
+
338
+ ### Email Forwarding
339
+
340
+ ```python
341
+ # Read
342
+ rules = nc.dns.get_email_forwarding("example.com")
343
+ for r in rules:
344
+ print(f"{r.mailbox} -> {r.forward_to}")
345
+
346
+ # Write (replaces all existing rules)
347
+ nc.dns.set_email_forwarding("example.com", [
348
+ EmailForward(mailbox="info", forward_to="me@gmail.com"),
349
+ EmailForward(mailbox="support", forward_to="help@gmail.com"),
350
+ ])
351
+ ```
352
+
353
+ ### Domain Contacts
354
+
355
+ ```python
356
+ contacts = nc.domains.get_contacts("example.com")
357
+ print(f"{contacts.registrant.first_name} {contacts.registrant.last_name}")
358
+ print(contacts.registrant.email)
359
+ ```
360
+
281
361
  ### Domain Management
282
362
 
283
363
  ```python
@@ -351,22 +431,23 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
351
431
  nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
352
432
  ```
353
433
 
354
- ## 🚧 Pending Features
355
-
356
- The following Namecheap API features are planned for future releases:
357
-
358
- - **SSL API** - Certificate management
359
- - **Domain Transfer API** - Transfer domains between registrars
360
- - **Domain NS API** - Glue record management (child nameservers)
361
- - **Users API** - Account management and balance checking
362
- - **Whois API** - WHOIS information lookups
363
- - **Email Forwarding** - Email forwarding configuration
434
+ ## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
364
435
 
365
- See [pending.md](pending.md) for full details.
436
+ | API | Status | Methods |
437
+ |-----|--------|---------|
438
+ | `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `getContacts`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
439
+ | `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding`, `setEmailForwarding` |
440
+ | `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
441
+ | `namecheap.domains.*` | 🚧 Planned | `getTldList`, `reactivate` |
442
+ | `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
443
+ | `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
444
+ | `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
445
+ | `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
446
+ | `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
366
447
 
367
448
  ## 🛠️ Development
368
449
 
369
- See [Development Guide](docs/dev/README.md) for detailed development instructions.
450
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
370
451
 
371
452
  ## 📝 License
372
453
 
@@ -378,4 +459,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. See the [D
378
459
 
379
460
  ### Contributors
380
461
 
462
+ - [@huntertur](https://github.com/huntertur) — Rich dependency fix
463
+ - [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
381
464
  - [@cosmin](https://github.com/cosmin) — Nameserver management
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "namecheap-python"
3
- version = "1.1.0"
3
+ version = "1.3.0"
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"
@@ -12,15 +12,29 @@ from __future__ import annotations
12
12
 
13
13
  from .client import Namecheap
14
14
  from .errors import ConfigurationError, NamecheapError, ValidationError
15
- from .models import Contact, DNSRecord, Domain, DomainCheck, Nameservers
15
+ from .models import (
16
+ AccountBalance,
17
+ Contact,
18
+ DNSRecord,
19
+ Domain,
20
+ DomainCheck,
21
+ DomainContacts,
22
+ DomainInfo,
23
+ EmailForward,
24
+ Nameservers,
25
+ )
16
26
 
17
- __version__ = "1.1.0"
27
+ __version__ = "1.3.0"
18
28
  __all__ = [
29
+ "AccountBalance",
19
30
  "ConfigurationError",
20
31
  "Contact",
21
32
  "DNSRecord",
22
33
  "Domain",
23
34
  "DomainCheck",
35
+ "DomainContacts",
36
+ "DomainInfo",
37
+ "EmailForward",
24
38
  "Namecheap",
25
39
  "NamecheapError",
26
40
  "Nameservers",
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, Literal
6
6
 
7
7
  import tldextract
8
8
 
9
- from namecheap.models import DNSRecord, Nameservers
9
+ from namecheap.models import DNSRecord, EmailForward, Nameservers
10
10
 
11
11
  from .base import BaseAPI
12
12
 
@@ -494,3 +494,75 @@ class DnsAPI(BaseAPI):
494
494
  nameservers = [ns_data] if isinstance(ns_data, str) else ns_data
495
495
 
496
496
  return Nameservers(is_default=is_default, nameservers=nameservers)
497
+
498
+ def get_email_forwarding(self, domain: str) -> list[EmailForward]:
499
+ """
500
+ Get email forwarding rules for a domain.
501
+
502
+ Args:
503
+ domain: Domain name
504
+
505
+ Returns:
506
+ List of EmailForward rules
507
+
508
+ Examples:
509
+ >>> rules = nc.dns.get_email_forwarding("example.com")
510
+ >>> for r in rules:
511
+ ... print(f"{r.mailbox} -> {r.forward_to}")
512
+ """
513
+ result: Any = self._request(
514
+ "namecheap.domains.dns.getEmailForwarding",
515
+ {"DomainName": domain},
516
+ path="DomainDNSGetEmailForwardingResult",
517
+ )
518
+
519
+ if not result:
520
+ return []
521
+
522
+ forwards = result.get("Forward", [])
523
+ if isinstance(forwards, dict):
524
+ forwards = [forwards]
525
+ assert isinstance(forwards, list), f"Unexpected Forward type: {type(forwards)}"
526
+
527
+ return [
528
+ EmailForward(mailbox=f.get("@mailbox", ""), forward_to=f.get("#text", ""))
529
+ for f in forwards
530
+ ]
531
+
532
+ def set_email_forwarding(
533
+ self, domain: str, rules: list[EmailForward] | list[dict[str, str]]
534
+ ) -> bool:
535
+ """
536
+ Set email forwarding rules for a domain. Replaces all existing rules.
537
+
538
+ Args:
539
+ domain: Domain name
540
+ rules: List of EmailForward or dicts with 'mailbox' and 'forward_to' keys
541
+
542
+ Returns:
543
+ True if successful
544
+
545
+ Examples:
546
+ >>> nc.dns.set_email_forwarding("example.com", [
547
+ ... EmailForward(mailbox="info", forward_to="me@gmail.com"),
548
+ ... EmailForward(mailbox="support", forward_to="help@gmail.com"),
549
+ ... ])
550
+ """
551
+ assert rules, "At least one forwarding rule is required"
552
+
553
+ params: dict[str, Any] = {"DomainName": domain}
554
+ for i, rule in enumerate(rules, 1):
555
+ if isinstance(rule, EmailForward):
556
+ params[f"MailBox{i}"] = rule.mailbox
557
+ params[f"ForwardTo{i}"] = rule.forward_to
558
+ else:
559
+ params[f"MailBox{i}"] = rule["mailbox"]
560
+ params[f"ForwardTo{i}"] = rule["forward_to"]
561
+
562
+ result: Any = self._request(
563
+ "namecheap.domains.dns.setEmailForwarding",
564
+ params,
565
+ path="DomainDNSSetEmailForwardingResult",
566
+ )
567
+
568
+ return bool(result and result.get("@IsSuccess", "false").lower() == "true")
@@ -9,7 +9,7 @@ from typing import Any
9
9
  import tldextract
10
10
 
11
11
  from namecheap.logging import logger
12
- from namecheap.models import Contact, Domain, DomainCheck
12
+ from namecheap.models import Contact, Domain, DomainCheck, DomainContacts, DomainInfo
13
13
 
14
14
  from .base import BaseAPI
15
15
 
@@ -110,6 +110,98 @@ class DomainsAPI(BaseAPI):
110
110
  return [results]
111
111
  return results if isinstance(results, list) else []
112
112
 
113
+ def get_info(self, domain: str) -> DomainInfo:
114
+ """
115
+ Get detailed information about a domain.
116
+
117
+ Args:
118
+ domain: Domain name
119
+
120
+ Returns:
121
+ DomainInfo with status, whoisguard, DNS provider, etc.
122
+
123
+ Examples:
124
+ >>> info = nc.domains.get_info("example.com")
125
+ >>> print(f"{info.domain} status={info.status} whoisguard={info.whoisguard_enabled}")
126
+ """
127
+ result: Any = self._request(
128
+ "namecheap.domains.getInfo",
129
+ {"DomainName": domain},
130
+ path="DomainGetInfoResult",
131
+ )
132
+
133
+ assert result, f"API returned empty result for {domain} getInfo"
134
+
135
+ # Extract nested fields into flat structure
136
+ domain_details = result.get("DomainDetails", {})
137
+ whoisguard = result.get("Whoisguard", {})
138
+ dns_details = result.get("DnsDetails", {})
139
+
140
+ flat = {
141
+ "@ID": result.get("@ID"),
142
+ "@DomainName": result.get("@DomainName"),
143
+ "@OwnerName": result.get("@OwnerName"),
144
+ "@IsOwner": result.get("@IsOwner"),
145
+ "@IsPremium": result.get("@IsPremium", "false"),
146
+ "@Status": result.get("@Status"),
147
+ "created": domain_details.get("CreatedDate"),
148
+ "expires": domain_details.get("ExpiredDate"),
149
+ "whoisguard_enabled": whoisguard.get("@Enabled", "false").lower() == "true"
150
+ if isinstance(whoisguard, dict)
151
+ else False,
152
+ "dns_provider": dns_details.get("@ProviderType")
153
+ if isinstance(dns_details, dict)
154
+ else None,
155
+ }
156
+
157
+ return DomainInfo.model_validate(flat)
158
+
159
+ def get_contacts(self, domain: str) -> DomainContacts:
160
+ """
161
+ Get contact information for a domain.
162
+
163
+ Args:
164
+ domain: Domain name
165
+
166
+ Returns:
167
+ DomainContacts with registrant, tech, admin, and aux_billing contacts
168
+
169
+ Examples:
170
+ >>> contacts = nc.domains.get_contacts("example.com")
171
+ >>> print(contacts.registrant.email)
172
+ """
173
+ result: Any = self._request(
174
+ "namecheap.domains.getContacts",
175
+ {"DomainName": domain},
176
+ path="DomainContactsResult",
177
+ )
178
+
179
+ assert result, f"API returned empty result for {domain} getContacts"
180
+
181
+ def parse_contact(data: dict[str, Any]) -> Contact:
182
+ return Contact.model_validate(
183
+ {
184
+ "FirstName": data.get("FirstName", ""),
185
+ "LastName": data.get("LastName", ""),
186
+ "Organization": data.get("Organization"),
187
+ "Address1": data.get("Address1", ""),
188
+ "Address2": data.get("Address2"),
189
+ "City": data.get("City", ""),
190
+ "StateProvince": data.get("StateProvince", ""),
191
+ "PostalCode": data.get("PostalCode", ""),
192
+ "Country": data.get("Country", ""),
193
+ "Phone": data.get("Phone", ""),
194
+ "EmailAddress": data.get("EmailAddress", ""),
195
+ }
196
+ )
197
+
198
+ return DomainContacts(
199
+ registrant=parse_contact(result.get("Registrant", {})),
200
+ tech=parse_contact(result.get("Tech", {})),
201
+ admin=parse_contact(result.get("Admin", {})),
202
+ aux_billing=parse_contact(result.get("AuxBilling", {})),
203
+ )
204
+
113
205
  def register(
114
206
  self,
115
207
  domain: str,
@@ -0,0 +1,32 @@
1
+ """Users API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from namecheap.models import AccountBalance
8
+
9
+ from .base import BaseAPI
10
+
11
+
12
+ class UsersAPI(BaseAPI):
13
+ """User account operations."""
14
+
15
+ def get_balances(self) -> AccountBalance:
16
+ """
17
+ Get account balance information.
18
+
19
+ Returns:
20
+ AccountBalance with available balance, earned amount, etc.
21
+
22
+ Examples:
23
+ >>> bal = nc.users.get_balances()
24
+ >>> print(f"{bal.available_balance} {bal.currency}")
25
+ """
26
+ result: Any = self._request(
27
+ "namecheap.users.getBalances",
28
+ path="UserGetBalancesResult",
29
+ )
30
+
31
+ assert result, "API returned empty result for getBalances"
32
+ return AccountBalance.model_validate(result)