Contents¶
Overview¶
An (a)sync e621 API wrapper library.
Free software: GNU Lesser General Public License v3 (LGPLv3)
Installation¶
pip install yippi
You can also install the in-development version with:
pip install git+ssh://git@github.com/rorre/yippi.git@master
Quickstart¶
Sync¶
>>> import requests
>>> from yippi import YippiClient
>>>
>>> session = requests.Session()
>>> client = YippiClient("MyProject", "1.0", "MyUsernameOnE621", session)
>>> posts = client.posts("m/m zeta-haru rating:s") # or ["m/m", "zeta-haru", "rating-s"], both works.
[Post(id=1383235), Post(id=514753), Post(id=514638), Post(id=356347), Post(id=355044)]
>>> posts[0].tags
{'artist': ['zeta-haru'],
'character': ['daniel_segja', 'joel_mustard'],
'copyright': ['patreon'],
'general': ['5_fingers', ..., 'spooning'],
'invalid': [],
'lore': [],
'meta': ['comic'],
'species': ['bird_dog', ... ]}
Async¶
>>> import aiohttp
>>> from yippi import AsyncYippiClient
>>>
>>> session = aiohttp.ClientSession()
>>> client = AsyncYippiClient("MyProject", "1.0", "MyUsernameOnE621", session)
>>> posts = await client.posts("m/m zeta-haru rating:s") # or ["m/m", "zeta-haru", "rating-s"], both works.
[Post(id=1383235), Post(id=514753), Post(id=514638), Post(id=356347), Post(id=355044)]
>>> posts[0].tags
{'artist': ['zeta-haru'],
'character': ['daniel_segja', 'joel_mustard'],
'copyright': ['patreon'],
'general': ['5_fingers', ..., 'spooning'],
'invalid': [],
'lore': [],
'meta': ['comic'],
'species': ['bird_dog', ... ]}
Examples are available in examples directory.
Documentation¶
Documentation is available on readthedocs: https://yippi.readthedocs.io/
Development¶
To run the all tests run:
tox
Note, to combine the coverage data from all the tox environments run:
Windows |
set PYTEST_ADDOPTS=--cov-append
tox
|
---|---|
Other |
PYTEST_ADDOPTS=--cov-append tox
|
Introduction¶
Installation¶
Yippi does not require many dependency,It only asks for the client as its dependency.
Currently there are two clients available:
aiohttp
(Async)requests
(Sync)
To install, you can use pip
.:
pip install yippi
And you should be ready to go.
Calling the API¶
Currenly, only GET routes are available for now. To initialize the client, you have to supply your project name, version, and your username on e621.:
from yippi import YippiClient, AsyncYippiClient
client = YippiClient("MyProject", "1.0", "MyUsername")
client = AsyncYippiClient("MyProject", "1.0", "MyUsername")
The only core functions are as follows.:
client.posts() # Searches e621
client.post() # Fetch for a post
client.notes() # Searches for notes
client.flags() # Searches for flags
client.pools() # Searches for pools
If you’re using the async client, you only need to prepend the await
statement.:
await client.posts()
await client.post()
await client.notes()
await client.flags()
await client.pools()
An example is available on the Overview page.
Advanced¶
Adding Custom Client¶
The module is written to be as easily portable as possible. Therefore, any custom
client should be easily written to this library. The yippi.AbstractYippi.AbstractYippi
class should be enough for you to inherit. And what you need to do is override the abstract functions.
For example, here’s a snippet from AsyncYippiClient.
from typing import List
from typing import Optional
from typing import Union
import aiohttp
from aiohttp import BasicAuth
from aiohttp import FormData
from .AbstractYippi import AbstractYippi
from .AbstractYippi import limiter
from .Classes import Flag
from .Classes import Note
from .Classes import Pool
from .Classes import Post
from .Exceptions import APIError
from .Exceptions import UserError
class AsyncYippiClient(AbstractYippi):
def __init__(
self,
project_name: str,
version: str,
creator: str,
loop=None,
session: aiohttp.ClientSession = None,
) -> None:
self._loop = loop
self._session: aiohttp.ClientSession = session or aiohttp.ClientSession()
super().__init__(project_name, version, creator)
async def close(self) -> None:
await self._session.close()
async def __aenter__(self) -> "AsyncYippiClient":
return self
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
await self.close()
@limiter.ratelimit("call_api", delay=True)
async def _call_api(
self,
method: str,
url: str,
data: Union[dict, FormData] = None,
file=None,
**kwargs
) -> Optional[Union[List[dict], dict]]:
auth = None
if self._login != ("", ""):
auth = BasicAuth(*self._login)
if file:
file = file["upload[file]"]
formdata = FormData()
formdata.add_field(
"upload[file]", file[1], filename=file[0], content_type=file[2]
)
formdata.add_fields(data)
data = formdata
query_string = self._generate_query_keys(**kwargs)
url += "?" + query_string
r = await self._session.request(
method, url, data=data, headers=self.headers, auth=auth
)
await self._verify_response(r)
if not r.status == 204:
return await r.json()
return None
async def _verify_response(self, r) -> None:
if 300 <= r.status < 500:
res = await r.json()
if r.status >= 400:
raise UserError(res.get("message") or res.get("reason"), json=res)
elif r.status >= 500:
raise APIError(r.reason)
if r.status != 204 and (
not r.headers.get("Content-Type")
or "application/json" not in r.headers.get("Content-Type")
):
res = await r.text()
if "Not found." in res:
raise UserError("Not found.")
raise UserError("Invalid input or server error.")
async def posts(
self,
tags: Union[List, str] = None,
limit: int = None,
page: Union[int, str] = None,
) -> List[Post]:
response = await self._get_posts(tags, limit, page) # type: ignore
posts = [Post(p, client=self) for p in response["posts"]]
return posts
async def post(self, post_id: int) -> Post:
api_res = await self._get_post(post_id) # type: ignore
return Post(api_res["post"], client=self)
async def notes(
self,
body_matches: str = None,
post_id: int = None,
post_tags_match: Union[List, str] = None,
creator_name: str = None,
creator_id: int = None,
is_active: bool = None,
limit: int = None,
) -> List[Note]:
response = await self._get_notes(
body_matches,
post_id,
post_tags_match,
creator_name,
creator_id,
is_active,
limit,
) # type: ignore
result = [Note(n, client=self) for n in response]
return result
async def flags(
self,
post_id: int = None,
creator_id: int = None,
creator_name: str = None,
limit: int = None,
) -> List[Flag]:
response = await self._get_flags(post_id, creator_id, creator_name) # type: ignore
result = [Flag(f, client=self) for f in response]
return result
async def pools(
self,
name_matches: str = None,
id_: Union[int, List[int]] = None,
description_matches: str = None,
creator_name: str = None,
creator_id: int = None,
is_active: bool = None,
is_deleted: bool = None,
category: str = None,
order: str = None,
limit: int = None,
) -> List[Pool]:
response = await self._get_pools(
name_matches,
id_,
description_matches,
creator_name,
creator_id,
is_active,
is_deleted,
category,
order,
limit,
) # type: ignore
result = [Pool(p, client=self) for p in response]
return result
async def pool(self, pool_id: int) -> Pool:
response = await self._get_pool(pool_id) # type: ignore
return Pool(response, client=self)
Reference¶
yippi package¶
yippi.AbstractYippi module¶
- class yippi.AbstractYippi.AbstractYippi(project_name: str, version: str, creator: str)[source]¶
Bases:
abc.ABC
An abstract class (abc) for all the Yippi’s client.
Generally you don’t really need to use this, except if you want to use different implementation for the client.
- Parameters
project_name – Your project’s name where this library is going to be used.
version – You project’s version number.
creator – Your e621 username.
session – The HTTP client session object.
loop – The event loop to run on. This is only required on async client.
- VALID_CATEGORY = ('series', 'collection')¶
- VALID_ORDER = ('name', 'created_at', 'updated_at', 'post_count')¶
- abstract flags(post_id: Optional[int] = None, creator_id: Optional[int] = None, creator_name: Optional[str] = None, limit: Optional[int] = None) Union[List[yippi.Classes.Flag], Awaitable[List[yippi.Classes.Flag]]] [source]¶
Search for flags
- Parameters
post_id – The ID of the flagged post.
creator_id – The user’s ID that created the flag.
creator_name – The user’s name that created the flag.
limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofFlag
of the flags.
- login(username: str, api_key: str) None [source]¶
Supply login credentials to client.
- Parameters
username – Your e621 username.
api_key – Your API key. Find it under “Account” on e621.
- abstract notes(body_matches: Optional[str] = None, post_id: Optional[int] = None, post_tags_match: Optional[Union[List, str]] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, limit: Optional[int] = None) Union[List[yippi.Classes.Note], Awaitable[List[yippi.Classes.Note]]] [source]¶
Search for notes.
- Parameters
body_matches – The note’s body matches the given terms. Use a * in the search terms to search for raw strings.
post_id – The post where the note is located.
post_tags_match – The note’s post’s tags match the given terms. Meta-tags are not supported.
creator_name – The creator’s name. Exact match.
creator_id – The creator’s user id.
is_active – Can be
True
orFalse
.limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofNote
of the notes.
- abstract pool(pool_id: int) Union[yippi.Classes.Pool, Awaitable[yippi.Classes.Pool]] [source]¶
Fetch for a pool.
- Parameters
pool_id – The pool’s ID to look up.
- Returns
Pool
of the pool.
- abstract pools(name_matches: Optional[str] = None, id_: Optional[Union[int, List[int]]] = None, description_matches: Optional[str] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, is_deleted: Optional[bool] = None, category: Optional[str] = None, order: Optional[str] = None, limit: Optional[int] = None) Union[List[yippi.Classes.Pool], Awaitable[List[yippi.Classes.Pool]]] [source]¶
Search for pools.
- Parameters
name_matches – Search for pool names.
id_ – Search for a pool ID. Multiple IDs are fine. .. warning:: Take note of the underscore (_) mark!
description_matches – Search for pool descriptions.
creator_name – Search for pools based on creator name.
creator_id – Search for pools based on creator ID.
is_active – If the pool is active or hidden. (True/False)
is_deleted – If the pool is deleted. (True/False)
category – Can either be “series” or “collection”.
order – The order that pools should be returned, can be any of:
name
,created_at
,updated_at
,post_count
. If not specified it orders byupdated_at
.limit – The limit of how many pools should be retrieved.
- Returns
list
ofPool
of the pools.
- abstract post(post_id: int) Union[yippi.Classes.Post, Awaitable[yippi.Classes.Post]] [source]¶
Fetch for a post.
- Parameters
post_id – The post’s ID to look up.
- Returns
Post
of the posts.
- abstract posts(tags: Optional[Union[List, str]] = None, limit: Optional[int] = None, page: Optional[Union[int, str]] = None) Union[List[yippi.Classes.Post], Awaitable[List[yippi.Classes.Post]]] [source]¶
Search for posts.
- Parameters
tags – The tags to search.
limit – Limits the amount of notes returned to the number specified.
page – The page that will be returned.
- Returns
list
ofPost
of the posts.
yippi.AsyncYippi module¶
- class yippi.AsyncYippi.AsyncYippiClient(project_name: str, version: str, creator: str, loop=None, session: Optional[aiohttp.client.ClientSession] = None)[source]¶
Bases:
yippi.AbstractYippi.AbstractYippi
- async flags(post_id: Optional[int] = None, creator_id: Optional[int] = None, creator_name: Optional[str] = None, limit: Optional[int] = None) List[yippi.Classes.Flag] [source]¶
Search for flags
- Parameters
post_id – The ID of the flagged post.
creator_id – The user’s ID that created the flag.
creator_name – The user’s name that created the flag.
limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofFlag
of the flags.
- async notes(body_matches: Optional[str] = None, post_id: Optional[int] = None, post_tags_match: Optional[Union[List, str]] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, limit: Optional[int] = None) List[yippi.Classes.Note] [source]¶
Search for notes.
- Parameters
body_matches – The note’s body matches the given terms. Use a * in the search terms to search for raw strings.
post_id – The post where the note is located.
post_tags_match – The note’s post’s tags match the given terms. Meta-tags are not supported.
creator_name – The creator’s name. Exact match.
creator_id – The creator’s user id.
is_active – Can be
True
orFalse
.limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofNote
of the notes.
- async pool(pool_id: int) yippi.Classes.Pool [source]¶
Fetch for a pool.
- Parameters
pool_id – The pool’s ID to look up.
- Returns
Pool
of the pool.
- async pools(name_matches: Optional[str] = None, id_: Optional[Union[int, List[int]]] = None, description_matches: Optional[str] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, is_deleted: Optional[bool] = None, category: Optional[str] = None, order: Optional[str] = None, limit: Optional[int] = None) List[yippi.Classes.Pool] [source]¶
Search for pools.
- Parameters
name_matches – Search for pool names.
id_ – Search for a pool ID. Multiple IDs are fine. .. warning:: Take note of the underscore (_) mark!
description_matches – Search for pool descriptions.
creator_name – Search for pools based on creator name.
creator_id – Search for pools based on creator ID.
is_active – If the pool is active or hidden. (True/False)
is_deleted – If the pool is deleted. (True/False)
category – Can either be “series” or “collection”.
order – The order that pools should be returned, can be any of:
name
,created_at
,updated_at
,post_count
. If not specified it orders byupdated_at
.limit – The limit of how many pools should be retrieved.
- Returns
list
ofPool
of the pools.
- async post(post_id: int) yippi.Classes.Post [source]¶
Fetch for a post.
- Parameters
post_id – The post’s ID to look up.
- Returns
Post
of the posts.
- async posts(tags: Optional[Union[List, str]] = None, limit: Optional[int] = None, page: Optional[Union[int, str]] = None) List[yippi.Classes.Post] [source]¶
Search for posts.
- Parameters
tags – The tags to search.
limit – Limits the amount of notes returned to the number specified.
page – The page that will be returned.
- Returns
list
ofPost
of the posts.
yippi.YippiSync module¶
- class yippi.YippiSync.YippiClient(project_name: str, version: str, creator: str, session: Optional[requests.sessions.Session] = None)[source]¶
Bases:
yippi.AbstractYippi.AbstractYippi
- flags(post_id: Optional[int] = None, creator_id: Optional[int] = None, creator_name: Optional[str] = None, limit: Optional[int] = None) List[yippi.Classes.Flag] [source]¶
Search for flags
- Parameters
post_id – The ID of the flagged post.
creator_id – The user’s ID that created the flag.
creator_name – The user’s name that created the flag.
limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofFlag
of the flags.
- notes(body_matches: Optional[str] = None, post_id: Optional[int] = None, post_tags_match: Optional[Union[List, str]] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, limit: Optional[int] = None) List[yippi.Classes.Note] [source]¶
Search for notes.
- Parameters
body_matches – The note’s body matches the given terms. Use a * in the search terms to search for raw strings.
post_id – The post where the note is located.
post_tags_match – The note’s post’s tags match the given terms. Meta-tags are not supported.
creator_name – The creator’s name. Exact match.
creator_id – The creator’s user id.
is_active – Can be
True
orFalse
.limit – Limits the amount of notes returned to the number specified.
- Returns
list
ofNote
of the notes.
- pool(pool_id: int) yippi.Classes.Pool [source]¶
Fetch for a pool.
- Parameters
pool_id – The pool’s ID to look up.
- Returns
Pool
of the pool.
- pools(name_matches: Optional[str] = None, id_: Optional[Union[int, List[int]]] = None, description_matches: Optional[str] = None, creator_name: Optional[str] = None, creator_id: Optional[int] = None, is_active: Optional[bool] = None, is_deleted: Optional[bool] = None, category: Optional[str] = None, order: Optional[str] = None, limit: Optional[int] = None) List[yippi.Classes.Pool] [source]¶
Search for pools.
- Parameters
name_matches – Search for pool names.
id_ – Search for a pool ID. Multiple IDs are fine. .. warning:: Take note of the underscore (_) mark!
description_matches – Search for pool descriptions.
creator_name – Search for pools based on creator name.
creator_id – Search for pools based on creator ID.
is_active – If the pool is active or hidden. (True/False)
is_deleted – If the pool is deleted. (True/False)
category – Can either be “series” or “collection”.
order – The order that pools should be returned, can be any of:
name
,created_at
,updated_at
,post_count
. If not specified it orders byupdated_at
.limit – The limit of how many pools should be retrieved.
- Returns
list
ofPool
of the pools.
- post(post_id: int) yippi.Classes.Post [source]¶
Fetch for a post.
- Parameters
post_id – The post’s ID to look up.
- Returns
Post
of the posts.
- posts(tags: Optional[Union[List, str]] = None, limit: Optional[int] = None, page: Optional[Union[int, str]] = None) List[yippi.Classes.Post] [source]¶
Search for posts.
- Parameters
tags – The tags to search.
limit – Limits the amount of notes returned to the number specified.
page – The page that will be returned.
- Returns
list
ofPost
of the posts.
yippi.Classes module¶
- class yippi.Classes.Flag(json_data=None, *args, **kwargs)[source]¶
Bases:
yippi.Classes._BaseMixin
Representation of e621’s Flag object.
- Parameters
data (optional) – The json server response of a
post_flags.json
call.client (optional) – The yippi client, used for api calls.
- Variables
All (Any) – Refer to e621 API docs for available attributes.
- get_post() Union[Post, Awaitable[Post]] [source]¶
Fetch the post linked with this flag.
- Returns
yippi.Classes.Post
– The post linked with this flag.
- class yippi.Classes.Note(json_data=None, *args, **kwargs)[source]¶
Bases:
yippi.Classes._BaseMixin
Representation of e621’s Note object.
- Parameters
data (optional) – The json server response of a
/notes.json
call.client (optional) – The yippi client, used for api calls.
- Variables
All (Any) – Refer to e621 API docs for available attributes.
- classmethod create(post: Union[Post, int], x: int, y: int, width: int, height: int, body: str, client: AbstractYippi) Note [source]¶
- get_post() Union[Post, Awaitable[Post]] [source]¶
Fetch the post linked with this note.
- Returns
yippi.Classes.Post
– The post linked with this note.
- revert(version_id: str) dict [source]¶
Reverts note to specified version_id. This function has not been tested.
- Parameters
version_id – Target version to revert.
- Raises
UserError – Raised if - Note does not come from
notes()
. -client
kwags was not supplied.- Returns
dict – JSON status response from API.
- class yippi.Classes.Pool(json_data=None, *args, **kwargs)[source]¶
Bases:
yippi.Classes._BaseMixin
Representation of e621’s Pool object.
- Parameters
data (optional) – The json server response of a
pools.json
call.client (optional) – The yippi client, used for api calls.
- Variables
All (Any) – Refer to e621 API docs for available attributes.
- get_posts() Union[List[Post], Awaitable[List[Post]]] [source]¶
Fetch all posts linked with this pool.
If the client is an async client, it will automatically call
get_posts_async()
.- Returns
list
ofyippi.Classes.Post
– All the posts linked with this pool.
- async get_posts_async() List[Post] [source]¶
Async representation of
get-posts()
- Returns
list
ofyippi.Classes.Post
– All the posts linked with this pool.
- revert(version_id: str) dict [source]¶
Reverts note to specified version_id. This function has not been tested.
- Parameters
version_id – Target version to revert.
- Raises
UserError – Raised if - Pool does not come from
pools()
. -client
kwargs was not supplied.- Returns
dict – JSON status response from API.
- class yippi.Classes.Post(json_data=None, *args, **kwargs)[source]¶
Bases:
yippi.Classes._BaseMixin
Representation of e621’s Post object.
- Parameters
data (optional) – The json server response of a
/posts.json
call.client (optional) – The yippi client, used for api calls.
- Variables
All (Any) – Refer to e621 API docs for available attributes.
- update(reason: Optional[str] = None) Union[List[dict], dict, Awaitable[Union[List[dict], dict]]] [source]¶
Updates the post. This function has not been tested.
- Parameters
reason (optional) – Reasoning behind the edit. Defaults to None.
- Raises
UserError – If the post did not come from any Post endpoint or if no changes has been made.
- vote(score: int = 1, replace: bool = False) dict [source]¶
Vote the post.
If you want to cancel your vote, repeat the same function again with same score value, but with replace set to False.
- Parameters
score (optional) – Score to be given, this could be either 1 or -1, with
1 represents vote up and -1 represent vote down. Defaults to 1.
replace (optional) – Replaces old vote or not. Defaults to False.
- Raises
UserError – Raised if - Post does not come from
post()
orposts()
. - If the value ofscore
is out of scope. -client
kwargs was not supplied.- Returns
dict – JSON response with keys
score
,up
,down
, andour_score
. Wheredict['our_score']
is 1, 0, -1 depending on the action.
yippi.Exceptions module¶
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
Bug reports¶
When reporting a bug please include:
Your operating system name and version.
Any details about your local setup that might be helpful in troubleshooting.
Detailed steps to reproduce the bug.
Documentation improvements¶
Yippi could always use more documentation, whether as part of the official yippi docs, in docstrings, or even on the web in blog posts, articles, and such.
Feature requests and feedback¶
The best way to send feedback is to file an issue at https://github.com/rorre/yippi/issues.
If you are proposing a feature:
Explain in detail how it would work.
Keep the scope as narrow as possible, to make it easier to implement.
Remember that this is a volunteer-driven project, and that code contributions are welcome :)
Development¶
To set up yippi for local development:
Fork yippi (look for the “Fork” button).
Clone your fork locally:
git clone git@github.com:rorre/yippi.git
Create a branch for local development:
git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes run all the checks and docs builder with tox one command:
tox
Commit your changes and push your branch to GitHub:
git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
If you need some code review or feedback while you’re developing the code just make the pull request.
For merging, you should:
Include passing tests (run
tox
) 1.Update documentation when there’s new API, functionality etc.
Add a note to
CHANGELOG.rst
about the changes.Add yourself to
AUTHORS.rst
.
- 1
If you don’t have all the necessary python versions available locally you can rely on Travis - it will run the tests for each change you add in the pull request.
It will be slower though …
Tips¶
To run a subset of tests:
tox -e envname -- pytest -k test_myfeature
To run all the test environments in parallel (you need to pip install detox
):
detox
Changelog¶
0.2.0 (2021-06-19)¶
Drop Python 3.6 support. The library now requires Python 3.7 or higher.
Bring back rate limit. It is now compliant to e621’s docs rate limiting, that is 2 requests per second.
Add ability to login with API key.
Add ability to search pools.
Post.rating is now an Enum.
Better type annotations. Thank you Deer-Spangle for the help!
Handle 204 response properly from server by returning None.
Handle errors properly by looking up if either message or response key exists in response.
Add ability to use custom requests session on YippiSync client.
All classes now have reference to the client. This is important for interaction abilities.
Add __repr__ to classes.
- Add ability to favorite, unfavorite, and vote Post.
Note: There are other functions, but those are not tested and should not be used as of now.
0.1.0 (2020-04-19)¶
Initial release.