[docs] Add a lot of documentation

This commit is contained in:
zevaryx 2025-03-07 08:25:57 -07:00
parent d26efec86b
commit 94d91974d1
17 changed files with 427 additions and 48 deletions

3
.gitignore vendored
View file

@ -15,3 +15,6 @@ report.xml
coverage.xml
.pytest_cache/
.ruff_cache/
# Docs
_build/

11
.readthedocs.yaml Normal file
View file

@ -0,0 +1,11 @@
version: 2
python:
install:
- requirements: docs/requirements.txt
- requirements: requirements.txt
sphinx:
builder: html
configuration: docs/conf.py

20
docs/Makefile Normal file
View file

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

43
docs/api.rst Normal file
View file

@ -0,0 +1,43 @@
.. currentmodule:: scryfall
.. _API Reference:
API Reference
===========================
Client
******
.. automodule:: scryfall.client
:members:
:member-order: bysource
HTTP
~~~~
.. automodule:: scryfall.client.http
:members:
:member-order: bysource
Card
----
.. automodule:: scryfall.client.http.card
:members:
:member-order: bysource
Set
----
.. automodule:: scryfall.client.http.set
:members:
:member-order: bysource
Models
******
.. automodule:: scryfall.models
:members:
:member-order: bysource
.. Card
.. ~~~~
.. .. automodule:: scryfall.models.cards
.. Set
.. ~~~~
.. .. automodule:: scryfall.models.sets

38
docs/conf.py Normal file
View file

@ -0,0 +1,38 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "scryfall-py"
copyright = "2025, zevaryx"
author = "zevaryx"
release = "0.1.1"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
"sphinx.ext.duration",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"enum_tools.autoenum",
]
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]
# Custom config
autodoc_default_flags = ["members"]
master_doc = "index"
source_suffix = [".rst", ".md"]

25
docs/guide.rst Normal file
View file

@ -0,0 +1,25 @@
Usage
===============
Installation
************
To get started, install ``scryfall-py``::
pip install scryfall-py
Getting Started
***************
Hashing Files
~~~~~~~~~~~~~
.. code-block:: python
from scryfall import Scryfall
client = Scryfall()
card = await client.search_cards_named("Arcades, the Strategist")
This will fetch the card Arcades, the Strategist from the Scryfall API

17
docs/index.rst Normal file
View file

@ -0,0 +1,17 @@
Welcome to the scryfall-py documentation!
=====================================
.. toctree::
:maxdepth: 2
:caption: Contents:
guide
api
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

35
docs/make.bat Normal file
View file

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View file

@ -38,6 +38,7 @@ dev = [
"sphinx>=8.2.1",
"sphinx-rtd-theme>=3.0.2",
"pytest-asyncio>=0.25.3",
"enum-tools>=0.12.0",
]
[build-system]

View file

@ -10,8 +10,8 @@ from httpx import AsyncClient
from scryfall.const import get_logger
from scryfall.client.error import LibraryException, HTTPException, ScryfallError, Forbidden, NotFound
from scryfall.client.http.http_requests.card import CardRequests
from scryfall.client.http.http_requests.set import SetRequests
from scryfall.client.http.card import CardRequests
from scryfall.client.http.set import SetRequests
from scryfall.client.route import Route

View file

@ -12,20 +12,44 @@ CLASS_LOOKUP = {"card": Card, "card_symbol": CardSymbol, "ruling": Ruling, "set"
class APIError(BaseModel):
"""Scryfall API error model."""
status: int
"""HTTP status code"""
code: str
"""HTTP status code text (i.e. not_found)"""
details: str
"""More details"""
type: str | None = None
"""The error type"""
warnings: list[str] | None = None
"""Any associated warnings that are not quite errors"""
class APIList(BaseAPIModel):
"""Scryfall API list model for paginated requests"""
object: Literal["list"]
"""The object type, `list` in this case"""
data: list[Card | CardSymbol | Ruling | Set]
"""The page of data, max 175 items unless it is an entire set of cards"""
has_more: bool
"""If there are more pages"""
next_page: HttpUrl | None = None
"""The URL to the next page"""
total_cards: int | None = None
"""The total number of cards found by the original query"""
warnings: list[str] | None = None
"""Warnings generated by the query that are not quite errors"""
@model_validator(mode="before")
@classmethod
@ -40,6 +64,7 @@ class APIList(BaseAPIModel):
return data
async def get_next_page(self) -> "APIList | None":
"""Get the next page of the query"""
if self.has_more and self.next_page is not None:
params = dict(self.next_page.query_params())
params.pop("format", None)

View file

@ -10,6 +10,7 @@ class BaseAPIModel(BaseModel):
"""Base API model for base API calls."""
_client: "Scryfall"
"""Internal Scryfall client"""
def __init__(self, **data):
client: "Scryfall" = data["_client"]

View file

@ -5,13 +5,34 @@ from pydantic import BaseModel, HttpUrl
class BulkData(BaseModel):
"""Bulk data model"""
id: UUID
"""UUID of the bulk data"""
uri: HttpUrl
"""URI of the bulk data endpoint"""
type: str
"""The type of data in the download"""
name: str
"""The name of the download"""
description: str
"""A description of the download"""
download_uri: HttpUrl
"""The download URL for the bulk data"""
updated_at: datetime
"""When this bulk data was last updated"""
size: int
"""The size of the data in bytes"""
content_type: str
"""The content type"""
content_encoding: str
"""The MIME encoding"""

View file

@ -13,44 +13,112 @@ if TYPE_CHECKING:
class RelatedCard(BaseModel):
"""A card that's related to the current card"""
id: UUID
"""The UUID of the related card"""
object: Literal["related_card"]
"""The object type, literally `related_card`"""
component: Literal["token", "meld_part", "meld_result", "combo_piece"]
"""How the card is related"""
name: str
"""The name of the related card"""
type_line: str
"""The type line of the related card"""
uri: HttpUrl
"""The URL of the related card"""
class CardFace(BaseModel):
"""A card face, for multi-faced cards"""
artist: str | None = None
"""The face artist"""
artist_id: str | None = None
"""The ID of the face artist"""
cmc: float | None = None
"""Card Mana Cost"""
color_indicator: list[Color] | None = None
"""The color indicator(s) of the face"""
colors: list[Color] | None = None
"""The color(s) of the face"""
defense: str | None = None
"""The toughness of the face, if it exists"""
flavor_text: str | None = None
"""The face's flavor text"""
illustration_id: UUID | None = None
"""The face's illustration UUID"""
image_uris: dict[Literal["small", "normal", "large", "png", "art_crop", "border_crop"], HttpUrl] | None = None
"""URLs for the image face for different formats."""
layout: str | None = None
"""Layout of the face."""
loyalty: str | None = None
"""Face loyalty"""
mana_cost: str
"""Face mana cost"""
name: str
"""Name of the face"""
object: Literal["card_face"]
"""Object type, literally `card_face`"""
oracle_id: UUID | None = None
"""Oracle ID of the face"""
oracle_text: str | None = None
"""Oracle text of the face"""
power: str | None = None
"""The face's power"""
printed_name: str | None = None
"""The printed name of the face"""
printed_text: str | None = None
"""The printed text of the face"""
printed_type_line: str | None = None
"""The printed type line of the face"""
toughness: str | None = None
"""The toughness of the face"""
type_line: str | None = None
"""The type line of the face"""
watermark: str | None = None
"""The watermark of the face"""
class Preview(BaseModel):
"""Object for defining when the card was previewed."""
previewed_at: date | None = None
"""When it was previewed"""
source_uri: HttpUrl | None = None
"""The URL of where it was previewed"""
source: str | None = None
"""The source of the preview"""
@field_validator("source_uri", mode="before")
@classmethod
@ -62,48 +130,104 @@ class Preview(BaseModel):
class Card(BaseAPIModel):
"""Main Card model."""
# Core fields
arena_id: int | None = None
"""MTG Arena ID"""
id: UUID
"""Scryfall card UUID"""
lang: str
"""Card language"""
mtgo_id: int | None = None
"""MTG Online ID"""
mtgo_foil_id: int | None = None
"""MTG Online Foil ID"""
multiverse_ids: list[int] | None = None
"""MTG Multiverse IDs"""
tcgplayer_id: int | None = None
"""TCGPlayer ID"""
tcgplayer_etched_id: int | None = None
"""TCGPlayer Etched ID"""
cardmarket_id: int | None = None
"""Cardmarket ID"""
object: Literal["card"]
"""Object type, literally `card`"""
layout: str
"""Card layout"""
oracle_id: UUID | None = None
"""Oracle UUID"""
prints_search_uri: HttpUrl
"""Print search URL for other printings"""
rulings_uri: HttpUrl
"""Rulings URL"""
scryfall_uri: HttpUrl
"""Scryfall URL"""
uri: HttpUrl
"""Card URL"""
# Gameplay fields
all_parts: list[RelatedCard] | None = None
"""If this card is closely related to other cards, this property will be an array with Related Card objects."""
card_faces: list[CardFace] | None = None
"""All faces of the card"""
cmc: float
"""Card mana value"""
color_identity: list[Color]
"""Card color identity"""
color_indicator: list[Color] | None = None
"""Card color indicator, if any"""
colors: list[Color] | None = None
"""Card colors, if any. Colors may be on the card's faces instead"""
defense: str | None = None
"""Toughness"""
edhrec_rank: int | None = None
"""EDHRec card rank/popularity"""
game_changer: bool | None = None
"""If this car is on the Commander Game Changer list"""
hand_modifier: str | None = None
"""Vanguard card hand modifier"""
keywords: list[str]
"""List of keywords, such as `Flying` or `Cumulative upkeep`"""
legalities: dict[str, Literal["legal", "not_legal", "restricted", "banned"]]
"""List of legalitites across different formats"""
life_modifier: str | None = None
"""The card's life modifier, if it is a Vanguard card. This value will contain a delta, such as `+2`."""
loyalty: str | None = None
"""This loyalty if any. Note that some cards have loyalties that are not numeric, such as X."""
mana_cost: str | None = None
"""The mana cost for this card. This value will be any empty string `""` if the cost is absent. Remember that per the game rules, a missing mana cost and a mana cost of `{0}` are different values. Multi-faced cards will report this value in card faces."""
name: str
"""The name of this card. If this card has multiple faces, this field will contain both names separated by `␣//␣`."""
oracle_text: str | None = None
"""The Oracle text for this card, if any."""
penny_rank: int | None = None
"""This card's rank/popularity on Penny Dreadful. Not all cards are ranked."""
power: str | None = None
"""This card's power, if any. Note that some cards have powers that are not numeric, such as `*`."""
produced_mana: list[Color] | None = None
"""Colors of mana that this card could produce."""
reserved: bool
"""True if this card is on the Reserved List."""
toughness: str | None = None
"""This card's toughness, if any. Note that some cards have toughnesses that are not numeric, such as `*`."""
type_line: str
"""The type line of this card."""
# Print fields
artist: str | None = None

105
uv.lock generated
View file

@ -297,6 +297,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/11/208f72084084d3f6a2ed5ebfdfc846692c3f7ad6dce65e400194924f7eed/domdf_python_tools-3.10.0-py3-none-any.whl", hash = "sha256:5e71c1be71bbcc1f881d690c8984b60e64298ec256903b3147f068bc33090c36", size = 126860 },
]
[[package]]
name = "enum-tools"
version = "0.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pygments" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/94/06/55dfd19df0386a2a90d325dba67b0b5ba0b879a4bdac9cd225c22f6736d8/enum_tools-0.12.0.tar.gz", hash = "sha256:13ceb9376a4c5f574a1e7c5f9c8eb7f3d3fbfbb361cc18a738df1a58dfefd460", size = 18931 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/75/fc/cc600677fe58519352ae5fe9367d05d0054faa47e8c57ef50a1bb9c77b0e/enum_tools-0.12.0-py3-none-any.whl", hash = "sha256:d69b019f193c7b850b17d9ce18440db7ed62381571409af80ccc08c5218b340a", size = 22356 },
]
[[package]]
name = "executing"
version = "2.2.0"
@ -716,51 +729,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
]
[[package]]
name = "scryfall"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "httpx" },
{ name = "pydantic" },
]
[package.dev-dependencies]
dev = [
{ name = "black" },
{ name = "ipython" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "rich" },
{ name = "ruff" },
{ name = "sphinx" },
{ name = "sphinx-rtd-theme" },
{ name = "sphinx-toolbox" },
]
[package.metadata]
requires-dist = [
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "pydantic", specifier = ">=2.10.6" },
]
[package.metadata.requires-dev]
dev = [
{ name = "black", specifier = ">=25.1.0" },
{ name = "ipython", specifier = ">=9.0.0" },
{ name = "pre-commit", specifier = ">=4.0.1" },
{ name = "pytest", specifier = ">=8.3.3" },
{ name = "pytest-asyncio", specifier = ">=0.25.3" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "rich", specifier = ">=13.9.4" },
{ name = "ruff", specifier = ">=0.9.9" },
{ name = "sphinx", specifier = ">=8.2.1" },
{ name = "sphinx-rtd-theme", specifier = ">=3.0.2" },
{ name = "sphinx-toolbox", specifier = ">=3.9.0" },
]
[[package]]
name = "pygments"
version = "2.19.1"
@ -898,6 +866,53 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 },
]
[[package]]
name = "scryfall-py"
version = "0.1.1"
source = { editable = "." }
dependencies = [
{ name = "httpx" },
{ name = "pydantic" },
]
[package.dev-dependencies]
dev = [
{ name = "black" },
{ name = "enum-tools" },
{ name = "ipython" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "rich" },
{ name = "ruff" },
{ name = "sphinx" },
{ name = "sphinx-rtd-theme" },
{ name = "sphinx-toolbox" },
]
[package.metadata]
requires-dist = [
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "pydantic", specifier = ">=2.10.6" },
]
[package.metadata.requires-dev]
dev = [
{ name = "black", specifier = ">=25.1.0" },
{ name = "enum-tools", specifier = ">=0.12.0" },
{ name = "ipython", specifier = ">=9.0.0" },
{ name = "pre-commit", specifier = ">=4.0.1" },
{ name = "pytest", specifier = ">=8.3.3" },
{ name = "pytest-asyncio", specifier = ">=0.25.3" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "rich", specifier = ">=13.9.4" },
{ name = "ruff", specifier = ">=0.9.9" },
{ name = "sphinx", specifier = ">=8.2.1" },
{ name = "sphinx-rtd-theme", specifier = ">=3.0.2" },
{ name = "sphinx-toolbox", specifier = ">=3.9.0" },
]
[[package]]
name = "six"
version = "1.17.0"