Skip to content

Commit

Permalink
fix all ruff lints
Browse files Browse the repository at this point in the history
Signed-off-by: Bradley Reynolds <[email protected]>
  • Loading branch information
shenanigansd committed Jul 12, 2023
1 parent 5af21da commit 1c12071
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 43 deletions.
4 changes: 1 addition & 3 deletions src/letsbuilda/pypi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""letsbuilda-pypi
A wrapper for PyPI's API and RSS feed
"""
"""A wrapper for PyPI's API and RSS feed."""

from .client import PyPIServices
from .models import JSONPackageMetadata, RSSPackageMetadata
Expand Down
20 changes: 12 additions & 8 deletions src/letsbuilda/pypi/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Service wrapper"""
"""Service wrapper."""

from typing import Final
from typing import Final, Self

import xmltodict
from aiohttp import ClientSession
Expand All @@ -9,23 +9,27 @@


class PyPIServices:
"""A class for interacting with PyPI"""
"""A class for interacting with PyPI."""

NEWEST_PACKAGES_FEED_URL: Final[str] = "https://pypi.org/rss/packages.xml"
PACKAGE_UPDATES_FEED_URL: Final[str] = "https://pypi.org/rss/updates.xml"

def __init__(self, http_session: ClientSession) -> None:
def __init__(self: Self, http_session: ClientSession) -> None:
self.http_session = http_session

async def get_rss_feed(self, feed_url: str) -> list[RSSPackageMetadata]:
"""Get the new packages RSS feed"""
async def get_rss_feed(self: Self, feed_url: str) -> list[RSSPackageMetadata]:
"""Get the new packages RSS feed."""
async with self.http_session.get(feed_url) as response:
response_text = await response.text()
rss_data = xmltodict.parse(response_text)["rss"]["channel"]["item"]
return [RSSPackageMetadata.build_from(package_data) for package_data in rss_data]

async def get_package_metadata(self, package_name: str, package_version: str | None = None) -> JSONPackageMetadata:
"""Get metadata for a package"""
async def get_package_metadata(
self: Self,
package_name: str,
package_version: str | None = None,
) -> JSONPackageMetadata:
"""Get metadata for a package."""
if package_version is not None:
url = f"https://pypi.org/pypi/{package_name}/{package_version}/json"
else:
Expand Down
8 changes: 4 additions & 4 deletions src/letsbuilda/pypi/distributions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Interactions with parsing package metadata and contents"""
"""Interactions with parsing package metadata and contents."""

import tarfile
from io import BytesIO
Expand All @@ -11,7 +11,7 @@ async def fetch_package_distribution(
http_session: aiohttp.ClientSession,
package_source_download_url: str,
) -> BytesIO:
"""Fetch a package distribution from PyPI"""
"""Fetch a package distribution from PyPI."""
buffer = BytesIO()
async with http_session.get(package_source_download_url) as response:
buffer.write(await response.content.read())
Expand All @@ -20,7 +20,7 @@ async def fetch_package_distribution(


def read_distribution_tarball(buffer: BytesIO) -> dict[str, str]:
"""Return a dictionary mapping filenames to content"""
"""Return a dictionary mapping filenames to content."""
files = {}
with tarfile.open(fileobj=buffer) as file:
for tarinfo in file:
Expand All @@ -30,7 +30,7 @@ def read_distribution_tarball(buffer: BytesIO) -> dict[str, str]:


def read_distribution_wheel(buffer: BytesIO) -> dict[str, str]:
"""Return a dictionary mapping filenames to content"""
"""Return a dictionary mapping filenames to content."""
files = {}
with ZipFile(file=buffer) as zip_file:
for zip_info in zip_file.infolist():
Expand Down
5 changes: 1 addition & 4 deletions src/letsbuilda/pypi/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
"""Response models"""

from .models_json import JSONPackageMetadata # noqa: F401
from .models_rss import RSSPackageMetadata # noqa: F401
"""Models to hold the data."""
37 changes: 22 additions & 15 deletions src/letsbuilda/pypi/models/models_json.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
"""Models for JSON responses"""
"""Models for JSON responses."""

from dataclasses import dataclass
from typing import Self

import pendulum
from pendulum import DateTime


@dataclass(frozen=True, slots=True)
class Vulnerability:
"""Security vulnerability"""
"""Security vulnerability."""

id: str
id: str # noqa: A003 - fields named exactly the same as the upstream
aliases: list[str]
link: str
source: str
Expand All @@ -20,7 +21,8 @@ class Vulnerability:
fixed_in: list[str]

@classmethod
def from_dict(cls, data: dict) -> "Vulnerability":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
if data["withdrawn"] is not None:
data["withdrawn"]: DateTime = pendulum.parse(data["withdrawn"])

Expand All @@ -29,33 +31,35 @@ def from_dict(cls, data: dict) -> "Vulnerability":

@dataclass(frozen=True, slots=True)
class Downloads:
"""Release download counts"""
"""Release download counts."""

last_day: int
last_month: int
last_week: int

@classmethod
def from_dict(cls, data: dict) -> "Downloads":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
return cls(**data)


@dataclass(frozen=True, slots=True)
class Digests:
"""URL file digests"""
"""URL file digests."""

blake2_b_256: str
md5: str
sha256: str

@classmethod
def from_dict(cls, data: dict) -> "Digests":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
return cls(**data)


@dataclass(frozen=True, slots=True)
class URL:
"""Package release URL"""
"""Package release URL."""

comment_text: str
digests: Digests
Expand All @@ -74,7 +78,8 @@ class URL:
yanked_reason: None

@classmethod
def from_dict(cls, data: dict, package_name: str, package_version: str) -> "URL":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
data["upload_time"]: DateTime = pendulum.parse(data["upload_time"])
data["upload_time_iso_8601"]: DateTime = pendulum.parse(data["upload_time_iso_8601"])

Expand All @@ -83,7 +88,7 @@ def from_dict(cls, data: dict, package_name: str, package_version: str) -> "URL"

@dataclass(frozen=True, slots=True)
class Info:
"""Package metadata internal info block"""
"""Package metadata internal info block."""

author: str
author_email: str
Expand All @@ -96,7 +101,7 @@ class Info:
downloads: Downloads
home_page: str
keywords: str
license: str
license: str # noqa: A003 - fields named exactly the same as the upstream
maintainer: str
maintainer_email: str
name: str
Expand All @@ -113,21 +118,23 @@ class Info:
yanked_reason: str | None

@classmethod
def from_dict(cls, data: dict) -> "Info":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
return cls(**data)


@dataclass(frozen=True, slots=True)
class JSONPackageMetadata:
"""Package metadata"""
"""Package metadata."""

info: Info
last_serial: int
urls: list[URL]
vulnerabilities: list["Vulnerability"]

@classmethod
def from_dict(cls, data: dict) -> "JSONPackageMetadata":
def from_dict(cls: type[Self], data: dict) -> Self:
"""Build an instance from a dictionary."""
info = Info.from_dict(data["info"])
return cls(
info=info,
Expand Down
16 changes: 7 additions & 9 deletions src/letsbuilda/pypi/models/models_rss.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
"""Models for RSS responses"""
"""Models for RSS responses."""

from dataclasses import dataclass
from datetime import datetime
from typing import Self


def _parse_publication_date(publication_date: str) -> datetime:
return datetime.strptime(publication_date, "%a, %d %b %Y %H:%M:%S %Z")
return datetime.strptime(publication_date, "%a, %d %b %Y %H:%M:%S %Z") # noqa: DTZ007 - uses `Z` instead of `z`


@dataclass(frozen=True, slots=True)
class RSSPackageMetadata:
"""RSS Package metadata"""
"""RSS Package metadata."""

title: str
version: str | None
Expand All @@ -21,14 +22,11 @@ class RSSPackageMetadata:
publication_date: datetime

@classmethod
def build_from(cls, data: dict[str, str]) -> "RSSPackageMetadata":
"""Build an instance from raw data"""
def build_from(cls: type[Self], data: dict[str, str]) -> "RSSPackageMetadata":
"""Build an instance from raw data."""
split_title = data.get("title").removesuffix(" added to PyPI").split()
title = split_title[0]
if len(split_title) == 2:
version = split_title[1]
else:
version = None
version = split_title[1] if len(split_title) == 2 else None # noqa: PLR2004 - is not magic

return cls(
title=title,
Expand Down

0 comments on commit 1c12071

Please sign in to comment.