diff --git a/lib/geoip2/__init__.py b/lib/geoip2/__init__.py deleted file mode 100644 index 590124a9..00000000 --- a/lib/geoip2/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# pylint:disable=C0111 - -__title__ = 'geoip2' -__version__ = '2.4.0' -__author__ = 'Gregory Oschwald' -__license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright (c) 2013-2016 Maxmind, Inc.' diff --git a/lib/geoip2/compat.py b/lib/geoip2/compat.py deleted file mode 100644 index 67c5fa65..00000000 --- a/lib/geoip2/compat.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Intended for internal use only.""" -import sys - -import ipaddress - -# pylint: skip-file - -if sys.version_info[0] == 2: - def compat_ip_address(address): - """Intended for internal use only.""" - if isinstance(address, bytes): - address = address.decode() - return ipaddress.ip_address(address) -else: - def compat_ip_address(address): - """Intended for internal use only.""" - return ipaddress.ip_address(address) diff --git a/lib/geoip2/database.py b/lib/geoip2/database.py deleted file mode 100644 index ed21d6d4..00000000 --- a/lib/geoip2/database.py +++ /dev/null @@ -1,199 +0,0 @@ -""" -====================== -GeoIP2 Database Reader -====================== - -""" -import inspect - -import maxminddb -# pylint: disable=unused-import -from maxminddb import (MODE_AUTO, MODE_MMAP, MODE_MMAP_EXT, MODE_FILE, - MODE_MEMORY) - -import geoip2 -import geoip2.models -import geoip2.errors - - -class Reader(object): - """GeoIP2 database Reader object. - - Instances of this class provide a reader for the GeoIP2 database format. - IP addresses can be looked up using the ``country`` and ``city`` methods. - - The basic API for this class is the same for every database. First, you - create a reader object, specifying a file name. You then call the method - corresponding to the specific database, passing it the IP address you want - to look up. - - If the request succeeds, the method call will return a model class for the - method you called. This model in turn contains multiple record classes, - each of which represents part of the data returned by the database. If the - database does not contain the requested information, the attributes on the - record class will have a ``None`` value. - - If the address is not in the database, an - ``geoip2.errors.AddressNotFoundError`` exception will be thrown. If the - database is corrupt or invalid, a ``maxminddb.InvalidDatabaseError`` will - be thrown. - -""" - - def __init__(self, filename, locales=None, mode=MODE_AUTO): - """Create GeoIP2 Reader. - - :param filename: The path to the GeoIP2 database. - :param locales: This is list of locale codes. This argument will be - passed on to record classes to use when their name properties are - called. The default value is ['en']. - - The order of the locales is significant. When a record class has - multiple names (country, city, etc.), its name property will return - the name in the first locale that has one. - - Note that the only locale which is always present in the GeoIP2 - data is "en". If you do not include this locale, the name property - may end up returning None even when the record has an English name. - - Currently, the valid locale codes are: - - * de -- German - * en -- English names may still include accented characters if that - is the accepted spelling in English. In other words, English does - not mean ASCII. - * es -- Spanish - * fr -- French - * ja -- Japanese - * pt-BR -- Brazilian Portuguese - * ru -- Russian - * zh-CN -- Simplified Chinese. - :param mode: The mode to open the database with. Valid mode are: - * MODE_MMAP_EXT - use the C extension with memory map. - * MODE_MMAP - read from memory map. Pure Python. - * MODE_FILE - read database as standard file. Pure Python. - * MODE_MEMORY - load database into memory. Pure Python. - * MODE_AUTO - try MODE_MMAP_EXT, MODE_MMAP, MODE_FILE in that order. - Default. - - """ - if locales is None: - locales = ['en'] - self._db_reader = maxminddb.open_database(filename, mode) - self._locales = locales - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - def country(self, ip_address): - """Get the Country object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.Country` object - - """ - - return self._model_for(geoip2.models.Country, 'Country', ip_address) - - def city(self, ip_address): - """Get the City object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.City` object - - """ - return self._model_for(geoip2.models.City, 'City', ip_address) - - def anonymous_ip(self, ip_address): - """Get the AnonymousIP object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.AnonymousIP` object - - """ - return self._flat_model_for(geoip2.models.AnonymousIP, - 'GeoIP2-Anonymous-IP', ip_address) - - def connection_type(self, ip_address): - """Get the ConnectionType object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.ConnectionType` object - - """ - return self._flat_model_for(geoip2.models.ConnectionType, - 'GeoIP2-Connection-Type', ip_address) - - def domain(self, ip_address): - """Get the Domain object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.Domain` object - - """ - return self._flat_model_for(geoip2.models.Domain, 'GeoIP2-Domain', - ip_address) - - def enterprise(self, ip_address): - """Get the Enterprise object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.Enterprise` object - - """ - return self._model_for(geoip2.models.Enterprise, 'Enterprise', - ip_address) - - def isp(self, ip_address): - """Get the ISP object for the IP address. - - :param ip_address: IPv4 or IPv6 address as a string. - - :returns: :py:class:`geoip2.models.ISP` object - - """ - return self._flat_model_for(geoip2.models.ISP, 'GeoIP2-ISP', - ip_address) - - def _get(self, database_type, ip_address): - if database_type not in self.metadata().database_type: - caller = inspect.stack()[2][3] - raise TypeError("The %s method cannot be used with the " - "%s database" % - (caller, self.metadata().database_type)) - record = self._db_reader.get(ip_address) - if record is None: - raise geoip2.errors.AddressNotFoundError( - "The address %s is not in the database." % ip_address) - return record - - def _model_for(self, model_class, types, ip_address): - record = self._get(types, ip_address) - record.setdefault('traits', {})['ip_address'] = ip_address - return model_class(record, locales=self._locales) - - def _flat_model_for(self, model_class, types, ip_address): - record = self._get(types, ip_address) - record['ip_address'] = ip_address - return model_class(record) - - def metadata(self): - """The metadata for the open database. - - :returns: :py:class:`maxminddb.reader.Metadata` object - """ - return self._db_reader.metadata() - - def close(self): - """Closes the GeoIP2 database.""" - - self._db_reader.close() diff --git a/lib/geoip2/errors.py b/lib/geoip2/errors.py deleted file mode 100644 index 468b5858..00000000 --- a/lib/geoip2/errors.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Errors -====== - -""" - - -class GeoIP2Error(RuntimeError): - """There was a generic error in GeoIP2. - - This class represents a generic error. It extends :py:exc:`RuntimeError` - and does not add any additional attributes. - - """ - - -class AddressNotFoundError(GeoIP2Error): - """The address you were looking up was not found.""" - - -class AuthenticationError(GeoIP2Error): - """There was a problem authenticating the request.""" - - -class HTTPError(GeoIP2Error): - """There was an error when making your HTTP request. - - This class represents an HTTP transport error. It extends - :py:exc:`GeoIP2Error` and adds attributes of its own. - - :ivar http_status: The HTTP status code returned - :ivar uri: The URI queried - - """ - - def __init__(self, message, http_status=None, uri=None): - super(HTTPError, self).__init__(message) - self.http_status = http_status - self.uri = uri - - -class InvalidRequestError(GeoIP2Error): - """The request was invalid.""" - - -class OutOfQueriesError(GeoIP2Error): - """Your account is out of funds for the service queried.""" - - -class PermissionRequiredError(GeoIP2Error): - """Your account does not have permission to access this service.""" diff --git a/lib/geoip2/mixins.py b/lib/geoip2/mixins.py deleted file mode 100644 index 7fb4c275..00000000 --- a/lib/geoip2/mixins.py +++ /dev/null @@ -1,16 +0,0 @@ -"""This package contains utility mixins""" -# pylint: disable=too-few-public-methods -from abc import ABCMeta - - -class SimpleEquality(object): - """Naive __dict__ equality mixin""" - - __metaclass__ = ABCMeta - - def __eq__(self, other): - return (isinstance(other, self.__class__) and - self.__dict__ == other.__dict__) - - def __ne__(self, other): - return not self.__eq__(other) diff --git a/lib/geoip2/models.py b/lib/geoip2/models.py deleted file mode 100644 index 15e951b0..00000000 --- a/lib/geoip2/models.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -Models -====== - -These classes provide models for the data returned by the GeoIP2 -web service and databases. - -The only difference between the City and Insights model classes is which -fields in each record may be populated. See -http://dev.maxmind.com/geoip/geoip2/web-services for more details. - -""" -# pylint: disable=too-many-instance-attributes,too-few-public-methods -from abc import ABCMeta - -import geoip2.records -from geoip2.mixins import SimpleEquality - - -class Country(SimpleEquality): - """Model for the GeoIP2 Precision: Country and the GeoIP2 Country database. - - This class provides the following attributes: - - .. attribute:: continent - - Continent object for the requested IP address. - - :type: :py:class:`geoip2.records.Continent` - - .. attribute:: country - - Country object for the requested IP address. This record represents the - country where MaxMind believes the IP is located. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: maxmind - - Information related to your MaxMind account. - - :type: :py:class:`geoip2.records.MaxMind` - - .. attribute:: registered_country - - The registered country object for the requested IP address. This record - represents the country where the ISP has registered a given IP block in - and may differ from the user's country. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: represented_country - - Object for the country represented by the users of the IP address - when that country is different than the country in ``country``. For - instance, the country represented by an overseas military base. - - :type: :py:class:`geoip2.records.RepresentedCountry` - - .. attribute:: traits - - Object with the traits of the requested IP address. - - :type: :py:class:`geoip2.records.Traits` - - """ - - def __init__(self, raw_response, locales=None): - if locales is None: - locales = ['en'] - self._locales = locales - self.continent = \ - geoip2.records.Continent(locales, - **raw_response.get('continent', {})) - self.country = \ - geoip2.records.Country(locales, - **raw_response.get('country', {})) - self.registered_country = \ - geoip2.records.Country(locales, - **raw_response.get('registered_country', - {})) - self.represented_country \ - = geoip2.records.RepresentedCountry(locales, - **raw_response.get( - 'represented_country', {})) - - self.maxmind = \ - geoip2.records.MaxMind(**raw_response.get('maxmind', {})) - - self.traits = geoip2.records.Traits(**raw_response.get('traits', {})) - self.raw = raw_response - - def __repr__(self): - return '{module}.{class_name}({data}, {locales})'.format( - module=self.__module__, - class_name=self.__class__.__name__, - data=self.raw, - locales=self._locales) - - -class City(Country): - """Model for the GeoIP2 Precision: City and the GeoIP2 City database. - - .. attribute:: city - - City object for the requested IP address. - - :type: :py:class:`geoip2.records.City` - - .. attribute:: continent - - Continent object for the requested IP address. - - :type: :py:class:`geoip2.records.Continent` - - .. attribute:: country - - Country object for the requested IP address. This record represents the - country where MaxMind believes the IP is located. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: location - - Location object for the requested IP address. - - .. attribute:: maxmind - - Information related to your MaxMind account. - - :type: :py:class:`geoip2.records.MaxMind` - - .. attribute:: registered_country - - The registered country object for the requested IP address. This record - represents the country where the ISP has registered a given IP block in - and may differ from the user's country. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: represented_country - - Object for the country represented by the users of the IP address - when that country is different than the country in ``country``. For - instance, the country represented by an overseas military base. - - :type: :py:class:`geoip2.records.RepresentedCountry` - - .. attribute:: subdivisions - - Object (tuple) representing the subdivisions of the country to which - the location of the requested IP address belongs. - - :type: :py:class:`geoip2.records.Subdivisions` - - .. attribute:: traits - - Object with the traits of the requested IP address. - - :type: :py:class:`geoip2.records.Traits` - - """ - - def __init__(self, raw_response, locales=None): - super(City, self).__init__(raw_response, locales) - self.city = \ - geoip2.records.City(locales, **raw_response.get('city', {})) - self.location = \ - geoip2.records.Location(**raw_response.get('location', {})) - self.postal = \ - geoip2.records.Postal(**raw_response.get('postal', {})) - self.subdivisions = \ - geoip2.records.Subdivisions(locales, - *raw_response.get('subdivisions', [])) - - -class Insights(City): - """Model for the GeoIP2 Precision: Insights web service endpoint. - - .. attribute:: city - - City object for the requested IP address. - - :type: :py:class:`geoip2.records.City` - - .. attribute:: continent - - Continent object for the requested IP address. - - :type: :py:class:`geoip2.records.Continent` - - .. attribute:: country - - Country object for the requested IP address. This record represents the - country where MaxMind believes the IP is located. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: location - - Location object for the requested IP address. - - .. attribute:: maxmind - - Information related to your MaxMind account. - - :type: :py:class:`geoip2.records.MaxMind` - - .. attribute:: registered_country - - The registered country object for the requested IP address. This record - represents the country where the ISP has registered a given IP block in - and may differ from the user's country. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: represented_country - - Object for the country represented by the users of the IP address - when that country is different than the country in ``country``. For - instance, the country represented by an overseas military base. - - :type: :py:class:`geoip2.records.RepresentedCountry` - - .. attribute:: subdivisions - - Object (tuple) representing the subdivisions of the country to which - the location of the requested IP address belongs. - - :type: :py:class:`geoip2.records.Subdivisions` - - .. attribute:: traits - - Object with the traits of the requested IP address. - - :type: :py:class:`geoip2.records.Traits` - - """ - - -class Enterprise(City): - """Model for the GeoIP2 Enterprise database. - - .. attribute:: city - - City object for the requested IP address. - - :type: :py:class:`geoip2.records.City` - - .. attribute:: continent - - Continent object for the requested IP address. - - :type: :py:class:`geoip2.records.Continent` - - .. attribute:: country - - Country object for the requested IP address. This record represents the - country where MaxMind believes the IP is located. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: location - - Location object for the requested IP address. - - .. attribute:: maxmind - - Information related to your MaxMind account. - - :type: :py:class:`geoip2.records.MaxMind` - - .. attribute:: registered_country - - The registered country object for the requested IP address. This record - represents the country where the ISP has registered a given IP block in - and may differ from the user's country. - - :type: :py:class:`geoip2.records.Country` - - .. attribute:: represented_country - - Object for the country represented by the users of the IP address - when that country is different than the country in ``country``. For - instance, the country represented by an overseas military base. - - :type: :py:class:`geoip2.records.RepresentedCountry` - - .. attribute:: subdivisions - - Object (tuple) representing the subdivisions of the country to which - the location of the requested IP address belongs. - - :type: :py:class:`geoip2.records.Subdivisions` - - .. attribute:: traits - - Object with the traits of the requested IP address. - - :type: :py:class:`geoip2.records.Traits` - - """ - - -class SimpleModel(SimpleEquality): - """Provides basic methods for non-location models""" - - __metaclass__ = ABCMeta - - def __repr__(self): - # pylint: disable=no-member - return '{module}.{class_name}({data})'.format( - module=self.__module__, - class_name=self.__class__.__name__, - data=str(self.raw)) - - -class AnonymousIP(SimpleModel): - """Model class for the GeoIP2 Anonymous IP. - - This class provides the following attribute: - - .. attribute:: is_anonymous - - This is true if the IP address belongs to any sort of anonymous network. - - :type: bool - - .. attribute:: is_anonymous_vpn - - This is true if the IP address belongs to an anonymous VPN system. - - :type: bool - - .. attribute:: is_hosting_provider - - This is true if the IP address belongs to a hosting provider. - - :type: bool - - .. attribute:: is_public_proxy - - This is true if the IP address belongs to a public proxy. - - :type: bool - - .. attribute:: is_tor_exit_node - - This is true if the IP address is a Tor exit node. - - :type: bool - - .. attribute:: ip_address - - The IP address used in the lookup. - - :type: unicode - """ - - def __init__(self, raw): - self.is_anonymous = raw.get('is_anonymous', False) - self.is_anonymous_vpn = raw.get('is_anonymous_vpn', False) - self.is_hosting_provider = raw.get('is_hosting_provider', False) - self.is_public_proxy = raw.get('is_public_proxy', False) - self.is_tor_exit_node = raw.get('is_tor_exit_node', False) - - self.ip_address = raw.get('ip_address') - self.raw = raw - - -class ConnectionType(SimpleModel): - """Model class for the GeoIP2 Connection-Type. - - This class provides the following attribute: - - .. attribute:: connection_type - - The connection type may take the following values: - - - Dialup - - Cable/DSL - - Corporate - - Cellular - - Additional values may be added in the future. - - :type: unicode - - .. attribute:: ip_address - - The IP address used in the lookup. - - :type: unicode - """ - - def __init__(self, raw): - self.connection_type = raw.get('connection_type') - self.ip_address = raw.get('ip_address') - self.raw = raw - - -class Domain(SimpleModel): - """Model class for the GeoIP2 Domain. - - This class provides the following attribute: - - .. attribute:: domain - - The domain associated with the IP address. - - :type: unicode - - .. attribute:: ip_address - - The IP address used in the lookup. - - :type: unicode - - """ - - def __init__(self, raw): - self.domain = raw.get('domain') - self.ip_address = raw.get('ip_address') - self.raw = raw - - -class ISP(SimpleModel): - """Model class for the GeoIP2 ISP. - - This class provides the following attribute: - - .. attribute:: autonomous_system_number - - The autonomous system number associated with the IP address. - - :type: int - - .. attribute:: autonomous_system_organization - - The organization associated with the registered autonomous system number - for the IP address. - - :type: unicode - - .. attribute:: isp - - The name of the ISP associated with the IP address. - - :type: unicode - - .. attribute:: organization - - The name of the organization associated with the IP address. - - :type: unicode - - .. attribute:: ip_address - - The IP address used in the lookup. - - :type: unicode - """ - - # pylint:disable=too-many-arguments - def __init__(self, raw): - self.autonomous_system_number = raw.get('autonomous_system_number') - self.autonomous_system_organization = raw.get( - 'autonomous_system_organization') - self.isp = raw.get('isp') - self.organization = raw.get('organization') - self.ip_address = raw.get('ip_address') - self.raw = raw diff --git a/lib/geoip2/records.py b/lib/geoip2/records.py deleted file mode 100644 index 7f99d121..00000000 --- a/lib/geoip2/records.py +++ /dev/null @@ -1,605 +0,0 @@ -""" - -Records -======= - -""" - -# pylint:disable=R0903 -from abc import ABCMeta - -from geoip2.mixins import SimpleEquality - - -class Record(SimpleEquality): - """All records are subclasses of the abstract class ``Record``.""" - - __metaclass__ = ABCMeta - - _valid_attributes = set() - - def __init__(self, **kwargs): - valid_args = dict((k, kwargs.get(k)) for k in self._valid_attributes) - self.__dict__.update(valid_args) - - def __setattr__(self, name, value): - raise AttributeError("can't set attribute") - - def __repr__(self): - args = ', '.join('%s=%r' % x for x in self.__dict__.items()) - return '{module}.{class_name}({data})'.format( - module=self.__module__, - class_name=self.__class__.__name__, - data=args) - - -class PlaceRecord(Record): - """All records with :py:attr:`names` subclass :py:class:`PlaceRecord`.""" - - __metaclass__ = ABCMeta - - def __init__(self, locales=None, **kwargs): - if locales is None: - locales = ['en'] - if kwargs.get('names') is None: - kwargs['names'] = {} - object.__setattr__(self, '_locales', locales) - super(PlaceRecord, self).__init__(**kwargs) - - @property - def name(self): - """Dict with locale codes as keys and localized name as value.""" - # pylint:disable=E1101 - return next( - (self.names.get(x) for x in self._locales - if x in self.names), None) - - -class City(PlaceRecord): - """Contains data for the city record associated with an IP address. - - This class contains the city-level data associated with an IP address. - - This record is returned by ``city``, ``enterprise``, and ``insights``. - - Attributes: - - .. attribute:: confidence - - A value from 0-100 indicating MaxMind's - confidence that the city is correct. This attribute is only available - from the Insights end point and the GeoIP2 Enterprise database. - - :type: int - - .. attribute:: geoname_id - - The GeoName ID for the city. - - :type: int - - .. attribute:: name - - The name of the city based on the locales list passed to the - constructor. - - :type: unicode - - .. attribute:: names - - A dictionary where the keys are locale codes - and the values are names. - - :type: dict - - """ - - _valid_attributes = set(['confidence', 'geoname_id', 'names']) - - -class Continent(PlaceRecord): - """Contains data for the continent record associated with an IP address. - - This class contains the continent-level data associated with an IP - address. - - Attributes: - - - .. attribute:: code - - A two character continent code like "NA" (North America) - or "OC" (Oceania). - - :type: unicode - - .. attribute:: geoname_id - - The GeoName ID for the continent. - - :type: int - - .. attribute:: name - - Returns the name of the continent based on the locales list passed to - the constructor. - - :type: unicode - - .. attribute:: names - - A dictionary where the keys are locale codes - and the values are names. - - :type: dict - - """ - - _valid_attributes = set(['code', 'geoname_id', 'names']) - - -class Country(PlaceRecord): - """Contains data for the country record associated with an IP address. - - This class contains the country-level data associated with an IP address. - - Attributes: - - - .. attribute:: confidence - - A value from 0-100 indicating MaxMind's confidence that - the country is correct. This attribute is only available from the - Insights end point and the GeoIP2 Enterprise database. - - :type: int - - .. attribute:: geoname_id - - The GeoName ID for the country. - - :type: int - - .. attribute:: iso_code - - The two-character `ISO 3166-1 - `_ alpha code for the - country. - - :type: unicode - - .. attribute:: name - - The name of the country based on the locales list passed to the - constructor. - - :type: unicode - - .. attribute:: names - - A dictionary where the keys are locale codes and the values - are names. - - :type: dict - - """ - - _valid_attributes = set(['confidence', 'geoname_id', 'iso_code', 'names']) - - -class RepresentedCountry(Country): - """Contains data for the represented country associated with an IP address. - - This class contains the country-level data associated with an IP address - for the IP's represented country. The represented country is the country - represented by something like a military base. - - Attributes: - - - .. attribute:: confidence - - A value from 0-100 indicating MaxMind's confidence that - the country is correct. This attribute is only available from the - Insights end point and the GeoIP2 Enterprise database. - - :type: int - - .. attribute:: geoname_id - - The GeoName ID for the country. - - :type: int - - .. attribute:: iso_code - - The two-character `ISO 3166-1 - `_ alpha code for the country. - - :type: unicode - - .. attribute:: name - - The name of the country based on the locales list passed to the - constructor. - - :type: unicode - - .. attribute:: names - - A dictionary where the keys are locale codes and the values - are names. - - :type: dict - - - .. attribute:: type - - A string indicating the type of entity that is representing the - country. Currently we only return ``military`` but this could expand to - include other types in the future. - - :type: unicode - - """ - - _valid_attributes = set(['confidence', 'geoname_id', 'iso_code', 'names', - 'type']) - - -class Location(Record): - """Contains data for the location record associated with an IP address. - - This class contains the location data associated with an IP address. - - This record is returned by ``city``, ``enterprise``, and ``insights``. - - Attributes: - - .. attribute:: average_income - - The average income in US dollars associated with the requested IP - address. This attribute is only available from the Insights end point. - - :type: int - - .. attribute:: accuracy_radius - - The radius in kilometers around the specified location where the IP - address is likely to be. - - :type: int - - .. attribute:: latitude - - The approximate latitude of the location associated with the IP - address. This value is not precise and should not be used to identify a - particular address or household. - - :type: float - - .. attribute:: longitude - - The approximate longitude of the location associated with the IP - address. This value is not precise and should not be used to identify a - particular address or household. - - :type: float - - .. attribute:: metro_code - - The metro code of the location if the - location is in the US. MaxMind returns the same metro codes as the - `Google AdWords API - `_. - - :type: int - - .. attribute:: population_density - - The estimated population per square kilometer associated with the IP - address. This attribute is only available from the Insights end point. - - :type: int - - .. attribute:: time_zone - - The time zone associated with location, as specified by the `IANA Time - Zone Database `_, e.g., - "America/New_York". - - :type: unicode - - """ - - _valid_attributes = set(['average_income', 'accuracy_radius', 'latitude', - 'longitude', 'metro_code', 'population_density', - 'postal_code', 'postal_confidence', 'time_zone']) - - -class MaxMind(Record): - """Contains data related to your MaxMind account. - - Attributes: - - .. attribute:: queries_remaining - - The number of remaining queries you have - for the end point you are calling. - - :type: int - - """ - - _valid_attributes = set(['queries_remaining']) - - -class Postal(Record): - """Contains data for the postal record associated with an IP address. - - This class contains the postal data associated with an IP address. - - This attribute is returned by ``city``, ``enterprise``, and ``insights``. - - Attributes: - - .. attribute:: code - - The postal code of the location. Postal - codes are not available for all countries. In some countries, this will - only contain part of the postal code. - - :type: unicode - - .. attribute:: confidence - - A value from 0-100 indicating - MaxMind's confidence that the postal code is correct. This attribute is - only available from the Insights end point and the GeoIP2 Enterprise - database. - - :type: int - - """ - - _valid_attributes = set(['code', 'confidence']) - - -class Subdivision(PlaceRecord): - """Contains data for the subdivisions associated with an IP address. - - This class contains the subdivision data associated with an IP address. - - This attribute is returned by ``city``, ``enterprise``, and ``insights``. - - Attributes: - - .. attribute:: confidence - - This is a value from 0-100 indicating MaxMind's - confidence that the subdivision is correct. This attribute is only - available from the Insights end point and the GeoIP2 Enterprise - database. - - :type: int - - .. attribute:: geoname_id - - This is a GeoName ID for the subdivision. - - :type: int - - .. attribute:: iso_code - - This is a string up to three characters long - contain the subdivision portion of the `ISO 3166-2 code - `_. - - :type: unicode - - .. attribute:: name - - The name of the subdivision based on the locales list passed to the - constructor. - - :type: unicode - - .. attribute:: names - - A dictionary where the keys are locale codes and the - values are names - - :type: dict - - """ - - _valid_attributes = set(['confidence', 'geoname_id', 'iso_code', 'names']) - - -class Subdivisions(tuple): - """A tuple-like collection of subdivisions associated with an IP address. - - This class contains the subdivisions of the country associated with the - IP address from largest to smallest. - - For instance, the response for Oxford in the United Kingdom would have - England as the first element and Oxfordshire as the second element. - - This attribute is returned by ``city``, ``enterprise``, and ``insights``. - """ - - def __new__(cls, locales, *subdivisions): - subdivisions = [Subdivision(locales, **x) for x in subdivisions] - obj = super(cls, Subdivisions).__new__(cls, subdivisions) - return obj - - def __init__(self, locales, *subdivisions): # pylint:disable=W0613 - self._locales = locales - super(Subdivisions, self).__init__() - - @property - def most_specific(self): - """The most specific (smallest) subdivision available. - - If there are no :py:class:`Subdivision` objects for the response, - this returns an empty :py:class:`Subdivision`. - - :type: :py:class:`Subdivision` - """ - try: - return self[-1] - except IndexError: - return Subdivision(self._locales) - - -class Traits(Record): - """Contains data for the traits record associated with an IP address. - - This class contains the traits data associated with an IP address. - - This class has the following attributes: - - - .. attribute:: autonomous_system_number - - The `autonomous system - number `_ - associated with the IP address. This attribute is only available from - the City and Insights web service end points and the GeoIP2 Enterprise - database. - - :type: int - - .. attribute:: autonomous_system_organization - - The organization associated with the registered `autonomous system - number `_ for - the IP address. This attribute is only available from the City and - Insights web service end points and the GeoIP2 Enterprise database. - - :type: unicode - - .. attribute:: connection_type - - The connection type may take the following values: - - - Dialup - - Cable/DSL - - Corporate - - Cellular - - Additional values may be added in the future. - - This attribute is only available in the GeoIP2 Enterprise database. - - :type: unicode - - .. attribute:: domain - - The second level domain associated with the - IP address. This will be something like "example.com" or - "example.co.uk", not "foo.example.com". This attribute is only available - from the City and Insights web service end points and the GeoIP2 - Enterprise database. - - :type: unicode - - .. attribute:: ip_address - - The IP address that the data in the model - is for. If you performed a "me" lookup against the web service, this - will be the externally routable IP address for the system the code is - running on. If the system is behind a NAT, this may differ from the IP - address locally assigned to it. - - :type: unicode - - .. attribute:: is_anonymous_proxy - - This is true if the IP is an anonymous - proxy. See http://dev.maxmind.com/faq/geoip#anonproxy for further - details. - - :type: bool - - .. deprecated:: 2.2.0 - Use our our `GeoIP2 Anonymous IP database - `_ - instead. - - .. attribute:: is_legitimate_proxy - - This attribute is true if MaxMind believes this IP address to be a - legitimate proxy, such as an internal VPN used by a corporation. This - attribute is only available in the GeoIP2 Enterprise database. - - :type: bool - - .. attribute:: is_satellite_provider - - This is true if the IP address is from a satellite provider that - provides service to multiple countries. - - :type: bool - - .. deprecated:: 2.2.0 - Due to the increased coverage by mobile carriers, very few - satellite providers now serve multiple countries. As a result, the - output does not provide sufficiently relevant data for us to maintain - it. - - .. attribute:: isp - - The name of the ISP associated with the IP address. This attribute is - only available from the City and Insights web service end points and the - GeoIP2 Enterprise database. - - :type: unicode - - .. attribute:: organization - - The name of the organization associated with the IP address. This - attribute is only available from the City and Insights web service end - points and the GeoIP2 Enterprise database. - - :type: unicode - - .. attribute:: user_type - - The user type associated with the IP - address. This can be one of the following values: - - * business - * cafe - * cellular - * college - * content_delivery_network - * dialup - * government - * hosting - * library - * military - * residential - * router - * school - * search_engine_spider - * traveler - - This attribute is only available from the Insights end point and the - GeoIP2 Enterprise database. - - :type: unicode - - """ - - _valid_attributes = set( - ['autonomous_system_number', 'autonomous_system_organization', - 'connection_type', 'domain', 'is_anonymous_proxy', - 'is_legitimate_proxy', 'is_satellite_provider', 'isp', 'ip_address', - 'organization', 'user_type']) - - def __init__(self, **kwargs): - for k in ['is_anonymous_proxy', 'is_legitimate_proxy', - 'is_satellite_provider']: - kwargs[k] = bool(kwargs.get(k, False)) - super(Traits, self).__init__(**kwargs) diff --git a/lib/geoip2/webservice.py b/lib/geoip2/webservice.py deleted file mode 100644 index c64f1b80..00000000 --- a/lib/geoip2/webservice.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -============================ -WebServices Client API -============================ - -This class provides a client API for all the GeoIP2 Precision web service end -points. The end points are Country, City, and Insights. Each end point returns -a different set of data about an IP address, with Country returning the least -data and Insights the most. - -Each web service end point is represented by a different model class, and -these model classes in turn contain multiple record classes. The record -classes have attributes which contain data about the IP address. - -If the web service does not return a particular piece of data for an IP -address, the associated attribute is not populated. - -The web service may not return any information for an entire record, in which -case all of the attributes for that record class will be empty. - -SSL ---- - -Requests to the GeoIP2 Precision web service are always made with SSL. - -""" - -import requests - -from requests.utils import default_user_agent - -import geoip2 -import geoip2.models - -from .compat import compat_ip_address - -from .errors import (AddressNotFoundError, AuthenticationError, GeoIP2Error, - HTTPError, InvalidRequestError, OutOfQueriesError, - PermissionRequiredError) - - -class Client(object): - """Creates a new client object. - - It accepts the following required arguments: - - :param user_id: Your MaxMind User ID. - :param license_key: Your MaxMind license key. - - Go to https://www.maxmind.com/en/my_license_key to see your MaxMind - User ID and license key. - - The following keyword arguments are also accepted: - - :param host: The hostname to make a request against. This defaults to - "geoip.maxmind.com". In most cases, you should not need to set this - explicitly. - :param locales: This is list of locale codes. This argument will be - passed on to record classes to use when their name properties are - called. The default value is ['en']. - - The order of the locales is significant. When a record class has - multiple names (country, city, etc.), its name property will return - the name in the first locale that has one. - - Note that the only locale which is always present in the GeoIP2 - data is "en". If you do not include this locale, the name property - may end up returning None even when the record has an English name. - - Currently, the valid locale codes are: - - * de -- German - * en -- English names may still include accented characters if that is - the accepted spelling in English. In other words, English does not - mean ASCII. - * es -- Spanish - * fr -- French - * ja -- Japanese - * pt-BR -- Brazilian Portuguese - * ru -- Russian - * zh-CN -- Simplified Chinese. - - """ - - def __init__(self, - user_id, - license_key, - host='geoip.maxmind.com', - locales=None, - timeout=None): - """Construct a Client.""" - # pylint: disable=too-many-arguments - if locales is None: - locales = ['en'] - self._locales = locales - self._user_id = user_id - self._license_key = license_key - self._base_uri = 'https://%s/geoip/v2.1' % host - self._timeout = timeout - - def city(self, ip_address='me'): - """Call GeoIP2 Precision City endpoint with the specified IP. - - :param ip_address: IPv4 or IPv6 address as a string. If no - address is provided, the address that the web service is - called from will be used. - - :returns: :py:class:`geoip2.models.City` object - - """ - return self._response_for('city', geoip2.models.City, ip_address) - - def country(self, ip_address='me'): - """Call the GeoIP2 Country endpoint with the specified IP. - - :param ip_address: IPv4 or IPv6 address as a string. If no address - is provided, the address that the web service is called from will - be used. - - :returns: :py:class:`geoip2.models.Country` object - - """ - return self._response_for('country', geoip2.models.Country, ip_address) - - def insights(self, ip_address='me'): - """Call the GeoIP2 Precision: Insights endpoint with the specified IP. - - :param ip_address: IPv4 or IPv6 address as a string. If no address - is provided, the address that the web service is called from will - be used. - - :returns: :py:class:`geoip2.models.Insights` object - - """ - return self._response_for('insights', geoip2.models.Insights, - ip_address) - - def _response_for(self, path, model_class, ip_address): - if ip_address != 'me': - ip_address = str(compat_ip_address(ip_address)) - uri = '/'.join([self._base_uri, path, ip_address]) - response = requests.get(uri, - auth=(self._user_id, self._license_key), - headers={'Accept': 'application/json', - 'User-Agent': self._user_agent()}, - timeout=self._timeout) - if response.status_code == 200: - body = self._handle_success(response, uri) - return model_class(body, locales=self._locales) - else: - self._handle_error(response, uri) - - def _user_agent(self): - return 'GeoIP2 Python Client v%s (%s)' % (geoip2.__version__, - default_user_agent()) - - def _handle_success(self, response, uri): - try: - return response.json() - except ValueError as ex: - raise GeoIP2Error('Received a 200 response for %(uri)s' - ' but could not decode the response as ' - 'JSON: ' % locals() + ', '.join(ex.args), 200, - uri) - - def _handle_error(self, response, uri): - status = response.status_code - - if 400 <= status < 500: - self._handle_4xx_status(response, status, uri) - elif 500 <= status < 600: - self._handle_5xx_status(status, uri) - else: - self._handle_non_200_status(status, uri) - - def _handle_4xx_status(self, response, status, uri): - if not response.content: - raise HTTPError('Received a %(status)i error for %(uri)s ' - 'with no body.' % locals(), status, uri) - elif response.headers['Content-Type'].find('json') == -1: - raise HTTPError('Received a %i for %s with the following ' - 'body: %s' % (status, uri, response.content), - status, uri) - try: - body = response.json() - except ValueError as ex: - raise HTTPError( - 'Received a %(status)i error for %(uri)s but it did' - ' not include the expected JSON body: ' % locals() + - ', '.join(ex.args), status, uri) - else: - if 'code' in body and 'error' in body: - self._handle_web_service_error( - body.get('error'), body.get('code'), status, uri) - else: - raise HTTPError( - 'Response contains JSON but it does not specify ' - 'code or error keys', status, uri) - - def _handle_web_service_error(self, message, code, status, uri): - if code in ('IP_ADDRESS_NOT_FOUND', 'IP_ADDRESS_RESERVED'): - raise AddressNotFoundError(message) - elif code in ('AUTHORIZATION_INVALID', 'LICENSE_KEY_REQUIRED', - 'USER_ID_REQUIRED', 'USER_ID_UNKNOWN'): - raise AuthenticationError(message) - elif code in ('INSUFFICIENT_FUNDS', 'OUT_OF_QUERIES'): - raise OutOfQueriesError(message) - elif code == 'PERMISSION_REQUIRED': - raise PermissionRequiredError(message) - - raise InvalidRequestError(message, code, status, uri) - - def _handle_5xx_status(self, status, uri): - raise HTTPError('Received a server error (%(status)i) for ' - '%(uri)s' % locals(), status, uri) - - def _handle_non_200_status(self, status, uri): - raise HTTPError('Received a very surprising HTTP status ' - '(%(status)i) for %(uri)s' % locals(), status, uri)