Skip to content

Commit 8705e88

Browse files
rewrite to use pdm and updated ruff (#23)
1 parent 42036aa commit 8705e88

File tree

10 files changed

+1735
-1532
lines changed

10 files changed

+1735
-1532
lines changed

.github/workflows/build.yaml

+24-46
Original file line numberDiff line numberDiff line change
@@ -27,58 +27,36 @@ jobs:
2727
with:
2828
fetch-depth: 0
2929

30-
- name: "Load cached poetry installation @ ${{ matrix.python-version }}"
31-
id: cached-poetry
32-
uses: actions/cache@v4
30+
- name: "Setup PDM @ ${{ matrix.python-version }}"
31+
uses: pdm-project/setup-pdm@v4
3332
with:
34-
path: ~/.local
35-
key: poetry-0
33+
python-version: ${{matrix.python-version}}
34+
cache: true
3635

37-
- name: "Setup Poetry @ ${{ matrix.python-version }}"
38-
if: steps.cached-poetry.outputs.cache-hit != 'true'
39-
uses: snok/install-poetry@v1
40-
with:
41-
version: latest
42-
virtualenvs-create: true
43-
virtualenvs-in-project: true
44-
virtualenvs-path: ~/.venv
45-
46-
- name: "Setup Python @ ${{ matrix.python-version }}"
47-
id: setup-python
48-
uses: actions/setup-python@v5
49-
with:
50-
python-version: "${{ matrix.python-version }}"
51-
cache: "poetry"
52-
53-
- name: "Load cached venv @ ${{ matrix.python-version }}"
54-
id: cached-pip-wheels
55-
uses: actions/cache@v4
56-
with:
57-
path: .venv/
58-
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
59-
60-
- name: "Install Python deps @ ${{ matrix.python-version }}"
61-
if: ${{ steps.cached-pip-wheels.outputs.cache-hit != 'true' }}
62-
id: install-deps
36+
- name: "Install deps @ ${{ matrix.python-version }}"
6337
run: |
64-
poetry install --no-interaction
38+
pdm install --prod --check --no-editable
6539
66-
- name: Activate venv @ ${{ matrix.python-version }}
40+
- name: "Activate venv @ ${{ matrix.python-version }}"
6741
run: |
68-
echo "$(poetry env info --path)/bin" >> $GITHUB_PATH
42+
echo "$(pdm info --where)/.venv/bin" >> $GITHUB_PATH
6943
7044
- name: "Check it imports @ ${{ matrix.python-version }}"
7145
run: |
72-
poetry run python -c 'import mystbin'
46+
pdm run python -c 'import hondana'
47+
48+
- name: "Test Suite @ ${{ matrix.python-version }}"
49+
run: |
50+
pdm run tests
7351
7452
- name: "Build wheels @ ${{ matrix.python-version}}"
7553
run: |
76-
poetry build
54+
pdm build
7755
7856
- name: "Build docs @ ${{ matrix.python-version}}"
57+
working-directory: docs/
7958
run: |
80-
cd docs/
81-
poetry run sphinx-build -aETW --keep-going . build
59+
pdm run docs
8260
8361
- name: "Upload artifacts @ ${{ matrix.python-version}}"
8462
if: ${{ matrix.python-version != '3.x' }}
@@ -94,6 +72,9 @@ jobs:
9472
needs: [build]
9573
runs-on: ubuntu-latest
9674
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
75+
permissions:
76+
contents: read
77+
id-token: write
9778

9879
steps:
9980
- uses: actions/checkout@v4
@@ -120,15 +101,12 @@ jobs:
120101
tag_name="${GITHUB_REF##*/}"
121102
gh release create "$tag_name" -F "CHANGELOG.md" "${assets[@]}"
122103
123-
- name: "Set up Poetry"
124-
uses: snok/install-poetry@v1
104+
- name: Set up PDM
105+
uses: pdm-project/setup-pdm@v4
125106
with:
126-
virtualenvs-create: true
127-
virtualenvs-in-project: false
107+
python-version: 3.8
108+
cache: true
128109

129110
- name: Publish to PyPI
130-
env:
131-
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
132111
run: |
133-
poetry config pypi-token.pypi $PYPI_TOKEN
134-
poetry publish
112+
pdm publish

.github/workflows/coverage_and_lint.yaml

+16-38
Original file line numberDiff line numberDiff line change
@@ -24,54 +24,32 @@ jobs:
2424
with:
2525
fetch-depth: 0
2626

27-
- name: "Load cached poetry installation @ ${{ matrix.python-version }}"
28-
id: cached-poetry
29-
uses: actions/cache@v4
27+
- name: Setup PDM @ ${{ matrix.python-version }}
28+
uses: pdm-project/setup-pdm@v4
3029
with:
31-
path: ~/.local
32-
key: poetry-0
30+
python-version: ${{ matrix.python-version }}
31+
cache: true
3332

34-
- name: "Setup Poetry @ ${{ matrix.python-version }}"
35-
if: steps.cached-poetry.outputs.cache-hit != 'true'
36-
uses: snok/install-poetry@v1
37-
with:
38-
version: latest
39-
virtualenvs-create: true
40-
virtualenvs-in-project: true
41-
virtualenvs-path: .venv
42-
43-
- name: "Setup Python @ ${{ matrix.python-version }}"
44-
id: setup-python
45-
uses: actions/setup-python@v5
46-
with:
47-
python-version: "${{ matrix.python-version }}"
48-
cache: "poetry"
49-
50-
- name: "Load cached venv @ ${{ matrix.python-version }}"
51-
id: cached-pip-wheels
52-
uses: actions/cache@v4
53-
with:
54-
path: ~/.venv/
55-
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
56-
57-
- name: "Install Python deps @ ${{ matrix.python-version }}"
58-
if: ${{ steps.cached-pip-wheels.outputs.cache-hit != 'true' }}
59-
id: install-deps
33+
- name: Install deps @ ${{ matrix.python-version }}
6034
run: |
61-
poetry install --all-extras -n --no-cache --no-interaction
35+
pdm install --check --no-editable
6236
6337
- name: Activate venv @ ${{ matrix.python-version }}
6438
run: |
65-
echo "$(poetry env info --path)/bin" >> $GITHUB_PATH
39+
echo "$(pdm info --where)/.venv/bin" >> $GITHUB_PATH
6640
6741
- name: "Run Pyright @ ${{ matrix.python-version }}"
6842
uses: jakebailey/pyright-action@v2
6943
with:
7044
warnings: false
71-
verify-types: "mystbin"
72-
ignore-external: true
73-
no-comments: ${{ matrix.python-version != '3.x' }}
45+
annotate: "${{ matrix.python-version != '3.x' }}"
46+
47+
- name: Lint check
48+
uses: astral-sh/ruff-action@v2
49+
with:
50+
args: check .
7451

75-
- name: Lint
76-
if: ${{ always() && steps.install-deps.outcome == 'success' }}
52+
- name: Formatting check
7753
uses: chartboost/ruff-action@v1
54+
with:
55+
args: format --check

.gitignore

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
.vscode/
21
*.py[cod]
32
.venv/
3+
.env
4+
venv/
5+
.ruff_cache/
6+
.pdm-python
7+
8+
.vscode/
49
.idea/
10+
511
docs/build
612
dist/
7-
.ruff_cache/
13+
.pdm-build/

mystbin/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
DEALINGS IN THE SOFTWARE.
2323
"""
2424

25-
__version__ = "7.0.2"
25+
__version__ = "7.1.0"
2626

2727
from typing import Literal, NamedTuple
2828

@@ -39,6 +39,6 @@ class VersionInfo(NamedTuple):
3939
serial: int
4040

4141

42-
version_info: VersionInfo = VersionInfo(major=7, minor=0, micro=2, releaselevel="final", serial=0)
42+
version_info: VersionInfo = VersionInfo(major=7, minor=1, micro=0, releaselevel="final", serial=0)
4343

4444
del NamedTuple, Literal, VersionInfo

mystbin/client.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,22 @@ class Client:
4848
If not provided, a new session will be created.
4949
api_base: :class:`str`
5050
The base URL for the mystbin instance.
51-
Defaults to ``https://mystb.in/``.
52-
This should begin with ``https://`` and should be the root URL of the mystbin instance.
51+
Defaults to ``mystb.in``.
5352
"""
53+
5454
__slots__ = ("http",)
5555

56-
def __init__(self, *, session: ClientSession | None = None, api_base: str = "https://mystb.in/") -> None:
56+
def __init__(self, *, session: ClientSession | None = None, api_base: str = "mystb.in") -> None:
5757
self.http: HTTPClient = HTTPClient(session=session, api_base=api_base)
5858

5959
async def __aenter__(self) -> Self:
6060
return self
6161

6262
async def __aexit__(
63-
self, exc_cls: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
63+
self,
64+
exc_cls: type[BaseException] | None,
65+
exc_value: BaseException | None,
66+
traceback: TracebackType | None,
6467
) -> None:
6568
await self.close()
6669

mystbin/http.py

+22-21
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1919
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2020
DEALINGS IN THE SOFTWARE.
21-
"""
21+
""" # noqa: A005 # we access this via namespace
2222

2323
from __future__ import annotations
2424

@@ -52,7 +52,6 @@
5252
T = TypeVar("T")
5353
Response = Coroutine[None, None, T]
5454
MU = TypeVar("MU", bound="MaybeUnlock")
55-
BE = TypeVar("BE", bound=BaseException)
5655
from .types.responses import CreatePasteResponse, GetPasteResponse
5756

5857

@@ -69,8 +68,14 @@ def _clean_dt(dt: datetime.datetime) -> str:
6968
return dt.isoformat()
7069

7170

72-
async def json_or_text(response: aiohttp.ClientResponse, /) -> dict[str, Any] | str:
73-
"""A quick method to parse a `aiohttp.ClientResponse` and test if it's json or text."""
71+
async def _json_or_text(response: aiohttp.ClientResponse, /) -> dict[str, Any] | str:
72+
"""A quick method to parse a `aiohttp.ClientResponse` and test if it's json or text.
73+
74+
Returns
75+
--------
76+
Union[Dict[:class:`str`, Any], :class:`str`]
77+
The JSON object, or request text.
78+
"""
7479
text = await response.text(encoding="utf-8")
7580
try:
7681
if response.headers["content-type"] == "application/json":
@@ -97,8 +102,8 @@ def defer(self) -> None:
97102

98103
def __exit__(
99104
self,
100-
exc_type: type[BE] | None,
101-
exc: BE | None,
105+
exc_type: type[BaseException] | None,
106+
exc: BaseException | None,
102107
traceback: TracebackType | None,
103108
) -> None:
104109
if self._unlock:
@@ -115,14 +120,14 @@ class Route:
115120
API_BASE: ClassVar[str] = "https://mystb.in/api"
116121

117122
def __init__(self, verb: SupportedHTTPVerb, path: str, **params: Any) -> None:
118-
119123
self.verb: SupportedHTTPVerb = verb
120124
self.path: str = path
121125
url = self.API_BASE + path
122126
if params:
123127
url = url.format_map({k: _uriquote(v) if isinstance(v, str) else v for k, v in params.items()})
124128
self.url: str = url
125129

130+
126131
class HTTPClient:
127132
__slots__ = (
128133
"_async",
@@ -131,7 +136,7 @@ class HTTPClient:
131136
"_session",
132137
"_token",
133138
"api_base",
134-
"user_agent"
139+
"user_agent",
135140
)
136141

137142
def __init__(self, *, session: aiohttp.ClientSession | None = None, api_base: str | None = None) -> None:
@@ -142,12 +147,9 @@ def __init__(self, *, session: aiohttp.ClientSession | None = None, api_base: st
142147
self.user_agent: str = user_agent.format(__version__, sys.version_info, aiohttp.__version__)
143148
self._resolve_api(api_base)
144149

145-
def _resolve_api(self, api: str | None) -> None:
146-
if api:
147-
Route.API_BASE = api + "api" if api.endswith("/") else api + "/api"
148-
self.api_base = api + ("/" if not api.endswith("/") else "")
149-
else:
150-
self.api_base = "https://mystb.in/"
150+
def _resolve_api(self, api_base: str | None, /) -> None:
151+
if api_base:
152+
Route.API_BASE = f"https://{api_base}/api"
151153

152154
async def close(self) -> None:
153155
if self._session and self._owns_session:
@@ -194,28 +196,28 @@ async def request(self, route: Route, **kwargs: Any) -> Any:
194196
retry = response.headers.get("x-ratelimit-retry-after", None)
195197
LOGGER.debug("retry is: %s", retry)
196198
if retry is not None:
197-
retry = datetime.datetime.fromtimestamp(int(retry))
199+
retry = datetime.datetime.fromtimestamp(int(retry), tz=datetime.timezone.utc)
198200
# The total ratelimit session hits
199201
limit = response.headers.get("x-ratelimit-limit", None)
200202
LOGGER.debug("limit is: %s", limit)
201203

202204
if remaining == "0" and response.status != 429:
203205
assert retry is not None
204-
delta = retry - datetime.datetime.now()
206+
delta = retry - datetime.datetime.now(datetime.timezone.utc)
205207
sleep = delta.total_seconds() + 1
206208
LOGGER.warning("A ratelimit has been exhausted, sleeping for: %d", sleep)
207209
maybe_lock.defer()
208210
loop = asyncio.get_running_loop()
209211
loop.call_later(sleep, lock.release)
210212

211-
data = await json_or_text(response)
213+
data = await _json_or_text(response)
212214

213215
if 300 > response.status >= 200:
214216
return data
215217

216218
if response.status == 429:
217219
assert retry is not None
218-
delta = retry - datetime.datetime.now()
220+
delta = retry - datetime.datetime.now(datetime.timezone.utc)
219221
sleep = delta.total_seconds() + 1
220222
LOGGER.warning("A ratelimit has been hit, sleeping for: %d", sleep)
221223
await asyncio.sleep(sleep)
@@ -227,15 +229,14 @@ async def request(self, route: Route, **kwargs: Any) -> Any:
227229
await asyncio.sleep(sleep_)
228230
continue
229231

230-
print(data)
231232
assert isinstance(data, dict)
232233
LOGGER.exception("Unhandled HTTP error occurred: %s -> %s", response.status, data)
233234
raise APIException(
234235
response=response,
235236
status_code=response.status,
236237
)
237-
except (aiohttp.ServerDisconnectedError, aiohttp.ServerTimeoutError) as error:
238-
LOGGER.exception("Network error occurred: %s", error)
238+
except (aiohttp.ServerDisconnectedError, aiohttp.ServerTimeoutError):
239+
LOGGER.exception("Network error occurred:")
239240
await asyncio.sleep(5)
240241
continue
241242

0 commit comments

Comments
 (0)