Skip to content

Commit c2b103d

Browse files
committed
chore: use uv to manage project, ruff to lint code
1 parent c1410cf commit c2b103d

File tree

13 files changed

+133
-76
lines changed

13 files changed

+133
-76
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ jobs:
2222
python-version: "3.8"
2323

2424
- name: Install dependencies
25-
run: pip install -r requirements.txt
25+
run: pip install -r requirements-dev.lock
2626

27-
- name: flake8 lint
28-
run: flake8 src/otpauth/
27+
- name: ruff lint
28+
run: ruff check
2929

3030
- name: mypy lint
3131
run: mypy

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,5 @@ cython_debug/
161161
# and can be added to the global gitignore or merged into this file. For a more nuclear
162162
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
163163
#.idea/
164+
165+
uv.lock

docs/conf.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import otpauth
22

3-
project = 'OTP Auth'
4-
copyright = '2013, Hsiaoming Yang'
5-
author = 'Hsiaoming Yang'
3+
project = "OTP Auth"
4+
copyright = "2013, Hsiaoming Yang"
5+
author = "Hsiaoming Yang"
66

7-
master_doc = 'index'
7+
master_doc = "index"
88

99
# The full version, including alpha/beta/rc tags
1010
version = otpauth.__version__
@@ -32,12 +32,12 @@
3232
sitemap_url_scheme = "{link}"
3333

3434
# Add any paths that contain templates here, relative to this directory.
35-
templates_path = ['_templates']
35+
templates_path = ["_templates"]
3636

3737
# List of patterns, relative to source directory, that match files and
3838
# directories to ignore when looking for source files.
3939
# This pattern also affects html_static_path and html_extra_path.
40-
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
40+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
4141

4242
html_static_path = ["_static"]
4343

@@ -47,41 +47,33 @@
4747
# The theme to use for HTML and HTML Help pages. See the documentation for
4848
# a list of builtin themes.
4949
#
50-
html_theme = 'shibuya'
50+
html_theme = "shibuya"
5151
html_theme_options = {
5252
"accent_color": "blue",
5353
"light_logo": "_static/light-logo.svg",
5454
"dark_logo": "_static/dark-logo.svg",
55-
'twitter_site': 'authlib',
56-
'twitter_creator': 'lepture',
57-
'twitter_url': 'https://twitter.com/authlib',
58-
'github_url': 'https://github.com/authlib/otpauth',
59-
'discord_url': 'https://discord.gg/HvBVAeNAaV',
55+
"twitter_site": "authlib",
56+
"twitter_creator": "lepture",
57+
"twitter_url": "https://twitter.com/authlib",
58+
"github_url": "https://github.com/authlib/otpauth",
59+
"discord_url": "https://discord.gg/HvBVAeNAaV",
6060
"carbon_ads_code": "CE7DKK3W",
6161
"carbon_ads_placement": "otpauthliborg",
6262
"nav_links": [
6363
{
6464
"title": "Projects",
6565
"children": [
66-
{
67-
"title": "Authlib",
68-
"url": "https://authlib.org/",
69-
"summary": "OAuth, JOSE, OpenID, etc."
70-
},
71-
{
72-
"title": "JOSE RFC",
73-
"url": "https://jose.authlib.org/",
74-
"summary": "JWS, JWE, JWK, and JWT."
75-
},
66+
{"title": "Authlib", "url": "https://authlib.org/", "summary": "OAuth, JOSE, OpenID, etc."},
67+
{"title": "JOSE RFC", "url": "https://jose.authlib.org/", "summary": "JWS, JWE, JWK, and JWT."},
7668
{
7769
"title": "OTP Auth",
7870
"url": "https://otp.authlib.org/",
7971
"summary": "One time password, HOTP/TOTP.",
8072
},
81-
]
73+
],
8274
},
8375
{"title": "Sponsor me", "url": "https://github.com/sponsors/authlib"},
84-
]
76+
],
8577
}
8678

8779
# Add any paths that contain custom static files (such as style sheets) here,

pyproject.toml

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,25 @@ classifiers = [
3030
]
3131

3232
[project.urls]
33+
Source = "https://github.com/authlib/otpauth"
3334
Documentation = "https://otp.authlib.org/"
3435
Donate = "https://github.com/sponsors/authlib"
3536
Blog = "https://blog.authlib.org/"
3637

3738
[build-system]
38-
requires = ["setuptools"]
39-
build-backend = "setuptools.build_meta"
39+
requires = ["hatchling"]
40+
build-backend = "hatchling.build"
4041

41-
[tool.setuptools.dynamic]
42-
version = {attr = "otpauth.__version__"}
43-
44-
[tool.setuptools.packages.find]
45-
where = ["src"]
42+
[dependency-groups]
43+
dev = [
44+
"mypy>=1.4.1",
45+
"pytest>=7.4.4",
46+
"pytest-cov>=4.1.0",
47+
"ruff>=0.9.9",
48+
]
4649

47-
[tool.setuptools.package-data]
48-
"otpauth" = ["py.typed"]
50+
[tool.hatch.version]
51+
path = "src/otpauth/__init__.py"
4952

5053
[tool.pytest.ini_options]
5154
pythonpath = ["src"]
@@ -72,3 +75,25 @@ python_version = "3.8"
7275
files = ["src/otpauth"]
7376
show_error_codes = true
7477
pretty = true
78+
79+
[tool.ruff]
80+
line-length = 120
81+
82+
[tool.ruff.lint]
83+
select = [
84+
"B", # flake8-bugbear
85+
"E", # pycodestyle
86+
"F", # pyflakes
87+
"I", # isort
88+
"UP", # pyupgrade
89+
]
90+
ignore = [
91+
"E501", # line-too-long
92+
"E722", # bare-except
93+
]
94+
95+
[tool.ruff.lint.isort]
96+
force-single-line = true
97+
98+
[tool.ruff.format]
99+
docstring-code-format = true

requirements-dev.lock

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# This file was autogenerated by uv via the following command:
2+
# uv export --no-hashes -o requirements-dev.lock
3+
-e .
4+
colorama==0.4.6 ; sys_platform == 'win32'
5+
coverage==7.2.7 ; python_full_version < '3.8'
6+
coverage==7.6.1 ; python_full_version == '3.8.*'
7+
coverage==7.6.12 ; python_full_version >= '3.9'
8+
exceptiongroup==1.2.2 ; python_full_version < '3.11'
9+
importlib-metadata==6.7.0 ; python_full_version < '3.8'
10+
iniconfig==2.0.0
11+
mypy==1.4.1 ; python_full_version < '3.8'
12+
mypy==1.14.1 ; python_full_version == '3.8.*'
13+
mypy==1.15.0 ; python_full_version >= '3.9'
14+
mypy-extensions==1.0.0
15+
packaging==24.0 ; python_full_version < '3.8'
16+
packaging==24.2 ; python_full_version >= '3.8'
17+
pluggy==1.2.0 ; python_full_version < '3.8'
18+
pluggy==1.5.0 ; python_full_version >= '3.8'
19+
pytest==7.4.4 ; python_full_version < '3.8'
20+
pytest==8.3.4 ; python_full_version >= '3.8'
21+
pytest-cov==4.1.0 ; python_full_version < '3.8'
22+
pytest-cov==5.0.0 ; python_full_version == '3.8.*'
23+
pytest-cov==6.0.0 ; python_full_version >= '3.9'
24+
ruff==0.9.9
25+
tomli==2.0.1 ; python_full_version < '3.8'
26+
tomli==2.2.1 ; python_full_version >= '3.8' and python_full_version <= '3.11'
27+
typed-ast==1.5.5 ; python_full_version < '3.8'
28+
typing-extensions==4.7.1
29+
zipp==3.15.0 ; python_full_version < '3.8'

requirements.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/otpauth/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from ._rfc4226 import HOTP
2+
from ._rfc4226 import generate_hotp
3+
from ._rfc6238 import TOTP
4+
from ._rfc6238 import generate_totp
15
from .core import SupportedAlgorithms
2-
from ._rfc4226 import HOTP, generate_hotp
3-
from ._rfc6238 import TOTP, generate_totp
4-
56

67
__author__ = "Hsiaoming Yang <[email protected]>"
78
__homepage__ = "https://otp.authlib.org/"

src/otpauth/_rfc4226.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import typing as t
2-
import struct
3-
import hmac
41
import hashlib
5-
from .core import OTP, SupportedAlgorithms
2+
import hmac
3+
import struct
4+
import typing as t
5+
6+
from .core import OTP
7+
from .core import SupportedAlgorithms
68

79

810
class HOTP(OTP):
@@ -27,7 +29,7 @@ def generate(self, counter: int) -> int:
2729
"""
2830
return generate_hotp(self.secret, counter, self.digit, self.algorithm)
2931

30-
def verify(self, code: int, counter: int) -> bool: # type: ignore[override]
32+
def verify(self, code: int, counter: int) -> bool:
3133
"""Valid a HOTP code at the given counter.
3234
3335
:param code: A number to be verified.
@@ -37,7 +39,7 @@ def verify(self, code: int, counter: int) -> bool: # type: ignore[override]
3739
return False
3840
return hmac.compare_digest(self.string_code(self.generate(counter)), self.string_code(code))
3941

40-
def to_uri(self, label: str, issuer: str, counter: int) -> str: # type: ignore[override]
42+
def to_uri(self, label: str, issuer: str, counter: int) -> str:
4143
"""Generate the otpauth protocal string for HOTP.
4244
4345
:param label: Label of the identifier.
@@ -60,7 +62,7 @@ def generate_hotp(secret: bytes, counter: int, digit: int = 6, algorithm: Suppor
6062
msg = struct.pack(">Q", counter)
6163
digest = hmac.new(secret, msg, hash_alg).digest()
6264
offset = digest[19] & 0xF
63-
bin_code: int = struct.unpack(">I", digest[offset: offset + 4])[0]
65+
bin_code: int = struct.unpack(">I", digest[offset : offset + 4])[0]
6466
total: int = bin_code & 0x7FFFFFFF
65-
power: int = 10 ** digit
67+
power: int = 10**digit
6668
return total % power

src/otpauth/_rfc6238.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import typing as t
2-
import time
31
import hmac
4-
from .core import OTP, SupportedAlgorithms
2+
import time
3+
import typing as t
4+
55
from ._rfc4226 import generate_hotp
6+
from .core import OTP
7+
from .core import SupportedAlgorithms
68

79

810
class TOTP(OTP):
@@ -32,7 +34,7 @@ def generate(self, timestamp: t.Optional[int] = None) -> int:
3234
"""
3335
return generate_totp(self.secret, self.period, timestamp, self.digit, self.algorithm)
3436

35-
def verify(self, code: int, timestamp: t.Optional[int] = None) -> bool: # type: ignore[override]
37+
def verify(self, code: int, timestamp: t.Optional[int] = None) -> bool:
3638
"""Valid a TOTP code for the given timestamp.
3739
3840
:param code: A number to be verified.
@@ -42,7 +44,7 @@ def verify(self, code: int, timestamp: t.Optional[int] = None) -> bool: # type:
4244
return False
4345
return hmac.compare_digest(self.string_code(self.generate(timestamp)), self.string_code(code))
4446

45-
def to_uri(self, label: str, issuer: str) -> str: # type: ignore[override]
47+
def to_uri(self, label: str, issuer: str) -> str:
4648
"""Generate the otpauth protocal string for TOTP.
4749
4850
:param label: Label of the identifier.
@@ -53,11 +55,12 @@ def to_uri(self, label: str, issuer: str) -> str: # type: ignore[override]
5355

5456

5557
def generate_totp(
56-
secret: bytes,
57-
period: int = 30,
58-
timestamp: t.Optional[int] = None,
59-
digit: int = 6,
60-
algorithm: SupportedAlgorithms = "SHA1") -> int:
58+
secret: bytes,
59+
period: int = 30,
60+
timestamp: t.Optional[int] = None,
61+
digit: int = 6,
62+
algorithm: SupportedAlgorithms = "SHA1",
63+
) -> int:
6164
"""Generate a TOTP code.
6265
6366
A TOTP code is an extension of TOTP algorithm.

src/otpauth/core.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import base64
22
import typing as t
3+
from abc import ABCMeta
4+
from abc import abstractmethod
35
from urllib.parse import quote
4-
from abc import ABCMeta, abstractmethod
56

67
SupportedAlgorithms = t.Literal["SHA1", "SHA256", "SHA512"]
78
Self = t.TypeVar("Self", bound="OTP")
@@ -33,10 +34,8 @@ def b32_secret(self) -> str:
3334

3435
@classmethod
3536
def from_b32encode(
36-
cls: t.Type[Self],
37-
secret: t.Union[bytes, str],
38-
digit: int = 6,
39-
algorithm: SupportedAlgorithms = "SHA1") -> Self:
37+
cls: t.Type[Self], secret: t.Union[bytes, str], digit: int = 6, algorithm: SupportedAlgorithms = "SHA1"
38+
) -> Self:
4039
"""Create the instance with a base32 encoded secret.
4140
4241
:param secret: A base32 encoded secret string or bytes.
@@ -80,16 +79,13 @@ def string_code(self, code: int) -> str:
8079
8180
:param code: The number that this OTP generated.
8281
"""
83-
return f'{code:0{self.digit}}'
82+
return f"{code:0{self.digit}}"
8483

8584
@abstractmethod
86-
def generate(self, *args: t.Any, **kwargs: t.Any) -> int:
87-
...
85+
def generate(self, *args: t.Any, **kwargs: t.Any) -> int: ...
8886

8987
@abstractmethod
90-
def verify(self, code: int, *args: t.Any, **kwargs: t.Any) -> bool:
91-
...
88+
def verify(self, code: int, *args: t.Any, **kwargs: t.Any) -> bool: ...
9289

9390
@abstractmethod
94-
def to_uri(self, label: str, issuer: str, *args: t.Any, **kwargs: t.Any) -> str:
95-
...
91+
def to_uri(self, label: str, issuer: str, *args: t.Any, **kwargs: t.Any) -> str: ...

0 commit comments

Comments
 (0)