Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
fab-dlock committed Aug 14, 2023
1 parent acd8989 commit 9391a2c
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 141 deletions.
39 changes: 36 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ env:
POETRY_CACHE: /opt/poetry_cache

jobs:
lint:
lint_and_test:
runs-on: ubuntu-22.04
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Set up Python 3.9
- name: Set up Python 3.7
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: 3.8
- name: Cache deps install
id: cache-deps
uses: actions/cache@v3
Expand Down Expand Up @@ -53,3 +53,36 @@ jobs:
- name: Run test
run: |
poetry run make test
- name: Make apidocs
run: |
poetry run make apidoc
- name: Upload API doc as an artifact
uses: actions/upload-artifact@v3
with:
name: apidoc
path: apihtml/

githubpages:
runs-on: ubuntu-latest
needs: lint_and_test
#permissions:
# contents: read
# pages: write
# id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Download API doc artifact
uses: actions/download-artifact@v3
with:
name: apidoc
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/.vscode
/apihtml
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,17 @@ else
pytest --no-cov-on-fail --cov=distributed_lock --cov-report=term --cov-report=html --cov-report=xml tests
endif

.PHONY: apidoc
apidoc:
@rm -Rf apihtml
pdoc -d google -o apihtml distributed_lock

.PHONY: clean
clean:
rm -Rf apihtml htmlcov
rm -Rf .mypy_cache .ruff_cache .pytest_cache
find . -type d -name __pycache__ -exec rm -Rf {} \; 2>/dev/null || true

.PHONY: bump_version
bump_version:
python ./bump_version.py
23 changes: 23 additions & 0 deletions bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

import os

from dunamai import Style, Version

version = Version.from_git().serialize(style=Style.SemVer)

with open("distributed_lock/__init__.py") as f:
c = f.read()

lines = []
for line in c.splitlines():
if line.startswith("VERSION = "):
lines.append(f'VERSION = "{version}"')
else:
lines.append(line)

with open("distributed_lock/__init__.py", "w") as g:
g.write("\n".join(lines))

print(f"Setting version={version}")
os.system(f"poetry version {version}")
22 changes: 12 additions & 10 deletions distributed_lock/__init__.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
from __future__ import annotations

from distributed_lock.const import DEFAULT_CLUSTER, DEFAULT_LIFETIME, DEFAULT_WAIT
from distributed_lock.common import AcquiredRessource
from distributed_lock.const import (
DEFAULT_CLUSTER,
DEFAULT_LIFETIME,
DEFAULT_SERVER_SIDE_WAIT,
)
from distributed_lock.exception import (
BadConfigurationError,
DistributedLockError,
DistributedLockException,
NotAcquiredError,
NotAcquiredException,
NotReleasedError,
NotReleasedException,
)
from distributed_lock.sync import AcquiredRessource, DistributedLockClient
from distributed_lock.sync import DistributedLockClient

__all__ = [
"DEFAULT_CLUSTER",
"DEFAULT_LIFETIME",
"DEFAULT_WAIT",
"DEFAULT_SERVER_SIDE_WAIT",
"AcquiredRessource",
"DistributedLockClient",
"DistributedlockException",
"NotAcquiredError",
"NotReleasedException",
"NotReleasedError",
"NotAcquiredException",
"BadConfigurationError",
"DistributedLockError",
"DistributedLockException",
]

__pdoc__ = {"sync": False, "exception": False, "common": False}

VERSION = "v0.0.0"
93 changes: 93 additions & 0 deletions distributed_lock/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from __future__ import annotations

import datetime
import os
from dataclasses import asdict, dataclass, field
from typing import Any

from distributed_lock.const import DEFAULT_CLUSTER
from distributed_lock.exception import BadConfigurationError, DistributedLockError


@dataclass
class AcquiredRessource:
"""This dataclass holds an acquired ressource."""

resource: str
"""The resource name."""

lock_id: str
"""The lock unique identifier (you will need it to unlock)."""

tenant_id: str
"""The tenant identifier."""

created: datetime.datetime = field(default_factory=datetime.datetime.utcnow)
"""The lock creation datetime."""

expires: datetime.datetime = field(default_factory=datetime.datetime.utcnow)
"""The lock expiration datetime."""

user_agent: str = ""
"""Your user-agent (warning: not supported in all plans)."""

user_data: Any = ""
"""User data stored with the lock (warning: not supported in all plans)."""

@classmethod
def from_dict(cls, d: dict) -> AcquiredRessource:
"""Create an AcquiredRessource from a dict."""
for f in (
"lock_id",
"resource",
"tenant_id",
"created",
"expires",
"user_agent",
"user_data",
):
if f not in d:
raise DistributedLockError(f"bad reply from service, missing {f}")
d2 = dict(d)
for f in ("created", "expires"):
if isinstance(d2[f], str):
d2[f] = datetime.datetime.fromisoformat(d2[f])
return cls(**d2)

def to_dict(self) -> dict:
"""Convert an AcquiredRessource to a dict."""
d = asdict(self)
for f in ("created", "expires"):
d[f] = d[f].isoformat()[0:19] + "Z"
return d


def get_cluster() -> str:
"""Get the target cluster from env or from default."""
if os.environ.get("DLOCK_CLUSTER"):
return os.environ["DLOCK_CLUSTER"].lower().strip()
return DEFAULT_CLUSTER


def get_token() -> str:
"""Get the service token from env (raise an exception if not set).
Raises:
BadConfigurationError: if the token is not set.
"""
if os.environ.get("DLOCK_TOKEN"):
return os.environ["DLOCK_TOKEN"].lower().strip()
raise BadConfigurationError("You must provide a token (or set DLOCK_TOKEN env var)")


def get_tenant_id() -> str:
"""Get the "tenant id" from env (raise an exception if not set).
Raises:
BadConfigurationError: if the "tenant id" is not set.
"""
if os.environ.get("DLOCK_TENANT_ID"):
return os.environ["DLOCK_TENANT_ID"].lower().strip()
raise BadConfigurationError(
"You must provide a tenant_id (or set DLOCK_TENANT_ID env var)"
)
7 changes: 6 additions & 1 deletion distributed_lock/const.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from __future__ import annotations

DEFAULT_CLUSTER = "europe-free"
"""Default cluster to request."""

DEFAULT_LIFETIME = 3600
DEFAULT_WAIT = 10
"""Default lock lifetime (in seconds)."""

DEFAULT_SERVER_SIDE_WAIT = 60
"""Default server side wait (in seconds)."""
16 changes: 12 additions & 4 deletions distributed_lock/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,36 @@


class DistributedLockException(Exception):
"""Base class for lock exceptions."""

pass


class DistributedLockError(DistributedLockException):
class DistributedLockError(Exception):
"""Base class for lock errors."""

pass


class BadConfigurationError(DistributedLockError):
"""Bad configuration."""

pass


class NotAcquiredException(DistributedLockException):
pass
"""Not acquired because the lock is still held by someone else."""


class NotReleasedException(DistributedLockException):
pass


class NotReleasedError(DistributedLockError):
"""Not released lock because some errors popped during the lock release."""

pass


class NotAcquiredError(DistributedLockError):
"""Not acquired because some errors popped during the lock acquisition."""

pass
Empty file added distributed_lock/py.typed
Empty file.
Loading

0 comments on commit 9391a2c

Please sign in to comment.