namecheap-python 1.0.6__tar.gz → 1.2.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.
- namecheap_python-1.2.0/CLAUDE.md +2 -0
- namecheap_python-1.2.0/CONTRIBUTING.md +113 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/PKG-INFO +105 -14
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/README.md +104 -13
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/pyproject.toml +1 -1
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/__init__.py +15 -2
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/_api/dns.py +148 -1
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/_api/domains.py +47 -1
- namecheap_python-1.2.0/src/namecheap/_api/users.py +32 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/client.py +8 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/models.py +67 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_cli/__main__.py +211 -50
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/uv.lock +1 -1
- namecheap_python-1.0.6/docs/dev/README.md +0 -220
- namecheap_python-1.0.6/pending.md +0 -91
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/.env.example +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/.github/cliff.toml +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/.github/workflows/release.yml +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/.gitignore +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/.pre-commit-config.yaml +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/CLI.md +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/LICENSE +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/MANIFEST.in +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/examples/README.md +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/examples/quickstart.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/_api/__init__.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/_api/base.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/errors.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap/logging.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_cli/README.md +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_cli/__init__.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_cli/completion.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/README.md +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/__init__.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/__main__.py +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/assets/screenshot1.png +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/assets/screenshot2.png +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/assets/screenshot3.png +0 -0
- {namecheap_python-1.0.6 → namecheap_python-1.2.0}/src/namecheap_dns_tui/assets/screenshot4.png +0 -0
|
@@ -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.0
|
|
3
|
+
Version: 1.2.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
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
46
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
47
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
48
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
49
|
+
[](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
|
|
@@ -302,6 +340,51 @@ nc.dns.set("example.com",
|
|
|
302
340
|
|
|
303
341
|
**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
342
|
|
|
343
|
+
### Nameserver Management
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
# Check current nameservers
|
|
347
|
+
ns = nc.dns.get_nameservers("example.com")
|
|
348
|
+
print(ns.nameservers) # ['dns1.registrar-servers.com', 'dns2.registrar-servers.com']
|
|
349
|
+
print(ns.is_default) # True
|
|
350
|
+
|
|
351
|
+
# Switch to custom nameservers (e.g., Cloudflare, Route 53)
|
|
352
|
+
nc.dns.set_custom_nameservers("example.com", [
|
|
353
|
+
"ns1.cloudflare.com",
|
|
354
|
+
"ns2.cloudflare.com",
|
|
355
|
+
])
|
|
356
|
+
|
|
357
|
+
# Reset back to Namecheap BasicDNS
|
|
358
|
+
nc.dns.set_default_nameservers("example.com")
|
|
359
|
+
```
|
|
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
|
+
rules = nc.dns.get_email_forwarding("example.com")
|
|
384
|
+
for r in rules:
|
|
385
|
+
print(f"{r.mailbox} -> {r.forward_to}")
|
|
386
|
+
```
|
|
387
|
+
|
|
305
388
|
### Domain Management
|
|
306
389
|
|
|
307
390
|
```python
|
|
@@ -375,22 +458,24 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
|
|
|
375
458
|
nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
|
|
376
459
|
```
|
|
377
460
|
|
|
378
|
-
##
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
461
|
+
## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
|
|
462
|
+
|
|
463
|
+
| API | Status | Methods |
|
|
464
|
+
|-----|--------|---------|
|
|
465
|
+
| `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
|
|
466
|
+
| `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding` |
|
|
467
|
+
| `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
|
|
468
|
+
| `namecheap.domains.*` | 🚧 Planned | `getContacts`, `getTldList`, `reactivate` |
|
|
469
|
+
| `namecheap.domains.dns.*` | 🚧 Planned | `setEmailForwarding` |
|
|
470
|
+
| `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
|
|
471
|
+
| `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
|
|
472
|
+
| `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
|
|
473
|
+
| `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
|
|
474
|
+
| `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
|
|
390
475
|
|
|
391
476
|
## 🛠️ Development
|
|
392
477
|
|
|
393
|
-
See [
|
|
478
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
|
|
394
479
|
|
|
395
480
|
## 📝 License
|
|
396
481
|
|
|
@@ -399,3 +484,9 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
399
484
|
## 🤝 Contributing
|
|
400
485
|
|
|
401
486
|
Contributions are welcome! Please feel free to submit a Pull Request. See the [Development Guide](docs/dev/README.md) for setup instructions and guidelines.
|
|
487
|
+
|
|
488
|
+
### Contributors
|
|
489
|
+
|
|
490
|
+
- [@huntertur](https://github.com/huntertur) — Rich dependency fix
|
|
491
|
+
- [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
|
|
492
|
+
- [@cosmin](https://github.com/cosmin) — Nameserver management
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Namecheap Python SDK
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
4
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
5
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
6
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
7
|
+
[](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
|
|
@@ -260,6 +298,51 @@ nc.dns.set("example.com",
|
|
|
260
298
|
|
|
261
299
|
**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
300
|
|
|
301
|
+
### Nameserver Management
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
# Check current nameservers
|
|
305
|
+
ns = nc.dns.get_nameservers("example.com")
|
|
306
|
+
print(ns.nameservers) # ['dns1.registrar-servers.com', 'dns2.registrar-servers.com']
|
|
307
|
+
print(ns.is_default) # True
|
|
308
|
+
|
|
309
|
+
# Switch to custom nameservers (e.g., Cloudflare, Route 53)
|
|
310
|
+
nc.dns.set_custom_nameservers("example.com", [
|
|
311
|
+
"ns1.cloudflare.com",
|
|
312
|
+
"ns2.cloudflare.com",
|
|
313
|
+
])
|
|
314
|
+
|
|
315
|
+
# Reset back to Namecheap BasicDNS
|
|
316
|
+
nc.dns.set_default_nameservers("example.com")
|
|
317
|
+
```
|
|
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
|
+
rules = nc.dns.get_email_forwarding("example.com")
|
|
342
|
+
for r in rules:
|
|
343
|
+
print(f"{r.mailbox} -> {r.forward_to}")
|
|
344
|
+
```
|
|
345
|
+
|
|
263
346
|
### Domain Management
|
|
264
347
|
|
|
265
348
|
```python
|
|
@@ -333,22 +416,24 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
|
|
|
333
416
|
nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
|
|
334
417
|
```
|
|
335
418
|
|
|
336
|
-
##
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
419
|
+
## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
|
|
420
|
+
|
|
421
|
+
| API | Status | Methods |
|
|
422
|
+
|-----|--------|---------|
|
|
423
|
+
| `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
|
|
424
|
+
| `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding` |
|
|
425
|
+
| `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
|
|
426
|
+
| `namecheap.domains.*` | 🚧 Planned | `getContacts`, `getTldList`, `reactivate` |
|
|
427
|
+
| `namecheap.domains.dns.*` | 🚧 Planned | `setEmailForwarding` |
|
|
428
|
+
| `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
|
|
429
|
+
| `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
|
|
430
|
+
| `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
|
|
431
|
+
| `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
|
|
432
|
+
| `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
|
|
348
433
|
|
|
349
434
|
## 🛠️ Development
|
|
350
435
|
|
|
351
|
-
See [
|
|
436
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
|
|
352
437
|
|
|
353
438
|
## 📝 License
|
|
354
439
|
|
|
@@ -357,3 +442,9 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
357
442
|
## 🤝 Contributing
|
|
358
443
|
|
|
359
444
|
Contributions are welcome! Please feel free to submit a Pull Request. See the [Development Guide](docs/dev/README.md) for setup instructions and guidelines.
|
|
445
|
+
|
|
446
|
+
### Contributors
|
|
447
|
+
|
|
448
|
+
- [@huntertur](https://github.com/huntertur) — Rich dependency fix
|
|
449
|
+
- [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
|
|
450
|
+
- [@cosmin](https://github.com/cosmin) — Nameserver management
|
|
@@ -12,16 +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
|
|
15
|
+
from .models import (
|
|
16
|
+
AccountBalance,
|
|
17
|
+
Contact,
|
|
18
|
+
DNSRecord,
|
|
19
|
+
Domain,
|
|
20
|
+
DomainCheck,
|
|
21
|
+
DomainInfo,
|
|
22
|
+
EmailForward,
|
|
23
|
+
Nameservers,
|
|
24
|
+
)
|
|
16
25
|
|
|
17
|
-
__version__ = "1.0
|
|
26
|
+
__version__ = "1.2.0"
|
|
18
27
|
__all__ = [
|
|
28
|
+
"AccountBalance",
|
|
19
29
|
"ConfigurationError",
|
|
20
30
|
"Contact",
|
|
21
31
|
"DNSRecord",
|
|
22
32
|
"Domain",
|
|
23
33
|
"DomainCheck",
|
|
34
|
+
"DomainInfo",
|
|
35
|
+
"EmailForward",
|
|
24
36
|
"Namecheap",
|
|
25
37
|
"NamecheapError",
|
|
38
|
+
"Nameservers",
|
|
26
39
|
"ValidationError",
|
|
27
40
|
]
|
|
@@ -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
|
|
9
|
+
from namecheap.models import DNSRecord, EmailForward, Nameservers
|
|
10
10
|
|
|
11
11
|
from .base import BaseAPI
|
|
12
12
|
|
|
@@ -381,3 +381,150 @@ class DnsAPI(BaseAPI):
|
|
|
381
381
|
>>> nc.dns.set("example.com", builder)
|
|
382
382
|
"""
|
|
383
383
|
return DNSRecordBuilder()
|
|
384
|
+
|
|
385
|
+
def set_custom_nameservers(self, domain: str, nameservers: list[str]) -> bool:
|
|
386
|
+
"""
|
|
387
|
+
Set custom nameservers for a domain.
|
|
388
|
+
|
|
389
|
+
This switches the domain from Namecheap's default DNS to custom
|
|
390
|
+
nameservers (e.g., Route 53, Cloudflare, etc.).
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
domain: Domain name
|
|
394
|
+
nameservers: List of nameserver hostnames (e.g., ["ns1.example.com", "ns2.example.com"])
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
True if successful
|
|
398
|
+
|
|
399
|
+
Examples:
|
|
400
|
+
>>> nc.dns.set_custom_nameservers("example.com", [
|
|
401
|
+
... "ns-123.awsdns-45.com",
|
|
402
|
+
... "ns-456.awsdns-67.net",
|
|
403
|
+
... "ns-789.awsdns-89.org",
|
|
404
|
+
... "ns-012.awsdns-12.co.uk",
|
|
405
|
+
... ])
|
|
406
|
+
"""
|
|
407
|
+
assert nameservers, "At least one nameserver is required"
|
|
408
|
+
assert len(nameservers) <= 5, "Maximum of 5 nameservers allowed"
|
|
409
|
+
|
|
410
|
+
ext = tldextract.extract(domain)
|
|
411
|
+
if not ext.domain or not ext.suffix:
|
|
412
|
+
raise ValueError(f"Invalid domain name: {domain}")
|
|
413
|
+
|
|
414
|
+
result: Any = self._request(
|
|
415
|
+
"namecheap.domains.dns.setCustom",
|
|
416
|
+
{
|
|
417
|
+
"SLD": ext.domain,
|
|
418
|
+
"TLD": ext.suffix,
|
|
419
|
+
"Nameservers": ",".join(nameservers),
|
|
420
|
+
},
|
|
421
|
+
path="DomainDNSSetCustomResult",
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
return bool(result and result.get("@Updated") == "true")
|
|
425
|
+
|
|
426
|
+
def set_default_nameservers(self, domain: str) -> bool:
|
|
427
|
+
"""
|
|
428
|
+
Reset domain to use Namecheap's default nameservers.
|
|
429
|
+
|
|
430
|
+
This switches the domain back to Namecheap BasicDNS from custom nameservers.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
domain: Domain name
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
True if successful
|
|
437
|
+
|
|
438
|
+
Examples:
|
|
439
|
+
>>> nc.dns.set_default_nameservers("example.com")
|
|
440
|
+
"""
|
|
441
|
+
ext = tldextract.extract(domain)
|
|
442
|
+
if not ext.domain or not ext.suffix:
|
|
443
|
+
raise ValueError(f"Invalid domain name: {domain}")
|
|
444
|
+
|
|
445
|
+
result: Any = self._request(
|
|
446
|
+
"namecheap.domains.dns.setDefault",
|
|
447
|
+
{
|
|
448
|
+
"SLD": ext.domain,
|
|
449
|
+
"TLD": ext.suffix,
|
|
450
|
+
},
|
|
451
|
+
path="DomainDNSSetDefaultResult",
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
return bool(result and result.get("@Updated") == "true")
|
|
455
|
+
|
|
456
|
+
def get_nameservers(self, domain: str) -> Nameservers:
|
|
457
|
+
"""
|
|
458
|
+
Get current nameservers for a domain.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
domain: Domain name
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
Nameservers with is_default flag and nameserver hostnames
|
|
465
|
+
|
|
466
|
+
Examples:
|
|
467
|
+
>>> ns = nc.dns.get_nameservers("example.com")
|
|
468
|
+
>>> ns.is_default
|
|
469
|
+
True
|
|
470
|
+
>>> ns.nameservers
|
|
471
|
+
['dns1.registrar-servers.com', 'dns2.registrar-servers.com']
|
|
472
|
+
"""
|
|
473
|
+
ext = tldextract.extract(domain)
|
|
474
|
+
if not ext.domain or not ext.suffix:
|
|
475
|
+
raise ValueError(f"Invalid domain name: {domain}")
|
|
476
|
+
|
|
477
|
+
result: Any = self._request(
|
|
478
|
+
"namecheap.domains.dns.getList",
|
|
479
|
+
{
|
|
480
|
+
"SLD": ext.domain,
|
|
481
|
+
"TLD": ext.suffix,
|
|
482
|
+
},
|
|
483
|
+
path="DomainDNSGetListResult",
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
assert result, f"API returned empty result for {domain} nameserver query"
|
|
487
|
+
|
|
488
|
+
is_default = result.get("@IsUsingOurDNS", "false").lower() == "true"
|
|
489
|
+
|
|
490
|
+
ns_data = result.get("Nameserver", [])
|
|
491
|
+
assert isinstance(ns_data, str | list), (
|
|
492
|
+
f"Unexpected Nameserver type: {type(ns_data)}"
|
|
493
|
+
)
|
|
494
|
+
nameservers = [ns_data] if isinstance(ns_data, str) else ns_data
|
|
495
|
+
|
|
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
|
+
]
|