def expand_address(address, languages=None, **kw):
    """
    Expand the given address into one or more normalized strings.

    Required
    --------
    @param address: the address as either Unicode or a UTF-8 encoded string

    Options
    -------
    @param languages: a tuple or list of ISO language code strings (e.g. "en", "fr", "de", etc.)
                      to use in expansion. If None is passed, use language classifier
                      to detect language automatically.
    @param address_components: an integer (bit-set) of address component expansions
                               to use e.g. ADDRESS_NAME | ADDRESS_STREET would use
                               only expansions which apply to venue names or streets.
    @param latin_ascii: use the Latin to ASCII transliterator, which normalizes e.g. æ => ae
    @param transliterate: use any available transliterators for non-Latin scripts, e.g.
                          for the Greek phrase διαφορετικούς becomes diaphoretikoús̱
    @param strip_accents: strip accented characters e.g. é => e, ç => c. This loses some
                          information in various languags, but in general we want
    @param decompose: perform Unicode normalization (NFD form)
    @param lowercase: UTF-8 lowercase the string
    @param trim_string: trim spaces on either side of the string
    @param replace_word_hyphens: add version of the string replacing hyphens with space
    @param delete_word_hyphens: add version of the string with hyphens deleted
    @param replace_numeric_hyphens: add version of the string with numeric hyphens replaced
                                    e.g. 12345-6789 => 12345 6789
    @param delete_numeric_hyphens: add version of the string with numeric hyphens removed
                                   e.g. 12345-6789 => 123456789
    @param split_alpha_from_numeric: split tokens like CR17 into CR 17, helps with expansion
                                     of certain types of highway abbreviations
    @param delete_final_periods: remove final periods on abbreviations e.g. St. => St
    @param delete_acronym_periods: remove periods in acronyms e.g. U.S.A. => USA
    @param drop_english_possessives: normalize possessives e.g. Mark's => Marks
    @param delete_apostrophes: delete other types of hyphens e.g. O'Malley => OMalley
    @param expand_numex: converts numeric expressions e.g. Twenty sixth => 26th,
                         using either the supplied languages or the result of
                         automated language classification.
    @param roman_numerals: normalize Roman numerals e.g. IX => 9. Since these can be
                           ambiguous (especially I and V), turning this on simply
                           adds another version of the string if any potential
                           Roman numerals are found.
    """
    address = safe_decode(address, 'utf-8')
    return _expand.expand_address(address, languages=languages, **kw)

def expand_address ( address , languages = None , ** kw ) : address = safe_decode ( address , 'utf-8' ) return _expand . expand_address ( address , languages = languages , ** kw )

Expand the given address into one or more normalized strings .


def normalized_tokens(s, string_options=DEFAULT_STRING_OPTIONS,
                      token_options=DEFAULT_TOKEN_OPTIONS,
                      strip_parentheticals=True, whitespace=False,
                      languages=None):
    '''
    Normalizes a string, tokenizes, and normalizes each token
    with string and token-level options.

    This version only uses libpostal's deterministic normalizations
    i.e. methods with a single output. The string tree version will
    return multiple normalized strings, each with tokens.

    Usage:
        normalized_tokens(u'St.-Barthélemy')
    '''
    s = safe_decode(s)
    normalized_tokens = _normalize.normalized_tokens(s, string_options, token_options, whitespace, languages=languages)

    if strip_parentheticals:
        normalized_tokens = remove_parens(normalized_tokens)

    return [(s, token_types.from_id(token_type)) for s, token_type in normalized_tokens]

def normalized_tokens ( s , string_options = DEFAULT_STRING_OPTIONS , token_options = DEFAULT_TOKEN_OPTIONS , strip_parentheticals = True , whitespace = False , languages = None ) : s = safe_decode ( s ) normalized_tokens = _normalize . normalized_tokens ( s , string_options , token_options , whitespace , languages = languages ) if strip_parentheticals : normalized_tokens = remove_parens ( normalized_tokens ) return [ ( s , token_types . from_id ( token_type ) ) for s , token_type in normalized_tokens ]

Normalizes a string tokenizes and normalizes each token with string and token - level options .


def parse_address(address, language=None, country=None):
    """
    Parse address into components.

    @param address: the address as either Unicode or a UTF-8 encoded string
    @param language (optional): language code
    @param country (optional): country code
    """
    address = safe_decode(address, 'utf-8')
    return _parser.parse_address(address, language=language, country=country)

def parse_address ( address , language = None , country = None ) : address = safe_decode ( address , 'utf-8' ) return _parser . parse_address ( address , language = language , country = country )


def near_dupe_hashes(labels, values, languages=None, **kw):
    """
    Hash the given address into normalized strings that can be used to group similar
    addresses together for more detailed pairwise comparison. This can be thought of
    as the blocking function in record linkage or locally-sensitive hashing in the
    document near-duplicate detection.

    Required
    --------
    @param labels: array of component labels as either Unicode or UTF-8 encoded strings
                   e.g. ["house_number", "road", "postcode"]
    @param values: array of component values as either Unicode or UTF-8 encoded strings
                   e.g. ["123", "Broadway", "11216"]. Note len(values) must be equal to
                   len(labels).

    Options
    -------
    @param languages: a tuple or list of ISO language code strings (e.g. "en", "fr", "de", etc.)
                      to use in expansion. If None is passed, use language classifier
                      to detect language automatically.
    @param with_name: use name in the hashes
    @param with_address: use house_number & street in the hashes
    @param with_unit: use secondary unit as part of the hashes
    @param with_city_or_equivalent: use the city, city_district, suburb, or island name as one of
                                    the geo qualifiers
    @param with_small_containing_boundaries: use small containing boundaries (currently state_district)
                                             as one of the geo qualifiers
    @param with_postal_code: use postal code as one of the geo qualifiers
    @param with_latlon: use geohash + neighbors as one of the geo qualifiers
    @param latitude: latitude (Y coordinate)
    @param longitude: longitude (X coordinate)
    @param geohash_precision: geohash tile size (default = 6)
    @param name_and_address_keys: include keys with name + address + geo
    @param name_only_keys: include keys with name + geo
    @param address_only_keys: include keys with address + geo
    """
    return _near_dupe.near_dupe_hashes(labels, values, languages=languages, **kw)

def near_dupe_hashes ( labels , values , languages = None , ** kw ) : return _near_dupe . near_dupe_hashes ( labels , values , languages = languages , ** kw )

Hash the given address into normalized strings that can be used to group similar addresses together for more detailed pairwise comparison . This can be thought of as the blocking function in record linkage or locally - sensitive hashing in the document near - duplicate detection .


def dict_to_object(item, object_name):
    """Converts a python dict to a namedtuple, saving memory."""
    fields = item.keys()
    values = item.values()
    return json.loads(json.dumps(item),
                      object_hook=lambda d:
                      namedtuple(object_name, fields)(*values))

def dict_to_object ( item , object_name ) : fields = item . keys ( ) values = item . values ( ) return json . loads ( json . dumps ( item ) , object_hook = lambda d : namedtuple ( object_name , fields ) ( * values ) )

Converts a python dict to a namedtuple saving memory .


def get_ticker_price(self, ticker,
                         startDate=None, endDate=None,
                         fmt='json', frequency='daily'):
        """By default, return latest EOD Composite Price for a stock ticker.
           On average, each feed contains 3 data sources.

            Supported tickers + Available Day Ranges are here:
            https://apimedia.tiingo.com/docs/tiingo/daily/supported_tickers.zip

            Args:
                ticker (string): Unique identifier for stock ticker
                startDate (string): Start of ticker range in YYYY-MM-DD format
                endDate (string): End of ticker range in YYYY-MM-DD format
                fmt (string): 'csv' or 'json'
                frequency (string): Resample frequency
        """
        url = self._get_url(ticker, frequency)
        params = {
            'format': fmt if fmt != "object" else 'json',  # conversion local
            'resampleFreq': frequency
        }

        if startDate:
            params['startDate'] = startDate
        if endDate:
            params['endDate'] = endDate

        # TODO: evaluate whether to stream CSV to cache on disk, or
        # load as array in memory, or just pass plain text
        response = self._request('GET', url, params=params)
        if fmt == "json":
            return response.json()
        elif fmt == "object":
            data = response.json()
            return [dict_to_object(item, "TickerPrice") for item in data]
        else:
            return response.content.decode("utf-8")

def get_ticker_price ( self , ticker , startDate = None , endDate = None , fmt = 'json' , frequency = 'daily' ) : url = self . _get_url ( ticker , frequency ) params = { 'format' : fmt if fmt != "object" else 'json' , 'resampleFreq' : frequency } if startDate : params [ 'startDate' ] = startDate if endDate : params [ 'endDate' ] = endDate response = self . _request ( 'GET' , url , params = params ) if fmt == "json" : return response . json ( ) elif fmt == "object" : data = response . json ( ) return [ dict_to_object ( item , "TickerPrice" ) for item in data ] else : return response . content . decode ( "utf-8" )

By default return latest EOD Composite Price for a stock ticker . On average each feed contains 3 data sources .


def get_dataframe(self, tickers,
                      startDate=None, endDate=None, metric_name=None, frequency='daily'):

        """ Return a pandas.DataFrame of historical prices for one or more ticker symbols.

            By default, return latest EOD Composite Price for a list of stock tickers.
            On average, each feed contains 3 data sources.

            Supported tickers + Available Day Ranges are here:
            https://apimedia.tiingo.com/docs/tiingo/daily/supported_tickers.zip
            or from the TiingoClient.list_tickers() method.

            Args:
                tickers (string/list): One or more unique identifiers for a stock ticker.
                startDate (string): Start of ticker range in YYYY-MM-DD format.
                endDate (string): End of ticker range in YYYY-MM-DD format.
                metric_name (string): Optional parameter specifying metric to be returned for each
                    ticker.  In the event of a single ticker, this is optional and if not specified
                    all of the available data will be returned.  In the event of a list of tickers,
                    this parameter is required.
                frequency (string): Resample frequency (defaults to daily).
        """

        valid_columns = ['open', 'high', 'low', 'close', 'volume', 'adjOpen', 'adjHigh', 'adjLow',
                         'adjClose', 'adjVolume', 'divCash', 'splitFactor']

        if metric_name is not None and metric_name not in valid_columns:
            raise APIColumnNameError('Valid data items are: ' + str(valid_columns))

        params = {
            'format': 'json',
            'resampleFreq': frequency
        }
        if startDate:
            params['startDate'] = startDate
        if endDate:
            params['endDate'] = endDate

        if pandas_is_installed:
            if type(tickers) is str:
                stock = tickers
                url = self._get_url(stock, frequency)
                response = self._request('GET', url, params=params)
                df = pd.DataFrame(response.json())
                if metric_name is not None:
                    prices = df[metric_name]
                    prices.index = df['date']
                else:
                    prices = df
                    prices.index = df['date']
                    del (prices['date'])
            else:
                prices = pd.DataFrame()
                for stock in tickers:
                    url = self._get_url(stock, frequency)
                    response = self._request('GET', url, params=params)
                    df = pd.DataFrame(response.json())
                    df.index = df['date']
                    df.rename(index=str, columns={metric_name: stock}, inplace=True)
                    prices = pd.concat([prices, df[stock]], axis=1)
            prices.index = pd.to_datetime(prices.index)
            return prices
        else:
            error_message = ("Pandas is not installed, but .get_ticker_price() was "
                             "called with fmt=pandas.  In order to install tiingo with "
                             "pandas, reinstall with pandas as an optional dependency. \n"
                             "Install tiingo with pandas dependency: \'pip install tiingo[pandas]\'\n"
                             "Alternatively, just install pandas: pip install pandas.")
            raise InstallPandasException(error_message)

Return a pandas . DataFrame of historical prices for one or more ticker symbols .


def get_bulk_news(self, file_id=None, fmt='json'):
        """Only available to institutional clients.
            If ID is NOT provided, return array of available file_ids.
            If ID is provided, provides URL which you can use to download your
            file, as well as some metadata about that file.
        """
        if file_id:
            url = "tiingo/news/bulk_download/{}".format(file_id)
        else:
            url = "tiingo/news/bulk_download"

        response = self._request('GET', url)
        data = response.json()
        if fmt == 'json':
            return data
        elif fmt == 'object':
            return dict_to_object(data, "BulkNews")

def get_bulk_news ( self , file_id = None , fmt = 'json' ) : if file_id : url = "tiingo/news/bulk_download/{}" . format ( file_id ) else : url = "tiingo/news/bulk_download" response = self . _request ( 'GET' , url ) data = response . json ( ) if fmt == 'json' : return data elif fmt == 'object' : return dict_to_object ( data , "BulkNews" )

Only available to institutional clients . If ID is NOT provided return array of available file_ids . If ID is provided provides URL which you can use to download your file as well as some metadata about that file .


def _request(self, method, url, **kwargs):
        """Make HTTP request and return response object

            Args:
                method (str): GET, POST, PUT, DELETE
                url (str): path appended to the base_url to create request
                **kwargs: passed directly to a requests.request object
        """
        resp = self._session.request(method,
                                     '{}/{}'.format(self._base_url, url),
                                     headers=self._headers,
                                     **kwargs)

        try:
            resp.raise_for_status()
        except HTTPError as e:
            logging.error(resp.content)
            raise RestClientError(e)

        return resp

def _request ( self , method , url , ** kwargs ) : resp = self . _session . request ( method , '{}/{}' . format ( self . _base_url , url ) , headers = self . _headers , ** kwargs ) try : resp . raise_for_status ( ) except HTTPError as e : logging . error ( resp . content ) raise RestClientError ( e ) return resp


async def get_bearer_info(self):
        """Get the application bearer token from client_id and client_secret."""
        if self.client_id is None:
            raise SpotifyException(_GET_BEARER_ERR % 'client_id')

        elif self.client_secret is None:
            raise SpotifyException(_GET_BEARER_ERR % 'client_secret')

        token = b64encode(':'.join((self.client_id, self.client_secret)).encode())

        kwargs = {
            'url': 'https://accounts.spotify.com/api/token',
            'data': {'grant_type': 'client_credentials'},
            'headers': {'Authorization': 'Basic ' + token.decode()}
        }

        async with self._session.post(**kwargs) as resp:
            return json.loads(await resp.text(encoding='utf-8'))

Get the application bearer token from client_id and client_secret .


async def request(self, route, **kwargs):
        """Make a request to the spotify API with the current bearer credentials.

        Parameters
        ----------
        route : Union[tuple[str, str], Route]
            A tuple of the method and url or a :class:`Route` object.
        kwargs : Any
            keyword arguments to pass into :class:`aiohttp.ClientSession.request`
        """
        if isinstance(route, tuple):
            method, url = route
        else:
            method = route.method
            url = route.url

        if self.bearer_info is None:
            self.bearer_info = bearer_info = await self.get_bearer_info()
            access_token = bearer_info['access_token']
        else:
            access_token = self.bearer_info['access_token']

        headers = {
            'Authorization': 'Bearer ' + access_token,
            'Content-Type': kwargs.get('content_type', 'application/json'),
            **kwargs.pop('headers', {})
        }

        for _ in range(self.RETRY_AMOUNT):
            r = await self._session.request(method, url, headers=headers, **kwargs)
            try:
                status = r.status

                try:
                    data = json.loads(await r.text(encoding='utf-8'))
                except json.decoder.JSONDecodeError:
                    data = {}

                if 300 > status >= 200:
                    return data

                if status == 401:
                    self.bearer_info = bearer_info = await self.get_bearer_info()
                    headers['Authorization'] = 'Bearer ' + bearer_info['access_token']
                    continue

                if status == 429:
                    # we're being rate limited.
                    amount = r.headers.get('Retry-After')
                    await asyncio.sleep(int(amount), loop=self.loop)
                    continue

                if status in (502, 503):
                    # unconditional retry
                    continue

                if status == 403:
                    raise Forbidden(r, data)
                elif status == 404:
                    raise NotFound(r, data)
            finally:
                await r.release()
        else:
            raise HTTPException(r, data)

async
def
request
(
self
,
route
,
**
kwargs
)
:
if
isinstance
(
route
,
tuple
)
:
method
,
url
=
route
else
:
method
=
route
.
method
url
=
route
.
url
if
self
.
bearer_info
is
:
self
.
bearer_info
=
bearer_info
=
await
self
.
get_bearer_info
(
)
access_token
=
bearer_info
[
'access_token'
]
else
:
access_token
=
self
.
bearer_info
[
'access_token'
]
headers
=
{
'Authorization'
:
'Bearer '
+
access_token
,
'Content-Type'
:
kwargs
.
get
(
'content_type'
,
'application/json'
)
,
**
kwargs
.
pop
(
'headers'
,
{
}
)
}
for
_
in
range
(
self
.
RETRY_AMOUNT
)
:
r
=
await
self
.
_session
.
request
(
method
,
url
,
headers
=
headers
,
**
kwargs
)
try
:
status
=
r
.
status
try
:
data
=
json
.
loads
(
await
r
.
text
(
encoding
=
'utf-8'
)
)
except
json
.
decoder
.
JSONDecodeError
:
data
=
{
}
if
300
>
status
>=
200
:
return
data
if
status
==
401
:
self
.
bearer_info
=
bearer_info
=
await
self
.
get_bearer_info
(
)
headers
[
'Authorization'
]
=
'Bearer '
+
bearer_info
[
'access_token'
]
continue
if
status
==
429
:
amount
=
r
.
headers
.
get
(
'Retry-After'
)
await
asyncio
.
sleep
(
int
(
amount
)
,
loop
=
self
.
loop
)
continue
if
status
in
(
502
,
503
)
:
continue
if
status
==
403
:
raise
Forbidden
(
r
,
data
)
elif
status
==
404
:
raise
NotFound
(
r
,
data
)
finally
:
await
r
.
release
(
)
else
:
raise
HTTPException
(
r
,
data
)

Make a request to the spotify API with the current bearer credentials .


def album_tracks(self, spotify_id, limit=20, offset=0, market='US'):
        """Get an albums tracks by an ID.

        Parameters
        ----------
        spotify_id : str
            The spotify_id to search by.
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optiona[int]
            The offset of which Spotify should start yielding from.
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.
        """
        route = Route('GET', '/albums/{spotify_id}/tracks', spotify_id=spotify_id)
        payload = {'limit': limit, 'offset': offset}

        if market:
            payload['market'] = market

        return self.request(route, params=payload)

def album_tracks ( self , spotify_id , limit = 20 , offset = 0 , market = 'US' ) : route = Route ( 'GET' , '/albums/{spotify_id}/tracks' , spotify_id = spotify_id ) payload = { 'limit' : limit , 'offset' : offset } if market : payload [ 'market' ] = market return self . request ( route , params = payload )


def artist(self, spotify_id):
        """Get a spotify artist by their ID.

        Parameters
        ----------
        spotify_id : str
            The spotify_id to search by.
        """
        route = Route('GET', '/artists/{spotify_id}', spotify_id=spotify_id)
        return self.request(route)

def artist ( self , spotify_id ) : route = Route ( 'GET' , '/artists/{spotify_id}' , spotify_id = spotify_id ) return self . request ( route )


def artist_albums(self, spotify_id, include_groups=None, limit=20, offset=0, market='US'):
        """Get an artists tracks by their ID.

        Parameters
        ----------
        spotify_id : str
            The spotify_id to search by.
        include_groups : INCLUDE_GROUPS_TP
            INCLUDE_GROUPS
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optiona[int]
            The offset of which Spotify should start yielding from.
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.
        """
        route = Route('GET', '/artists/{spotify_id}/albums', spotify_id=spotify_id)
        payload = {'limit': limit, 'offset': offset}

        if include_groups:
            payload['include_groups'] = include_groups

        if market:
            payload['market'] = market

        return self.request(route, params=payload)

def artist_albums ( self , spotify_id , include_groups = None , limit = 20 , offset = 0 , market = 'US' ) : route = Route ( 'GET' , '/artists/{spotify_id}/albums' , spotify_id = spotify_id ) payload = { 'limit' : limit , 'offset' : offset } if include_groups : payload [ 'include_groups' ] = include_groups if market : payload [ 'market' ] = market return self . request ( route , params = payload )


def artist_top_tracks(self, spotify_id, country):
        """Get an artists top tracks per country with their ID.

        Parameters
        ----------
        spotify_id : str
            The spotify_id to search by.
        country : COUNTRY_TP
            COUNTRY
        """
        route = Route('GET', '/artists/{spotify_id}/top-tracks', spotify_id=spotify_id)
        payload = {'country': country}
        return self.request(route, params=payload)

def artist_top_tracks ( self , spotify_id , country ) : route = Route ( 'GET' , '/artists/{spotify_id}/top-tracks' , spotify_id = spotify_id ) payload = { 'country' : country } return self . request ( route , params = payload )

Get an artists top tracks per country with their ID .


def artist_related_artists(self, spotify_id):
        """Get related artists for an artist by their ID.

        Parameters
        ----------
        spotify_id : str
            The spotify_id to search by.
        """
        route = Route('GET', '/artists/{spotify_id}/related-artists', spotify_id=spotify_id)
        return self.request(route)

def artist_related_artists ( self , spotify_id ) : route = Route ( 'GET' , '/artists/{spotify_id}/related-artists' , spotify_id = spotify_id ) return self . request ( route )

Get related artists for an artist by their ID .


def artists(self, spotify_ids):
        """Get a spotify artists by their IDs.

        Parameters
        ----------
        spotify_id : List[str]
            The spotify_ids to search with.
        """
        route = Route('GET', '/artists')
        payload = {'ids': spotify_ids}
        return self.request(route, params=payload)

def artists ( self , spotify_ids ) : route = Route ( 'GET' , '/artists' ) payload = { 'ids' : spotify_ids } return self . request ( route , params = payload )


def category(self, category_id, country=None, locale=None):
        """Get a single category used to tag items in Spotify.

        Parameters
        ----------
        category_id : str
            The Spotify category ID for the category.
        country : COUNTRY_TP
            COUNTRY
        locale : LOCALE_TP
            LOCALE
        """
        route = Route('GET', '/browse/categories/{category_id}', category_id=category_id)
        payload = {}

        if country:
            payload['country'] = country

        if locale:
            payload['locale'] = locale

        return self.request(route, params=payload)

def category ( self , category_id , country = None , locale = None ) : route = Route ( 'GET' , '/browse/categories/{category_id}' , category_id = category_id ) payload = { } if country : payload [ 'country' ] = country if locale : payload [ 'locale' ] = locale return self . request ( route , params = payload )

Get a single category used to tag items in Spotify .


def category_playlists(self, category_id, limit=20, offset=0, country=None):
        """Get a list of Spotify playlists tagged with a particular category.

        Parameters
        ----------
        category_id : str
            The Spotify category ID for the category.
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optional[int]
            The index of the first item to return. Default: 0
        country : COUNTRY_TP
            COUNTRY
        """
        route = Route('GET', '/browse/categories/{category_id}/playlists', category_id=category_id)
        payload = {'limit': limit, 'offset': offset}

        if country:
            payload['country'] = country

        return self.request(route, params=payload)

Get a list of Spotify playlists tagged with a particular category .


def categories(self, limit=20, offset=0, country=None, locale=None):
        """Get a list of categories used to tag items in Spotify.

        Parameters
        ----------
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optional[int]
            The index of the first item to return. Default: 0
        country : COUNTRY_TP
            COUNTRY
        locale : LOCALE_TP
            LOCALE
        """
        route = Route('GET', '/browse/categories')
        payload = {'limit': limit, 'offset': offset}

        if country:
            payload['country'] = country

        if locale:
            payload['locale'] = locale

        return self.request(route, params=payload)

def categories ( self , limit = 20 , offset = 0 , country = None , locale = None ) : route = Route ( 'GET' , '/browse/categories' ) payload = { 'limit' : limit , 'offset' : offset } if country : payload [ 'country' ] = country if locale : payload [ 'locale' ] = locale return self . request ( route , params = payload )

Get a list of categories used to tag items in Spotify .


def featured_playlists(self, locale=None, country=None, timestamp=None, limit=20, offset=0):
        """Get a list of Spotify featured playlists.

        Parameters
        ----------
        locale : LOCALE_TP
            LOCALE
        country : COUNTRY_TP
            COUNTRY
        timestamp : TIMESTAMP_TP
            TIMESTAMP
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optional[int]
            The index of the first item to return. Default: 0
        """
        route = Route('GET', '/browse/featured-playlists')
        payload = {'limit': limit, 'offset': offset}

        if country:
            payload['country'] = country

        if locale:
            payload['locale'] = locale

        if timestamp:
            payload['timestamp'] = timestamp

        return self.request(route, params=payload)

def featured_playlists ( self , locale = None , country = None , timestamp = None , limit = 20 , offset = 0 ) : route = Route ( 'GET' , '/browse/featured-playlists' ) payload = { 'limit' : limit , 'offset' : offset } if country : payload [ 'country' ] = country if locale : payload [ 'locale' ] = locale if timestamp : payload [ 'timestamp' ] = timestamp return self . request ( route , params = payload )

Get a list of Spotify featured playlists .


def new_releases(self, *, country=None, limit=20, offset=0):
        """Get a list of new album releases featured in Spotify.

        Parameters
        ----------
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optional[int]
            The index of the first item to return. Default: 0
        country : COUNTRY_TP
            COUNTRY
        """
        route = Route('GET', '/browse/new-releases')
        payload = {'limit': limit, 'offset': offset}

        if country:
            payload['country'] = country

        return self.request(route, params=payload)

def new_releases ( self , * , country = None , limit = 20 , offset = 0 ) : route = Route ( 'GET' , '/browse/new-releases' ) payload = { 'limit' : limit , 'offset' : offset } if country : payload [ 'country' ] = country return self . request ( route , params = payload )

Get a list of new album releases featured in Spotify .


def recommendations(self, seed_artists, seed_genres, seed_tracks, *, limit=20, market=None, **filters):
        """Get Recommendations Based on Seeds.

        Parameters
        ----------
        seed_artists : str
            A comma separated list of Spotify IDs for seed artists. Up to 5 seed values may be provided.
        seed_genres : str
            A comma separated list of any genres in the set of available genre seeds. Up to 5 seed values may be provided.
        seed_tracks : str
            A comma separated list of Spotify IDs for a seed track. Up to 5 seed values may be provided.
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.
        max_* : Optional[Keyword arguments]
            For each tunable track attribute, a hard ceiling on the selected track attribute’s value can be provided.
        min_* : Optional[Keyword arguments]
            For each tunable track attribute, a hard floor on the selected track attribute’s value can be provided.
        target_* : Optional[Keyword arguments]
            For each of the tunable track attributes (below) a target value may be provided.
        """
        route = Route('GET', '/recommendations')
        payload = {'seed_artists': seed_artists, 'seed_genres': seed_genres, 'seed_tracks': seed_tracks, 'limit': limit}

        if market:
            payload['market'] = market

        if filters:
            payload.update(filters)

        return self.request(route, param=payload)

def recommendations ( self , seed_artists , seed_genres , seed_tracks , * , limit = 20 , market = None , ** filters ) : route = Route ( 'GET' , '/recommendations' ) payload = { 'seed_artists' : seed_artists , 'seed_genres' : seed_genres , 'seed_tracks' : seed_tracks , 'limit' : limit } if market : payload [ 'market' ] = market if filters : payload . update ( filters ) return self . request ( route , param = payload )


def following_artists_or_users(self, ids, *, type='artist'):
        """Check to see if the current user is following one or more artists or other Spotify users.

        Parameters
        ----------
        ids : List[str]
            A comma-separated list of the artist or the user Spotify IDs to check.
            A maximum of 50 IDs can be sent in one request.
        type : Optional[str]
            The ID type: either "artist" or "user".
            Default: "artist"
        """
        route = Route('GET', '/me/following/contains')
        payload = {'ids': ids, 'type': type}

        return self.request(route, params=payload)

def following_artists_or_users ( self , ids , * , type = 'artist' ) : route = Route ( 'GET' , '/me/following/contains' ) payload = { 'ids' : ids , 'type' : type } return self . request ( route , params = payload )

Check to see if the current user is following one or more artists or other Spotify users .


async def get_albums(self, *, limit: Optional[int] = 20, offset: Optional[int] = 0, include_groups=None, market: Optional[str] = None) -> List[Album]:
        """Get the albums of a Spotify artist.

        Parameters
        ----------
        limit : Optional[int]
            The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
        offset : Optiona[int]
            The offset of which Spotify should start yielding from.
        include_groups : INCLUDE_GROUPS_TP
            INCLUDE_GROUPS
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.

        Returns
        -------
        albums : List[Album]
            The albums of the artist.
        """
        from .album import Album

        data = await self.__client.http.artist_albums(self.id, limit=limit, offset=offset, include_groups=include_groups, market=market)
        return list(Album(self.__client, item) for item in data['items'])

async def get_albums ( self , * , limit : Optional [ int ] = 20 , offset : Optional [ int ] = 0 , include_groups = None , market : Optional [ str ] = None ) -> List [ Album ] : from . album import Album data = await self . __client . http . artist_albums ( self . id , limit = limit , offset = offset , include_groups = include_groups , market = market ) return list ( Album ( self . __client , item ) for item in data [ 'items' ] )


async def get_all_albums(self, *, market='US') -> List[Album]:
        """loads all of the artists albums, depending on how many the artist has this may be a long operation.

        Parameters
        ----------
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.

        Returns
        -------
        albums : List[Album]
            The albums of the artist.
        """
        from .album import Album

        albums = []
        offset = 0
        total = await self.total_albums(market=market)

        while len(albums) < total:
            data = await self.__client.http.artist_albums(self.id, limit=50, offset=offset, market=market)

            offset += 50
            albums += list(Album(self.__client, item) for item in data['items'])

        return albums

async def get_all_albums ( self , * , market = 'US' ) -> List [ Album ] : from . album import Album albums = [ ] offset = 0 total = await self . total_albums ( market = market ) while len ( albums ) < total : data = await self . __client . http . artist_albums ( self . id , limit = 50 , offset = offset , market = market ) offset += 50 albums += list ( Album ( self . __client , item ) for item in data [ 'items' ] ) return albums

loads all of the artists albums depending on how many the artist has this may be a long operation .


async def total_albums(self, *, market: str = None) -> int:
        """get the total amout of tracks in the album.

        Parameters
        ----------
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code.

        Returns
        -------
        total : int
            The total amount of albums.
        """
        data = await self.__client.http.artist_albums(self.id, limit=1, offset=0, market=market)
        return data['total']

async def total_albums ( self , * , market : str = None ) -> int : data = await self . __client . http . artist_albums ( self . id , limit = 1 , offset = 0 , market = market ) return data [ 'total' ]

get the total amout of tracks in the album .


async def related_artists(self) -> List[Artist]:
        """Get Spotify catalog information about artists similar to a given artist.

        Similarity is based on analysis of the Spotify community’s listening history.

        Returns
        -------
        artists : List[Artits]
            The artists deemed similar.
        """
        related = await self.__client.http.artist_related_artists(self.id)
        return list(Artist(self.__client, item) for item in related['artists'])

async def related_artists ( self ) -> List [ Artist ] : related = await self . __client . http . artist_related_artists ( self . id ) return list ( Artist ( self . __client , item ) for item in related [ 'artists' ] )

Get Spotify catalog information about artists similar to a given artist .


async def currently_playing(self) -> Tuple[Context, Track]:
        """Get the users currently playing track.

        Returns
        -------
        context, track : Tuple[Context, Track]
            A tuple of the context and track.
        """
        data = await self.http.currently_playing()

        if data.get('item'):
            data['Context'] = Context(data.get('context'))
            data['item'] = Track(self.__client, data.get('item'))

        return data

async def currently_playing ( self ) -> Tuple [ Context , Track ] : data = await self . http . currently_playing ( ) if data . get ( 'item' ) : data [ 'Context' ] = Context ( data . get ( 'context' ) ) data [ 'item' ] = Track ( self . __client , data . get ( 'item' ) ) return data


async def get_player(self) -> Player:
        """Get information about the users current playback.

        Returns
        -------
        player : Player
            A player object representing the current playback.
        """
        self._player = player = Player(self.__client, self, await self.http.current_player())
        return player

async def get_player ( self ) -> Player : self . _player = player = Player ( self . __client , self , await self . http . current_player ( ) ) return player

Get information about the users current playback .


async def get_devices(self) -> List[Device]:
        """Get information about the users avaliable devices.

        Returns
        -------
        devices : List[Device]
            The devices the user has available.
        """
        data = await self.http.available_devices()
        return [Device(item) for item in data['devices']]

async def get_devices ( self ) -> List [ Device ] : data = await self . http . available_devices ( ) return [ Device ( item ) for item in data [ 'devices' ] ]

Get information about the users avaliable devices .


async def recently_played(self) -> List[Dict[str, Union[Track, Context, str]]]:
        """Get tracks from the current users recently played tracks.

        Returns
        -------
        playlist_history : List[Dict[str, Union[Track, Context, str]]]
            A list of playlist history object.
            Each object is a dict with a timestamp, track and context field.
        """
        data = await self.http.recently_played()
        f = lambda data: {'context': Context(data.get('context')), 'track': Track(self.__client, data.get('track'))}
        # List[T] where T: {'track': Track, 'content': Context: 'timestamp': ISO8601}
        return [{'timestamp': track['timestamp'], **f(track)} for track in data['items']]

async def recently_played ( self ) -> List [ Dict [ str , Union [ Track , Context , str ] ] ] : data = await self . http . recently_played ( ) f = lambda data : { 'context' : Context ( data . get ( 'context' ) ) , 'track' : Track ( self . __client , data . get ( 'track' ) ) } return [ { 'timestamp' : track [ 'timestamp' ] , ** f ( track ) } for track in data [ 'items' ] ]

Get tracks from the current users recently played tracks .


async def replace_tracks(self, playlist, *tracks) -> str:
        """Replace all the tracks in a playlist, overwriting its existing tracks.
        This powerful request can be useful for replacing tracks, re-ordering existing tracks, or clearing the playlist.

        Parameters
        ----------
        playlist : Union[str, PLaylist]
            The playlist to modify
        tracks : Sequence[Union[str, Track]]
            Tracks to place in the playlist
        """
        tracks = [str(track) for track in tracks]
        await self.http.replace_playlist_tracks(self.id, str(playlist), tracks=','.join(tracks))

async def replace_tracks ( self , playlist , * tracks ) -> str : tracks = [ str ( track ) for track in tracks ] await self . http . replace_playlist_tracks ( self . id , str ( playlist ) , tracks = ',' . join ( tracks ) )

Replace all the tracks in a playlist overwriting its existing tracks . This powerful request can be useful for replacing tracks re - ordering existing tracks or clearing the playlist .


async def reorder_tracks(self, playlist, start, insert_before, length=1, *, snapshot_id=None):
        """Reorder a track or a group of tracks in a playlist.

        Parameters
        ----------
        playlist : Union[str, Playlist]
            The playlist to modify
        start : int
            The position of the first track to be reordered.
        insert_before : int
            The position where the tracks should be inserted.
        length : Optional[int]
            The amount of tracks to be reordered. Defaults to 1 if not set.
        snapshot_id : str
            The playlist’s snapshot ID against which you want to make the changes.

        Returns
        -------
        snapshot_id : str
            The snapshot id of the playlist.
        """
        data = await self.http.reorder_playlists_tracks(self.id, str(playlist), start, length, insert_before, snapshot_id=snapshot_id)
        return data['snapshot_id']

async def reorder_tracks ( self , playlist , start , insert_before , length = 1 , * , snapshot_id = None ) : data = await self . http . reorder_playlists_tracks ( self . id , str ( playlist ) , start , length , insert_before , snapshot_id = snapshot_id ) return data [ 'snapshot_id' ]

Reorder a track or a group of tracks in a playlist .


async def create_playlist(self, name, *, public=True, collaborative=False, description=None):
        """Create a playlist for a Spotify user.

        Parameters
        ----------
        name : str
            The name of the playlist.
        public : Optional[bool]
            The public/private status of the playlist.
            `True` for public, `False` for private.
        collaborative : Optional[bool]
            If `True`, the playlist will become collaborative and other users will be able to modify the playlist.
        description : Optional[str]
            The playlist description

        Returns
        -------
        playlist : Playlist
            The playlist that was created.
        """
        data = {
            'name': name,
            'public': public,
            'collaborative': collaborative
        }

        if description:
            data['description'] = description

        playlist_data = await self.http.create_playlist(self.id, data)
        return Playlist(self.__client, playlist_data)

async def create_playlist ( self , name , * , public = True , collaborative = False , description = None ) : data = { 'name' : name , 'public' : public , 'collaborative' : collaborative } if description : data [ 'description' ] = description playlist_data = await self . http . create_playlist ( self . id , data ) return Playlist ( self . __client , playlist_data )


async def get_playlists(self, *, limit=20, offset=0):
        """get the users playlists from spotify.

        Parameters
        ----------
         limit : Optional[int]
             The limit on how many playlists to retrieve for this user (default is 20).
         offset : Optional[int]
             The offset from where the api should start from in the playlists.

        Returns
        -------
        playlists : List[Playlist]
            A list of the users playlists.
        """
        if hasattr(self, 'http'):
            http = self.http
        else:
            http = self.__client.http

        data = await http.get_playlists(self.id, limit=limit, offset=offset)
        return [Playlist(self.__client, playlist_data) for playlist_data in data['items']]

async def get_playlists ( self , * , limit = 20 , offset = 0 ) : if hasattr ( self , 'http' ) : http = self . http else : http = self . __client . http data = await http . get_playlists ( self . id , limit = limit , offset = offset ) return [ Playlist ( self . __client , playlist_data ) for playlist_data in data [ 'items' ] ]


async def get_tracks(self, *, limit: Optional[int] = 20, offset: Optional[int] = 0) -> List[Track]:
        """get the albums tracks from spotify.

        Parameters
        ----------
        limit : Optional[int]
            The limit on how many tracks to retrieve for this album (default is 20).
        offset : Optional[int]
            The offset from where the api should start from in the tracks.

        Returns
        -------
        tracks : List[Track]
            The tracks of the artist.
        """
        data = await self.__client.http.album_tracks(self.id, limit=limit, offset=offset)
        return list(Track(self.__client, item) for item in data['items'])

async def get_tracks ( self , * , limit : Optional [ int ] = 20 , offset : Optional [ int ] = 0 ) -> List [ Track ] : data = await self . __client . http . album_tracks ( self . id , limit = limit , offset = offset ) return list ( Track ( self . __client , item ) for item in data [ 'items' ] )


async def get_all_tracks(self, *, market: Optional[str] = 'US') -> List[Track]:
        """loads all of the albums tracks, depending on how many the album has this may be a long operation.

        Parameters
        ----------
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking.

        Returns
        -------
        tracks : List[Track]
            The tracks of the artist.
        """
        tracks = []
        offset = 0
        total = self.total_tracks or None

        while True:
            data = await self.__client.http.album_tracks(self.id, limit=50, offset=offset, market=market)

            if total is None:
                total = data['total']

            offset += 50
            tracks += list(Track(self.__client, item) for item in data['items'])

            if len(tracks) >= total:
                break

        return tracks

async def get_all_tracks ( self , * , market : Optional [ str ] = 'US' ) -> List [ Track ] : tracks = [ ] offset = 0 total = self . total_tracks or None while True : data = await self . __client . http . album_tracks ( self . id , limit = 50 , offset = offset , market = market ) if total is None : total = data [ 'total' ] offset += 50 tracks += list ( Track ( self . __client , item ) for item in data [ 'items' ] ) if len ( tracks ) >= total : break return tracks

loads all of the albums tracks depending on how many the album has this may be a long operation .


def oauth2_url(self, redirect_uri: str, scope: Optional[str] = None, state: Optional[str] = None) -> str:
        """Generate an outh2 url for user authentication.

        Parameters
        ----------
        redirect_uri : str
            Where spotify should redirect the user to after authentication.
        scope : Optional[str]
            Space seperated spotify scopes for different levels of access.
        state : Optional[str]
            Using a state value can increase your assurance that an incoming connection is the result of an authentication request.

        Returns
        -------
        url : str
            The OAuth2 url.
        """
        return OAuth2.url_(self.http.client_id, redirect_uri, scope=scope, state=state)

def oauth2_url ( self , redirect_uri : str , scope : Optional [ str ] = None , state : Optional [ str ] = None ) -> str : return OAuth2 . url_ ( self . http . client_id , redirect_uri , scope = scope , state = state )

Generate an outh2 url for user authentication .


async def get_album(self, spotify_id: str, *, market: str = 'US') -> Album:
        """Retrive an album with a spotify ID.

        Parameters
        ----------
        spotify_id : str
            The ID to search for.
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code

        Returns
        -------
        album : Album
            The album from the ID
        """
        data = await self.http.album(to_id(spotify_id), market=market)
        return Album(self, data)

async def get_album ( self , spotify_id : str , * , market : str = 'US' ) -> Album : data = await self . http . album ( to_id ( spotify_id ) , market = market ) return Album ( self , data )


async def get_artist(self, spotify_id: str) -> Artist:
        """Retrive an artist with a spotify ID.

        Parameters
        ----------
        spotify_id : str
            The ID to search for.

        Returns
        -------
        artist : Artist
            The artist from the ID
        """
        data = await self.http.artist(to_id(spotify_id))
        return Artist(self, data)

async def get_artist ( self , spotify_id : str ) -> Artist : data = await self . http . artist ( to_id ( spotify_id ) ) return Artist ( self , data )


async def get_track(self, spotify_id: str) -> Track:
        """Retrive an track with a spotify ID.

        Parameters
        ----------
        spotify_id : str
            The ID to search for.

        Returns
        -------
        track : Track
            The track from the ID
        """
        data = await self.http.track(to_id(spotify_id))
        return Track(self, data)

async def get_track ( self , spotify_id : str ) -> Track : data = await self . http . track ( to_id ( spotify_id ) ) return Track ( self , data )


async def get_user(self, spotify_id: str) -> User:
        """Retrive an user with a spotify ID.

        Parameters
        ----------
        spotify_id : str
            The ID to search for.

        Returns
        -------
        user : User
            The user from the ID
        """
        data = await self.http.user(to_id(spotify_id))
        return User(self, data)

async def get_user ( self , spotify_id : str ) -> User : data = await self . http . user ( to_id ( spotify_id ) ) return User ( self , data )


async def get_albums(self, *ids: List[str], market: str = 'US') -> List[Album]:
        """Retrive multiple albums with a list of spotify IDs.

        Parameters
        ----------
        ids : List[str]
            the ID to look for
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code

        Returns
        -------
        albums : List[Album]
            The albums from the IDs
        """
        data = await self.http.albums(','.join(to_id(_id) for _id in ids), market=market)
        return list(Album(self, album) for album in data['albums'])

async def get_albums ( self , * ids : List [ str ] , market : str = 'US' ) -> List [ Album ] : data = await self . http . albums ( ',' . join ( to_id ( _id ) for _id in ids ) , market = market ) return list ( Album ( self , album ) for album in data [ 'albums' ] )

Retrive multiple albums with a list of spotify IDs .


async def get_artists(self, *ids: List[str]) -> List[Artist]:
        """Retrive multiple artists with a list of spotify IDs.

        Parameters
        ----------
        ids : List[str]
            the IDs to look for

        Returns
        -------
        artists : List[Artist]
            The artists from the IDs
        """
        data = await self.http.artists(','.join(to_id(_id) for _id in ids))
        return list(Artist(self, artist) for artist in data['artists'])

async def get_artists ( self , * ids : List [ str ] ) -> List [ Artist ] : data = await self . http . artists ( ',' . join ( to_id ( _id ) for _id in ids ) ) return list ( Artist ( self , artist ) for artist in data [ 'artists' ] )

Retrive multiple artists with a list of spotify IDs .


async def search(self, q: str, *, types: Optional[Iterable[str]] = ['track', 'playlist', 'artist', 'album'], limit: Optional[int] = 20, offset: Optional[int] = 0, market: Optional[str] = None) -> Dict[str, List[Union[Track, Playlist, Artist, Album]]]:
        """Access the spotify search functionality.

        Parameters
        ----------
        q : str
            the search query
        types : Optional[Iterable[str]]
            A sequence of search types (can be any of `track`, `playlist`, `artist` or `album`) to refine the search request.
            A `ValueError` may be raised if a search type is found that is not valid.
        limit : Optional[int]
            The limit of search results to return when searching.
            Maximum limit is 50, any larger may raise a :class:`HTTPException`
        offset : Optional[int]
            The offset from where the api should start from in the search results.
        market : Optional[str]
            An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking.

        Returns
        -------
        results : Dict[str, List[Union[Track, Playlist, Artist, Album]]]
            The results of the search.
        """
        if not hasattr(types, '__iter__'):
            raise TypeError('types must be an iterable.')

        elif not isinstance(types, list):
            types = list(item for item in types)

        types_ = set(types)

        if not types_.issubset(_SEARCH_TYPES):
            raise ValueError(_SEARCH_TYPE_ERR % types_.difference(_SEARCH_TYPES).pop())

        kwargs = {
            'q': q.replace(' ', '+'),
            'queary_type': ','.join(tp.strip() for tp in types),
            'market': market,
            'limit': limit,
            'offset': offset
        }

        data = await self.http.search(**kwargs)

        return {key: [_TYPES[obj['type']](self, obj) for obj in value['items']] for key, value in data.items()}

async
def
search
(
self
,
q
:
str
,
*
,
types
:
Optional
[
Iterable
[
str
]
]
=
[
'track'
,
'playlist'
,
'artist'
,
'album'
]
,
limit
:
Optional
[
int
]
=
20
,
offset
:
Optional
[
int
]
=
0
,
market
:
Optional
[
str
]
=
)
->
Dict
[
str
,
List
[
Union
[
Track
,
Playlist
,
Artist
,
Album
]
]
]
:
if
not
hasattr
(
types
,
'__iter__'
)
:
raise
TypeError
(
'types must be an iterable.'
)
elif
not
isinstance
(
types
,
list
)
:
types
=
list
(
item
for
item
in
types
)
types_
=
set
(
types
)
if
not
types_
.
issubset
(
_SEARCH_TYPES
)
:
raise
ValueError
(
_SEARCH_TYPE_ERR
%
types_
.
difference
(
_SEARCH_TYPES
)
.
pop
(
)
)
kwargs
=
{
'q'
:
q
.
replace
(
' '
,
'+'
)
,
'queary_type'
:
','
.
join
(
tp
.
strip
(
)
for
tp
in
types
)
,
'market'
:
market
,
'limit'
:
limit
,
'offset'
:
offset
}
data
=
await
self
.
http
.
search
(
**
kwargs
)
return
{
key
:
[
_TYPES
[
obj
[
'type'
]
]
(
self
,
obj
)
for
obj
in
value
[
'items'
]
]
for
key
,
value
in
data
.
items
(
)
}

Access the spotify search functionality .


def to_id(string: str) -> str:
    """Get a spotify ID from a URI or open.spotify URL.

    Paramters
    ---------
    string : str
        The string to operate on.

    Returns
    -------
    id : str
        The Spotify ID from the string.
    """
    string = string.strip()

    match = _URI_RE.match(string)

    if match is None:
        match = _OPEN_RE.match(string)

        if match is None:
            return string
        else:
            return match.group(2)
    else:
        return match.group(1)

def to_id ( string : str ) -> str : string = string . strip ( ) match = _URI_RE . match ( string ) if match is None : match = _OPEN_RE . match ( string ) if match is None : return string else : return match . group ( 2 ) else : return match . group ( 1 )

Get a spotify ID from a URI or open . spotify URL .


def assert_hasattr(attr: str, msg: str, tp: BaseException = SpotifyException) -> Callable:
    """decorator to assert an object has an attribute when run."""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def decorated(self, *args, **kwargs):
            if not hasattr(self, attr):
                raise tp(msg)
            return func(self, *args, **kwargs)

        if inspect.iscoroutinefunction(func):
            @functools.wraps(func)
            async def decorated(*args, **kwargs):
                return await decorated(*args, **kwargs)

        return decorated
    return decorator

def assert_hasattr ( attr : str , msg : str , tp : BaseException = SpotifyException ) -> Callable : def decorator ( func : Callable ) -> Callable : @ functools . wraps ( func ) def decorated ( self , * args , ** kwargs ) : if not hasattr ( self , attr ) : raise tp ( msg ) return func ( self , * args , ** kwargs ) if inspect . iscoroutinefunction ( func ) : @ functools . wraps ( func ) async def decorated ( * args , ** kwargs ) : return await decorated ( * args , ** kwargs ) return decorated return decorator

decorator to assert an object has an attribute when run .


def from_client(cls, client, *args, **kwargs):
        """Construct a OAuth2 object from a `spotify.Client`."""
        return cls(client.http.client_id, *args, **kwargs)

def from_client ( cls , client , * args , ** kwargs ) : return cls ( client . http . client_id , * args , ** kwargs )

Construct a OAuth2 object from a spotify . Client .


def url_(client_id: str, redirect_uri: str, *, scope: str = None, state: str = None, secure: bool = True) -> str:
        """Construct a OAuth2 URL instead of an OAuth2 object."""
        attrs = {
            'client_id': client_id,
            'redirect_uri': quote(redirect_uri)
        }

        if scope is not None:
            attrs['scope'] = quote(scope)

        if state is not None:
            attrs['state'] = state

        parameters = '&'.join('{0}={1}'.format(*item) for item in attrs.items())

        return OAuth2._BASE.format(parameters=parameters)

def url_ ( client_id : str , redirect_uri : str , * , scope : str = None , state : str = None , secure : bool = True ) -> str : attrs = { 'client_id' : client_id , 'redirect_uri' : quote ( redirect_uri ) } if scope is not None : attrs [ 'scope' ] = quote ( scope ) if state is not None : attrs [ 'state' ] = state parameters = '&' . join ( '{0}={1}' . format ( * item ) for item in attrs . items ( ) ) return OAuth2 . _BASE . format ( parameters = parameters )

Construct a OAuth2 URL instead of an OAuth2 object .


def attrs(self):
        """Attributes used when constructing url parameters."""
        data = {
            'client_id': self.client_id,
            'redirect_uri': quote(self.redirect_uri),
        }

        if self.scope is not None:
            data['scope'] = quote(self.scope)

        if self.state is not None:
            data['state'] = self.state

        return data

def attrs ( self ) : data = { 'client_id' : self . client_id , 'redirect_uri' : quote ( self . redirect_uri ) , } if self . scope is not None : data [ 'scope' ] = quote ( self . scope ) if self . state is not None : data [ 'state' ] = self . state return data

Attributes used when constructing url parameters .


def parameters(self) -> str:
        """URL parameters used."""
        return '&'.join('{0}={1}'.format(*item) for item in self.attrs.items())

def parameters ( self ) -> str : return '&' . join ( '{0}={1}' . format ( * item ) for item in self . attrs . items ( ) )


async def build(self):
        """get the track object for each link in the partial tracks data

        Returns
        -------
        tracks : List[Track]
            The tracks
        """
        data = await self.__func()
        return list(PlaylistTrack(self.__client, track) for track in data['items'])

async def build ( self ) : data = await self . __func ( ) return list ( PlaylistTrack ( self . __client , track ) for track in data [ 'items' ] )


async def get_all_tracks(self) -> List[PlaylistTrack]:
        """Get all playlist tracks from the playlist.

        Returns
        -------
        tracks : List[PlaylistTrack]
            The playlists tracks.
        """
        if isinstance(self._tracks, PartialTracks):
            return await self._tracks.build()

        _tracks = []
        offset = 0
        while len(self.tracks) < self.total_tracks:
            data = await self.__client.http.get_playlist_tracks(self.owner.id, self.id, limit=50, offset=offset)

            _tracks += [PlaylistTrack(self.__client, item) for item in data['items']]
            offset += 50

        self.total_tracks = len(self._tracks)
        return list(self._tracks)

async def get_all_tracks ( self ) -> List [ PlaylistTrack ] : if isinstance ( self . _tracks , PartialTracks ) : return await self . _tracks . build ( ) _tracks = [ ] offset = 0 while len ( self . tracks ) < self . total_tracks : data = await self . __client . http . get_playlist_tracks ( self . owner . id , self . id , limit = 50 , offset = offset ) _tracks += [ PlaylistTrack ( self . __client , item ) for item in data [ 'items' ] ] offset += 50 self . total_tracks = len ( self . _tracks ) return list ( self . _tracks )

Get all playlist tracks from the playlist .


async def resume(self, *, device: Optional[SomeDevice] = None):
        """Resume playback on the user's account.

        Parameters
        ----------
        device : Optional[:obj:`SomeDevice`]
            The Device object or id of the device this command is targeting.
            If not supplied, the user’s currently active device is the target.
        """
        await self._user.http.play_playback(None, device_id=str(device))

async def resume ( self , * , device : Optional [ SomeDevice ] = None ) : await self . _user . http . play_playback ( None , device_id = str ( device ) )


async def transfer(self, device: SomeDevice, ensure_playback: bool = False):
        """Transfer playback to a new device and determine if it should start playing.

        Parameters
        ----------
        device : :obj:`SomeDevice`
            The device on which playback should be started/transferred.
        ensure_playback : bool
            if `True` ensure playback happens on new device.
            else keep the current playback state.
        """
        await self._user.http.transfer_player(str(device), play=ensure_playback)

async def transfer ( self , device : SomeDevice , ensure_playback : bool = False ) : await self . _user . http . transfer_player ( str ( device ) , play = ensure_playback )

Transfer playback to a new device and determine if it should start playing .


async def from_href(self):
        """Get the full object from spotify with a `href` attribute."""
        if not hasattr(self, 'href'):
            raise TypeError('Spotify object has no `href` attribute, therefore cannot be retrived')

        elif hasattr(self, 'http'):
            return await self.http.request(('GET', self.href))

        else:
            cls = type(self)

        try:
            client = getattr(self, '_{0}__client'.format(cls.__name__))
        except AttributeError:
            raise TypeError('Spotify object has no way to access a HTTPClient.')
        else:
            http = client.http

        data = await http.request(('GET', self.href))

        return cls(client, data)

Get the full object from spotify with a href attribute .


def get(self):  # pragma: no cover
        """
        Execute the logic behind the meaning of ExpirationDate + return the matched status.

        :return:
            The status of the tested domain.
            Can be one of the official status.
        :rtype: str
        """

        # We get the status of the domain validation.
        domain_validation = self.checker.is_domain_valid()
        # We get the status of the IPv4 validation.
        ip_validation = self.checker.is_ip_valid()

        if "current_test_data" in PyFunceble.INTERN:
            # The end-user want more information whith his test.

            # We update some index.
            PyFunceble.INTERN["current_test_data"].update(
                {
                    "domain_syntax_validation": domain_validation,
                    "ip4_syntax_validation": ip_validation,
                }
            )

        if (
            domain_validation
            and not ip_validation
            or domain_validation
            or PyFunceble.CONFIGURATION["local"]
        ):
            # * The element is a valid domain.
            # and
            # * The element is not ahe valid IPv4.
            # or
            # * The element is a valid domain.

            # * We get the HTTP status code of the currently tested element.
            # and
            # * We try to get the element status from the IANA database.
            PyFunceble.INTERN.update(
                {"http_code": HTTPCode().get(), "referer": Referer().get()}
            )

            if not PyFunceble.INTERN["referer"]:
                # We could not get the referer.

                # We parse the referer status into the upstream call.
                return PyFunceble.INTERN["referer"]

            # The WHOIS record status is not into our list of official status.

            if PyFunceble.INTERN["referer"] and not self.checker.is_subdomain():
                # * The iana database comparison status is not None.
                # and
                # * The domain we are testing is not a subdomain.

                # We try to extract the expiration date from the WHOIS record.
                # And we return the matched status.
                return self._extract()

            # The iana database comparison status is None.

            # We log our whois record if the debug mode is activated.
            Logs().whois(self.whois_record)

            # And we return None, we could not extract the expiration date.
            return None

        if (
            ip_validation
            and not domain_validation
            or ip_validation
            or PyFunceble.CONFIGURATION["local"]
        ):
            # * The element is a valid IPv4.
            # and
            # * The element is not a valid domain.
            # or
            # * The element is a valid IPv4.

            # We get the HTTP status code.
            PyFunceble.INTERN["http_code"] = HTTPCode().get()

            # We log our whois record if the debug mode is activated.
            Logs().whois(self.whois_record)

            # And we return None, there is no expiration date to look for.
            return None

        # The validation was not passed.

        # We log our whois record if the debug mode is activated.
        Logs().whois(self.whois_record)

        # And we return False, the domain could not pass the IP and domains syntax validation.
        return False

def get ( self ) : domain_validation = self . checker . is_domain_valid ( ) ip_validation = self . checker . is_ip_valid ( ) if "current_test_data" in PyFunceble . INTERN : PyFunceble . INTERN [ "current_test_data" ] . update ( { "domain_syntax_validation" : domain_validation , "ip4_syntax_validation" : ip_validation , } ) if ( domain_validation and not ip_validation or domain_validation or PyFunceble . CONFIGURATION [ "local" ] ) : PyFunceble . INTERN . update ( { "http_code" : HTTPCode ( ) . get ( ) , "referer" : Referer ( ) . get ( ) } ) if not PyFunceble . INTERN [ "referer" ] : return PyFunceble . INTERN [ "referer" ] if PyFunceble . INTERN [ "referer" ] and not self . checker . is_subdomain ( ) : return self . _extract ( ) Logs ( ) . whois ( self . whois_record ) return None if ( ip_validation and not domain_validation or ip_validation or PyFunceble . CONFIGURATION [ "local" ] ) : PyFunceble . INTERN [ "http_code" ] = HTTPCode ( ) . get ( ) Logs ( ) . whois ( self . whois_record ) return None Logs ( ) . whois ( self . whois_record ) return False

Execute the logic behind the meaning of ExpirationDate + return the matched status .


def _convert_or_shorten_month(cls, data):
        """
        Convert a given month into our unified format.

        :param data: The month to convert or shorten.
        :type data: str

        :return: The unified month name.
        :rtype: str
        """

        # We map the different month and their possible representation.
        short_month = {
            "jan": [str(1), "01", "Jan", "January"],
            "feb": [str(2), "02", "Feb", "February"],
            "mar": [str(3), "03", "Mar", "March"],
            "apr": [str(4), "04", "Apr", "April"],
            "may": [str(5), "05", "May"],
            "jun": [str(6), "06", "Jun", "June"],
            "jul": [str(7), "07", "Jul", "July"],
            "aug": [str(8), "08", "Aug", "August"],
            "sep": [str(9), "09", "Sep", "September"],
            "oct": [str(10), "Oct", "October"],
            "nov": [str(11), "Nov", "November"],
            "dec": [str(12), "Dec", "December"],
        }

        for month in short_month:
            # We loop through our map.

            if data in short_month[month]:
                # If the parsed data (or month if you prefer) is into our map.

                # We return the element (or key if you prefer) assigned to
                # the month.
                return month

        # The element is not into our map.

        # We return the parsed element (or month if you prefer).
        return data

def _convert_or_shorten_month ( cls , data ) : short_month = { "jan" : [ str ( 1 ) , "01" , "Jan" , "January" ] , "feb" : [ str ( 2 ) , "02" , "Feb" , "February" ] , "mar" : [ str ( 3 ) , "03" , "Mar" , "March" ] , "apr" : [ str ( 4 ) , "04" , "Apr" , "April" ] , "may" : [ str ( 5 ) , "05" , "May" ] , "jun" : [ str ( 6 ) , "06" , "Jun" , "June" ] , "jul" : [ str ( 7 ) , "07" , "Jul" , "July" ] , "aug" : [ str ( 8 ) , "08" , "Aug" , "August" ] , "sep" : [ str ( 9 ) , "09" , "Sep" , "September" ] , "oct" : [ str ( 10 ) , "Oct" , "October" ] , "nov" : [ str ( 11 ) , "Nov" , "November" ] , "dec" : [ str ( 12 ) , "Dec" , "December" ] , } for month in short_month : if data in short_month [ month ] : return month return data

Convert a given month into our unified format .


def _update_code_urls(self):
        """
        Read the code and update all links.
        """

        to_ignore = [".gitignore", ".keep"]

        for root, _, files in PyFunceble.walk(
            PyFunceble.CURRENT_DIRECTORY
            + PyFunceble.directory_separator
            + "PyFunceble"
            + PyFunceble.directory_separator
        ):
            # We loop through every directories and files in the `PyFunceble` directory.

            for file in files:
                # We loop through the list of files of the currently read directory.

                if file not in to_ignore and "__pycache__" not in root:
                    # * The filename is not into the list of file to ignore.
                    # and
                    # * The directory we are reading is not `__pycache__`.

                    if root.endswith(PyFunceble.directory_separator):
                        # The root directory ends with the directory separator.

                        # We fix the path in the currently read file.
                        self._update_docs(root + file)
                    else:
                        # The root directory does not ends with the directory separator.

                        # We fix the path in the currently read file.
                        # (after appending the directory separator between the root and file)
                        self._update_docs(root + PyFunceble.directory_separator + file)

        for root, _, files in PyFunceble.walk(
            PyFunceble.CURRENT_DIRECTORY
            + PyFunceble.directory_separator
            + "tests"
            + PyFunceble.directory_separator
        ):
            # We loop through every directories and files in the `tests` directory.
            for file in files:
                # We loop through the list of files of the currently read directory.

                if file not in to_ignore and "__pycache__" not in root:
                    # * The filename is not into the list of file to ignore.
                    # and
                    # * The directory we are reading is not `__pycache__`.

                    if root.endswith(PyFunceble.directory_separator):
                        # The root directory ends with the directory separator.

                        # We fix the path in the currently read file.
                        self._update_docs(root + file)
                    else:
                        # The root directory does not ends with the directory separator.

                        # We fix the path in the currently read file.
                        # (after appending the directory separator between the root and file)
                        self._update_docs(root + PyFunceble.directory_separator + file)

def _update_code_urls ( self ) : to_ignore = [ ".gitignore" , ".keep" ] for root , _ , files in PyFunceble . walk ( PyFunceble . CURRENT_DIRECTORY + PyFunceble . directory_separator + "PyFunceble" + PyFunceble . directory_separator ) : for file in files : if file not in to_ignore and "__pycache__" not in root : if root . endswith ( PyFunceble . directory_separator ) : self . _update_docs ( root + file ) else : self . _update_docs ( root + PyFunceble . directory_separator + file ) for root , _ , files in PyFunceble . walk ( PyFunceble . CURRENT_DIRECTORY + PyFunceble . directory_separator + "tests" + PyFunceble . directory_separator ) : for file in files : if file not in to_ignore and "__pycache__" not in root : if root . endswith ( PyFunceble . directory_separator ) : self . _update_docs ( root + file ) else : self . _update_docs ( root + PyFunceble . directory_separator + file )


def _is_version_greater(self):
        """
        Check if the current version is greater as the older older one.
        """

        # we compare the 2 versions.
        checked = Version(True).check_versions(
            self.current_version[0], self.version_yaml
        )

        if checked is not None and not checked:
            # The current version is greater as the older one.

            # We return True.
            return True

        # We return False
        return False

def _is_version_greater ( self ) : checked = Version ( True ) . check_versions ( self . current_version [ 0 ] , self . version_yaml ) if checked is not None and not checked : return True return False

Check if the current version is greater as the older older one .


def is_dev_version(cls):
        """
        Check if the current branch is `dev`.
        """

        # We initiate the command we have to run in order to
        # get the branch we are currently working with.
        command = "git branch"

        # We execute and get the command output.
        command_result = Command(command).execute()

        for branch in command_result.split("\n"):
            # We loop through each line of the command output.

            if branch.startswith("*") and "dev" in branch:
                # The current branch is `dev`.

                # We return True.
                return True

        # The current branch is not `dev`.

        # We return False.
        return False

def
is_dev_version
(
cls
)
:
command
=
"git branch"
command_result
=
Command
(
command
)
.
execute
(
)
for
branch
in
command_result
.
split
(
"\n"
)
:
if
branch
.
startswith
(
"*"
)
and
"dev"
in
branch
:
return
True
return
False


def _does_require_deprecation(self):
        """
        Check if we have to put the previous version into the deprecated list.
        """

        for index, version_number in enumerate(self.current_version[0][:2]):
            # We loop through the 2 last elements of the version.

            if version_number > self.version_yaml[index]:
                # The currently read version number is greater than the one we have in
                # the version.yaml.

                # We return True.
                return True

        # We return False, we do not need to deprecate anything.
        return False

def _does_require_deprecation ( self ) : for index , version_number in enumerate ( self . current_version [ 0 ] [ : 2 ] ) : if version_number > self . version_yaml [ index ] : return True return False

Check if we have to put the previous version into the deprecated list .


def backup(self):
        """
        Backup the current execution state.
        """

        if PyFunceble.CONFIGURATION["auto_continue"]:
            # The auto_continue subsystem is activated.

            # We initiate the location where we are going to save the data to backup.
            data_to_backup = {}
            # We get the current counter states.
            configuration_counter = PyFunceble.INTERN["counter"]["number"]

            # We initiate the data we have to backup.
            data_to_backup[PyFunceble.INTERN["file_to_test"]] = {
                # We backup the number of tested.
                "tested": configuration_counter["tested"],
                # We backup the number of up.
                "up": configuration_counter["up"],
                # We backup the number of down.
                "down": configuration_counter["down"],
                # We backup the number of invalid.
                "invalid": configuration_counter["invalid"],
            }

            # We initiate the final data we have to save.
            # We initiate this variable instead of updating backup_content because
            # we do not want to touch the backup_content.
            to_save = {}

            # We add the backup_content into to_save.
            to_save.update(self.backup_content)
            # And we overwrite with the newly data to backup.
            to_save.update(data_to_backup)

            # Finaly, we save our informations into the log file.
            Dict(to_save).to_json(self.autocontinue_log_file)

def backup ( self ) : if PyFunceble . CONFIGURATION [ "auto_continue" ] : data_to_backup = { } configuration_counter = PyFunceble . INTERN [ "counter" ] [ "number" ] data_to_backup [ PyFunceble . INTERN [ "file_to_test" ] ] = { "tested" : configuration_counter [ "tested" ] , "up" : configuration_counter [ "up" ] , "down" : configuration_counter [ "down" ] , "invalid" : configuration_counter [ "invalid" ] , } to_save = { } to_save . update ( self . backup_content ) to_save . update ( data_to_backup ) Dict ( to_save ) . to_json ( self . autocontinue_log_file )


def restore(self):
        """
        Restore data from the given path.
        """

        if PyFunceble.CONFIGURATION["auto_continue"] and self.backup_content:
            # The auto_continue subsystem is activated and the backup_content
            # is not empty.

            # We get the file we have to restore.
            file_to_restore = PyFunceble.INTERN["file_to_test"]

            if file_to_restore in self.backup_content:
                # The file we are working with is already into the backup content.

                # We initiate the different status to set.
                to_initiate = ["up", "down", "invalid", "tested"]

                # Because at some time it was not the current status, we have to map
                # the new with the old. This way, if someone is running the latest
                # version but with old data, we still continue like nothing happend.
                alternatives = {
                    "up": "number_of_up",
                    "down": "number_of_down",
                    "invalid": "number_of_invalid",
                    "tested": "number_of_tested",
                }

                for string in to_initiate:
                    # We loop over the status we have to initiate.

                    try:
                        # We try to update the counters by using the currently read status.
                        PyFunceble.INTERN["counter"]["number"].update(
                            {string: self.backup_content[file_to_restore][string]}
                        )
                    except KeyError:
                        # But if the status is not present, we try with the older index
                        # we mapped previously.
                        PyFunceble.INTERN["counter"]["number"].update(
                            {
                                string: self.backup_content[file_to_restore][
                                    alternatives[string]
                                ]
                            }
                        )

def restore ( self ) : if PyFunceble . CONFIGURATION [ "auto_continue" ] and self . backup_content : file_to_restore = PyFunceble . INTERN [ "file_to_test" ] if file_to_restore in self . backup_content : to_initiate = [ "up" , "down" , "invalid" , "tested" ] alternatives = { "up" : "number_of_up" , "down" : "number_of_down" , "invalid" : "number_of_invalid" , "tested" : "number_of_tested" , } for string in to_initiate : try : PyFunceble . INTERN [ "counter" ] [ "number" ] . update ( { string : self . backup_content [ file_to_restore ] [ string ] } ) except KeyError : PyFunceble . INTERN [ "counter" ] [ "number" ] . update ( { string : self . backup_content [ file_to_restore ] [ alternatives [ string ] ] } )


def _is_to_ignore(cls, line):
        """
        Check if we have to ignore the given line.

        :param line: The line from the file.
        :type line: str
        """

        # We set the list of regex to match to be
        # considered as ignored.
        to_ignore = [r"(^!|^@@|^\/|^\[|^\.|^-|^_|^\?|^&)"]  # , r"(\$|,)(image)"]

        for element in to_ignore:
            # We loop through the list of regex.

            if Regex(line, element, return_data=False).match():
                # The currently read line match the currently read
                # regex.

                # We return true, it has to be ignored.
                return True

        # Wer return False, it does not has to be ignored.
        return False

def _is_to_ignore ( cls , line ) : to_ignore = [ r"(^!|^@@|^\/|^\[|^\.|^-|^_|^\?|^&)" ] for element in to_ignore : if Regex ( line , element , return_data = False ) . match ( ) : return True return False

Check if we have to ignore the given line .


def _handle_options(self, options):
        """
        Handle the data from the options.

        :param options: The list of options from the rule.
        :type options: list

        :return: The list of domains to return globally.
        :rtype: list
        """

        # We initiate a variable which will save our result
        result = []

        # We initiate the regex which will be used to extract the domain listed
        # under the option domain=
        regex_domain_option = r"domain=(.*)"

        for option in options:
            # We loop through the list of option.
            try:
                # We try to extract the list of domains from the currently read
                # option.
                domains = Regex(
                    option, regex_domain_option, return_data=True, rematch=True, group=0
                ).match()[-1]

                if domains:
                    # We could extract something.

                    if self.aggressive:  # pragma: no cover
                        result.extend(
                            [
                                x
                                for x in domains.split("|")
                                if x and not x.startswith("~")
                            ]
                        )
                    else:
                        # We return True.
                        return True
            except TypeError:
                pass

        # We return the result.
        return result

def _handle_options ( self , options ) : result = [ ] regex_domain_option = r"domain=(.*)" for option in options : try : domains = Regex ( option , regex_domain_option , return_data = True , rematch = True , group = 0 ) . match ( ) [ - 1 ] if domains : if self . aggressive : result . extend ( [ x for x in domains . split ( "|" ) if x and not x . startswith ( "~" ) ] ) else : return True except TypeError : pass return result


def _extract_base(self, element):
        """
        Extract the base of the given element.

        .. example:
            given "hello/world?world=beautiful" return "hello"

        :param element: The element we are working with.
        :type element: str|list
        """

        if isinstance(element, list):
            # The given element is a list.

            # We get the base of each element of the list.
            return [self._extract_base(x) for x in element]

        # We get the base if it is an URL.
        base = self.checker.is_url_valid(url=element, return_base=True)

        if base:
            # It is an URL.

            # We return the extracted base.
            return base

        if "/" in element:
            # / is in the given element.

            # We return the first element before the
            # first /
            return element.split("/")[0]

        # / is not in the given element.

        # We return the given element.
        return element

def _extract_base ( self , element ) : if isinstance ( element , list ) : return [ self . _extract_base ( x ) for x in element ] base = self . checker . is_url_valid ( url = element , return_base = True ) if base : return base if "/" in element : return element . split ( "/" ) [ 0 ] return element


def _format_decoded(self, to_format, result=None):  # pragma: no cover
        """
        Format the exctracted adblock line before passing it to the system.

        :param to_format: The extracted line from the file.
        :type to_format: str

        :param result: A list of the result of this method.
        :type result: list

        :return: The list of domains or IP to test.
        :rtype: list
        """

        if not result:
            # The result is not given.

            # We set the result as an empty list.
            result = []

        for data in List(to_format).format():
            # We loop through the different lines to format.

            if data:
                # The currently read line is not empty.

                if "^" in data:
                    # There is an accent in the currently read line.

                    # We recall this method but with the current result state
                    # and splited data.
                    return self._format_decoded(data.split("^"), result)

                if "#" in data:
                    # There is a dash in the currently read line.

                    # We recall this method but with the current result state
                    # and splited data.
                    return self._format_decoded(data.split("#"), result)

                if "," in data:
                    # There is a comma in the currently read line.

                    # We recall this method but with the current result state
                    # and splited data.
                    return self._format_decoded(data.split(","), result)

                if "!" in data:
                    # There is an exclamation mark in the currently read line.

                    # We recall this method but with the current result state
                    # and splited data.
                    return self._format_decoded(data.split("!"), result)

                if "|" in data:
                    # There is a vertival bar in the currently read line.

                    # We recall this method but with the current result state
                    # and splited data.
                    return self._format_decoded(data.split("|"), result)

                if data:
                    # The currently read line is not empty.

                    data = self._extract_base(data)

                    if data and (
                        self.checker.is_domain_valid(data)
                        or self.checker.is_ip_valid(data)
                    ):
                        # The extraced base is not empty.
                        # and
                        # * The currently read line is a valid domain.
                        # or
                        # * The currently read line is a valid IP.

                        # We append the currently read line to the result.
                        result.append(data)
                    elif data:
                        # * The currently read line is not a valid domain.
                        # or
                        # * The currently read line is not a valid IP.

                        # We try to get the url base.
                        url_base = self.checker.is_url_valid(data, return_base=True)

                        if url_base:
                            # The url_base is not empty or equal to False or None.

                            # We append the url base to the result.
                            result.append(url_base)

        # We return the result element.
        return result

def _format_decoded ( self , to_format , result = None ) : if not result : result = [ ] for data in List ( to_format ) . format ( ) : if data : if "^" in data : return self . _format_decoded ( data . split ( "^" ) , result ) if "#" in data : return self . _format_decoded ( data . split ( "#" ) , result ) if "," in data : return self . _format_decoded ( data . split ( "," ) , result ) if "!" in data : return self . _format_decoded ( data . split ( "!" ) , result ) if "|" in data : return self . _format_decoded ( data . split ( "|" ) , result ) if data : data = self . _extract_base ( data ) if data and ( self . checker . is_domain_valid ( data ) or self . checker . is_ip_valid ( data ) ) : result . append ( data ) elif data : url_base = self . checker . is_url_valid ( data , return_base = True ) if url_base : result . append ( url_base ) return result

Format the exctracted adblock line before passing it to the system .


def _access(self):  # pragma: no cover
        """
        Get the HTTP code status.

        :return: The matched HTTP status code.
        :rtype: int|None
        """

        try:
            # We try to get the HTTP status code.

            if PyFunceble.INTERN["to_test_type"] == "url":
                # We are globally testing a URL.

                # We get the head of the URL.
                req = PyFunceble.requests.head(
                    self.to_get,
                    timeout=PyFunceble.CONFIGURATION["seconds_before_http_timeout"],
                    headers=self.headers,
                    verify=PyFunceble.CONFIGURATION["verify_ssl_certificate"],
                )
            else:
                # We are not globally testing a URL.

                # We get the head of the constructed URL.
                req = PyFunceble.requests.head(
                    self.to_get,
                    timeout=PyFunceble.CONFIGURATION["seconds_before_http_timeout"],
                    headers=self.headers,
                )

            # And we try to get the status code.
            return req.status_code

        except (
            PyFunceble.requests.exceptions.InvalidURL,
            PyFunceble.socket.timeout,
            PyFunceble.requests.exceptions.Timeout,
            PyFunceble.requests.ConnectionError,
            urllib3_exceptions.InvalidHeader,
            UnicodeDecodeError,  # The probability that this happend in production is minimal.
        ):
            # If one of the listed exception is matched, that means that something
            # went wrong and we were unable to extract the status code.

            # We return None.
            return None

def _access ( self ) : try : if PyFunceble . INTERN [ "to_test_type" ] == "url" : req = PyFunceble . requests . head ( self . to_get , timeout = PyFunceble . CONFIGURATION [ "seconds_before_http_timeout" ] , headers = self . headers , verify = PyFunceble . CONFIGURATION [ "verify_ssl_certificate" ] , ) else : req = PyFunceble . requests . head ( self . to_get , timeout = PyFunceble . CONFIGURATION [ "seconds_before_http_timeout" ] , headers = self . headers , ) return req . status_code except ( PyFunceble . requests . exceptions . InvalidURL , PyFunceble . socket . timeout , PyFunceble . requests . exceptions . Timeout , PyFunceble . requests . ConnectionError , urllib3_exceptions . InvalidHeader , UnicodeDecodeError , ) : return None


def get(self):
        """
        Return the HTTP code status.

        :return: The matched and formatted status code.
        :rtype: str|int|None
        """
        if PyFunceble.HTTP_CODE["active"]:
            # The http status code extraction is activated.

            # We get the http status code.
            http_code = self._access()

            # We initiate a variable which will save the list of allowed
            # http status code.
            list_of_valid_http_code = []

            for codes in [
                PyFunceble.HTTP_CODE["list"]["up"],
                PyFunceble.HTTP_CODE["list"]["potentially_down"],
                PyFunceble.HTTP_CODE["list"]["potentially_up"],
            ]:
                # We loop throught the list of http status code.

                # We extend the list of valid with the currently read
                # codes.
                list_of_valid_http_code.extend(codes)

            if http_code not in list_of_valid_http_code or http_code is None:
                # * The extracted http code is not in the list of valid http code.
                # or
                # * The extracted http code is equal to `None`.

                # We return 3 star in order to mention that we were not eable to extract
                # the http status code.
                return "*" * 3

            # * The extracted http code is in the list of valid http code.
            # or
            # * The extracted http code is not equal to `None`.

            # We return the extracted http status code.
            return http_code

        # The http status code extraction is activated.

        # We return None.
        return None

def get ( self ) : if PyFunceble . HTTP_CODE [ "active" ] : http_code = self . _access ( ) list_of_valid_http_code = [ ] for codes in [ PyFunceble . HTTP_CODE [ "list" ] [ "up" ] , PyFunceble . HTTP_CODE [ "list" ] [ "potentially_down" ] , PyFunceble . HTTP_CODE [ "list" ] [ "potentially_up" ] , ] : list_of_valid_http_code . extend ( codes ) if http_code not in list_of_valid_http_code or http_code is None : return "*" * 3 return http_code return None


def syntax_check(domain):  # pragma: no cover
    """
    Check the syntax of the given domain.

    :param domain: The domain to check the syntax for.
    :type domain: str

    :return: The syntax validity.
    :rtype: bool

    .. warning::
        If an empty or a non-string :code:`domain` is given, we return :code:`None`.
    """

    if domain and isinstance(domain, str):
        # * The given domain is not empty nor None.
        # and
        # * The given domain is a string.

        # We silently load the configuration.
        load_config(True)

        return Check(domain).is_domain_valid()

    # We return None, there is nothing to check.
    return None

def syntax_check ( domain ) : if domain and isinstance ( domain , str ) : load_config ( True ) return Check ( domain ) . is_domain_valid ( ) return None


def is_subdomain(domain):  # pragma: no cover
    """
    Check if the given domain is a subdomain.

    :param domain: The domain we are checking.
    :type domain: str

    :return: The subdomain state.
    :rtype: bool

    .. warning::
        If an empty or a non-string :code:`domain` is given, we return :code:`None`.
    """

    if domain and isinstance(domain, str):
        # * The given domain is not empty nor None.
        # and
        # * The given domain is a string.

        # We silently load the configuration.
        load_config(True)

        return Check(domain).is_subdomain()

    # We return None, there is nothing to check.
    return None

def is_subdomain ( domain ) : if domain and isinstance ( domain , str ) : load_config ( True ) return Check ( domain ) . is_subdomain ( ) return None

Check if the given domain is a subdomain .


def ipv4_syntax_check(ip):  # pragma: no cover
    """
    Check the syntax of the given IPv4.

    :param ip: The IPv4 to check the syntax for.
    :type ip: str

    :return: The syntax validity.
    :rtype: bool

    .. warning::
        If an empty or a non-string :code:`ip` is given, we return :code:`None`.
    """

    if ip and isinstance(ip, str):
        # The given IP is not empty nor None.
        # and
        # * The given IP is a string.

        # We silently load the configuration.
        load_config(True)

        return Check(ip).is_ip_valid()

    # We return None, there is nothing to check.
    return None

def ipv4_syntax_check ( ip ) : if ip and isinstance ( ip , str ) : load_config ( True ) return Check ( ip ) . is_ip_valid ( ) return None


def is_ipv4_range(ip):  # pragma: no cover
    """
    Check if the given IP is an IP range.

    :param ip: The IP we are checking.
    :type ip: str

    :return: The IPv4 range state.
    :rtype: bool

    .. warning::
        If an empty or a non-string :code:`ip` is given, we return :code:`None`.
    """

    if ip and isinstance(ip, str):
        # The given IP is not empty nor None.
        # and
        # * The given IP is a string.

        # We silently load the configuration.
        load_config(True)

        return Check(ip).is_ip_range()

    # We return None, there is nothing to check.
    return None

def is_ipv4_range ( ip ) : if ip and isinstance ( ip , str ) : load_config ( True ) return Check ( ip ) . is_ip_range ( ) return None