diff --git a/.gitignore b/.gitignore index 965a7a1..05221f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -*__pycache__ -venv/ \ No newline at end of file +**/__pycache__ +venv/ +test.md diff --git a/README.md b/README.md index 68bab85..a07b50b 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ An Api Wrapper for extracting info from: quran api and bible api. I didn\'t made the document for this so for now read the file to find all functions there. I will add more holybooks in the future! -# Installation +## Installation -Find the module in here [Module](https://pypi.org/project/holybooks/) +Find the module in here [Module](https://pypi.org/project/holybooks/) ```bash pip install holybooks ``` -# Usage +## Usage ```python # To return a verse(s) info inside the surah(Chapter) @@ -35,7 +35,7 @@ for verse in bible_verses.verses: verse.citation, # The citation of the current verse verse # executes the __str__ method of the ChapterVerse class (it returns the verse itself) ) - + #=============================================================================================================================== from holybooks import Torah @@ -52,7 +52,7 @@ for verse in torah_verses.verses: ) ``` -# API That I Used +## API That I Used [Qur'an](https://alquran.cloud/api) @@ -60,12 +60,12 @@ for verse in torah_verses.verses: [Torah](https://bible-api.com/) -# Contributing +## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate. -# License +## License [MIT](https://choosealicense.com/licenses/mit/) diff --git a/holybooks/__init__.py b/holybooks/__init__.py index 3be5a55..038adce 100644 --- a/holybooks/__init__.py +++ b/holybooks/__init__.py @@ -1,3 +1,4 @@ from .quran import * from .bible import * from .torah import * +from .errors import * diff --git a/holybooks/bible.py b/holybooks/bible.py index 6892949..d1fb839 100644 --- a/holybooks/bible.py +++ b/holybooks/bible.py @@ -1,16 +1,6 @@ -__all__ = ("ApiError", "NotFound", "ChapterVerse", "Bible") +from .errors import ApiError, NotFound - -class ApiError(Exception): - def __init__(self, status: int, msg: str) -> None: - super().__init__(f"Api has an error, return code: {status}.\n{msg}") - - -class NotFound(Exception): - def __init__(self, book: str, chapter: int, verse: str) -> None: - super().__init__( - f"Book {book}, Chapter {chapter}, Verse(s) {verse} Wasn't Found." - ) +__all__ = ("ChapterVerse", "Bible") def _build_verse(start: int, end: int = None) -> str: @@ -39,10 +29,21 @@ def citation(self) -> str: class Bible: def __init__(self, book: str) -> None: self.book = book + self._session = None + self._async_session = None + self.json = None + self.verses = None + self.raw_verse = None + self._request = None @classmethod def request( - cls, book: str, *, chapter: int, starting_verse: int, ending_verse: int = None + cls, + book: str, + *, + chapter: int, + starting_verse: int, + ending_verse: int = None, ): try: import requests @@ -54,15 +55,21 @@ def request( self = cls(book) verse = _build_verse(starting_verse, ending_verse) - self.request = requests.get(f"https://bible-api.com/{book}+{chapter}:{verse}") - if self.request.status_code == 404: + if not self._session: + self._session = requests.Session() + + self._request = self._session.get( + f"https://bible-api.com/{book}+{chapter}:{verse}" + ) + if self._request.status_code == 404: raise NotFound(book, chapter, verse) - elif self.request.status_code > 202: + elif self._request.status_code > 202: raise ApiError( - self.request.status_code, self.request.json().get("error", "") + self._request.status_code, + self._request.json().get("error", ""), ) - self.json = self.request.json() + self.json = self._request.json() self.verses = [ChapterVerse(i) for i in self.json["verses"]] return self @@ -87,26 +94,45 @@ async def async_request( self = cls(book) verse = _build_verse(starting_verse, ending_verse) - async with aiohttp.ClientSession(loop=loop) as session: + if not self._async_session: + self._async_session = aiohttp.ClientSession(loop=loop) + + async with self._async_session as session: async with session.get( f"https://bible-api.com/{book}+{chapter}:{verse}" ) as resp: - self.request = resp + self._request = resp self.json = await resp.json() self.verses = [ChapterVerse(i) for i in self.json["verses"]] self.raw_verse = self.json["text"] - if self.request.status == 404: + if self._request.status == 404: raise NotFound(book, chapter, verse) - elif self.request.status > 202: - raise ApiError(self.request.status, self.json.get("error", "")) + elif self._request.status > 202: + raise ApiError(self._request.status, self.json.get("error", "")) return self @property def citation(self) -> str: + if not self.json: + return None return self.json["reference"] @property def translation(self) -> str: + if not self.json: + return None return self.json["translation_name"] + + def __enter__(self): + return self + + def __exit__(self, *args): + return + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return diff --git a/holybooks/errors.py b/holybooks/errors.py new file mode 100644 index 0000000..c3f81be --- /dev/null +++ b/holybooks/errors.py @@ -0,0 +1,67 @@ +from typing import TYPE_CHECKING +from typing import Optional, Union + +__all__ = ( + "ApiError", + "ContentTypeError", + "NumberError", + "WrongLang", + "NotFound", + "BibleOnly", +) + + +class ApiError(Exception): + def __init__(self, status: int, msg: str) -> None: + super().__init__(f"Api has an error, return code: {status}.\n{msg}") + + +class ContentTypeError(Exception): + def __init__( + self, + class_: str, + mode: str, + first_query: Optional[Union[str, int]], + second_query: Optional[Union[str, int]], + ) -> None: + super().__init__( + f"Attempt to decode JSON with unexpected mimetype: Return code: None\nlink: http://api.alquran.cloud/v1/{mode}/{first_query}:{second_query}/en.asad" + if class_ == "Ayah" + else f"Attempt to decode JSON with unexpected mimetype: Return code: None\nlink: http://api.alquran.cloud/v1/{mode}/{first_query}/{second_query}/en.asad" + ) + + +class NumberError(Exception): + def __init__( + self, + mode: int, + obj: str, + first_query: int, + second_query: Optional[Union[str, int]], + ) -> None: + super().__init__( + f"{obj} must above {first_query}" + if mode == 0 + else f"{obj} must be between {first_query} to {second_query}" + ) + + +class WrongLang(Exception): + def __init__(self, lang: str) -> None: + super().__init__( + f"The lang '{lang}' is not supported, it only support arabic(ar) and english(eng)" + ) + + +class NotFound(Exception): + def __init__(self, book: str, chapter: int, verse: str) -> None: + super().__init__( + f"Book {book}, Chapter {chapter}, Verse(s) {verse} Wasn't Found." + ) + + +class BibleOnly(Exception): + def __init__(self, book: str) -> None: + super().__init__( + f"The book {book} wasn't found because its available only in Bible" + ) diff --git a/holybooks/quran.py b/holybooks/quran.py index 7ad8699..7ead2df 100644 --- a/holybooks/quran.py +++ b/holybooks/quran.py @@ -1,61 +1,21 @@ from typing import Union, Optional +from .errors import ApiError, WrongLang, NumberError, ContentTypeError __all__ = ( "Surah", "Ayah", "Search", - "ApiError", - "ContentTypeError", - "NumberError", - "WrongLang", ) -class ApiError(Exception): - def __init__(self, status: int, msg: str) -> None: - super().__init__(f"Api has an error, return code: {status}.\n{msg}") - - -class ContentTypeError(Exception): - def __init__( - self, - class_: str, - mode: str, - first_query: Optional[Union[str, int]], - second_query: Optional[Union[str, int]], - ) -> None: - super().__init__( - f"Attempt to decode JSON with unexpected mimetype: Return code: None\nlink: http://api.alquran.cloud/v1/{mode}/{first_query}:{second_query}/en.asad" - if class_ == "Ayah" - else f"Attempt to decode JSON with unexpected mimetype: Return code: None\nlink: http://api.alquran.cloud/v1/{mode}/{first_query}/{second_query}/en.asad" - ) - - -class NumberError(Exception): - def __init__( - self, - mode: int, - obj: str, - first_query: int, - second_query: Optional[Union[str, int]], - ) -> None: - super().__init__( - f"{obj} must above {first_query}" - if mode == 0 - else f"{obj} must be between {first_query} to {second_query}" - ) - - -class WrongLang(Exception): - def __init__(self, lang: str) -> None: - super().__init__( - f"The lang '{lang}' is not supported, it only support arabic(ar) and english(eng)" - ) - - class Surah: def __init__(self, surah: int = None): self.surah = surah + self._session = None + self._async_session = None + self.data = None + self.ayah = None + self._request = None @classmethod def request(cls, surah: int = None): @@ -68,13 +28,18 @@ def request(cls, surah: int = None): self = cls(surah) - self.request = requests.get( + if not self._session: + self._session = requests.Session() + + self._request = self._session.get( f"https://api.alquran.cloud/v1/surah/{surah}/en.asad" ).json() - self.data = self.request["data"] + self.data = self._request["data"] self.ayah = self.data["ayahs"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @@ -88,26 +53,38 @@ async def async_request(cls, surah: int = None, *, loop=None): ) self = cls(surah) - async with aiohttp.ClientSession(loop=loop) as session: + + if not self._async_session: + self._async_session = aiohttp.ClientSession(loop=loop) + + async with self._async_session as session: async with session.get( f"https://api.alquran.cloud/v1/surah/{surah}/en.asad" ) as resp: - self.request = await resp.json() - self.data = self.request["data"] + self._request = await resp.json() + self.data = self._request["data"] self.ayah = self.data["ayahs"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @property def api_code(self): - return self.request["code"] + if not self._request: + return None + return self._request["code"] @property def api_status(self): - return self.request["status"] + if not self._request: + return None + return self._request["status"] def name(self, lang: str = "ar"): + if not self.data: + return None if lang == "ar" or lang.lower() == "arabic": return self.data["name"] @@ -130,6 +107,8 @@ def number_ayahs(self): return self.data["numberOfAyahs"] def request_ayahs(self): + if not self.ayah: + return None data = [] for number in range(Surah.request(self.surah).number_ayahs): data.append(f"{self.ayah[number]['text']}") @@ -150,7 +129,9 @@ async def ayah_info( sajda: bool = True, ): if ayah <= 0: - raise NumberError(mode=0, obj="ayah", first_query=1, second_query=None) + raise NumberError( + mode=0, obj="ayah", first_query=1, second_query=None + ) elif ayah > int(Surah.request(self.surah).number_ayahs): raise NumberError( @@ -172,20 +153,35 @@ async def ayah_info( else None, "juz": self.ayah[ayah]["juz"] if juz else None, "manzil": self.ayah[ayah]["manzil"] if manzil else None, - "page": self.ayah[ayah]["page"] if text else None, - "ruku": self.ayah[ayah]["ruku"] if number_in_quran else None, + "page": self.ayah[ayah]["page"] if page else None, + "ruku": self.ayah[ayah]["ruku"] if ruku else None, "hizbquarter": self.ayah[ayah]["hizbQuarter"] - if number_in_surah + if hizbquarter else None, - "sajda": self.ayah[ayah]["sajda"] if juz else None, + "sajda": self.ayah[ayah]["sajda"] if sajda else None, } return data + def __enter__(self): + return self + + def __exit__(self, *args): + return + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return class Ayah: def __init__(self, surah: int = None, *, ayah: int = None): self.surah = surah self.ayah = ayah + self._request = None + self.data = None + self._session = None + self._async_session = None @classmethod def request(cls, surah: int = None, *, ayah: int = None, loop=None): @@ -197,16 +193,24 @@ def request(cls, surah: int = None, *, ayah: int = None, loop=None): ) self = cls(surah, ayah=ayah) - self.request = requests.get( + + if not self._session: + self._session = requests.Session() + + self._request = self._session.get( f"http://api.alquran.cloud/v1/ayah/{surah}:{ayah}/en.asad" ).json() - self.data = self.request["data"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + self.data = self._request["data"] + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @classmethod - async def async_request(cls, surah: int = None, *, ayah: int = None, loop=None): + async def async_request( + cls, surah: int = None, *, ayah: int = None, loop=None + ): try: import aiohttp except ImportError: @@ -216,30 +220,53 @@ async def async_request(cls, surah: int = None, *, ayah: int = None, loop=None): self = cls(surah, ayah=ayah) + if not self._async_session: + self._async_session = aiohttp.ClientSession(loop=loop) + try: - async with aiohttp.ClientSession(loop=loop) as session: + async with self._async_session as session: async with session.get( f"http://api.alquran.cloud/v1/ayah/{surah}:{ayah}/en.asad" ) as resp: - self.request = await resp.json() + self._request = await resp.json() except aiohttp.client_exceptions.ContentTypeError: raise ContentTypeError( - class_="Ayah", mode="ayah", first_query=surah, second_query=surah + class_="Ayah", + mode="ayah", + first_query=surah, + second_query=surah, ) - self.data = self.request["data"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + self.data = self._request["data"] + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @property def api_code(self): - return self.request["code"] + if not self._request: + return None + return self._request["code"] @property def api_status(self): - return self.request["status"] + if not self._request: + return None + return self._request["status"] + + def __enter__(self): + return self + + def __exit__(self, *args): + return + + async def __aenter__(self): + return self + async def __aexit__(self, *args): + return class Search: def __init__( @@ -252,6 +279,10 @@ def __init__( self.mention = mention self.surah = surah self.req = request + self.data = None + self.matches = None + self._session = self._async_session = None + self._request = None @classmethod async def async_request( @@ -271,21 +302,29 @@ async def async_request( self = cls(mention, surah=surah, request=request) + if not self._async_session: + self._async_session = aiohttp.ClientSession(loop=loop) + try: - async with aiohttp.ClientSession(loop=loop) as session: + async with self._async_session as session: async with session.get( f"http://api.alquran.cloud/v1/search/{mention}/{surah}/en.pickthall" ) as resp: - self.request = await resp.json() + self._request = await resp.json() except aiohttp.client_exceptions.ContentTypeError: raise ContentTypeError( - class_="Search", mode="search", first_query=mention, second_query=surah + class_="Search", + mode="search", + first_query=mention, + second_query=surah, ) - self.data = self.request["data"] + self.data = self._request["data"] self.matches = self.data["matches"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @@ -305,28 +344,42 @@ def request( ) self = cls(mention, surah=surah, request=request) - self.request = requests.get( + + if not self._session: + self._session = requests.Session() + + self._request = self._session.get( f"http://api.alquran.cloud/v1/search/{mention}/{surah}/en.pickthall" ).json() - self.data = self.request["data"] + self.data = self._request["data"] self.matches = self.data["matches"] - if self.request["code"] > 202: - raise ApiError(code=self.request["code"], msg=self.request["data"]) + if self._request["code"] > 202: + raise ApiError( + code=self._request["code"], msg=self._request["data"] + ) return self @property def count(self): + if not self.data: + return None return self.data["count"] @property def api_code(self): - return self.request["code"] + if not self._request: + return None + return self._request["code"] @property def api_status(self): - return self.request["status"] + if not self._request: + return None + return self._request["status"] def find(self): + if self.data is None or self.matches is None: + return None data = [] if self.req == None: for num in range(self.data["count"]): @@ -337,3 +390,15 @@ def find(self): for num in range(self.req): data.append(self.matches[num]["text"]) return data + + def __enter__(self): + return self + + def __exit__(self, *args): + return + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return diff --git a/holybooks/torah.py b/holybooks/torah.py index 67b9f72..6764198 100644 --- a/holybooks/torah.py +++ b/holybooks/torah.py @@ -1,26 +1,7 @@ from .bible import Bible +from .errors import BibleOnly - -__all__ = ("ApiError", "NotFound", "Torah", "BibleOnly") - - -class ApiError(Exception): - def __init__(self, status: int, msg: str) -> None: - super().__init__(f"Api has an error, return code: {status}.\n{msg}") - - -class NotFound(Exception): - def __init__(self, book: str, chapter: int, verse: str) -> None: - super().__init__( - f"Book {book}, Chapter {chapter}, Verse(s) {verse} Wasn't Found." - ) - - -class BibleOnly(Exception): - def __init__(self, book: str) -> None: - super().__init__( - f"The book {book} wasn't found because its available only in Bible" - ) +__all__ = ("Torah",) class Torah(Bible): @@ -29,7 +10,12 @@ def __init__(self, book: str) -> None: @classmethod def request( - cls, book: str, *, chapter: int, starting_verse: int, ending_verse: int = None + cls, + book: str, + *, + chapter: int, + starting_verse: int, + ending_verse: int = None, ): if book.lower() not in [ "genesis", @@ -80,8 +66,24 @@ async def async_request( @property def citation(self) -> str: + if not self.json: + return None return self.json["reference"] @property def translation(self) -> str: + if not self.json: + return None return self.json["translation_name"] + + def __enter__(self): + return self + + def __exit__(self, *args): + return + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return diff --git a/setup.py b/setup.py index a169117..dcf3544 100644 --- a/setup.py +++ b/setup.py @@ -1,28 +1,36 @@ from setuptools import setup, find_packages - -with open('README.md', 'r', encoding = 'utf-8') as f: + +with open("README.md", "r", encoding="utf-8") as f: long_description = f.read() classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Education', - 'Operating System :: Microsoft :: Windows :: Windows 10', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3' + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Education", + "Operating System :: Microsoft :: Windows :: Windows 10", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", ] - + setup( - name='holybooks', - version='0.1.2', - url='https://github.com/TheGenocides/holybooks', - description='An Api Wrapper for extracting info from: quran api and bible api.', - long_description=long_description, - long_description_content_type = 'text/markdown', - author='TheGenocide', - author_email='luke.genesis.hyder@gmail.com', - license='MIT', - classifiers=classifiers, - keywords=['holybooks', 'holybook', 'scripture', 'kitab', 'quran', 'bible', 'torah'], - packages=find_packages(), - install_requires=['requests', 'aiohttp'] + name="holybooks", + version="0.1.2", + url="https://github.com/TheGenocides/holybooks", + description="An Api Wrapper for extracting info from: quran api and bible api.", + long_description=long_description, + long_description_content_type="text/markdown", + author="TheGenocide", + author_email="luke.genesis.hyder@gmail.com", + license="MIT", + classifiers=classifiers, + keywords=[ + "holybooks", + "holybook", + "scripture", + "kitab", + "quran", + "bible", + "torah", + ], + packages=find_packages(), + install_requires=["requests", "aiohttp"], )