Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61086e0b9b | ||
|
|
ce53920340 | ||
|
|
78c5c56220 | ||
|
|
873303661a | ||
|
|
0a8775dfc9 | ||
|
|
015eebd6ed | ||
|
|
3df8cfffc7 | ||
|
|
85a1c47c8c | ||
|
|
f91384173b | ||
|
|
f253115e62 | ||
|
|
75c96efc76 | ||
|
|
15a937e39c | ||
|
|
d1deaad03f |
@@ -9,3 +9,4 @@ coverage.xml
|
||||
build
|
||||
dist
|
||||
.pytest_cache
|
||||
words.txt
|
||||
|
||||
@@ -18,22 +18,22 @@ OPEN ?= xdg-open
|
||||
|
||||
.PHONY: help
|
||||
help: ## show this message
|
||||
@echo "Usage: $(MAKE) [target1] [target2] ..."
|
||||
@echo ""
|
||||
@echo "Commands/Targets:"
|
||||
@cat $(MAKEFILE_LIST) | grep -E '(^[a-zA-Z0-9_%-]+:.*?##.*$$)|(^##)' | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
|
||||
@echo ""
|
||||
@echo "Environment:"
|
||||
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z0-9_-]+\s*\??=.*$$' | grep -Eo '^[a-zA-Z0-9_-]+' | xargs -I {} $(MAKE) -s print-{} 2> /dev/null
|
||||
echo "Usage: $(MAKE) [target1] [target2] ..."
|
||||
echo ""
|
||||
echo "Commands/Targets:"
|
||||
cat $(MAKEFILE_LIST) | grep -E '(^[a-zA-Z0-9_%-]+:.*?##.*$$)|(^##)' | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
|
||||
echo ""
|
||||
echo "Environment:"
|
||||
cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z0-9_-]+\s*\??=.*$$' | grep -Eo '^[a-zA-Z0-9_-]+' | xargs -I {} $(MAKE) -s print-{} 2> /dev/null
|
||||
|
||||
.PHONY: print-%
|
||||
print-%:
|
||||
@echo -e '\033[32m$*\033[0m = $($*)'
|
||||
echo -e '\033[32m$*\033[0m = $($*)'
|
||||
|
||||
# FILES
|
||||
|
||||
.venv: uv.lock
|
||||
@$(MAKE) -s uv-sync
|
||||
$(MAKE) -s uv-sync
|
||||
|
||||
# ACTIONS
|
||||
|
||||
@@ -57,7 +57,7 @@ test: unittest ## test project
|
||||
|
||||
.PHONY: test-%
|
||||
test-%: ## test project with specific test
|
||||
@$(MAKE) -s unittest-$*
|
||||
$(MAKE) -s unittest-$*
|
||||
|
||||
.PHONY: coverage
|
||||
coverage: coverage-unittest coverage-xml coverage-report ## test project with coverage
|
||||
@@ -66,53 +66,61 @@ coverage: coverage-unittest coverage-xml coverage-report ## test project with co
|
||||
|
||||
.PHONY: uv-sync
|
||||
uv-sync: ## uv sync
|
||||
@$(UV) sync --active
|
||||
$(UV) sync --active
|
||||
|
||||
.PHONY: uv-update
|
||||
uv-upgrade: ## uv sync upgrade
|
||||
@$(UV) sync --active --upgrade
|
||||
$(UV) sync --active --upgrade
|
||||
|
||||
.PHONY: ruff
|
||||
ruff: .venv ## ruff check
|
||||
@$(RUFF) check
|
||||
$(RUFF) check
|
||||
|
||||
.PHONY: ruff-fix
|
||||
ruff-fix: .venv ## ruff check (and fix)
|
||||
@$(RUFF) check --fix --unsafe-fixes
|
||||
$(RUFF) check --fix --unsafe-fixes
|
||||
|
||||
.PHONY: ruff-format
|
||||
ruff-format: .venv ## ruff format
|
||||
@$(RUFF) format
|
||||
$(RUFF) format
|
||||
|
||||
.PHONY: ruff-format-check
|
||||
ruff-format-check: .venv ## ruff format (check only)
|
||||
@$(RUFF) format --check
|
||||
$(RUFF) format --check
|
||||
|
||||
.PHONY: ty
|
||||
ty: .venv ## ty check
|
||||
@$(TY) check
|
||||
$(TY) check
|
||||
|
||||
.PHONY: unittest
|
||||
unittest: .venv ## unittest
|
||||
@$(UNITTEST) -v
|
||||
$(UNITTEST) -v
|
||||
|
||||
.PHONY: unittest-%
|
||||
unittest-%: .venv ## unittest -k [filter]
|
||||
@$(UNITTEST) -v -k $*
|
||||
$(UNITTEST) -v -k $*
|
||||
|
||||
.PHONY: coverage-unittest
|
||||
coverage-unittest: .venv ## coverage run -m unittest
|
||||
@$(COVERAGE) run -m unittest -v
|
||||
$(COVERAGE) run -m unittest -v
|
||||
|
||||
.PHONY: coverage-report
|
||||
coverage-report: .venv ## coverage report
|
||||
@$(COVERAGE) report
|
||||
$(COVERAGE) report
|
||||
|
||||
.PHONY: coverage-html
|
||||
coverage-html: .venv ## coverage html
|
||||
@$(COVERAGE) html
|
||||
@$(OPEN) htmlcov/index.html || true
|
||||
$(COVERAGE) html
|
||||
$(OPEN) htmlcov/index.html || true
|
||||
|
||||
.PHONY: coverage-xml
|
||||
coverage-xml: .venv ## coverage xml
|
||||
@$(COVERAGE) xml
|
||||
$(COVERAGE) xml
|
||||
|
||||
.PHONY: release-%
|
||||
release-%: .venv ## release major/minor/patch
|
||||
$(UV) version --bump=$*
|
||||
git add pyproject.toml uv.lock
|
||||
git commit -m "chore: $$(uv version --short)"
|
||||
git tag "release/$$(uv version --short)" -m "$$(uv version)"
|
||||
$(UV) build
|
||||
|
||||
@@ -23,15 +23,18 @@ uvx --from git+https://git.klemek.fr/klemek/cool-domain cool-domain
|
||||
## Usage
|
||||
|
||||
```txt
|
||||
usage: cool-domain [-h] [--count DOMAIN_COUNT] [--lang {english,french}] [--words WORD_COUNT] [--dig | --no-dig] [--debug | --no-debug]
|
||||
[--quiet | --no-quiet] [--cache | --no-cache] [--cache-file CACHE_FILE] [--tld-list-uri TLD_LIST_URI]
|
||||
usage: cool-domain [-h] [--count DOMAIN_COUNT] [--words WORD_COUNT] [--lang {english,french,custom}] [--custom-words CUSTOM_WORDS]
|
||||
[--dig | --no-dig] [--debug | --no-debug] [--quiet | --no-quiet] [--cache | --no-cache] [--cache-file CACHE_FILE]
|
||||
[--tld-list-uri TLD_LIST_URI]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--count DOMAIN_COUNT number of domains to generate (default: 1)
|
||||
--lang {english,french}
|
||||
word list lang (default: english)
|
||||
--words WORD_COUNT number of words per domain (default: 1)
|
||||
--lang {english,french,custom}
|
||||
word list lang (default: english)
|
||||
--custom-words CUSTOM_WORDS
|
||||
custom word list location (default: words.txt)
|
||||
--dig, --no-dig check availability with dig (default: true)
|
||||
--debug, --no-debug
|
||||
--quiet, --no-quiet
|
||||
@@ -41,3 +44,45 @@ options:
|
||||
--tld-list-uri TLD_LIST_URI
|
||||
Top Level Domains list (default: https://publicsuffix.org/list/public_suffix_list.dat)
|
||||
```
|
||||
|
||||
# Development
|
||||
|
||||
## Updating `tld.txt`
|
||||
|
||||
Go to [tld-list.com](https://tld-list.com) and open console.
|
||||
|
||||
```js
|
||||
async function getValidTlds() {
|
||||
const delay = (millis) =>
|
||||
new Promise((resolve, reject) => {
|
||||
setTimeout((_) => {
|
||||
resolve();
|
||||
}, millis);
|
||||
});
|
||||
|
||||
const sourceData = await (
|
||||
await fetch("https://tld-list.com/tlds-from-a-z")
|
||||
).text();
|
||||
const regexp = /href="\/tld\/([\w\.]+)"/gm;
|
||||
const outTlds = [];
|
||||
let match;
|
||||
|
||||
while ((match = regexp.exec(sourceData)) !== null) {
|
||||
const tld = match[1];
|
||||
await delay(100);
|
||||
console.log(tld);
|
||||
const resp = await fetch(`https://tld-list.com/tld/${tld}`);
|
||||
if (resp.status === 200) {
|
||||
const content = await resp.text();
|
||||
if (!content.includes("Registrars (0)")) {
|
||||
outTlds.push(tld);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outTlds;
|
||||
}
|
||||
|
||||
const tlds = await getValidTlds();
|
||||
|
||||
console.log(tlds.join(','));
|
||||
```
|
||||
|
||||
+15
-10
@@ -1,23 +1,24 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from . import PKG_NAME, PKG_VERSION
|
||||
from .cache import Cache
|
||||
from .constants import LANGS
|
||||
from .factory import Factory
|
||||
from .logs import setup_logs
|
||||
from .params import parse_parameters
|
||||
from .tld_list import TLDList
|
||||
from cool_domain import PKG_NAME, PKG_VERSION
|
||||
from cool_domain.constants import LANGS
|
||||
from cool_domain.factory import Factory
|
||||
from cool_domain.logs import setup_logs
|
||||
from cool_domain.params import parse_parameters
|
||||
from cool_domain.tld_list import TLDList
|
||||
from cool_domain.word_list import WordList
|
||||
|
||||
|
||||
def main() -> int:
|
||||
params = parse_parameters(sys.argv[1:])
|
||||
setup_logs(params)
|
||||
logging.getLogger().info("%s %s", PKG_NAME, PKG_VERSION)
|
||||
langs = LANGS
|
||||
langs["custom"] = WordList("custom", params.custom_words)
|
||||
with (
|
||||
Cache(filepath=params.cache_file, no_load=params.cache) as cache,
|
||||
TLDList(cache=cache, remote_uri=params.tld_list_uri) as tld_list,
|
||||
LANGS[params.lang] as word_list,
|
||||
TLDList() as tld_list,
|
||||
langs[params.lang] as word_list,
|
||||
):
|
||||
factory = Factory(tld_list=tld_list, word_list=word_list, words=params.words)
|
||||
logging.getLogger().info(
|
||||
@@ -29,3 +30,7 @@ def main() -> int:
|
||||
else:
|
||||
logging.getLogger().info(factory.random_domain())
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import json
|
||||
import pathlib
|
||||
import typing
|
||||
|
||||
T = typing.TypeVar("T")
|
||||
|
||||
|
||||
class Cache:
|
||||
__slots__ = ["__data", "__filepath", "__no_load"]
|
||||
|
||||
def __init__(self, filepath: str, no_load: bool) -> None: # noqa: FBT001
|
||||
self.__filepath = pathlib.Path(filepath)
|
||||
self.__no_load = no_load
|
||||
self.__data: dict[str, typing.Any] | None = None
|
||||
|
||||
def load(self) -> None:
|
||||
if not self.__no_load and self.__filepath.exists():
|
||||
self.__data = json.loads(self.__filepath.read_text())
|
||||
else:
|
||||
self.__data = {}
|
||||
|
||||
def write(self) -> None:
|
||||
self.__filepath.write_text(json.dumps(self.__data))
|
||||
|
||||
def __enter__(self) -> typing.Self:
|
||||
self.load()
|
||||
return self
|
||||
|
||||
def __exit__(self, *_: object) -> None:
|
||||
self.write()
|
||||
|
||||
def has(self, key: str) -> bool:
|
||||
return self.__data is not None and key in self.__data
|
||||
|
||||
def get(self, key: str) -> typing.Any:
|
||||
if self.__data is not None and key in self.__data:
|
||||
return self.__data[key]
|
||||
return None
|
||||
|
||||
def set(self, key: str, value: typing.Any) -> None:
|
||||
if self.__data is not None:
|
||||
self.__data[key] = value
|
||||
self.write()
|
||||
|
||||
def get_or_set(self, key: str, callback: typing.Callable[[], T]) -> T:
|
||||
if not self.has(key):
|
||||
self.set(key, callback())
|
||||
return self.get(key)
|
||||
@@ -1,6 +1,6 @@
|
||||
from .word_list import WordList
|
||||
from cool_domain.word_list import WordList
|
||||
|
||||
LANGS: dict[str, WordList] = {
|
||||
"english": WordList("english", "english", "words_alpha.txt"),
|
||||
"french": WordList("french", "french", "francais.txt"),
|
||||
"english": WordList("english", "english.txt"),
|
||||
"french": WordList("french", "french.txt"),
|
||||
}
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../english/words_alpha.txt
|
||||
@@ -4,9 +4,8 @@ import shutil
|
||||
import subprocess
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .tld_list import TLDList
|
||||
from .word_list import WordList
|
||||
from cool_domain.tld_list import TLDList
|
||||
from cool_domain.word_list import WordList
|
||||
|
||||
|
||||
class FactoryError(Exception):
|
||||
@@ -14,7 +13,7 @@ class FactoryError(Exception):
|
||||
|
||||
|
||||
class Factory:
|
||||
__slots__ = ["__found", "__tld_list", "__word_list", "logger", "__words"]
|
||||
__slots__ = ["__found", "__tld_list", "__word_list", "__words", "logger"]
|
||||
|
||||
def __init__(self, tld_list: TLDList, word_list: WordList, words: int) -> None:
|
||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||
@@ -71,7 +70,7 @@ class Factory:
|
||||
tld_escaped = tld.replace(".", "")
|
||||
if word.endswith(tld_escaped) and len(word) > len(tld_escaped):
|
||||
domain = word[: -len(tld_escaped)] + "." + tld
|
||||
if domain not in self.__found:
|
||||
if "-." not in domain and domain not in self.__found:
|
||||
self.__found.add(domain)
|
||||
return domain
|
||||
return None
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../french/francais.txt
|
||||
+1
-2
@@ -2,8 +2,7 @@ import enum
|
||||
import logging
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .params import Parameters
|
||||
from cool_domain.params import Parameters
|
||||
|
||||
|
||||
class TermColor(enum.StrEnum):
|
||||
|
||||
+17
-34
@@ -1,12 +1,10 @@
|
||||
import argparse
|
||||
import dataclasses
|
||||
import os
|
||||
import pathlib
|
||||
import tempfile
|
||||
import typing
|
||||
|
||||
from . import PKG_NAME
|
||||
from .constants import LANGS
|
||||
from cool_domain import PKG_NAME
|
||||
from cool_domain.constants import LANGS
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True, slots=True)
|
||||
@@ -18,11 +16,10 @@ class Parameters:
|
||||
words: int = 1
|
||||
dig: bool = True
|
||||
lang: str = "english"
|
||||
cache_file: str = str(pathlib.Path(tempfile.gettempdir()) / ".cool-domain-cache")
|
||||
tld_list_uri: str = "https://publicsuffix.org/list/public_suffix_list.dat"
|
||||
custom_words: str = "words.txt"
|
||||
|
||||
@classmethod
|
||||
def from_namespace(cls, args: argparse.Namespace) -> Parameters:
|
||||
def from_namespace(cls, args: argparse.Namespace) -> "Parameters":
|
||||
return Parameters(**vars(args))
|
||||
|
||||
|
||||
@@ -90,7 +87,6 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
|
||||
default_values = Parameters()
|
||||
parser = argparse.ArgumentParser(
|
||||
PKG_NAME,
|
||||
suggest_on_error=True,
|
||||
)
|
||||
__add_arg_int(
|
||||
parser,
|
||||
@@ -99,12 +95,6 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
|
||||
default=default_values.count,
|
||||
help_txt="number of domains to generate",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lang",
|
||||
default=default_values.lang,
|
||||
help="word list lang (default: english)",
|
||||
choices=LANGS.keys(),
|
||||
)
|
||||
__add_arg_int(
|
||||
parser,
|
||||
"--words",
|
||||
@@ -112,6 +102,19 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
|
||||
default=default_values.words,
|
||||
help_txt="number of words per domain",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lang",
|
||||
default=default_values.lang,
|
||||
help="word list lang (default: english)",
|
||||
choices=[*list(LANGS.keys()), "custom"],
|
||||
)
|
||||
__add_arg_str(
|
||||
parser,
|
||||
"--custom-words",
|
||||
env_var="CUSTOM_WORDS",
|
||||
default=default_values.custom_words,
|
||||
help_txt="custom word list location",
|
||||
)
|
||||
__add_arg_bool(
|
||||
parser,
|
||||
"--dig",
|
||||
@@ -124,25 +127,5 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
|
||||
parser.add_argument(
|
||||
"--quiet", action=argparse.BooleanOptionalAction, default=default_values.quiet
|
||||
)
|
||||
__add_arg_bool(
|
||||
parser,
|
||||
"--cache",
|
||||
default=default_values.cache,
|
||||
help_txt="use cache",
|
||||
)
|
||||
__add_arg_str(
|
||||
parser,
|
||||
"--cache-file",
|
||||
env_var="CACHE_FILE",
|
||||
default=default_values.cache_file,
|
||||
help_txt="cache file location",
|
||||
)
|
||||
__add_arg_str(
|
||||
parser,
|
||||
"--tld-list-uri",
|
||||
env_var="TLD_LIST_URI",
|
||||
default=default_values.tld_list_uri,
|
||||
help_txt="Top Level Domains list",
|
||||
)
|
||||
parsed_args = parser.parse_args(args)
|
||||
return Parameters.from_namespace(parsed_args)
|
||||
|
||||
+2598
File diff suppressed because it is too large
Load Diff
+9
-34
@@ -1,54 +1,29 @@
|
||||
import http
|
||||
import logging
|
||||
import pathlib
|
||||
import typing
|
||||
|
||||
import requests
|
||||
import unidecode
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .cache import Cache
|
||||
|
||||
|
||||
class TLDError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TLDList:
|
||||
__slots__ = ["__cache", "__remote_uri", "logger", "tlds"]
|
||||
__slots__ = ["logger", "tlds"]
|
||||
|
||||
def __init__(self, cache: Cache, remote_uri: str) -> None:
|
||||
def __init__(self) -> None:
|
||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||
self.tlds: list[str] | None = None
|
||||
self.__cache = cache
|
||||
self.__remote_uri = remote_uri
|
||||
|
||||
def __load_remote(self) -> list[str]:
|
||||
response = requests.get(self.__remote_uri, timeout=5)
|
||||
if response.status_code != http.HTTPStatus.OK:
|
||||
msg = f"Failed to read {self.__remote_uri}."
|
||||
raise TLDError(msg)
|
||||
raw_lines = response.content.decode().split("\n")
|
||||
def __load_file(self) -> list[str]:
|
||||
out_lines = []
|
||||
for line in raw_lines:
|
||||
stripped_line = line.strip()
|
||||
if (
|
||||
len(stripped_line)
|
||||
and not stripped_line.startswith("//")
|
||||
and not stripped_line.startswith("*")
|
||||
and unidecode.unidecode(stripped_line) == stripped_line
|
||||
):
|
||||
if "." in stripped_line:
|
||||
for other in out_lines:
|
||||
if stripped_line.endswith(other):
|
||||
break
|
||||
else:
|
||||
out_lines += [stripped_line]
|
||||
else:
|
||||
out_lines += [stripped_line]
|
||||
with (pathlib.Path(__file__).parent / "tld.txt").open() as file:
|
||||
for line in file:
|
||||
if len(line.strip()):
|
||||
out_lines += [line.strip()]
|
||||
return out_lines
|
||||
|
||||
def load(self) -> None:
|
||||
self.tlds = self.__cache.get_or_set("tld_list", self.__load_remote)
|
||||
self.tlds = self.__load_file()
|
||||
self.logger.info("Loaded %d TLD entries", len(self.tlds))
|
||||
|
||||
def __enter__(self) -> typing.Self:
|
||||
|
||||
@@ -8,10 +8,10 @@ import unidecode
|
||||
class WordList:
|
||||
__slots__ = ["__filepath", "__name", "logger", "words"]
|
||||
|
||||
def __init__(self, name: str, path: str, filename: str) -> None:
|
||||
def __init__(self, name: str, path: str) -> None:
|
||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||
self.__name = name
|
||||
self.__filepath = pathlib.Path(__file__).parent / ".." / path / filename
|
||||
self.__filepath = pathlib.Path(__file__).parent / path
|
||||
self.words: list[str] = []
|
||||
|
||||
def open(self) -> None:
|
||||
@@ -22,7 +22,10 @@ class WordList:
|
||||
self.logger.info("[%s] Loaded %d words", self.__name, len(self.words))
|
||||
|
||||
def __escape_word(self, word: str) -> str:
|
||||
return unidecode.unidecode(word.strip()).lower()
|
||||
word = unidecode.unidecode(word.strip()).lower()
|
||||
if " " in word:
|
||||
return ""
|
||||
return word
|
||||
|
||||
def close(self) -> None:
|
||||
pass
|
||||
|
||||
+2
-3
@@ -1,11 +1,10 @@
|
||||
[project]
|
||||
name = "cool-domain"
|
||||
version = "1.1.0"
|
||||
version = "1.4.1"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"requests>=2.34.2,<3.0.0",
|
||||
"unidecode>=1.4.0,<2.0.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,63 +1,12 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.14"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.5.20"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "cool-domain"
|
||||
version = "1.1.0"
|
||||
version = "1.4.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "requests" },
|
||||
{ name = "unidecode" },
|
||||
]
|
||||
|
||||
@@ -68,10 +17,7 @@ dev = [
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "requests", specifier = ">=2.34.2,<3.0.0" },
|
||||
{ name = "unidecode", specifier = ">=1.4.0,<2.0.0" },
|
||||
]
|
||||
requires-dist = [{ name = "unidecode", specifier = ">=1.4.0,<2.0.0" }]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
@@ -79,30 +25,6 @@ dev = [
|
||||
{ name = "ty", specifier = ">=0.0.34,<0.0.35" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.34.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.12"
|
||||
@@ -160,12 +82,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/94/7d/a8a765761bbc0c836
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/b7/559f59d57d18b44c6d1250d2eeaa676e028b9c527431f5d0736478a73ba1/Unidecode-1.4.0-py3-none-any.whl", hash = "sha256:c3c7606c27503ad8d501270406e345ddb480a7b5f38827eafe4fa82a137f0021", size = 235837, upload-time = "2025-04-24T08:45:01.609Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user