<commit_before>#!/usr/bin/env python3


"""A simple Python client implementation of WebFinger (RFC 7033).

WebFinger is a discovery protocol that allows you to find information about
people or things in a standardized way.
"""


from collections import OrderedDict

import logging
import requests


__version__ = "2.2dev"


logger = logging.getLogger("webfinger")


class WebFingerException(Exception):
    """Exception class for WebFinger errors.

    This can be raised due to content encoding or parsing errors.
    """


class WebFingerContentError(WebFingerException):
    """There was a problem with the WebFinger content.

    This error may be thrown if the server sends us the incorrect WebFinger
    content type.

    This is also the base class for WebFingerJRDError.
    """


class WebFingerJRDError(WebFingerContentError):
    """Error parsing the JRD.

    This could be due to expected elements that are missing, or the response
    is not a WebFinger response at all.
    """


class WebFingerNetworkError(WebFingerException):
    """An error occured on the network.

    This could be abrupt termination of the connection, or a connection could
    not be established.

    This is also the base class for WebFingerHTTPError.
    """


class WebFingerHTTPError(WebFingerNetworkError):
    """A bad HTTP response was received.

    Any HTTP code except 200 OK will cause this.
    """


class WebFingerResponse:
    """Response that wraps an RD object.

    It stores the aliases, properties, and links fields from the JRD. If these
    are not present, the attributes are set to blank.

    For conveience, links are also stored in as lists in rels, using the rel
    attribute of links as a key (or None for links where rel is ommitted).
    URI's will be mapped to friendly attribute names if known (visible in the
    RELS attribute).
    """

    REL_NAMES = {
        "http://activitystrea.ms/spec/1.0": "activity_streams",
        "http://webfinger.net/rel/avatar": "avatar",
        "http://microformats.org/profile/hcard": "hcard",
        "http://specs.openid.net/auth/2.0/provider": "open_id",
        "http://ns.opensocial.org/2008/opensocial/activitystreams":
            "opensocial",
        "http://portablecontacts.net/spec/1.0": "portable_contacts",
        "http://webfinger.net/rel/profile-page": "profile",
        "http://webfist.org/spec/rel": "webfist",
        "http://gmpg.org/xfn/11": "xfn",
    }

    # Reverse mapping for convenience
    RELS = {v: k for k, v in REL_NAMES.items()}

    def __init__(self, jrd):
        """Initalise WebFingeResponse object with jrd.

        args:
        jrd - the JRD of the WebFinger response.
        """
        self.jrd = jrd

        try:
            self.subject = jrd["subject"]
        except KeyError:
            raise WebFingerJRDError("subject is required in jrd")

        self.aliases = jrd.get("aliases", [])
        self.properties = jrd.get("properties", {})
        self.links = jrd.get("links", [])
        self.rels = OrderedDict()
        for link in self.links:
            rel = link.get("rel", None)
            rel = self.REL_NAMES.get(rel, rel)

            if rel not in self.rels:
                rel_list = self.rels[rel] = list()
            else:
                rel_list = self.rels[rel]

            rel_list.append(link)

    def rel(self, relation, attr=None):
        """Return a given relation, with an optional attribute.

        If attr is not set, nothing is returned.
        """
        if relation in self.REL_NAMES:
            relation = self.REL_NAMES[relation]

        if relation not in self.rels:
            return

        rel = self.rels[relation]

        if attr is not None:
            return [x[attr] for x in rel]

        return rel


class WebFingerClient:
    """Class for requesting WebFinger lookups as a client.

    You can subclass this for your own needs.
    """

    WEBFINGER_TYPE = "application/jrd+json"
    LEGACY_WEBFINGER_TYPES = ["application/json"]
    WEBFINGER_URL = "https://{host}/.well-known/webfinger"
    USER_AGENT = "Python-Webfinger/{version}".format(version=__version__)

    def __init__(self, timeout=None, session=None):
        """Create a WebFingerClient instance.

        args:
        timeout - default timeout to use (default None)
        session - requests session to use (default is to create our own)
        """
        self.timeout = timeout
        if session is None:
            session = self.session = requests.Session()
        else:
            self.session = session

        headers = session.headers
        headers["User-Agent"] = self.USER_AGENT
        headers["Accept"] = self.WEBFINGER_TYPE

    def _parse_host(self, resource):
        """Parse WebFinger URI."""
        host = resource.split("@")[-1]
        return host

    def _parse_response(self, response):
        """Parse WebFinger response."""
        return WebFingerResponse(response)

    def finger(self, resource, host=None, rel=None, raw=False, params=dict(),
               headers=dict()):
        """Perform a WebFinger lookup.

        args:
        resource - resource to look up
        host - host to use for resource lookup
        rel - relation to request
        raw - return unparsed JRD
        params - HTTP parameters to pass (note: resource and rel will be
                 overwritten)
        headers - HTTP headers to send with the request
        """
        if not host:
            host = self._parse_host(resource)

        url = self.WEBFINGER_URL.format(host=host)

        params["resource"] = resource<commit_msg>close session when webfinger class is freed<commit_after>#!/usr/bin/env python3


"""A simple Python client implementation of WebFinger (RFC 7033).

WebFinger is a discovery protocol that allows you to find information about
people or things in a standardized way.
"""


from collections import OrderedDict

import logging
import requests


__version__ = "2.2dev"


logger = logging.getLogger("webfinger")


class WebFingerException(Exception):
    """Exception class for WebFinger errors.

    This can be raised due to content encoding or parsing errors.
    """


class WebFingerContentError(WebFingerException):
    """There was a problem with the WebFinger content.

    This error may be thrown if the server sends us the incorrect WebFinger
    content type.

    This is also the base class for WebFingerJRDError.
    """


class WebFingerJRDError(WebFingerContentError):
    """Error parsing the JRD.

    This could be due to expected elements that are missing, or the response
    is not a WebFinger response at all.
    """


class WebFingerNetworkError(WebFingerException):
    """An error occured on the network.

    This could be abrupt termination of the connection, or a connection could
    not be established.

    This is also the base class for WebFingerHTTPError.
    """


class WebFingerHTTPError(WebFingerNetworkError):
    """A bad HTTP response was received.

    Any HTTP code except 200 OK will cause this.
    """


class WebFingerResponse:
    """Response that wraps an RD object.

    It stores the aliases, properties, and links fields from the JRD. If these
    are not present, the attributes are set to blank.

    For conveience, links are also stored in as lists in rels, using the rel
    attribute of links as a key (or None for links where rel is ommitted).
    URI's will be mapped to friendly attribute names if known (visible in the
    RELS attribute).
    """

    REL_NAMES = {
        "http://activitystrea.ms/spec/1.0": "activity_streams",
        "http://webfinger.net/rel/avatar": "avatar",
        "http://microformats.org/profile/hcard": "hcard",
        "http://specs.openid.net/auth/2.0/provider": "open_id",
        "http://ns.opensocial.org/2008/opensocial/activitystreams":
            "opensocial",
        "http://portablecontacts.net/spec/1.0": "portable_contacts",
        "http://webfinger.net/rel/profile-page": "profile",
        "http://webfist.org/spec/rel": "webfist",
        "http://gmpg.org/xfn/11": "xfn",
    }

    # Reverse mapping for convenience
    RELS = {v: k for k, v in REL_NAMES.items()}

    def __init__(self, jrd):
        """Initalise WebFingeResponse object with jrd.

        args:
        jrd - the JRD of the WebFinger response.
        """
        self.jrd = jrd

        try:
            self.subject = jrd["subject"]
        except KeyError:
            raise WebFingerJRDError("subject is required in jrd")

        self.aliases = jrd.get("aliases", [])
        self.properties = jrd.get("properties", {})
        self.links = jrd.get("links", [])
        self.rels = OrderedDict()
        for link in self.links:
            rel = link.get("rel", None)
            rel = self.REL_NAMES.get(rel, rel)

            if rel not in self.rels:
                rel_list = self.rels[rel] = list()
            else:
                rel_list = self.rels[rel]

            rel_list.append(link)

    def rel(self, relation, attr=None):
        """Return a given relation, with an optional attribute.

        If attr is not set, nothing is returned.
        """
        if relation in self.REL_NAMES:
            relation = self.REL_NAMES[relation]

        if relation not in self.rels:
            return

        rel = self.rels[relation]

        if attr is not None:
            return [x[attr] for x in rel]

        return rel


class WebFingerClient:
    """Class for requesting WebFinger lookups as a client.

    You can subclass this for your own needs.
    """

    WEBFINGER_TYPE = "application/jrd+json"
    LEGACY_WEBFINGER_TYPES = ["application/json"]
    WEBFINGER_URL = "https://{host}/.well-known/webfinger"
    USER_AGENT = "Python-Webfinger/{version}".format(version=__version__)

    def __init__(self, timeout=None, session=None):
        """Create a WebFingerClient instance.

        args:
        timeout - default timeout to use (default None)
        session - requests session to use (default is to create our own)
        """
        self.timeout = timeout
        if session is None:
            session = self.session = requests.Session()
        else:
            self.session = session

        headers = session.headers
        headers["User-Agent"] = self.USER_AGENT
        headers["Accept"] = self.WEBFINGER_TYPE

    def __del__(self):
        self.session.close()

    def _parse_host(self, resource):
        """Parse WebFinger URI."""
        host = resource.split("@")[-1]
        return host

    def _parse_response(self, response):
        """Parse WebFinger response."""
        return WebFingerResponse(response)

    def finger(self, resource, host=None, rel=None, raw=False, params=dict(),
               headers=dict()):
        """Perform a WebFinger lookup.

        args:
        resource - resource to look up
        host - host to use for resource lookup
        rel - relation to request
        raw - return unparsed JRD
        params - HTTP parameters to pass (note: resource and rel will be
                 overwritten)
        headers - HTTP headers to send with the request
        """
        if not host:
            host = self._parse_host(resource)

        url = self.WEBFINGER_URL.format(host=host)

        params["resource"] = resource
{'fraction_non_alphanumeric': 0.09615214506855374, 'fraction_numerical': 0.004068996019460416, 'mean_word_length': 2.392139213921392, 'pattern_counts': {'":': 18, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '449998', 'n_tokens_mistral': 3340, 'n_tokens_neox': 3174, 'n_words': 1064}