from typing import Awaitable as Aw
from typing import Dict, List, Optional, Type
from typing import Union as Un
from .data_classes import (
CalendarDateArg,
Data,
Date,
Ism,
NextPrayerData,
Parameters,
Prayer,
Qibla,
Timings,
TimingsDateArg,
)
from .http import HTTPClient
from .methods import Method, all_methods
from .types import IMR, SDR, StatusR
TimingsR = Un[Timings, Aw[Timings]]
_Calendar = Un[List[Timings], Dict[str, Timings]]
CalendarR = Un[_Calendar, Aw[_Calendar]]
QiblaR = Un[Qibla, Aw[Qibla]]
AsmaR = Un[List[Ism], Aw[List[Ism]]]
DateR = Un[Date, Aw[Date]]
LDateR = Un[List[Date], Aw[List[Date]]]
PrayerR = Un[Prayer, Aw[Prayer]]
IntR = Un[int, Aw[int]]
StrR = Un[str, Aw[str]]
ListR = Un[list, Aw[list]]
__all__ = ("Client",)
[docs]class Client:
"""
Al-adhan API client.
Set to synchronous usage by default, set is_async to True
if asynchronous usage wanted.
Auto handles rate limits by default, set auto_manage_rate to False if
otherwise is wanted. *New in v1.2.0*
Example
.. tab:: Synchronous
.. code:: py
import aladhan
client = aladhan.Client()
times = client.get_timings_by_address("New York")
print(times)
.. tab:: Asynchronous
.. code:: py
import aladhan, asyncio
async def main():
# --- manual closing session
client = aladhan.Client(is_async=True)
times = await client.get_timings_by_address("New York")
print(times)
await client.close()
# --- using context
async with aladhan.Client(is_async=True) as client:
times = await client.get_timings_by_address("New York")
print(times)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
.. note::
For Asynchronous usage you need to initialize this class in
a |coroutine_link|_.
Parameters
----------
is_async: :class:`bool`
Whether to be used asynchronously or not.
auto_manage_rate: :class:`bool`
Whether to handle rate limits automatically or not.
"""
__slots__ = "converter", "http"
def __init__(self, is_async: bool = False, auto_manage_rate: bool = True):
self.converter: Un[Type[_AsyncConverter], Type[_SyncConverter]]
if is_async:
self.converter = _AsyncConverter
else:
self.converter = _SyncConverter
self.http = HTTPClient(
is_async=is_async, auto_manage_rate=auto_manage_rate
)
[docs] def close(self):
"""Closes the connection."""
return self.http.close() # this can be a coroutine
@property
def is_async(self) -> bool:
return self.http.is_async
def __enter__(self):
if self.is_async: # pragma: no cover
raise TypeError(
"Asynchronous client must be used in an asynchronous context"
"manager (async with) not in a synchronous one (with)."
)
return self
def __exit__(self, *_):
self.close()
async def __aenter__(self):
if not self.is_async: # pragma: no cover
raise TypeError(
"Synchronous client must be used in a synchronous context"
"manager (with) not in an asynchronous one (async with)."
)
return self
async def __aexit__(self, *_):
await self.close()
[docs] def get_next_prayer_by_address(
self,
address: str,
date: Optional[TimingsDateArg] = None,
params: Optional[Parameters] = None,
) -> PrayerR:
"""
Get next upcoming prayer from address.
Parameters
----------
address: :class:`str`
An address string.
Example: "London, United Kingdom"
date: Optional[:class:`TimingsDateArg`]
Date for the prayer times.
Default: Current date.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`Prayer`
Prayer obj from the API response.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
*New in v1.2.0*
"""
date_str = "" if date is None else date.date
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(dict(address=address))
return self.converter.to_prayer(
self,
self.http.get_next_prayer_by_address(date_str, params_dict),
)
[docs] def get_timings(
self,
longitude: Un[int, float],
latitude: Un[int, float],
date: Optional[TimingsDateArg] = None,
params: Optional[Parameters] = None,
) -> TimingsR:
"""
Get prayer times from coordinates (longitude, latitude).
Parameters
----------
longitude: :class:`int` or :class:`float`
Longitude coordinate of the location.
latitude: :class:`int` or :class:`float`
Latitude coordinate of the location.
date: Optional[:class:`TimingsDateArg`]
Date for the prayer times.
Default: Current date.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`Timings`
Timings obj from the API response.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
date_str = "" if date is None else date.date
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(
dict(longitude=str(longitude), latitude=str(latitude))
)
return self.converter.to_timings(
self, self.http.get_timings(date_str, params_dict)
)
[docs] def get_timings_by_address(
self,
address: str,
date: Optional[TimingsDateArg] = None,
params: Optional[Parameters] = None,
) -> TimingsR:
"""
Get prayer times from address.
Parameters
----------
address: :class:`str`
An address string.
Example: "London, United Kingdom"
date: Optional[:class:`TimingsDateArg`]
Date for the prayer times.
Default: Current date.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`Timings`
Timings obj from the API response.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
date_str = "" if date is None else date.date
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(dict(address=address))
return self.converter.to_timings(
self, self.http.get_timings_by_address(date_str, params_dict)
)
[docs] def get_timings_by_city(
self,
city: str,
country: str,
state: Optional[str] = None,
date: Optional[TimingsDateArg] = None,
params: Optional[Parameters] = None,
) -> TimingsR:
"""
Get prayer times from city, country and state.
Parameters
----------
city: :class:`str`
The city name.
Example: "London"
country: :class:`str`
The country name or 2 character alpha ISO 3166 code.
Example: "GB" or "United Kingdom"
state: Optional[:class:`str`]
State or province. The state name or abbreviation..
Example: "Bexley"
date: Optional[:class:`TimingsDateArg`]
Date for the prayer times.
Default: Current date.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`Timings`
Timings obj from the API response.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
date_str = "" if date is None else date.date
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(dict(city=city, country=country, state=state))
if state is None:
del params_dict["state"]
return self.converter.to_timings(
self, self.http.get_timings_by_city(date_str, params_dict)
)
[docs] def get_calendar(
self,
longitude: Un[int, float],
latitude: Un[int, float],
date: CalendarDateArg,
params: Optional[Parameters] = None,
) -> CalendarR:
"""
Get all prayer times for a specific calendar month/year from \
coordinates (longitude, latitudes).
Parameters
----------
longitude: :class:`int` or :class:`float`
Longitude coordinate of the location.
latitude: :class:`int` or :class:`float`
Latitude coordinate of the location.
date: :class:`CalendarDateArg`
Date for the prayer times.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`list` of :class:`Timings` or dict[:class:`str`, \
:class:`list` of :class:`Timings`]
A month calendar if month parameter was given in date
argument otherwise a year calendar.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(longitude=str(longitude), latitude=str(latitude))
params_dict.update(date.as_dict)
return self.converter.to_timings(
self, self.http.get_calendar(params_dict, date.hijri)
)
[docs] def get_calendar_by_address(
self,
address: str,
date: CalendarDateArg,
params: Optional[Parameters] = None,
) -> CalendarR:
"""
Get all prayer times for a specific calendar month/year
from address.
Parameters
----------
address: :class:`str`
An address string.
Example: "London, United Kingdom"
date: :class:`CalendarDateArg`
Date for the prayer times.
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`list` of :class:`Timings` or dict[:class:`str`, \
:class:`list` of :class:`Timings`]
A month calendar if month parameter was given in date
argument otherwise a year calendar.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(dict(address=address))
params_dict.update(date.as_dict)
return self.converter.to_timings(
self,
self.http.get_calendar_by_address(params_dict, date.hijri),
)
[docs] def get_calendar_by_city(
self,
city: str,
country: str,
date: CalendarDateArg,
state: Optional[str] = None,
params: Optional[Parameters] = None,
) -> CalendarR:
"""
Get all prayer times for a specific calendar month/year
from address.
Parameters
----------
city: :class:`str`
The city name.
Example: "London"
country: :class:`str`
The country name or 2 character alpha ISO 3166 code.
Example: "GB" or "United Kingdom"
date: :class:`CalendarDateArg`
Date for the prayer times.
state: Optional[:class:`str`]
State or province. The state name or abbreviation..
Example: "Bexley"
params: Optional[:class:`Parameters`]
Default: ``Parameters()``
Returns
-------
:class:`list` of :class:`Timings` or dict[:class:`str`, \
:class:`list` of :class:`Timings`]
A month calendar if month parameter was given in date
argument otherwise a year calendar.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
"""
if params is None:
params = Parameters()
params_dict = params.as_dict
params_dict.update(dict(city=city, country=country, state=state))
if state is None:
del params_dict["state"]
params_dict.update(date.as_dict)
return self.converter.to_timings(
self, self.http.get_calendar_by_city(params_dict, date.hijri)
)
[docs] @staticmethod
def get_all_methods() -> Dict[int, Method]:
"""
Gives all available prayer times calculation method.
Returns
-------
dict[:class:`int`, :class:`Method`]
A dict of available calculation method from 0 to 15.
"""
return all_methods
[docs] def get_qibla(
self, longitude: Un[int, float], latitude: Un[int, float]
) -> QiblaR:
"""
Get the Qibla direction from a pair of coordinates.
Parameters
----------
longitude: :class:`int` or :class:`float`
Longitude co-ordinate.
latitude: :class:`int` or :class:`float`
Latitude co-ordinate.
Returns
-------
:class:`Qibla`
The qibla.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
*New in v0.1.3*
"""
return self.converter.to_obj_kwa(
self.http.get_qibla(latitude, longitude), Qibla
)
[docs] def get_asma(self, *n: int) -> AsmaR:
"""
Returns a list of asma from giving numbers.
Parameters
----------
n: :class:`int`
Numbers from range 1-99.
Returns
-------
:class:`list` of :class:`Ism`
A list of asma.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid parameter was passed.
*New in v0.1.3*
"""
assert n, "No arguments was passed."
return self.converter.to_list_of_obj(
self.http.get_asma(",".join(map(str, n))), Ism
)
[docs] def get_all_asma(self) -> AsmaR:
"""
Returns all 1-99 asma (allah names).
Returns
-------
:class:`list` of :class:`Ism`
A list of all asma.
*New in v0.1.3*
"""
return self.get_asma(*range(1, 100))
[docs] def get_hijri_from_gregorian(
self, date: Optional[TimingsDateArg] = None, adjustment: int = 0
) -> DateR:
"""
Convert a gregorian date to a hijri date.
Parameters
----------
date: Optional[:class:`TimingsDateArg`]
Gregorian date.
Default: Current date
adjustment: Optional[:class:`int`]
Number of days to adjust.
Default: 0
Returns
-------
:class:`Date`
Date in hijri.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid date or unable to convert it.
*New in v1.1.0*
"""
date = TimingsDateArg() if date is None else date
return self.converter.to_obj_kwa(
self.http.get_hijri_from_gregorian(
date=date.date, adjustment=adjustment
),
Date,
)
[docs] def get_gregorian_from_hijri(
self, date: TimingsDateArg, adjustment: int = 0
) -> DateR:
"""
Convert a hijri date to a gregorian date.
Parameters
----------
date: Optional[:class:`TimingsDateArg`]
Gregorian date.
Default: Current date
adjustment: Optional[:class:`int`]
Number of days to adjust.
Default: 0
Returns
-------
:class:`Date`
Date in gregorian.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid date or unable to convert it.
*New in v1.1.0*
"""
return self.converter.to_obj_kwa(
self.http.get_gregorian_from_hijri(
date=date.date, adjustment=adjustment
),
Date,
)
[docs] def get_hijri_calendar_from_gregorian(
self, month: int, year: int, adjustment: int = 0
) -> LDateR:
"""
Get a hijri calendar for a gregorian month.
Parameters
----------
month: :class:`int`
Gregorian month.
year: :class:`int`
Gregorian year.
adjustment: Optional[:class:`int`]
Number of days to adjust.
Default: 0
Returns
-------
:class:`list` of :class:`Date`
Hijri Calendar.
*New in v1.1.0*
"""
return self.converter.to_list_of_obj(
self.http.get_hijri_calendar_from_gregorian(
month, year, adjustment
),
Date,
)
[docs] def get_gregorian_calendar_from_hijri(
self, month: int, year: int, adjustment: int = 0
) -> LDateR:
"""
Get a gregorian calendar for a hijri month.
Parameters
----------
month: :class:`int`
Hijri month.
year: :class:`int`
Hijri year.
adjustment: Optional[:class:`int`]
Number of days to adjust.
Default: 0
Returns
-------
:class:`list` of :class:`Date`
Gregorian Calendar.
*New in v1.1.0*
"""
return self.converter.to_list_of_obj(
self.http.get_gregorian_calendar_from_hijri(
month, year, adjustment
),
Date,
)
[docs] def get_islamic_year_from_gregorian_for_ramadan(self, year: int) -> IntR:
"""
Get which islamic year for ramadan from a gregorian year.
Parameters
----------
year: :class:`int`
Gregorian year.
Returns
-------
:class:`int`
Hijri year.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Unable to compute year.
"""
return self.converter.to_obj_a(
self.http.get_islamic_year_from_gregorian_for_ramadan(year), int
)
[docs] def get_current_time(self, zone: str) -> StrR:
"""
Parameters
----------
zone: :class:`str`
Timezone string. ex: `"Europe/London"`
Returns
-------
:class:`str`
The current time for the specified time zone.
ex: `"13:56"`
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid timezone.
"""
return self.http.get_current_time(zone=zone)
[docs] def get_current_date(self, zone: str) -> StrR:
"""
Parameters
----------
zone: :class:`str`
Timezone string. ex: `"Europe/London"`
Returns
-------
:class:`str`
The current Date for the specified time zone in DD-MM-YYYY.
ex: `"23-02-2021"`
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid timezone.
"""
return self.http.get_current_date(zone=zone)
[docs] def get_current_timestamp(self, zone: str) -> IntR:
"""
Parameters
----------
zone: :class:`str`
Timezone string. ex: `"Europe/London"`
Returns
-------
:class:`int`
The current UNIX timestamp for the specified time zone.
ex: `1503495668`
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid timezone.
"""
return self.converter.to_obj_a(
self.http.get_current_timestamp(zone=zone), int
)
[docs] def get_current_islamic_year(self, adjustment: int = 0) -> IntR:
"""
Parameters
----------
adjustment: :class:`int`
Number of days to adjust hijri date.
Returns
-------
:class:`int`
The current islamic year.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Unable to compute year.
"""
return self.converter.to_obj_a(
self.http.get_current_islamic_year(adjustment=adjustment), int
)
[docs] def get_current_islamic_month(self, adjustment: int = 0) -> IntR:
"""
Parameters
----------
adjustment: :class:`int`
Number of days to adjust hijri date.
Returns
-------
:class:`int`
The current islamic month.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Unable to compute month.
"""
return self.converter.to_obj_a(
self.http.get_current_islamic_month(adjustment=adjustment), int
)
[docs] def get_next_hijri_holiday(self, adjustment: int = 0) -> DateR:
"""
Parameters
----------
adjustment: :class:`int`
Number of days to adjust hijri date.
Returns
-------
:class:`~aladhan.Date`
The Next upcoming hijri holiday.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Unable to compute next holiday.
"""
return self.converter.to_obj_kwa(
self.http.get_next_hijri_holiday(adjustment=adjustment), Date
)
[docs] def get_hijri_holidays(self, day: int, month: int) -> ListR:
"""
Parameters
----------
day: :class:`int`
Hijri day.
month: :class:`int`
Hijri month.
Returns
-------
:class:`list` of :class:`str`
All day's holidays, can be empty list.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Invalid day or month.
"""
return self.http.get_hijri_holidays(day, month)
[docs] def get_islamic_holidays(self, year: int, adjustment: int = 0) -> LDateR:
"""
Parameters
----------
year: :class:`int`
Hijri year.
adjustment: :class:`int`
Number of days to adjust hijri date.
Returns
-------
:class:`list` of :class:`~aladhan.Date`
All year's holidays, 19 in total.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Something went wrong.
"""
return self.converter.to_list_of_obj(
self.http.get_islamic_holidays(year, adjustment), Date
)
[docs] def get_status(self) -> StatusR:
"""
Returns
-------
:class:`dict`
Api's status.
Raises
------
:exc:`~aladhan.exceptions.InternalServerError`
Status Check Failed.
"""
return self.http.get_status()
[docs] def get_special_days(self) -> SDR:
"""
Get all islamic special days (holidays).
Returns
-------
:class:`list` of :class:`dict`
A list of all special days with ``day,month,name`` keys.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Something went wrong.
"""
return self.http.get_special_days()
[docs] def get_islamic_months(self) -> IMR:
"""
Get all 12 islamic months.
Returns
-------
:class:`dict` of :class:`str` and :class:`dict`
A dict of '1' to '12' as keys and dicts of ``number,en,ar`` keys.
Raises
------
:exc:`~aladhan.exceptions.BadRequest`
Something went wrong.
"""
return self.http.get_islamic_months()
def _decide_timings(client, data):
if isinstance(data, list): # it is a month calendar
return [Data(**day, client=client).timings for day in data]
# it is a dict
if "1" in data: # it is a year calendar
return {
month: [Data(**day, client=client).timings for day in days]
for month, days in data.items()
}
# it is just a day timings
return Data(**data, client=client).timings
class _SyncConverter:
@staticmethod
def to_prayer(client, o):
return NextPrayerData(client=client, **o).prayer
@staticmethod
def to_timings(client, o):
return _decide_timings(client, o)
@staticmethod
def to_obj_a(o, obj):
return obj(o)
@staticmethod
def to_obj_kwa(o, obj):
return obj(**o)
@staticmethod
def to_list_of_obj(o, obj):
return [obj(**d) for d in o]
class _AsyncConverter:
@staticmethod
async def to_prayer(client, o):
return NextPrayerData(client=client, **(await o)).prayer
@staticmethod
async def to_timings(client, o):
return _decide_timings(client, await o)
@staticmethod
async def to_obj_a(o, obj):
return obj(await o)
@staticmethod
async def to_obj_kwa(o, obj):
return obj(**(await o))
@staticmethod
async def to_list_of_obj(o, obj):
return [obj(**d) for d in await o]