Skip to content

Commit

Permalink
Update packages and support python 3.12 (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
wgordon17 authored Apr 16, 2024
2 parents 3c9d210 + 3804819 commit d9cbdbe
Show file tree
Hide file tree
Showing 6 changed files with 928 additions and 699 deletions.
4 changes: 3 additions & 1 deletion local/tests/test_collision.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from collections import Counter
from functools import reduce
from math import ceil, floor
Expand Down Expand Up @@ -42,7 +44,7 @@ def _id_to_int(value: str, radix: int = str_base) -> int:

bucket_length: int = ceil(str_base**23 / bucket_count)

for _ in range(0, max_ids):
for _ in range(max_ids):
uid: str = cuid()
result["ids"].add(uid)
result["id_histogram"][_id_to_int(uid[1:]) // bucket_length] += 1
Expand Down
1,523 changes: 873 additions & 650 deletions pdm.lock

Large diffs are not rendered by default.

74 changes: 37 additions & 37 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,46 +35,47 @@ source = "scm"

[tool.pdm.scripts]
# Default to linting the entire src/ directory, but allow overriding with specific files
black = "black {args:src/ local/tests/}"
ruff = "ruff check --fix --exit-zero {args:src/ local/tests/}"
ruff-check = "ruff check --fix --exit-zero {args:src/ local/tests/}"
ruff-format = "ruff format {args:src/ local/tests/}"
spelling = "codespell {args:src/ local/tests/}"
pylint = "pylint {args:src/ local/tests/}"
safety = "safety {args:check --bare}"
typing = "mypy {args:src/ local/tests/}"
lint-fast = {composite = ["black", "ruff"]}
lint-full = {composite = ["lint-fast", "spelling", "pylint", "typing", "safety"]}
update-pip = "pip install --upgrade pip"
ruff-fix = {composite = ["ruff-check", "ruff-format"]}
lint-full = {composite = ["ruff-fix", "spelling", "pylint", "typing", "safety"]}
tox-full = {composite = ["update-pip", "lint-full"]}
testing = "pytest local/tests"
testing-slow = "pytest local/tests --runslow"
tox = "tox --parallel auto"

[tool.pdm.dev-dependencies]
lint = [
"black~=23.3.0", # https://github.com/psf/black (latest: 23.3.0)
"codespell~=2.2.5", # https://github.com/codespell-project/codespell (latest: 2.2.5)
"pylint~=2.17.4", # https://github.com/PyCQA/pylint (latest: 2.17.4)
"requests>=2.31.0", # https://github.com/psf/requests (latest: 2.31.0)
"ruff~=0.0.275", # https://github.com/charliermarsh/ruff (latest: 0.0.275)
"safety==2.4.0b1", # https://github.com/pyupio/safety (latest: 2.3.5)
"codespell~=2.2.6", # https://github.com/codespell-project/codespell (latest: 2.2.6)
"pylint~=3.1.0", # https://github.com/pylint-dev/pylint (latest: 3.1.0)
"requests>=2.31.0", # https://github.com/psf/requests (latest: 2.31.0)
"ruff~=0.3.7", # https://github.com/astral-sh/ruff (latest: 0.3.7)
"safety==3.1.0", # https://github.com/pyupio/safety (latest: 3.1.0)
]
test = [
"pytest~=7.4.0", # https://github.com/pytest-dev/pytest (latest: 7.4.0)
"pytest-mock~=3.11.1", # https://github.com/pytest-dev/pytest-mock/ (latest: 3.11.1)
"pytest-sugar~=0.9.7", # https://github.com/Teemu/pytest-sugar/ (latest: 0.9.7)
"pytest~=8.1.1", # https://github.com/pytest-dev/pytest (latest: 8.1.1)
"pytest-mock~=3.14.0", # https://github.com/pytest-dev/pytest-mock/ (latest: 3.14.0)
"pytest-sugar~=1.0.0", # https://github.com/Teemu/pytest-sugar (latest: 1.0.0)
]
tox = [
# Version reduced to prevent `packaging` conflict with safety
"tox~=4.4.12", # https://github.com/tox-dev/tox (latest: 4.6.3)
"tox-pdm~=0.6.1", # https://github.com/pdm-project/tox-pdm (latest: 0.6.1)
"tox~=4.14.2", # https://github.com/tox-dev/tox (latest: 4.14.2)
"tox-pdm~=0.7.2", # https://github.com/pdm-project/tox-pdm (latest: 0.7.2)
]
typing = [
"mypy~=1.4.1", # https://github.com/python/mypy (latest: 1.4.1)
"mypy~=1.9.0", # https://github.com/python/mypy (latest: 1.9.0)
]

[tool.tox]
legacy_tox_ini = """
[tox]
min_version = 4
env_list = py3{8,9,10,11}, check
env_list = py3{8,9,10,11,12}, check
work_dir = local/.tox
isolated_build = True
Expand All @@ -87,54 +88,53 @@ legacy_tox_ini = """
description = run linters and typing
skip_install = true
groups = lint, typing, test
commands = lint-full
commands = tox-full
"""

[tool.black]
line-length = 120
target_version = ["py38"]

[tool.ruff]
line-length = 120
src = ["src"]
target-version = "py38"
cache-dir = "local/.ruff_cache"
# "E", "F" already included from `select`
# https://beta.ruff.rs/docs/rules
extend-select = [
"W", "C90", "I", "N", "UP", "S", "BLE",
"B", "A", "COM", "C4", "DTZ", "T10", "EM",
"ISC", "ICN", "G", "INP", "PIE", "T20", "PT",
"Q", "RSE", "RET", "SLF", "SIM", "INT", "ARG",
"PTH", "PGH", "PL", "TRY", "RUF", "D", "ANN",
"PYI", "TCH", "ERA",
force-exclude = true

[tool.ruff.lint]
# https://docs.astral.sh/ruff/rules/
select = [
"F", "E", "W", "C90", "I", "N", "ASYNC",
"TRIO", "S", "BLE", "B", "A", "COM", "C4",
"DTZ", "T10", "EM", "FA", "ISC", "ICN",
"G", "INP", "PIE", "T20", "PT", "Q", "RSE",
"RET", "SLF", "SLOT", "SIM", "INT", "ARG",
"PTH", "PGH", "PL", "TRY", "FLY", "PERF",
"LOG", "RUF",
] # purposely not including "DJ", "YTT", "EXE", "PD", "NPY", "FBT" (boolean checking)
extend-ignore = [
ignore = [
"D100", # Missing docstring in public module
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
"D104", # Missing docstring in public package
"D205", # Blank line required between summary line and description
"D401", # First line should be in imperative mood
"COM812", "ISC001", # Conflict with formatter
]
# Don't automatically remove `print`
# Stop automatically removing unused imports
unfixable = ["T201", "F401", "F841"]
force-exclude = true

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
# Ignore `assert` in test files (S101), magic values (PLR2004), and private member accessed (SLF001)
"test_*.py" = ["S101", "PLR2004", "SLF001"]

[tool.ruff.isort]
[tool.ruff.lint.isort]
known-first-party = ["src"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]

[tool.ruff.pydocstyle]
[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.pytest.ini_options]
minversion = "7.3"
minversion = "8.1"
cache_dir = "local/.pytest_cache"
python_files = "test_*.py"

Expand Down
1 change: 1 addition & 0 deletions src/cuid2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Next generation GUIDs. Collision-resistant ids optimized for horizontal scaling and performance."""

from .generator import DEFAULT_LENGTH, INITIAL_COUNT_MAX, Cuid, cuid_wrapper

__all__ = ["Cuid", "DEFAULT_LENGTH", "INITIAL_COUNT_MAX", "cuid_wrapper"]
15 changes: 8 additions & 7 deletions src/cuid2/generator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import time
from math import floor
from secrets import SystemRandom
Expand All @@ -9,8 +11,7 @@
from _random import Random

class FingerprintCallable(Protocol): # pylint: disable=too-few-public-methods
def __call__(self: "FingerprintCallable", random_generator: Random) -> str:
...
def __call__(self: FingerprintCallable, random_generator: Random) -> str: ...


# ~22k hosts before 50% chance of initial counter collision
Expand All @@ -22,11 +23,11 @@ def __call__(self: "FingerprintCallable", random_generator: Random) -> str:

class Cuid: # pylint: disable=too-few-public-methods
def __init__(
self: "Cuid",
random_generator: Callable[[], "Random"] = SystemRandom,
self: Cuid,
random_generator: Callable[[], Random] = SystemRandom,
counter: Callable[[int], Callable[[], int]] = utils.create_counter,
length: int = DEFAULT_LENGTH,
fingerprint: "FingerprintCallable" = utils.create_fingerprint,
fingerprint: FingerprintCallable = utils.create_fingerprint,
) -> None:
"""Initialization function for the Cuid class that generates a universally unique,
base36 encoded string.
Expand Down Expand Up @@ -55,12 +56,12 @@ def __init__(
msg = "Length must never exceed 98 characters."
raise ValueError(msg)

self._random: "Random" = random_generator()
self._random: Random = random_generator()
self._counter: Callable[[], int] = counter(floor(self._random.random() * INITIAL_COUNT_MAX))
self._length: int = length
self._fingerprint: str = fingerprint(random_generator=self._random)

def generate(self: "Cuid", length: Optional[int] = None) -> str:
def generate(self: Cuid, length: Optional[int] = None) -> str:
"""Generates a universally unique, base36 encoded string with a specified length.
Parameters
Expand Down
10 changes: 6 additions & 4 deletions src/cuid2/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import os
import socket
import string
Expand Down Expand Up @@ -46,7 +48,7 @@ def counter() -> int:
return counter


def create_fingerprint(random_generator: "Random", fingerprint_data: Optional[str] = "") -> str:
def create_fingerprint(random_generator: Random, fingerprint_data: Optional[str] = "") -> str:
"""Creates a fingerprint, by default combining process ID, hostname, and environment variables
with entropy and then hashing the result.
Expand Down Expand Up @@ -76,7 +78,7 @@ def create_fingerprint(random_generator: "Random", fingerprint_data: Optional[st
return create_hash(fingerprint)[0:BIG_LENGTH]


def create_entropy(random_generator: "Random", length: int = 4) -> str:
def create_entropy(random_generator: Random, length: int = 4) -> str:
"""Creates a random string of specified length using a base36 encoding.
Parameters
Expand Down Expand Up @@ -126,14 +128,14 @@ def create_hash(data: str = "") -> str:
Base36 encoding of the SHA-512 hash of the input string `data`, with the first character dropped.
"""
hashed_value: "_Hash" = sha512(data.encode())
hashed_value: _Hash = sha512(data.encode())
hashed_int: int = int.from_bytes(hashed_value.digest(), byteorder="big")

# Drop the first character because it will bias the histogram to the left.
return base36_encode(hashed_int)[1:]


def create_letter(random_generator: "Random") -> str:
def create_letter(random_generator: Random) -> str:
"""Generates a random lowercase letter using a given random number generator.
Parameters
Expand Down

0 comments on commit d9cbdbe

Please sign in to comment.